Tools
MCP
Build and publish your nertia sites from Claude, on the web or on the desktop.
Remote MCP
Paste into Claude → Customize → Connectors
nertia is a connector for Claude. Add it once and Claude can read your sites, edit copy and sections, set styles, manage pages and SEO, and publish. Claude on the web and Claude Desktop share the same tools. Short link: ner.sh/mcp.
The quickest route is the Remote MCP box at the top of this page. Copy the URL and follow the Claude on the web steps below.
Endpoints
Claude on the web, and any client that speaks Streamable HTTP, uses the connector URL:
https://ner.sh/api/mcp/streamable
Claude Desktop and scripts use the JSON-RPC endpoint, with a bearer token:
POST https://ner.sh/api/mcp
Authorization: Bearer <firebase-id-token | cli-session-token>
Signing in from a CLI or from Claude issues a signed CLI session token that lasts about 30 days. It comes from the device-code login at /cli/auth. For local development, dev-bypass works as the token when DEV_BYPASS_AUTH=true.
Claude on the web
- In Claude, open Customize, then Connectors, then Add custom connector.
- Name it
nertia, or anything you like. - Paste the URL:
https://ner.sh/api/mcp/streamable - Leave OAuth Client ID and Secret blank. nertia registers the client and signs you in itself.
- Press Add, then Connect when Claude asks, and sign in on www.nertia.ai. Use Google if that is how your nertia account signs in.
- Claude shows Connected. Switch the connector on and the tools appear.
nertia's authorization server lives on ner.sh (/.well-known/oauth-authorization-server, /oauth/mcp/authorize, /api/oauth/mcp/token, /api/oauth/mcp/register). The access token is the same CLI session token the desktop uses.
If your Claude plan shows Request headers, you can skip OAuth and paste Authorization: Bearer <cli-token> from npx @nersh/mcp login instead.
How the transport works: Streamable HTTP answers with JSON and keeps a durable mcp-session-id, stored in the database. A standalone GET or SSE stream is not offered and answers 405. That keeps Claude from opening an empty stream that closes at once, which it reports as "connection stopped working".
To try connectors against a local build, tunnel localhost:3000 to a public address, or use production ner.sh after a deploy. Bearer dev-bypass still works for direct HTTP tests when DEV_BYPASS_AUTH=true.
The tools
Each tool has a CLI equivalent where one exists.
| MCP tool | What it does |
|---|---|
add_canvas_section | add a section (CLI add-section) |
remove_canvas_section | remove a section (CLI remove-section) |
reorder_canvas_sections | set the section order (CLI reorder-sections) |
switch_site_skin | change the skin (CLI set-skin) |
set_section_layout | change a section's layout (CLI set-layout) |
validate_library_config | validate a design system library |
get_site | the full site JSON |
get_site_schema | the pages, sections and copy slots, so Claude knows what it can edit |
list_site_media | the site's uploaded images, newest first (limit, default 20, max 50), each with url, name, contentType and inUse, plus the copy keys in usedBy that point at it. Write a url into an image slot with update_copy |
update_copy | patch copy values by instanceId.slotKey |
update_seo | the site's SEO, or one page's with page (id or path): title, description, canonicalUrl, ogImageUrl, robotsIndex |
update_social | add the site's social profiles: profiles of { network, url }, where network is one of twitch, youtube, soundcloud, spotify, bandcamp, facebook, instagram, tiktok, twitter (X), linkedin, github, threads, bluesky, substack, mastodon, and url is the https profile URL. A listed network takes the new URL and nothing is removed. The footer's social links refill from them as icons |
add_page | add a page. A single-page site becomes multi-page on its own. parentId nests it under a top-level page in the nav |
update_pages | replace the whole pages[] array, including each page's parentId and nav |
remove_page | remove a page by id |
duplicate_page | copy a page by id: a new id, a -copy path, fresh section ids, the words carried over |
upsert_pricing_row | update one tier on a pricing section |
import_site_from_url | soft-deprecated; prefer create_site |
create_site | create a site from a brief: a Foundation skin plus a page pattern, expanded into sections and copy |
delete_site | permanently delete a site you own |
publish_site | publish to staging or production |
publish_from_github | code sites: start the site's own GitHub Action, which uploads a build to the preview |
rollback_release | put an earlier release live (get_site lists them, newest first) |
take_offline | show coming soon on every address that serves the live release |
put_back_online | reverse take_offline |
Images. An image slot (logoImage, a hero's or a block-media block's image) takes an https URL, a data:image URI, or a site path to an image file. Anything else is refused. Pick the URL from list_site_media, never a made-up path. The editor chat shares these tools: an image attached in the chat uploads to the site's media first, and the agent gets its URL with the message.
Multi-page sites. add_canvas_section, remove_canvas_section, reorder_canvas_sections and set_section_layout work on the home page, the pages[] entry with the empty slug. They keep pages[] and composition.sections in step. Use add_page and update_pages for the other pages.
Page nav. Every header's links come from the pages, in the order of the Pages tab, unless that header's links are customised. A page's parentId names a top-level page other than home and shows the page in a dropdown under it. Nesting is one level deep: home never nests, a page with children takes no parent, and nesting never changes the path. nav: "hide" keeps a live page out of the nav. nav: "pin" keeps a child linked at the top level when its parent is left out. update_pages replaces the whole array, so send each page's parentId and nav back as get_site returned them, or the tree flattens. A write that breaks a nesting rule is refused and changes nothing.
Node styles. set_node_style sets real CSS on one node (a band, a container or a block) at a breakpoint: desktop (the default), tablet (below 64rem) or phone (below 48rem). Tablet and phone inherit desktop unless they set a value. Keys are CSS longhands (width, min-height, padding-top, background-color, box-shadow, font-size, position, top, and the rest of the list in tools/list), plus the shorthands padding, margin, border, border-width, border-style, border-color and border-radius. A value is a preset id that follows the design system (sm, md, lg, xl for spacing, md for the site radius, lg for the large shadow, full, screen) or a custom value the property accepts (240px, 16 / 9). unset clears a property at that breakpoint. "Make the hero taller on phone" looks like this:
{ "slug": "typeshit", "instanceId": "hero-band-x1", "breakpoint": "phone", "set": { "min-height": "80vh" } }
An unknown key or a refused value answers invalid_args, naming each key, with accepts listing what every key takes. The old --section-bg style tokens are still accepted at desktop and land in the node's styles like any other key. A token with a CSS equivalent on that node becomes the property, so --section-bg on a band is its background-color. The tokens that components read (--section-divider, the gradient stops, the fonts) stay custom properties.
Page SEO. update_seo with page writes that page's seo. A page field left unset inherits the site's value. Send "" to clear a text field or URL, and robotsIndex: null to clear the flag. robotsIndex: false hides the page from search engines. A page with no ogImageUrl shares the site's image, or a generated card with the page title when the site has none. Page settings go live on the next publish, like every other edit.
The routes: src/app/api/mcp/route.ts (desktop), src/app/api/mcp/streamable/route.ts (connectors), and the site tools in src/app/api/mcp/siteTools.ts.
Code sites
A code site is built from your own GitHub repository. Your workflow builds a static export and uploads it to the site's preview. Setup, the workflow file and the limits are in Code sites. The connector never edits code. Edit the repository with GitHub's own tools, the GitHub connector or Claude Code. On a code site:
| Tool | What it does |
|---|---|
get_site | { v: 1, kind: 'code', source: { repo, defaultRef }, live, releases }, releases newest first (the kept 20) |
get_site_schema | the schema of that object |
publish_from_github { slug, ref? } | starts the site's workflow at ref (default: the default branch) through the Nertia GitHub app, or the dispatch token stored on Settings, and answers with the run address. Needs publish |
publish_site staging | the same as publish_from_github at the default branch |
publish_site production | puts the preview live. It never starts a build, so what goes live is what you checked at the preview address. Needs promote |
rollback_release { slug, releaseId } | puts an earlier release live. Needs promote |
take_offline, put_back_online | coming soon on every live address, and back. Need promote |
every document tool (update_copy, add_canvas_section, set_node_style and the rest) | refuses: "{slug} is built from {repo}. Edit the code there; publish_from_github builds it." |
rollback_release, take_offline and put_back_online answer for a document site too. publish_from_github on a document site refuses, because it is not a code site.
Claude Desktop
Install with npx. There is no repository path to set and no token to paste after the first login:
{
"mcpServers": {
"nertia": {
"command": "npx",
"args": ["-y", "@nersh/mcp"]
}
}
}
The first run opens https://ner.sh/cli/auth?code=…. Sign in, and the token is saved to ~/.config/nersh/credentials.json. To sign in again later, run npx @nersh/mcp login.
The package lives in packages/mcp and publishes as @nersh/mcp.
Local development
{
"mcpServers": {
"nertia": {
"command": "npx",
"args": ["-y", "@nersh/mcp"],
"env": {
"NERTIA_MCP_URL": "http://localhost:3000/api/mcp",
"NERTIA_MCP_TOKEN": "dev-bypass"
}
}
}
}
Start the emulator and the dev server with npm run dev:setup. NERTIA_MCP_TOKEN=dev-bypass skips the saved credentials.
When something is off
| Symptom | Fix |
|---|---|
| Login loop | Run npx @nersh/mcp login and finish the sign-in in the browser |
| Invalid token | Delete ~/.config/nersh/credentials.json and log in again |
| 401 locally | Check DEV_BYPASS_AUTH=true and the emulator env on the dev server |
The old stdio proxy at scripts/nertia-mcp-stdio.mjs is retired. Use @nersh/mcp.
Importing a site from a URL
./scripts/import-client-url.sh vantagecompute https://www.vantagecompute.ai
How a request travels
Claude Desktop talks to @nersh/mcp over stdio, which posts to /api/mcp. Claude on the web talks to /api/mcp/streamable through its connector. Both land in the same place: the token is checked, the shared tool handler runs, and the change is written to the site, which updates the hosted pages.