@editpink/mcp
v0.7.3
Published
Pink Editor MCP server — gives your AI client editorial framework + content graph + recall over your published content
Maintainers
Readme
@editpink/mcp — Pink Editor MCP server
Local stdio MCP server that gives your AI client (Claude Code, Claude Desktop, Cursor, Windsurf) the editorial framework + content graph + recall + publishing flow over your Pink-Editor-managed content.
AI client ──stdio──► editpink-mcp ──HTTPS──► app.edit.pink /api/cm/mcp/* ──► executorWhy: drive content work from the client you're already in, using your own Pro/Max subscription quota instead of paying API tokens — and have the host model adopt your editorial framework automatically on every session.
What it does
Three mechanisms converge on "playbook loaded" so the host model behaves like your editorial team, not generic Claude:
/pinkslash prompt — type/pinkin any MCP client; the prompt server-side hydrates the editorial playbook + the current site's foundation (brand, audiences, goals, CTAs) + the list of available sites. First call loads everything; later calls in the same session are lightweight reminders.get_foundationtool — embeds the same playbook in its response, so models that skip/pinkstill learn the rules the moment they call it.- Tool description breadcrumbs —
list_sites→set_site→get_foundationwalks any first-time model through the bootstrap.
Install
Claude Code
Add to ~/.claude.json:
{
"mcpServers": {
"editpink": {
"command": "npx",
"args": ["-y", "@editpink/mcp"],
"env": {
"EDITPINK_API_URL": "https://app.edit.pink"
}
}
}
}Or, while developing against this repo, point at the local file:
{
"mcpServers": {
"editpink": {
"command": "node",
"args": ["/Users/dk/Desktop/main/edit.pink/editor/mcp-servers/pink/index.mjs"],
"env": {
"EDITPINK_API_URL": "http://localhost:3000"
}
}
}
}Then restart Claude Code. Tools appear as mcp__editpink__<tool> (e.g.
mcp__editpink__list_sites). The /pink prompt appears in the slash menu.
Claude Desktop
Same JSON block goes in
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or
%APPDATA%/Claude/claude_desktop_config.json (Windows). Restart the app.
Cursor
Add to ~/.cursor/mcp.json (or the project-local .cursor/mcp.json) with the
same shape.
Windsurf
Add to ~/.codeium/windsurf/mcp_config.json with the same shape.
Auth — first call triggers device flow
On the first tool call, the bridge runs the RFC 8628 device-authorization flow:
- Hits
POST /api/mcp/device-code, gets auser_code+ verification URL. - Opens your default browser to the verification page (or prints the URL on stderr if no browser is available).
- You sign in to Pink Editor and approve the code.
- The bridge polls
POST /api/mcp/device-code/polluntil approved, then persists the issued token to~/.editpink-mcp-state.json. - Subsequent tool calls use that token; no re-auth until you revoke it.
You can skip the flow entirely by setting EDITPINK_API_KEY in the env block
above to an admin API key — useful for headless / CI usage. The env var always
wins over the on-disk token, and is never written to the state file.
First run
/pink— loads playbook, foundation (if a site is already pinned), and the list of your sites.- If no site is pinned, the prompt tells the model to call
list_sitesthenset_site({site_id: <id>}). The pinned site survives across restarts. get_foundation— pulls the site's brand, audiences, goals, CTAs, playbook,writable_cols, skills/memory registries, today's date, and current selection in one call.
Tool surface
Everything below is exposed by the stdio bridge and dispatches to
/api/cm/mcp/call (or a dedicated bridge route for the lifecycle tools).
Site lifecycle (bootstrap)
| Tool | What |
|---|---|
| list_sites | Every site you can read. Each row carries is_member and read_only. |
| set_site({ site_id }) | Pin a site for the rest of the session. Returns the foundation immediately so you don't need a second call. |
| get_site | The pinned site row only (light). |
| get_foundation | Full foundation refresh — same shape set_site returned. |
| get_selection | What the user has open in the Pink Editor UI right now (page/topic with keywords, pains, questions, notes pre-joined). Call before acting on "this" / "the current page". |
Reads & planning
| Tool | What |
|---|---|
| db_select | Site-scoped read of any allowed cm_* table. |
| graph({ predicate }) | Dense CSV map of the content graph. Predicates: inventory, cannibals, orphans, stale, underlinked, missing_primary, coverage, intent_mix, neighbors. |
| recall | Vector recall across the site's cm_chunks. Cheap (one embedding call); run before research. |
| research | Serper SERP + URL scrape + LLM synthesis. Paid. |
| fetch_keywords | Google Keyword Planner volume + CPC for a list of keywords (paid). |
| query_stats | Stats summary across the site's plan. |
Writes (CRUD)
| Tool | What |
|---|---|
| db_insert | Insert into allowed cm_* tables. site_id auto-stamped. |
| db_update | Patch existing rows by id (or composite PK). |
| db_delete | Soft-delete on tables that support it; hard-delete elsewhere. |
| apply_link_delta | Bulk-edit cm_page_links with a soft floor warning when deletes shrink the link count. |
| merge_topics | Merges only — creates and renames go through db_insert / db_update. |
Publishing — the ONLY MCP path to take a page live
Works for every page_type — editorial (Article / Pillar / FAQ / Landing) and Utility (privacy / terms / about). The publish flow is uniform; Utility pages just use a smaller block subset and skip the AUDIENCE: line.
| Tool | What |
|---|---|
| get_article_brief({ page_id }) | Foundation + page + questions + research + recall + block vocab in one read. Free. |
| save_article_copy({ page_id, payload }) | Phase 1 of 2. Write the article PROSE as plain markdown (first line AUDIENCE: <name>). Creates a draft content_articles row and vectorizes the prose. Page is NOT live yet. 1 credit. |
| save_article_blocks({ page_id, body }) | Phase 2 of 2. Convert the prose to Block[] per publisher/schema/blocks.json. Server validates blocks, flips cm_pages.published = 1, AND pushes to the live site via the Pink Publish Protocol. 0 credits (paid in phase 1). |
| generate_article({ page_id }) | Server-side alternative — Anthropic API runs the full pipeline. We pay the tokens. Pillars cascade through child pages (15–30+ min). |
| get_pipeline_status({ page_id }) | Poll the server-side pipeline progress. Don't tight-loop in one turn — check on the user's next message. |
cm_pages.published and cm_questions.published are READ-ONLY through
db_update — flipping them directly bypasses block validation and the
live-site push (the reader never sees a "published" row that wasn't pushed). Use
the two-phase flow above instead.
There is no MCP path to UNPUBLISH or move a page to DRAFT yet — flag to the user if asked.
GSC analytics (read-only)
| Tool | What |
|---|---|
| gsc_query_stats | Aggregated impressions / clicks / position per query, with filters. |
| gsc_striking_distance | Queries ranking 8–20 — close to page-1 wins. |
| gsc_content_decay | Pages losing clicks vs trailing 4-week baseline. |
| gsc_cannibalization | Multiple URLs competing for the same query. |
| gsc_hidden_winners | High-impression, low-CTR rows worth retitling. |
| gsc_forecast | Projected clicks for a planned page. |
Skills, memory, recovery
| Tool | What |
|---|---|
| load_skill({ name }) | Pull a per-site editorial playbook on demand. Discovered via get_foundation. |
| load_memory({ name }) | Pull a per-site memory blob. |
| remember({ name, body }) | Persist future-conversation facts (not drafts/deliverables). |
| undo({ scope: 'last_call' \| 'last_turn' }) | Roll back via the journal. No approval gate — every write commits and is journaled. |
Env vars
| Var | Default | Notes |
|---|---|---|
| EDITPINK_API_URL | http://localhost:3000 | Pink Editor backend origin. Use https://app.edit.pink in production. |
| EDITPINK_API_KEY | — | Optional. If set, skips the device flow. Useful for headless / CI usage. |
State persists at ~/.editpink-mcp-state.json (pinned site_id + issued token)
so you don't have to re-pick a site or re-auth every session.
Troubleshooting
- Browser opened to a
/devicepage asking for a code — that's the device flow on first call. Sign in to Pink Editor and approve the code shown on stderr. Tool "X" failed: HTTP 401— token revoked orEDITPINK_API_KEYmismatch. Delete~/.editpink-mcp-state.jsonto re-trigger the device flow, or fix the env var.Tool "X" failed: HTTP 404 cm_sites N not found—set_sitepointed at a non-existent site. Runlist_sitesand pick a real id.No current site— callset_sitebefore invoking any site-scoped tool.- Connection refused — the backend at
EDITPINK_API_URLisn't reachable. - State file corruption — delete
~/.editpink-mcp-state.jsonand let the device flow run again.
