Description
status: proposed
contact: "@Serjbory"
date: 2026-04-08
MCP _meta support for tool calls
What is the goal of this feature?
Enable users to pass arbitrary request metadata (_meta) to MCP servers when tools are invoked during agent execution. This supports distributed tracing correlation, user context propagation (locale, session info), and any server-specific request metadata the MCP protocol allows.
We know we're successful when users can attach per-request context (correlation IDs, locale, session info) to MCP tool calls without custom middleware or workarounds, and the metadata arrives in the MCP server's _meta field as defined by the protocol spec.
What is the problem being solved?
Today, MCPTool.call_tool() only injects OpenTelemetry trace context into the MCP _meta field. Users have no way to include additional metadata such as:
- Request correlation IDs for debugging and distributed tracing across services
- User context (locale, session info) for MCP servers that need request-specific behavior
- Custom authorization context that MCP servers read from
_meta rather than headers
The helper _inject_otel_into_mcp_meta() already accepts an initial meta dict parameter, but call_tool() always passes None. The plumbing exists — it's just not wired up.
Users who need this today must either fork the framework or implement fragile workarounds via function middleware that monkey-patch internal state. Both OpenAI Agents SDK and LangChain MCP Adapters have recently added _meta support, making this an expected capability.
API Changes
No new public types, parameters, or methods. The change is entirely internal to MCPTool.call_tool():
- Extract
_meta from the incoming **kwargs (via kwargs.pop("_meta", None))
- Add
"_meta" to the existing filtered-kwargs exclusion set (safety net so it never reaches tool arguments)
- Pass the extracted dict to
_inject_otel_into_mcp_meta(user_meta) instead of _inject_otel_into_mcp_meta() — user keys take precedence, OTel keys fill in non-conflicting slots
The user-facing API is the existing function_invocation_kwargs parameter on agent.run(), which is the established mechanism for per-request context injection (used in 6+ existing samples for API keys, user IDs, session metadata, etc.).
E2E Code Samples
Basic: Pass correlation ID and locale
from agent_framework import Agent, MCPStreamableHTTPTool
from agent_framework.openai import OpenAIChatClient
agent = Agent(
client=OpenAIChatClient(),
name="assistant",
tools=MCPStreamableHTTPTool(
name="github",
url="https://api.example.com/mcp",
),
)
# _meta is passed via the standard function_invocation_kwargs mechanism
response = await agent.run(
"Search for Python repos",
function_invocation_kwargs={
"_meta": {
"correlation_id": "req-456",
"session_id": "abc-123",
"locale": "fr-FR",
},
},
)
On the wire, the MCP call_tool request will include:
{
"method": "tools/call",
"params": {
"name": "search",
"arguments": {"query": "Python repos"},
"_meta": {
"correlation_id": "req-456",
"session_id": "abc-123",
"locale": "fr-FR",
"traceparent": "00-abc123-def456-01"
}
}
}
With middleware: Inject _meta from request context
from agent_framework import Agent, FunctionMiddleware, FunctionInvocationContext
class InjectMetaMiddleware(FunctionMiddleware):
"""Middleware that adds _meta to every MCP tool call."""
async def process(self, context: FunctionInvocationContext, call_next):
context.kwargs["_meta"] = {
"user_id": context.kwargs.get("user_id"),
"tenant": "acme-corp",
}
await call_next()
agent = Agent(
client=client,
tools=mcp_tool,
middleware=[InjectMetaMiddleware()],
)
response = await agent.run(
"List files",
function_invocation_kwargs={"user_id": "user-42"},
)
Combined with header_provider (MCPStreamableHTTPTool)
mcp_tool = MCPStreamableHTTPTool(
name="secure-api",
url="https://api.example.com/mcp",
# header_provider injects HTTP headers (transport level)
header_provider=lambda kwargs: {"Authorization": f"Bearer {kwargs['token']}"},
)
# _meta injects MCP protocol metadata (application level)
# Both work together — headers for auth, _meta for request context
response = await agent.run(
"Fetch data",
function_invocation_kwargs={
"token": "sk-xxx",
"_meta": {"request_id": "req-789", "locale": "en-US"},
},
)
Behavioral details
| Scenario |
Result |
_meta provided, OTel active |
Both user keys and OTel keys in meta; user keys win on conflicts |
_meta provided, OTel inactive |
Only user keys in meta |
No _meta provided, OTel active |
OTel-only meta (existing behavior) |
No _meta provided, OTel inactive |
meta=None (existing behavior) |
_meta in kwargs |
Never sent as tool argument — extracted before filtering |
Files to modify
| File |
Change |
python/packages/core/agent_framework/_mcp.py |
call_tool(): extract _meta from kwargs, add to filter set, pass to _inject_otel_into_mcp_meta() |
python/packages/core/tests/core/test_mcp.py |
Tests: meta propagation, OTel merge, precedence, backward compat, exclusion from arguments |
Test plan
_meta propagated — pass _meta in kwargs, verify session.call_tool() receives it in meta=
- Merge with OTel — pass
_meta with OTel active, verify both user and OTel keys present
- User wins conflicts — pass
_meta with key matching OTel key, verify user value wins
- No
_meta — call without _meta, verify existing OTel-only behavior unchanged
_meta excluded from arguments — pass _meta, verify it's not in arguments dict sent to server
MCPStreamableHTTPTool override — pass _meta through HTTP subclass, verify it reaches super().call_tool()
References
Code Sample
Language/SDK
Python
Description
status: proposed
contact: "@Serjbory"
date: 2026-04-08
MCP
_metasupport for tool callsWhat is the goal of this feature?
Enable users to pass arbitrary request metadata (
_meta) to MCP servers when tools are invoked during agent execution. This supports distributed tracing correlation, user context propagation (locale, session info), and any server-specific request metadata the MCP protocol allows.We know we're successful when users can attach per-request context (correlation IDs, locale, session info) to MCP tool calls without custom middleware or workarounds, and the metadata arrives in the MCP server's
_metafield as defined by the protocol spec.What is the problem being solved?
Today,
MCPTool.call_tool()only injects OpenTelemetry trace context into the MCP_metafield. Users have no way to include additional metadata such as:_metarather than headersThe helper
_inject_otel_into_mcp_meta()already accepts an initialmetadict parameter, butcall_tool()always passesNone. The plumbing exists — it's just not wired up.Users who need this today must either fork the framework or implement fragile workarounds via function middleware that monkey-patch internal state. Both OpenAI Agents SDK and LangChain MCP Adapters have recently added
_metasupport, making this an expected capability.API Changes
No new public types, parameters, or methods. The change is entirely internal to
MCPTool.call_tool():_metafrom the incoming**kwargs(viakwargs.pop("_meta", None))"_meta"to the existing filtered-kwargs exclusion set (safety net so it never reaches tool arguments)_inject_otel_into_mcp_meta(user_meta)instead of_inject_otel_into_mcp_meta()— user keys take precedence, OTel keys fill in non-conflicting slotsThe user-facing API is the existing
function_invocation_kwargsparameter onagent.run(), which is the established mechanism for per-request context injection (used in 6+ existing samples for API keys, user IDs, session metadata, etc.).E2E Code Samples
Basic: Pass correlation ID and locale
On the wire, the MCP
call_toolrequest will include:{ "method": "tools/call", "params": { "name": "search", "arguments": {"query": "Python repos"}, "_meta": { "correlation_id": "req-456", "session_id": "abc-123", "locale": "fr-FR", "traceparent": "00-abc123-def456-01" } } }With middleware: Inject _meta from request context
Combined with header_provider (MCPStreamableHTTPTool)
Behavioral details
_metaprovided, OTel activemeta; user keys win on conflicts_metaprovided, OTel inactivemeta_metaprovided, OTel activemeta(existing behavior)_metaprovided, OTel inactivemeta=None(existing behavior)_metain kwargsFiles to modify
python/packages/core/agent_framework/_mcp.pycall_tool(): extract_metafrom kwargs, add to filter set, pass to_inject_otel_into_mcp_meta()python/packages/core/tests/core/test_mcp.pyTest plan
_metapropagated — pass_metain kwargs, verifysession.call_tool()receives it inmeta=_metawith OTel active, verify both user and OTel keys present_metawith key matching OTel key, verify user value wins_meta— call without_meta, verify existing OTel-only behavior unchanged_metaexcluded from arguments — pass_meta, verify it's not inargumentsdict sent to serverMCPStreamableHTTPTooloverride — pass_metathrough HTTP subclass, verify it reachessuper().call_tool()References
_metaparameter (PR #468)Code Sample
Language/SDK
Python