Feature Request: Two-Step MCP Tool Discovery for On-Demand Loading
Summary
Implement a two-step MCP tool discovery mechanism where tool definitions are loaded lazily on-demand rather than eagerly fetching all tools from all connected MCP servers on every LLM call.
Current Implementation
Tool Resolution Flow
-
MCP Client Connection (packages/opencode/src/mcp/index.ts:183)
- When a project loads, MCP clients are created via
create() during Instance.state() initialization
- All configured MCP servers with
enabled !== false attempt to connect
-
Tool Definition Fetching (packages/opencode/src/mcp/index.ts:566-606)
MCP.tools() is called during every LLM call via resolveTools()
- For each connected MCP client, it calls
client.listTools() to fetch ALL tool definitions
- No caching mechanism exists - tools are fetched fresh on every call
// mcp/index.ts:578-604
const toolsResults = await Promise.all(
connectedClients.map(async ([clientName, client]) => {
const toolsResult = await client.listTools() // Fetches ALL tools
// ...
for (const mcpTool of toolsResult.tools) {
result[sanitizedClientName + "_" + sanitizedToolName] = await convertMcpTool(...)
}
}),
)
-
Tools Passed to LLM (packages/opencode/src/session/llm.ts:207-208)
- ALL built-in tools + ALL MCP tools are passed to the model
- No filtering or selection mechanism exists
// llm.ts:207-208
activeTools: Object.keys(tools).filter((x) => x !== "invalid"),
tools,
Key Files
packages/opencode/src/mcp/index.ts - MCP client management and tool fetching
packages/opencode/src/session/prompt.ts:730-909 - resolveTools() function
packages/opencode/src/session/llm.ts:154-208 - Tool resolution and passing to LLM
The Problem
-
Context Bloat: Every LLM call includes ALL tool definitions from ALL connected MCP servers, even if only a small subset is relevant to the current task.
-
No Lazy Loading: Tools are fetched eagerly - there's no mechanism to defer loading tool definitions until they're actually needed.
-
Performance Overhead: Each LLM call triggers listTools() for every connected MCP server, even if the tools won't be used.
-
Scalability Issues: As users add more MCP servers with many tools, the context size grows linearly, potentially hitting model token limits.
Proposed Solution: Two-Step Tool Discovery
Step 1: Tool Listing (Metadata Only)
When resolving tools for an LLM call:
- First, fetch only the tool names and descriptions from MCP servers (not full schemas)
- Store this metadata in a cache with TTL
- Pass only tool names/descriptions to the model (lightweight)
This step would use MCP's existing tools/list method but would cache results.
Step 2: On-Demand Tool Loading
- The model decides which tools to use based on the lightweight metadata
- When a tool is actually invoked, fetch its full definition (input schema, description)
- Cache the full definition for subsequent calls
Alternatively, implement a tool selection mechanism where:
- User/agent explicitly specifies which MCP servers to enable per task
- Or context-aware filtering based on task keywords
Implementation Approach
Option A: Cached Tool Listings
// Pseudocode
async function resolveTools() {
// Step 1: Get lightweight tool listings (cached)
const toolMeta = await MCP.toolMetadata() // Only names + descriptions
// Step 2: Pass metadata to model
// Model selects tools...
// Step 3: On actual invocation, load full definition
const fullTool = await MCP.getToolDefinition(toolName)
}
Option B: Explicit Tool Selection
Add configuration to enable/disable MCP servers per context:
// In opencode.json or session config
{
"mcp": {
"server1": { "type": "remote", "url": "...", "enabled": false },
"server2": { "type": "remote", "url": "...", "enabled": true }
}
}
// Or dynamically in session
session.mcpServers = ["server2"] // Only load tools from server2
Option C: Context-Aware Filtering
Implement intelligent filtering based on:
- Task description/keywords
- File types being edited
- Recent tool usage patterns
Benefits
- Reduced Context Size: Only relevant tool definitions are loaded
- Faster LLM Calls: Less data to serialize and send to model
- Better Scalability: Users can connect more MCP servers without performance degradation
- Token Savings: More room for actual code/context in prompts
Backward Compatibility
- Default to current behavior (load all tools) for existing configurations
- New behavior opt-in via config flag:
experimental.mcp_lazy_loading: true
Related
Feature Request: Two-Step MCP Tool Discovery for On-Demand Loading
Summary
Implement a two-step MCP tool discovery mechanism where tool definitions are loaded lazily on-demand rather than eagerly fetching all tools from all connected MCP servers on every LLM call.
Current Implementation
Tool Resolution Flow
MCP Client Connection (
packages/opencode/src/mcp/index.ts:183)create()duringInstance.state()initializationenabled !== falseattempt to connectTool Definition Fetching (
packages/opencode/src/mcp/index.ts:566-606)MCP.tools()is called during every LLM call viaresolveTools()client.listTools()to fetch ALL tool definitionsTools Passed to LLM (
packages/opencode/src/session/llm.ts:207-208)Key Files
packages/opencode/src/mcp/index.ts- MCP client management and tool fetchingpackages/opencode/src/session/prompt.ts:730-909-resolveTools()functionpackages/opencode/src/session/llm.ts:154-208- Tool resolution and passing to LLMThe Problem
Context Bloat: Every LLM call includes ALL tool definitions from ALL connected MCP servers, even if only a small subset is relevant to the current task.
No Lazy Loading: Tools are fetched eagerly - there's no mechanism to defer loading tool definitions until they're actually needed.
Performance Overhead: Each LLM call triggers
listTools()for every connected MCP server, even if the tools won't be used.Scalability Issues: As users add more MCP servers with many tools, the context size grows linearly, potentially hitting model token limits.
Proposed Solution: Two-Step Tool Discovery
Step 1: Tool Listing (Metadata Only)
When resolving tools for an LLM call:
This step would use MCP's existing
tools/listmethod but would cache results.Step 2: On-Demand Tool Loading
Alternatively, implement a tool selection mechanism where:
Implementation Approach
Option A: Cached Tool Listings
Option B: Explicit Tool Selection
Add configuration to enable/disable MCP servers per context:
Option C: Context-Aware Filtering
Implement intelligent filtering based on:
Benefits
Backward Compatibility
experimental.mcp_lazy_loading: trueRelated