feat: prepare ARR for controlled public deployment

This commit is contained in:
Wyndham ARR
2026-07-29 16:38:05 +08:00
commit a701de9f0e
271 changed files with 48472 additions and 0 deletions

70
arr_mcp/README.md Normal file
View File

@@ -0,0 +1,70 @@
# ARR MCP ingestion gateway
This is a standalone Streamable HTTP MCP server with exactly one tool:
`arr_submit_processing_result`.
The protocol process is intentionally thin. It authenticates the HTTP request,
advertises the frozen four-field tool Schema, bounds concurrent processor
replays, and calls `DirectSubmissionService`. It does not contain SQL or trust
Agent-provided Finance facts.
## Runtime
The official MCP Python SDK 2.0 requires Python 3.10+. Keep it separate from the
portal's historical Python 3.9 virtual environment:
```bash
python3.12 -m venv .venv-mcp
.venv-mcp/bin/python -m pip install -r requirements-arr-mcp.txt
```
The server uses the recommended stateless Streamable HTTP transport with JSON
responses, a 4 MiB HTTP-body limit, DNS-rebinding Host allowlisting, and a
required bearer token. See the
[official Python SDK](https://github.com/modelcontextprotocol/python-sdk) and
[low-level server guidance](https://py.sdk.modelcontextprotocol.io/v2/advanced/low-level-server/).
Local launch with Keychain-backed secrets:
```bash
.venv-mcp/bin/python -m arr_mcp.launch \
--db-config /path/to/private/booking-test-db.env \
--host 127.0.0.1 \
--port 8890
```
The launcher reads only OSS routing values from the owned `0600` route file.
It loads OSS credentials and `ARR_MCP_BEARER_TOKEN` from macOS Keychain account
`arr-web`; no secret belongs in this repository, command line, Main Prompt, or
Agent output.
When binding beyond loopback, pass every accepted reverse-proxy Host header:
```bash
--host 0.0.0.0 --allowed-host mcp.example.com
```
Do not expose plain HTTP publicly. Terminate HTTPS at a controlled reverse proxy
or tunnel and forward only the MCP endpoint.
## SuperAgent MCP entry
- Service identifier: `arr_ingestion_gateway`
- Display name: `ARR Ingestion Gateway`
- Transport: `HTTP`
- Service address: `https://<controlled-host>/mcp`
- Status: activate only after the no-PII acceptance test passes
- Headers: `Authorization: Bearer <ARR_MCP_BEARER_TOKEN>` supplied through the
platform's secret facility
- OAuth JSON: `{}` unless the deployment later adopts a dedicated OAuth server
The bearer protects the transport. The short-lived `submission_grant` inside
the tool call separately binds the write to one registered job attempt. Both
must be valid.
## Success and error behavior
Success returns only the strict receipt: `committed` or `already_committed`,
job/attempt, business date, daily version identity and record count. A business
or validation error is an MCP tool error containing only a stable code, safe
message and `retryable` flag. Request payloads and grants are never logged.

5
arr_mcp/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
"""Thin MCP transport boundary for ARR direct result ingestion."""
from arr_mcp.gateway import DirectResultGateway
__all__ = ["DirectResultGateway"]

93
arr_mcp/auth.py Normal file
View File

@@ -0,0 +1,93 @@
"""Transport-level bearer protection for the ARR MCP ASGI application."""
from __future__ import annotations
import hashlib
import hmac
import os
from dataclasses import dataclass, field
from typing import Any, Awaitable, Callable, Dict, List, Tuple
MCP_BEARER_ENV = "ARR_MCP_BEARER_TOKEN"
ASGIApp = Callable[
[Dict[str, Any], Callable[[], Awaitable[Dict[str, Any]]], Callable[[Dict[str, Any]], Awaitable[None]]],
Awaitable[None],
]
@dataclass(frozen=True)
class BearerAuthConfig:
token: str = field(repr=False)
@classmethod
def from_environment(cls) -> "BearerAuthConfig":
value = os.environ.get(MCP_BEARER_ENV, "")
try:
encoded = value.encode("ascii")
except UnicodeEncodeError:
encoded = b""
if (
not 32 <= len(encoded) <= 256
or any(byte <= 32 or byte == 127 for byte in encoded)
):
raise ValueError(f"{MCP_BEARER_ENV} is unavailable or invalid")
return cls(value)
class BearerAuthASGI:
"""Pure ASGI middleware; never logs or exposes the configured token."""
def __init__(self, application: ASGIApp, config: BearerAuthConfig) -> None:
self._application = application
self._expected_sha256 = hashlib.sha256(
("Bearer " + config.token).encode("ascii")
).digest()
@staticmethod
def _authorization_values(scope: Dict[str, Any]) -> List[bytes]:
headers: List[Tuple[bytes, bytes]] = scope.get("headers", [])
return [
value
for name, value in headers
if name.lower() == b"authorization"
]
def _authorized(self, scope: Dict[str, Any]) -> bool:
values = self._authorization_values(scope)
if len(values) != 1:
return False
return hmac.compare_digest(
hashlib.sha256(values[0]).digest(),
self._expected_sha256,
)
@staticmethod
async def _reject(
send: Callable[[Dict[str, Any]], Awaitable[None]],
) -> None:
body = b'{"error":"unauthorized"}'
await send(
{
"type": "http.response.start",
"status": 401,
"headers": [
(b"content-type", b"application/json"),
(b"content-length", str(len(body)).encode("ascii")),
(b"cache-control", b"no-store"),
(b"www-authenticate", b"Bearer"),
],
}
)
await send({"type": "http.response.body", "body": body})
async def __call__(
self,
scope: Dict[str, Any],
receive: Callable[[], Awaitable[Dict[str, Any]]],
send: Callable[[Dict[str, Any]], Awaitable[None]],
) -> None:
if scope.get("type") == "http" and not self._authorized(scope):
await self._reject(send)
return
await self._application(scope, receive, send)

67
arr_mcp/database.py Normal file
View File

@@ -0,0 +1,67 @@
"""Controlled local PostgreSQL connector for the standalone MCP process."""
from __future__ import annotations
import os
import stat
from pathlib import Path
from typing import Any, Callable, Dict
_REQUIRED = (
"ARR_DB_HOST",
"ARR_DB_PORT",
"ARR_DB_USER",
"ARR_DB_PASSWORD",
"ARR_DB_NAME",
)
def _read_private_database_config(path: Path) -> Dict[str, object]:
candidate = path.expanduser()
if candidate.is_symlink():
raise ValueError("controlled database configuration is invalid")
resolved = candidate.resolve(strict=True)
metadata = resolved.stat()
if (
not stat.S_ISREG(metadata.st_mode)
or metadata.st_uid != os.getuid()
or stat.S_IMODE(metadata.st_mode) != 0o600
):
raise ValueError("controlled database configuration is invalid")
values: Dict[str, str] = {}
for raw in resolved.read_text(encoding="utf-8").splitlines():
line = raw.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
if key in values:
raise ValueError("controlled database configuration is invalid")
values[key] = value.strip().strip('"').strip("'")
if any(not values.get(key) for key in _REQUIRED):
raise ValueError("controlled database configuration is incomplete")
try:
port = int(values["ARR_DB_PORT"])
except ValueError:
raise ValueError("controlled database configuration is invalid") from None
if not 1 <= port <= 65535:
raise ValueError("controlled database configuration is invalid")
return {
"host": values["ARR_DB_HOST"],
"port": port,
"user": values["ARR_DB_USER"],
"password": values["ARR_DB_PASSWORD"],
"dbname": values["ARR_DB_NAME"],
}
def controlled_connect(path: Path) -> Callable[[str], Any]:
import psycopg # type: ignore[import-not-found]
parameters = _read_private_database_config(path)
def connect(_dsn: str) -> Any:
return psycopg.connect(**parameters, autocommit=False)
return connect

45
arr_mcp/gateway.py Normal file
View File

@@ -0,0 +1,45 @@
"""Framework-neutral adapter for the single ARR MCP tool."""
from __future__ import annotations
import threading
from typing import Any, Dict, Protocol
from arr_ingestion.contracts import IngestionError
from arr_ingestion.direct_contracts import DirectSubmissionReceipt
class DirectResultSubmitter(Protocol):
def submit(self, raw_request: Any) -> DirectSubmissionReceipt:
...
class DirectResultGateway:
"""Expose one bounded command without duplicating ingestion logic."""
def __init__(
self,
service: DirectResultSubmitter,
*,
max_concurrency: int = 2,
) -> None:
if (
not isinstance(max_concurrency, int)
or isinstance(max_concurrency, bool)
or not 1 <= max_concurrency <= 8
):
raise ValueError("direct gateway concurrency is invalid")
self._service = service
self._slots = threading.BoundedSemaphore(max_concurrency)
def submit_processing_result(self, arguments: Any) -> Dict[str, Any]:
if not self._slots.acquire(blocking=False):
raise IngestionError(
"DIRECT_GATEWAY_BUSY",
"direct result gateway is busy; retry later",
retryable=True,
)
try:
return self._service.submit(arguments).to_dict()
finally:
self._slots.release()

108
arr_mcp/launch.py Normal file
View File

@@ -0,0 +1,108 @@
"""Local secure launcher that loads routes and Keychain-backed MCP secrets."""
from __future__ import annotations
import argparse
import os
import stat
import subprocess
from pathlib import Path
from typing import Dict, Optional, Sequence
from arr_mcp.run import main as run_mcp
KEYCHAIN_ACCOUNT = "arr-web"
KEYCHAIN_SERVICES = {
"OSS_ACCESS_KEY_ID": "com.chillishark.arr.oss-access-key-id",
"OSS_ACCESS_KEY_SECRET": "com.chillishark.arr.oss-access-key-secret",
"ARR_MCP_BEARER_TOKEN": "com.chillishark.arr.mcp-bearer",
}
ROUTE_KEYS = frozenset(
{
"ARR_OSS_REGION",
"ARR_OSS_ENDPOINT",
"ARR_OSS_BUCKET",
"ARR_OBJECT_PREFIX",
}
)
def _private_route_values(path: Path) -> Dict[str, str]:
candidate = path.expanduser()
if candidate.is_symlink():
raise ValueError("ARR MCP route configuration is invalid")
resolved = candidate.resolve(strict=True)
metadata = resolved.stat()
if (
not stat.S_ISREG(metadata.st_mode)
or metadata.st_uid != os.getuid()
or stat.S_IMODE(metadata.st_mode) != 0o600
):
raise ValueError("ARR MCP route configuration is invalid")
values: Dict[str, str] = {}
for raw in resolved.read_text(encoding="utf-8").splitlines():
line = raw.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
if key in ROUTE_KEYS:
if key in values:
raise ValueError("ARR MCP route configuration is invalid")
values[key] = value.strip().strip('"').strip("'")
if any(not values.get(key) for key in ("ARR_OSS_REGION", "ARR_OSS_BUCKET")):
raise ValueError("ARR MCP route configuration is incomplete")
return values
def _keychain_secret(service: str) -> str:
try:
completed = subprocess.run(
[
"security",
"find-generic-password",
"-s",
service,
"-a",
KEYCHAIN_ACCOUNT,
"-w",
],
check=True,
capture_output=True,
text=True,
timeout=10,
)
except (OSError, subprocess.SubprocessError):
raise ValueError("required ARR MCP Keychain item is unavailable") from None
value = completed.stdout.strip()
if not value:
raise ValueError("required ARR MCP Keychain item is unavailable")
return value
def load_runtime_environment(route_config: Path) -> None:
for key, value in _private_route_values(route_config).items():
os.environ[key] = value
for environment_name, service in KEYCHAIN_SERVICES.items():
os.environ[environment_name] = _keychain_secret(service)
def _parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
"--route-config",
type=Path,
default=Path("~/.config/arr/agent-writeback.env"),
)
return parser
def main(argv: Optional[Sequence[str]] = None) -> int:
args, remaining = _parser().parse_known_args(argv)
load_runtime_environment(args.route_config)
return run_mcp(remaining)
if __name__ == "__main__":
raise SystemExit(main())

89
arr_mcp/run.py Normal file
View File

@@ -0,0 +1,89 @@
"""Standalone Streamable HTTP entry point for ARR MCP."""
from __future__ import annotations
import argparse
from pathlib import Path
from typing import Optional, Sequence
from arr_mcp.auth import BearerAuthConfig
from arr_mcp.database import controlled_connect
from arr_mcp.gateway import DirectResultGateway
from arr_mcp.server import create_http_application
from arr_web.direct_ingestion_runtime import compose_oss_direct_ingestion
PROJECT_ROOT = Path(__file__).resolve().parents[1]
_LOOPBACK_HOSTS = {"127.0.0.1", "localhost", "::1"}
def _parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="arr-mcp")
parser.add_argument("--host", default="127.0.0.1")
parser.add_argument("--port", type=int, default=8890)
parser.add_argument(
"--db-config",
type=Path,
help="private 0600 ARR_DB_* configuration file",
)
parser.add_argument(
"--allowed-host",
action="append",
default=[],
help="exact HTTP Host value or host:*; repeat for reverse-proxy names",
)
parser.add_argument(
"--max-concurrency",
type=int,
default=2,
help="maximum concurrent deterministic replays (1-8)",
)
return parser
def _allowed_hosts(bind_host: str, configured: list[str]) -> list[str]:
if configured:
return list(dict.fromkeys(configured))
if bind_host not in _LOOPBACK_HOSTS:
raise ValueError(
"--allowed-host is required when ARR MCP binds beyond loopback"
)
return ["localhost:*", "127.0.0.1:*", "[::1]:*"]
def main(argv: Optional[Sequence[str]] = None) -> int:
args = _parser().parse_args(argv)
if not 1 <= args.port <= 65535:
raise ValueError("ARR MCP port is invalid")
connect = controlled_connect(args.db_config) if args.db_config else None
runtime = compose_oss_direct_ingestion(
project_root=PROJECT_ROOT,
connect=connect,
)
try:
runtime.service.assert_ready()
gateway = DirectResultGateway(
runtime.service,
max_concurrency=args.max_concurrency,
)
application = create_http_application(
gateway,
BearerAuthConfig.from_environment(),
allowed_hosts=_allowed_hosts(args.host, args.allowed_host),
)
import uvicorn
uvicorn.run(
application,
host=args.host,
port=args.port,
access_log=True,
log_level="info",
)
finally:
runtime.close()
return 0
if __name__ == "__main__":
raise SystemExit(main())

260
arr_mcp/server.py Normal file
View File

@@ -0,0 +1,260 @@
"""Official MCP SDK binding for the single ARR direct-ingestion tool."""
from __future__ import annotations
import json
import logging
from pathlib import Path
from typing import Any, Dict, Mapping, Tuple
from arr_ingestion.contracts import IngestionError
from arr_mcp.auth import BearerAuthASGI, BearerAuthConfig
from arr_mcp.gateway import DirectResultGateway
TOOL_NAME = "arr_submit_processing_result"
SERVER_VERSION = "1.0.1"
MAX_MCP_REQUEST_BYTES = 4 * 1024 * 1024
PROJECT_ROOT = Path(__file__).resolve().parents[1]
REQUEST_SCHEMA = (
PROJECT_ROOT
/ "database"
/ "contracts"
/ "arr-submit-processing-result-v1.schema.json"
)
RECEIPT_SCHEMA = (
PROJECT_ROOT
/ "database"
/ "contracts"
/ "arr-submit-processing-result-receipt-v1.schema.json"
)
STRUCTURED_SCHEMA = (
PROJECT_ROOT
/ "arr-opera-daily-ingest"
/ "references"
/ "structured-result.schema.json"
)
LOGGER = logging.getLogger(__name__)
def _json_object(path: Path) -> Dict[str, Any]:
value = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(value, dict):
raise ValueError("ARR MCP schema is invalid")
return value
def _advertised_payload_schema(payload: Mapping[str, Any]) -> Dict[str, Any]:
"""Project the strict payload contract into an LLM-tool-safe Schema.
The authoritative payload validation stays in ``DirectSubmissionRequest``
and the independent source replay. Tool discovery only needs a compact
shape that tells the Agent to pass every top-level structured-result field.
Keeping local ``$ref`` values inside a schema nested below ``payload`` is
not portable: some MCP-to-LLM adapters resolve ``#`` from the outer request
document and reject the tool before the model can run.
"""
required = payload.get("required")
properties = payload.get("properties")
definitions = payload.get("$defs")
if (
not isinstance(required, list)
or not all(isinstance(value, str) and value for value in required)
or not isinstance(properties, Mapping)
or not isinstance(definitions, Mapping)
):
raise ValueError("ARR MCP structured payload schema is invalid")
required_set = set(required)
if required_set != set(properties):
raise ValueError("ARR MCP structured payload fields are invalid")
sha256 = definitions.get("sha256")
channel = definitions.get("channel")
if not isinstance(sha256, Mapping) or not isinstance(channel, Mapping):
raise ValueError("ARR MCP structured payload definitions are invalid")
count_names = {
"source_rows",
"removed_by_rate_code",
"removed_as_duplicates",
"output_rows",
}
advertised_properties: Dict[str, Any] = {
"result_schema_version": {"type": "string", "const": "3.0"},
"status": {"type": "string", "const": "success"},
"activation_eligible": {"type": "boolean", "const": True},
"ingestion_mode": {"type": "string", "const": "opera_xml"},
"business_date": {"type": "string", "format": "date"},
"processor_version": {"type": "string", "minLength": 1},
"rule_set_sha256": dict(sha256),
"outcome_counts": {
"type": "object",
"description": "Complete unmodified outcome_counts object.",
},
"channels": {
"type": "array",
"items": dict(channel),
"description": "Complete unmodified channels array.",
},
"artifacts": {
"type": "object",
"description": "Complete unmodified artifacts object.",
},
"records": {
"type": "array",
"items": {"type": "object"},
"description": "Every unmodified structured-result record.",
},
"errors": {
"type": "array",
"items": {"type": "object"},
"maxItems": 0,
},
}
for name in count_names:
advertised_properties[name] = {"type": "integer", "minimum": 0}
if set(advertised_properties) != required_set:
raise ValueError("ARR MCP advertised payload fields are invalid")
return {
"type": "object",
"description": (
"The complete, unmodified successful structured-result.json "
"object. ARR performs the full contract validation and source replay."
),
"additionalProperties": False,
"required": list(required),
"properties": advertised_properties,
}
def load_tool_schemas() -> Tuple[Dict[str, Any], Dict[str, Any]]:
"""Return a portable advertised request schema and strict receipt."""
request = _json_object(REQUEST_SCHEMA)
receipt = _json_object(RECEIPT_SCHEMA)
payload = _json_object(STRUCTURED_SCHEMA)
try:
payload_contract = request["properties"]["payload"]["allOf"]
reference = payload_contract[0]["$ref"]
except (KeyError, IndexError, TypeError):
raise ValueError("ARR MCP request schema is invalid") from None
expected = (REQUEST_SCHEMA.parent / reference).resolve()
if expected != STRUCTURED_SCHEMA.resolve():
raise ValueError("ARR MCP request payload schema is invalid")
request["properties"]["payload"] = _advertised_payload_schema(payload)
request.pop("$schema", None)
request.pop("$id", None)
return request, receipt
def _json_text(value: Mapping[str, Any]) -> str:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
)
def create_mcp_server(gateway: DirectResultGateway) -> Any:
"""Create the low-level server so the advertised Schema stays exact."""
import anyio
import mcp.types as types
from mcp.server import Server
request_schema, receipt_schema = load_tool_schemas()
tool = types.Tool(
name=TOOL_NAME,
title="Submit ARR processing result",
description=(
"Submit the exact frozen successful ARR structured-result payload "
"once. ARR independently replays the registered source XML before "
"atomically activating Finance facts."
),
inputSchema=request_schema,
outputSchema=receipt_schema,
)
async def list_tools(_context: Any, _params: Any) -> Any:
return types.ListToolsResult(tools=[tool])
async def call_tool(_context: Any, params: Any) -> Any:
if params.name != TOOL_NAME:
error = {
"code": "MCP_TOOL_NOT_FOUND",
"message": "requested ARR MCP tool is unavailable",
"retryable": False,
}
return types.CallToolResult(
content=[types.TextContent(text=_json_text(error))],
isError=True,
)
try:
result = await anyio.to_thread.run_sync(
gateway.submit_processing_result,
params.arguments,
)
except IngestionError as error:
payload = {
"code": error.code,
"message": error.safe_message,
"retryable": error.retryable,
}
return types.CallToolResult(
content=[types.TextContent(text=_json_text(payload))],
isError=True,
)
except Exception as error:
LOGGER.error(
"ARR MCP tool failed with unexpected %s",
type(error).__name__,
)
payload = {
"code": "DIRECT_GATEWAY_INTERNAL",
"message": "direct result gateway failed safely",
"retryable": True,
}
return types.CallToolResult(
content=[types.TextContent(text=_json_text(payload))],
isError=True,
)
return types.CallToolResult(
content=[types.TextContent(text=_json_text(result))],
structuredContent=result,
)
return Server(
"arr-ingestion-gateway",
version=SERVER_VERSION,
title="ARR ingestion gateway",
description="Attempt-bound validated ARR daily-result ingestion",
on_list_tools=list_tools,
on_call_tool=call_tool,
)
def create_http_application(
gateway: DirectResultGateway,
bearer: BearerAuthConfig,
*,
allowed_hosts: list[str],
) -> Any:
if not allowed_hosts or any(not value for value in allowed_hosts):
raise ValueError("ARR MCP allowed hosts are required")
from mcp.server.transport_security import TransportSecuritySettings
server = create_mcp_server(gateway)
application = server.streamable_http_app(
json_response=True,
stateless_http=True,
max_request_body_size=MAX_MCP_REQUEST_BYTES,
transport_security=TransportSecuritySettings(
enable_dns_rebinding_protection=True,
allowed_hosts=list(allowed_hosts),
allowed_origins=[],
),
)
return BearerAuthASGI(application, bearer)