"""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)