mcp_proxy
mcp_proxy
¶
MCP Proxy Service for managing MCP server subprocesses.
Provides MCP client functionality for Marianne, enabling tool discovery and execution through MCP servers. This is an IN-PROCESS manager, not a separate proxy process (see ADR-001 in architecture design).
The service: - Spawns MCP server subprocesses with proper lifecycle management - Handles JSON-RPC 2.0 communication over stdio pipes - Caches tool manifests with configurable TTL - Executes tools and translates results
Security Note: This module uses asyncio.create_subprocess_exec() which is the safe subprocess method in Python - it does NOT use shell=True, so there is no shell injection risk. Arguments are passed as a list, not interpolated into a shell command string. This mirrors the pattern in claude_cli.py.
Example usage
async with MCPProxyService(servers=[config]) as proxy: tools = await proxy.list_tools() result = await proxy.execute_tool("read_file", {"path": "/tmp/test"})
Classes¶
ContentBlock
dataclass
¶
Content block in tool results.
MCPTool
dataclass
¶
Represents an MCP tool definition.
ServerCapabilities
dataclass
¶
Capabilities reported by MCP server.
MCPConnection
dataclass
¶
MCPConnection(config, process, stdin, stdout, capabilities=None, tools=list(), last_tool_refresh=0.0)
Active connection to an MCP server.
ToolNotFoundError
¶
Bases: Exception
Raised when requested tool doesn't exist.
ToolExecutionTimeout
¶
Bases: Exception
Raised when tool execution times out.
MCPProxyService
¶
MCP client that manages server subprocesses and executes tools.
This service acts as an MCP CLIENT - it spawns and communicates with MCP SERVERS. Marianne's existing MCP server (for external integration) is separate from this client functionality.
Lifecycle
- start() - Spawn server processes and perform initialize handshake
- list_tools() - Get available tools (cached with TTL)
- execute_tool() - Call a specific tool
- stop() - Clean shutdown of all servers
Initialize MCP proxy service.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
servers
|
list[MCPServerConfig]
|
List of MCP server configurations to connect to |
required |
tool_cache_ttl
|
int
|
Seconds before refreshing tool list from servers |
300
|
request_timeout
|
float
|
Default timeout for JSON-RPC requests |
30.0
|
Source code in src/marianne/bridge/mcp_proxy.py
Functions¶
start
async
¶
Start all configured MCP servers.
For each server: 1. Spawn subprocess with asyncio.create_subprocess_exec 2. Send initialize request (JSON-RPC) 3. Wait for initialize response 4. Send initialized notification 5. Call tools/list to cache available tools
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If no servers could be started (total failure). |
Source code in src/marianne/bridge/mcp_proxy.py
stop
async
¶
Stop all MCP server subprocesses.
Sends SIGTERM and waits for graceful shutdown, then SIGKILL if needed.
Source code in src/marianne/bridge/mcp_proxy.py
__aenter__
async
¶
__aexit__
async
¶
Async context manager exit.
list_tools
async
¶
Get all available tools from all servers.
Uses cached tool list if within TTL, otherwise refreshes.
Returns:
| Type | Description |
|---|---|
list[MCPTool]
|
List of MCPTool objects from all connected servers |
Source code in src/marianne/bridge/mcp_proxy.py
execute_tool
async
¶
Execute a tool by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tool_name
|
str
|
Name of the tool to execute |
required |
arguments
|
dict[str, Any]
|
Arguments to pass to the tool |
required |
Returns:
| Type | Description |
|---|---|
ToolResult
|
ToolResult with content and error status |
Raises:
| Type | Description |
|---|---|
ToolNotFoundError
|
If tool doesn't exist |
ToolExecutionTimeout
|
If execution times out |