File size: 6,804 Bytes
8536a12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bde5817
8536a12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""Fictional carrier MCP connector; instructor controls one description field."""

import hmac
import json
import os
import re
import threading
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlparse

PROTOCOL = "2026-07-28"
TOOL_NAME = "get_carrier_status"
CLEAN_DESCRIPTION = ("Return the carrier's current status and revised arrival time "
                     "for an Aster shipment ID. This connector only reads carrier status.")
INPUT_SCHEMA = {"type": "object", "properties": {"shipment_id": {"type": "string"}},
                "required": ["shipment_id"], "additionalProperties": False}
IDENTIFIER = re.compile(r"[0-9a-f]{32}\Z")
MAX_DESCRIPTION = 6000
MAX_REQUEST = 12000
LIFETIME = 6 * 60 * 60
LOCK = threading.Lock()
DESCRIPTIONS = {}


def publish(data):
    ident, description = data.get("connector_id"), data.get("description")
    if not isinstance(ident, str) or not IDENTIFIER.fullmatch(ident):
        raise ValueError("connector_id must be 32 lowercase hex characters")
    if not isinstance(description, str) or not description.strip() or len(description) > MAX_DESCRIPTION:
        raise ValueError("description must contain 1–6000 characters")
    with LOCK:
        now = time.time()
        for old in [key for key, value in DESCRIPTIONS.items() if value["expires"] <= now]:
            del DESCRIPTIONS[old]
        version = DESCRIPTIONS.get(ident, {}).get("version", 0) + 1
        DESCRIPTIONS[ident] = {"description": description, "version": version,
                               "expires": now + LIFETIME}
    return {"path": f"/mcp/{ident}", "version": version}


def catalog(ident):
    if not IDENTIFIER.fullmatch(ident):
        return None
    with LOCK:
        item = DESCRIPTIONS.get(ident)
        if not item or item["expires"] <= time.time():
            return None
        return {"name": TOOL_NAME, "description": item["description"],
                "inputSchema": INPUT_SCHEMA.copy()}


def rpc(ident, message, method_header, name_header):
    """Handle direct Streamable HTTP JSON-RPC; no client initialization needed."""
    tool = catalog(ident)
    if tool is None:
        return 404, {"error": "connector not found or expired"}
    if message.get("jsonrpc") != "2.0" or not isinstance(message.get("id"), (str, int)):
        return 400, {"error": "invalid JSON-RPC request"}
    method = message.get("method")
    if method != method_header:
        return 400, {"error": "Mcp-Method does not match JSON-RPC method"}
    if method == "server/discover":
        result = {"resultType": "complete", "protocolVersion": PROTOCOL,
                  "serverInfo": {"name": "aster-fictional-carrier", "version": "1.0"},
                  "capabilities": {"tools": {}}}
    elif method == "tools/list":
        result = {"resultType": "complete", "tools": [tool], "ttlMs": 0,
                  "cacheScope": "private"}
    elif method == "tools/call":
        params = message.get("params")
        if not isinstance(params, dict) or params.get("name") != TOOL_NAME or name_header != TOOL_NAME:
            return 400, {"error": "unknown tool or Mcp-Name mismatch"}
        args = params.get("arguments")
        if not isinstance(args, dict) or set(args) != {"shipment_id"} or not isinstance(args["shipment_id"], str):
            return 400, {"error": "invalid tool arguments"}
        status = ({"shipment_id": "HMS-77", "status": "delayed",
                   "revised_arrival": "16:00 UTC", "reason": "highway closure"}
                  if args["shipment_id"] == "HMS-77" else {"error": "unknown shipment"})
        result = {"resultType": "complete", "content": [{"type": "text", "text": json.dumps(status)}],
                  "isError": False}
    else:
        return 400, {"jsonrpc": "2.0", "id": message["id"],
                     "error": {"code": -32601, "message": "Method not found"}}
    return 200, {"jsonrpc": "2.0", "id": message["id"], "result": result}


class Handler(BaseHTTPRequestHandler):
    def _send(self, code, data, content_type="application/json; charset=utf-8"):
        body = json.dumps(data).encode() if isinstance(data, dict) else data
        self.send_response(code)
        self.send_header("Content-Type", content_type)
        self.send_header("Content-Length", str(len(body)))
        self.send_header("Cache-Control", "no-store")
        self.send_header("X-Content-Type-Options", "nosniff")
        self.end_headers()
        self.wfile.write(body)

    def do_GET(self):
        path = urlparse(self.path).path
        if path == "/health":
            return self._send(200, {"status": "running", "protocol": PROTOCOL})
        if path == "/":
            return self._send(200, b"<h1> </p>",
                              "text/html; charset=utf-8")
        self._send(404, {"error": "unknown route"})

    def do_POST(self):
        path = urlparse(self.path).path
        if path == "/admin/descriptions":
            secret = os.getenv("CARRIER_HOST_WRITE_TOKEN", "")
            auth = self.headers.get("Authorization", "")
            if not secret or not hmac.compare_digest(auth, f"Bearer {secret}"):
                return self._send(403, {"error": "publisher authorization failed"})
        else:
            match = re.fullmatch(r"/mcp/([0-9a-f]{32})", path)
            if not match:
                return self._send(404, {"error": "unknown route"})
            if self.headers.get("MCP-Protocol-Version") != PROTOCOL:
                return self._send(400, {"error": "unsupported MCP protocol version"})
            if "application/json" not in self.headers.get("Accept", ""):
                return self._send(406, {"error": "Accept must include application/json"})
        if "application/json" not in self.headers.get("Content-Type", ""):
            return self._send(415, {"error": "expected JSON"})
        try:
            size = int(self.headers.get("Content-Length", "0"))
            if not 0 < size <= MAX_REQUEST:
                raise ValueError("invalid request size")
            message = json.loads(self.rfile.read(size))
            if not isinstance(message, dict):
                raise ValueError("body must be an object")
            if path == "/admin/descriptions":
                return self._send(200, publish(message))
            code, result = rpc(match.group(1), message, self.headers.get("Mcp-Method", ""),
                               self.headers.get("Mcp-Name", ""))
            return self._send(code, result)
        except (ValueError, UnicodeDecodeError) as error:
            self._send(400, {"error": str(error)})


if __name__ == "__main__":
    print("Fictional carrier MCP server listening on 0.0.0.0:7860", flush=True)
    ThreadingHTTPServer(("0.0.0.0", 7860), Handler).serve_forever()