From b71c1bdca7dd0aa6323d7b8074bf4be25aa44a9b Mon Sep 17 00:00:00 2001 From: Re-bin Date: Thu, 5 Mar 2026 14:44:45 +0000 Subject: [PATCH] fix(mcp): hoist sse/http imports, annotate auto-detection heuristic, restore field comments --- README.md | 4 ++-- nanobot/agent/tools/mcp.py | 6 +++--- nanobot/config/schema.py | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 6c9304d..5bc70b8 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ 🐈 **nanobot** is an **ultra-lightweight** personal AI assistant inspired by [OpenClaw](https://github.com/openclaw/openclaw) -⚡️ Delivers core agent functionality in just **~4,000** lines of code — **99% smaller** than Clawdbot's 430k+ lines. +⚡️ Delivers core agent functionality with **99% fewer lines of code** than OpenClaw, making it more customizable and understandable. -📏 Real-time line count: **3,935 lines** (run `bash core_agent_lines.sh` to verify anytime) +📏 Real-time line count: run `bash core_agent_lines.sh` to verify anytime ## 📢 News diff --git a/nanobot/agent/tools/mcp.py b/nanobot/agent/tools/mcp.py index 151aa55..2cbffd0 100644 --- a/nanobot/agent/tools/mcp.py +++ b/nanobot/agent/tools/mcp.py @@ -58,7 +58,9 @@ async def connect_mcp_servers( ) -> None: """Connect to configured MCP servers and register their tools.""" from mcp import ClientSession, StdioServerParameters + from mcp.client.sse import sse_client from mcp.client.stdio import stdio_client + from mcp.client.streamable_http import streamable_http_client for name, cfg in mcp_servers.items(): try: @@ -67,6 +69,7 @@ async def connect_mcp_servers( if cfg.command: transport_type = "stdio" elif cfg.url: + # Convention: URLs ending with /sse use SSE transport; others use streamableHttp transport_type = ( "sse" if cfg.url.rstrip("/").endswith("/sse") else "streamableHttp" ) @@ -80,8 +83,6 @@ async def connect_mcp_servers( ) read, write = await stack.enter_async_context(stdio_client(params)) elif transport_type == "sse": - from mcp.client.sse import sse_client - def httpx_client_factory( headers: dict[str, str] | None = None, timeout: httpx.Timeout | None = None, @@ -99,7 +100,6 @@ async def connect_mcp_servers( sse_client(cfg.url, httpx_client_factory=httpx_client_factory) ) elif transport_type == "streamableHttp": - from mcp.client.streamable_http import streamable_http_client # Always provide an explicit httpx client so MCP HTTP transport does not # inherit httpx's default 5s timeout and preempt the higher-level tool timeout. http_client = await stack.enter_async_context( diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index 9f2e5b3..1f2f946 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -329,13 +329,13 @@ class ExecToolConfig(Base): class MCPServerConfig(Base): """MCP server connection configuration (stdio or HTTP).""" - type: Literal["stdio", "sse", "streamableHttp"] | None = None - command: str = "" - args: list[str] = Field(default_factory=list) - env: dict[str, str] = Field(default_factory=dict) - url: str = "" - headers: dict[str, str] = Field(default_factory=dict) - tool_timeout: int = 30 + type: Literal["stdio", "sse", "streamableHttp"] | None = None # auto-detected if omitted + command: str = "" # Stdio: command to run (e.g. "npx") + args: list[str] = Field(default_factory=list) # Stdio: command arguments + env: dict[str, str] = Field(default_factory=dict) # Stdio: extra env vars + url: str = "" # HTTP/SSE: endpoint URL + headers: dict[str, str] = Field(default_factory=dict) # HTTP/SSE: custom headers + tool_timeout: int = 30 # seconds before a tool call is cancelled class ToolsConfig(Base):