npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@editpink/mcp

v0.11.0

Published

Pink Editor MCP server — gives your AI client editorial framework + content graph + recall over your published content

Downloads

53

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/*  ──►  executor

Why: 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:

  1. /pink slash prompt — type /pink in 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.
  2. get_foundation tool — embeds the same playbook in its response, so models that skip /pink still learn the rules the moment they call it.
  3. Tool description breadcrumbs — list_sites → set_site → get_foundation walks any first-time model through the bootstrap.

Install — pick the path that matches your client

There are two transports. If your client accepts a URL, use the remote URL — it's the simplest and most reliable path and needs no local install. The local stdio bridge is for clients that only run a command (Claude Code, Claude Desktop).

| Your client | Use | |---|---| | ChatGPT, Cursor, Perplexity, Codex, Claude.ai, any "add a URL" connector | Remote URL (below) | | Claude Code, Claude Desktop, Windsurf (local command) | Stdio bridge (below) |

First, mint a key at https://app.edit.pink/dashboard/api-keys (cm_live_…).

Remote URL — simplest, no OAuth (recommended)

Paste your key into the URL. No header config, no OAuth round-trip:

https://app.edit.pink/api/mcp/cm_live_<your-key>

Or, if your client prefers a header:

{
  "mcpServers": {
    "editpink": {
      "url": "https://app.edit.pink/api/mcp",
      "headers": { "Authorization": "Bearer cm_live_<your-key>" }
    }
  }
}

OAuth (only if your client insists on it): point it at https://app.edit.pink/api/mcp with no key and it'll discover the OAuth flow via WWW-Authenticate / /.well-known/oauth-protected-resource. This works, but the URL-embedded key above sidesteps OAuth entirely and is the path we recommend when a client supports it.

Codex CLI

Codex's remote-MCP + OAuth support is experimental and must be enabled. Easiest is the URL-embedded key, which avoids OAuth altogether — add to ~/.codex/config.toml:

[mcp_servers.editpink]
url = "https://app.edit.pink/api/mcp/cm_live_<your-key>"

If you want the header form with a bearer token instead:

[mcp_servers.editpink]
url = "https://app.edit.pink/api/mcp"
bearer_token_env_var = "EDITPINK_API_KEY"   # export EDITPINK_API_KEY=cm_live_…

To use the OAuth flow specifically, enable the experimental client and log in:

[features]
experimental_use_rmcp_client = true
codex mcp login editpink

Stdio bridge — Claude Code / Claude Desktop

Add to ~/.claude.json (Claude Code) or ~/Library/Application Support/Claude/claude_desktop_config.json (Claude Desktop, macOS — %APPDATA%/Claude/claude_desktop_config.json on Windows):

{
  "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 the client. Tools appear as mcp__editpink__<tool> (e.g. mcp__editpink__list_sites). The /pink prompt appears in the slash menu.

Windsurf: same JSON block in ~/.codeium/windsurf/mcp_config.json.

Auth — stdio bridge device flow

The remote URL paths above carry the key directly, so there's nothing to authorize. The stdio bridge authorizes on the first tool call via the RFC 8628 device-authorization flow:

  1. The first tool call requests a user_code + verification URL and opens your browser (also printed to stderr).
  2. The bridge does not block: it waits ~25s for approval, then returns the verification URL + code as the tool result so your AI client shows you exactly where to approve. Approve, then ask it to run the tool again — the next call picks up the approval automatically and persists the token to ~/.editpink-mcp-state.json.
  3. Subsequent tool calls use that token; no re-auth until you revoke it.

Skip the flow entirely by setting EDITPINK_API_KEY (a cm_live_… key) in the env block — 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

  1. /pink — loads playbook, foundation (if a site is already pinned), and the list of your sites.
  2. If no site is pinned, the prompt tells the model to call list_sites then set_site({site_id: <id>}). The pinned site survives across restarts.
  3. 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. | | generate_article({ page_id }) | The only publish entry point. State machine: every response carries { run_token, step, instruction, payload, next_call } — follow it until step: "done". Your only job is the write_* step(s): execute payload.prompts[*].prompt verbatim and return plain markdown prose. The server does research, structuring into Block[], link + citation resolution, and the push. | | get_pipeline_status({ page_id }) | Poll run progress. Don't tight-loop in one turn — check on the user's next message. |

Submit ONE page per call. payload is { copies: { "<page_id>": "<prose>" }, append?: true, more?: true }. The server banks each copy and re-asks for whatever is still missing (a partial submission is a normal response, not an error), and only publishes once every id in payload.expected_keys is in. Returning every child of a pillar in one payload is what silently stalls runs — hosts cap or mangle very large tool arguments. For a single long article, split on blank lines and send each continuation with append: true plus more: true ("not finished"), dropping more on the last chunk.

The older two-phase save_article_copy / save_article_blocks tools are gone — generate_article drives both phases internally.

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 generate_article 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 /device page 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 or EDITPINK_API_KEY mismatch. Delete ~/.editpink-mcp-state.json to re-trigger the device flow, or fix the env var.
  • Tool "X" failed: HTTP 404 cm_sites N not found — set_site pointed at a non-existent site. Run list_sites and pick a real id.
  • No current site — call set_site before invoking any site-scoped tool.
  • Connection refused — the backend at EDITPINK_API_URL isn't reachable.
  • State file corruption — delete ~/.editpink-mcp-state.json and let the device flow run again.