The standard MCP workflow
What this is
The standard workflow for managing a ComStack site over MCP, written as the first-run loop — the path a fresh chat follows with zero prior context. Given only a page URL, an assistant should resolve the project and page, open the right editor, draft a change, get the user’s approval, and publish — without getting lost when the document id changes underneath it.
This page is the human-readable companion to two machine resources every client already loads: the server instructions (sent once per session) and the get-guide tool (full schema + Lighthouse rules). The canonical machine-readable agent guide is also the transformento://guide resource.
How it works
The loop has seven moves:
- Project. Call
get-project-statefirst. It returnsproject_name(echo it back on destructive tools),site_url,available_doc_templates, the live and draft pages, a compactnavtree, and aworkflow_hintthat restates this loop inline. - Page. Address a page by its slug + language — the durable handle.
get-page-contentandupdate-pageaccept either the Firestore doc id (path) or a(slug, language)pair;languagedefaults to the project’s default locale. A reference that matches nothing returns a cleanPage not found, never a raw database error. - Editor. Each page carries a
page_type. Aknowledgepage is markdown — edit it withcreate-page/update-page. Acustompage is vibe-codedbody_source— edit it withupload-custom-page. Using the wrong tool is refused; see Page types. - Draft. Editing a live page never changes it in place — it writes a draft and leaves the live URL up. Repeated edits to the same page collapse into one draft, and a no-op edit creates no draft at all.
- Approval.
publishis a dry run: it returns a diff manifest and a single-useconfirmation_token(5-minute TTL) and changes nothing. Show the manifest to the user and get explicit approval — every time. - Publish.
publish-confirmconsumes the token, re-checks that nothing drifted since the dry run, and starts the deploy asynchronously, returning{ publish_id, status: "running" }. - Verify. Poll
publish-statusuntilsucceeded. Itspublished_urlscarry the resulting live path — the new doc id — so you read the post-publish identity back instead of guessing it.
When to use it
- A fresh MCP session where the only input is a page URL or a vague request
- Onboarding an assistant or a teammate to the publish flow end to end
- Debugging “which step am I on?” when a tool response is unexpected
Tools in the loop
| Step | Tool | Notes |
|---|---|---|
| Project snapshot | get-project-state | First call. Carries workflow_hint, nav, and page_type on every entry. |
| Read a page | get-page-content | By path or (slug, language). Returns page_type + the resolved path. |
| Edit knowledge | create-page / update-page | Markdown. Editing live creates a draft. |
| Edit custom | upload-custom-page | Opaque body_source + translation markers. |
| Drop a draft | discard-draft | Removes a pending draft; refuses live, hidden, or archived paths. |
| Dry run | publish | Manifest + confirmation_token. No writes. |
| Deploy | publish-confirm | Async. Returns publish_id. |
| Poll | publish-status | succeeded → published_urls. |
Example
“Change the headline on https://example.com/pricing/ to ‘Simple, honest pricing’.”
get-project-state(project_id) -> project_name, site_url, the pricing page (slug 'pricing', page_type 'knowledge')
update-page(project_id, slug: 'pricing', language: 'en', content: '## Simple, honest pricing ...') -> { action: 'draft_created_from_live', draft_id, draft_url }
(present the draft_url and the change, get approval, then publish:)publish(project_id, project_name) -> manifest { drafts: [ { slug: 'pricing', change_type: 'replace', will_publish_to } ] }, confirmation_token (single-use, 5-min TTL)
publish-confirm(project_id, confirmation_token) -> { publish_id, status: 'running' }
publish-status(project_id, publish_id) -> { status: 'succeeded', published_urls: [ { slug: 'pricing', url } ] }Common errors
- Editing with the wrong tool. A markdown edit on a
custompage (orupload-custom-pageon a knowledge page) is refused with a redirect — checkpage_typefirst. See Page types. - Caching a doc id. The
pathrotates across an edit-publish cycle; address pages by(slug, language)and read the newpathback frompublish-status. See Publishing. - Publishing without approval.
publishonly returns a token; nothing deploys untilpublish-confirm. Always show the manifest first. - Expired token. The
confirmation_tokenlasts 5 minutes and is single-use — callpublishagain for a fresh one.
Full recovery steps for every structured error are in the error reference.
Related
- Set up the server — connect a client and try the first prompts
- Page types — knowledge vs custom, and the editor each uses
- Custom pages — author vibe-coded pages end to end
- Publishing — draft lifecycle, doc-id rotation, the dry-run to confirm flow
- Errors — every structured error and how to recover
- Tool catalogue and Workflow examples