symfonic.tools.mcp.provider¶
provider ¶
MCPToolProvider -- bridge between MCP servers and LangChain tools.
Connects to one or more MCP servers, discovers their tools, and converts them to LangChain StructuredTools for use in agent graphs.
Naming and dispatch live in :mod:symfonic.tools.mcp.routing, which is where
T4.2.2's HIGH finding was closed: this provider used to key discovered tools on
the bare name, so a second server advertising an existing name inherited its
traffic.
JSONRPCMCPConnection moved to :mod:symfonic.tools.mcp.connection when this
module went over its size budget. It is re-exported here so the
symfonic.tools.mcp.provider import path keeps working; the supported import
is from symfonic.tools.mcp import JSONRPCMCPConnection.
Optional dependencies: - httpx>=0.27 required for JSONRPCMCPConnection HTTP transport - langchain-core required for to_langchain_tools()
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 src/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 src/symfonic/tools/mcp/connection.py
close
async
¶
Release connection state and close the pooled HTTP client.
Source code in src/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 src/symfonic/tools/mcp/connection.py
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 src/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 src/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 src/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 src/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. |