"""Minimal JSON-lines stdio bridge; tokens are read only for the call and never emitted.""" from __future__ import annotations import json import os from collections.abc import Callable, Iterable, Iterator, Mapping from typing import Any, Protocol class BridgeTransport(Protocol): def send( self, request: Mapping[str, Any], *, token: str, endpoint: str ) -> Mapping[str, Any] | None: ... class StdioBridge: """Translate bounded JSON-lines messages to remote MCP without shell or token output.""" ALLOWED_METHODS = frozenset( { "initialize", "notifications/initialized", "ping", "tools/list", "tools/call", "resources/list", "resources/templates/list", "resources/read", "prompts/list", "prompts/get", } ) def __init__(self, *, endpoint: str, token_env: str | None = None) -> None: if not endpoint.startswith("https://"): raise ValueError("stdio bridge endpoints must use HTTPS") self.endpoint = endpoint self.token_env = token_env or "ITWORX_MCP_ACCESS_TOKEN" def forward( self, lines: Iterable[str], transport: BridgeTransport, *, environ: Mapping[str, str] | None = None, token_provider: Callable[[], str] | None = None, ) -> Iterator[str]: values = environ if environ is not None else os.environ static_token = values.get(self.token_env) if not static_token and token_provider is None: yield _error("authentication_required") return for raw in lines: if len(raw.encode("utf-8")) > 4 * 1024 * 1024: yield _error("request_too_large") continue try: request = json.loads(raw) except json.JSONDecodeError: yield _error("invalid_json") continue if not isinstance(request, dict) or request.get("method") not in self.ALLOWED_METHODS: yield _error("unsupported_method") continue try: token = token_provider() if token_provider is not None else static_token if not token: yield _error("authentication_required") continue result = transport.send(request, token=token, endpoint=self.endpoint) if result is not None: yield json.dumps(dict(result), separators=(",", ":")) except Exception: yield _error("upstream_unavailable") del static_token def _error(code: str) -> str: return json.dumps({"error": {"code": code}}, separators=(",", ":"))