[GH-ISSUE #7155] MCP tools/list is 3.16 MB - outputSchema is 74.5% of it` #4290

Open
opened 2026-10-31 00:48:33 +00:00 by bsakaguchi · 6 comments
Owner

Originally created by @joneminiss on GitHub (Oct 25, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7155

Summary

tools/list returns 207 tools / 3,158,352 bytes. Clients that keep the catalog in every request (Claude Code, Cline, Roo, Codex, Cursor, DeepSeek Harness) then spend ~215,000 tokens every turn regardless of the prompt. On 200k-context models REA eats the entire budget before the first user message; on 128k it will not load at all.

Measured on rea-agents@6.7.0, Windows 11 x64, Node 24.22.1, Ghidra 12.1.4, rea mcp over stdio, protocolVersion: 2024-11-05.

Byte composition

Component Bytes Share
outputSchema 2,353,893 74.5%
inputSchema 667,641 21.1%
description 92,265 2.9%
annotations 18,534 0.6%
title 4,078 0.1%

Three quarters of the payload is outputSchema.

Largest tools

Tool Total output input
compare_web_captures 181,042 14,248 165,694
inspect_analysis_view 170,393 137,985 25,282
compare_application_versions 62,206 31,285 30,271
capture_browser_scenario 58,724 36,152 21,352
reconcile_javascript_runtime 46,395 30,167 15,489

Top 10 tools = 727,403 bytes = 23.0%.

Cross-tool duplication

Hashing every inputSchema sub-object over 800 bytes:

  • 38 fragments appear in more than one tool
  • Redundant bytes: 1,083,810 (34% of the catalog)
  • One 6,941-byte fragment x 23 tools = 152,702 redundant bytes
  • One 7,249-byte fragment x 18 tools = 123,233 redundant bytes

"evidence_id" appears 542 times because the Evidence envelope is inlined everywhere instead of referenced.

Suggestions, by impact per effort

1. Make outputSchema opt-in (-74.5%). A model cannot use an output schema before calling the tool, and most clients do not render it. Omit by default behind --full-schemas, or publish once out of band and $ref it.

2. Let the server filter its own list. Today a client wanting 20 tools must wrap the server:

rea mcp --tools binary_session,analyze_function,xrefs
rea mcp --tool-groups binary,android
rea mcp --tools-file ./rea-tools.json

3. Factor out the Evidence envelope (-34%). One canonical $defs.Evidence + $defs.Location, $ref from each tool. Standard JSON Schema. Removes over 1 MB.

4. Group the compare_* / project_* families. compare_web_captures is 181 KB because it inlines the whole inspect_web_page input shape twice. Use a single compare(kind, left, right), or accept evidence_id instead of inline Evidence. You already return evidence_id from every call, so accept it back.

5. Add rea mcp --tier minimal|standard|full. minimal (~15 core tools) should land under ~50 KB.

6. Add rea mcp --print-schema-stats. You already have --token-count; let users see this before wiring it in.

Workaround I used

A stdio proxy filtering tools/list by allow-list, passing tools/call through. 207 -> 29 tools took the catalog from ~215k tokens to ~6.1k with no capability loss. I then found the CLI covers the same ground and dropped MCP entirely (206 commands, zero resident tools).

That works, but every downstream user should not have to write it.

Reproduce

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}

Measure JSON.stringify(response.result.tools).length.


Happy to PR any of these. $defs factoring (#3) and the --tools allow-list (#2) both look self-contained. Thanks for REA: the CLI surface is excellent and the Evidence model is good design, only the wire format at tools/list needs trimming.

Originally created by @joneminiss on GitHub (Oct 25, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7155 ## Summary `tools/list` returns **207 tools / 3,158,352 bytes**. Clients that keep the catalog in every request (Claude Code, Cline, Roo, Codex, Cursor, DeepSeek Harness) then spend ~**215,000 tokens every turn** regardless of the prompt. On 200k-context models REA eats the entire budget before the first user message; on 128k it will not load at all. Measured on `rea-agents@6.7.0`, Windows 11 x64, Node 24.22.1, Ghidra 12.1.4, `rea mcp` over stdio, `protocolVersion: 2024-11-05`. ## Byte composition | Component | Bytes | Share | |---|---:|---:| | `outputSchema` | 2,353,893 | **74.5%** | | `inputSchema` | 667,641 | 21.1% | | `description` | 92,265 | 2.9% | | `annotations` | 18,534 | 0.6% | | `title` | 4,078 | 0.1% | Three quarters of the payload is `outputSchema`. ## Largest tools | Tool | Total | output | input | |---|---:|---:|---:| | `compare_web_captures` | 181,042 | 14,248 | 165,694 | | `inspect_analysis_view` | 170,393 | 137,985 | 25,282 | | `compare_application_versions` | 62,206 | 31,285 | 30,271 | | `capture_browser_scenario` | 58,724 | 36,152 | 21,352 | | `reconcile_javascript_runtime` | 46,395 | 30,167 | 15,489 | Top 10 tools = 727,403 bytes = **23.0%**. ## Cross-tool duplication Hashing every `inputSchema` sub-object over 800 bytes: - **38 fragments** appear in more than one tool - Redundant bytes: **1,083,810** (34% of the catalog) - One 6,941-byte fragment x 23 tools = 152,702 redundant bytes - One 7,249-byte fragment x 18 tools = 123,233 redundant bytes `"evidence_id"` appears **542 times** because the Evidence envelope is inlined everywhere instead of referenced. ## Suggestions, by impact per effort **1. Make `outputSchema` opt-in (-74.5%).** A model cannot use an output schema before calling the tool, and most clients do not render it. Omit by default behind `--full-schemas`, or publish once out of band and `$ref` it. **2. Let the server filter its own list.** Today a client wanting 20 tools must wrap the server: ``` rea mcp --tools binary_session,analyze_function,xrefs rea mcp --tool-groups binary,android rea mcp --tools-file ./rea-tools.json ``` **3. Factor out the Evidence envelope (-34%).** One canonical `$defs.Evidence` + `$defs.Location`, `$ref` from each tool. Standard JSON Schema. Removes over 1 MB. **4. Group the `compare_*` / `project_*` families.** `compare_web_captures` is 181 KB because it inlines the whole `inspect_web_page` input shape twice. Use a single `compare(kind, left, right)`, or accept `evidence_id` instead of inline Evidence. You already return `evidence_id` from every call, so accept it back. **5. Add `rea mcp --tier minimal|standard|full`.** `minimal` (~15 core tools) should land under ~50 KB. **6. Add `rea mcp --print-schema-stats`.** You already have `--token-count`; let users see this before wiring it in. ## Workaround I used A stdio proxy filtering `tools/list` by allow-list, passing `tools/call` through. 207 -> 29 tools took the catalog from ~215k tokens to **~6.1k** with no capability loss. I then found the CLI covers the same ground and dropped MCP entirely (206 commands, zero resident tools). That works, but every downstream user should not have to write it. ## Reproduce ```json {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}} {"jsonrpc":"2.0","method":"notifications/initialized"} {"jsonrpc":"2.0","id":2,"method":"tools/list"} ``` Measure `JSON.stringify(response.result.tools).length`. --- Happy to PR any of these. `$defs` factoring (#3) and the `--tools` allow-list (#2) both look self-contained. Thanks for REA: the CLI surface is excellent and the Evidence model is good design, only the wire format at `tools/list` needs trimming.
Author
Owner

@Navneet14-cmd commented on GitHub (Oct 25, 2026):

I’d like to take #2. Proposal: rea mcp --tools a,b,c and --tool-groups , default behavior unchanged. Unknown tool names fail at startup with a clear error, and tools/call for a filtered-out tool returns the standard unknown-tool error. Happy to adjust names or semantics before I start. I’ll leave #3 for a separate PR if you want it.

<!-- gh-comment-id:6400862778 --> @Navneet14-cmd commented on GitHub (Oct 25, 2026): I’d like to take #2. Proposal: rea mcp --tools a,b,c and --tool-groups <names>, default behavior unchanged. Unknown tool names fail at startup with a clear error, and tools/call for a filtered-out tool returns the standard unknown-tool error. Happy to adjust names or semantics before I start. I’ll leave #3 for a separate PR if you want it.
Author
Owner

@definitelynotchirag commented on GitHub (Oct 25, 2026):

Two of these already exist, in case you missed them on 6.7.0. REA_MCP_OUTPUT_SCHEMA_PROFILE=off in the server env drops every outputSchema from tools/list, which is your suggestion 1. REA_MCP_INPUT_SCHEMA_PROFILE=compact trims the inputs. Both are in docs/client-configuration.md. Your suggestion 2 is close to the open PR #3026, which filters the advertised list by family.

One thing to be aware of from the other direction: #7154 tags every tool with its family in _meta, which adds about 14 KB to the list (0.3 to 0.4% depending on profile). That's the vocabulary a --tool-groups flag could key on, so it should help here rather than hurt, but it is a few more bytes.

<!-- gh-comment-id:6400912063 --> @definitelynotchirag commented on GitHub (Oct 25, 2026): Two of these already exist, in case you missed them on 6.7.0. `REA_MCP_OUTPUT_SCHEMA_PROFILE=off` in the server env drops every outputSchema from tools/list, which is your suggestion 1. `REA_MCP_INPUT_SCHEMA_PROFILE=compact` trims the inputs. Both are in docs/client-configuration.md. Your suggestion 2 is close to the open PR #3026, which filters the advertised list by family. One thing to be aware of from the other direction: #7154 tags every tool with its family in `_meta`, which adds about 14 KB to the list (0.3 to 0.4% depending on profile). That's the vocabulary a `--tool-groups` flag could key on, so it should help here rather than hurt, but it is a few more bytes.
Author
Owner

@ashishgit4 commented on GitHub (Oct 25, 2026):

/assign

<!-- gh-comment-id:6401157198 --> @ashishgit4 commented on GitHub (Oct 25, 2026): /assign
Author
Owner

@XinwangTao commented on GitHub (Oct 25, 2026):

The 3.16 MB catalog is brutal for 128k-context models — outputSchema alone eating 74.5% makes the case for making it opt-in very strong. On top of that, the cross-tool duplication data (1 MB+ redundant, evidence_id inlined 542 times) suggests switching to shared schema references (JSON Schema $defs/$ref or equivalent) would cut payload size dramatically without losing information. Definitely a bug worth prioritizing.

<!-- gh-comment-id:6401942891 --> @XinwangTao commented on GitHub (Oct 25, 2026): The 3.16 MB catalog is brutal for 128k-context models — outputSchema alone eating 74.5% makes the case for making it opt-in very strong. On top of that, the cross-tool duplication data (1 MB+ redundant, `evidence_id` inlined 542 times) suggests switching to shared schema references (JSON Schema `$defs`/`$ref` or equivalent) would cut payload size dramatically without losing information. Definitely a bug worth prioritizing.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Current native-investigation catalog capture

A fresh stdio MCP SDK tools/list capture on REA b43c329df779708e43ae297f498a844e45d9c923 (server and contract source unchanged at checked main 23de10bb2849cdce8ddbe70504aa7cc57de75a98) returned these exact serialized SDK result sizes. Estimated tokens use tiktoken 0.12.0 o200k_base over the entire serialized result; they are not measured host prompt usage.

Configuration Tools Serialized bytes Estimated tokens
Default 233 3,907,046 1,037,315
Compact output schema 233 572,979 123,924
Output schema off 233 316,026 68,097
session,direct,enhanced,application and compact output 100 234,338 49,880

In the default response, inspect_analysis_view alone occupied 594,287 bytes including a 578,094-byte output schema; batch_decompile occupied 21,486 bytes including 20,564 output-schema bytes. The compact output profile widens advertised schemas, while the scoped profile omits other tools; those are real contract/capability tradeoffs, not cost-free equivalent catalogs. The SDK received the complete payloads successfully here; no client line limit, autonomous tool-selection outcome, per-turn prompt cost, or analysis latency was measured. Host behavior determines which fields, if any, enter a model prompt. This refreshes the earlier capture at the newer source baseline rather than establishing a performance regression from a matched before/after workload.

<!-- gh-comment-id:6544466553 --> @morluto commented on GitHub (Oct 31, 2026): ### Current native-investigation catalog capture A fresh stdio MCP SDK `tools/list` capture on REA `b43c329df779708e43ae297f498a844e45d9c923` (server and contract source unchanged at checked main `23de10bb2849cdce8ddbe70504aa7cc57de75a98`) returned these exact serialized SDK result sizes. Estimated tokens use `tiktoken` 0.12.0 `o200k_base` over the entire serialized result; they are **not** measured host prompt usage. | Configuration | Tools | Serialized bytes | Estimated tokens | | --- | ---: | ---: | ---: | | Default | 233 | 3,907,046 | 1,037,315 | | Compact output schema | 233 | 572,979 | 123,924 | | Output schema off | 233 | 316,026 | 68,097 | | `session,direct,enhanced,application` and compact output | 100 | 234,338 | 49,880 | In the default response, `inspect_analysis_view` alone occupied 594,287 bytes including a 578,094-byte output schema; `batch_decompile` occupied 21,486 bytes including 20,564 output-schema bytes. The compact output profile widens advertised schemas, while the scoped profile omits other tools; those are real contract/capability tradeoffs, not cost-free equivalent catalogs. The SDK received the complete payloads successfully here; no client line limit, autonomous tool-selection outcome, per-turn prompt cost, or analysis latency was measured. Host behavior determines which fields, if any, enter a model prompt. This refreshes the earlier capture at the newer source baseline rather than establishing a performance regression from a matched before/after workload.
Author
Owner

@XinwangTao commented on GitHub (Nov 4, 2026):

morluto 这份 capture 把问题的重心讲清楚了:scoped + compact output 组合已经能把 233 个工具压到 100 个 / ~50K tokens,但默认配置仍然是全量 1M+ tokens。结合 #5861 的「compact 存在但默认关闭」和 #7654 的「文档遗漏该选项」,核心矛盾其实是「可用配置没有成为默认路径」。#8388 的发现性设计是对的——建议同时考虑:1) setup/首次启动引导中默认勾选 compact 或按场景生成 scoped profile;2) 让默认 tools/list 至少应用 compact output schema(把 3.9MB 降到 573KB,客户端仍然拿到全部工具,只是省掉绝大多数 outputSchema)。这样对 128K 模型的可用性提升立竿见影,且不牺牲能力声明的完整性。

<!-- gh-comment-id:6622361369 --> @XinwangTao commented on GitHub (Nov 4, 2026): morluto 这份 capture 把问题的重心讲清楚了:scoped + compact output 组合已经能把 233 个工具压到 100 个 / ~50K tokens,但默认配置仍然是全量 1M+ tokens。结合 #5861 的「compact 存在但默认关闭」和 #7654 的「文档遗漏该选项」,核心矛盾其实是「可用配置没有成为默认路径」。#8388 的发现性设计是对的——建议同时考虑:1) setup/首次启动引导中默认勾选 compact 或按场景生成 scoped profile;2) 让默认 tools/list 至少应用 compact output schema(把 3.9MB 降到 573KB,客户端仍然拿到全部工具,只是省掉绝大多数 outputSchema)。这样对 128K 模型的可用性提升立竿见影,且不牺牲能力声明的完整性。
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4290
No description provided.