# Understanding the log_tool Decorator in MCP Ambari API: Implementation and Usage Guide

> Explore the log_tool decorator in MCP Ambari API. Learn how this Python wrapper instruments FastMCP tools with structured logging execution timing and intelligent error classification.

- Repository: [JungJungIn/mcp-ambari-api](https://github.com/call518/mcp-ambari-api)
- Tags: how-to-guide
- Published: 2026-02-26

---

**The `log_tool` decorator is a Python wrapper defined in [`src/mcp_ambari_api/functions.py`](https://github.com/call518/mcp-ambari-api/blob/main/src/mcp_ambari_api/functions.py) that automatically instruments FastMCP tools with structured logging, execution timing, and intelligent error classification.**

The `call518/mcp-ambari-api` repository implements a Model Context Protocol (MCP) server for Apache Ambari cluster management. To maintain operational visibility without cluttering business logic, the codebase centers its observability strategy on the **`log_tool` decorator**—a reusable mechanism that ensures every tool function emits consistent telemetry.

## What Is the log_tool Decorator?

The **`log_tool` decorator** is implemented in [`src/mcp_ambari_api/functions.py`](https://github.com/call518/mcp-ambari-api/blob/main/src/mcp_ambari_api/functions.py) (spanning lines 25–60) and designed specifically for async functions exposed to the FastMCP server. It is applied alongside `@mcp.tool()` on every public tool function declared in [`src/mcp_ambari_api/mcp_main.py`](https://github.com/call518/mcp-ambari-api/blob/main/src/mcp_ambari_api/mcp_main.py), including utilities like `dump_configurations`, `list_hosts`, and `restart_service`.

By centralizing logging in a single decorator, the codebase eliminates repetitive instrumentation and guarantees uniform measurement across all tool invocations.

## How the log_tool Decorator Works

The decorator intercepts tool calls to provide five core observability features:

### Execution Timing with Monotonic Clocks

Before the wrapped coroutine executes, `log_tool` records the start time using `time.monotonic()`. After completion, it calculates the elapsed duration in milliseconds. This approach ensures that system clock adjustments do not skew performance measurements.

### Safe Argument Preview

The decorator inspects keyword arguments and constructs a text preview for logging. To prevent log flooding, it truncates values longer than **120 characters**, ensuring large configuration payloads or cluster dumps do not overwhelm log files while still providing context for debugging.

### Automatic Result Classification

After the wrapped function returns, `log_tool` categorizes the outcome based on the return value:

- **Success**: If the result is a standard string, it logs `TOOL SUCCESS` at the INFO level
- **Error Return**: If the result begins with `"Error:"` or `"[ERROR]"`, it logs `TOOL ERROR_RETURN` at the WARNING level

This distinction allows operators to filter logs for functional errors versus successful executions without parsing stack traces.

### Exception Handling with Tracebacks

If the wrapped coroutine raises an exception, the decorator logs `TOOL EXCEPTION` with the full traceback at the ERROR level, then re-raises the exception to allow the MCP framework to handle the failure appropriately. This ensures no error occurs silently while preserving the original exception context.

### Consistent Log Format

All log entries use standardized prefixes followed by the tool name:
- `TOOL START` – marks invocation with argument preview
- `TOOL SUCCESS` – indicates clean completion with duration and result length
- `TOOL ERROR_RETURN` – flags error-string returns with timing data
- `TOOL EXCEPTION` – captures unhandled exceptions with full tracebacks

## Real-World Usage Examples

### Decorating an MCP Tool

In [`src/mcp_ambari_api/mcp_main.py`](https://github.com/call518/mcp-ambari-api/blob/main/src/mcp_ambari_api/mcp_main.py) (line 39), the `dump_configurations` tool demonstrates standard usage:

```python
@mcp.tool(title="Dump Configurations")
@log_tool
async def dump_configurations(
    config_type: Optional[str] = None,
    service_filter: Optional[str] = None,
    filter: Optional[str] = None,
    summarize: bool = False,
    include_values: bool = True,
    limit: int = 0,
    max_chars: int = 30000,
) -> str:
    """Dumps Ambari configuration data with optional filtering."""
    # Implementation details...

```

### Log Output Scenarios

When a client invokes the `echo` tool with valid input:

```python
@mcp.tool(title="Echo")
@log_tool
async def echo(message: str) -> str:
    return message

```

The decorator produces:

```text
INFO  TOOL START echo message=Hello world
INFO  TOOL SUCCESS echo took=2.3ms len=11

```

If the function returns an error string:

```python
return "Error: something went wrong"

```

The log changes to:

```text
INFO  TOOL START echo message=Bad input
WARN  TOOL ERROR_RETURN echo took=1.7ms len=27

```

When an exception occurs (e.g., network failure during `restart_service`):

```text
INFO  TOOL START restart_service service_name=HDFS
ERROR TOOL EXCEPTION restart_service failed after 45.2ms
Traceback (most recent call last):
  ...
aiohttp.ClientError: Connection refused

```

## Summary

- The **`log_tool` decorator** lives in [`src/mcp_ambari_api/functions.py`](https://github.com/call518/mcp-ambari-api/blob/main/src/mcp_ambari_api/functions.py) and wraps all MCP tools in [`mcp_main.py`](https://github.com/call518/mcp-ambari-api/blob/main/mcp_main.py)
- It uses **`time.monotonic()`** for accurate execution timing in milliseconds
- Arguments are logged with **120-character truncation** to balance visibility and log size
- Return values are classified automatically: strings starting with `"Error:"` trigger WARN logs, otherwise INFO
- **Exceptions** are logged with full tracebacks at ERROR level and re-raised for framework handling
- The decorator ensures **consistent log prefixes** (`TOOL START`, `TOOL SUCCESS`, `TOOL ERROR_RETURN`, `TOOL EXCEPTION`) for easy parsing by monitoring systems

## Frequently Asked Questions

### Where is the log_tool decorator defined?

The **`log_tool` decorator** is defined in [`src/mcp_ambari_api/functions.py`](https://github.com/call518/mcp-ambari-api/blob/main/src/mcp_ambari_api/functions.py) between lines 25 and 60, according to the source code repository. This file contains shared utilities for the MCP server, with `log_tool` serving as the primary observability mechanism for tool functions.

### How does log_tool handle large function arguments?

The decorator automatically truncates argument values to **120 characters** when constructing log previews. This safety measure prevents massive configuration payloads or cluster state dumps from flooding log files while still providing enough context to identify the nature of the request.

### What is the difference between TOOL ERROR_RETURN and TOOL EXCEPTION?

`TOOL ERROR_RETURN` indicates the tool function completed execution but returned a string beginning with `"Error:"` or `"[ERROR]"`, representing a functional error handled by the application logic. `TOOL EXCEPTION` indicates an unhandled exception was raised during execution (such as network timeouts or parsing errors), triggering a full traceback log.

### Can log_tool be used with synchronous functions?

The implementation is designed specifically for **async coroutines** used with FastMCP. While Python decorators can technically wrap sync functions, the `log_tool` implementation uses `async`/`await` patterns to measure timing accurately around suspended operations, making it unsuitable for standard synchronous methods in this codebase.