Mods: `$.tool.call` type-checking cost grows with the number of connected MCP tools (8M instantiations, 1.4 GB for one mod)
What happenstsc --noEmit on a mod with 20 $.tool.call(...) sites (tests included) takes 5–9 s and 1.4 GB (8.0M instantiations). The generated claude-code-mcp types declare 437 MCP tools (all servers connected in the session that wrote them); the mod calls one of them.
Measurements (TypeScript 7.0.2, Claude Code 2.1.300, same mod, --extendedDiagnostics):
| McpToolInputs entries | Instantiations | Check time |
|---|---|---|
| 437 (as generated) | 8.0M | 4–9 s, 1.4 GB |
| 49 (one server only) | 0.63M | 0.8 s |
| 0 | 0.28M | 0.2 s, 75 MB |
Roughly 12k instantiations per MCP tool, multiplied by the number of call sites.
Cause (as far as I can tell)
The first ToolCallOverloads signature checks the argument against ToolCallArgs & ToolNamed<T>, where ToolCallArgs distributes Omit<…> over the whole builtin + MCP union. Every call site compares against every tool. Replacing that signature, on a copy of the generated types, with one indexed by the tool name (<T extends …>(input: { tool: T } & InputFor<T>)) and dropping the wide fallback brought the same mod to 1.9M instantiations / 2.2 s with no errors. The remaining cost is in on(event, { tool: "…" }, handler) filters (ToolNamed<T> over the same union). Dropping the fallback breaks non-literal tool names, so this is a pointer, not a patch.
Side effect
Because claude-code-mcp is a snapshot of the writing session's connected servers, the same mod type-checks differently depending on which session last loaded it (e.g. a server that timed out on connect is missing).
Expected
Per-call cost independent of the number of declared tools (index by name), and/or a way for a mod to declare only the MCP tools it uses.
Related: #100736, #99771.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗