-
Notifications
You must be signed in to change notification settings - Fork 11.8k
Description
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()duringInstance.state()initialization - All configured MCP servers with
enabled !== falseattempt to connect
- When a project loads, MCP clients are created via
-
Tool Definition Fetching (
packages/opencode/src/mcp/index.ts:566-606)MCP.tools()is called during every LLM call viaresolveTools()- 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 fetchingpackages/opencode/src/session/prompt.ts:730-909-resolveTools()functionpackages/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 server2Option 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
- MCP Protocol supports tool listing without full schema loading
- https://github.com/philschmid/mcp-cli