MCP Server
The built-in Model Context Protocol server — read and write content from AI tools.
MCP Server
Invert ships with two MCP (Model Context Protocol) servers:
- Local (stdio) — runs on your machine during development, reads and writes files directly
- Edge (HTTP) — runs on Cloudflare Pages, readable and writable from anywhere via the deployed URL
Both expose the same core tools. Which one you use depends on where you're working.
Local MCP server
Start alongside your dev server:
npm run mcp
The server runs on stdio. It reads content from your local content/ directory and writes JSON files directly to disk. Changes appear immediately in dev mode.
Connecting to Claude Code (local)
Add to your project .mcp.json:
{
"mcpServers": {
"invert": {
"command": "npm",
"args": ["run", "mcp"],
"cwd": "/path/to/your/invert/site"
}
}
}
Connecting to Claude Desktop (local)
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"invert": {
"command": "npm",
"args": ["run", "mcp"],
"cwd": "/path/to/your/invert/site"
}
}
}
Restart Claude Desktop after saving.
Edge MCP server (Cloudflare Pages)
Setup required: The edge MCP server uses Cloudflare Pages Functions and lives at
functions/api/mcp/— the path Cloudflare Pages serves automatically. Configure your KV namespace and GitHub vars inwrangler.jsonc, then deploy. See Cloudflare Pages deployment.
When deployed to Cloudflare Pages, your site exposes an MCP server at /api/mcp. This uses the MCP Streamable HTTP transport and is accessible from Claude Code, Claude Desktop, or any MCP client.
Once deployed:
Connecting to Claude Code (edge)
Claude Code supports Streamable HTTP natively. Add to your project .mcp.json, or run:
claude mcp add --transport http my-site https://your-project.pages.dev/api/mcp
Or manually in .mcp.json:
{
"mcpServers": {
"my-site": {
"type": "http",
"url": "https://your-project.pages.dev/api/mcp"
}
}
}
The "type": "http" field is required — without it Claude Code won't connect.
Connecting to Claude Desktop (edge)
Claude Desktop does not support "type": "http" MCP entries — it silently skips them. Use mcp-remote as a stdio bridge instead:
{
"mcpServers": {
"my-site": {
"command": "npx",
"args": ["mcp-remote", "https://your-project.pages.dev/api/mcp"]
}
}
}
mcp-remote proxies the stdio transport that Claude Desktop expects to your HTTP endpoint. No global install needed — npx fetches it on first use.
Restart Claude Desktop after saving.
Verifying the edge server
GET /api/mcp returns 405 — that's correct per the MCP spec. For a human-readable status page:
https://your-project.pages.dev/api/mcp/info
Returns tool names and whether GitHub write-back sync is configured.
Tools
Both servers expose the same core tools. The local server additionally exposes invert_normalize_and_create for importing content from external sources — this is local-only since it requires direct filesystem access.
invert_list
List content items, optionally filtered by type.
invert_list(contentType?: string, limit?: number, offset?: number)
invert_get
Get a single content item by type and slug.
invert_get(contentType: string, slug: string)
invert_search
Full-text search across all content (searches title, body, and excerpt).
invert_search(query: string)
invert_types
List all available content types.
invert_types()
invert_create
Create a new content item.
invert_create(id, slug, title, body, contentType, status?, date?, author?, excerpt?, ...)
The optional status field controls where the content is stored:
status |
Local | Edge |
|---|---|---|
"draft" |
.drafts/{type}/{slug}.json (gitignored) |
Draft KV prefix — no GitHub commit |
"published" or omitted |
content/{type}/{slug}.json |
Live KV + async GitHub commit |
Draft content is only accessible via MCP read tools and the /preview/{type}/{slug} URL. It is never served at the canonical URL and never included in the static site build.
invert_update
Update fields on an existing content item.
invert_update(contentType: string, slug: string, updates: Partial<InvertContent>)
Accepts status in the updates object. Changing status between "draft" and "published" moves the item between stores — no manual file management required.
invert_publish
Promote a draft to published.
invert_publish(contentType: string, slug: string)
Moves the item from the draft store to the live store and sets status: "published". On the edge, queues the async GitHub commit. Locally, moves the file from .drafts/ to content/.
Prefer this over calling invert_update with status: "published" — it handles the promotion atomically.
Returns an error if no draft exists with that type and slug.
invert_delete
Delete a content item.
invert_delete(contentType: string, slug: string)
Checks both the live and draft stores. On the edge, only queues a GitHub commit if the item was in the live store — deleting a draft has no GitHub side-effect.
invert_normalize_and_create
Normalize raw content returned by a source MCP and import it as Invert content. This tool handles field mapping from WordPress and Drupal data shapes to InvertContent — you pass the raw object from the source, it normalizes and writes.
invert_normalize_and_create(raw: object, sourceType: "wordpress" | "drupal", contentType?: string)
raw is whatever the source MCP returned. sourceType determines the normalization mapping. contentType optionally overrides the type derived from the source (e.g. map a WP "post" to "articles").
WordPress expects the shape returned by the WP REST API, ideally fetched with ?_embed to include author, featured image, and taxonomy terms.
Drupal expects a JSON:API node resource object (data.type, data.id, data.attributes).
MCP-to-MCP import pattern
When Claude is connected to both a source MCP (e.g. a WordPress or Drupal site's MCP server) and the Invert MCP simultaneously, you can sync specific content items with a natural language instruction:
"Pull the post about Python from myblog and add it to this Invert site."
Claude fetches the post via the source MCP, then passes the raw result to invert_normalize_and_create. Field mapping stays in code rather than in the AI's reasoning chain, so imports are consistent regardless of how the instruction is phrased.
Re-importing the same slug overwrites the existing file in place.
Draft workflow
Drafts let you create and iterate on content before it is publicly visible.
# 1. Create a draft
invert_create({ ..., status: "draft" })
2. Read or iterate — only visible via MCP tools and /preview/ URL
invert_get("posts", "my-slug")
invert_update("posts", "my-slug", { body: "revised content" })
3. Publish when ready
invert_publish("posts", "my-slug")
On the local MCP, drafts live in .drafts/ which is gitignored. They are never committed and never included in a Cloudflare Pages build. npm run dev serves them at /preview/{type}/{slug} for local review.
On the edge MCP, drafts live in a separate KV prefix. No GitHub commit is triggered. The static site rebuild cycle is not involved. When you call invert_publish, the item moves to the live KV namespace and the GitHub commit is queued — the static site updates in ~1-2 minutes.
Write durability (edge)
Edge writes go to Cloudflare KV first — content is readable immediately. An async GitHub API commit then syncs the change back to the git repo, which triggers a GitHub Actions rebuild (~1-2 minutes). If the GitHub token is not configured, content lives only in KV and will be lost on the next full rebuild.