{
  "markdown": "# @velocms/mcp\n\nAn [MCP](https://modelcontextprotocol.io) server for [VeloCMS](https://velocms.org) —\ndraft, edit, publish, and manage your blog (posts, media, comments, members,\nsite settings) from Claude Code, Claude Desktop, Cursor, or any other MCP\nclient.\n\nIt's a thin, typed wrapper around the VeloCMS Public API\n(`https://<your-blog>/api/v1`, documented at\n[`/api/openapi`](https://velocms.org/api/openapi) on any VeloCMS site): every\ntool maps to one real API call, with Zod-validated inputs and readable\nerrors — no scraping, no browser automation, just the platform's own\nsupported REST API.\n\n## What it does\n\n| Tool | What it does |\n|---|---|\n| `list_posts` | List posts, paginated, optionally filtered by status (`draft`/`published`) |\n| `get_post` | Fetch a single post by ID, including its full content and SEO fields |\n| `create_post` | Create a new post (defaults to `draft`) |\n| `update_post` | Partially update an existing post — only the fields you pass change |\n| `delete_post` | Permanently delete a post |\n| `publish_post` | Set a post's status to `published` (stamps `published_at`) |\n| `unpublish_post` | Set a post's status back to `draft` |\n| `list_media` | List the media library, paginated, optionally filtered by MIME type |\n| `list_comments` | List comments, paginated, optionally filtered by post or moderation status |\n| `moderate_comment` | Set a comment's status to `approved`, `pending`, or `spam` |\n| `list_members` | List subscribers/readers (emails are masked, e.g. `u***@example.com`) |\n| `get_site_settings` | Read the blog's name, description, and feature flags |\n\nEvery tool ships a description an LLM can read to figure out when and how to\nuse it — that's the point of MCP, not just a REST proxy with extra steps.\nSee [Tool reference](#tool-reference) below for full argument lists.\n\n## Setup\n\n### 1. Get a VeloCMS API key\n\nIn your VeloCMS dashboard: **Settings → API Keys** → create a key with the\nscopes you need (`posts:read`, `posts:write`, `media:read`, `comments:read`,\n`comments:moderate`, `members:read`, `site-settings:read`, etc.). API access\nrequires the **Pro plan or higher**.\n\n### 2. Configure your MCP client\n\nYou don't need to clone this repo — `npx` fetches and runs it on demand.\n\n#### Claude Code\n\n```bash\nclaude mcp add velocms \\\n  --env VELOCMS_SITE_URL=https://myblog.velocms.org \\\n  --env VELOCMS_API_KEY=velo_your_64_char_hex_key_here \\\n  -- npx -y -p @velocms/mcp velocms-mcp\n```\n\nMCP servers register their tools at session start, so restart your Claude\nCode session after adding this.\n\n#### Claude Desktop\n\nAdd to your `claude_desktop_config.json` (Settings → Developer → Edit\nConfig):\n\n```json\n{\n  \"mcpServers\": {\n    \"velocms\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"-p\", \"@velocms/mcp\", \"velocms-mcp\"],\n      \"env\": {\n        \"VELOCMS_SITE_URL\": \"https://myblog.velocms.org\",\n        \"VELOCMS_API_KEY\": \"velo_your_64_char_hex_key_here\"\n      }\n    }\n  }\n}\n```\n\nRestart Claude Desktop after editing.\n\n#### Cursor / any other MCP client\n\nSame shape as above — point the client's MCP config at\n`npx -y -p @velocms/mcp velocms-mcp` with `VELOCMS_SITE_URL` and\n`VELOCMS_API_KEY` set in its `env`. The server speaks standard MCP over\nstdio, so any client that supports stdio MCP servers works.\n\n### 3. Local install (contributing / running from source)\n\n```bash\ngit clone https://github.com/VeloCMS/velocms-mcp.git\ncd velocms-mcp\nnpm install\nnpm run build\ncp .env.example .env\n# edit .env and set VELOCMS_SITE_URL + VELOCMS_API_KEY\nVELOCMS_SITE_URL=... VELOCMS_API_KEY=... node dist/index.js\n```\n\n## Config reference\n\n| Env var | Required | Description |\n|---|---|---|\n| `VELOCMS_SITE_URL` | yes | Your blog's base URL — a `*.velocms.org` subdomain or a bound custom domain. No trailing slash needed. |\n| `VELOCMS_API_KEY` | yes | An API key from `/admin/settings → API Keys`. Requires Pro plan or higher. Never logged. |\n\nIf either is missing, the server prints a clear message to stderr and exits\nimmediately (`process.exit(1)`) — it never starts half-configured.\n\n## Tool reference\n\nArgument names are camelCase; the server maps them to the API's snake_case\nJSON fields for you.\n\n**`list_posts`** — `page?`, `perPage?` (max 100), `status?` (`draft` |\n`published`).\n\n**`get_post`** — `id` (required).\n\n**`create_post`** — `title` (required, ≤255 chars), `slug?`, `contentHtml?`,\n`contentJson?` (TipTap ProseMirror document — prefer `contentHtml` unless\nyou need this), `excerpt?` (≤500), `status?` (`draft` default | `published`),\n`tags?` (string array), `seoTitle?` (≤60), `seoDescription?` (≤160).\n\n**`update_post`** — `id` (required) + any of the `create_post` fields\n(all optional here). At least one field besides `id` is required — the tool\nrejects a no-op call before making any network request.\n\n**`delete_post`** — `id` (required). Permanent.\n\n**`publish_post`** / **`unpublish_post`** — `id` (required). Shorthand for\n`update_post({ status: \"published\" })` / `update_post({ status: \"draft\" })`.\n\n**`list_media`** — `page?`, `perPage?`, `type?` (MIME type prefix, e.g.\n`\"image\"`).\n\n**`list_comments`** — `page?`, `perPage?`, `postId?`, `status?` (`approved`\n| `pending` | `spam`).\n\n**`moderate_comment`** — `id` (required), `status` (required: `approved` |\n`pending` | `spam`).\n\n**`list_members`** — `page?`, `perPage?`, `tier?` (`free` | `paid`).\n\n**`get_site_settings`** — no arguments.\n\n## Error handling model\n\nThe VeloCMS API returns a consistent JSON error envelope:\n\n```json\n{ \"error\": { \"code\": \"RATE_LIMITED\", \"message\": \"...\", \"details\": {} } }\n```\n\nThis client surfaces that message directly (never a raw stack trace),\nenriched with actionable hints:\n\n- **401** (`UNAUTHORIZED`) — the message tells you to check `VELOCMS_API_KEY`.\n- **403** (`PLAN_UPGRADE_REQUIRED`) — the message points at `/admin/billing`.\n- **403** (`INVALID_SCOPE`/`FORBIDDEN`) — the message tells you to check the\n  key's scopes.\n- **429** (`RATE_LIMITED`) — the `Retry-After` response header (seconds) is\n  parsed and included in the error message, and returned as\n  `retryAfterSeconds` if you're calling the client library directly.\n- **Any other non-2xx** — the API's own `code` + `message` are surfaced as-is.\n\nRate limits are plan-based (Pro: 30/min, 1,000/hr · Business: 120/min,\n5,000/hr · Agency: 300/min, 20,000/hr) — this server does not retry\nautomatically; it fails fast with the wait time so an interactive tool call\nnever blocks silently.\n\n## Security\n\n- Your API key is a **tenant-scoped** credential — it can only reach the\n  one blog it was issued for, and only the endpoints its scopes allow.\n- The key is read once from the environment and never logged, echoed, or\n  included in any tool output.\n- Member emails returned by `list_members` are masked by the API itself\n  (e.g. `u***@example.com`) — this server never sees or handles unmasked\n  member PII.\n- Encrypted tenant settings (Stripe keys, AI provider keys) are excluded\n  from `get_site_settings` by the API — there is no way to read them\n  through this server.\n\n## Notes on this MCP server\n\n- **`moderate_comment`'s body field is `status`, not `action`** —\n  `PATCH /api/v1/comments/{id}/moderate` takes `{ \"status\": \"approved\" |\n  \"pending\" | \"spam\" }` per the API's own schema, so the tool's input is\n  named to match.\n- **`get_post` and `get_site_settings` return the record directly** (not\n  wrapped in `{ \"data\": ... }`) — only the write endpoints (`create_post`,\n  `update_post`, `moderate_comment`) wrap their response, matching the API's\n  own inconsistency here (documented in `openapi.yaml`).\n- There is currently no `upload_media` tool — `POST /api/v1/media` takes\n  `multipart/form-data`, which doesn't map cleanly onto typical MCP client\n  transports. `list_media` is available for referencing media already\n  uploaded through the dashboard. Contributions welcome if you need this.\n\n## Development\n\n```bash\nnpm install\nnpm run typecheck   # tsc --noEmit\nnpm test             # vitest — all tests run against a mocked fetch, no live API calls\nnpm run build        # emits dist/\n```\n\nThe test suite covers: the `Authorization: Bearer` header on every request,\neach tool's exact method/URL/body mapping, error-code mapping for\n401/403/429/500 responses, and the missing-env-var fail-fast path. No test\nin this repository makes a real network call.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n",
  "bytes": 8349,
  "sha": "3b17fd06c35349951f0d6ca5e255030b75194decbbf447e7e1ec65fa18790b8c",
  "repo_slug": "velocms/velocms-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_org_velocms_mcp_73076d97/readme"
}