Skip to content

fix(mcp): retroactively wrap handlers registered before wrapMcpServerWithSentry#20699

Draft
betegon wants to merge 2 commits intodevelopfrom
fix/mcp-retroactive-handler-wrapping
Draft

fix(mcp): retroactively wrap handlers registered before wrapMcpServerWithSentry#20699
betegon wants to merge 2 commits intodevelopfrom
fix/mcp-retroactive-handler-wrapping

Conversation

@betegon
Copy link
Copy Markdown
Member

@betegon betegon commented May 6, 2026

wrapMcpServerWithSentry works by patching the registration methods (registerTool, registerResource, registerPrompt) so that any handler passed to them gets wrapped with Sentry error capture. The problem: if tools are registered before wrapping, those already-stored handlers are never touched, so errors from them are silently dropped.

This fix adds a retroactive pass (wrapExistingHandlers) that runs immediately after the registration methods are patched. It walks the McpServer's internal registries (_registeredTools, _registeredResources, _registeredResourceTemplates, _registeredPrompts) and wraps the stored callables in-place — executor for tools, readCallback for resources and templates, handler for prompts. These are the properties the MCP SDK reads at call time (not captured by closure at registration), so replacing them is equivalent to having wrapped the original call.

Both orderings are now fully supported. Wrapping at construction is still recommended for consistency with how other SDK integrations work, but it's not required.

Recommended (wrap at construction):

const server = Sentry.wrapMcpServerWithSentry(
  new McpServer({ name: 'my-server', version: '1.0.0' }),
);

server.registerTool('my-tool', schema, handler);

Also works (wrap after registration):

const server = new McpServer({ name: 'my-server', version: '1.0.0' });

server.registerTool('my-tool', schema, handler);

Sentry.wrapMcpServerWithSentry(server); // retroactively wraps the already-registered tool

The retroactive wrapping intentionally accesses private MCP SDK internals. The JSDoc on wrapExistingHandlers includes a pinned upstream source link and a note to re-verify the internal shapes when upgrading the MCP SDK — all access is defensive and skips silently if a property isn't found.

Checklist

  • Tests added (Retroactive handler wrapping describe block in mcpServerWrapper.test.ts)
  • No lint changes needed (pure TS, no new deps)
  • Link an issue if there is one related to your pull request. If no issue is linked, one will be auto-generated and linked.

Closes #issue_link_here

@betegon betegon force-pushed the fix/mcp-retroactive-handler-wrapping branch from c1e7151 to 365c47a Compare May 6, 2026 10:19
…WithSentry

wrapMcpServerWithSentry patches the registration methods (registerTool etc.)
so only handlers registered after wrapping get instrumented. When wrapping
happens after registration, the already-stored executors/callbacks are never
touched, silently skipping error capture for those handlers.

Fix by scanning the McpServer's internal registries (_registeredTools,
_registeredResources, _registeredResourceTemplates, _registeredPrompts) after
patching the registration methods, and wrapping the stored callables in-place:
- tools: wrap `executor` (what the SDK calls on tools/call)
- resources/templates: wrap `readCallback`
- prompts: wrap `handler`

Both call orderings are now fully supported. JSDoc updated to reflect this
and reference the upstream SDK source line where executor is invoked.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@betegon betegon force-pushed the fix/mcp-retroactive-handler-wrapping branch from 365c47a to 8b9fd2c Compare May 6, 2026 10:58
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

size-limit report 📦

Path Size % Change Change
@sentry/browser 26.3 kB - -
@sentry/browser - with treeshaking flags 24.78 kB - -
@sentry/browser (incl. Tracing) 44.17 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 46.39 kB - -
@sentry/browser (incl. Tracing, Profiling) 49.14 kB - -
@sentry/browser (incl. Tracing, Replay) 83.55 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 73.01 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 88.23 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 100.84 kB - -
@sentry/browser (incl. Feedback) 43.44 kB - -
@sentry/browser (incl. sendFeedback) 31.11 kB - -
@sentry/browser (incl. FeedbackAsync) 36.19 kB - -
@sentry/browser (incl. Metrics) 27.6 kB - -
@sentry/browser (incl. Logs) 27.73 kB - -
@sentry/browser (incl. Metrics & Logs) 28.43 kB - -
@sentry/react 28.04 kB - -
@sentry/react (incl. Tracing) 46.4 kB - -
@sentry/vue 31.18 kB - -
@sentry/vue (incl. Tracing) 46.02 kB - -
@sentry/svelte 26.32 kB - -
CDN Bundle 28.91 kB - -
CDN Bundle (incl. Tracing) 46.94 kB - -
CDN Bundle (incl. Logs, Metrics) 30.34 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 48.04 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 69.4 kB - -
CDN Bundle (incl. Tracing, Replay) 84.07 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 85.15 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 89.89 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 90.97 kB - -
CDN Bundle - uncompressed 84.88 kB - -
CDN Bundle (incl. Tracing) - uncompressed 140.44 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 89.08 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 143.9 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 212.99 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 258.24 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 261.69 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 271.94 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 275.38 kB - -
@sentry/nextjs (client) 48.9 kB - -
@sentry/sveltekit (client) 44.64 kB - -
@sentry/node-core 59.81 kB +0.02% +9 B 🔺
@sentry/node 163.43 kB +0.01% +7 B 🔺
@sentry/node - without tracing 72.28 kB +0.02% +8 B 🔺
@sentry/aws-serverless 106.95 kB +0.01% +7 B 🔺
@sentry/cloudflare (withSentry) - minified 168.38 kB - -
@sentry/cloudflare (withSentry) 424.9 kB - -

View base workflow run

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.

1 participant