@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 +66 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +97 -0
- package/package.json +36 -0
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)
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|