Why now
MCP is the standard interface for connecting LLMs to external tools. Wrapping each MCP server manually via defineTool() is unnecessary friction.
See updated DECISIONS.md for full rationale.
Design
Core idea
One function that connects to an MCP server, discovers its tools, and registers them as standard ToolDefinitions in the existing ToolRegistry. No changes to the agent loop or tool executor.
import { connectMCPTools } from 'open-multi-agent/mcp'
const tools = await connectMCPTools({
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-github'],
env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN },
})
const agent = await oma.runAgent({
name: 'github-agent',
model: 'claude-sonnet-4-20250514',
tools: tools.map(t => t.name),
}, prompt)
Integration surface
| Component |
Change |
New src/tool/mcp.ts |
Connect to MCP server, convert tools, manage lifecycle |
ToolDefinition |
MCP tools use z.any() for inputSchema (MCP server handles its own validation) |
package.json |
@modelcontextprotocol/sdk as optional peerDependency |
| Agent config |
No changes needed. MCP tools are just tools |
Transport
Support stdio (local processes) first. SSE/streamable HTTP can follow if there's demand.
Lifecycle
connectMCPTools() returns tools + a disconnect() handle. User is responsible for cleanup. No magic.
Open questions
- Tool naming collisions -- if an MCP server exposes a tool named
bash, it conflicts with built-in. Namespace prefix (e.g. mcp_github/bash) or let user handle it?
- Reconnection -- should we auto-reconnect on stdio crash, or just surface the error?
Acceptance criteria
Non-goals
- SSE transport (follow-up)
- MCP resources/prompts (tools only for now)
- Auto-discovery of MCP servers
Why now
MCP is the standard interface for connecting LLMs to external tools. Wrapping each MCP server manually via
defineTool()is unnecessary friction.See updated DECISIONS.md for full rationale.
Design
Core idea
One function that connects to an MCP server, discovers its tools, and registers them as standard
ToolDefinitions in the existingToolRegistry. No changes to the agent loop or tool executor.Integration surface
src/tool/mcp.tsToolDefinitionz.any()for inputSchema (MCP server handles its own validation)package.json@modelcontextprotocol/sdkas optionalpeerDependencyTransport
Support stdio (local processes) first. SSE/streamable HTTP can follow if there's demand.
Lifecycle
connectMCPTools()returns tools + adisconnect()handle. User is responsible for cleanup. No magic.Open questions
bash, it conflicts with built-in. Namespace prefix (e.g.mcp_github/bash) or let user handle it?Acceptance criteria
connectMCPTools(config)function that returnsToolDefinition[]+disconnect()@modelcontextprotocol/sdkas optional peer dep (zero impact when unused)Non-goals