Skip to content

Commit 0e3d1dc

Browse files
kingpanther13claude
andcommitted
research: context reduction strategies for ha-mcp tool token bloat
Summary of research into approaches for reducing the ~35K token idle cost of 96 tool definitions. Key finding: proxy/meta-tool patterns (Tool Search, Semantic Search) add round trips but save ~93% tokens overall because the dominant cost is idle tool definitions on every turn, not the occasional schema lookup. Covers: PR homeassistant-ai#616, tool search proxy, semantic search, hybrid core+proxy, dynamic registration, defer_loading, ENABLED_TOOL_MODULES. Related: homeassistant-ai#614, homeassistant-ai#567, homeassistant-ai#605, PR homeassistant-ai#616 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 99cec2f commit 0e3d1dc

1 file changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
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

Comments
 (0)