feat(mcp): load MCP servers asynchronously at startup

Boot MCP servers (npx/uvx) on a background thread instead of blocking
agent init. Built-in tools serve traffic immediately while MCP comes
online; each new agent reads whatever is ready at creation time.
Idempotent via _mcp_loaded flag — concurrent sessions never re-fork
subprocesses. Per-server failures are isolated and warmup is triggered
in app.py so loading overlaps with channel startup.
This commit is contained in:
zhayujie
2026-05-08 15:22:42 +08:00
parent 9a09e057d6
commit 307769b949
5 changed files with 133 additions and 27 deletions

18
app.py
View File

@@ -274,6 +274,20 @@ def sigterm_handler_wrap(_signo):
signal.signal(_signo, func)
def _warmup_mcp_tools():
"""
Kick off MCP server loading at process startup so subprocesses
(npx / uvx etc.) finish initializing before the first user message
arrives. Returns immediately — the actual work happens on a daemon
thread inside ToolManager. Safe to call when MCP is not configured.
"""
try:
from agent.tools import ToolManager
ToolManager()._load_mcp_tools()
except Exception as e:
logger.warning(f"[App] MCP warmup failed (non-fatal): {e}")
def _sync_builtin_skills():
"""Sync builtin skills from project skills/ to workspace skills/ on startup."""
import shutil
@@ -335,6 +349,10 @@ def run():
# Sync builtin skills to workspace before channels start
_sync_builtin_skills()
# Kick off MCP server loading in the background so first-message
# latency isn't dominated by npx package downloads.
_warmup_mcp_tools()
logger.info(f"[App] Starting channels: {channel_names}")
_channel_mgr = ChannelManager()