
Connects Cursor to Pincushion's visual feedback system, where stakeholders drop annotation pins directly on live web pages via Chrome extension. Exposes eight MCP tools including get_actionable_pins, search_annotations, claim_pin, and fix_and_resolve. Ships with four slash commands that let you view all open pins, filter by mentions, resolve issues, and generate priority summaries. Useful when you're working with non-technical stakeholders who review staging or production sites and need a tighter loop than screenshots in Slack. The plugin handles MCP setup automatically, or you can configure it manually with npx and your project directory.
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 an agent work packet. Each one carries everything an agent needs to implement the change without a back-and-forth:
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.
Use Node.js 22.22+ within Node 22, or Node 24, and run this in your app's repository:
npx pincushion-mcp setup
The guided wizard signs you in to Pincushion, asks you to review the project URLs,
and explicitly choose an editor. It writes supported project MCP configuration when
safe; Manual, Skip, or a conflicting existing configuration can leave it unchanged.
Restart the editor and ask your agent to call get_project_context and
get_actionable_pins; configuration written alone does not verify a connection.
If you already have a critique report, run its npx pincushion-mcp claim <report-token>
command from the owning app's repository instead, then verify a real pin read.
Pincushion hosts cloud sync. The normal setup does not require your own Supabase
project, database table, or backend API key. Sign-in supplies the existing account's
cloud credentials automatically; npx pincushion-mcp login is the recovery command
if sign-in is missing or expired. Do not paste credentials into issue reports.
Setup connects feedback access; it does not generate an AI critique. Critique execution requires a supported coding-agent workflow and real page captures. A public report lets reviewers add pins without an extension. Install the optional Chrome extension only when you want to place feedback directly on other web pages.
Use the maintained MCP setup guide for exact Claude Code, Cursor, VS Code, and Codex commands, and the public documentation for current plan limits and troubleshooting. The optional legacy self-host connection flags below are not prerequisites for hosted setup.
npx pincushion-mcp [flags]
| Flag | Description | Default |
|---|---|---|
--project-dir PATH | Root directory containing .feedback/ | Current working directory |
--sync-url URL | Optional legacy self-host adapter endpoint; not hosted setup | None |
--api-key KEY | Optional legacy self-host adapter credential; not hosted setup | None |
--license-key KEY | Explicit account credential override; normal hosted setup uses sign-in | Signed-in account when available |
--rest | Enable REST API mode | Disabled (uses MCP/stdio) |
--port PORT | Port for REST API server | 3456 |
Local project:
npx pincushion-mcp --project-dir /path/to/project
Optional legacy self-host adapter (not hosted Pincushion setup):
npx pincushion-mcp \
--project-dir /path/to/project \
--sync-url https://abcd1234.supabase.co/api \
--api-key sb_project_key_abc123...
REST API server:
npx pincushion-mcp --rest --port 8080
get_annotationsRetrieve annotations from .feedback/. Filter by page, component, or status.
Parameters:
pageUrl (string, optional) — Filter by page URL (partial match)componentName (string, optional) — Filter by LWC component namestatus (string, optional) — Filter by open, in-progress, or resolvedExample:
await mcp.callTool('get_annotations', {
componentName: 'wmlHomePage',
status: 'open'
});
search_annotationsFull-text search across all annotations, comments, selectors, and tags.
Parameters:
query (string, required) — Search termExample:
await mcp.callTool('search_annotations', {
query: 'button label'
});
get_feedback_summaryHigh-level rollup of all feedback: counts by status, priority, page, and component.
Example:
await mcp.callTool('get_feedback_summary', {});
get_component_feedbackGet all feedback for a specific LWC component with a plain-language summary.
Parameters:
componentName (string, required) — LWC component nameExample:
await mcp.callTool('get_component_feedback', {
componentName: 'wmlHomePage'
});
resolve_annotationMark an annotation as resolved after fixing the issue.
Parameters:
annotationId (string, required) — Annotation IDcomment (string, optional) — Resolution messageresolvedBy (string, optional) — Name to attribute resolution (default: "AI Agent")Example:
await mcp.callTool('resolve_annotation', {
annotationId: 'ann_abc123',
comment: 'Updated button label in line 42 of wmlHomePage.js'
});
add_agent_replyAdd a reply to an annotation thread (e.g., ask clarifying questions).
Parameters:
annotationId (string, required) — Annotation IDbody (string, required) — Reply messageauthor (string, optional) — Author name (default: "AI Agent")Example:
await mcp.callTool('add_agent_reply', {
annotationId: 'ann_abc123',
body: 'Is this button in the main navigation or sidebar?'
});
fix_and_resolveCombine 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.
Parameters:
annotationId (string, required) — Annotation IDfixDescription (string, required) — Description of the fixfilePath (string, optional) — File where fix was appliedlineNumber (number, optional) — Line number of the fixcommitSha (string, optional) — Commit SHA that landed the changebranchName (string, optional) — Branch the commit was made onprUrl (string, optional) — Pull request URL (GitHub/GitLab/Bitbucket; shape-validated)Example:
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'
});
get_implementation_packetFetch 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' });
assign_pin_to_agentDispatch a pin straight to your local coding agent. Promotes the pin to ready if not already, marks pending_implementation, and writes a .feedback/.agent-queue/<id>.json trigger file that agent-loop.mjs picks up and shells out to Cursor / Claude Code / Codex.
await mcp.callTool('assign_pin_to_agent', { annotationId: 'ann_abc123' });
link_pin_deployAttach 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'
});
record_pin_verificationWrite Pincushion AI's post-deploy verdict back to the pin. Called by the critic agent after /critique-latest-deploy runs 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.'
});
get_time_to_fix_metricsPro/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_instructions (NEW)Get setup and configuration instructions for all supported agents.
Example:
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.
Recommended use cases:
pin_ready and follow_upmention and optionally resolvedpageUrlPatterns plus pin_ready, follow_up, and resolvedExample:
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, use create_slack_install_link when 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.
Use list_collaboration_integrations to audit configured destinations, remove_collaboration_integration to disconnect one, and preview_collaboration_notification to 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.mjs polls .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-detects claude or cursor on the PATH; falls
back to generic (writes the prompt to .feedback/.agent-prompt and
stdout). Run with --interval 3000 to control poll cadence.
The server reads annotations from .feedback/ in your project:
.feedback/
├── annotations/
│ ├── example-com-login.json
│ ├── example-com-dashboard.json
│ └── ...
└── index.json
Each annotation file contains:
{
"pageUrl": "https://example.com/login",
"pageTitle": "Login",
"annotations": [
{
"id": "ann_abc123",
"status": "open",
"priority": "high",
"tags": ["design", "accessibility"],
"createdAt": "2026-03-19T10:30:00Z",
"element": {
"lwcComponent": "wmlLoginForm",
"selector": ".login-button",
"textContent": "Sign In"
},
"thread": [
{
"author": "Design Team",
"timestamp": "2026-03-19T10:30:00Z",
"body": "Button label should say 'Sign In' not 'Login'",
"type": "comment"
}
]
}
]
}
The --sync-url and --api-key flags select a separate legacy sync adapter.
They are not needed to use Pincushion's hosted service. The older instructions to
create a Supabase project and annotations table are not a supported hosted
onboarding path; simply creating those resources does not establish a compatible
backend. Use this adapter only with an already compatible operator-managed endpoint.
See hosted setup for the default path and
current pricing for account plans.
Make sure you have a supported Node.js runtime: Node 22.22+ within Node 22, or Node 24:
node --version
Install dependencies:
npm install @modelcontextprotocol/sdk
Check that .feedback/ exists in your project directory:
ls -la .feedback/
If it doesn't exist, create it and add some test annotations, or the extension will create it when you pin your first feedback.
Run npx pincushion-mcp login if sign-in is missing or expired, then rerun setup
in the intended repository. Confirm the acknowledged project and registered page
URLs, restart the selected editor, and ask it to read get_project_context and
get_actionable_pins. Preserve existing project identity; do not create a replacement
project or backend to work around an access failure. See the
maintained troubleshooting guide.
Rerun npx pincushion-mcp setup from the intended repository, preserving the
acknowledged project identity and existing configuration. Follow the
maintained editor setup guide if setup leaves a conflicting
configuration unchanged. Restart the selected editor and verify a real project/pin
read. Avoid replacing a project-scoped configuration with an unscoped global entry.
Clone the repository and install dependencies:
git clone https://github.com/jcooley8/pincushion-plugin.git
cd pincushion-plugin
npm install
Run the server:
npm start
Or with test data:
npm start -- --project-dir ./test-feedback
MIT License. See LICENSE file for details.
.feedback/ file supportfix_and_resolve, get_setup_instructions