Skip to content

feat(figma): add Figma MCP with 17 tools via REST API#387

Open
JonasJesus42 wants to merge 2 commits intomainfrom
JonasJesus42/figma-mcp
Open

feat(figma): add Figma MCP with 17 tools via REST API#387
JonasJesus42 wants to merge 2 commits intomainfrom
JonasJesus42/figma-mcp

Conversation

@JonasJesus42
Copy link
Copy Markdown
Contributor

@JonasJesus42 JonasJesus42 commented Apr 15, 2026

Summary

  • Adds a new Figma MCP server with OAuth authentication (authorization code flow) and 17 tools covering the full Figma REST API
  • File tools: get_file, get_file_nodes, get_images, get_image_fills, get_file_metadata, get_file_versions
  • Comment tools: get/post/delete comments and emoji reactions
  • Team tools: get_team_projects, get_project_files, get_team_components, get_team_styles
  • User tools: whoami

Test plan

  • Set FIGMA_CLIENT_ID and FIGMA_CLIENT_SECRET env vars on Kubernetes
  • Verify OAuth flow completes and tokens are exchanged
  • Test figma_whoami tool to confirm authentication works
  • Test figma_get_file with a known file key
  • Test figma_post_comment and figma_get_comments on a test file

🤖 Generated with Claude Code


Summary by cubic

Adds a Figma MCP server with OAuth (PKCE) and 17 tools for files, images, comments/reactions, versions, and team resources via the Figma REST API. Also removes the deprecated TypeScript baseUrl from tsconfig to avoid warnings.

  • New Features

    • 17 tools: files (file, nodes, images, image fills, metadata, versions), comments (get/post/delete, reactions), teams/projects (projects, files, components, styles), user (whoami).
    • OAuth PKCE with code exchange and refresh.
    • MCP app config via figma/app.json (HTTP endpoint).
    • deploy.json adds the figma service (./dist/server/main.js) and new @decocms/figma workspace package.
  • Migration

    • Set FIGMA_CLIENT_ID and FIGMA_CLIENT_SECRET in Kubernetes.
    • Deploy the new figma service and complete OAuth.
    • Verify with figma_whoami, figma_get_file, and a comment roundtrip.

Written for commit 7894b52. Summary will update on new commits.

JonasJesus42 and others added 2 commits April 15, 2026 16:48
Implements a Figma MCP server with OAuth authentication and full coverage
of the Figma REST API: files, nodes, images, comments, reactions, version
history, team projects, components, and styles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TypeScript 5.9+ deprecates baseUrl — not needed since all imports use
relative paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="figma/server/constants.ts">

<violation number="1" location="figma/server/constants.ts:34">
P1: Add the `file_versions:read` OAuth scope; otherwise file version requests may be unauthorized.</violation>
</file>

<file name="figma/server/lib/figma-client.ts">

<violation number="1" location="figma/server/lib/figma-client.ts:191">
P1: `deleteCommentReaction` sends `emoji` in the DELETE body, but the Figma API requires `emoji` as a query parameter.</violation>
</file>

<file name="figma/server/lib/env.ts">

<violation number="1" location="figma/server/lib/env.ts:10">
P2: Bearer token parsing is too permissive; it accepts whitespace inside the token value.</violation>
</file>

<file name="figma/server/tools/files.ts">

<violation number="1" location="figma/server/tools/files.ts:27">
P2: Validate `depth` as a positive integer instead of any number.</violation>
</file>

<file name="figma/server/tools/teams.ts">

<violation number="1" location="figma/server/tools/teams.ts:42">
P2: `page_size` is documented as max 30 but not validated, so invalid values can pass schema checks and fail at runtime.</violation>
</file>

<file name="figma/server/lib/types.ts">

<violation number="1" location="figma/server/lib/types.ts:147">
P2: `cursor` should be optional in `FigmaPaginatedResponse.meta` because API responses may omit it on the last page.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread figma/server/constants.ts
export const FIGMA_SCOPES = [
"files:read",
"file_comments:write",
"file_dev_resources:read",
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Add the file_versions:read OAuth scope; otherwise file version requests may be unauthorized.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At figma/server/constants.ts, line 34:

<comment>Add the `file_versions:read` OAuth scope; otherwise file version requests may be unauthorized.</comment>

<file context>
@@ -0,0 +1,36 @@
+export const FIGMA_SCOPES = [
+  "files:read",
+  "file_comments:write",
+  "file_dev_resources:read",
+  "library_content:read",
+];
</file context>
Fix with Cubic

Comment on lines +191 to +194
await this.request<void>(ENDPOINTS.COMMENT_REACTIONS(fileKey, commentId), {
method: "DELETE",
body: JSON.stringify({ emoji }),
});
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: deleteCommentReaction sends emoji in the DELETE body, but the Figma API requires emoji as a query parameter.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At figma/server/lib/figma-client.ts, line 191:

<comment>`deleteCommentReaction` sends `emoji` in the DELETE body, but the Figma API requires `emoji` as a query parameter.</comment>

<file context>
@@ -0,0 +1,246 @@
+    commentId: string,
+    emoji: string,
+  ): Promise<void> {
+    await this.request<void>(ENDPOINTS.COMMENT_REACTIONS(fileKey, commentId), {
+      method: "DELETE",
+      body: JSON.stringify({ emoji }),
</file context>
Suggested change
await this.request<void>(ENDPOINTS.COMMENT_REACTIONS(fileKey, commentId), {
method: "DELETE",
body: JSON.stringify({ emoji }),
});
await this.request<void>(
`${ENDPOINTS.COMMENT_REACTIONS(fileKey, commentId)}?emoji=${encodeURIComponent(emoji)}`,
{
method: "DELETE",
},
);
Fix with Cubic

Comment thread figma/server/lib/env.ts
"Missing authorization header. Please authenticate with Figma first.",
);
}
const match = auth.match(/^Bearer\s+(.+)$/i);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Bearer token parsing is too permissive; it accepts whitespace inside the token value.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At figma/server/lib/env.ts, line 10:

<comment>Bearer token parsing is too permissive; it accepts whitespace inside the token value.</comment>

<file context>
@@ -0,0 +1,15 @@
+      "Missing authorization header. Please authenticate with Figma first.",
+    );
+  }
+  const match = auth.match(/^Bearer\s+(.+)$/i);
+  if (!match || !match[1].trim()) {
+    throw new Error("Invalid authorization header. Expected Bearer token.");
</file context>
Fix with Cubic

"List of node IDs to retrieve. If specified, only those nodes and their children are returned.",
),
depth: z
.number()
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Validate depth as a positive integer instead of any number.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At figma/server/tools/files.ts, line 27:

<comment>Validate `depth` as a positive integer instead of any number.</comment>

<file context>
@@ -0,0 +1,177 @@
+          "List of node IDs to retrieve. If specified, only those nodes and their children are returned.",
+        ),
+      depth: z
+        .number()
+        .optional()
+        .describe(
</file context>
Fix with Cubic

@@ -0,0 +1,94 @@
import { createPrivateTool } from "@decocms/runtime/tools";
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: page_size is documented as max 30 but not validated, so invalid values can pass schema checks and fail at runtime.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At figma/server/tools/teams.ts, line 42:

<comment>`page_size` is documented as max 30 but not validated, so invalid values can pass schema checks and fail at runtime.</comment>

<file context>
@@ -0,0 +1,94 @@
+      "Get all published components in a Figma team library. Returns component metadata including names, descriptions, thumbnails, and containing frames. Supports cursor-based pagination.",
+    inputSchema: z.object({
+      team_id: z.string().describe("The ID of the Figma team."),
+      page_size: z
+        .number()
+        .optional()
</file context>
Fix with Cubic

Comment thread figma/server/lib/types.ts
meta: {
components?: T[];
styles?: T[];
cursor: Record<string, number>;
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: cursor should be optional in FigmaPaginatedResponse.meta because API responses may omit it on the last page.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At figma/server/lib/types.ts, line 147:

<comment>`cursor` should be optional in `FigmaPaginatedResponse.meta` because API responses may omit it on the last page.</comment>

<file context>
@@ -0,0 +1,149 @@
+  meta: {
+    components?: T[];
+    styles?: T[];
+    cursor: Record<string, number>;
+  };
+}
</file context>
Suggested change
cursor: Record<string, number>;
cursor?: { before?: number; after?: number };
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant