ecb52e9978
A server is a row with a URL; its tools are discovered by a button and cached, then offered beside the built-in ones. Written by hand rather than taken from the reference SDK, because that SDK's transport does its own connecting -- and the one thing that must not be bypassed is check_url on every hop. Owning the transport is the point; the framing beside it is the small part. Sessions are per call: initialize, initialized, the call, a best-effort DELETE. Caching one wants an owner, a TTL, eviction, a lock and a shutdown hook, and the server may expire it under all of that anyway -- ToolContext is a session-free snapshot precisely so nothing in a tool holds live state. A server's names and descriptions reach the model as instructions and are bounded before they do; what it returns is escaped preformatted text, never markdown. Tools are namespaced per server, so two servers exposing "search" do not collide and neither shadows a built-in. Also: a round's calls now run together under a semaphore, results indexed so each tool turn stays paired with its call, and generation.status names what is running -- a remote tool is latency-bound, and a silent pause is what a hang looks like. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
27 lines
919 B
Python
27 lines
919 B
Python
"""An MCP client: remote servers over streamable HTTP.
|
|
|
|
Three parts. `protocol` is the wire format and nothing else -- it knows no
|
|
database and no HTTP. `client` owns the transport, which is where the SSRF guard
|
|
lives and the reason none of this comes from the reference SDK. `registry` is
|
|
where a row becomes a tool the chat loop can be offered.
|
|
|
|
Local stdio servers are deliberately absent. Spawning a subprocess is the
|
|
agentic-execution feature, which wants a confirmation model before it does
|
|
anything; a URL is a different act with a different blast radius.
|
|
"""
|
|
|
|
from lembas.services.mcp.client import McpSpec, call_tool, list_tools, spec_from
|
|
from lembas.services.mcp.protocol import McpError
|
|
from lembas.services.mcp.registry import offer_name, refresh, tool_defs
|
|
|
|
__all__ = [
|
|
"McpError",
|
|
"McpSpec",
|
|
"call_tool",
|
|
"list_tools",
|
|
"offer_name",
|
|
"refresh",
|
|
"spec_from",
|
|
"tool_defs",
|
|
]
|