Files
ITWorx_MCP_Hub-Public/packages/hub_clients/bridge.py
T

81 lines
2.7 KiB
Python

"""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=(",", ":"))