From 098be760c2eae691f2f3d56d6008b0e7dec7df38 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2026 02:07:33 +0000 Subject: [PATCH 1/2] feat(mcp): Count outputSchema in the token cost report measure-token-cost now converts each tool's outputSchema the way the MCP SDK does when it lists the tool, so the report covers the whole tools/list payload. 64 of the 73 stable top-level tools declare a schema, and the total goes from 47,940 to 75,631 tokens. Co-Authored-By: David Cramer <23610+dcramer@users.noreply.github.com> --- docs/operations/token-cost-tracking.md | 3 ++- packages/mcp-core/scripts/measure-token-cost.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/operations/token-cost-tracking.md b/docs/operations/token-cost-tracking.md index 3c043d4b9..a27dd4abe 100644 --- a/docs/operations/token-cost-tracking.md +++ b/docs/operations/token-cost-tracking.md @@ -8,6 +8,7 @@ The token cost of tool metadata that MCP sends to clients via `tools/list`: - Tool names and descriptions - Parameter schemas (JSON Schema) +- Output schemas (`outputSchema`, JSON Schema) for tools that declare one - Total overhead per tool and across all tools **Exclusions:** @@ -123,7 +124,7 @@ tsx measure-token-cost.ts --help # Show help - Remove redundant examples - Focus on unique, non-obvious details -**Simplify parameter schemas:** +**Simplify parameter and output schemas:** - Use `.describe()` sparingly - Avoid duplicate descriptions in nested schemas diff --git a/packages/mcp-core/scripts/measure-token-cost.ts b/packages/mcp-core/scripts/measure-token-cost.ts index 2807e2f67..c807f76f6 100644 --- a/packages/mcp-core/scripts/measure-token-cost.ts +++ b/packages/mcp-core/scripts/measure-token-cost.ts @@ -58,6 +58,7 @@ type ToolDefinition = { title: string; description: string | ((context: { experimentalMode: boolean }) => string); inputSchema: Record; + outputSchema?: ZodTypeAny; experimental?: boolean; hideInExperimentalMode?: boolean; deprecated?: { replacedBy: readonly string[] }; @@ -93,6 +94,13 @@ function formatToolsForMCP(tools: Record) { experimentalMode: false, }), inputSchema: jsonSchema, + // the same conversion the MCP SDK applies when it lists the tool + ...(tool.outputSchema && { + outputSchema: z.toJSONSchema(tool.outputSchema, { + io: "output", + target: "draft-7", + }), + }), ...(tool.annotations && { annotations: tool.annotations }), }; }); From 6bffd5425d97e2344c87ff1f76405c63e66971fe Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2026 04:44:57 +0000 Subject: [PATCH 2/2] fix(mcp): Measure outputSchema as draft-2020-12 @modelcontextprotocol/server's standardSchemaToJsonSchema(schema, "output") always targets draft-2020-12, so the draft-7 target made the report drift from the live tools/list. Refresh the stale numbers and tool-count limits in the token cost docs. Co-Authored-By: David Cramer <23610+dcramer@users.noreply.github.com> --- docs/operations/token-cost-tracking.md | 37 +++++++++---------- .../mcp-core/scripts/measure-token-cost.ts | 8 ++-- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/docs/operations/token-cost-tracking.md b/docs/operations/token-cost-tracking.md index a27dd4abe..f018f2cc7 100644 --- a/docs/operations/token-cost-tracking.md +++ b/docs/operations/token-cost-tracking.md @@ -28,9 +28,9 @@ pnpm run measure-tokens ``` 📊 MCP Server Token Cost Report ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -Total Tokens: 14,068 -Tool Count: 24 -Average/Tool: 586 +Total Tokens: 75,759 +Tool Count: 73 +Average/Tool: 1,038 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Per-Tool Breakdown: @@ -38,9 +38,9 @@ Per-Tool Breakdown: ┌─────────────────────────────┬────────┬─────────┐ │ Tool │ Tokens │ % Total │ ├─────────────────────────────┼────────┼─────────┤ -│ search_events │ 1192 │ 8.5% │ -│ update_issue │ 1166 │ 8.3% │ -│ search_docs │ 975 │ 6.9% │ +│ get_alert_rule │ 2517 │ 3.3% │ +│ find_releases │ 2033 │ 2.7% │ +│ update_alert_rule │ 1975 │ 2.6% │ ... ``` @@ -59,11 +59,11 @@ JSON format: ```json { - "total_tokens": 14068, - "tool_count": 24, - "avg_tokens_per_tool": 586, + "total_tokens": 75759, + "tool_count": 73, + "avg_tokens_per_tool": 1038, "tools": [ - {"name": "search_events", "tokens": 1192, "percentage": 8.5}, + {"name": "get_alert_rule", "tokens": 2517, "percentage": 3.3}, ... ] } @@ -88,19 +88,18 @@ GitHub Actions workflow runs on every PR and push to main: ## Understanding the Results -- ~14,068 tokens total -- ~586 tokens/tool average +Run `pnpm run measure-tokens` for current numbers; the examples above are a +snapshot and go stale. -**Tool count limits:** - -- **Target:** ≤20 tools (current best practice) -- **Maximum:** ≤25 tools (hard limit for AI agents) +**Tool count limits:** the gateway surface (`TOP_LEVEL_TOOL_NAMES`) targets ≤20 +tools, hard limit 25. `EXPOSE_ALL_TOOLS` lists every tool and relies on harness +tool search. **When to investigate:** -- Total tokens increase >10% without new tools -- Individual tool >1,000 tokens (indicates overly verbose descriptions) -- New tool adds >500 tokens (review description clarity) +- Total tokens increase >10% without new tools or new output schemas +- A tool sits well above the average (check the top of the per-tool table) +- A new tool, or a new output schema, adds far more than its neighbours ## Implementation Details diff --git a/packages/mcp-core/scripts/measure-token-cost.ts b/packages/mcp-core/scripts/measure-token-cost.ts index c807f76f6..57aab0c2b 100644 --- a/packages/mcp-core/scripts/measure-token-cost.ts +++ b/packages/mcp-core/scripts/measure-token-cost.ts @@ -94,12 +94,10 @@ function formatToolsForMCP(tools: Record) { experimentalMode: false, }), inputSchema: jsonSchema, - // the same conversion the MCP SDK applies when it lists the tool + // matches @modelcontextprotocol/server's + // standardSchemaToJsonSchema(schema, "output") (draft-2020-12) ...(tool.outputSchema && { - outputSchema: z.toJSONSchema(tool.outputSchema, { - io: "output", - target: "draft-7", - }), + outputSchema: z.toJSONSchema(tool.outputSchema, { io: "output" }), }), ...(tool.annotations && { annotations: tool.annotations }), };