Repository navigation
feat(mcp): tag server spans with client info - #21189
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codeowners resolved asResolved from the full PR diff against |
Dependency direction analysis
|
🎉 All green!🧪 All tests passed 🔗 Commit SHA: 9a55f35 | Docs | View more details | Give us feedback! |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
Security findingsAdvisory findings (1)
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
🛡️ Codex Security Review · Automatically triggered
Here are some automated security review suggestions for this pull request.
Reviewed commit: 5ef5aae28a
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ad335eb0ec
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
More details
Runtime compatibility remains unverified because the MCP SDK is not installed. The completed static review found no reportable defect in the client-info lookup or tag emission.
🤖 Bits Code Review · Commit ad335eb
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt info Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ncybul
left a comment
There was a problem hiding this comment.
Would be nice to have an example trace to show this working E2E, but otherwise LGTM!
…ity spans Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
## Description Adds support for `mcp` 2.x. mcp 2.0 removed `mcp.shared.session` (`BaseSession`, `RequestResponder`), so `patch()` hit an `ImportError` and silently skipped instrumentation. mcp 2.x is also the only Python SDK that speaks the stateless 2026-07-28 protocol (no `initialize` handshake, no `Mcp-Session-Id`). - Server: a Datadog middleware is added to `Server.middleware` when a `ServerRunner` is built. Every transport and protocol version builds one per connection and runs each inbound message through the middleware, so servers constructed before `patch()` are traced too. It produces the same spans, tags, distributed tracing and intent capture as the 1.x `RequestResponder` patches, for both handshake and stateless clients. Client info comes from the session's `client_params`, which mcp 2.x fills from either the handshake or the request envelope. Reserved `io.modelcontextprotocol/*` `_meta` keys are left out of the recorded input. - Client: existing `ClientSession` wrappers handle snake_case fields (`is_error`, `server_info`), `ClientSession.adopt()` (used by every stateless negotiation path) tags the session with server info so stateless clients still get `mcp_server_name`, and trace context is injected into mcp 2.x request models. - Shared server tagging between 1.x and 2.x. ## Testing New `test_mcp_v2.py` covers tool calls, tool errors and intent capture in both `legacy` (handshake) and `auto` (stateless) client modes. `Client(server)` runs the server in the client's context, which hides broken `_meta` propagation, so distributed tracing (enabled and disabled) and servers constructed before `patch()` are tested over separate memory streams. v1 test files are skipped on 2.x and vice versa. `test_mcp_patch.py` asserts the right wrapped functions per major. Suitespec adds an `mcp<2` variant so the latest 1.x stays tested while the unpinned `mcp` variant now resolves to 2.x. - [Before trace](https://app.datadoghq.com/api/v2/switch_to_user/a1ff33a1-3eb2-11f0-977a-966bfc9ad571?next=%2Fllm%2Ftraces%3Fend%3D1792785357085%26paused%3Dtrue%26query%3Dtrace_id%253A6adbbaa10000000052cb4d9cd1de767f%26start%3D1792784757040&flow=org_switch) - [After trace](https://app.datadoghq.com/api/v2/switch_to_user/a1ff33a1-3eb2-11f0-977a-966bfc9ad571?next=%2Fllm%2Ftraces%3Fend%3D1792785358714%26paused%3Dtrue%26query%3Dtrace_id%253A6adbbaa2000000009c15923d0e15c1dc%26start%3D1792784758672&flow=org_switch) ## Risks mcp 2.x marks `Server.middleware` as provisional. The middleware reads its context defensively and falls back to the plain handler, so an incompatible SDK change drops MCP server spans instead of failing requests. ## Additional Notes Stacked on #21189. Includes test requirement locks for the `mcp<2` (1.30.0) and unpinned `mcp` (2.3.0) environments, plus the regenerated integration registry. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: max.zhang <max.zhang@datadoghq.com>
Description
Tags MCP server
initializeand tool call spans with the calling client's self-reported identity:mcp_client_name,mcp_client_title,mcp_client_version.Previously client info was only on the server
initializespan, so tool calls could only be tied to a client by joining onmcp_session_id(streamable HTTP only). The server session already stores the handshake'sclientInfo, so tool call spans now read it directly. Existingclient_name/client_versiontags are unchanged.Values are capped at 256 characters since they are client-supplied and repeated on every tool call span.
Known limitation: on mcp 1.x servers using stateless streamable HTTP (
stateless_http=True), each request gets a freshServerSessionwith no storedclient_params, and 1.x tool calls do not carryclientInfo, so those tool call spans have nomcp_client_*tags. There is no session ID to correlate them with the earlierinitialize. The 2026-07-28 protocol (mcp 2.x, follow-up PR) sendsclientInfoon every request, which closes this gap.Testing
Updated expected tags in existing LLMObs tests. Added
test_server_tool_call_tagged_with_client_info(custom client info on a tool call),test_client_info_tags_are_truncated(256-character cap) andtest_client_info_tags_empty_without_client_info(no client info, as on stateless HTTP).llmobs/mcpCI passed on mcp 1.10 and latest 1.x.Risks
None. Additive tags only.
Additional Notes
Follow-up PR adds mcp 2.x support.
🤖 Generated with Claude Code