-
-
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
base: develop
Are you sure you want to change the base?
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 |
…duration. adds configuration, extraction, and transport utilities. Introduce span creation functions and improves method wrapping for improved telemetry.
… files for attribute extraction, correlation, and handler wrapping, removing deprecated utilities and configuration files. Improve transport instrumentation for better telemetry and span handling.
…n handling functions. Update type definitions and separate method wrapping for transport handlers.
… to remove sensitive data based on the sendDefaultPii setting.
- Introduced new attributes for tool result content count and error status. - Updated attribute extraction functions to utilize new constants for better maintainability. - Added error capturing utilities to handle tool execution errors and transport errors gracefully.
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.
1st pass.
Before I review further, I'd like to see the develop docs PR so that I can make sure all the attributes are being added as we expect. Right now it's a bit hard to know if an attribute is missing or not.
I assume much of this is AI generated, which is fine, but let's make sure we clean the comments up (and expand the jsdoc string appropriately).
@@ -0,0 +1,166 @@ | |||
/** | |||
* types for MCP server instrumentation |
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.
Are these vendored in from the mcp package? If so we need to put the mcp library license + sha we grabbed the types from in this file.
if (isJsonRpcRequest(jsonRpcMessage)) { | ||
const messageTyped = jsonRpcMessage as { method: string; id: string | number }; | ||
|
||
// Create isolation scope for this request (standard Sentry pattern) |
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.
m: Instead of inline comments like this, I would prefer if we added notes in the jsdoc to document behaviour. Right now this comment is a bit redundant with the code.
Ditto with other instances of this.
|
||
// Use Object.fromEntries with filter for a more functional approach | ||
return Object.fromEntries( | ||
Object.entries(spanData).filter(([key]) => { |
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.
l: I would prefer if we called reduce
on Object.entries(spanData)
to do this operation instead of calling filter
+ Object.fromEntries
on the constructed array.
|
||
captureException(error, { | ||
tags: { | ||
mcp_error_type: errorType || 'handler_execution', |
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.
The sdk should not be setting tags by default. Tags are meant to be only set by users.
What we can instead do is set a mechanism: https://develop.sentry.dev/sdk/data-model/event-payloads/exception/#exception-mechanism.
* | ||
* Compatible with versions `^1.9.0` of the `@modelcontextprotocol/sdk` package. | ||
*/ | ||
export function wrapMcpServerWithSentry<S extends object>(mcpServerInstance: S): S { |
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.
Let's add an example usage snippet to the jsdoc here.
@AbhiPrasad wdyt of this? Bugbot said that From what I’ve tested, fill works fine. the MCP server sets Do you think it makes sense to get lazy loading handlers? Not sure if it’s worthy adding more complexity for this. Cursor suggested something like a function wrapTransportProperty(obj, key, wrapFn) {
let current = obj[key];
let wrapped = false;
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
get() {
return current;
},
set(newValue) {
if (typeof newValue === 'function' && !wrapped) {
current = wrapFn(newValue);
wrapped = true;
} else {
current = newValue;
wrapped = false;
}
}
});
// in case the property was already assigned before wrapping
if (typeof current === 'function') {
obj[key] = current;
}
} |
yeah as long as it exists when we wrap it I think we are fine. |
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: Session-Specific ID Collision in Span Correlation
The new MCP server span correlation system uses a global requestIdToSpanMap
keyed only by requestId
. This change from session-scoped tracking creates a collision risk: JSON-RPC request IDs are only unique within a session, allowing concurrent MCP sessions to reuse IDs. This can lead to incorrect span correlation and potential data leakage between sessions. Additionally, SPAN_STATUS_ERROR
is imported but not exported from ../../tracing
, causing a compilation error.
packages/core/src/integrations/mcp-server/correlation.ts#L6-L26
import { getClient } from '../../currentScopes'; | |
import { SPAN_STATUS_ERROR, withActiveSpan } from '../../tracing'; | |
import type { Span } from '../../types-hoist/span'; | |
import { extractToolResultAttributes } from './attributeExtraction'; | |
import { filterMcpPiiFromSpanData } from './piiFiltering'; | |
import type { RequestId, RequestSpanMapValue } from './types'; | |
// Simplified correlation system that works with or without sessionId | |
// Maps requestId directly to span data for stateless operation | |
const requestIdToSpanMap = new Map<RequestId, RequestSpanMapValue>(); | |
/** | |
* Stores span context for later correlation with handler execution | |
*/ | |
export function storeSpanForRequest(requestId: RequestId, span: Span, method: string): void { | |
requestIdToSpanMap.set(requestId, { | |
span, | |
method, | |
startTime: Date.now(), | |
}); | |
} |
Was this report helpful? Give feedback by reacting with 👍 or 👎
Closes #16826, #16654, #16666
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