@protoscan/mcp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @protoscan/mcp
2
+
3
+ MCP server for ProtoScan — expose Figma prototype QA as a tool for Claude and other MCP clients.
4
+
5
+ Gives Claude (and any MCP-compatible client) a `scan_figma_prototype` tool that detects dead-end screens, orphan screens, broken back-navigation, small touch targets, overlapping hotspots, missing scroll, and overlay traps in any Figma prototype.
6
+
7
+ ## Setup with Claude Desktop
8
+
9
+ Add this to your `claude_desktop_config.json`:
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "protoscan": {
15
+ "command": "npx",
16
+ "args": ["-y", "@protoscan/mcp"],
17
+ "env": {
18
+ "FIGMA_TOKEN": "your_figma_token_here"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ Config file location:
26
+ - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
27
+ - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
28
+
29
+ Get your Figma token at: **https://www.figma.com/settings** → Personal access tokens
30
+
31
+ ## Tool: `scan_figma_prototype`
32
+
33
+ | Parameter | Type | Required | Description |
34
+ |-----------|------|----------|-------------|
35
+ | `file_key` | string | Yes | Figma file key or full URL |
36
+ | `token` | string | No | Figma PAT (falls back to `FIGMA_TOKEN` env var) |
37
+ | `format` | `terminal` \| `json` | No | Output format (default: `terminal`) |
38
+ | `min_touch_target` | number | No | Minimum touch target size in px (default: 44) |
39
+ | `skip` | string[] | No | Checks to skip |
40
+
41
+ ### Example prompts in Claude
42
+
43
+ ```
44
+ Scan this Figma prototype for issues: https://www.figma.com/design/abc123/My-App
45
+ ```
46
+
47
+ ```
48
+ Run protoscan on file key PDShpnJG1i8wGIf4ErIiRL and return JSON
49
+ ```
50
+
51
+ ## Manual usage
52
+
53
+ ```bash
54
+ # Run directly
55
+ FIGMA_TOKEN=your_token npx @protoscan/mcp
56
+
57
+ # Or install globally
58
+ npm install -g @protoscan/mcp
59
+ FIGMA_TOKEN=your_token mcp-protoscan
60
+ ```
61
+
62
+ ## Related packages
63
+
64
+ - [`@protoscan/cli`](https://www.npmjs.com/package/@protoscan/cli) — Command-line interface
65
+ - [`@protoscan/core`](https://www.npmjs.com/package/@protoscan/core) — Core analysis library
66
+ - [GitHub](https://github.com/oxxo/protoscan)
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import {
7
+ CallToolRequestSchema,
8
+ ListToolsRequestSchema
9
+ } from "@modelcontextprotocol/sdk/types.js";
10
+ import { z } from "zod";
11
+ import { FigmaClient, FigmaApiError, scan, formatTerminal, formatJson } from "@protoscan/core";
12
+ var ScanArgsSchema = z.object({
13
+ file_key: z.string().describe('Figma file key or full URL (e.g. "abc123" or "https://figma.com/design/abc123/Name?node-id=7-2")'),
14
+ token: z.string().optional().describe("Figma Personal Access Token. Falls back to FIGMA_TOKEN env var."),
15
+ format: z.enum(["terminal", "json"]).optional().default("terminal").describe("Output format"),
16
+ min_touch_target: z.number().optional().default(44).describe("Minimum touch target size in px"),
17
+ skip: z.array(z.string()).optional().describe("Checks to skip: dead-end, orphan, back-nav, touch-target, overlap, scroll, overlay-trap")
18
+ });
19
+ function parseFigmaInput(input) {
20
+ const urlMatch = input.match(/figma\.com\/(?:design|file)\/([a-zA-Z0-9]+)/);
21
+ if (urlMatch) {
22
+ const fileKey = urlMatch[1];
23
+ const nodeMatch = input.match(/node-id=([0-9]+-[0-9]+)/);
24
+ const pageIds = nodeMatch ? [nodeMatch[1].replace("-", ":")] : [];
25
+ return { fileKey, pageIds };
26
+ }
27
+ return { fileKey: input, pageIds: [] };
28
+ }
29
+ var server = new Server(
30
+ { name: "protoscan", version: "0.0.1" },
31
+ { capabilities: { tools: {} } }
32
+ );
33
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
34
+ tools: [
35
+ {
36
+ name: "scan_figma_prototype",
37
+ description: "Scan a Figma file for prototype navigation issues: dead-end screens, orphan screens, missing back navigation, undersized touch targets, overlapping hotspots, missing scroll, and overlay traps. Returns a detailed report.",
38
+ inputSchema: {
39
+ type: "object",
40
+ properties: {
41
+ file_key: { type: "string", description: "Figma file key from the URL" },
42
+ token: { type: "string", description: "Figma PAT (optional, falls back to FIGMA_TOKEN env var)" },
43
+ format: { type: "string", enum: ["terminal", "json"], default: "terminal" },
44
+ min_touch_target: { type: "number", default: 44, description: "Min touch target px" },
45
+ skip: { type: "array", items: { type: "string" }, description: "Checks to skip" }
46
+ },
47
+ required: ["file_key"]
48
+ }
49
+ }
50
+ ]
51
+ }));
52
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
53
+ if (request.params.name !== "scan_figma_prototype") {
54
+ throw new Error(`Unknown tool: ${request.params.name}`);
55
+ }
56
+ const args = ScanArgsSchema.parse(request.params.arguments);
57
+ const token = args.token || process.env.FIGMA_TOKEN;
58
+ if (!token) {
59
+ return {
60
+ content: [{
61
+ type: "text",
62
+ text: 'Error: Figma token required. Pass it as "token" parameter or set FIGMA_TOKEN env var.'
63
+ }],
64
+ isError: true
65
+ };
66
+ }
67
+ try {
68
+ const { fileKey, pageIds } = parseFigmaInput(args.file_key);
69
+ const client = new FigmaClient(token);
70
+ const file = await client.getFile(fileKey);
71
+ const result = await scan(file, {
72
+ fileKey,
73
+ minTouchTarget: args.min_touch_target,
74
+ skip: args.skip,
75
+ pageIds: pageIds.length ? pageIds : void 0
76
+ });
77
+ const output = args.format === "json" ? formatJson(result) : formatTerminal(result);
78
+ return {
79
+ content: [{ type: "text", text: output }]
80
+ };
81
+ } catch (error) {
82
+ const message = error instanceof FigmaApiError ? error.message : error instanceof Error ? error.message : String(error);
83
+ return {
84
+ content: [{ type: "text", text: `Error: ${message}` }],
85
+ isError: true
86
+ };
87
+ }
88
+ });
89
+ async function main() {
90
+ const transport = new StdioServerTransport();
91
+ await server.connect(transport);
92
+ console.error("ProtoScan MCP server started");
93
+ }
94
+ main().catch((error) => {
95
+ console.error("Fatal:", error);
96
+ process.exit(1);
97
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@protoscan/mcp",
3
+ "version": "0.0.1",
4
+ "description": "MCP server for ProtoScan — expose Figma prototype QA as a tool for Claude and other MCP clients.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "mcp-protoscan": "./dist/index.js"
16
+ },
17
+ "files": ["dist", "README.md"],
18
+ "scripts": {
19
+ "build": "tsup src/index.ts --format esm --dts",
20
+ "prepublishOnly": "npm run build",
21
+ "dev": "tsx src/index.ts",
22
+ "clean": "rm -rf dist"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.17.1",
29
+ "@protoscan/core": "0.0.1",
30
+ "zod": "^3.23.8"
31
+ },
32
+ "license": "MIT",
33
+ "engines": {
34
+ "node": ">=20.0.0"
35
+ }
36
+ }