-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(core): MCP server instrumentation without breaking Miniflare #16817
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(core): MCP server instrumentation without breaking Miniflare #16817
Conversation
…improved test isolation
…racing and monitoring
…r MCP server instrumentation
…ibute names to match OTEL draft semantic convention
…r improved instrumentation
…or stdio and SSE transports
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice, all good from my eyes in overall direction!
Thanks @AbhiPrasad! Will keep working on this and close the other PR using |
@AbhiPrasad just finished suggested changes + added some new stuff:
|
## DESCRIBE YOUR PR Closes #14106 Adds developer docs for our MCP Server instrumentation. Route for the documentation `sdk/expected-features/mcp-instrumentation` that includes: ```bash .mcp-instrumentation ├── index.mdx # brief description on current features and limitations ├── errors.mdx # draft errors doc └── tracing.mdx # Tracing and span conventions ``` Followed this file structure as a start to (in the future) have this structure for all the other features, having something like this below, where we have : ``` .[Feature 1] ├── index.mdx ├── logs.mdx ├── errors.mdx . . . .[Feature 2] ├── index.mdx ├── errors.mdx ├── tracing.mdx . . . ``` The structure idea is from @abhi, who helped me settle this <3. **NOTE:** Error docs are going to change, addressing feedback from [sentry-javascript/pull/16817](getsentry/sentry-javascript#16817) ## IS YOUR CHANGE URGENT? ## PRE-MERGE CHECKLIST *Make sure you've checked the following before merging your changes:* - [x] Checked Vercel preview for correctness, including links - [ ] PR was reviewed and approved by any necessary SMEs (subject matter experts) - [ ] PR was reviewed and approved by a member of the [Sentry docs team](https://github.com/orgs/getsentry/teams/docs) --------- Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io>
size-limit report 📦
|
78e92a2
to
55aa5ac
Compare
} | ||
|
||
if (config.captureUri && params?.uri) { | ||
args[`${MCP_REQUEST_ARGUMENT}.uri`] = JSON.stringify(params.uri); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: MCP Instrumentation Fails on Non-Serializable Input
The MCP instrumentation calls JSON.stringify
on user-provided input parameters (specifically values within the arguments
object, uri
, and name
) without error handling. This can lead to crashes if these parameters contain non-serializable data (e.g., circular references, functions, BigInts). Implement error handling (e.g., try-catch) or use a safe serialization method to prevent unhandled exceptions.
…6817) Closes #16826, #16654, #16666 #16978 Different approach from #16807 . Using `Proxy` was causing issues in cloudflare #16182. Now using `fill` we shouldn't have those problems as `fill` doesn't create a new wrapper object with a different identity, so now: 1. `fill` just replaces the method on the existing object 2. The transport object keeps its original identity 3. When `transport.start()` runs and accesses private fields, this is still the original transport object 4. The `WeakMap` recognizes it as the same object that owns the private fields - Support for new MCP SDK methods (`mcpServerInstance.tool()`, `mcpServerInstance.resource()`, etc.) - Tracing instrumentation - Error handling It follows [OTEL semantic conventions for MCP](https://github.com/open-telemetry/semantic-conventions/blob/3097fb0af5b9492b0e3f55dc5f6c21a3dc2be8df/docs/gen-ai/mcp.md) and adds more attributes we thought are useful. It also handles PII based on user setting of `sendDefaultPii`. 1. **Transport receives tools/call request (`id: 2`)** 2. **Create INACTIVE `mcp.server` span** 3. **Store span in**`requestIdToSpanMap[2] = { span, method: "tools/call", startTime }` 4. **Execute handler with span context** *(handler gets requestId: 2)* 5. **Handler finds span using** `requestId: 2` 6. **Tool execution happens within span context** 7. **Response sent with tool results** 8. `completeSpanWithToolResults(2, result)` **enriches and completes span** 1. **error capture** - `errorCapture.ts` - Non-invasive error reporting that never interferes with MCP service operation - Error context with MCP-specific metadata - PII filtering respects `sendDefaultPii` settings - Resilient to Sentry failures (wrapped in try-catch) 2. **Tool execution error capturing** - `handlers.ts` - Captures exceptions thrown during tool execution - Preserves normal MCP behaviour (errors converted to `isError: true`) - Includes tool name, arguments, and request context - Handles both sync and async tool handlers 3. **Transport error instrumentation** - `transport.ts` - Captures connection errors and network failures - Intercepts JSON-RPC error responses - Includes transport type and session information - Handles error responses being sent back to clients
…6817) Closes #16826, #16654, #16666 #16978 Different approach from #16807 . Using `Proxy` was causing issues in cloudflare #16182. Now using `fill` we shouldn't have those problems as `fill` doesn't create a new wrapper object with a different identity, so now: 1. `fill` just replaces the method on the existing object 2. The transport object keeps its original identity 3. When `transport.start()` runs and accesses private fields, this is still the original transport object 4. The `WeakMap` recognizes it as the same object that owns the private fields - Support for new MCP SDK methods (`mcpServerInstance.tool()`, `mcpServerInstance.resource()`, etc.) - Tracing instrumentation - Error handling It follows [OTEL semantic conventions for MCP](https://github.com/open-telemetry/semantic-conventions/blob/3097fb0af5b9492b0e3f55dc5f6c21a3dc2be8df/docs/gen-ai/mcp.md) and adds more attributes we thought are useful. It also handles PII based on user setting of `sendDefaultPii`. 1. **Transport receives tools/call request (`id: 2`)** 2. **Create INACTIVE `mcp.server` span** 3. **Store span in**`requestIdToSpanMap[2] = { span, method: "tools/call", startTime }` 4. **Execute handler with span context** *(handler gets requestId: 2)* 5. **Handler finds span using** `requestId: 2` 6. **Tool execution happens within span context** 7. **Response sent with tool results** 8. `completeSpanWithToolResults(2, result)` **enriches and completes span** 1. **error capture** - `errorCapture.ts` - Non-invasive error reporting that never interferes with MCP service operation - Error context with MCP-specific metadata - PII filtering respects `sendDefaultPii` settings - Resilient to Sentry failures (wrapped in try-catch) 2. **Tool execution error capturing** - `handlers.ts` - Captures exceptions thrown during tool execution - Preserves normal MCP behaviour (errors converted to `isError: true`) - Includes tool name, arguments, and request context - Handles both sync and async tool handlers 3. **Transport error instrumentation** - `transport.ts` - Captures connection errors and network failures - Intercepts JSON-RPC error responses - Includes transport type and session information - Handles error responses being sent back to clients
backport of #16817 Co-authored-by: Miguel Betegón <miguelbetegongarcia@gmail.com>
## DESCRIBE YOUR PR Closes #14106 Adds developer docs for our MCP Server instrumentation. Route for the documentation `sdk/expected-features/mcp-instrumentation` that includes: ```bash .mcp-instrumentation ├── index.mdx # brief description on current features and limitations ├── errors.mdx # draft errors doc └── tracing.mdx # Tracing and span conventions ``` Followed this file structure as a start to (in the future) have this structure for all the other features, having something like this below, where we have : ``` .[Feature 1] ├── index.mdx ├── logs.mdx ├── errors.mdx . . . .[Feature 2] ├── index.mdx ├── errors.mdx ├── tracing.mdx . . . ``` The structure idea is from @abhi, who helped me settle this <3. **NOTE:** Error docs are going to change, addressing feedback from [sentry-javascript/pull/16817](getsentry/sentry-javascript#16817) ## IS YOUR CHANGE URGENT? ## PRE-MERGE CHECKLIST *Make sure you've checked the following before merging your changes:* - [x] Checked Vercel preview for correctness, including links - [ ] PR was reviewed and approved by any necessary SMEs (subject matter experts) - [ ] PR was reviewed and approved by a member of the [Sentry docs team](https://github.com/orgs/getsentry/teams/docs) --------- Co-authored-by: Alex Krawiec <alex.krawiec@sentry.io>
Closes #16826, #16654, #16666 #16978
Different approach from #16807 .
Using
Proxy
was causing issues in cloudflare #16182.Now using
fill
we shouldn't have those problems asfill
doesn't create a new wrapper object with a different identity, so now:fill
just replaces the method on the existing objecttransport.start()
runs and accesses private fields, this is still the original transport objectWeakMap
recognizes it as the same object that owns the private fieldsWhat's inside
mcpServerInstance.tool()
,mcpServerInstance.resource()
, etc.)Tracing
It follows OTEL semantic conventions for MCP and adds more attributes we thought are useful.
It also handles PII based on user setting of
sendDefaultPii
.Tracing flow
id: 2
)mcp.server
spanrequestIdToSpanMap[2] = { span, method: "tools/call", startTime }
requestId: 2
completeSpanWithToolResults(2, result)
enriches and completes spanError handling
errorCapture.ts
sendDefaultPii
settingshandlers.ts
isError: true
)transport.ts