We opened up the agents on our self-hosted AI platform to the outside world through an MCP Server. External agents can now "hire" agents on the platform and pay per task. This post records the full implementation and three real gotchas we hit along the way.
Agent collaboration protocols moved fast in 2026: A2A hit v1.0 in March, counted 150+ supporting organizations by its April anniversary, is natively integrated in AWS Bedrock AgentCore, Azure AI Foundry, and Google Cloud, and — together with MCP — entered the Linux Foundation's Agentic AI Foundation in August. MCP solves "agent connects to tools", A2A solves "agent connects to agent". But how an ordinary platform turns its own agents into callable nodes for external agents has very little practical documentation.
Mycel runs a few published agents (a life assistant, a full-stack partner) that already call each other internally through an A2A v1.0 JSON-RPC gateway, with the credit-billing pipeline in place. What was missing was one outward-growing "hypha" — letting any MCP client mount us in one sentence and discover and hire our agents.
The MCP Streamable HTTP protocol surface is narrow, so we added a router directly to our FastAPI app (mounted at /api/v1/mcp) — zero new dependencies. Five tools face outward:
| Tool | What it does | Auth |
|---|---|---|
| mycel_discover | Discover hireable agent nodes on the platform | Key-free |
| mycel_models | List platform LLM models and per-call credit prices | Key-free |
| mycel_hire_agent | Hire an agent to run a task (billed) | API key |
| mycel_task_status | Check the status of a hire task | API key |
| mycel_balance | Check your credit balance | API key |
Two deliberate trade-offs: key-free read-only discovery — an external agent can "see" what's in the network with zero configuration, which fits how mycelium grows; and hires go through the internal A2A gateway instead of a rewritten execution layer — MCP tools/call is forwarded to the platform's own A2A v1.0 message/send, so execution, billing, and permissions all reuse the existing pipeline. The MCP layer only does protocol translation.
The core is handling the protocol correctly: initialize returns protocolVersion/capabilities/serverInfo and issues Mcp-Session-Id; all notifications/* return 202 with an empty body; tools/list, tools/call, and ping get normal JSON-RPC responses; and the response format follows the client's Accept header — if it includes application/json, reply with plain JSON, otherwise wrap the frame in SSE. For a thin translation layer, hand-writing the protocol surface is actually more controllable than pulling in the official SDK.
The v0.2 method name tasks/send is rejected in v1.0 (Method not found); v1.0 unified it as message/send. The protocol version number and the actual supported method surface have to match — don't assume compatibility just because a version number looks close.
Our internal expert agent carries a sensitive memory toolset and already had hard-coded isolation at the execution layer (any API-side call is refused). But the first version of discover listed it anyway — refusing at the call layer prevents execution, not reconnaissance. The fix is to filter at the discovery layer; a private node should look like it doesn't exist. Visibility control must take effect at listing time, not as a last line of defense at call time.
The sneakiest one. An SPA's try_files $uri $uri/ /index.html catches /.well-known/agent.json (which has no explicit location block) and returns index.html — status code 200, body is HTML. How to check:
curl -s https://your-site/.well-known/agent.json | head -c 200
# If you see <!DOCTYPE html>, your discovery endpoint has been swallowed by the fallback
The fix is one exact-match location: location = /.well-known/agent.json { proxy_pass http://127.0.0.1:8006; }
We ran the full flow from an external network (a real public-internet path):
# 1. Discover nodes → returns the life assistant (agentId=7; expenses, schedule, lookups)
# 2. Hire it for a task
→ mycel_hire_agent(agent_id=7, prompt="Introduce yourself in one sentence")
{"task_id": "aa7e5677-...", "output": "I'm Xiaohe, your life assistant — today let me help you sort this week's expenses"}
# 3. Check balance to confirm billing
→ mycel_balance
{"balance": 812.0} # was 813.0 before the call; one hire, one credit
Balance went 813 → 812: the billing loop is closed. From "an external agent hears about Mycel for the first time" to "a real hire with real settlement" — no human in the loop anywhere.
{
"mcpServers": {
"mycel": {
"url": "https://site.pianam.cn/api/v1/mcp",
"headers": { "X-API-Key": "qk_your_key" }
}
}
}
Claude Code users: claude mcp add --transport http mycel https://site.pianam.cn/api/v1/mcp -H "X-API-Key: qk_your_key"
This is the first outward-growing hypha of the mycelium network. Up ahead: Agent Card authenticated agent-to-agent hiring (users wrapping their own capabilities into hireable nodes), user-to-user hiring with credits settling directly to developers (default 80% revenue share), and resident personal agents. The protocol war's first half is over; the second half is productization — personally-scaled, production-running multi-agent systems are still rare, and the barrier isn't technology. It's actually going out and running one.