Tools
MCP
MCP tools for Claude Desktop and Claude online Connectors.
Remote MCP
Paste into Claude → Customize → Connectors
Same tool engine for Desktop (JSON-RPC) and Claude online (Streamable HTTP). Use the Remote MCP box above for Claude Connectors. Short link: ner.sh/mcp.
Endpoints
Desktop / CLI proxy (JSON-RPC 2.0) — POST to:
https://ner.sh/api/mcp
Auth header:
Authorization: Bearer <firebase-id-token | cli-session-token>
Claude online Connectors (Streamable HTTP) — connector URL:
https://ner.sh/api/mcp/streamable
Local dev: use dev-bypass as token when DEV_BYPASS_AUTH=true.
CLI / Claude sessions use a signed CLI session token (~30 days) issued after device-code login at /cli/auth.
Tools ↔ CLI
| MCP tool | CLI equivalent |
|---|---|
add_canvas_section | add-section |
remove_canvas_section | remove-section |
reorder_canvas_sections | reorder-sections |
switch_site_skin | set-skin |
set_section_layout | set-layout |
validate_library_config | design system library validation |
get_site | full site JSON |
get_site_schema | pages, sections, copy slots (discoverability) |
list_site_media | read-only: the site's uploaded images, newest first (limit, default 20, max 50), each with url, name, contentType, and inUse plus the copy keys (usedBy) that point at it. Write a url into an image slot with update_copy |
update_copy | patch instanceId.slotKey copy values |
update_seo | site 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 }, network one of twitch, youtube, soundcloud, spotify, bandcamp, facebook, instagram, tiktok, twitter (X), linkedin, github, threads, bluesky, substack, mastodon, url the https profile URL on that network. A listed network takes the new URL; nothing is removed. The footer's social link group refills from them as network icons |
add_page | add a page (auto-migrates single-page sites); optional parentId nests it under a top-level page in the nav |
update_pages | replace full pages[] array, including each page's parentId and nav |
remove_page | remove page by id |
duplicate_page | copy a page by id: new id, -copy path, fresh section ids, words carried over |
upsert_pricing_row | update one pricing 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 an owned composition site |
publish_site | publish to staging or production |
publish_from_github | Vantage: dispatch GitHub Action → build + commit export to vantagecompute.nertia.ai (not S3) |
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 target the home page (pages[] entry with empty slug). They keep pages[] and composition.sections in sync. Use add_page / update_pages for other pages.
Page nav: every header's links derive from the pages, in the Pages tab's order, 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 (home never nests, and a page with children takes no parent) and 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, container or 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":
{ "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 (--section-bg on a band is its background-color), and the tokens 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.
Route implementation: src/app/api/mcp/route.ts (Desktop) · src/app/api/mcp/streamable/route.ts (Connectors) · site tools: src/app/api/mcp/siteTools.ts.
Claude online (Customize → Connectors)
Use remote MCP from claude.ai without the Desktop stdio package:
- In Claude: Customize → Connectors → Add custom connector.
- Name:
nertia(anything). - URL:
https://ner.sh/api/mcp/streamable - Leave OAuth Client ID / Secret blank (Nertia supports dynamic registration + sign-in).
- Click Add, then Connect when prompted — sign in on www.nertia.ai (Google preferred if that’s how your nertia account works).
- You should see Connected to Claude, then return to Claude and enable the connector →
tools/list.
Nertia’s authorization server lives on ner.sh (/.well-known/oauth-authorization-server, /oauth/mcp/authorize, /api/oauth/mcp/token, /api/oauth/mcp/register). Access tokens are the same CLI session tokens Desktop uses.
Optional (beta): if your Claude plan shows Request headers, you can still paste Authorization: Bearer <cli-token> from npx @nersh/mcp login instead of OAuth.
Transport notes: Streamable HTTP uses JSON responses + durable mcp-session-id (stored in RTDB). Standalone GET/SSE is not offered (405) — that avoids Claude seeing an immediately-closed empty stream (“connection stopped working”).
Local Connectors dogfood: tunnel localhost:3000 publicly, or use production ner.sh after deploy. Dev bypass (Bearer dev-bypass) still works for direct HTTP tests when DEV_BYPASS_AUTH=true.
publish_from_github (Vantage)
Dispatches Publish Vantage from GitHub on ps2pdx/nertia (ubuntu runner builds James’s Next.js repo, syncs out/ to public/clients/vantagecompute, commits to main). Live at vantagecompute.nertia.ai. Target is nersh, not James’s S3/CloudFront on www.vantagecompute.ai.
- Local sync (dev):
./scripts/sync-vantage-next-export.sh [path/to/out] - MCP (prod):
publish_from_github{ repo?, ref?, slug? }— admin orvantagecomputemanager. Returns a GitHub Actions run URL in ~2s; build + commit ~2–3 min; Vercel deploy ~2 min after that. Allowlisted repos:ps2pdx/vantagecompute-website,jamesbeedy/vantagecompute-website. - Secret:
NERTIA_GITHUB_PATon Vercel withactions:write+contents:writeonps2pdx/nertiaand read on the source repo.
Why not inline build on MCP? Claude Connectors times out before npm ci + npm run build finish on Vercel serverless (~3+ min). Dispatch returns immediately with a run link.
Webflow ZIP unpack (scripts/unpack-webflow-export.sh) is legacy staging only.
Claude Desktop (@nersh/mcp)
Install with npx — no repo path, no manual Firebase token after first login:
{
"mcpServers": {
"nertia": {
"command": "npx",
"args": ["-y", "@nersh/mcp"]
}
}
}
First run: opens https://ner.sh/cli/auth?code=… → sign in → token saved to ~/.config/nersh/credentials.json.
Re-login: npx @nersh/mcp login
Package: packages/mcp · publish name @nersh/mcp.
Local dev
{
"mcpServers": {
"nertia": {
"command": "npx",
"args": ["-y", "@nersh/mcp"],
"env": {
"NERTIA_MCP_URL": "http://localhost:3000/api/mcp",
"NERTIA_MCP_TOKEN": "dev-bypass"
}
}
}
}
Start emulator + dev server (npm run dev:setup). NERTIA_MCP_TOKEN=dev-bypass skips saved credentials.
Troubleshooting
| Symptom | Fix |
|---|---|
| Login loop | Run npx @nersh/mcp login and finish browser sign-in |
| Invalid token | Delete ~/.config/nersh/credentials.json, log in again |
| Local 401 | Confirm DEV_BYPASS_AUTH=true and emulator env on dev server |
Legacy stdio proxy
scripts/nertia-mcp-stdio.mjs is deprecated — use @nersh/mcp instead.
Blob import bootstrap
./scripts/import-client-url.sh vantagecompute https://www.vantagecompute.ai
Request flow
Claude Desktop → @nersh/mcp (stdio) → POST /api/mcp
Claude online → Customize → Connectors → GET/POST /api/mcp/streamable
│
▼
verifyToken → handleMcpJsonRpc (shared tools)
│
▼
Firebase RTDB → hosted site updates
Quick reference
Desktop: POST https://ner.sh/api/mcp
Connectors: https://ner.sh/api/mcp/streamable
Auth: Bearer <firebase-id-token | cli-session-token>
Local: Bearer dev-bypass (DEV_BYPASS_AUTH=true)
Login: npx @nersh/mcp login → ~/.config/nersh/credentials.json
Tools: add/remove/reorder sections, switch skin, set layout, get site/schema, list site media, update copy/seo, pages, pricing, import, publish, publish_from_github (Vantage)