Repository navigation
Report resource reads by registered name, not URI, in analytics - #271
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
tnsardesai
marked this pull request as ready for review
November 10, 2026 00:58
Sayan-
approved these changes
Nov 10, 2026
Sayan-
left a comment
Contributor
There was a problem hiding this comment.
Checked the resources/read path end to end: the category lookup follows McpServer's own resources/read matching, and the success, not-found, unparseable-URI and hook-throw paths all leave the URI out of captured events.
- p2 (pre-existing):
$mcp_tool_callstill sends a client-chosen string as$mcp_resource_nameand$mcp_tool_namewhen the requested tool does not exist. Atools/callwith namesk_secret_TOKEN123on this branch capturedsk_secret_TOKEN123in both properties. The SDK builds the event fromrequest.params.namebefore the tool lookup (@posthog/mcpdist/extensions/instrumentation.js~136-145), and both properties are inSENT_PROPERTIES(src/lib/mcp/analytics.ts:160).
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
$mcp_resource_readevents sent the requested resource URI to PostHog as$mcp_resource_name. Resource URIs are client-controlled, so whatever a client put in the path (ids, names, or a bearer-like value) was captured, including for reads that matched no registered resource and for reads that failed.Resource reads now report a bounded category instead:
browser,browsers,profile,profiles,app,apps,browser_pool,browser_pools,kernel-managed-auth-login)unregisteredotherwise, including URIs that don't parseThe category is computed in the existing
eventPropertieshook from the server's live registrations (_registeredResources/_registeredResourceTemplates), matched the same wayMcpServer'sresources/readhandler matches them, so there is no hand-maintained list.beforeSend(sanitizeMcpAnalyticsEvent) swaps it into$mcp_resource_namefor resource reads and drops the property if the category is missing. The allowlist is unchanged. Resource behavior and responses are unchanged.Unregistered tool calls
The same applies to
tools/call: the SDK builds the event fromrequest.params.namebefore looking the tool up, so a call to a tool that doesn't exist sent the client's string as$mcp_tool_nameand$mcp_resource_name.eventPropertiesnow checks the name against the server's registered tools (_registeredTools, the field the SDK itself reads), andbeforeSenddrops the event when the tool isn't registered. In prod that is 7 events in the last 90 days, compared with about 1.3M real tool calls in the last 30 days alone, so no tool analytics are lost. Calls to registered tools are unchanged.What
@posthog/mcp0.17.0 sends todayresources/readgoes throughcaptureResourceRequest(src/extensions/instrumentation.ts, line 540;dist/extensions/instrumentation.js, line 259), which setsresourceName: request.params?.urifor every read. The event is published from the same prepared event on success and when the handler throws, so unknown and failed reads carry the URI too.addCommonEventProperties(src/extensions/posthog-events.ts, line 121) mapsresourceNameto$mcp_resource_name.sanitizeCapturedValue) only rewrites URL userinfo and sensitive query keys. Path segments pass through. On main, a read ofhttps://<token>@files.example.test/<token>?key=<token>was captured ashttps://%5Bredacted%5D@files.example.test/<token>?key=%5Bredacted%5D, andkernel://orgs/.../secrets/<token>was captured verbatim.$mcp_parametersand$mcp_error_message(for exampleResource ... not found), but neither is on the allowlist.$mcp_error_typeis the exception class name (Error,ProtocolError).$mcp_intentis not set for reads.$exceptionevents are already dropped, and$identify(which would carry the URI throughgetRequestResourceName) is never sent becauseidentifyisnull.resources/listandresources/templates/listset no resource name. Their response goes to$mcp_response, which is not allowlisted.prompts/getandprompts/listare not instrumented by this SDK version.Tests
New tests in
src/lib/mcp/analytics.test.tsgo through the real instrumentation and registrations (registerMcpCapabilitieswith MCP Apps enabled) over an in-memory client, using synthetic ids and tokens:is_error: false, and a duration. No URI, project id, or identifier appears in the events.browser. Akernel://URI and anhttps://URI containing a synthetic token reportunregistered. All three reportis_error: true, and neither the token nor the URI appears anywhere in the captured events.constructorto cover inherited object keys) produce no$mcp_tool_callevent, and the token appears nowhere in the captured events. This test fails against the previous commit.get_connection_contexttool call still reports the tool name in$mcp_tool_name/$mcp_resource_name, plusis_errorand a duration.Both resource tests fail against main's
analytics.ts, which reports the raw URIs. The control test passes on both.Ran:
bun test: 1143 pass, 0 failbunx tsc --noEmit: cleanbunx prettier --checkon the changed files: clean. Repo-wideformat:checkflags onlyAGENTS.md, which this PR doesn't touch.bun run lintcan't run on this repo (next lintwas removed in Next.js 16, as noted in AGENTS.md).Notes
McpServerprivate fields, the same way the PostHog SDK reads_registeredTools. If a future@modelcontextprotocol/serverrenames them, reads reportunregistered, which still sends no URI, and the registered-name test fails.browser_pools/browser_poolaren't read in the tests because that registration ignores injected dependencies and would call the real API. They use the same template path as the others.Note
Medium Risk
Changes privacy-sensitive MCP analytics (what is sent to PostHog) and uses McpServer private registry fields; behavior is test-covered but could mislabel resources if SDK internals change.
Overview
MCP PostHog analytics no longer emit client-controlled URIs or arbitrary tool names on resource reads and invalid tool calls.
For
$mcp_resource_read, the instrumentation hook resolves eachresources/readURI against the server's live resource registry (same matching logic as the MCP handler) and records a bounded$mcp_resource_name—registered names likebrowser/profile/kernel-managed-auth-login, orunregisteredwhen nothing matches.sanitizeMcpAnalyticsEventmaps that category into the public property and strips internal fields via the existing allow-list, so path segments, tokens, and project ids from the request URI do not reach PostHog (including on failed reads).For
tools/call, calls to tools that are not in_registeredToolsare flagged and the entire$mcp_tool_callevent is dropped inbeforeSend, so client-supplied names (e.g. secrets orconstructor) are not captured. Registered tool calls behave as before.Integration tests exercise real
instrumentMcpAnalytics+registerMcpCapabilitiespaths and assert registered names, error flags, and absence of sensitive strings in captured events.Reviewed by Cursor Bugbot for commit fa2811e. Bugbot is set up for automated code reviews on this repo. Configure here.