# MCP and Agility Gotchas to Know Before You Build

> Source: https://agilitycms.com/docs/developers/vibe-coding-gotchas

Most of the time lost in our proof-of-concept builds (see [Vibe Coding with Agility](/docs/developers/vibe-coding-with-agility)) wasn't spent writing code. It went on behavior that fails silently: a call reports success, and the site doesn't show what you expect. These are the gotchas that cost the most time, with the symptom, the reason and what to do.

Copy the ones that apply into the "MCP gotchas" list in your `AGENTS.md` (see [Writing an AGENTS.md for Agility Projects](/docs/developers/agents-md-for-agility#carry-an-mcp-gotchas-list-forward)), so your AI coding tool reads them before it starts.

Where this article says **we have seen**, the behavior comes from our builds and isn't described in the product documentation. Treat it as something to check for, not as documented behavior.

## Quick reference

| Gotcha | Rule for AGENTS.md |
| --- | --- |
| A past pull date hides an item | Save with null release and pull dates unless you mean to schedule. |
| Saving never publishes | Nothing is live until it's published. Check against the published key. |
| Publishing a page doesn't publish its components | Publish the component content too, then check the live page. |
| Updates replace the whole item | Fetch, merge, then save, with every field. |
| Component reordering doesn't take effect | Set the order with two page saves, then read the page back. |
| Reference names are normalized | Read the name back from the save result and use that. |
| Numbers can arrive as strings | Convert field values explicitly in the data layer. |
| One preview domain per instance | Plan multi-site preview routing up front. |

## A past pull date silently hides items

**Symptom.** An item saves without error and shows in the Agility app, but never appears in the published API or on the site.

**Why.** A pull date takes an item down from the site once it passes, and a release date keeps an item off the site until it passes (see [Schedule Content Changes](/docs/editors/schedule-content-changes)). An item saved with a pull date that's already in the past is taken down as soon as it would go live. This happens easily when an AI tool fills in date properties itself, or copies them from an older item. The MCP server's `save_content_items` also interprets release and pull dates in Eastern time, so a date that looks a few hours in the future can already be past.

**What to do.** Tell the AI tool to send `null` for release and pull dates on every save unless you're deliberately scheduling content. When an item is missing from the live site, check its release and pull dates before anything else.

## Saving never publishes

**Symptom.** The AI tool reports that content was created, and you can see it in preview and in development, but it's missing on the deployed site.

**Why.** `save_content_items` always saves to the instance's default workflow state, usually Staging. Any `state` you pass on save is ignored. Saving a change to a published item creates a new Staging version and leaves the live version as it was. In development, the Agility Next.js Starter always fetches with the preview API key, so staged content renders locally and hides the problem.

**What to do.** Publish deliberately, with `publish_content` and `publish_page`, at the publishing level your team has chosen (see [Governing AI Access to Agility CMS](/docs/owners-admins/governing-ai-access)). Before a demo or a launch, check the site against the published fetch key in a fresh incognito window, not just in development.

## Publishing a page doesn't publish its components

**Symptom.** You publish a page and it appears on the site, but some of its components are missing, empty or out of date.

**Why.** A page and the content items its components use are separate things to publish. Publishing a page and the content it depends on in one step is a distinct "cascade" operation (the .NET Management SDK exposes it as `PublishPageCascadeAsync`), not what a plain page publish does.

**What to do.** After publishing a page, publish the content items its components use. Then load the live page and confirm each component shows the latest content. The publish tools accept up to 50 IDs per call; we have seen large publish batches time out, and batches of 16 or fewer were reliable.

## Updates replace the whole item

**Symptom.** You ask the AI tool to change one field, and other fields on the item come back empty.

**Why.** An update through `save_content_items` replaces the item. Any field you leave out is wiped. Models behave the same way: when you save a content or component model, undefined field properties are treated as empty, and saving a page model with a zone left out removes that zone (and the components placed in it).

**What to do.** Always fetch, merge, then save: `get_content_item` for the current fields and `versionID`, change only what you mean to change, and send every field back. Do the same for models with `get_content_model_details` or `get_component_model_details`. Read the item back after saving to confirm nothing was lost.

## Component reordering through the MCP server doesn't take effect

**Symptom.** `reorder_page_modules` returns success and the page's version number goes up, but the components are still in the old order.

**Why.** `reorder_page_modules` currently reports success without changing the order. `save_page` doesn't reorder either: it ignores the order of the zone array for components already on the page. Existing components keep their old order, and new ones are appended after them.

**What to do.** Set the order with two saves. First save the page with only the components you want at the top, then save it again with the rest appended in the order you want. Components re-added this way are cloned to new content IDs, in array order, so record the new IDs in your instance ledger. The original component items become unused once the new page version is published. Read the page back with `get_page` after each save to confirm the order and the content IDs.

## Reference names get normalized

**Symptom.** Code that fetches a list by the reference name you asked for returns nothing, or a linked-content dropdown renders blank in the editor.

**Why.** Reference names aren't always stored exactly as you typed them. The read API returns container reference names lowercased, and the MCP server normalizes linked-content reference names to the container's canonical case when you save. We have also seen container reference names shortened on creation, and in our builds a reference name couldn't be changed once the container existed.

**What to do.** After creating a container or model, take the reference name from the save result or from `get_containers`, record it in your instance ledger, and use that value in code. When writing a User Selectable linked-content field, use the exact case `get_containers` returns.

## Numbers and booleans can arrive as strings

**Symptom.** A comparison or sort on a numeric field gives odd results, or a true/false toggle is always treated as true.

**Why.** We have seen number and boolean field values come back from the Fetch API as strings, such as `"12"` and `"false"`. The string `"false"` is truthy in JavaScript.

**What to do.** Convert field values explicitly in your data layer (`Number(...)`, `value === "true"`), and type the converted shape, not the raw response. Dates deserve the same care: we have seen date fields shift by the instance's time zone, so test a date field with a value near midnight.

## One preview domain per instance

**Symptom.** In a multi-site build, clicking Preview for one site opens another site's domain.

**Why.** We have seen an instance support only one preview domain, which doesn't fit a build where several sites share one instance. The preview setup itself is described in [Setting Up Preview](/docs/developers/setting-up-preview).

**What to do.** Plan multi-site preview routing up front. One approach that worked: pass a site or channel identifier on the preview URL, and have the preview route redirect to the right site's hostname before it turns on draft mode.

## Other things worth knowing

- **Some setup is still done in the Agility app.** In our builds a person created the instance, API keys and sitemaps, set up roles, users and approval workflow, and initialized pages in other locales. Plan for that time.
- **Scheduled changes aren't instant.** Because of caching, a scheduled release or pull can take up to 15 minutes to reach the site.
- **Development reads Staging.** Never accept "it works locally" as proof that content is published.

## Related

- [From Brief to Working Site: The Vibe Coding Workflow](/docs/developers/vibe-coding-workflow)
- [Agility CMS MCP Server](/docs/developers/agility-cms-mcp-server)
- [From Proof of Concept to Production](/docs/developers/vibe-coding-to-production)
