Skip to content

Conversation

strawgate
Copy link
Collaborator

Add comprehensive documentation for integrating OpenTelemetry with FastMCP for distributed tracing and observability.

Changes

  • New integration guide at docs/integrations/opentelemetry.mdx
  • Covers logging integration with LoggingHandler
  • Demonstrates span creation via custom middleware
  • Includes production OTLP export configuration
  • Provides complete working example
  • Updated related docs with OpenTelemetry references
  • Registered in docs.json under new Observability section
  • Added working example in examples/opentelemetry_example.py

Closes #1998

Generated with Claude Code

Add comprehensive documentation for integrating OpenTelemetry with FastMCP:
- New integration guide at docs/integrations/opentelemetry.mdx
- Covers logging integration with LoggingHandler
- Demonstrates span creation via custom middleware
- Includes production OTLP export configuration
- Provides complete working example

Also update related docs:
- Add OpenTelemetry reference in middleware.mdx
- Add tip about OpenTelemetry in logging.mdx
- Register doc in docs.json under new Observability section

Example code:
- examples/opentelemetry_example.py with working weather server

Closes #1998

Co-authored-by: William Easton <[email protected]>
@marvin-context-protocol marvin-context-protocol bot added the documentation Updates to docs, examples, or guides. Primary change is documentation-related. label Oct 4, 2025
Copy link
Contributor

@chrisguidry chrisguidry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering why we wouldn't just bake the instrumentation directly into FastMCP and enable it by default? It's not harmful to have the no-op/non-recording spans if you're not exporting them anywhere. There may be a very slight performance effect, but I think it's pretty minimal. Worst case, we could give people an option to disable it if they didn't want OTEL.

Comment on lines +63 to +75
# Set up tracing
trace_provider = TracerProvider(resource=resource)
trace_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(trace_provider)

# Set up logging
logger_provider = LoggerProvider(resource=resource)
logger_provider.add_log_record_processor(BatchLogRecordProcessor(ConsoleLogExporter()))
set_logger_provider(logger_provider)

# Attach OpenTelemetry to FastMCP's logger
fastmcp_logger = get_logger("my_server")
fastmcp_logger.addHandler(LoggingHandler(logger_provider=logger_provider))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this approach would be considered "manual" instrumentation, which is always an option, but wouldn't most folks want something like the "Zero Code" version: https://opentelemetry.io/docs/zero-code/python/

I think we should at least point folks to this as the default, and then we can explain in more detail the manual approach.

I do think there are some additional envvars or CLI switches required to get logging setup with the zero code approach.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this a little more? What we would have to do to give folks the zero code experience?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sure! The OTEL libraries (at least for Python, but I think for most languages) make a pretty strong distinction between producing telemetry (opentelemetry-api) and exporting it (opentelemetry-sdk) and they offer these zero-code approaches where you are essentially wrapping your entrypoint with opentelemetry-instrument (in Python) so that your code doesn't need to set up any exporters. Their canonical example looks like this:

opentelemetry-instrument \
    --traces_exporter console,otlp \
    --metrics_exporter console \
    --service_name your-service-name \
    --exporter_otlp_endpoint 0.0.0.0:4317 \
    python myapp.py

What would happen here is that your myapp.py might be using opentelemetry-api to make spans and metrics, and then when you run the program this way, the spans get exported to whichever ones you've configured there on the CLI. This example is very similar in spirit to what you laid out here, setting up OTLP exporters and attaching them to the providers, it's just handled by opentelemetry-instrument.

So my proposal is just to point people to that link for how they'd run their fastmcp servers by, say, wrapping them like:

opentelemetry-instrument \
    --traces_exporter console,otlp \
    --metrics_exporter console \
    --service_name my-mcp-server \
    --exporter_otlp_endpoint 0.0.0.0:4317 \
    fastmcp run mymcpserver.py


### Basic Tracing Middleware

Create a middleware that emits spans for all MCP requests:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we just ship this middleware with FastMCP? I'd argue it should be on by default, even.

@strawgate
Copy link
Collaborator Author

strawgate commented Oct 12, 2025

@chrisguidry I had Claude generate this, I was mostly going to stick with logging for now

I thought the problem with emitting spans was going to be that we don't have a way to accept or propagate the trace id through MCP calls so the spans would all be uncorrelated

I'd be very happy to do more if we thought it was useful and sensible

@chrisguidry
Copy link
Contributor

we don't have a way to accept or propagate the trace id through MCP calls

Yep, that's a great call, and I think it might need to be pushed lower into the base SDK? I would make an argument that even without trace propagation, there's still some value because you'd see all of the things your MCP server called out to, even if each tool call was a separate trace. Also, as soon as the community figures out client-side trace propagation, it would all "just work" (LOL).

Didn't mean to block your efforts to get great documentation in for sure!

@strawgate
Copy link
Collaborator Author

we don't have a way to accept or propagate the trace id through MCP calls

Yep, that's a great call, and I think it might need to be pushed lower into the base SDK? I would make an argument that even without trace propagation, there's still some value because you'd see all of the things your MCP server called out to, even if each tool call was a separate trace. Also, as soon as the community figures out client-side trace propagation, it would all "just work" (LOL).

Didn't mean to block your efforts to get great documentation in for sure!

I'm ok adding it to our client and server as experimental until the lower level sdk gets it together, would that be interesting for you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Updates to docs, examples, or guides. Primary change is documentation-related.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document logging with OpenTelemetry

2 participants