This package contains lightweight utilities for wiring OpenTelemetry tracing into FastMCP servers. It is designed around the Model Context Protocol's _meta propagation field, allowing client applications to forward tracing information via the traceparent header to the server.
- Extract OpenTelemetry context from
_metaobjects using the MCP basic protocol specification. - Start server spans for each tool invocation handled by FastMCP.
- Attach useful span attributes (tool name, call ID, namespace) to provide richer trace data.
- Convenience helper for registering the middleware with FastMCP applications while remaining compatible with multiple FastMCP releases.
uv add https://github.com/codeactual/fastmcp_otel_middleware.gitfrom fastmcp import FastMCP
from fastmcp_otel_middleware import instrument_fastmcp
app = FastMCP("MyServer")
# Basic usage: attach the middleware with default configuration
instrument_fastmcp(app)
# Optionally customize span naming and other behavior
instrument_fastmcp(
app,
span_name_prefix="tool.", # Creates spans like "tool.get_temperature"
include_arguments=True, # Include tool arguments in span attributes
langfuse_compatible=True, # Enable Langfuse-prefixed metadata (disabled by default)
)When a client invokes a tool and includes tracing headers inside the _meta
object, the middleware extracts the headers, continues the trace, and wraps the
handler invocation with a server span. The span automatically includes:
- Tool name:
fastmcp.tool.nameattribute fromcontext.message.name - MCP metadata:
mcp.methodandmcp.sourceattributes - Success status:
fastmcp.tool.successindicating if the call succeeded - Arguments (optional):
fastmcp.tool.argumentsifinclude_arguments=True
Enable the langfuse_compatible option when you need Langfuse-prefixed metadata
fields (for example, to make attributes queryable in the Langfuse UI). The
middleware leaves these attributes out by default to keep traces lean for other
exporters.
The get_context_from_meta helper can be used independently of the middleware
for cases where you need to manually work with the propagated context:
from fastmcp_otel_middleware import get_context_from_meta
meta = {
"otel": {
"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}
}
ctx = get_context_from_meta(meta)- FastMCP 2.13.1+
- Python 3.12+: Required for proper type annotation support
- OpenTelemetry API: For tracing functionality
This middleware uses FastMCP's hook-based middleware pattern (introduced in v2.9) to reliably access tool names and MCP protocol information. The on_call_tool hook receives a MiddlewareContext object that contains:
context.message.name: The tool name being invokedcontext.message.arguments: The tool argumentscontext.message._meta: Metadata sent by the client (including OTel headers)context.method: The MCP method (e.g., "tools/call")context.source: Source of the request ("client" or "server")
This approach ensures that tool names are always correctly captured in traces, unlike older callable-style middleware that relied on kwargs.