Skip to content

feat(mcp): tag server spans with client info - #21189

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 5 commits into
mainfrom
max.zhang/mcp-client-tags
Oct 24, 2026
Merged

gh-worker-dd-mergequeue-cf854d[bot] merged 5 commits into
mainfrom
max.zhang/mcp-client-tags

Conversation

@mz1119

@mz1119 mz1119 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Description

Tags MCP server initialize and 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 initialize span, so tool calls could only be tied to a client by joining on mcp_session_id (streamable HTTP only). The server session already stores the handshake's clientInfo, so tool call spans now read it directly. Existing client_name/client_version tags 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 fresh ServerSession with no stored client_params, and 1.x tool calls do not carry clientInfo, so those tool call spans have no mcp_client_* tags. There is no session ID to correlate them with the earlier initialize. The 2026-07-28 protocol (mcp 2.x, follow-up PR) sends clientInfo on 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) and test_client_info_tags_empty_without_client_info (no client info, as on stateless HTTP). llmobs/mcp CI 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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Codeowners resolved as

Resolved from the full PR diff against main using the target branch CODEOWNERS file.
CODEOWNERS team requests not listed below are not required by the current file set.

ddtrace/llmobs/_integrations/mcp.py                                     @DataDog/ml-observability
releasenotes/notes/mcp-server-client-info-70cd76a7405676fe.yaml         @DataDog/apm-python
tests/contrib/mcp/conftest.py                                           @DataDog/ml-observability
tests/contrib/mcp/test_mcp_llmobs.py                                    @DataDog/ml-observability

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Dependency direction analysis

⚠️ Existing dependency direction violations

There are 133 dependency direction violations that already exist on the base branch and have not been changed by this PR.

Show existing violations (showing 5 of 133 highest severity)
ddtrace.internal.tracemethods -×-> ddtrace.trace  (internal-core -> product:tracing, score=119)
ddtrace.llmobs._integrations.llama_index -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=117)
ddtrace.llmobs._integrations.openai -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=117)
ddtrace.internal.openfeature._span_enrichment -×-> ddtrace.trace  (product:openfeature -> product:tracing, score=117)
ddtrace.llmobs._integrations.base -×-> ddtrace.trace  (product:llmobs -> product:tracing, score=117)

To see all violations, download the layers-base.json and layers-pr.json artifacts from this CI job and run:

uv run --script scripts/import-analysis/layers.py compare layers-base.json layers-pr.json

@datadog-prod-us1-5

datadog-prod-us1-5 Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Pipelines  Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 9a55f35 | Docs | View more details | Give us feedback!

@mz1119

mz1119 commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T17:14:41.509486Z d5a31ba New commits
🔒 Security Review ✅ Completed 2026-10-23T17:17:30.100532Z d5a31ba New commits

Security findings

Advisory findings (1)

ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 5ef5aae28a

ℹ️ 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".

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛡️ 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.

Comment thread ddtrace/llmobs/_integrations/mcp.py Resolved
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mz1119
mz1119 marked this pull request as ready for review October 23, 2026 15:23
@mz1119
mz1119 requested review from a team as code owners October 23, 2026 15:23
@mz1119
mz1119 requested review from sabrenner and removed request for a team October 23, 2026 15:23

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment thread ddtrace/llmobs/_integrations/mcp.py Resolved

@datadog-prod-us1-5 datadog-prod-us1-5 Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bits Code Review: PASS

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.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Bits Code Review · Commit ad335eb

mz1119 and others added 2 commits October 23, 2026 11:53
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt info

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@ncybul ncybul left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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>
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot merged commit 384ef6b into main Oct 24, 2026
483 checks passed
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot deleted the max.zhang/mcp-client-tags branch October 24, 2026 16:47
gh-worker-dd-mergequeue-cf854d Bot pushed a commit that referenced this pull request Oct 28, 2026
## 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>
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.

3 participants