Switch Agent0 from MCP to REST API (/api/api_message)

MCP endpoint returns 403 (wrong token) and the mcp library's TaskGroup
error is opaque. The REST API (X-API-KEY header, /api/api_message) is
already validated in connectivity tests and returns proper responses.

mcp_key field is reused as the X-API-KEY value.
context_id replaces chat_id for conversation continuity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-02 19:44:11 +02:00
parent 1006d4490a
commit 07329d8672
+29 -39
View File
@@ -268,60 +268,50 @@ async def handle_agent0_mcp(
valid_poses: list[str],
) -> ChatResponse:
"""
Send a message to a live Agent Zero instance via its MCP streamable-http
server and return the response as a ChatResponse.
Send a message to a live Agent Zero instance via its REST API
(POST /api/api_message, X-API-KEY header).
MCP URL format: {endpoint}/mcp/t-{mcp_key}/http
Tool called: send_message (built into every Agent Zero instance)
Conversation continuity is maintained via Agent Zero's chat_id, which maps
to a gnommoweb conversation_id in _a0_sessions (in-memory).
Conversation continuity is maintained via Agent Zero's context_id, which
maps to a gnommoweb conversation_id in _a0_sessions (in-memory).
"""
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession
endpoint = (model.endpoint or "").strip().rstrip("/")
mcp_key = model.mcp_key or ""
mcp_url = f"{endpoint}/mcp/t-{mcp_key}/http"
api_key = model.mcp_key or "" # stored in mcp_key field; used as X-API-KEY
api_url = f"{endpoint}/api/api_message"
# Retrieve existing Agent0 chat_id for this conversation (if any)
a0_chat_id = _a0_sessions.get(req.conversation_id) if req.conversation_id else None
# Retrieve existing Agent0 context_id for this conversation (if any)
a0_context_id = _a0_sessions.get(req.conversation_id) if req.conversation_id else None
log.info(
f"[{agent.name}] → Agent0 MCP url={mcp_url} "
f"conv={req.conversation_id} a0_chat={a0_chat_id or 'new'} "
f"[{agent.name}] → Agent0 REST url={api_url} "
f"conv={req.conversation_id} a0_ctx={a0_context_id or 'new'} "
f"msg='{req.message[:60]}'"
)
try:
async with streamablehttp_client(mcp_url) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tool_args: dict = {
payload: dict = {
"message": req.message,
"persistent_chat": True,
"lifetime_hours": 24,
}
if a0_chat_id:
tool_args["chat_id"] = a0_chat_id
if a0_context_id:
payload["context_id"] = a0_context_id
result = await session.call_tool("send_message", tool_args)
async with httpx.AsyncClient(timeout=120.0) as client:
r = await client.post(
api_url,
headers={"X-API-KEY": api_key, "Content-Type": "application/json"},
json=payload,
)
r.raise_for_status()
# The tool returns a JSON-serialised ToolResponse / ToolError
raw = result.content[0].text if result.content else "{}"
log.info(f"[{agent.name}] ← Agent0 MCP raw: {raw[:300]}")
data = r.json()
log.info(f"[{agent.name}] ← Agent0 REST: {str(data)[:300]}")
parsed = json.loads(raw)
response_text = data.get("response", "")
new_context_id = data.get("context_id", "")
if parsed.get("status") == "error":
raise RuntimeError(parsed.get("error", "Unknown Agent0 error"))
response_text = parsed.get("response", "")
new_chat_id = parsed.get("chat_id", "")
# Persist the Agent0 chat_id so the next turn continues the same context
if new_chat_id and req.conversation_id is not None:
_a0_sessions[req.conversation_id] = new_chat_id
# Persist context_id for conversation continuity
if new_context_id and req.conversation_id is not None:
_a0_sessions[req.conversation_id] = new_context_id
pose = pick_pose(response_text, valid_poses)
@@ -334,7 +324,7 @@ async def handle_agent0_mcp(
)
except Exception as e:
log.error(f"[{agent.name}] Agent0 MCP error: {e}")
log.error(f"[{agent.name}] Agent0 REST error: {e}")
fallback_pose = next(
(p for p in ("sorry", "annoyed", "neutral") if p in valid_poses),
valid_poses[0],