Connect Claude Code to Your Headless CMS over MCP
Run a Next.js example app, connect Claude Code to the same cmssy workspace over MCP, and watch content changes land on your own site.
What We're Building
By the end of this guide, Claude Code will create a page, fill it with content, publish it and edit it again, and a Next.js app running on your machine will render each change. Nobody opens an admin panel.
Three pieces are involved, and it helps to keep them apart:
- Your Next.js app renders the site. Here it is
simple-blogfrom the cmssy examples repository. cmssy never renders or hosts it. - Your cmssy workspace stores the content and serves it to the app over the delivery API.
@cmssy/mcp-serverconnects Claude Code to that workspace. Claude reads and writes content through it. It does not touch your code: the server writes no files and keeps no local state.
Prerequisites
- Node 22.13 or newer, and pnpm (npm works too)
- Claude Code installed
- A cmssy workspace. The free plan is enough for this guide.
Step 1: Run the Example App
Clone the examples repository and start simple-blog:
git clone https://github.com/cmssy-io/examples
cd examples/simple-blog
pnpm install
cp .env.example .env.local
pnpm devOpen http://localhost:3000. The root redirects to /blog, which renders content from a public demo workspace. You don't need an account for this step. Next, point the app at your own workspace.
Step 2: Create an API Token
In the cmssy dashboard, open API Tokens and click Create Token. Give it a name and, under Workspace, pick the workspace this project uses. A token scoped to one workspace can open that workspace only, which is all an agent in one repository needs.
Copy the cs_... value as soon as it appears. It is shown once.
A token has no permissions of its own. It acts as you, with the role you hold in that workspace, so an editor's token can edit and a read-only member's token can only read.
Step 3: Link the App to Your Workspace
npx @cmssy/cli link --token cs_your_tokenlink fetches your organization slug, workspace slug and draft secret, and writes them to .env.local. If the workspace has no block manifest yet, it also pushes the app's one. That manifest is the list of block types the app can render, here prose and blog-index. Restart pnpm dev afterwards.
The manifest is the reason Claude won't put a block on a page that your site can't render: the server refuses block types the manifest doesn't list.
Step 4: Find Your Workspace ID
The MCP server takes a workspace ID next to the token. Open your workspace's Settings. The ID is under Workspace Information.
Step 5: Register the MCP Server
Add .mcp.json to the project root. It contains no secrets, so you can commit it:
{
"mcpServers": {
"cmssy": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cmssy/mcp-server@latest"]
}
}
}Keep the token in your shell instead. The server reads CMSSY_API_TOKEN and CMSSY_WORKSPACE_ID from its environment, and Claude Code starts it with yours:
export CMSSY_API_TOKEN=cs_your_token
export CMSSY_WORKSPACE_ID=your_workspace_id
claudeThe first time you start claude in the project, Claude Code asks whether to trust the MCP server from .mcp.json. Approve it, then run /mcp. You should see cmssy connected.
The server also accepts --token and --workspace-id flags. Use them for a quick local test, but keep the token out of committed files.
Step 6: Ask What the Site Can Render
Start with something read-only:
Which block types can my site render, and which pages exist?Claude calls list_block_types and list_pages. The block types it lists are the ones in your app's manifest, with their fields, so Claude knows that prose has a rich-text body before it writes anything.
Step 7: Build and Publish a Page
Create a /blog page with a blog index that lists its child pages.
Under it, add a post called "Hello from Claude Code" with two short
paragraphs in a prose block. Fill every language the workspace has
enabled, then publish both pages.Claude works through get_site_config (to see your languages), create_page, add_block_to_page and publish_page. Claude Code asks before it runs each tool you haven't allowed yet, so you see every write before it happens.
Refresh http://localhost:3000/blog. The index lists the new post, and the post renders through the app's own prose component.
Step 8: Edit, Then Publish Again
Rewrite the first paragraph of "Hello from Claude Code" to mention
MCP, and change its SEO title. Then publish it.Edits land in the page draft first. Your site serves only the published version, so nothing changes in the app until publish_page runs. After that, refresh and the new text is there: in development, Next renders each request fresh.
For one paragraph in a long article, Claude can use patch_block_content, which sends only the changed part instead of the whole HTML.
Decide What Claude May Do Unasked
Claude Code asks before every MCP call you haven't allowed. Reads are safe to allow. Leave writes, and especially publish_page and delete_page, on the prompt. In .claude/settings.json:
{
"permissions": {
"allow": [
"mcp__cmssy__list_block_types",
"mcp__cmssy__list_pages",
"mcp__cmssy__get_page",
"mcp__cmssy__get_site_config"
]
}
}The server labels its tools for the client: read tools as read-only, and the ones that delete, publish or record a refund as destructive.
Going to Production
In production, simple-blog caches each page for up to an hour (revalidate = 3600), so a publish would show up late. Turn on immediate updates with a revalidate route and a webhook:
import { createCmssyRevalidateRoute } from "@cmssy/next/server";
export const POST = createCmssyRevalidateRoute({
secret: process.env.CMSSY_WEBHOOK_SECRET,
});Put that in app/api/revalidate/route.ts, then subscribe a webhook to content.changed pointing at it. Claude can create the webhook with create_webhook. The signing secret comes back once, so put it straight into CMSSY_WEBHOOK_SECRET on your host. The route verifies the signature and drops the cmssy cache tags.
No Token? Sign In Instead
Claude Code can also use cmssy's hosted MCP server, which signs you in through the browser instead of using a token:
claude mcp add --transport http cmssy https://api.cmssy.io/mcpRun /mcp and authenticate. You choose which workspaces Claude may use and whether it gets read only or read and write. The tools are the same, but each one takes the workspace as org/workspace, so one connection can serve several workspaces. Use the token setup for CI or anything without a browser.
Troubleshooting
- Every call returns "Not authorized": the token is revoked or expired, or it can't open the workspace in
CMSSY_WORKSPACE_ID. The server still starts with a bad token, so/mcpshowing "connected" doesn't prove the token is valid. - The server doesn't appear: the variables weren't exported in the shell that started
claude. Without a token the server exits at startup. - A tool you expect is missing: an old build may be cached by
npx. Comparenpx -y @cmssy/mcp-server --versionwithnpm view @cmssy/mcp-server version. - A block is refused: its type isn't in the workspace manifest. Ask Claude to run
list_block_typesand use one that is.
The full reference, including every tool and its required permission, is in the MCP server docs. Tokens are covered in API tokens.