Skip to main content
Glama

Update site plan

update_site_plan

Writes the site plan (.xodia/plan.json) on a branch as an unsaved change. Send one of: plan with the whole plan or only the top-level fields you change (brief, inspiration, brand, audience, goals, motion, sitemap, pages, globals, library; the rest keeps its values; removing or renaming a page updates the nav and footer), pageStatus to set pages to planned, wireframed or built (one { path, status }, or a list), or globalsStatus for the shared nav and footer. Statuses are read from the site, so pageStatus and globalsStatus are optional: Xodia sets them when a request finishes and on sync_site_plan. With plan, always pass the etag from your last get_site_plan (null when there was no plan): if someone changed the plan since, it answers plan_conflict and you read again. pageStatus or globalsStatus alone needs no etag (they set one field on the plan as it is now); an etag you do pass is checked. Refuses a plan that isn't valid with each problem and where it is. Keep the user's brief and inspiration notes as they wrote them. Limits: 40 pages, 4 levels, 20 sections a page, 5 inspiration sites. Every field is optional and defaults to empty. Fields (lists in brackets are the allowed values): brief {name, business, audience, markets[], locales[], look, existingContent, links[] {url, label}}: the user's words, keep them. inspiration[] {url, likes[] (layout, colours, fonts, tone, motion), note, takeaways, reachable (true, false or null)}: the user writes url, likes and note; you write takeaways and reachable. brand {name (from brief.name, never invented), pitch, toneOfVoice, colours[] {name, value (#hex), role (suggested: primary, secondary, accent, background, surface, text, muted, border)}, fonts[] {family, role (heading, body, accent, mono), source, locale?}, logo (a repo path or null), iconSet? (the site's one icon set: lucide, tabler, heroicons, phosphor, iconoir, mingcute, material-symbols, radix, bootstrap; left out: keep the site's own set, else lucide)}. Fonts are one per role and locale: a font with a locale (one of audience.locales) serves that language beside the default one. audience {markets[], personas[] {name, description, needs}, goals[], locales[] (language codes, default first)}. goals {primaryAction {kind (book, buy, sign_up, contact, enquire, call, visit, subscribe, download, other), label, target (a page path or https URL)}, successMeasures[]}. motion {level (none, calm, lively, expressive; default calm), signature (the one memorable moment, in words), principles[] (up to 8 short rules), reducedMotion ("respect", always)}. options? {motionFeel? (soft, crisp, springy, cinematic; default soft), copyMode? (what Build writes: placeholder, draft, keep; default draft; wireframes always use placeholder copy), voice? {use[]?, avoid[]? (up to 20 words or phrases each), readingLevel? (simple, standard, expert; default standard)}, internalLinks? (default true), externalLinks? (none, cite, recommend; default cite), visuals? (what fills image slots: placeholder, svg, mixed; default mixed)}: the owner's build options. Every field may be left out (its default applies); keep the ones the owner set. sitemap[] {path, title, purpose, seo {title, description, keyword? (the main search phrase; leave it out on thank-you and legal pages), noindex? (true keeps the page out of search and the XML sitemap: thank-you and legal pages)}, children[]? (may be left out)}: "/" first. Site size counts content pages only; legal, thank-you and not-found pages come on top. pages {"": {sections[] {id (lowercase-dashes, unique on the page), type (from the section library, by group: opening: hero, page-header, announcement; story: about, story, mission, timeline; offer: features, benefits, services, menu, products, pricing, comparison; proof: testimonials, logos, stats, case-studies, reviews; people: team, careers; media: gallery, video, portfolio-list, portfolio-item; help: faq, process, contact, map, schedule, booking, form; content: blog-list, blog-post, events-list, event-item, content; close: cta, newsletter, footer-cta; any other short name is a custom type with no layouts), variant? (a layout of that type, listed by get_site_plan), motion? (reveal, stagger, parallax, marquee, pinned, counter, hover-lift, image-zoom, video-loop, page-transition, svg-draw, svg-morph, icon-motion, text-reveal, list-motion, beam, tilt-spotlight, scroll-story, ambient, none), visuals? (this section's image slots over options.visuals: placeholder, svg, mixed), list? {collection, filter?, limit?} (the section shows a collection's items), purpose, contentNotes} (each section's id is the data-section id of its element on the site), status (planned, wireframed, built; read from the site, keep what get_site_plan shows), inNav? (a hint used only when the menu is generated or inherited: absent means top-level pages in; legal, thank-you, nested and dynamic pages out; globals.nav.items decides the menu), navLabel? (a short menu label), prompt? (the owner's note for this page, keep it), template? (the page template it started from; template kinds: home, about, services, menu, pricing, contact, booking, events, blog-index, blog-post, portfolio-list, portfolio-item, faq, careers, legal, thank-you), collection? (a collections[] id: this is that collection's item page, path //:slug), localized? {"": {title, seo {title, description, keyword?}}}}}: localized holds other languages' title and SEO; each locale must be in audience.locales. globals {nav {items[] {path, label? (left out: the page's navLabel, else its title), children[]? (page paths, one level; may be left out)}, cta? {label, path or url} (the one menu button), layout (simple, centered, split, minimal), mobile (drawer, full-screen, inline), sticky, announcement? {text, path or url}}, footer {columns[] {title, links[] {label, path or url}}, contact, social, newsletter, legal[] (page paths as strings, such as "/privacy"; request texts show them resolved as {path, label}, but you write paths), layout (columns, simple, big-cta)}}: the shared nav and footer. nav.items is the menu, exactly as written: never "/" (the logo links home) and never the cta's page again as an item. Links point at sitemap pages (not dynamic ones) or https addresses; up to 12 menu items. Removing a page drops its links and renaming one keeps its place. Sending globals makes nav.items the menu as you wrote it; leaving globals out keeps them and lets new top-level pages join. Their status is read from the site (globalsStatus is optional), never set here. library {sections[] {id, name, type, variant?, purpose, contentNotes, motion?, visuals?}, pages[] {id, name, purpose, sections[]}}: sections and pages the owner saved to reuse. Keep it unless asked. collections[]? {id (lowercase-dashes, the content/ folder), name, purpose, starter? (blog, projects, team, products, events, jobs, testimonials, faq), fields[] {id (camelCase), type (CMS field types: plainText, richText, image, multiImage, videoLink, link, email, phone, number, date, dateTime, switch, color, option, file, reference, multiReference), label?, collection? (reference targets)}, urlFolder? (item pages on //:slug)}: CMS collections the site needs (blog, projects, team, menu, events...), up to 20. Each gets a collection page: a sitemap page on //:slug whose pages entry has collection: "". A section that lists items has list {collection, filter? (in words, like "featured, newest first"), limit?}. Sync copies whether a collection exists, its fields and URL folder from the site (onSite marks it); purpose stays yours. Icon sets (brand.iconSet takes a ui set; lucide, tabler, heroicons, phosphor, iconoir, mingcute, material-symbols, radix, bootstrap, line-md, simple-icons in all): get_site_plan lists each with its packages and when to use it.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "projectId": "<projectId>"
    },
    "name": "update_site_plan"
  }
}

Input Schema

NameRequiredDescriptionDefault
etagNoThe etag from your last get_site_plan; null when it said there is no plan file yet. Required with plan. Optional with pageStatus or globalsStatus alone (checked when you give it). A mismatch answers plan_conflict: read again and reapply your change.
planNoThe plan (file version 2), or only the top-level fields you change: brief, inspiration, brand, audience, goals, motion, options, sitemap, pages, globals, library, collections. Fields you leave out keep their current values. version, updatedAt and updatedBy are set for you. Field shapes and allowed values are in the tool description.
branchNoDefault: the branch the workspace has open. Pass the request's branch when a request names one.
projectIdYesProject id from list_projects
pageStatusNoInstead of plan: one page's status { path, status }, or a list of them. Optional: Xodia reads page statuses from the site (sync_site_plan, and after each request), so you don't need to set them.
globalsStatusNoInstead of plan and pageStatus: the shared nav and footer's status, "wireframed" or "built" ("planned" resets it). Optional: Xodia reads it from the site when a globals request finishes.

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare the write/non-destructive profile, and the description goes well beyond them: it discloses conflict behavior (plan_conflict, re-read and reapply), validation refusal with per-problem locations, merge semantics for omitted top-level fields, nav/footer effects of removed or renamed pages, and hard limits (40 pages, 4 levels, 20 sections, 5 inspiration sites). That is exactly the behavioral context an agent needs before calling a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is front-loaded correctly, with modes and etag rules before the field reference, and the size is partly justified by documenting a large free-form payload. However there is genuine duplication: the icon-set list appears twice (inline on brand.iconSet and again in the closing paragraph), and the rule that statuses are read from the site is restated for plan, pageStatus and globalsStatus after being stated up front.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a high-complexity, nested-payload mutation tool with no output schema, the description covers the success path, the conflict path, validation failure, defaults, and the interaction with get_site_plan and sync_site_plan. An agent has enough to construct a correct call without consulting other docs.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but the schema's plan object is an opaque additionalProperties bag, so the field-by-field reference (brief, brand, sitemap, pages, globals, library, collections, allowed enums, defaults) adds meaning nothing else supplies. The etag null/non-null case and its per-mode requirement are also stated more precisely than in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names a specific verb, resource and destination: writes the site plan (.xodia/plan.json) on a branch as an unsaved change. That distinguishes it cleanly from get_site_plan (read, supplies the etag) and sync_site_plan (which also sets statuses), both of which are named in the text.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit mode selection ("Send one of: plan ... pageStatus ... or globalsStatus") and precise etag rules per mode, including that pageStatus/globalsStatus alone needs no etag. What it lacks is a direct statement of the read-then-write workflow versus sync_site_plan as a sibling alternative, though that is strongly implied by "the etag from your last get_site_plan" and the sync references.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources