Skip to content

feat(@modelcontextprotocol/sdk): add tracing for MCP server requests and client operations#8916

Merged
BridgeAR merged 27 commits into
masterfrom
apm-integration-toolkit/modelcontextprotocol-sdk/server-tracing
Jul 14, 2026
Merged

feat(@modelcontextprotocol/sdk): add tracing for MCP server requests and client operations#8916
BridgeAR merged 27 commits into
masterfrom
apm-integration-toolkit/modelcontextprotocol-sdk/server-tracing

Conversation

@wconti27

@wconti27 wconti27 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds APM tracing for @modelcontextprotocol/sdk server requests and client operations.

Server-side:

  • mcp.request spans for handled MCP requests, parented under inbound transport spans when available.
  • Python-parity resource names for common MCP operations, including server_tool_call for tools/call.
  • mcp.server.tool.call spans for McpServer tool handler execution.
  • Distributed trace context injection from MCP client requests to out-of-process MCP servers.
  • Finish handling for normal completion, validation/method-not-found paths, handler errors, and transport-close cleanup.

Client-side:

  • mcp.request / client_tool_call for Client.callTool().
  • mcp.request / ClientSession.list_tools for Client.listTools().
  • JS SDK coverage for mcp.resources.list, mcp.resource.read, mcp.prompts.list, and mcp.prompt.get.

Tagging:

  • Keeps APM tags conservative and avoids high-cardinality MCP payload metadata such as tool names, argument keys/counts, resource URIs, prompt names/descriptions, and response content shapes.
  • Keeps error metadata for thrown errors and MCP isError tool-call results.

Motivation

MCP clients and servers are protocol infrastructure used by AI applications. These spans make MCP tool/resource/prompt activity visible in APM and preserve trace relationships across client/server boundaries while keeping common span shapes aligned with the Python tracer where the SDKs overlap.

Additional Notes

Local verification:

  • env -u OTEL_TRACES_EXPORTER -u OTEL_LOGS_EXPORTER -u OTEL_METRICS_EXPORTER PLUGINS=modelcontextprotocol-sdk npm run test:plugins:ci - passed, 51 passing.
  • ./node_modules/.bin/eslint packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js packages/datadog-plugin-modelcontextprotocol-sdk/src/utils.js packages/datadog-plugin-modelcontextprotocol-sdk/test/index.spec.js packages/datadog-plugin-modelcontextprotocol-sdk/test/integration-test/client.spec.js --max-warnings 0 - passed.
  • git diff --check - passed.

…._onrequest and McpServer.executeToolHandler

- Add orchestrion rewriter entries for Protocol._onrequest (Sync) and McpServer.executeToolHandler (Async)
  in both CJS (dist/cjs) and ESM (dist/esm) paths
- Add McpServerRequestPlugin: creates mcp.server.request span for every incoming JSON-RPC request,
  extracting trace context from HTTP transport headers when available
- Add McpServerToolCallPlugin: creates mcp.server.tool.call child span for tool executions,
  marking isError results as errors
- Fix span lifecycle: use end() (not finish()) for Sync orchestrion hooks, which emit start/end
  events rather than start/finish
- Add server-side test assertions for both new span types across two SDK versions

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@wconti27
wconti27 requested a review from a team as a code owner June 15, 2026 18:45
@wconti27
wconti27 requested review from tlhunter and removed request for a team June 15, 2026 18:45
@dd-octo-sts

dd-octo-sts Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Overall package size

Self size: 6.72 MB
Deduped: 7.38 MB
No deduping: 7.38 MB

Dependency sizes | name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.3.1 | 122.62 kB | 438.86 kB | | opentracing | 0.14.7 | 194.81 kB | 194.81 kB | | dc-polyfill | 0.1.11 | 25.74 kB | 25.74 kB |

🤖 This report was automatically generated by heaviest-objects-in-the-universe

@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.23400% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.58%. Comparing base (4398f17) to head (c469830).
⚠️ Report is 12 commits behind head on master.

Files with missing lines Patch % Lines
...g-instrumentations/src/modelcontextprotocol-sdk.js 97.17% 7 Missing ⚠️
...dog-plugin-modelcontextprotocol-sdk/src/tracing.js 99.30% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8916      +/-   ##
==========================================
+ Coverage   96.57%   96.58%   +0.01%     
==========================================
  Files         918      919       +1     
  Lines      121521   121955     +434     
  Branches    21089    21035      -54     
==========================================
+ Hits       117355   117796     +441     
+ Misses       4166     4159       -7     
Flag Coverage Δ
aiguard 53.53% <100.00%> (-0.11%) ⬇️
aiguard-integration 56.99% <100.00%> (-0.05%) ⬇️
apm-bucket-0 53.76% <100.00%> (-0.11%) ⬇️
apm-bucket-1 58.05% <100.00%> (-0.11%) ⬇️
apm-bucket-2 56.08% <100.00%> (-0.10%) ⬇️
apm-capabilities-tracing 60.33% <60.48%> (-0.01%) ⬇️
apm-integrations-aerospike 52.90% <100.00%> (-0.11%) ⬇️
apm-integrations-confluentinc-kafka-javascript 57.03% <100.00%> (-0.11%) ⬇️
apm-integrations-couchbase 53.24% <100.00%> (-0.11%) ⬇️
apm-integrations-http 57.94% <100.00%> (-0.10%) ⬇️
apm-integrations-kafkajs 57.68% <100.00%> (-0.13%) ⬇️
apm-integrations-next 54.21% <100.00%> (-0.11%) ⬇️
apm-integrations-prisma 54.14% <100.00%> (-0.10%) ⬇️
apm-integrations-tedious 53.01% <100.00%> (-0.11%) ⬇️
appsec 68.95% <100.00%> (-0.11%) ⬇️
appsec-express_fastify_graphql 65.59% <100.00%> (-0.08%) ⬇️
appsec-integration 45.49% <30.46%> (-0.12%) ⬇️
appsec-kafka_ldapjs_lodash 59.07% <100.00%> (-0.10%) ⬇️
appsec-mongodb-core_mongoose_mysql 62.23% <100.00%> (-0.09%) ⬇️
appsec-next 52.82% <100.00%> (-0.09%) ⬇️
appsec-node-serialize_passport_postgres 61.86% <100.00%> (-0.09%) ⬇️
appsec-sourcing_stripe_template 60.32% <100.00%> (+0.01%) ⬆️
debugger 65.71% <100.00%> (-0.08%) ⬇️
instrumentations-bucket-0 48.72% <100.00%> (-0.11%) ⬇️
instrumentations-bucket-1 54.93% <100.00%> (-0.10%) ⬇️
instrumentations-bucket-10 56.60% <100.00%> (-0.09%) ⬇️
instrumentations-bucket-11 48.71% <100.00%> (-0.11%) ⬇️
instrumentations-bucket-12 48.81% <100.00%> (-0.11%) ⬇️
instrumentations-bucket-13 48.64% <100.00%> (-0.11%) ⬇️
instrumentations-bucket-2 49.71% <100.00%> (-0.10%) ⬇️
instrumentations-bucket-3 53.81% <100.00%> (-0.10%) ⬇️
instrumentations-bucket-4 49.13% <100.00%> (-0.11%) ⬇️
instrumentations-bucket-5 52.99% <100.00%> (-0.09%) ⬇️
instrumentations-bucket-6 55.57% <100.00%> (-0.11%) ⬇️
instrumentations-bucket-7 53.58% <100.00%> (-0.10%) ⬇️
instrumentations-bucket-8 54.61% <100.00%> (-0.10%) ⬇️
instrumentations-bucket-9 56.08% <100.00%> (-0.09%) ⬇️
instrumentations-instrumentation-couchbase 48.14% <100.00%> (-0.11%) ⬇️
instrumentations-integration-esbuild 33.87% <30.46%> (-0.10%) ⬇️
llmobs-ai_anthropic_bedrock 57.71% <100.00%> (-0.10%) ⬇️
llmobs-bucket-1 56.78% <100.00%> (-0.27%) ⬇️
llmobs-openai 57.36% <46.57%> (-0.27%) ⬇️
llmobs-sdk 59.92% <100.00%> (-0.11%) ⬇️
llmobs-vertex-ai 54.18% <100.00%> (-0.11%) ⬇️
master-coverage 96.58% <98.23%> (?)
openfeature 54.60% <100.00%> (-0.05%) ⬇️
openfeature-unit 49.80% <100.00%> (-0.11%) ⬇️
platform-core_esbuild_instrumentations-misc 38.37% <30.46%> (-0.11%) ⬇️
platform-integration 62.22% <100.00%> (+<0.01%) ⬆️
platform-shimmer_unit-guardrails_webpack 37.20% <30.46%> (-0.07%) ⬇️
plugins-bucket-0 53.15% <100.00%> (-0.10%) ⬇️
plugins-bucket-1 55.06% <100.00%> (-0.06%) ⬇️
plugins-bucket-11 57.44% <98.23%> (+1.80%) ⬆️
plugins-bucket-18 57.05% <100.00%> (-0.09%) ⬇️
plugins-bucket-19 55.33% <100.00%> (-0.13%) ⬇️
plugins-bucket-20 57.42% <100.00%> (-0.11%) ⬇️
plugins-bucket-4 53.80% <100.00%> (-0.13%) ⬇️
plugins-bullmq_cassandra_cookie 57.36% <100.00%> (-0.11%) ⬇️
plugins-cookie-parser_crypto_dd-trace-api 52.29% <100.00%> (-0.11%) ⬇️
plugins-fetch_fs_generic-pool 54.43% <100.00%> (-0.11%) ⬇️
plugins-google-cloud-pubsub_grpc_handlebars 59.84% <100.00%> (-0.11%) ⬇️
plugins-hapi_hono_ioredis 55.83% <100.00%> (-0.11%) ⬇️
plugins-jest_knex_langgraph 51.63% <100.00%> (-0.11%) ⬇️
plugins-ldapjs_light-my-request_limitd-client 53.39% <100.00%> (-0.10%) ⬇️
plugins-lodash_mariadb_memcached 54.18% <100.00%> (-0.11%) ⬇️
plugins-moleculer_mongodb_mongodb-core 57.26% <100.00%> (-0.11%) ⬇️
plugins-mongoose_multer_mysql 54.68% <100.00%> (-0.11%) ⬇️
plugins-mysql2_nats_node-serialize 56.18% <100.00%> (-0.11%) ⬇️
plugins-opensearch_passport-http_pino 55.03% <100.00%> (-0.11%) ⬇️
plugins-postgres_process_pug 54.26% <100.00%> (-0.11%) ⬇️
plugins-redis_router_sequelize 57.00% <100.00%> (-0.11%) ⬇️
plugins-test-and-upstream-rhea_undici_url 56.98% <100.00%> (-0.11%) ⬇️
plugins-valkey_vm_winston 53.81% <100.00%> (-0.11%) ⬇️
plugins-ws 54.77% <100.00%> (-0.11%) ⬇️
profiling 58.17% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-aws-sdk 50.64% <100.00%> (-0.09%) ⬇️
serverless-aws-sdk-bedrockruntime 50.66% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-client 51.94% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-dynamodb 51.75% <100.00%> (-0.07%) ⬇️
serverless-aws-sdk-eventbridge 46.22% <100.00%> (-0.09%) ⬇️
serverless-aws-sdk-kinesis 54.69% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-lambda 52.76% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-s3 51.51% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-serverless-peer-service 54.70% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-sns 55.46% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-sqs 55.89% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-stepfunctions 51.22% <100.00%> (-0.10%) ⬇️
serverless-aws-sdk-util 48.44% <100.00%> (-0.11%) ⬇️
serverless-bucket-0 55.20% <100.00%> (-0.05%) ⬇️
serverless-bucket-1 55.98% <100.00%> (-0.10%) ⬇️
test-optimization-cucumber 72.94% <100.00%> (-0.04%) ⬇️
test-optimization-cypress 66.32% <100.00%> (+<0.01%) ⬆️
test-optimization-jest 74.24% <100.00%> (+0.28%) ⬆️
test-optimization-mocha 74.70% <100.00%> (+0.41%) ⬆️
test-optimization-playwright-playwright-atr 61.55% <100.00%> (-0.02%) ⬇️
test-optimization-playwright-playwright-efd 61.74% <100.00%> (-0.02%) ⬇️
test-optimization-playwright-playwright-final-status 61.70% <100.00%> (-0.02%) ⬇️
test-optimization-playwright-playwright-impacted-tests 61.42% <100.00%> (+0.15%) ⬆️
test-optimization-playwright-playwright-reporting 61.32% <100.00%> (-0.02%) ⬇️
test-optimization-playwright-playwright-test-management 62.23% <100.00%> (-0.07%) ⬇️
test-optimization-playwright-playwright-test-span 61.47% <100.00%> (-0.07%) ⬇️
test-optimization-selenium 60.65% <100.00%> (-0.18%) ⬇️
test-optimization-testopt 59.14% <100.00%> (+0.04%) ⬆️
test-optimization-vitest 71.58% <100.00%> (-0.89%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@wconti27
wconti27 marked this pull request as draft June 15, 2026 18:50

@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: 87f73a07a3

ℹ️ 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 packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js Outdated
@wconti27 wconti27 added the apm-integration-toolkit PR Generated by APM AI Integration Toolkit label Jun 15, 2026
…cycle comment, and add context propagation test

- McpServerToolCallPlugin: RegisteredTool objects have no .name property — the
  name is the key in McpServer._registeredTools. Look up by reference via ctx.self
  so mcp.server.tool.call spans now have the correct resource tag (was undefined).

- McpServerRequestPlugin: document why kind:'Sync'/end() is intentional.
  Protocol._onrequest is fire-and-forget (returns void, schedules async work
  internally). Async kind would await undefined and resolve immediately — same
  lifecycle problem. The span represents "request received" not "request completed";
  mcp.server.tool.call on executeToolHandler covers the meaningful duration.
  Add matching comment to the rewriter config entry.

- setIsErrorTag: extract shared module-level helper used by both McpToolCallPlugin
  and McpServerToolCallPlugin (no functional change; already deduplicated).

- Tests: add context propagation test asserting mcp.server.request is a child of
  mcp.client.tool.call when using InMemoryTransport (same process, async context
  propagates naturally). Use single assertSomeTraces callback so both span IDs are
  captured from the same trace payload. Also assert resource:'test-tool' on the
  mcp.server.tool.call happy path test to cover the name-extraction fix.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Jun 15, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 98.23%
Overall Coverage: 96.55% (+0.02%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: c469830 | Docs | Datadog PR Page | Give us feedback!

@pr-commenter

pr-commenter Bot commented Jun 15, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-07-13 14:37:15

Comparing candidate commit c469830 in PR branch apm-integration-toolkit/modelcontextprotocol-sdk/server-tracing with baseline commit 4398f17 in branch master.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 2310 metrics, 48 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:appsec-appsec-enabled-24

  • unstable execution_time [-206.442ms; +212.093ms] or [-7.775%; +7.988%]

scenario:appsec-appsec-enabled-26

  • unstable execution_time [-228.763ms; +236.311ms] or [-8.950%; +9.245%]

scenario:appsec-appsec-enabled-with-attacks-24

  • unstable execution_time [-160.253ms; +163.078ms] or [-5.185%; +5.276%]

scenario:appsec-appsec-enabled-with-attacks-26

  • unstable execution_time [-182.533ms; +189.388ms] or [-6.295%; +6.532%]

scenario:appsec-control-20

  • unstable execution_time [-127.499ms; +123.189ms] or [-7.743%; +7.481%]

scenario:appsec-control-24

  • unstable execution_time [-101.838ms; +123.887ms] or [-8.321%; +10.123%]

scenario:appsec-control-26

  • unstable execution_time [-123056.074µs; +122908.607µs] or [-9.974%; +9.962%]

scenario:appsec-iast-no-vulnerability-iast-enabled-always-active-20

  • unstable execution_time [-13598.810µs; +14685.168µs] or [-5.252%; +5.671%]

scenario:appsec-iast-no-vulnerability-iast-enabled-default-config-20

  • unstable execution_time [-10.071ms; +28.777ms] or [-3.952%; +11.291%]

scenario:debugger-line-probe-with-snapshot-default-24

  • unstable cpu_user_time [-485.495ms; +340.491ms] or [-6.048%; +4.242%]
  • unstable instructions [-4075.7M instructions; +2893.3M instructions] or [-6.223%; +4.417%]

scenario:debugger-line-probe-with-snapshot-default-26

  • unstable cpu_user_time [-1887.743ms; +595.356ms] or [-20.213%; +6.375%]
  • unstable execution_time [-1978.857ms; +646.900ms] or [-19.655%; +6.425%]
  • unstable instructions [-16.5G instructions; +5.2G instructions] or [-21.153%; +6.619%]
  • unstable throughput [-142.554op/s; +430.237op/s] or [-4.358%; +13.153%]

scenario:debugger-line-probe-with-snapshot-minimal-24

  • unstable cpu_user_time [-2752.882ms; +1893.461ms] or [-28.879%; +19.863%]
  • unstable execution_time [-2752.015ms; +1917.332ms] or [-26.806%; +18.675%]
  • unstable instructions [-23.7G instructions; +16.2G instructions] or [-30.208%; +20.662%]
  • unstable max_rss_usage [-12.974MB; +8.265MB] or [-8.031%; +5.116%]
  • unstable throughput [-554.914op/s; +719.810op/s] or [-16.993%; +22.042%]

scenario:debugger-line-probe-with-snapshot-minimal-26

  • unstable cpu_user_time [-1.743s; +5.282s] or [-16.485%; +49.968%]
  • unstable execution_time [-1.791s; +5.373s] or [-15.853%; +47.552%]
  • unstable instructions [-15.3G instructions; +47.2G instructions] or [-17.233%; +53.161%]
  • unstable max_rss_usage [-4.939MB; +16.782MB] or [-3.032%; +10.300%]
  • unstable throughput [-1171.588op/s; +414.142op/s] or [-39.116%; +13.827%]

scenario:debugger-line-probe-without-snapshot-24

  • unstable cpu_user_time [-2.750s; +0.395s] or [-30.384%; +4.361%]
  • unstable execution_time [-2.791s; +0.388s] or [-28.643%; +3.982%]
  • unstable instructions [-23.4G instructions; +3.2G instructions] or [-31.570%; +4.352%]
  • unstable throughput [-109.178op/s; +699.882op/s] or [-3.150%; +20.193%]

scenario:debugger-line-probe-without-snapshot-26

  • unstable cpu_user_time [-2487.567ms; +4091.638ms] or [-26.401%; +43.425%]
  • unstable execution_time [-2553.486ms; +4124.465ms] or [-25.277%; +40.828%]
  • unstable instructions [-22.4G instructions; +36.8G instructions] or [-28.559%; +46.885%]
  • unstable max_rss_usage [-7.842MB; +13.723MB] or [-4.912%; +8.595%]
  • unstable throughput [-719.173op/s; +467.964op/s] or [-22.055%; +14.351%]

scenario:dogstatsd-with-tags-20

  • unstable cpu_user_time [-376.991ms; +294.568ms] or [-7.822%; +6.112%]
  • unstable execution_time [-376.733ms; +294.818ms] or [-7.692%; +6.020%]
  • unstable throughput [-101603.678op/s; +130410.085op/s] or [-5.930%; +7.612%]

scenario:plugin-claude-agent-sdk-compact-stream-scan-26

  • unstable cpu_user_time [-3087679.684ns; +3087679.684ns] or [-5.164%; +5.164%]

scenario:plugin-graphql-long-with-depth-on-max-20

  • unstable cpu_user_time [-725.134ms; +742.431ms] or [-5.648%; +5.782%]
  • unstable execution_time [-730.394ms; +750.661ms] or [-5.565%; +5.720%]
  • unstable throughput [-3.434op/s; +3.349op/s] or [-5.598%; +5.459%]

scenario:plugin-pg-service-20

  • unstable cpu_usage_percentage [-8.504%; +5.394%]
  • unstable execution_time [-126.951ms; +202.591ms] or [-7.820%; +12.479%]
  • unstable throughput [-332706.434op/s; +210455.084op/s] or [-8.895%; +5.627%]

scenario:plugin-pg-service-26

  • unstable cpu_usage_percentage [-10.203%; +6.674%]
  • unstable execution_time [-73.790ms; +155.828ms] or [-8.032%; +16.961%]
  • unstable throughput [-791784.720op/s; +331975.317op/s] or [-11.899%; +4.989%]

scenario:test-optimization-large-suite-20

  • unstable max_rss_usage [-5167.765KB; +3329.099KB] or [-6.447%; +4.153%]

…for full-lifecycle server request spans

Protocol._onrequest is fire-and-forget: it returns void and schedules all async
work via an internal Promise chain. Using kind:'Sync' ended the span immediately
on return, before any handler ran; kind:'Async' was equally wrong (awaiting
undefined resolves instantly).

Replace with shimmer.wrap on Protocol.prototype._onrequest:
- Publish apm:mcp:server:request:start via runStores to establish async context
  for the full Promise chain (enabling correct parent-child relationships)
- Intercept _requestHandlerAbortControllers.delete (called in .finally()) to
  publish apm:mcp:server:request:finish when the chain fully completes

Update McpServerRequestPlugin to subscribe to the new channels instead of the
orchestrion prefix, and use the finish event to close the span.

Remove the four _onrequest entries (ESM+CJS) from the orchestrion rewriter config.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
tlhunter
tlhunter previously approved these changes Jun 15, 2026
@wconti27
wconti27 force-pushed the apm-integration-toolkit/modelcontextprotocol-sdk/server-tracing branch from 1dee468 to 94d9ded Compare June 15, 2026 20:12
@wconti27
wconti27 marked this pull request as ready for review June 15, 2026 20:16

@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: 94d9dedd17

ℹ️ 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 packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js Outdated
Comment thread packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js Outdated
…d and transport disconnect

Two bugs identified by Codex review:

1. When the SDK has no registered handler for a method, _onrequest() sends
   MethodNotFound and returns without creating an AbortController. Previously, the
   pending context was recorded before calling original(), so those spans leaked.
   Fix: after calling original(), check whether the SDK registered a controller for
   this request.id — if not, publish finish immediately.

2. When a transport disconnects with in-flight requests, _onclose() calls
   _requestHandlerAbortControllers.clear(), bypassing the wrapped delete().
   Fix: also wrap clear() on the map to publish finish for all pending contexts.

Tests added for both paths.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Comment thread packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js
Comment thread packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js
…hod-specific tags

Add resource/name/uri/args/response tags to mcp.server.request spans:
- tools/call: mcp.tool.name, mcp.request.arguments, mcp.tool.response
- tools/list: mcp.tool.names
- resources/read: mcp.resource.uri
- resources/list: mcp.resource.uris
- prompts/get: mcp.prompt.name, mcp.request.arguments
- prompts/list: mcp.prompt.names, mcp.prompt.descriptions

Replace INTERNAL_METHODS denylist with an allowlist regex
(^(?:tools|resources|prompts)\/) so unknown future methods
(ping, initialize, tasks/*, sampling/*, etc.) are silently
skipped rather than requiring an explicit exclusion list.

Use a WeakMap (toolNames) for O(1) tool name lookup in
McpServerToolCallPlugin instead of a linear _registeredTools scan.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@wconti27 wconti27 changed the title feat(@modelcontextprotocol/sdk): add server-side tracing for Protocol._onrequest and McpServer.executeToolHandler feat(@modelcontextprotocol/sdk): add server-side tracing for MCP requests / tool calls Jun 16, 2026
…helpers, clarify tool name lookup

- Replace bare diagnostic channels with tracingChannel('apm:mcp:server:request')
  so the instrumentation follows the same convention as undici and other plugins;
  update McpServerRequestPlugin prefix to 'tracing:apm:mcp:server:request' and
  rename finish() → asyncEnd() to match TracingChannel semantics
- Extract tagRequestParams / tagRequestResult helpers in tracing.js to remove
  inline tag logic from bindStart / asyncEnd
- Add comment on _createRegisteredTool shimmer explaining it populates toolNames
  WeakMap in tracing.js for O(1) resource tag on McpServerToolCallPlugin spans

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@wconti27
wconti27 requested a review from BridgeAR June 16, 2026 18:27
…t span coverage

Introduce McpPlugin base class so each plugin only declares static id/prefix/
spanName/kind and overrides getResource/onStart/onEnd — eliminating duplicated
bindStart/asyncEnd boilerplate across all four plugins.

Add client-side spans for the remaining MCP operations that previously had none:
- mcp.resources.list  (Client.listResources)
- mcp.resource.read   (Client.readResource,  resource = URI)
- mcp.prompts.list    (Client.listPrompts)
- mcp.prompt.get      (Client.getPrompt,     resource = prompt name)

Expand orchestrion rewriter config to use helper functions instead of repeating
the ESM/CJS entry pair for every method; add the four new client methods and
keep the existing server entry.

Fix: use dc-polyfill channel instead of diagnostics_channel directly.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
crysmags
crysmags previously approved these changes Jun 30, 2026

@crysmags crysmags left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

All change requests were made, approved!

@BridgeAR BridgeAR left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice, this is looking very promising!

A few small things, while nothing major.

Comment thread packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js Outdated
Comment thread packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/test/test-setup.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/test/index.spec.js Outdated
Comment thread packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js
@BridgeAR
BridgeAR dismissed their stale review July 1, 2026 22:01

Nothing really blocking

@wconti27
wconti27 requested a review from BridgeAR July 9, 2026 15:56
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/utils.js Outdated
@wconti27
wconti27 force-pushed the apm-integration-toolkit/modelcontextprotocol-sdk/server-tracing branch from aad45e3 to 4e08adb Compare July 9, 2026 16:16
@wconti27
wconti27 requested a review from BridgeAR July 9, 2026 16:20
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/utils.js Outdated
@wconti27
wconti27 requested a review from BridgeAR July 9, 2026 19:31
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js Outdated
Comment thread packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js Outdated
@wconti27
wconti27 requested a review from BridgeAR July 13, 2026 14:55

@BridgeAR BridgeAR left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall LGTM. There are a few smaller things but they are not blocking and could be improved as follow-up. I also believe we could change the architecture a bit for very minor performance improvements, but that is likely not going to show much and this seems a relatively straight forward implementation.

Comment on lines +39 to +40
function getPendingContext (protocol, requestId) {
return pendingContexts.get(protocol)?.get(requestId)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: this should be inlined

Comment on lines +16 to +19
function getServerRequestResource (request) {
if (request?.method === 'tools/call') return SERVER_TOOL_CALL_RESOURCE
return request?.method
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: I would inline this method

Comment on lines +54 to +57
this.addSub('apm:mcp:client:request:inject', this.injectTraceContext.bind(this))
}

injectTraceContext (ctx) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: the class method should ideally just be inlined, since it is not used elsewhere as far as I can tell

Comment on lines +57 to +61
proc = await spawnPluginIntegrationTestProcAndExpectExit(sandboxCwd(), 'server.mjs', agent.port, {
NODE_OPTIONS: nodeOptions,
})

await res

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit

Suggested change
proc = await spawnPluginIntegrationTestProcAndExpectExit(sandboxCwd(), 'server.mjs', agent.port, {
NODE_OPTIONS: nodeOptions,
})
await res
([proc] = await Promise.all([
spawnPluginIntegrationTestProcAndExpectExit(sandboxCwd(), 'server.mjs', agent.port, {
NODE_OPTIONS: nodeOptions,
}),
res,
])

Comment on lines +148 to +150
await testSetup.clientCallTool('empty-error-tool')

return await traceAssertion

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit (these are about theoretical unhandled rejections. We just have a lot of tests written like this and due to the way the helper works it should be safe)

Suggested change
await testSetup.clientCallTool('empty-error-tool')
return await traceAssertion
await Promise.all([
testSetup.clientCallTool('empty-error-tool'),
traceAssertion,
])

@BridgeAR
BridgeAR merged commit 466de2b into master Jul 14, 2026
657 checks passed
@BridgeAR
BridgeAR deleted the apm-integration-toolkit/modelcontextprotocol-sdk/server-tracing branch July 14, 2026 12:29
This was referenced Jul 14, 2026
juan-fernandez pushed a commit that referenced this pull request Jul 14, 2026
…and client operations (#8916)

This makes MCP protocol activity visible in APM across server requests, tool handlers, and client operations while preserving trace relationships across process boundaries through MCP metadata propagation. Span tags remain low-cardinality without copying MCP payloads, while errors from failed handlers and tool results remain observable.
juan-fernandez pushed a commit that referenced this pull request Jul 14, 2026
…and client operations (#8916)

This makes MCP protocol activity visible in APM across server requests, tool handlers, and client operations while preserving trace relationships across process boundaries through MCP metadata propagation. Span tags remain low-cardinality without copying MCP payloads, while errors from failed handlers and tool results remain observable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

apm-integration-toolkit PR Generated by APM AI Integration Toolkit semver-minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants