[PR #8177] feat: Dynamic (lazy) mcp tool schema loading #12647

Open
opened 2026-02-16 18:17:32 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/anomalyco/opencode/pull/8177

State: open
Merged: No


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:

  1. Show a lightweight index in the system prompt with server names and tool names only (~150 tokens vs ~1.5k)
  2. Provide a mcp_load_tools meta-tool that loads specific tools on-demand
  3. 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)
Screenshot 2026-01-13 at 1 16 27 AM

Closes #8277

**Original Pull Request:** https://github.com/anomalyco/opencode/pull/8177 **State:** open **Merged:** No --- ### 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: 1. Show a lightweight index in the system prompt with server names and tool names only (~150 tokens vs ~1.5k) 2. Provide a mcp_load_tools meta-tool that loads specific tools on-demand 3. 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) <img width="674" height="553" alt="Screenshot 2026-01-13 at 1 16 27 AM" src="https://github.com/user-attachments/assets/5d716854-cce6-4dd4-86a3-ac952d22960e" /> Closes #8277
yindo added the pull-request label 2026-02-16 18:17:32 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#12647