Pincushion
About
Stakeholders pin feedback on your live app; your AI coding agent reads it via MCP and fixes it
Details
- Author
- jcooley8
- Categories
- Developer Tools, Other
Jump to
Setup
Install Pincushion in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/jcooley8/pincushion-plugin
Follow the installation instructions in the repository README, then restart your MCP client.
The implementation-context layer for AI-native development. Stakeholders drop visual pins on any page of your live app; your AI coding agent reads each pin through MCP and ships the fix — in Claude Code, Cursor, VS Code, Windsurf, or any MCP client.
A pin isn't a feedback item — it's anagent work packet. Each one carries everything an agent needs to implement the change without a back-and-forth:
- URL + element selector— exactly what, exactly where
- Screenshot + viewport + DOM snippet— the visual and structural context
- Thread + project context— the conversation and the codebase it lives in
- Likely files + acceptance criteria— where to look, and how to know it's done
The loop closes itself: a stakeholder pins it → your agent reads it via MCP and fixes it in your IDE → the resolve records the commit, branch, and PR → an optional post-deploy critique verifies the fix actually landed.
This server is also how Pincushion AI runs design/copy/a11y critiques on a live page and writes the pins straight back onto it.
# npm npm install -g pincushion-mcp # pnpm pnpm add -g pincushion-mcp # yarn yarn global add pincushion-mcp
# npm npx pincushion-mcp --project-dir . # pnpm pnpm dlx pincushion-mcp --project-dir . # yarn yarn dlx pincushion-mcp --project-dir .
Download the Pincushion Chrome extension frompincushion.io/install/chrome.
Pick your AI agent below and follow the configuration for your setup.
- See all feedback:get_feedback_summary
- Find specific pins:search_annotations
- Fix and mark as done:fix_and_resolve
{ "mcpServers": { "pincushion": { "command": "npx", "args": ["pincushion-mcp", "--project-dir", "."] } } }
pnpm / yarn users:replace"command": "npx"with"command": "pnpm"and add"dlx"as the first arg, or use"command": "yarn"with"dlx"likewise.
{ "mcpServers": { "pincushion": { "command": "npx", "args": [ "pincushion-mcp", "--project-dir", ".", "--sync-url", "https://your-supabase.com/api", "--api-key", "YOUR_API_KEY" ] } } }
File:~/.config/Claude/claude_desktop_config.json(Linux/Windows) or~/Library/Application Support/Claude/claude_desktop_config.json(macOS)
{ "mcpServers": { "pincushion": { "command": "npx", "args": ["pincushion-mcp", "--project-dir", "/path/to/your/project"] } } }
{ "mcpServers": { "pincushion": { "command": "pnpm", "args": ["dlx", "pincushion-mcp", "--project-dir", "/path/to/your/project"] } } }
{ "mcpServers": { "pincushion": { "command": "yarn", "args": ["dlx", "pincushion-mcp", "--project-dir", "/path/to/your/project"] } } }
{ "mcpServers": { "pincushion": { "command": "npx", "args": [ "pincushion-mcp", "--project-dir", "/path/to/your/project", "--sync-url", "https://your-supabase.com/api", "--api-key", "YOUR_API_KEY" ] } } }
Run this command to add Pincushion to Claude Code:
claude mcp add pincushion -- npx pincushion-mcp --project-dir .
claude mcp add pincushion -- npx pincushion-mcp --project-dir . --sync-url https://your-supabase.com/api --api-key YOUR_API_KEY
{ "mcp.servers": { "pincushion": { "command": "npx", "args": ["pincushion-mcp", "--project-dir", "${workspaceFolder}"] } } }
File:~/.windsurf/mcp.jsonor~/.config/windsurf/mcp.json
{ "mcpServers": { "pincushion": { "command": "npx", "args": ["pincushion-mcp", "--project-dir", "."] } } }
{ "mcpServers": { "pincushion": { "command": "npx", "args": ["pincushion-mcp", "--project-dir", "."] } } }
For tools that don't support MCP directly, use the REST API wrapper:
This starts an HTTP server onlocalhost:3456. Endpoints:
- GET /health— Check server status
- POST /call-tool— Invoke a tool
- Body:{ "toolName": "get_feedback_summary", "args": {} }
curl -X POST http://localhost:3456/call-tool \ -H "Content-Type: application/json" \ -d '{"toolName": "get_feedback_summary", "args": {}}'
npx pincushion-mcp --project-dir /path/to/project
npx pincushion-mcp \ --project-dir /path/to/project \ --sync-url https://abcd1234.supabase.co/api \ --api-key sb_project_key_abc123...
Retrieve annotations from.feedback/. Filter by page, component, or status.
- pageUrl(string, optional) — Filter by page URL (partial match)
- componentName(string, optional) — Filter by LWC component name
- status(string, optional) — Filter byopen,in-progress, orresolved
await mcp.callTool('get_annotations', { componentName: 'wmlHomePage', status: 'open' });
Full-text search across all annotations, comments, selectors, and tags.
await mcp.callTool('search_annotations', { query: 'button label' });
High-level rollup of all feedback: counts by status, priority, page, and component.
await mcp.callTool('get_feedback_summary', {});
Get all feedback for a specific LWC component with a plain-language summary.
- componentName(string, required) — LWC component name
await mcp.callTool('get_component_feedback', { componentName: 'wmlHomePage' });
Mark an annotation as resolved after fixing the issue.
- annotationId(string, required) — Annotation ID
- comment(string, optional) — Resolution message
- resolvedBy(string, optional) — Name to attribute resolution (default: "AI Agent")
await mcp.callTool('resolve_annotation', { annotationId: 'ann_abc123', comment: 'Updated button label in line 42 of wmlHomePage.js' });
Add a reply to an annotation thread (e.g., ask clarifying questions).
- annotationId(string, required) — Annotation ID
- body(string, required) — Reply message
- author(string, optional) — Author name (default: "AI Agent")
await mcp.callTool('add_agent_reply', { annotationId: 'ann_abc123', body: 'Is this button in the main navigation or sidebar?' });
Combine fixing code and marking an annotation as resolved in one call. Optionally records commit / branch / PR metadata so the dashboard can backlink to what shipped.
- annotationId(string, required) — Annotation ID
- fixDescription(string, required) — Description of the fix
- filePath(string, optional) — File where fix was applied
- lineNumber(number, optional) — Line number of the fix
- commitSha(string, optional) — Commit SHA that landed the change
- branchName(string, optional) — Branch the commit was made on
- prUrl(string, optional) — Pull request URL (GitHub/GitLab/Bitbucket; shape-validated)
await mcp.callTool('fix_and_resolve', { annotationId: 'ann_abc123', fixDescription: 'Updated button label to match design spec', filePath: 'src/components/wmlHomePage.js', lineNumber: 42, commitSha: 'abc123def456', branchName: 'pincushion/checkout-fix', prUrl: 'https://github.com/acme/app/pull/142' });
Fetch a single implementation packet for one page URL — selector list, full pin payloads, suggested branch name, and traceability config. Use when an agent wants to batch-fix one page in a single branch.
await mcp.callTool('get_implementation_packet', { pageUrl: '/checkout' });
Dispatch a pin straight to your local coding agent. Promotes the pin toreadyif not already, markspending_implementation, and writes a.feedback/.agent-queue/<id>.jsontrigger file thatagent-loop.mjspicks up and shells out to Cursor / Claude Code / Codex.
await mcp.callTool('assign_pin_to_agent', { annotationId: 'ann_abc123' });
Attach a deploy URL to a resolved pin. Typically called by the deploy-hook edge function once production includes the fix, but available manually too.
await mcp.callTool('link_pin_deploy', { annotationId: 'ann_abc123', deployUrl: 'https://acme-app.vercel.app' });
Write Pincushion AI's post-deploy verdict back to the pin. Called by the critic agent after/critique-latest-deployruns against a fresh deploy.
await mcp.callTool('record_pin_verification', { annotationId: 'ann_abc123', status: 'verified', // or 'regressed' or 'inconclusive' notes: 'Button matches the primary token. No regression on adjacent CTAs.' });
Pro/Team feature— Free callers get sample size + upgrade hint. Median + p25/p75 of pin-to-resolve duration, with a 5-pin minimum so the metric is never noise.
await mcp.callTool('get_time_to_fix_metrics', { scope: 'project', projectId: 'pc_proj_abc' }); // → { sampleSize, thresholdMet, median, p25, p75, medianHuman, ... }
Get setup and configuration instructions for all supported agents.
await mcp.callTool('get_setup_instructions', {});
Pincushion can notify Slack or Microsoft Teams through project-scoped incoming webhooks. The defaults are intentionally quiet and Figma-inspired: notify when a pin is ready for implementation, when someone is @mentioned, and when a collaborator adds follow-up on work already being handled. Every newly dropped pin and every resolution are opt-in events.
- Developer channel:pin_readyandfollow_up
- Design or PM channel:mentionand optionallyresolved
- Launch or QA channel:pageUrlPatternspluspin_ready,follow_up, andresolved
- Temporary incident channel: enable a focused subscription, then pause it after the ship window
await mcp.callTool('configure_collaboration_integration', { projectId: 'my-project', provider: 'slack', webhookUrl: 'https://hooks.slack.com/services/...', targetLabel: '#product-feedback', events: ['pin_ready', 'mention', 'follow_up'], pageUrlPatterns: ['staging.example.com/checkout'], sendTest: true });
For Slack, usecreate_slack_install_linkwhen the hosted Slack app secrets are configured. It returns an Add-to-Slack URL; after approval, Slack returns the incoming webhook and Pincushion stores it automatically.
Uselist_collaboration_integrationsto audit configured destinations,remove_collaboration_integrationto disconnect one, andpreview_collaboration_notificationto see the payload shape before adding a real webhook. Webhook URLs are stored server-side and returned only as masked values.
For agents that don't watch the file system (Claude Code, Cursor, generic),agent-loop.mjspolls.feedback/.agent-queue/and dispatches new pins to the configured agent automatically.
# from inside the pincushion-mcp directory npm run agent-loop -- --project-dir /path/to/your/project # or directly node agent-loop.mjs --project-dir /path/to/your/project [--agent claude-code|cursor|generic] [--interval 3000]
The bridge (server.js) writes one trigger file per approved pin into.feedback/.agent-queue/. The loop reads them, builds a prompt with the pin's thread + element selector, and shells out to the chosen agent. The agent uses MCP tools (claim_pin→ fix →fix_and_resolve) and the queue file is removed when the pin closes.
detectAgent()auto-detectsclaudeorcursoron thePATH; falls back togeneric(writes the prompt to.feedback/.agent-promptand stdout). Run with--interval 3000to control poll cadence.
The server reads annotations from.feedback/in your project:
.feedback/ ├── annotations/ │ ├── example-com-login.json │ ├── example-com-dashboard.json │ └── ... └── index.json
To sync annotations with a remote Supabase database:
- Set up a Supabase project atsupabase.com
- Create anannotationstable with columns matching the annotation schema
- Generate an API key from your project settings
- Configure the server with--sync-urland--api-key
npx pincushion-mcp \ --project-dir . \ --sync-url https://your-project.supabase.co/rest/v1 \ --api-key sb_project_key_abc123...
The server merges local.feedback/files with remote data, with remote taking precedence on newer updates.
Pincushion Pro includes additional features. Activate with--license-key:
npx pincushion-mcp --project-dir . --license-key YOUR_PRO_KEY
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.





