molecule-core/scripts/bridge/server.py
Hongming Wang 24fec62d7f initial commit — Molecule AI platform
Forked clean from public hackathon repo (Starfire-AgentTeam, BSL 1.1)
with full rebrand to Molecule AI under github.com/Molecule-AI/molecule-monorepo.

Brand: Starfire → Molecule AI.
Slug: starfire / agent-molecule → molecule.
Env vars: STARFIRE_* → MOLECULE_*.
Go module: github.com/agent-molecule/platform → github.com/Molecule-AI/molecule-monorepo/platform.
Python packages: starfire_plugin → molecule_plugin, starfire_agent → molecule_agent.
DB: agentmolecule → molecule.

History truncated; see public repo for prior commits and contributor
attribution. Verified green: go test -race ./... (platform), pytest
(workspace-template 1129 + sdk 132), vitest (canvas 352), build (mcp).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:55:37 -07:00

100 lines
3.4 KiB
Python

"""A2A HTTP server for the external workspace bridge."""
import json
import logging
import uuid
from http.server import HTTPServer, BaseHTTPRequestHandler
from pathlib import Path
from typing import Callable
from .processor import MessageProcessor
logger = logging.getLogger("bridge.server")
class A2AHandler(BaseHTTPRequestHandler):
"""Handles incoming A2A JSON-RPC requests, delegates to a MessageProcessor."""
processor: MessageProcessor
inbox_path: Path
resolve_name: Callable[[str], str]
def log_message(self, format, *args):
pass
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
try:
request = json.loads(body)
except json.JSONDecodeError:
self.send_error(400)
return
method = request.get("method", "")
req_id = request.get("id", str(uuid.uuid4()))
if method == "message/send":
params = request.get("params", {})
message = params.get("message", {})
parts = message.get("parts", [])
text = parts[0].get("text", "") if parts else ""
sender_id = self.headers.get("X-Workspace-ID", "")
sender_name = A2AHandler.resolve_name(sender_id) if sender_id else "canvas"
logger.info(f"📨 {sender_name}: {text[:80]}")
# Log to inbox
entry = {
"id": req_id, "sender_id": sender_id,
"sender_name": sender_name, "text": text,
}
with open(A2AHandler.inbox_path, "a") as f:
f.write(json.dumps(entry) + "\n")
# Process with the configured backend
context = {"sender_id": sender_id, "sender_name": sender_name}
response_text = A2AHandler.processor.process(text, sender_name, context)
self._send_a2a_response(req_id, response_text)
elif method == "agent/card":
self._send_json(200, {
"jsonrpc": "2.0", "id": req_id,
"result": {
"name": A2AHandler.processor.name,
"description": f"External agent powered by {A2AHandler.processor.name}",
"version": "1.0.0",
},
})
else:
self._send_json(200, {
"jsonrpc": "2.0", "id": req_id,
"error": {"code": -32601, "message": f"Unsupported: {method}"},
})
def _send_a2a_response(self, req_id, text):
self._send_json(200, {
"jsonrpc": "2.0", "id": req_id,
"result": {
"kind": "message", "messageId": str(uuid.uuid4()),
"role": "agent",
"parts": [{"kind": "text", "text": text}],
},
})
def _send_json(self, status, data):
body = json.dumps(data).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def create_server(port: int, processor: MessageProcessor, inbox_path: Path, resolve_name) -> HTTPServer:
"""Create an A2A HTTP server with the given processor."""
A2AHandler.processor = processor
A2AHandler.inbox_path = inbox_path
A2AHandler.resolve_name = resolve_name
return HTTPServer(("0.0.0.0", port), A2AHandler)