|
| 1 | +# ha-mcp Context Reduction Research |
| 2 | + |
| 3 | +**Date:** 2026-02-14 |
| 4 | +**Related Issues:** #614, #567, #605 |
| 5 | +**Related PR:** #616 (progressive disclosure / `ha_get_tool_guide`) |
| 6 | +**Status:** Preliminary research — no implementation yet |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +## Key Insight: Round-Trip Token Cost Is a Red Herring |
| 11 | + |
| 12 | +The proxy/meta-tool approaches (Tool Search, Semantic Search) add 2-3 extra round trips per tool invocation. The instinct is that this "costs more tokens." **It doesn't — it saves massively.** |
| 13 | + |
| 14 | +### The Math |
| 15 | + |
| 16 | +**Current system (96 tools always loaded):** |
| 17 | +``` |
| 18 | +Every API turn pays ~35,000 tokens for tool definitions sitting in context. |
| 19 | +20-turn conversation: 20 × 35K = ~700K tokens just for idle tool defs. |
| 20 | +``` |
| 21 | + |
| 22 | +**Proxy pattern (3 meta-tools + on-demand lookups):** |
| 23 | +``` |
| 24 | +Every API turn pays ~2K tokens for 3 meta-tool definitions. |
| 25 | +20-turn conversation: 20 × 2K = ~40K tokens for tool defs. |
| 26 | +5 on-demand schema lookups × ~1K each = ~5K tokens. |
| 27 | +Total: ~45K tokens. |
| 28 | +``` |
| 29 | + |
| 30 | +**Result: ~93% fewer tokens overall.** The extra round trips add a small amount of tokens for the schema lookups, but this is dwarfed by eliminating 35K tokens of dead weight from every single turn. The dominant cost in the current system is paying for 96 tool definitions on every turn regardless of whether any of them get used. |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## The Problem |
| 35 | + |
| 36 | +ha-mcp v6.6.1 registers 96 tools. Their combined definitions consume: |
| 37 | + |
| 38 | +| Metric | Value | |
| 39 | +|--------|-------| |
| 40 | +| Total tokens (compact JSON) | ~35,500 | |
| 41 | +| Total characters | ~89,000 | |
| 42 | +| Context consumed on Claude (200K) | 17.8% | |
| 43 | +| Context consumed on GPT-4o (128K) | 27.8% | |
| 44 | +| Cost per request on Opus ($15/M input) | $0.53 | |
| 45 | + |
| 46 | +### Where tokens go: |
| 47 | +| Component | Tokens | % | |
| 48 | +|-----------|--------|---| |
| 49 | +| Tool descriptions | 21,487 | 48% | |
| 50 | +| Parameter schemas | 18,215 | 41% | |
| 51 | +| Annotations | 2,382 | 5% | |
| 52 | +| Structural JSON overhead | 2,101 | 5% | |
| 53 | +| Tool names | 537 | 1% | |
| 54 | + |
| 55 | +This creates two problems: |
| 56 | +1. **Idle context waste** — all 96 tools are loaded on every turn even when unused |
| 57 | +2. **Client-specific hard limits** — ChatGPT has a ~16K token limit for tool definitions (per #614 reporter), making ha-mcp completely unusable there |
| 58 | + |
| 59 | +--- |
| 60 | + |
| 61 | +## Approaches Researched |
| 62 | + |
| 63 | +### 1. PR #616: Progressive Disclosure (`ha_get_tool_guide`) |
| 64 | + |
| 65 | +**What it does:** Trims the 10 most verbose tool descriptions, moves full docs to an on-demand `ha_get_tool_guide()` meta-tool. Adds a required `guide_response` parameter to enforce the LLM reads the guide before calling the tool. |
| 66 | + |
| 67 | +**Results:** ~89K chars → ~68K chars (24% reduction on 10 tools). If expanded to all 96 tools, reduction would be much larger. |
| 68 | + |
| 69 | +**Pros:** |
| 70 | +- Works with every client (no special MCP features needed) |
| 71 | +- No architectural change to how tools are registered |
| 72 | +- Already implemented and passing CI |
| 73 | + |
| 74 | +**Cons:** |
| 75 | +- The `guide_response` required parameter creates a multi-step workflow (call guide → pipe output to tool) that weaker models (Qwen-7B, small Llama, etc.) may not follow |
| 76 | +- If the LLM ignores the guide instruction, it gets a one-liner description with no useful context — hard failure, no graceful degradation |
| 77 | +- Even fully expanded, still may not fit within ChatGPT's tool token limit |
| 78 | + |
| 79 | +**Maintainer feedback:** Concern about tool descriptions being "deleted or overlooked by the AI." Wants LLM testing before merge. |
| 80 | + |
| 81 | +### 2. Tool Search Tool Pattern (Proxy) |
| 82 | + |
| 83 | +**What it does:** Replace 96 individual tool registrations with 2-3 meta-tools: |
| 84 | +``` |
| 85 | +ha_search_tools(query, category?) → returns matching tool names + descriptions |
| 86 | +ha_get_tool_schema(tool_name) → returns full schema + docs for one tool |
| 87 | +ha_execute_tool(tool_name, args) → proxies the call to the real tool |
| 88 | +``` |
| 89 | + |
| 90 | +**Results:** ~1-2K tokens idle (3 tool definitions) vs ~35K today. **95%+ reduction.** |
| 91 | + |
| 92 | +**Pros:** |
| 93 | +- Works with every client and every model — just standard tool calls with simple parameters |
| 94 | +- Scales to any number of tools without growing context |
| 95 | +- Each step is a simple tool call (no multi-step piping like `guide_response`) |
| 96 | +- Even Qwen-7B can call `ha_search_tools("automation")` |
| 97 | +- Matches Anthropic's own recommended pattern for large tool libraries |
| 98 | + |
| 99 | +**Cons:** |
| 100 | +- Every tool call becomes 2-3 round trips (search → schema → execute), adding latency |
| 101 | +- `ha_execute_tool` takes freeform JSON args — MCP client can't validate against schema |
| 102 | +- Bigger architectural change (need tool registry, search index, proxy dispatcher) |
| 103 | +- LLM must correctly construct args from a schema it read a turn earlier (not inline) |
| 104 | + |
| 105 | +**Token math:** Extra round trips cost far less than loading all tools idle (see Key Insight above). |
| 106 | + |
| 107 | +**References:** |
| 108 | +- [Anthropic engineering blog on code execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp) — Anthropic's recommended approach |
| 109 | +- Anthropic saw Opus 4 accuracy improve from 49% to 74% with tool search |
| 110 | +- 85% token reduction while maintaining full tool access |
| 111 | + |
| 112 | +### 3. Semantic Search over Tool Embeddings |
| 113 | + |
| 114 | +**What it does:** Same as #2 but uses vector embeddings instead of keyword/prefix search: |
| 115 | +``` |
| 116 | +ha_find_tools("I want to create an automation that turns on lights at sunset") |
| 117 | +→ returns best matching tools ranked by semantic similarity |
| 118 | +``` |
| 119 | + |
| 120 | +**Results:** ~2K initial tokens. Even lower than prefix-based search. |
| 121 | + |
| 122 | +**Pros:** |
| 123 | +- Most natural for LLMs — describe what you want in plain language |
| 124 | +- Lower initial tokens than prefix search (~1,300 vs ~2,500 per Speakeasy benchmarks) |
| 125 | + |
| 126 | +**Cons:** |
| 127 | +- Requires an embedding model and index (though for 96 tools this is tiny — could be precomputed) |
| 128 | +- Embedding quality is critical — missed tools = silent failures |
| 129 | +- Less deterministic than explicit browsing |
| 130 | +- Extra dependency |
| 131 | + |
| 132 | +**References:** |
| 133 | +- [Speakeasy - 100x token reduction with dynamic toolsets](https://www.speakeasy.com/blog/100x-token-reduction-dynamic-toolsets) |
| 134 | + |
| 135 | +### 4. Hybrid: Core Tools + Proxy for the Rest |
| 136 | + |
| 137 | +**What it does:** Keep 10-15 most-used tools registered normally with full MCP schemas. Proxy the remaining 80+ tools through meta-tools. |
| 138 | + |
| 139 | +``` |
| 140 | +Always loaded (normal MCP tools with full schemas): |
| 141 | + ha_search_entities, ha_get_state, ha_get_entity, ha_call_service, |
| 142 | + ha_set_entity, ha_get_overview, ha_eval_template, ... |
| 143 | + ha_find_tools(query) ← discovers the rest |
| 144 | + ha_execute_tool(name, args) ← proxies the rest |
| 145 | +
|
| 146 | +Not loaded until discovered via ha_find_tools: |
| 147 | + ha_config_set_automation, ha_config_set_dashboard, ha_config_set_script, |
| 148 | + ha_get_history, ha_get_statistics, ha_manage_backups, ... (80+ tools) |
| 149 | +``` |
| 150 | + |
| 151 | +**Pros:** |
| 152 | +- Schema validation preserved for the most common tools |
| 153 | +- Massive context reduction for the long tail |
| 154 | +- Works with every client and every model |
| 155 | +- Essentially what Anthropic's `defer_loading` does, but implemented server-side so it's universal |
| 156 | + |
| 157 | +**Cons:** |
| 158 | +- Still a significant architectural change |
| 159 | +- Need to decide which tools are "core" vs "proxied" |
| 160 | + |
| 161 | +### 5. Dynamic Tool Registration (`listChanged`) |
| 162 | + |
| 163 | +**What it does:** Start with minimal tools, dynamically register/unregister tool modules at runtime. Emit `notifications/tools/list_changed` when tools change. |
| 164 | + |
| 165 | +**Client support (as of Feb 2026):** |
| 166 | +| Client | Supports `listChanged`? | |
| 167 | +|--------|------------------------| |
| 168 | +| Claude Code | Yes (confirmed in docs) | |
| 169 | +| Claude Desktop | Unknown — last confirmed "no" was Jul 2025, discussion closed | |
| 170 | +| Claude.ai | Unknown — no technical docs found | |
| 171 | +| ChatGPT | No — requires manual "Refresh" button | |
| 172 | +| Qwen Code | No mention in docs | |
| 173 | +| Gemini CLI | No — open issue requesting it | |
| 174 | +| GitHub Copilot | Yes | |
| 175 | + |
| 176 | +**Verdict:** Too many major clients don't support it to rely on as a primary strategy. |
| 177 | + |
| 178 | +### 6. `ENABLED_TOOL_MODULES` (Static Server Config) |
| 179 | + |
| 180 | +**What it does:** User sets which tool modules to load at startup via addon config. |
| 181 | + |
| 182 | +**Pros:** Universal compatibility, zero complexity. |
| 183 | +**Cons:** User must know what they need. Static — can't adapt per session. Requires server restart to change. Not AI-controlled. |
| 184 | + |
| 185 | +**Verdict:** Useful as a last-resort escape hatch for hard-limited clients (ChatGPT), but too clunky as a primary solution. |
| 186 | + |
| 187 | +### 7. `defer_loading` (Claude API Feature) |
| 188 | + |
| 189 | +**What it does:** Tools marked `defer_loading: true` are withheld from Claude's context by Anthropic's API. Claude uses a built-in Tool Search Tool to discover them on demand. |
| 190 | + |
| 191 | +**Key finding:** This is a **Claude API/platform feature**, NOT an MCP protocol feature. All tool definitions are still sent to the API — Anthropic's server-side infrastructure handles the filtering. Only works with Claude (Sonnet 4+, Opus 4+, no Haiku). Not usable by any other LLM. |
| 192 | + |
| 193 | +**References:** |
| 194 | +- [Claude API - Tool search tool docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) |
| 195 | +- [Anthropic - Introducing advanced tool use](https://www.anthropic.com/engineering/advanced-tool-use) |
| 196 | + |
| 197 | +--- |
| 198 | + |
| 199 | +## Comparison Matrix |
| 200 | + |
| 201 | +| Approach | Token Reduction | Works All Models | Works All Clients | Complexity | Round Trips | |
| 202 | +|----------|----------------|-----------------|-------------------|------------|-------------| |
| 203 | +| PR #616 (guide tool) | ~24% (expandable) | Strong models only | Yes | Low | +1 per guided tool | |
| 204 | +| Tool Search Proxy | ~95% | Yes | Yes | High | +2-3 per call | |
| 205 | +| Semantic Search | ~95%+ | Yes | Yes | High | +1-2 per call | |
| 206 | +| Hybrid (core + proxy) | ~80-90% | Yes | Yes | Medium-High | +2-3 for proxied tools | |
| 207 | +| Dynamic registration | ~60-80% | Yes | Only `listChanged` clients | Medium | +1 to load domain | |
| 208 | +| `ENABLED_TOOL_MODULES` | Variable | Yes | Yes | Trivial | 0 | |
| 209 | +| `defer_loading` | ~95% | Claude only | Claude API only | Low (client-side) | Built-in | |
| 210 | + |
| 211 | +--- |
| 212 | + |
| 213 | +## Recommended Direction |
| 214 | + |
| 215 | +**Primary approach: Tool Search Proxy (Option 2) or Hybrid (Option 4)** |
| 216 | + |
| 217 | +These provide the largest universal reduction while working across all clients and models. The extra round trips are a net token savings, not a cost (see Key Insight section). |
| 218 | + |
| 219 | +The Hybrid approach preserves schema validation for the most commonly used tools while dramatically reducing context for the long tail — essentially a server-side implementation of what `defer_loading` does for Claude only. |
| 220 | + |
| 221 | +**#616 as complementary:** The progressive disclosure pattern from PR #616 can still be applied to the core tools that remain always-loaded in the hybrid approach, further reducing their description sizes. |
| 222 | + |
| 223 | +**`ENABLED_TOOL_MODULES` as escape hatch:** Expose in addon config for users on hard-limited clients (ChatGPT) who need to manually reduce tool count. |
| 224 | + |
| 225 | +--- |
| 226 | + |
| 227 | +## Community Proposals & References |
| 228 | + |
| 229 | +- [MCP Discussion #532 - Hierarchical Tool Management](https://github.com/orgs/modelcontextprotocol/discussions/532) — Proposed spec extension (not adopted yet) |
| 230 | +- [MCP Discussion #76 - listChanged support](https://github.com/orgs/modelcontextprotocol/discussions/76) — Client support tracking |
| 231 | +- [Speakeasy - Progressive Discovery vs Semantic Search](https://www.speakeasy.com/blog/100x-token-reduction-dynamic-toolsets) — 100x token reduction benchmarks |
| 232 | +- [Klavis - 4 MCP Design Patterns](https://www.klavis.ai/blog/less-is-more-mcp-design-patterns-for-ai-agents) — Semantic search, workflow-based, code mode, progressive discovery |
| 233 | +- [Anthropic - Code Execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp) — Tool search tool pattern |
| 234 | +- [Anthropic - Context Engineering](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) — Minimum context principle |
| 235 | +- [Merge - MCP Tool Description Guide](https://www.merge.dev/blog/mcp-tool-description) — 1-2 sentence descriptions recommended |
| 236 | +- [Philipp Schmid - MCP Best Practices](https://www.philschmid.de/mcp-best-practices) — Context window as finite resource |
| 237 | + |
| 238 | +--- |
| 239 | + |
| 240 | +## Next Steps |
| 241 | + |
| 242 | +1. Investigate FastMCP's support for dynamic tool registration and proxy patterns |
| 243 | +2. Prototype the hybrid approach (core tools + proxy) in a branch |
| 244 | +3. Test with multiple models (Claude, Qwen, GPT) to validate compatibility |
| 245 | +4. Measure actual token usage with the proxy pattern vs current baseline |
| 246 | +5. Decide on search implementation (keyword/prefix vs semantic embeddings) |
0 commit comments