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:
@@ -268,60 +268,50 @@ async def handle_agent0_mcp(
|
|||||||
valid_poses: list[str],
|
valid_poses: list[str],
|
||||||
) -> ChatResponse:
|
) -> ChatResponse:
|
||||||
"""
|
"""
|
||||||
Send a message to a live Agent Zero instance via its MCP streamable-http
|
Send a message to a live Agent Zero instance via its REST API
|
||||||
server and return the response as a ChatResponse.
|
(POST /api/api_message, X-API-KEY header).
|
||||||
|
|
||||||
MCP URL format: {endpoint}/mcp/t-{mcp_key}/http
|
Conversation continuity is maintained via Agent Zero's context_id, which
|
||||||
Tool called: send_message (built into every Agent Zero instance)
|
maps to a gnommoweb conversation_id in _a0_sessions (in-memory).
|
||||||
|
|
||||||
Conversation continuity is maintained via Agent Zero's chat_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("/")
|
endpoint = (model.endpoint or "").strip().rstrip("/")
|
||||||
mcp_key = model.mcp_key or ""
|
api_key = model.mcp_key or "" # stored in mcp_key field; used as X-API-KEY
|
||||||
mcp_url = f"{endpoint}/mcp/t-{mcp_key}/http"
|
api_url = f"{endpoint}/api/api_message"
|
||||||
|
|
||||||
# Retrieve existing Agent0 chat_id for this conversation (if any)
|
# Retrieve existing Agent0 context_id for this conversation (if any)
|
||||||
a0_chat_id = _a0_sessions.get(req.conversation_id) if req.conversation_id else None
|
a0_context_id = _a0_sessions.get(req.conversation_id) if req.conversation_id else None
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
f"[{agent.name}] → Agent0 MCP url={mcp_url} "
|
f"[{agent.name}] → Agent0 REST url={api_url} "
|
||||||
f"conv={req.conversation_id} a0_chat={a0_chat_id or 'new'} "
|
f"conv={req.conversation_id} a0_ctx={a0_context_id or 'new'} "
|
||||||
f"msg='{req.message[:60]}'"
|
f"msg='{req.message[:60]}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with streamablehttp_client(mcp_url) as (read, write, _):
|
payload: dict = {
|
||||||
async with ClientSession(read, write) as session:
|
"message": req.message,
|
||||||
await session.initialize()
|
"lifetime_hours": 24,
|
||||||
|
}
|
||||||
|
if a0_context_id:
|
||||||
|
payload["context_id"] = a0_context_id
|
||||||
|
|
||||||
tool_args: dict = {
|
async with httpx.AsyncClient(timeout=120.0) as client:
|
||||||
"message": req.message,
|
r = await client.post(
|
||||||
"persistent_chat": True,
|
api_url,
|
||||||
}
|
headers={"X-API-KEY": api_key, "Content-Type": "application/json"},
|
||||||
if a0_chat_id:
|
json=payload,
|
||||||
tool_args["chat_id"] = a0_chat_id
|
)
|
||||||
|
r.raise_for_status()
|
||||||
|
|
||||||
result = await session.call_tool("send_message", tool_args)
|
data = r.json()
|
||||||
|
log.info(f"[{agent.name}] ← Agent0 REST: {str(data)[:300]}")
|
||||||
|
|
||||||
# The tool returns a JSON-serialised ToolResponse / ToolError
|
response_text = data.get("response", "")
|
||||||
raw = result.content[0].text if result.content else "{}"
|
new_context_id = data.get("context_id", "")
|
||||||
log.info(f"[{agent.name}] ← Agent0 MCP raw: {raw[:300]}")
|
|
||||||
|
|
||||||
parsed = json.loads(raw)
|
# Persist context_id for conversation continuity
|
||||||
|
if new_context_id and req.conversation_id is not None:
|
||||||
if parsed.get("status") == "error":
|
_a0_sessions[req.conversation_id] = new_context_id
|
||||||
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
|
|
||||||
|
|
||||||
pose = pick_pose(response_text, valid_poses)
|
pose = pick_pose(response_text, valid_poses)
|
||||||
|
|
||||||
@@ -334,7 +324,7 @@ async def handle_agent0_mcp(
|
|||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
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(
|
fallback_pose = next(
|
||||||
(p for p in ("sorry", "annoyed", "neutral") if p in valid_poses),
|
(p for p in ("sorry", "annoyed", "neutral") if p in valid_poses),
|
||||||
valid_poses[0],
|
valid_poses[0],
|
||||||
|
|||||||
Reference in New Issue
Block a user