Skip to content

Track client-declared MCP capabilities - #159

Open
rgarcia wants to merge 2 commits into
mainfrom
hypeship/client-capability-analytics
Open

Track client-declared MCP capabilities#159
rgarcia wants to merge 2 commits into
mainfrom
hypeship/client-capability-analytics

Conversation

@rgarcia

@rgarcia rgarcia commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

summary

  • add bounded capability properties to each $mcp_initialize analytics event
  • distinguish sampling with tool support and elicitation form versus URL support
  • track an explicit allowlist of official MCP extensions without retaining extension settings or unknown identifiers
  • normalize legacy core task support with the Tasks extension

validation

  • bun test
  • bunx tsc --noEmit
  • bunx prettier --check src/lib/mcp/analytics.ts src/lib/mcp/analytics.test.ts src/app/[transport]/route.ts
  • bun run build compiled and passed TypeScript, then stopped during page-data collection because KERNEL_CLI_PROD_CLIENT_ID is not available locally

Note

Low Risk
Analytics-only changes on initialize with strict allowlisting; no auth or tool behavior changes.

Overview
Adds bounded client capability telemetry to each $mcp_initialize PostHog event so product can see what MCP clients advertise without storing raw capability maps or extension settings.

The MCP POST handler parses the body once, runs clientCapabilityAnalyticsFromInitialize on initialize requests, and threads the result through auth into authInfo.extra. Instrumentation merges those properties only for initialize events, alongside the existing connection analytics context.

clientCapabilityAnalyticsFromInitialize normalizes the client capabilities object into fixed booleans and an elicitation mode (none / form / url / form_and_url), including sampling-with-tools. Official MCP extensions are reduced via an explicit allowlist (UI/apps, tasks, OAuth client credentials, enterprise auth); unknown extension IDs and nested settings never enter analytics. Legacy core tasks and the Tasks extension both set task support. Missing capabilities are recorded as explicit false / none rather than omitted fields. New properties are added to the existing SENT_PROPERTIES allow-list so sanitization stays strict.

Reviewed by Cursor Bugbot for commit c30eec0. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Aug 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mcp Ready Ready Preview Aug 15, 2026 7:21pm

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: URL-only elicitation misclassified
    • Updated elicitation mode derivation to return "url" for URL-only capability maps and corrected the integration test expectation accordingly.

Create PR

Or push these changes by commenting:

@cursor push d154b7717f
Preview (d154b7717f)
diff --git a/src/lib/mcp/analytics.test.ts b/src/lib/mcp/analytics.test.ts
--- a/src/lib/mcp/analytics.test.ts
+++ b/src/lib/mcp/analytics.test.ts
@@ -606,7 +606,7 @@
     expect(initialize.properties).toMatchObject({
       [MCP_CLIENT_SUPPORTS_SAMPLING_PROPERTY]: true,
       [MCP_CLIENT_SUPPORTS_SAMPLING_TOOLS_PROPERTY]: true,
-      [MCP_CLIENT_ELICITATION_MODE_PROPERTY]: "form_and_url",
+      [MCP_CLIENT_ELICITATION_MODE_PROPERTY]: "url",
       [MCP_CLIENT_SUPPORTS_APPS_PROPERTY]: true,
       [MCP_CLIENT_SUPPORTS_TASKS_PROPERTY]: false,
     });

diff --git a/src/lib/mcp/analytics.ts b/src/lib/mcp/analytics.ts
--- a/src/lib/mcp/analytics.ts
+++ b/src/lib/mcp/analytics.ts
@@ -84,7 +84,11 @@
 export type McpClientCapabilityAnalytics = {
   [MCP_CLIENT_SUPPORTS_SAMPLING_PROPERTY]: boolean;
   [MCP_CLIENT_SUPPORTS_SAMPLING_TOOLS_PROPERTY]: boolean;
-  [MCP_CLIENT_ELICITATION_MODE_PROPERTY]: "none" | "form" | "form_and_url";
+  [MCP_CLIENT_ELICITATION_MODE_PROPERTY]:
+    | "none"
+    | "form"
+    | "url"
+    | "form_and_url";
   [MCP_CLIENT_SUPPORTS_APPS_PROPERTY]: boolean;
   [MCP_CLIENT_SUPPORTS_TASKS_PROPERTY]: boolean;
   [MCP_CLIENT_SUPPORTS_OAUTH_CLIENT_CREDENTIALS_PROPERTY]: boolean;
@@ -212,15 +216,23 @@
   const extensions = isRecord(capabilities.extensions)
     ? capabilities.extensions
     : null;
+  const supportsUrlElicitationMode = elicitation
+    ? hasOwn(elicitation, "url")
+    : false;
+  const supportsFormElicitationMode = elicitation
+    ? hasOwn(elicitation, "form") || !supportsUrlElicitationMode
+    : false;
 
   const properties: McpClientCapabilityAnalytics = {
     [MCP_CLIENT_SUPPORTS_SAMPLING_PROPERTY]: sampling !== null,
     [MCP_CLIENT_SUPPORTS_SAMPLING_TOOLS_PROPERTY]:
       sampling !== null && hasOwn(sampling, "tools"),
     [MCP_CLIENT_ELICITATION_MODE_PROPERTY]: elicitation
-      ? hasOwn(elicitation, "url")
+      ? supportsFormElicitationMode && supportsUrlElicitationMode
         ? "form_and_url"
-        : "form"
+        : supportsUrlElicitationMode
+          ? "url"
+          : "form"
       : "none",
     [MCP_CLIENT_SUPPORTS_APPS_PROPERTY]: false,
     [MCP_CLIENT_SUPPORTS_TASKS_PROPERTY]: hasOwn(capabilities, "tasks"),

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit fcb99bb. Configure here.

Comment thread src/lib/mcp/analytics.ts Outdated
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