feat: Dynamic (lazy) mcp tool schema loading
What does this PR do?
- Implement lazy loading for MCP tools to reduce initial context bloat by 95%+
- Add mcp_load_tools meta-tool for on-demand tool loading
- Add mcp_lazy_load experimental config flag (off by default)
Problem
When multiple MCP servers are enabled, all tool definitions load into the agent's context at session startup. A single MCP server like GitHub can add 15-20k+ tokens just for tool schemas. With several MCPs enabled, context bloat can exceed 50k tokens before the user even sends a message. This is even mentioned in the docs: https://opencode.ai/docs/mcp-servers/
Solution
Lazy loading pattern: Instead of loading full tool schemas at startup, we:
- Show a lightweight index in the system prompt with server names and tool names only (~150 tokens vs ~1.5k)
- Provide a mcp_load_tools meta-tool that loads specific tools on-demand
- The agent loop naturally picks up newly loaded tools on the next iteration
Example Flow
System prompt shows: "playwright (20 tools): browser_snapshot, browser_click, browser_navigate, ..."
User: "Take a screenshot of google.com"
Agent calls: mcp_load_tools("playwright", ["browser_navigate", "browser_snapshot"]) → Tools loaded into session state
Agent calls: playwright_browser_navigate({ url: "https://google.com" }) Agent calls: playwright_browser_snapshot()
Changes File: config/config.ts Changes: Add mcp_lazy_load to experimental options ──────────────────────────────────────── File: session/index.ts Changes: Add mcpLoadedTools to Session.Info schema ──────────────────────────────────────── File: mcp/index.ts Changes: Add index(), loadToolsForSession(), modify tools() and create() ──────────────────────────────────────── File: session/system.ts Changes: Add mcpIndex() for system prompt generation ──────────────────────────────────────── File: session/prompt.ts Changes: Include MCP index in system prompt, pass session state to MCP.tools() ──────────────────────────────────────── File: tool/mcp-load-tools.ts Changes: New meta-tool implementation ──────────────────────────────────────── File: tool/registry.ts Changes: Register meta-tool conditionally
How did you verify your code works?
- Enable with "experimental": { "mcp_lazy_load": true } in config
- Verify MCP servers appear in system prompt with tool names
- Verify mcp_load_tools tool is available
- Ask agent to use MCP tools, confirm it calls mcp_load_tools first
- Verify loaded tools work correctly after loading
- Verify backward compatibility with flag disabled (default)
Closes #8277