Skip to content

Conversation

@codefromthecrypt
Copy link

@codefromthecrypt codefromthecrypt commented Apr 25, 2025

This documents the convention of using the request parameter key _meta to hold metadata for use in trace identifiers or correlation IDs.

For example, if using w3c trace-context propagation (commonly used by opentelemetry), and progress flow, the client would encode a tools/call message like this:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "location": "New York"
    },
    "_meta": {
      "progressToken": "abc123",
      "traceparent": "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
    }
  }
}

Motivation and Context

Right now there's an emerging convention of using the request.params._meta field to propagate extra metadata. For example, it is used in the MCP progress flow specification. It is also used to pass trace identifiers between MCP clients to the server, in order to establish causal links needed in observability. The motivation to standardize on _meta is to avoid portability accidents (such as one side using meta and the other _meta).

Longer discussion is here

How Has This Been Tested?

Arize OpenInference released request.params._meta pattern in their instrumentation for the official MCP python and typescript SDKs. This was a collaboration between Arize and Elastic with mammoth effort by @anuraaga recently, including ensuring it can integrate with MCP inspector. Arize did a video about it and have a code example here.

The csharp-sdk has an existing implementation, but using request.params instead of request.params._meta. I backfilled a test and raised a PR to switch to the latter.

Breaking Changes

This is not a breaking change as the specification already allows this parameter value.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Fixes #246

Why document this considering extra params are not forbidden?

Neither JSON-RPC nor Model Context Protocol prohibit extra request parameters. However, documentation is essential to standardize context propagation (e.g., correlation and trace IDs) between client and server. Without it, parties must coordinate ad hoc, which can lead to inconsistent conventions or conflation of metadata with request parameters.

Why choose "_meta" instead of "meta" or "extra"?

The "_meta" field was selected for header-like data based on precedent and feedback:

  • Specified location of the "progressToken" field (string or int) in the MCP progress flow specification.
  • Implemented in the MCP csharp-sdk here .
    • Specifically, this propagates w3c trace-context as fields in request.params._meta
  • Implemented in Arize OpenInference MCP Python and TypeScript SDKs.
    • Specifically, this propagates w3c trace-context as fields in request.params._meta
  • Implemented in Dylibso mcp-otel TypeScript SDK.
    • Specifically, this propagates w3c trace-context as fields in request.params._meta.__traceContext
  • Discussed for other use cases by @ZengyiZhao here and @wdawson here.

Why not solve this only for W3C trace-context?

Focusing solely on W3C trace-context (e.g., "traceparent", "tracestate") restricts flexibility. DistributedContextPropagator supports fields like "Request-Id", "baggage", and "Correlation-Context", while OpenTelemetry includes B3, Jaeger, and more. A generic "_meta" field supports diverse systems, including non-tracing use cases, such as progress flow. Finally, documenting a single field keeps the spec changes to a minimum.

That said, as you can tell from the description above, some agreement should be made as there are at least 2 different incompatible ways in use to propagation trace context.

Why not mix metadata with existing parameters?

Storing metadata in request.params._meta prevents key conflicts and serialization errors. For example, an easy bug could be using OpenTelemetry text map serialization. If applied to the entire parameters object, it would corrupt the arguments like this.

{
  "jsonrpc": "2.0",
  "id": "2",
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": "{\"location\": \"New York\"}",
    "progressToken": "abc123",
    "traceparent": "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
  }
}

Another reason to not co-mingle is to prevent leaking secrets to an LLM. For example, if custom metadata is mixed with regular params, they could end up being sent to an LLM. If stored in _meta, they can be summarily stripped. This redaction is important when custom metadata includes an auth value.

Why not handle this directly in JSON-RPC?

The "meta" field is commonplace in many protocols over JSON-RPC. While not formally included in JSON-RPC 2.0, it is possible to have this discussion for 2.1, as started here.

@samsp-msft
Copy link

I would love the text in the spec to include more of what you have written in the PR description, and that the recommendation is to use traceparent, tracestate and baggage parameters under _meta to propagate OpenTelemetry context information. The format of each of the values should be the same as the http headers in the corresponding w3c specs.

It could be labelled as "Optional: OpenTelemetry context".

I hope that SDKs decide to implement it where they have existing OTel ecosystems that they can leverage. MCP clients are not required to send it, and MCP servers that do not support OTel can safely ignore those parameters.

This may be too much for the index file, but they could be added to the typescript and documentation.

@codefromthecrypt
Copy link
Author

codefromthecrypt commented Apr 25, 2025

@samsp-msft, thanks for your thoughtful feedback! I’ve updated the "Basic" section to keep it neutral and avoid implying MCP defines OpenTelemetry (OTel) specifics:

- Params **MAY** include a `_meta` key with value type `[key: string]: unknown;`. This
  allows propagation of metadata from client to server. For example, OpenTelemetry SDKs
  [default](https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_propagators) to propagate W3C `traceparent`, `tracestate` and `baggage` fields.

Updated Rationale:

This keeps _meta flexible, recommending to use what OTel SDKs are configured. The nuance is me using "default" which helps relay who owns the decision. OTel supports alternatives (like Jaeger) or excluding baggage for privacy/overhead: we could get into a slippery slope saying too much here.

Specifically, we want to make room in otel for what's likely an inevitable OTEP for MCP. I say inevitable because the other discussion includes transport details (e.g. OTLP which is not propagation). This feels a lot like past discussions that led to specification clarifications including env variable propagation and how to handle messaging. In fact, there's already work in Otel for semantic conventions (thanks for participating in that). Ideally an engineer will be able to see the otel details coherently in one place (e.g. an OTEP).

Long story short, deferring OTel keeps MCP focused on protocol mechanics, not telemetry details. By adding an example, we hint of how to hand-over to otel for clarity on telemetry. Does this address your concern?

@codefromthecrypt
Copy link
Author

codefromthecrypt commented Apr 27, 2025

I took time to update the description with feedback in the comments and also relating to discussions by @ZengyiZhao here and @wdawson here. Hope we can land this soon!

@codefromthecrypt
Copy link
Author

@dsp-ant @jspahrsummers So, for anecdotal context. What drove me to the discussion leading to this and the PR itself was your podcast on latent space with @FanaHOVA and @swyxio

Was a knock-out episode, and since working in oss since 2008 this made me feel the best:

I want to caveat it slightly that on the Internet, it's very easy to be part of a discussion and having an opinion without then actually doing the work. And so I think there. We were. Both Jansen and I are very old school. Open source people that like it's it's marriage driven in the sense that if you have done work and if you if you showcase this with like practical examples and work in SDKs towards the ex extensions you want to make, you have a good chance that it gets in. If you're just there to have an opinion, you're very likely just being ignored

So, my goal with this change was to make the absolutely least change possible, with the highest rigor. Even if it is a no, all good. Thanks for inspiring me to give it a go. I love the do the work, then let's talk approach to change.

@samsp-msft
Copy link

@samsp-msft, thanks for your thoughtful feedback! I’ve updated the "Basic" section to keep it neutral and avoid implying MCP defines OpenTelemetry (OTel) specifics:

- Params **MAY** include a `_meta` key with value type `[key: string]: unknown;`. This
  allows propagation of metadata from client to server. For example, OpenTelemetry SDKs
  [default](https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_propagators) to propagate W3C `traceparent`, `tracestate` and `baggage` fields.

LGTM

There is a balance between what needs to go into the base specification - providing the place to pass context - and having agreement amongst the SDK as to how they will use the extensibility mechanisms to implement telemetry propagation. The details of which key values pairs should be used and their values can probably be delegated to docs in the OTel space. It can probably go in the docs for the semantic conventions.

I am hoping that we can get common agreement amongst most MCP SDKs about how to incorporate telemetry so that they can interoperate nicely, and it doesn't need major retrofits later.

@codefromthecrypt
Copy link
Author

@samsp-msft thanks for the support. PS I raised a PR to csharp-sdk to change the carrier from request.params -> request.params._meta (and backfilled a test). I didn't see before that an impl was here, too, so wanted to own responsibility of change implied here, as well.

codefromthecrypt and others added 4 commits April 28, 2025 20:12
This documents the convention of using the request parameter key `_meta` to hold metadata for use in trace identifiers or correlation IDs.

For example, if using [w3c trace-context propagation](https://www.w3.org/TR/trace-context/#relationship-between-the-headers) (commonly used by opentelemetry), the client would encode its trace IDs like this

```
request.params._meta[traceparent]="00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01"
```

This does not constrain metadata values to stringly typed to allow portability with ACP which hast some structured metadata.

A common request may be: why define this? Rationale of the question centered on that the MCP specification already permits extra fields. The main answer is consistency so that other specifications can reliably build on it, and reduce confusion in potential alternate key names.

Fixes modelcontextprotocol#246
Signed-off-by: Adrian Cole <adrian.cole@elastic.co>
Signed-off-by: Adrian Cole <adrian.cole@elastic.co>
Signed-off-by: Adrian Cole <adrian.cole@elastic.co>
@codefromthecrypt
Copy link
Author

Earlier I mis-attributed request.params._meta[progressToken] to BeeAI ACP. It is not BeeAI specific. Rather it is in the specification already. So, we already document use of request.params._meta, just not generally yet.

I've revised the spec change to link to the progress spec, and also shored up the description accordingly.

@codefromthecrypt
Copy link
Author

@dsp-ant do you have any advice for us on how to progress this PR? I'm happy to revise it, but it seems stalled.

@codefromthecrypt
Copy link
Author

codefromthecrypt commented May 12, 2025

Right now, by naming convention (Request and JSONRPCRequest) any *Request type already implies folks should use _meta as described in this PR, just not with the exact guidance. I suggest we update the schema once the docs around it (this PR) is merged.


Specifically, this PR describes some use cases:

  • Params MAY include a _meta key with value type [key: string]: unknown;. This
    allows propagation of metadata from client to server, such as progressToken.
    OpenTelemetry SDKs default to propagate W3C traceparent,tracestate and baggage
    fields inside _meta as well.

Whereas in the schema today, there are 13 occurrences of this advice on _meta

This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.

The recent work by @findleyr and friends on the Go SDK design implies code generation, and there's a small gap you can see if you look carefully here.
https://go.googlesource.com/tools/+/refs/heads/master/internal/mcp/design/design.md#protocol-types

Request and JSONRPCRequest are implied as base types, and document _meta, but AFAICT, they are not strictly defined as base types. This means code generators can make a miss and prevent _meta from flowing through as desired. Specifically, it is easy to not attach the implied base properties on a request, specifically _meta here.

There are a number of ways to address code generation coherency.. we could make a single "meta" type and use that for _meta everywhere (e.g. to consolidate docs). We could add meta to all places already implied by the Request conventions (so that they are honored in code generators). We could also use other techniques to make a strict relationship to Request so that there's less duplication.

Finally, we can decide to not solve it strictly in the schema. Rather, stick with advice here and mention to code generator authors that there's a relationship with anything ending in Request, Response etc that should have special casing.

I don't have a preferred way out, but I would like to help close out this topic. Any thoughts?

@codefromthecrypt
Copy link
Author

added a section to the description that it is possible a future JSON-RPC 2.1 could formalize "_meta". That said, I don't expect this to change any current practice. cc @mpcm

@mpcm
Copy link

mpcm commented May 12, 2025

@codefromthecrypt See recently posted: https://groups.google.com/g/json-rpc/c/pFFuI0JN8Cs
tl;dr> I think it should live as an extension spec outside of json-rpc, lots of dust to settle yet, but the concept clearly is going to be needed with the payloads that want all this context to track perf/perf-spend, history of actions, consequence, model seeds, etc. The 'old http header' answers wont really work well here.

@jonathanhefner
Copy link
Member

In the JSON-RPC group discussion, I mentioned that responses could benefit from a meta property too. That can be discussed and handled separately from this PR, but I wanted to bring up naming consistency. If we standardize request.params._meta, what would be the equivalent for reponse? response.result._meta plus reponse.error._meta? response._meta?

The protocol currently uses request.params._meta for progressToken, but would a top-level request._meta or request.meta be better? I think the biggest argument against it would be backward compatibility, but the SDKs could handle that based on the MCP version.

@anuraaga
Copy link

@jonathanhefner Thanks for the suggestion - personally, if the compatibility issue is acceptable I would suggest top level meta fields request.meta and response.meta. We see the sdks currently have to do some awkward typing to have method-specific param types that also accept the generic meta, with the json schema version of the protocol effectively not modeling this at all. If moving meta, including the current progress token, to top level it could really simplify that.

@bhelx
Copy link

bhelx commented May 14, 2025

We have a similar need. I too would prefer something outside of the params, although it's a good way to experiment for the time being.

@codefromthecrypt
Copy link
Author

going to close this out as it hasn't moved forward in a month. happy to re-open when maintainers are interested in a change. Meanwhile, per the description, there are enough artifacts here and there to suggest request.params._meta is already in use by the spec and so the documentation concern is less due to that.

@bhelx
Copy link

bhelx commented May 27, 2025

FWIW, i was able to abuse the protocol's _meta for this without changing the request schema or the protocol or the clients:

sending: https://github.com/dylibso/mcp-otel/blob/2407c736c92d6a5e71b454845d839f33dabbbfca/src/agent.ts#L122
receiving: https://github.com/dylibso/mcp-otel/blob/2407c736c92d6a5e71b454845d839f33dabbbfca/src/servers/fetch.ts#L27

I think this should be safe if you do your best to avoid name collisions, but going to ask around.

Doing the actual context propagation and naming of everything works, but is a little tedious if you're not familiar with it. This could perhaps be part of an otel adapter for the SDKs, or one day adopted with the SDKs if some things can be agreed upon.

@codefromthecrypt
Copy link
Author

@bhelx thx for the feedback. I will add to the description your use of this pattern which aligns with others mentioned there including Arize Openinference which is an otel SDK. That way folks don't have to scroll through comments should there be a desire to formalize this later.

timvw added a commit to timvw/fastmcp-otel-langfuse that referenced this pull request Oct 14, 2025
…ropagation

Replace HTTP header-based trace propagation with MCP _meta field convention.
This makes the solution transport-agnostic and follows the emerging MCP
standard for metadata propagation.

Changes:
- Update otel_utils.py with _meta field extraction/injection functions
- Replace with_otel_context_from_headers with with_otel_context_from_meta
- Add _meta parameter to server tools (get_weather, get_forecast)
- Update client to inject context into _meta field instead of headers
- Add inject_otel_context_to_meta() and extract_otel_context_from_meta()
- Update README with comprehensive documentation on _meta approach
- Add CHANGELOG.md documenting migration path and breaking changes

Benefits:
- Transport agnostic: works with stdio, HTTP, and SSE transports
- Follows emerging MCP standard (PR #414)
- Compatible with openinference-instrumentation-mcp
- Maintains W3C Trace Context compatibility

BREAKING CHANGE: Server tools now require _meta parameter, and clients
must use inject_otel_context_to_meta() instead of HTTP header injection.
See CHANGELOG.md for migration guide.

References:
- modelcontextprotocol/modelcontextprotocol#414
- https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-mcp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Include OpenTelemetry Trace identifiers as part of the MCP client -> server protocol

6 participants