Beta

让外部 client 或 SDK 通过 A2A 协议调用 Manyfold 上的 Agent。

Manyfold 可以把一个 Agent 暴露成 A2A server,这样任何 A2A client(官方 SDK、其它 Agent 平台,或者直接 curl)都能给它派活并取回结果。

调用方说 A2A 协议时用这条路。只要一个 HTTP 聊天端点的话,用 OpenAI 兼容 Chat API 更简单。

第 1 步:暴露 Agent

打开 Agent,进 A2A tab,打开暴露开关。没打开之前所有 A2A 请求都返回 404——未暴露的 Agent 和不存在的 Agent 对外没有区别。

也可以从 CLI 完成同样的操作。Agent runtime 永远只能管理自己;使用 mf login 或 personal API token 时,通过全局 --agent-id(或 MF_AGENT_ID)选择你拥有的 Agent:

mf --agent-id {agentId} a2a exposure enable
mf --agent-id {agentId} a2a exposure get

tab 上会给出该 Agent 的两个公开 URL:

GET  /api/a2a/agents/{agentId}/agent-card.json
POST /api/a2a/agents/{agentId}/rpc

第 2 步:给你的 client 铸一个 token

还是 A2A tab:Add caller → External client。填个名字(比如 zapier-integration),可选填过期天数,然后把 token 复制走——它只显示一次。

也可以用 CLI 创建:

mf --agent-id {agentId} a2a callers add \
  --external \
  --name zapier-integration \
  --expires-in-days 30

human mode 下 stdout 只有这一行一次性 bearer,方便直接写进 secret store;警告和 endpoint 信息写到 stderr。--json 会返回包括一次性 token 在内的完整结果。CLI 不保存 token,也无法再次显示。

新增 caller 不会自动打开 exposure。两步是有意独立的;只有准备好让 公共 A2A server 可访问时,才运行 mf a2a exposure enable

关于这个 token:

  • 只能调这一个 Agent。拿去调你名下另一个 Agent 会返回 403
  • 它是 Bearer token:请求头带 Authorization: Bearer <token>
  • 在 callers 列表里 revoke,下一次调用立即失效。
  • Settings → API tokens 里铸的个人 token 在这里用不了,即使勾了 a2a:edit scope。只有 External client 铸出来的 token 有效。

mf a2a callers list 查看 peer 和 External client grant,用 mf a2a callers revoke <tokenId> --yes 撤销。读取 callers/exposure 需要 a2a:read;enable、disable、add、revoke 需要 a2a:edit

第 3 步:免复制粘贴接入应用

第三方应用可以走 device-code Connect flow,让已登录用户选择一个或多个 Agent。应用不需要获得用户的 Manyfold session 或 personal API token。

先免鉴权创建一个 15 分钟有效的连接会话:

curl -X POST https://api.manyfold.ai/api/connect/a2a/start \
  -H "Content-Type: application/json" \
  -d '{
    "clientName": "Team Agents",
    "clientUrl": "https://team-agents.example.com"
  }'

clientName 必填(最多 60 字符);clientUrl 可选(最多 200 字符)且必须使用 HTTPS。两者都由调用方自报,授权页会明确标成未经验证

{
  "requestId": "acs_…",
  "userCode": "UQMU-2H6P",
  "authUrl": "https://manyfold.ai/connect/a2a?request=acs_…&code=UQMU-2H6P",
  "deviceCode": "mf_cnx_…",
  "expiresAt": "2026-08-05T12:15:00.000Z"
}

应用界面展示 userCode 并打开 authUrl。用户登录 Manyfold、核对 user code、选择最多 20 个 Agent,并决定是否开启 A2A exposure。deviceCode 必须按 secret 保存:明文只在 start 响应出现,Manyfold 数据库只保存它的 hash。

应用每两秒轮询一次:

curl -X POST https://api.manyfold.ai/api/connect/a2a/poll \
  -H "Content-Type: application/json" \
  -d '{ "deviceCode": "mf_cnx_…" }'

用户尚未决定时返回 { "status": "pending" };拒绝或超时返回 deniedexpired。批准后的第一次 poll 只返回一次凭据:

{
  "status": "approved",
  "userEmail": "owner@example.com",
  "agents": [
    {
      "agentId": "agt_…",
      "name": "release-agent",
      "rpcUrl": "https://api.manyfold.ai/api/a2a/agents/agt_…/rpc",
      "cardUrl": "https://api.manyfold.ai/api/a2a/agents/agt_…/agent-card.json",
      "token": "nca_…",
      "expiresAt": null
    }
  ]
}

立即保存每个 token。每个 Agent 都有一枚独立、只允许调用该 Agent 的 External client token;重复 poll 或并发 poll 的输家返回 expired,凭据不会再次出现。赢家 poll 发生前不会铸任何 token。RPC 返回 403 表示该 Agent 的 grant 已撤销或过期,应提示用户重新连接。

完整 endpoint 契约:

Endpoint鉴权用途
POST /api/connect/a2a/start{clientName, clientUrl?} 创建会话;按来源 IP 限流。
GET /api/connect/a2a/session/{requestId}/{userCode}给授权页读取未经验证的 client metadata 与状态;按来源 IP 限流。
POST /api/connect/a2a/approve仅 human session批准 {requestId, userCode, agentIds, enableExposure?, expiresInDays?};只记录 consent,不返回 token。
POST /api/connect/a2a/deny仅 human session拒绝 {requestId, userCode}
POST /api/connect/a2a/pollDevice code单次消费批准结果并返回各 Agent 的凭据;按来源 IP 限流。

Connect device code 不是 Manyfold API bearer,也不能拿到 CLI login flow 里兑换。用户从每个 Agent 的 A2A callers 列表撤销最终生成的 token;当前还没有应用级一键断开端点。

第 4 步:取 Agent Card

Card 是公开的,不需要 token,里面有协议版本、传输方式和 skill:

curl https://api.manyfold.ai/api/a2a/agents/{agentId}/agent-card.json

同一份 card 也挂在 Agent 路径下的 /.well-known/agent-card.json,所以那些从 base URL 解析 card 的 SDK 不需要你手工给出完整 card URL:

from a2a.client import A2ACardResolver

resolver = A2ACardResolver(
    httpx_client,
    base_url="https://api.manyfold.ai/api/a2a/agents/{agentId}",
)
card = await resolver.get_agent_card()

第 5 步:发消息

message/send 跑完一轮并返回完成的 task:

curl -X POST https://api.manyfold.ai/api/a2a/agents/{agentId}/rpc \
  -H "Authorization: Bearer $MF_A2A_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "message/send",
    "params": {
      "message": {
        "kind": "message",
        "role": "user",
        "parts": [{ "kind": "text", "text": "总结一下今天还开着的 PR。" }],
        "messageId": "11111111-2222-3333-4444-555555555555"
      }
    }
  }'

返回是包着 A2A task 的 JSON-RPC envelope:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "kind": "task",
    "id": "aat_…",
    "contextId": "aac_…",
    "status": { "state": "completed", "timestamp": "2026-07-26T20:39:45.757Z" },
    "artifacts": [{ "artifactId": "artifact-1", "parts": [{ "kind": "text", "text": "…" }] }]
  }
}

message.kindmessage.rolemessage.partsmessage.messageId 都是必填,缺了会返回 JSON-RPC 错误 -32602。重复用同一个 messageId 会拿回原来那个 task,而不是再跑一轮——所以重试是安全的。

接着上一轮聊

把上一个 task 的 contextId 带上就复用同一个会话:

"message": {
  "kind": "message",
  "role": "user",
  "contextId": "aac_…",
  "parts": [{ "kind": "text", "text": "再按作者分个组。" }],
  "messageId": "66666666-7777-8888-9999-000000000000"
}

只有你这个 token 创建的 task 能这样引用。

长任务

一轮可能跑很久时有两种做法:

  • 流式——message/stream 返回 Server-Sent Events(先 status-update,然后是 artifact-update 分片)。
  • 提交后轮询——message/send 带上 "configuration": { "blocking": false },立刻拿到 working 状态的 task,之后用 task id 轮询 tasks/get

支持的方法

方法用途
message/send跑一轮(阻塞,或 blocking:false 提交后轮询)
message/stream跑一轮并用 SSE 推送事件
tasks/get按 id 取单个 task
tasks/list列出该 token 在这个 Agent 上的 task
tasks/cancel取消尚未结束的 task
tasks/resubscribe重新挂回某个 task 的事件流

错误

传输与授权问题以 HTTP 状态码 + {"error": "..."} 返回;调用本身的问题以 HTTP 200 + JSON-RPC error 对象返回。

状态码含义
401token 缺失、已 revoke 或已过期
403该 token 不是这个 Agent 的 External client token
404Agent 不存在,或没打开 A2A 暴露
429触发限流——等窗口过去再重试
200 + error参数错误、方法未知,或 task 不存在

A2A 调用和账号其它调用共用同一份 API 配额。

这个页面有帮助吗?