一个 403 引发的血案
那天我在 Kimi Code 里配 Figma Remote MCP:
json
{
"mcpServers": {
"figma": {
"url": "https://mcp.figma.com/mcp"
}
}
}
然后执行 /mcp-config login figma。浏览器弹出来,授权,跳转......
javascript
HTTP 403: Invalid OAuth error response: SyntaxError: Unexpected token 'F', "Forbidden" is not valid JSON. Raw body: Forbidden
连个 JSON 错误都懒得给,直接甩一个 Forbidden 字符串。
我在 Kimi Code 的 repo 提了个 issue #134,然后转头去查怎么回事。
不是 Bug,是门禁
Figma 的 Remote MCP Server(https://mcp.figma.com/mcp)使用 OAuth 2.0 with PKCE 进行认证。但它有一个关键限制:OAuth 动态注册的客户端名称必须在 Figma 的白名单(allowlist)中。
目前谁在白名单里?OpenAI Codex 。这就是为什么 Codex CLI 的 codex mcp login figma 一路通畅------注册时 client_name: "Codex" 在 allowlist 里。
而其他 MCP 客户端(Kimi Code、OpenCode、Cline 等)注册时用的都是自己的名字,不在白名单中,Figma 直接返回 403 Forbidden。
Figma 正在逐步开放 MCP,但到目前为止,通过 allowlist 限制已知客户端是他们的安全策略。相关的讨论和跟踪:
- sst/opencode#5636 --- MCP OAuth allowlist 支持
- sst/opencode#5583 --- Figma MCP 认证问题
- Figma forum: OAuth-less Access to Figma MCP Tools?
一个聪明(且合理)的"钻空子"
社区很快找到了一个优雅的 workaround:既然 Figma allowlist 只检查 client_name,那动态注册的时候把名字设成 Codex 不就行了?
OAuth 2.0 动态客户端注册(RFC 7591)允许客户端在注册时指定自己的名称。Figma 的 OAuth 端点的白名单逻辑只校验 client_name 字段,并不验证申请者是否真的是 Codex。一旦注册成功,access token 就是标准的 OAuth token------它不绑定到某个特定客户端工具。
这个发现最早出自 @connorads 的 opencode fork,然后被 @sdaoudi 封装成了独立的 CLI 工具 mcp-auth-helper,专门解决 Figma MCP 的认证问题。也有 pi-figma-remote-auth 这样的 Pi 扩展。
思路简单到极致------核心就改一个字符串:
typescript
const reg = await fetch(meta.registration_endpoint, {
body: JSON.stringify({
client_name: "Codex", // ← 关键:冒充 Codex
redirect_uris: [callbackUri],
grant_types: ['authorization_code', 'refresh_token'],
token_endpoint_auth_method: 'none',
}),
});
然后走标准的 PKCE 流程拿到 token,存本地。之后任何 MCP 客户端拿着这个 token 去连 Figma 都行------token 不校验来源。
等等,那为什么还要写个 CLI?
这里有个关键点:改 client_name 本身只是一行代码,但为了完成 OAuth 握手,你必须有一整套基础设施:
- 从 Figma 的
.well-known端点发现 OAuth 配置 - 动态注册客户端
- 生成 PKCE code challenge
- 启动一个本地 HTTP 服务器接收回调
- 打开浏览器让用户授权
- 接收授权码后交换 access token
- 把 token 持久化到本地
这些事不属于任何 MCP 客户端的配置范畴------MCP 客户端的 OAuth 处理是内置的,不会让你覆盖 client_name。所以必须有一个独立于客户端之外的认证工具来做这件事。
mcp-auth-helper、pi-figma-remote-auth 都是干这个的。本质上它们不解决 Figma API 调用的问题,也不是 MCP Server 的替代品------它们只跑一次 OAuth,拿到 token,你的客户端拿着 token 去直连 mcp.figma.com 就好。
具体怎么用
目前有几套现成的工具:
figma-cli(推荐)
bash
npm install -g @boltdoggy/figma
# 一键 OAuth 登录
figma auth oauth-login # 自动打开浏览器完成 PKCE 授权
# 查看认证状态
figma auth status
# 启动 MCP 代理供 AI Agent 使用
figma mcp # stdio 模式,把全部 Figma MCP 工具透传给 Agent
在 Claude Code 中配成 stdio MCP Server:
json
{
"mcpServers": {
"figma": {
"command": "npx",
"args": ["-y", "@boltdoggy/figma", "mcp"]
}
}
}
也可以直接在终端调用 Figma MCP 工具,无需 Agent 中间层:
bash
figma call get_screenshot --arg.fileKey=abc123 --arg.nodeId=1-2
figma call search_design_system --arg.fileKey=abc123 --arg.query=button
mcp-auth-helper
适合 OpenCode 用户,token 写入 OpenCode 默认路径:
bash
git clone https://github.com/sdaoudi/mcp-auth-helper.git
cd mcp-auth-helper && npm install && npm run build
node dist/index.js auth figma --url https://mcp.figma.com/mcp
pi-figma-remote-auth
Pi 生态用户可以直接安装:
bash
pi install npm:pi-figma-remote-auth
pi install npm:pi-mcp-adapter
/figma-remote-auth login
注意: Figma Remote MCP Server 目前仍处于 Beta 阶段。Starter 免费版账户每月限制 6 次 MCP 工具调用。OAuth token 有有效期,注意刷新。
一点思考
这个 workaround 巧妙的地方在于它不 hack 任何协议------OAuth 2.0 的动态注册就是这么设计的,Figma 的 allowlist 也是这么设计的,它只是把两个规范之间留出的灰色地带利用了一下。
Figma 如果真想堵这个洞,要么引入 client_id 签名验证,要么走应用审核流程。但话说回来,堵了之后没有 Codex 的用户用什么呢?所以可以理解这个 allowlist 更多是一个"君子协定"式的过渡方案。
不过在那之前,结果就是谁用谁知道。
相关项目:
- figma-cli ---
npm install -g @boltdoggy/figma,支持认证 + MCP 代理 + 终端调用 - mcp-auth-helper --- 独立 CLI,token 输出到 OpenCode 格式
- pi-figma-remote-auth --- Pi 扩展
- connorads/opencode workaround commit --- 最早出处