-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver_manager.py
More file actions
65 lines (53 loc) · 2.1 KB
/
Copy pathserver_manager.py
File metadata and controls
65 lines (53 loc) · 2.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from __future__ import annotations
import os
import subprocess
import threading
from dataclasses import dataclass
from typing import Dict, Optional
from core.mcp.client import StdioMCPClient
from core.mcp.config import MCPServerConfig, load_mcp_servers
@dataclass
class _ServerHandle:
cfg: MCPServerConfig
proc: subprocess.Popen
client: StdioMCPClient
class MCPServerManager:
"""Starts and manages stdio MCP server subprocesses (demo: one process per server id)."""
def __init__(self) -> None:
self._lock = threading.Lock()
self._servers_by_id: Dict[str, MCPServerConfig] = {s.id: s for s in load_mcp_servers()}
self._handles: Dict[str, _ServerHandle] = {}
def list_server_ids(self) -> list[str]:
with self._lock:
return sorted(self._servers_by_id.keys())
def get_client(self, *, server_id: str) -> StdioMCPClient:
with self._lock:
if server_id in self._handles:
return self._handles[server_id].client
cfg = self._servers_by_id[server_id]
if cfg.transport != "stdio":
raise ValueError(f"Unsupported MCP transport: {cfg.transport}")
env = dict(**(cfg.env or {}))
proc = subprocess.Popen(
cfg.command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=cfg.cwd,
env={**os.environ, **env},
)
assert proc.stdin is not None
assert proc.stdout is not None
client = StdioMCPClient(server_id=server_id, stdin=proc.stdin, stdout=proc.stdout)
# initialize eagerly to fail fast
client.initialize()
self._handles[server_id] = _ServerHandle(cfg=cfg, proc=proc, client=client)
return client
def shutdown(self) -> None:
with self._lock:
for hid, h in list(self._handles.items()):
try:
h.proc.terminate()
except Exception:
pass
self._handles.pop(hid, None)