How to Add Custom MCP Tools to Extend Server Functionality in MCP Ambari API

You can extend the MCP Ambari API server by creating async Python functions decorated with @mcp.tool() and optionally @log_tool, which FastMCP registers automatically at import time and exposes to all MCP clients without additional configuration.

The MCP Ambari API server is built on FastMCP to provide programmatic access to Apache Ambari cluster operations. To add custom MCP tools that extend server functionality, you write async functions in src/mcp_ambari_api/mcp_main.py (or any module imported before main() executes) and apply the @mcp.tool decorator, reusing helpers like make_ambari_request from functions.py for consistent HTTP handling and structured logging.

How FastMCP Tool Registration Works

In src/mcp_ambari_api/mcp_main.py, the global FastMCP instance is instantiated at lines 79-80:

mcp = FastMCP("mcp-ambari-api")

When the module is imported, FastMCP collects every function decorated with @mcp.tool(). The server then starts via mcp.run() at line 3731 inside the main() function. Consequently, any async function decorated before main() executes becomes instantly available to MCP clients (CLI, OpenWebUI, or MCPO proxy) without further configuration.

Step-by-Step Guide to Adding Custom Tools

Follow these steps to implement new functionality:

1. Create an Async Function

Define a new async def function in src/mcp_ambari_api/mcp_main.py or a separate module that is imported before main() runs. FastMCP registers tools at import time, so the function must be defined before the server starts.

2. Apply the @mcp.tool Decorator

Use @mcp.tool(title="Your Tool Name") to register the callable with the global FastMCP instance. The title parameter specifies the name shown to clients.

3. Add Optional @log_tool Wrapper

Apply @log_tool to enable automatic start/stop timing and consistent log formatting. Existing tools like dump_configurations use this wrapper at lines 39-42 in mcp_main.py.

4. Implement Business Logic

Write the async body using helpers from src/mcp_ambari_api/functions.py. The make_ambari_request function handles HTTP transport, error handling, and response parsing uniformly.

5. Export Public API (Optional)

If you want the tool importable from the package root, append its name to __all__ in src/mcp_ambari_api/__init__.py.

6. Test Locally

Run the server in stdio mode to verify registration:

PYTHONPATH=./src uv run python -m mcp_ambari_api --type stdio

Then invoke the tool via the MCP inspector or a simple client to validate output formatting.

Complete Example: Adding a "Ping Ambari" Tool

Here is a production-ready example that adds a lightweight health-check tool to src/mcp_ambari_api/mcp_main.py:

from .functions import make_ambari_request, AMBARI_CLUSTER_NAME

@mcp.tool(title="Ping Ambari")
@log_tool
async def ping_ambari() -> str:
    """
    Very lightweight health-check: tries to fetch the cluster summary.
    Returns a one-line status that LLM agents can use in decision trees.
    """
    endpoint = f"/clusters/{AMBARI_CLUSTER_NAME}"
    resp = await make_ambari_request(endpoint)

    if resp is None:
        return "❌ Unable to contact Ambari – no response."

    if "error" in resp:
        return f"❌ Ambari returned an error: {resp['error']}"

    version = resp.get("Clusters", {}).get("version", "unknown")
    return f"✅ Ambari reachable – cluster version: {version}"

This tool is now listed alongside built-in tools like get_cluster_info and can be invoked from any MCP client using the command ping_ambari.

Core Files and Helper Functions

When adding custom MCP tools, reference these key files:

  • src/mcp_ambari_api/mcp_main.py: Contains the FastMCP instance creation (lines 79-80), the mcp.run() call (line 3731), and existing tool implementations like dump_configurations and start_service.

  • src/mcp_ambari_api/functions.py: Provides make_ambari_request for Ambari HTTP calls, timestamp handling, and metric caching. Reuse these helpers to ensure consistent error handling and response parsing.

  • src/mcp_ambari_api/__init__.py: Package initializer where you can add tool names to __all__ for public API exports.

Best Practices for Custom MCP Tools

  • Keep tools atomic: Each tool should perform one logical operation. Compose complex workflows from the client side by chaining multiple tool calls.

  • Reuse existing infrastructure: Always use make_ambari_request for HTTP transport and @log_tool for uniform logging rather than implementing custom network or logging code.

  • Document thoroughly: Include docstrings following the repository's existing format (sections like [Tool Role], [Core Functions], [Required Usage Scenarios]) to enable automatic help generation.

  • Avoid blocking I/O: Never use synchronous libraries like requests inside tools. All network calls must be awaited to maintain server responsiveness.

  • Validate with MCP Inspector: Test new tools using ./run-mcp-inspector-local.sh to verify JSON schema compliance and output formatting before deployment.

Summary

  • Write async functions decorated with @mcp.tool() anywhere imported before main() executes in mcp_main.py.

  • Apply @log_tool for automatic timing and consistent log formatting across all custom tools.

  • Leverage make_ambari_request from functions.py to ensure uniform HTTP handling and error management.

  • Optionally export the tool name in __init__.py to maintain a tidy public API.

  • Test locally using stdio mode and the MCP inspector before committing changes.

Frequently Asked Questions

How does FastMCP know which functions to expose as tools?

FastMCP scans for the @mcp.tool() decorator at import time. When you decorate an async function with @mcp.tool(title="Name"), the decorator registers the callable with the global FastMCP instance created at lines 79-80 in src/mcp_ambari_api/mcp_main.py. The server then exposes these functions through mcp.run() at line 3731 without requiring explicit registration calls.

Can I place custom tools in separate files instead of mcp_main.py?

Yes. You can define custom tools in any Python module provided it is imported before main() executes in mcp_main.py. Add an import statement at the top of mcp_main.py to ensure FastMCP registers the decorated functions during module initialization. The tools become available immediately without modifying the server startup code.

What is the purpose of the @log_tool decorator?

The @log_tool wrapper provides automatic start/stop timing and consistent log formatting for every tool invocation. According to the source code in mcp_main.py (see lines 39-42 in the dump_configurations implementation), this decorator ensures all tools follow the same logging standards, making debugging and performance monitoring easier across custom and built-in tools alike.

Should I use make_ambari_request or write my own HTTP client?

Always use make_ambari_request from src/mcp_ambari_api/functions.py. This helper handles authentication, error parsing, timeout management, and response formatting consistently across the codebase. Writing a custom HTTP client risks inconsistent error handling and bypasses the centralized logging and retry logic implemented in the helper function.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →