Figma MCP 403 Forbidden: Why client_name allowlists fail Figma's remote MCP server returns HTTP 403 Forbidden to unlisted MCP clients because it allowlists the self-asserted client_name field sent during Dynamic Client Registration, a Figma staff member confirmed on the company's forum in May 2026 after a developer reported the failure in April 2026. The gate, which Figma says is "intentionally gated to supported clients while we're in beta" for the mcp:connect scope, blocks write-to-canvas access that "requires the remote Figma MCP server," and users bypassed it by registering as "Claude Code" or "Codex" — while GitHub's Copilot CLI was rejected as "copilot-cli" but accepted as "GitHub Copilot CLI." The OpenID Foundation's October 2025 paper on identity for agentic AI cited the same Dynamic Client Registration weakness, since every registration field is written by the client itself. Figma MCP 403 Forbidden: Why client name allowlists fail igma's remote MCP server admits clients by the client name they send during dynamic client registration. Why that check proves nothing, what CIMD fixes and what it doesn't, and what to gate on instead. Dynamic Client Registration DCR lets an OAuth client that has never met a server register itself and receive a client ID. Every field in that registration, including the client's name, is written by the client. A server that admits clients by name is trusting a label anyone can type. Point opencode at Figma's remote MCP server, run opencode mcp auth figma , and the OAuth flow dies with an HTTP 403 whose entire body is the word Forbidden . There is nothing to parse and nothing to look up: A developer reported that on Figma's forum in April 2026. The mechanism behind it is more interesting than the outage: Figma's authorization server is checking a string that the client fills in itself. Why does Figma's MCP server return 403 Forbidden? A Figma staff member explained it on the same thread in May: the remote MCP server allowlists client name during dynamic client registration, opencode isn't on the list, and the mcp:connect scope is "intentionally gated to supported clients while we're in beta." Dynamic Client Registration is how an MCP client that has never met a server gets a client ID. The client POSTs its own metadata redirect uris , client name , grant types , response types , token endpoint auth method , and the authorization server stores it and issues an ID. Every value in that request comes from the client. Figma reads client name out of it and compares it against its supported-client list, which its help center says includes Claude Code, Claude Desktop, Cursor, VS Code, Codex, Gemini CLI, Kiro, Warp, Replit, and Xcode in beta, among others. The same gate surfaces in three ways depending on how you approach it: - Register through DCR with an unlisted name and the registration endpoint returns a bare 403. - Request the scope as a normal third-party OAuth app and the MCP endpoint answers with www-authenticate: Bearer scope="mcp:connect" , while the authorization request fails with "Invalid scopes for app" . - Ask about policy and Figma's Community Support says MCP access is "currently limited to supported clients and integrations," pointing to a waitlist. What sits behind the gate matters. Figma calls its remote server the preferred option, and write-to-canvas "requires the remote Figma MCP server." So the allowlist decides which agents can edit design files, not only read them. Why isn't a client name allowlist an identity check? An allowlist keyed on self-asserted metadata sorts clients by honesty, not by identity. The Hacker News thread "Figma restricts MCP access to whitelisted clients, excluding Pi" passed 180 points and 100 comments on October 1, and worked that out in a few replies. One commenter put the bypass plainly: "Rather than constraining the callback url to pre-registered partners, you just need to enter 'Claude Code' as your product name" Another explained why the usual OAuth guardrail does nothing here: "callback urls are all localhost, there's no domain to whitelist, just client names" Then came the demonstrations. A Pi user reported that after Pi shipped an OAuth client-name field, "I just wrote Codex and Figma mcp works now." A public repository now automates the same trick by registering as "Claude Code." And GitHub's own Copilot CLI hit the wall from the other side: an open issue shows registration with client name: "copilot-cli" rejected with a 403, while "GitHub Copilot CLI" passes. Same client, same code, different string. The OpenID Foundation's paper on identity for agentic AI, published in October 2025, named this failure in the general case: "The MCP protocol's approach to scalability leveraged Dynamic Client Registration, allowing any client to register with a server and obtain credentials. While this model offers frictionless onboarding in a "many-to-many" ecosystem, it introduces a critical security flaw: it creates a large number of anonymous clients." The same paper is blunt that a client ID generated during dynamic client registration "is not a robust stand-alone workload identity." Figma built its access policy on top of exactly that. The policy excludes clients that describe themselves accurately and admits anything willing to type a different name. That is lock-in, without the assurance that is supposed to come with it. What does onboarding look like today? The second cost is procedural. Figma's Community Support tells developers that "we're being intentional about the integrations we support in the near term," and that the way in is the MCP catalog waitlist, Figma Support, or your account representative. On the opencode thread, Figma suggested developers upvote a GitHub issue and later said it was "actively working on it," with no ETA. On Hacker News, opencode's maintainer was quoted saying "we've had an email thread going on for 8 months trying to get it setup in opencode." Granularity suffers too. GitHub's Copilot CLI works under the right name, while the Copilot desktop app gets a 403. An internal AWS Bedrock AgentCore gateway, neither a public client nor an IDE plugin, hits the same wall, and Figma's support answer is that there is no self-serve path for internal clients. That is the predictable result when access is a hand-maintained list of display names rather than a policy evaluated against something the server can check. A partnership program is a legitimate business decision. Running one through OAuth's registration endpoint, where developers expect a protocol, is how you end up with a 403 whose body is one word. Is the allowlist a reasonable tradeoff? The strongest defense of Figma's position came from a commenter who runs security review at their own company and reached the same design: "we decided to do an allowlist pattern because it was a reasonable tradeoff. The solution is allowing per-tenant client configuration, but that comes with its own set of issues" That tradeoff is real, and the context supports it. Figma's MCP server is in beta and free, with Figma saying it "will eventually be a usage-based paid feature," and write access to customer design files is not a surface you open casually. Gating is defensible. Gating on a field the client fills in itself is not. What did the MCP spec change? MCP's 2026-07-28 revision deprecated Dynamic Client Registration: "Dynamic Client Registration is deprecated. New implementations should use Client ID Metadata Documents instead. This option remains available for backwards compatibility with authorization servers that do not support Client ID Metadata Documents." Clients now follow a priority order: pre-registered client information first, then Client ID Metadata Documents CIMD if the authorization server advertises client id metadata document supported , then DCR as a fallback, then prompting the user. Under CIMD, the client id is an HTTPS URL with a path component, and the document it points to must include at least client id , client name , and redirect uris . The spec's own example: The authorization server fetches that URL, MUST check that the document's client id matches the URL exactly, and MUST validate the redirect URI in the authorization request against the list in the document. Because the identifier is a URL the client hosts, it is portable: "No re-registration is needed when the authorization server changes." Nobody has to break existing partners to get there. A server can advertise CIMD support and keep its registration endpoint running for clients that haven't moved, which is the backwards compatibility the spec deliberately preserves. The allowlist can survive the migration; what changes is the field it reads. Does CIMD prove which client is connecting? Partly, and the part it doesn't prove is exactly where Figma's problem lives. For a hosted client with an HTTPS redirect URI, CIMD gives you a real anchor. The client's identity is a document on a domain someone controls, and an impostor who presents that client's metadata URL never receives the authorization code, because the code goes to the real app's HTTPS redirect. As WorkOS has put it, "CIMD proves you control a domain, not that you're trustworthy," and for an allowlist, domain control is a far better key than a typed name. For CLIs and desktop apps, the clients this whole dispute is about, the redirect URI is localhost . The spec says so directly: "Client ID Metadata Documents cannot prevent localhost URL impersonation by themselves." Any local program can present Claude Code's metadata URL and listen on the loopback port the code is sent to. The spec's answer is to warn users about localhost-only redirect URIs, "clearly display the redirect URI hostname during authorization," and optionally require additional attestation. That changes the conclusion. For distributed native clients, no registration method proves which binary is calling. CIMD makes the identity claim honest and stable, but it can't make an allowlist of public CLIs enforceable. Figma's list doesn't fail because it picked the wrong field. It fails because it is trying to enforce something the protocol can't prove for these clients. What should an MCP server gate on instead? Verify the client where you can, and say so where you can't. Accept URL-formatted client IDs, fetch the document, and enforce the exact client id match and the redirect URI check. For hosted clients with HTTPS redirects, key your policy on that domain. For localhost-only clients, treat client identity as a claim, show the redirect host and a warning on the consent screen as the spec requires, and don't hang write access on it. Tier on scopes, not names. A bare 403 is the least informative answer a server can give. The spec separates "token invalid" 401 from "invalid scopes or insufficient permissions" 403 and tells servers to name the required scopes in the WWW-Authenticate challenge. A signed-in user on an unknown client can hold read scopes while write-to-canvas sits behind a higher tier, and the client learns exactly which scope it lacks: Let the customer's admin decide. The commenter defending allowlists named the real fix: "per-tenant client configuration." Write access to a company's design files is that company's call. A per-organization policy, set by the customer's admin, for which clients may hold which scopes puts the decision with the party that owns the data, instead of a vendor list that every new tool has to petition. Publish the path to the next tier. Stated requirements, a visible queue, and a self-serve way for an organization to approve the tools its own people use. "Email your account representative" and an eight-month thread are not an onboarding process. They are a bottleneck that scales with headcount. Key revocation and audit on the same identifiers. Log the client ID, user, organization, and scope behind every grant and every write. A bad client can then be denied by its client ID and by org policy, rather than by a display name anyone can retype. That is the lifecycle and audit discipline the OpenID Foundation paper asks for, where audit logs record "not only who authorized an action but also which specific agent instance performed it." WorkOS implements the server half of this: if an MCP client shows up with a URL-style client ID, "AuthKit will fetch, validate, and cache that metadata per the 2025-11-25 spec, no Dynamic Client Registration required." The mechanics of both registration patterns are covered in CIMD vs DCR https://workos.com/blog/mcp-client-registration-cimd-vs-dcr , Client ID Metadata Documents in MCP https://workos.com/blog/client-id-metadata-documents-cimd-oauth-client-registration-mcp , and where DCR still makes sense https://workos.com/blog/dynamic-client-registration-dcr-mcp-oauth . Figma's beta will end, and the features behind this gate are slated to become a paid, usage-based product. At that point the allowlist stops being a beta guardrail and becomes the access-control model customers are paying for. The first security questionnaire will ask what proves a client is the client it claims to be. For a list of CLI names, the honest answer is nothing, and the better answer is to stop asking that question of the client and start asking it of the user, the organization, and the scopes they were granted.