#!/usr/bin/env python3 """Stable, multiplexed stdio supervisor for the Napseer MCP worker. Codex keeps the stdio process it launches for the lifetime of a client session. This supervisor owns those client-facing pipes while treating ``napseer_mcp_server.py`` as a replaceable worker: - valid requests are forwarded without head-of-line blocking and responses are correlated by JSON-RPC request id; - notifications never create client-facing responses; - cancellation releases the matching pending request and forwards the notification to the worker; - a worker exit fails every affected in-flight request once and never replays it automatically; - a replaced worker, or an explicitly configured runtime dependency behind a stable launcher, is reloaded at the next quiescent request boundary. The supervisor never logs MCP payloads, tool results, auth state, or child stderr. Protocol stdout contains JSON-RPC messages only. """ from __future__ import annotations import concurrent.futures import json import os import pathlib import queue import signal import subprocess import sys import threading from dataclasses import dataclass, field from typing import BinaryIO, Callable WORKER_PATH = pathlib.Path( os.environ.get( "NAPSEER_MCP_WORKER_PATH", pathlib.Path(__file__).resolve().with_name("napseer_mcp_server.py"), ) ).expanduser() WORKER_WATCH_PATHS = tuple( pathlib.Path(value).expanduser() for value in os.environ.get("NAPSEER_MCP_WORKER_WATCH_PATHS", "").split(os.pathsep) if value.strip() ) RESPONSE_TIMEOUT_SECONDS = max( 1, int(os.environ.get("NAPSEER_MCP_RESPONSE_TIMEOUT_SECONDS", "60")), ) MAX_IN_FLIGHT_REQUESTS = max( 2, int(os.environ.get("NAPSEER_MCP_MAX_IN_FLIGHT_REQUESTS", "32")), ) WORKER_RESTARTED_ERROR = -32098 _CANCELLED = object() class WorkerUnavailable(RuntimeError): """The replaceable MCP worker could not complete a request.""" def source_identity(path: pathlib.Path) -> tuple[int, int, int]: stat = path.stat() return stat.st_ino, stat.st_size, stat.st_mtime_ns def runtime_identity( worker_path: pathlib.Path, watch_paths: tuple[pathlib.Path, ...] = (), ) -> tuple[tuple[int, int, int], ...]: """Identify every source whose replacement changes the worker runtime.""" return tuple(source_identity(path) for path in (worker_path, *watch_paths)) def parse_message(message: bytes): try: payload = json.loads(message) except (TypeError, ValueError): return None return payload if isinstance(payload, dict) else None def request_id(message: bytes): payload = parse_message(message) return payload.get("id") if payload is not None else None def request_key(value) -> str: return json.dumps(value, ensure_ascii=False, separators=(",", ":")) def request_method(message: bytes) -> str: payload = parse_message(message) return str(payload.get("method") or "") if payload is not None else "" def is_notification(message: bytes) -> bool: payload = parse_message(message) return payload is not None and "id" not in payload def cancelled_request_id(message: bytes): payload = parse_message(message) if payload is None or payload.get("method") != "notifications/cancelled": return False, None params = payload.get("params") or {} if not isinstance(params, dict) or "requestId" not in params: return False, None return True, params.get("requestId") def worker_error(message: bytes) -> bytes: payload = { "jsonrpc": "2.0", "id": request_id(message), "error": { "code": WORKER_RESTARTED_ERROR, "message": ( "Napseer MCP could not complete this request. The outcome may be " "uncertain; inspect state before retrying a mutation." ), }, } return json.dumps(payload, separators=(",", ":")).encode("utf-8") + b"\n" @dataclass class PendingRequest: message: bytes key: str generation: int response: queue.Queue = field(default_factory=lambda: queue.Queue(maxsize=1)) class Worker: def __init__( self, path: pathlib.Path, unsolicited: Callable[[bytes], None] | None = None, watch_paths: tuple[pathlib.Path, ...] = (), ): self.path = path self.watch_paths = tuple(watch_paths) self.process: subprocess.Popen[bytes] | None = None self.identity: tuple[tuple[int, int, int], ...] | None = None self.generation = 0 self.unsolicited = unsolicited self._lifecycle_lock = threading.RLock() self._stdin_lock = threading.Lock() self._pending_lock = threading.Lock() self._pending: dict[str, PendingRequest] = {} self._reload_pending = False def _discard_stderr(self, stream: BinaryIO) -> None: try: for _line in stream: pass finally: stream.close() def _pending_count(self, generation: int | None = None) -> int: with self._pending_lock: if generation is None: return len(self._pending) return sum(1 for item in self._pending.values() if item.generation == generation) def _signal(self, pending: PendingRequest, value) -> None: try: pending.response.put_nowait(value) except queue.Full: pass def _fail_generation(self, generation: int, error: WorkerUnavailable) -> None: failed = [] with self._pending_lock: for key, pending in list(self._pending.items()): if pending.generation != generation: continue self._pending.pop(key, None) failed.append(pending) for pending in failed: self._signal(pending, error) def _deliver_response(self, generation: int, response: bytes) -> bool: payload = parse_message(response) if payload is None: return False normalized = response if response.endswith(b"\n") else response + b"\n" if "id" in payload: key = request_key(payload.get("id")) with self._pending_lock: pending = self._pending.get(key) if pending is not None and pending.generation == generation: self._pending.pop(key, None) else: pending = None if pending is not None: self._signal(pending, normalized) return True if "method" in payload and self.unsolicited is not None: self.unsolicited(normalized) return True def _read_stdout(self, process: subprocess.Popen[bytes], generation: int) -> None: stream = process.stdout if stream is None: self._fail_generation(generation, WorkerUnavailable("Napseer MCP worker output is unavailable")) return invalid_response = False try: for response in stream: if not self._deliver_response(generation, response): invalid_response = True break except (OSError, ValueError): pass if invalid_response and process.poll() is None: try: process.terminate() except OSError: pass self._fail_generation( generation, WorkerUnavailable( "Napseer MCP worker returned invalid JSON" if invalid_response else "Napseer MCP worker exited before responding" ), ) def _stop_locked(self) -> None: process = self.process generation = self.generation self.process = None self.identity = None self._reload_pending = False if process is None: return if process.poll() is None: process.terminate() try: process.wait(timeout=2) except subprocess.TimeoutExpired: process.kill() process.wait() for stream in (process.stdin, process.stdout): if stream is not None: try: stream.close() except OSError: pass self._fail_generation(generation, WorkerUnavailable("Napseer MCP worker stopped")) def start(self) -> None: with self._lifecycle_lock: self._stop_locked() try: identity = runtime_identity(self.path, self.watch_paths) process = subprocess.Popen( [sys.executable, "-u", str(self.path)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, ) except (OSError, subprocess.SubprocessError) as exc: raise WorkerUnavailable("unable to start Napseer MCP worker") from exc if process.stdin is None or process.stdout is None or process.stderr is None: process.kill() process.wait() raise WorkerUnavailable("Napseer MCP worker pipes are unavailable") self.generation += 1 generation = self.generation self.process = process self.identity = identity threading.Thread( target=self._discard_stderr, args=(process.stderr,), name=f"napseer-mcp-stderr-{generation}", daemon=True, ).start() threading.Thread( target=self._read_stdout, args=(process, generation), name=f"napseer-mcp-stdout-{generation}", daemon=True, ).start() def stop(self) -> None: with self._lifecycle_lock: self._stop_locked() def ensure_current(self) -> None: with self._lifecycle_lock: try: current_identity = runtime_identity(self.path, self.watch_paths) except OSError as exc: self._stop_locked() raise WorkerUnavailable("Napseer MCP worker is missing") from exc process_dead = self.process is None or self.process.poll() is not None source_changed = self.identity is not None and self.identity != current_identity if source_changed and not process_dead and self._pending_count(self.generation): self._reload_pending = True return if process_dead or self.identity is None or source_changed or self._reload_pending: self.start() def _write(self, process: subprocess.Popen[bytes], message: bytes) -> None: with self._stdin_lock: if self.process is not process or process.poll() is not None or process.stdin is None: raise WorkerUnavailable("Napseer MCP worker is not running") try: process.stdin.write(message if message.endswith(b"\n") else message + b"\n") process.stdin.flush() except (BrokenPipeError, OSError) as exc: raise WorkerUnavailable("Napseer MCP worker input closed") from exc def submit(self, message: bytes) -> PendingRequest | None: self.ensure_current() with self._lifecycle_lock: process = self.process generation = self.generation if process is None: raise WorkerUnavailable("Napseer MCP worker is not running") if is_notification(message): self._write(process, message) return None key = request_key(request_id(message)) pending = PendingRequest(message=message, key=key, generation=generation) with self._pending_lock: if key in self._pending: raise WorkerUnavailable("duplicate in-flight JSON-RPC request id") if len(self._pending) >= MAX_IN_FLIGHT_REQUESTS: raise WorkerUnavailable("too many in-flight Napseer MCP requests") self._pending[key] = pending try: self._write(process, message) except WorkerUnavailable: with self._pending_lock: if self._pending.get(key) is pending: self._pending.pop(key, None) raise return pending def cancel(self, value) -> bool: key = request_key(value) with self._pending_lock: pending = self._pending.get(key) if pending is None or request_method(pending.message) == "initialize": return False self._pending.pop(key, None) self._signal(pending, _CANCELLED) return True def wait(self, pending: PendingRequest) -> bytes | None: try: result = pending.response.get(timeout=RESPONSE_TIMEOUT_SECONDS) except queue.Empty as exc: with self._pending_lock: if self._pending.get(pending.key) is pending: self._pending.pop(pending.key, None) raise WorkerUnavailable("Napseer MCP worker response timed out") from exc if result is _CANCELLED: return None if isinstance(result, WorkerUnavailable): raise result return result def supervise() -> int: output_lock = threading.Lock() closing = threading.Event() def emit(response: bytes) -> None: if closing.is_set(): return with output_lock: sys.stdout.buffer.write(response if response.endswith(b"\n") else response + b"\n") sys.stdout.buffer.flush() worker = Worker( WORKER_PATH, unsolicited=emit, watch_paths=WORKER_WATCH_PATHS, ) waiters = concurrent.futures.ThreadPoolExecutor( max_workers=MAX_IN_FLIGHT_REQUESTS, thread_name_prefix="napseer-mcp-request", ) def finish(message: bytes, pending: PendingRequest) -> None: try: response = worker.wait(pending) except WorkerUnavailable: response = worker_error(message) if response is not None: emit(response) def stop_worker(_signum=None, _frame=None): closing.set() worker.stop() waiters.shutdown(wait=False, cancel_futures=True) raise SystemExit(0) signal.signal(signal.SIGTERM, stop_worker) signal.signal(signal.SIGINT, stop_worker) try: for message in sys.stdin.buffer: if not message.strip(): continue cancelled, value = cancelled_request_id(message) if cancelled: worker.cancel(value) try: worker.submit(message) except WorkerUnavailable: pass continue try: pending = worker.submit(message) except WorkerUnavailable: if not is_notification(message): emit(worker_error(message)) continue if pending is not None: waiters.submit(finish, message, pending) finally: closing.set() worker.stop() waiters.shutdown(wait=True, cancel_futures=True) return 0 if __name__ == "__main__": raise SystemExit(supervise())