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 theFastMCPinstance creation (lines 79-80), themcp.run()call (line 3731), and existing tool implementations likedump_configurationsandstart_service. -
src/mcp_ambari_api/functions.py: Providesmake_ambari_requestfor 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_requestfor HTTP transport and@log_toolfor 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
requestsinside tools. All network calls must be awaited to maintain server responsiveness. -
Validate with MCP Inspector: Test new tools using
./run-mcp-inspector-local.shto verify JSON schema compliance and output formatting before deployment.
Summary
-
Write async functions decorated with
@mcp.tool()anywhere imported beforemain()executes inmcp_main.py. -
Apply
@log_toolfor automatic timing and consistent log formatting across all custom tools. -
Leverage
make_ambari_requestfromfunctions.pyto ensure uniform HTTP handling and error management. -
Optionally export the tool name in
__init__.pyto 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →