Documents
Home>Documents>AI>Agent>Xgen

Building MCP Station: A Multi-Server Routing System

6 min readOct 1, 2025Feb 22, 2026

MCP Station - Building an MCP Server Routing System

MCP Station was a project I built before Claude Control. It's a system for managing and routing across multiple MCP servers through a single endpoint. I started it in late September 2025 and wrapped it up by mid-October — five commits total.

Why It Was Needed

Running multiple MCP servers means managing each process separately. Clients need to know which tool lives on which server, and if a server dies, it has to be restarted manually. I built a centralized routing system to solve this.

Core Components

MCP Station is built around three core components.

import asyncio
import subprocess
from typing import Dict, List, Optional
from dataclasses import dataclass, field

@dataclass
class MCPServerInfo:
    name: str
    command: str
    args: List[str]
    process: Optional[subprocess.Popen] = None
    tools: List[str] = field(default_factory=list)
    status: str = "stopped"

class SessionManager:
    """세션 이름 추적 및 관리"""
    def __init__(self):
        self.sessions: Dict[str, dict] = {}
        self.session_names: Dict[str, str] = {}

    def register_session(self, session_id: str, name: str):
        self.sessions[session_id] = {
            "name": name,
            "created_at": datetime.now(KST).isoformat(),
            "server_bindings": [],
        }
        self.session_names[name] = session_id

    def get_by_name(self, name: str) -> Optional[str]:
        return self.session_names.get(name)

class ProcessManager:
    """MCP 서버 프로세스 생명주기 관리"""
    def __init__(self):
        self.servers: Dict[str, MCPServerInfo] = {}

    async def start_server(self, info: MCPServerInfo) -> bool:
        if info.name in self.servers and info.status == "running":
            return True
        process = subprocess.Popen(
            [info.command] + info.args,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        info.process = process
        info.status = "running"
        self.servers[info.name] = info
        # 도구 목록 요청
        info.tools = await self._discover_tools(info)
        return True

    async def stop_server(self, name: str):
        if name in self.servers:
            self.servers[name].process.terminate()
            self.servers[name].status = "stopped"

Router Implementation

When a client makes a request by tool name, the router dispatches it to whichever MCP server provides that tool.

class Router:
    """도구 이름 → MCP 서버 라우팅"""
    def __init__(self, process_manager: ProcessManager):
        self.pm = process_manager
        self._tool_map: Dict[str, str] = {}  # tool_name -> server_name

    async def build_routing_table(self):
        self._tool_map.clear()
        for name, server in self.pm.servers.items():
            for tool in server.tools:
                self._tool_map[tool] = name

    async def route_request(self, tool_name: str, params: dict) -> dict:
        server_name = self._tool_map.get(tool_name)
        if not server_name:
            raise ValueError(f"Unknown tool: {tool_name}")
        server = self.pm.servers[server_name]
        return await self._send_jsonrpc(server, tool_name, params)

    async def _send_jsonrpc(self, server: MCPServerInfo,
                            method: str, params: dict) -> dict:
        request = {
            "jsonrpc": "2.0",
            "method": f"tools/{method}",
            "params": params,
            "id": self._next_id(),
        }
        server.process.stdin.write(
            json.dumps(request).encode() + b"\n"
        )
        server.process.stdin.flush()
        response = server.process.stdout.readline().decode()
        return json.loads(response)

KST Timezone Handling

All logs and timestamps are recorded in Korea Standard Time (KST).

from datetime import timezone, timedelta

KST = timezone(timedelta(hours=9))

def now_kst():
    return datetime.now(KST)

Retrospective

MCP Station was a small project, but it gave me a lot of insight when I later built the MCP auto-loading feature in Claude Control. The tool-name-based routing pattern in particular turned out to be highly reusable.

Tags
MCPMCP Stationroutingserver managementPython