FastMCP
Instrument your FastMCP (Model Context Protocol) server with OpenTelemetry to send traces, metrics, and logs to Last9
Use OpenTelemetry to instrument your FastMCP server and send telemetry data to Last9. FastMCP includes built-in OpenTelemetry tracing for MCP operations (tool calls, resource reads, prompts), and auto-instrumentation adds spans for underlying libraries like httpx.
You can either run OpenTelemetry Collector as a separate service or send telemetry directly from your application to Last9.
Prerequisites
Before setting up FastMCP monitoring, ensure you have:
- Python 3.10 or higher installed
- FastMCP application running
- Last9 account with integration credentials
pippackage manager available
-
Install OpenTelemetry Packages
Install the required OpenTelemetry packages alongside FastMCP:
pip install fastmcp opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp opentelemetry-distroYou can also add these packages to your
requirements.txtfile and install them using:pip install -r requirements.txt -
Set Environment Variables
Configure OpenTelemetry environment variables for your FastMCP server. Replace the placeholder values with your actual Last9 credentials:
export OTEL_SERVICE_NAME=<service_name>export OTEL_EXPORTER_OTLP_ENDPOINT={{ .Logs.WriteURL }}export OTEL_EXPORTER_OTLP_HEADERS="Authorization={{ .Logs.AuthValue }}"export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"export OTEL_TRACES_EXPORTER=otlpexport OTEL_METRICS_EXPORTER=otlpexport OTEL_LOGS_EXPORTER=otlpexport OTEL_RESOURCE_ATTRIBUTES="service.name=<service_name>,service.version=1.0.0,deployment.environment=local"export OTEL_TRACES_SAMPLER="always_on"export OTEL_LOG_LEVEL=errorEnvironment Variables Explained:
OTEL_SERVICE_NAME: Name of your FastMCP serviceOTEL_EXPORTER_OTLP_ENDPOINT: Last9 endpoint URL for telemetry dataOTEL_EXPORTER_OTLP_HEADERS: Authentication header for Last9OTEL_METRICS_EXPORTER: Set tootlpto export runtime and library metricsOTEL_LOGS_EXPORTER: Set tootlpto export logs with trace correlationOTEL_RESOURCE_ATTRIBUTES: Service metadata including name, version, and environment
-
Bootstrap Automatic Instrumentation
FastMCP supports automatic instrumentation with OpenTelemetry. Run the following command to detect and install the required instrumentation packages for your application:
opentelemetry-bootstrap -a requirementsThis command analyzes your dependencies and outputs the OpenTelemetry instrumentation packages you need. For example:
- If you use
httpx, it will suggestopentelemetry-instrumentation-httpx - If you use
requests, it will suggestopentelemetry-instrumentation-requests - If you use
sqlalchemy, it will suggestopentelemetry-instrumentation-sqlalchemy
- If you use
-
Install Instrumentation Packages
Install all the recommended instrumentation packages automatically:
opentelemetry-bootstrap -a installThis command installs all the necessary instrumentation packages based on your application’s dependencies.
Recommendation: Last9 recommends using automatic instrumentation for most applications as it provides comprehensive coverage with minimal configuration.
-
Run Your FastMCP Server
Start your FastMCP server with OpenTelemetry instrumentation:
opentelemetry-instrument fastmcp run server.pyThis command starts your FastMCP server with automatic OpenTelemetry instrumentation enabled. The instrumentation will:
- Collect traces for every MCP tool call, resource read, and prompt render
- Monitor outbound HTTP requests made inside tool handlers
- Track database queries and external API calls from tool logic
- Send all telemetry data to Last9
What Gets Instrumented
When you use automatic instrumentation with FastMCP, OpenTelemetry automatically captures:
MCP Operations (FastMCP Built-in)
FastMCP creates spans for every MCP operation with no code changes:
- Tool Calls:
tools/call {name}— every tool invocation with arguments and results - Resource Reads:
resources/read {uri}— every resource access - Prompt Renders:
prompts/get {name}— every prompt template render
Each span includes attributes like rpc.system=mcp, rpc.method, session ID, server name, and component type.
HTTP Requests (Auto-instrumented)
- Outbound
httpxandrequestscalls from tool handlers - Request method, URL, status code, and timing
- Error details for failed requests
Database Operations
- SQL queries and execution times (if using
sqlalchemy,psycopg2, etc.) - Database connection details and query parameters
Metrics
With OTEL_METRICS_EXPORTER=otlp, the auto-instrumentation collects runtime and library metrics automatically:
- httpx — request duration histograms, active connections
- Python runtime — GC counts, memory usage, CPU time
- Process — open file descriptors, thread count
These metrics give you request rate, error rate, and latency (RED metrics) out of the box in Last9.
Logs (Trace-Log Correlation)
With OTEL_LOGS_EXPORTER=otlp, opentelemetry-instrument patches Python’s logging module so that every log record automatically includes trace_id, span_id, and trace_flags. This enables:
- Logs exported to Last9 via OTLP alongside traces
- Click a trace in Last9 Grafana to see its correlated log lines
- No manual trace context injection needed in your code
import logginglogger = logging.getLogger("my-server")
@mcp.tool()async def add_note(title: str, content: str) -> str: logger.info("Note '%s' added", title) # trace_id auto-injectedAdvanced Configuration
Error Recording on Spans
MCP tools return errors as strings, so spans look healthy by default even when the operation failed. Use span.record_exception() and span.set_status(StatusCode.ERROR) to make errors visible in the trace waterfall:
from opentelemetry.trace import StatusCodefrom fastmcp.telemetry import get_tracer
@mcp.tool()async def fetch_url(url: str) -> str: tracer = get_tracer() with tracer.start_as_current_span("fetch_url_request") as span: try: async with httpx.AsyncClient() as client: resp = await client.get(url, timeout=10) resp.raise_for_status() return resp.text except httpx.HTTPStatusError as exc: span.record_exception(exc) span.set_status(StatusCode.ERROR, f"HTTP {exc.response.status_code}") return f"Error: HTTP {exc.response.status_code}"This makes failed tool calls show up as red error spans in Last9, filterable via StatusCode=ERROR.
Custom Spans in Tool Handlers
Use fastmcp.telemetry.get_tracer() to add custom spans inside your tools:
from fastmcp import FastMCPfrom fastmcp.telemetry import get_tracer
mcp = FastMCP("my-server")
@mcp.tool()async def process_data(input: str) -> str: tracer = get_tracer() with tracer.start_as_current_span("parse_input") as span: span.set_attribute("input.length", len(input)) parsed = parse(input)
with tracer.start_as_current_span("transform_data") as span: span.set_attribute("data.count", len(parsed)) result = transform(parsed)
return resultCustom spans are automatically nested under the parent tools/call span.
Custom Service Metadata
Enhance your service metadata by updating the OTEL_RESOURCE_ATTRIBUTES environment variable:
export OTEL_RESOURCE_ATTRIBUTES="service.name=mcp-server,service.version=2.1.0,deployment.environment=production,service.instance.id=mcp-1,team=backend"Sampling Configuration
Control trace sampling to manage data volume:
# Always sample (development)export OTEL_TRACES_SAMPLER="always_on"
# Sample 10% of traces (production)export OTEL_TRACES_SAMPLER="traceidratio"export OTEL_TRACES_SAMPLER_ARG="0.1"Verification
-
Check Application Startup
When you start your FastMCP server, you should see OpenTelemetry initialization messages in the console output.
-
Generate Test Traffic
Use an MCP client or the FastMCP dev tools to invoke your tools:
# Using fastmcp dev modefastmcp dev server.pyOr use any MCP-compatible client (Claude Desktop, Cursor, etc.) to call your tools.
-
Verify Data in Last9
Log into your Last9 account and check that traces are being received in Grafana.
Look for:
tools/callspans for each tool invocationresources/readspans for resource access- Child spans from
httpxor database calls within tools - Error spans (red) when validation fails or HTTP requests error out
- Correlated log lines attached to each trace
- Runtime and httpx metrics in the metrics explorer
Troubleshooting
Common Issues
Missing Instrumentation Packages If some operations aren’t being traced, run the bootstrap command again:
opentelemetry-bootstrap -a requirementsopentelemetry-bootstrap -a installEnvironment Variables Not Set Verify your environment variables are properly configured:
env | grep OTEL_Connection Issues Check if your application can reach Last9:
curl -v -H "Authorization: <your-auth-header>" <your-endpoint>Performance Considerations
- Use sampling in production to control data volume
- Monitor the overhead of instrumentation on your application
- Consider using the OpenTelemetry Collector as a buffer for high-traffic applications
Best Practices
- Set meaningful service names that reflect your architecture
- Use consistent environment naming across services
- Include version information in resource attributes
- Add custom spans for critical business operations within tool handlers
- Use
get_tracer()fromfastmcp.telemetryinstead of creating your own tracer
Supported Libraries
OpenTelemetry provides automatic instrumentation for many popular Python libraries commonly used with FastMCP:
- HTTP Clients:
httpx,requests,aiohttp - Databases:
sqlalchemy,psycopg2,pymongo,redis - Message Queues:
celery,pika(RabbitMQ) - Cloud Services:
boto3(AWS),google-cloud
For a complete list of supported libraries, visit the OpenTelemetry Python Contrib documentation.
Need Help?
If you encounter any issues or have questions:
- Join our Discord community for real-time support
- Contact our support team at support@last9.io