zah-site-mcp
Allows creating site forms that submit to Airtable as an external form endpoint when ZAH CRM is not enabled.
Allows adding site assets such as images and video from Dropbox shared links via the add_asset URL option.
Allows creating site forms that submit to Formspree as an external form endpoint when ZAH CRM is not enabled.
Allows creating site forms that submit to Google Forms as an external form endpoint when ZAH CRM is not enabled.
Allows adding site assets from Google Drive direct links via the add_asset URL option.
Allows embedding Vimeo videos in site pages.
Allows embedding YouTube videos in site pages.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@zah-site-mcpUpdate the homepage hero heading to 'New Vision Therapy & Wellness' and publish."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ZAH Site MCP
Lets a client's own AI read and build on their ZAH-built website through MCP, and gives Zah Editor a real Publish so edits reach every visitor.
Do not rebuild this per site. Install it and mount it. If the contract below does not fit a site, extend it here so every site gets the change.
The model, in one paragraph
The files Zah shipped are the permanent default. Everything the client's AI
does is an overlay on a Railway volume: new pages, new sections, layout, CSS,
images, video, embeds and forms. reset_page and reset_site put the build
back at any time; every write is versioned and revert restores any version.
Forms go to ZAH CRM by default. The host declares its lead seam
(crm: { leadPath: '/api/lead', enabled }); when Zah has switched the CRM on,
a form with that action sends every submission to the client's CRM as a lead.
When it is not on, the form is kept but inert and the AI is told to ask the
client "would you like this form connected to ZAH CRM?" and send them to Zah.
The client's other option is their own outside service (Formspree, Google
Forms, Airtable) via an https action. This server keeps no form data and
nothing can be built on it through the MCP. Storage stops at a quota.
Related MCP server: Sophia Stack MCP
Install
npm i github:Yawitazah/zah-site-mcpAlpine images need git for that: RUN apk add --no-cache git in the Dockerfile.
Mount (the whole integration)
const path = require('path');
const zahSite = require('zah-site-mcp');
const site = zahSite.mount(app, {
siteId: 'new-vision', // matches SITE_ID / ZAH Gate
name: 'New Vision Therapy & Wellness',
dataDir: process.env.DATA_DIR || '/data', // a Railway volume
token: process.env.SITE_MCP_TOKEN, // the client's AI presents this
adminHash: process.env.EDITOR_ADMIN_HASH, // the SPARE key: sha256(email:password)
accountUrl: process.env.ACCOUNT_URL, // ZAH Account; the client's own login (default zahbrandsolutions.com)
publicUrl: process.env.PUBLIC_URL,
pages: [
{ path: '/', file: path.join(__dirname, 'index.html'), root: 'main' },
],
settings: { // values the client may change
bookingUrl: { label: 'Where "Book" buttons go', kind: 'url', default: process.env.BOOKING_URL },
phone: { label: 'Contact phone', kind: 'phone', default: process.env.CONTACT_PHONE },
},
quotaMb: 250, maxFileMb: 30, // the client's storage
crm: { leadPath: crm.leadPath, enabled: crm.leadsEnabled, notice: '...' }, // the ZAH CRM door for forms; notice = a host rule every form note carries (e.g. no health information)
});
// ...then ZAH Pay (reads site.settings()), express.static and the 404 handler.Rules:
Mount before
express.staticand before any product that readssite.settings(). It serves the listed pages and every client-created page.The first page in
pagesis the template: client-created pages take its head, CSS, header, footer and chrome.rootis the selector Zah Editor edits (usuallymain). The MCP edits the whole body: header, nav, main, footer.Each page needs a
fileon disk. Redeploying new source changes a page that has NOT been restructured; a restructured page keeps its snapshot untilreset_page. Say so in the handoff.
On the page, after zah-editor.js:
<script src="/zah-site/publish.js"></script>The one login (0.5.0)
The client already has a login: the email and password they made at zahbrandsolutions.com/account, the one that opens their billing, their desk and their Dispatch board. It now opens the pencil on their own site too. Zah, 2026-09-10: "If they are paying for it they should have access without having to create multiple logins."
POST /zah-site/login { email, password } takes two kinds of answer:
Their ZAH Account | asked server to server at |
| the spare key. Needs no network, so a bad day at head office never locks a client out of their own page. Zah's way in. |
Either way the answer is { token, who, via } and the token is the site's own
SITE_MCP_TOKEN. Five wrong answers a minute from one address earns a 429;
right answers never count. Set accountLogin: false to switch the account
route off for a site.
The back office, on the client's own domain
A client looks for their things on their site, not on ours. Five short
paths are opened unless a real page already lives there, each noindex,
each landing on the one login:
Path | Goes to |
|
|
|
|
| ZAH Account's signed hand-off to their Dispatch board, no second password |
| the same hand-off to ZAH CRM |
backOffice: false turns them all off; backOffice: { crm: false } drops
one; backOffice: { dispatch: '/board' } moves one. A site that serves its
own page at one of these paths silently keeps it.
What it owns
Thing | Where |
Routes |
|
Pages | the configured ones, plus any path the client created |
Data |
|
Styling | none of its own. Custom CSS the client adds is injected as |
Chrome |
|
Environment
Variable | Required | What |
| for MCP + publish | The site's own token. Generate: |
| yes on Railway | Mount a volume at |
| no | The spare key: |
| no | Where ZAH Account lives. Defaults to |
Without a token: pages serve, everything under /zah-site/* refuses (503),
and the editor falls back to adminHash and localStorage.
Connect a client's AI
claude mcp add --transport http my-site https://YOUR-SITE/zah-site/mcp --header "Authorization: Bearer zs_..."claude.ai custom connectors cannot set headers; use the keyed URL:
https://YOUR-SITE/zah-site/mcp/k/zs_...
The tools
Read | |
| id, pages, settings, usage, and how editing works. Call first. |
| every page, built or client-created |
| header, nav, main, sections, footer, blocks: keys, classes, depth |
| every heading, paragraph, item, link, button, image, video with a key |
| one element; its HTML, or the whole body |
| the site's CSS plus custom CSS, so new markup looks native |
| is ZAH CRM connected, the action and fields to use, what to tell the client if not |
Words, links, images | |
| small edits; child elements (icons) kept |
Structure | |
| a new section, card, image, embed, form |
| rewrite an element, or the whole body |
|
|
Pages, style, files | |
| starts as a copy of |
| SEO for any page: title, description, share image (og:image). |
| the site-wide custom stylesheet |
| images, video, audio, PDF; per-file cap and site quota |
Settings and safety | |
| values the host declared (booking link, phone). Empty resets to default |
| every version kept |
| back to the build; assets and settings survive |
Keys
Every editable element (h1 h2 h3 h4 h5 p li blockquote figcaption a button img video label) and structural block (header nav main section article aside footer figure ul ol table, and div with an id or class) inside
<body> gets a data-zs key. Untouched pages use positional keys (h2:3,
section:1). Once a page has been restructured ("materialised"), keys are
baked into its markup and new nodes get random keys (n:3f9a1c), so later
insertions never shift anything. An element that already carries
data-zs="hero.title" keeps that name.
Order of application
snapshot (the page as the client restructured it) or the file's body
chrome from the file re-attached
keys
small keyed edits
custom CSS
A Zah Editor publish replaces only the editor's root (main) inside that
model, so header and footer edits made by the AI survive an editor save.
What the sanitiser does
Allowed: any HTML, inline styles, <style>, external and inline <script>
(embeds, widgets), <iframe> from https, <form> posting to the ZAH CRM lead
path (when connected) or to an https address that is not this site. Refused:
javascript: URLs, on* handler attributes, srcdoc,
object/embed/applet/base/meta. Any other <form> is kept visible but made
inert with a note in data-zs-inert saying why. Every structural result lists
the forms it contained with a status (zah-crm, zah-crm-off, external,
inert) and a note for the client.
Failure modes already met
"Saved here only" in the editor. No token reached the bridge: the page's admin hash does not match
EDITOR_ADMIN_HASH, orSITE_MCP_TOKENis unset.Edits vanished after a deploy. No volume. Set
DATA_DIRto a mounted path.Zah's source change is not showing on a page. That page is materialised; the client restructured it.
reset_pageshows the new build (their changes go to history and can be reverted, or redone).claude.ai cannot connect. It cannot send headers; use
/mcp/k/<token>.Video upload fails. Base64 through MCP is limited to a few MB; give
add_asseta URL instead (Google Drive direct link, Dropbox?dl=1), or embed from YouTube/Vimeo.
Test
npm testFifty checks: keys, keyed edits, structure, pages, CSS, assets and quota, forms policy, settings, editor snapshot, chrome survival, history, resets, auth.
The house credit
Every page a site serves carries a link to zahbrandsolutions.com. Mark the
one in the footer data-zs-credit and it is chrome: not keyed, not editable
by the AI or the editor. If a page ends up with no link to the house at all
(a client page without a footer, an edit that removed it), render() adds a
small one before </body>. mount({ credit: false }) is the only switch,
and it is Zah's.
Publishing and upgrades (0.6.0)
Use root: 'body' in both the mount and ZAH_EDITOR_CFG to edit the
header, navigation, main content, footer and mobile contact bar. Zah Editor
1.2.0 excludes its own toolbar/scripts from edits and undo snapshots.
Normal text, link and style saves are keyed patches. They no longer freeze
the entire page, so a later source build can add features without discarding
client text. Keep explicit data-zs keys in source stable. A source edit
must not rename a client's edited key or replace it with positional numbering.
Structural edits still create snapshots; never reset a client snapshot just
to make a new source build visible. Export it, compare, and migrate deliberately.
Saves carry the rendered version and source hash. A stale tab receives 409 and keeps its unsaved work visible. Save only reports Published after the server confirms. Reset waits for confirmation and successful completion; cancelling it sends no request. Server-managed pages do not load an old localStorage draft over published content.
Optional settings bindings on page elements:
<a data-zs="contact.phone" data-zs-setting="phone"
data-zs-setting-text data-zs-setting-href="tel"
href="tel:+15551234567">555.123.4567</a>data-zs-setting-text binds the visible text; data-zs-setting-href supports
tel, sms, and mailto. data-zs-setting-prefix="call " preserves a label
prefix. Saved setting values update every binding. An editor patch to a bound
value also updates that setting. With no saved setting, source/overlay text
remains authoritative.
Authenticated maintenance endpoints:
GET /zah-site/exportexports current content/settings;?version=Nexports a history version. Treat exports as private client data.POST /zah-site/rebaseatomically replaces one old snapshot with reviewed keyed edits on the current source. Requirespage,baseVersion, SHA-256sourceHash,edits(keyed patch object), andconfirm: "replace-snapshot-with-reviewed-edits". The complete previous state remains in history; concurrent changes refuse with 409. Export and compare first. This is a maintenance operation, not a routine deploy step.
MCP is standard Streamable HTTP. Any compatible AI client can use the endpoint with a Bearer token; a keyed URL supports clients without custom headers. Keep these URLs/tokens out of shared reports and source control.
Hosts with scroll animations can declare ZAH_EDITOR_CFG.transientClasses
and transientStyles arrays (for example in, stuck, and --sd). These
are stripped from the comparison baseline and published markup, so scrolling
does not turn a text edit into a structural snapshot.
This server cannot be deployed
Maintenance
Related MCP Connectors
Create, edit, preview, publish, and manage web pages from MCP-capable AI clients.
Build, edit and run real hosted websites from your AI - content, SEO, menus, store, rollback.
Publish a site your AI built to paid hosting, then read it back, change a page or restore a version.
Create and manage Lindo AI websites, pages, blogs, clients, and credits from any MCP client.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceMCP server for AI-assisted WordPress editing across 12 page builders. 172 tools for content management, page builder editing, WooCommerce, SEO analysis, accessibility scanning, and site intelligence. Edits native builder formats (Elementor, Bricks, Divi, Gutenberg, Beaver Builder, and 7 more) with duplicate-before-edit safety, optimistic locking, and surgical element-level operations9MIT
- FlicenseNot gradedqualityCmaintenanceEnables external AI agents to edit and manage a self-hosted website via a token-gated MCP server, including page content, data, media, and sandboxed functions with validation and rollback.3-
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to interact with WordPress sites over MCP, providing read-only tools for AEO/GEO readiness, AI visibility, traffic, request logs, bot identification, page checks, and schema/markdown previews, plus opt-in write tools to draft, edit, and publish posts with permission checks and auditing.2GPL 2.0
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to draft, review, preview, and publish content across multiple websites through typed MCP tools. Ensures safe, policy-gated, and auditable publication without requiring a conventional CMS admin interface.Apache 2.0