symfonic.tools.mcp¶
mcp ¶
symfonic.tools.mcp -- MCP (Model Context Protocol) server adapter.
Provides: - MCPToolDefinition / MCPToolResult -- frozen dataclass models - MCPServerConnection -- structural Protocol (runtime_checkable) - JSONRPCMCPConnection -- HTTP JSON-RPC 2.0 transport (requires httpx) - MCPToolProvider -- discovery + routing bridge to LangChain
JSONRPCMCPConnection ¶
MCP server connection via JSON-RPC 2.0 over HTTP.
Implements the MCP wire protocol: - tools/list โ discover available tools - tools/call โ invoke a tool by name
Requires httpx::
pip install symfonic-core[mcp]
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
server_url
|
str
|
Full HTTP(S) URL to the MCP server endpoint. |
required |
Example::
conn = JSONRPCMCPConnection("http://localhost:3000/mcp")
tools = await conn.list_tools()
result = await conn.call_tool("search", {"query": "force majeure"})
Source code in symfonic/tools/mcp/connection.py
call_tool
async
¶
Send tools/call request and return parsed result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tool_name
|
str
|
Name of the tool to invoke. |
required |
arguments
|
dict[str, Any]
|
Arguments matching the tool's input schema. |
required |
Returns:
| Type | Description |
|---|---|
MCPToolResult
|
MCPToolResult with concatenated text content from the response. |
Source code in symfonic/tools/mcp/connection.py
close
async
¶
Release connection state and close the pooled HTTP client.
Source code in symfonic/tools/mcp/connection.py
list_tools
async
¶
Send tools/list request and parse response.
A row without a usable name is skipped with a warning rather than
raising. tool_data["name"] used to be an unguarded index, and since
MCPToolProvider.discover_tools catches per server, a single
malformed entry discarded every other tool that server advertised.
Returns:
| Type | Description |
|---|---|
list[MCPToolDefinition]
|
List of MCPToolDefinition objects for each well-formed tool the |
list[MCPToolDefinition]
|
server exposes. |
Source code in symfonic/tools/mcp/connection.py
MCPServerConnection ¶
Bases: Protocol
Abstract interface for connecting to MCP servers.
Any object that implements list_tools, call_tool, and close
with the correct signatures satisfies this protocol via structural
subtyping โ no inheritance required.
Example::
class MyConnection:
async def list_tools(self) -> list[MCPToolDefinition]: ...
async def call_tool(self, tool_name, arguments) -> MCPToolResult: ...
async def close(self) -> None: ...
assert isinstance(MyConnection(), MCPServerConnection)
call_tool
async
¶
Execute a tool on the server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tool_name
|
str
|
Name of the tool to invoke. |
required |
arguments
|
dict[str, Any]
|
Key-value arguments matching the tool's input schema. |
required |
Returns:
| Type | Description |
|---|---|
MCPToolResult
|
MCPToolResult with the server's response content. |
Source code in symfonic/tools/mcp/protocol.py
close
async
¶
list_tools
async
¶
Discover available tools from the server.
Returns:
| Type | Description |
|---|---|
list[MCPToolDefinition]
|
List of tool definitions reported by the MCP server. |
MCPToolDefinition
dataclass
¶
MCPToolDefinition(name: str, description: str, input_schema: dict[str, Any] = dict(), server_url: str = '')
A tool discovered from an MCP server.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Unique tool identifier as reported by the server. |
description |
str
|
Human-readable description of what the tool does. |
input_schema |
dict[str, Any]
|
JSON Schema object describing accepted arguments. |
server_url |
str
|
URL of the MCP server that owns this tool. |
MCPToolProvider ¶
Bridge that connects to MCP servers and provides LangChain-compatible tools.
Registers one or more MCPServerConnection instances, discovers their tools, and routes tool calls to the correct server.
Usage::
provider = MCPToolProvider()
provider.add_server("legal", JSONRPCMCPConnection("http://localhost:3000"))
definitions = await provider.discover_tools()
lc_tools = provider.to_langchain_tools()
# Later, invoke a tool directly:
result = await provider.call_tool("search", {"query": "breach of contract"})
# Cleanup:
await provider.close_all()
Source code in symfonic/tools/mcp/provider.py
add_server ¶
Register an MCP server connection under a logical name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Logical label for this server (used in log messages). |
required |
connection
|
MCPServerConnection
|
Any object satisfying MCPServerConnection protocol. |
required |
Source code in symfonic/tools/mcp/provider.py
call_tool
async
¶
Execute a previously discovered tool by name.
The call goes to the connection the tool was discovered on, and is
placed under the name that server advertised. When a name collided
the exposed name carries a "{server}." prefix that no server ever
sent, so passing the exposed name back over the wire would be a call
for a tool that does not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tool_name
|
str
|
Name of the tool to invoke, as exposed by
:meth: |
required |
arguments
|
dict[str, Any]
|
Arguments to pass to the tool. |
required |
Returns:
| Type | Description |
|---|---|
MCPToolResult
|
MCPToolResult. If |
MCPToolResult
|
error result without raising. |
Source code in symfonic/tools/mcp/provider.py
close_all
async
¶
Close all registered server connections.
discover_tools
async
¶
Discover all tools from all registered servers.
Failed servers are skipped with a warning โ partial discovery is preferred over a hard failure when one server is unavailable.
Re-discovery replaces each answering server's catalogue rather than adding to it, so a tool the server has withdrawn stops being callable. A server that fails to answer keeps the catalogue it last advertised: replacement is per server precisely so a momentary outage is not a silent deregistration of a whole integration.
A row with no usable name is dropped with a warning and its siblings
are kept. It used to raise out of list_tools and, because the
except below is per server, take that server's whole catalogue
with it.
Returns:
| Type | Description |
|---|---|
list[MCPToolDefinition]
|
Flat list of MCPToolDefinition from all reachable servers, each |
list[MCPToolDefinition]
|
carrying the name it is exposed under (see |
list[MCPToolDefinition]
|
mod: |
Source code in symfonic/tools/mcp/provider.py
to_langchain_tools ¶
Convert discovered MCP tools to LangChain StructuredTools.
Each tool's name and description from the MCP server are mapped
to the StructuredTool. Invocations are delegated back through
MCPToolProvider.call_tool.
Returns:
| Type | Description |
|---|---|
list[Any]
|
List of LangChain StructuredTool instances, or an empty list |
list[Any]
|
if langchain-core is not installed. |
Source code in symfonic/tools/mcp/provider.py
MCPToolResult
dataclass
¶
MCPToolResult(tool_name: str, content: str, is_error: bool = False, metadata: dict[str, Any] = dict())
Result from executing an MCP tool.
Attributes:
| Name | Type | Description |
|---|---|---|
tool_name |
str
|
Name of the tool that was called. |
content |
str
|
Text content returned by the tool. |
is_error |
bool
|
True when the server reported an error response. |
metadata |
dict[str, Any]
|
Optional extra data from the server response. |