MCP 接入
错误与排查
按你实际看到的现象分类。先看 HTTP 状态码 —— 它告诉你是三层里的哪一层坏了。
三层错误,三个查法
- HTTP 层 ——
401/400/404/405/415/403。请求压根没走到工具。要么凭证不对,要么形状不对。 - JSON-RPC 层 —— HTTP 200,但响应体里有
error。绝大多数是-32602:这个工具名对这枚凭证不存在。 - 业务层 —— HTTP 200 +
isError: true,机器码在_meta["com.popkit/error"].code。请求被理解了但被拒绝,或者下游失败了。限流和权限走的都是这条路 —— 请按 code 分支处理,别当异常抛。
{ "isError": true,
"content": [{ "type": "text",
"text": "rate_limited: rate limit for create_post: 10 calls per 60s; retry after 42s {\"limit\":10,\"windowSec\":60,\"retryAfterSec\":42}" }],
"_meta": { "com.popkit/error": {
"code": "rate_limited",
"params": { "limit": 10, "windowSec": 60, "retryAfterSec": 42 } } } }HTTP 层
| 你看到 | 意思是 | 怎么办 |
|---|---|---|
401 + WWW-Authenticate | 没带凭证 / 过期 / 已被停用吊销。鉴权跑在一切之前,所以凭证不对时,哪怕请求形状也错了,你看到的还是 401 | A 类走授权流程;B 类查密钥、找管理员 |
400 -32020 | Mcp-Method / Mcp-Name 与请求体对不上,或者缺了这个头 | 把头补上,并让它与 params.name(或 params.uri)一致 |
400 -32602 | _meta 信封少了必带键,或没带 io.modelcontextprotocol/ 前缀 | 补上 protocolVersion 与 clientCapabilities,两个都要带前缀 |
404,空响应体 | 这个环境没开 MCP,或者地址写错了 | 核对地址 / 环境 |
404 -32601 | 方法名拼错了(tools/lists…) | 查拼写 |
405 | 你发了 GET 或 DELETE。本端点无状态 —— 这是预期内的,无害;MCP 官方 SDK 把它当正常情况 | 如果来自 SDK 的监听流,不用管 |
415 | Content-Type 不是 application/json | 补上这个头 |
403 | Origin 不被允许(只有浏览器会带这个头) | 改从服务端调用,或从本站同源调用 |
业务机器码
| 机器码 | 含义 | 该做什么 |
|---|---|---|
| unauthenticated | 没有可用身份。这种情况下业务工具根本不在 tools/list 里,硬调会得到 -32602 | 换 / 刷新凭证;用 ping 的 authorized 判断 |
| workspace_not_found | 凭证指向的工作区不存在、已解散,或授权人已不是成员 | A 类重新授权选工作区;B 类找管理员重签 |
| forbidden | 角色矩阵、凭证 scope 或发布落点不允许 | 降档(action:"draft"),或申请更多 scope / 落点 |
| not_found | 对象不存在,或不属于本工作区(两者不区分,防枚举) | 核对 id;别用别的工作区的 id |
| invalid_input | 入参不合法(长度 / 枚举 / 日期 / 游标 / 幂等键);params 里有细节 | 按 params 自纠后重试 |
| plan_limit | 套餐配额耗尽(当前是月度定时发布条数;失败内容重投也吃当月配额) | 等下月或升级套餐 —— 不要重试 |
| no_account | 目标平台没有可用账号 | 先在 popkit 连接账号 |
| needs_reauth | 目标账号的平台授权失效 | 要真人回 popkit 重新授权,外部系统代劳不了 |
| rate_limited | 撞了三档限流、同账号间隔、每日外发上限,或回评的两条上限。params.reason 会说明是哪一条:comment_reply_cap(同一条评论 24h 内回太多次)/ comment_reply_daily_cap(该凭证当日回评总量用完);其余来源不带 reason | 按 params.retryAfterSec 等待后重试。撞 comment_reply_cap 时重试无用 —— 换一条评论回 |
| replay | 同一个 clientRequestId 的上一次调用还在执行中(占位最长 2 分钟) | 等几秒用同一个键重试,会拿到原结果 |
| external_writes_off | 该环境用安全阀关闭了对外发布(只影响平台渠道) | 只做草稿 / 排期,或换环境 |
| platform_error | 平台侧拒绝或失败(政策 / 判重 / 5xx);params.reason 带机器码 | 不是入参能自纠的;人工看 deepLink |
| internal | 未归类的服务端错误(已记日志) | 稍后重试;持续出现请联系我们 |
按现象查
- 全是 401 —— 是凭证的问题,不是网络。同一个 token 用
curl冒烟能过,那就是你客户端的请求头配置。 ping回authorized: false—— 凭证本身有效,但解析不出工作区:被吊销了,或者签发它的人已经不是成员。不是 scope 的问题。- 某个工具不在
tools/list里 —— 这枚凭证没有那个 scope。A 类重新授权时勾上;B 类的 scope 签发后不可改,找管理员重签。 - scope 都有却还是
forbidden—— 要么角色不够,要么那一档发布落点没勾。看get_workspace_context的credential.allowedActions。 - 只有重试时才
invalid_input—— 同一个clientRequestId换了入参。重试必须原样重发;内容变了就换一个新键。 - 发了但没反应 —— 发布是异步的。用
get_post轮询,看targets[].status/errorCode。 - n8n 疯狂发
GET—— 旧版本 n8n 在执行结束后不回收 MCP 会话。升级到 2.33 或更新版本。
还是定位不了?把失败那一次的请求头、请求体和完整响应(凭证打码)发给我们,再附上同一枚凭证调 ping 的返回 —— 这两样合起来基本一眼就能定位到是哪一层。你那个客户端怎么配见 客户端配置。