MCP playbook
Recipes for driving Particles with an AI agent over MCP. The /docs/mcp page covers connecting and lists every tool; this page is the manual — the flows to follow, the guardrails to respect, and copy-paste sequences for the things agents do most.
This page assumes you have already connected an agent and created an access token. If not, start at Connect AI agents. Everything below works from Claude Code, Codex, Claude Desktop, or any MCP client.
The govern-first loop#
Every write in Particles follows the same shape as the Studio editor: you browse read-only by default, and every edit happens inside a Change — a working branch you open, edit, and hand back for review. An agent never edits main directly and never merges; it proposes changes on a branch and a human reviews & merges in Studio. Keep this loop in mind and the rest of the playbook falls out of it.
discover → read → open a Change → edit on the branch → hand off for review
1. list_projects no input needed — enumerates every accessible org's projects;
match the project the user named (name + kind), never ask for ids
2. list_branches is a Change already open, or start from main?
3. get_token / find_tokens read the current state before touching anything
4. create_branch open a Change (a working branch) to edit on
5. create_tokens / … make the edits on that branch
→ a human reviews and merges the Change in StudioThere is no merge tool. Merging a Change into main is a deliberate human step in Studio — agents propose, people ship. Editing a protected main directly is refused with MAIN_PROTECTED; open a Change instead.
Finding your way around#
Before acting, resolve what the user named to concrete ids. Discovery tools are reads — a read-only token is enough.
| To find… | Call | Notes |
|---|---|---|
| A project by name | list_projects {} | No input needed — enumerates every organization the credential can access and returns all their projects (id, name, kind, token mode, description, organizationId). Pass organizationId only to narrow. |
| The organizations themselves | list_organizations {} | First hop of id resolution — id, name, slug for every org the credential can access. Resolve org ids here, never by asking the user. |
| The branches / open Changes | list_branches { projectId } | Shows main plus any working branches. Discover branchId values here. |
| A token | get_token / find_tokens | get_token is exact-name; find_tokens is natural-language ("a calm card background"). |
| Token types / groups | list_token_types / list_token_groups | The structure a project organizes tokens into. |
| Platforms | list_platforms | Web/iOS/… targets, on multi-level projects. |
| Themes | list_themes / get_theme | The same themes the Studio switcher shows (branch-scoped, default main). get_theme returns a theme’s full override set, resolved to token names. |
When a user says “update the button colour in Astryx Foundation”, call list_projects first: the row’s name and kind let you pick the right projectId even when several projects share a similar name.
Editing tokens#
Open a Change, make the edits on its branch, then let a reviewer merge. Writing needs a read-write access token (a read-only one is refused with a clear message).
# "Darken the brand ramp one step"
create_branch {
projectId, name: "darken-brand-ramp",
description: "brand.600 fails on marketing surfaces; darkening one step"
}
→ { id: branchId, name: "darken-brand-ramp" }
update_token { projectId, tokenId, value: "#1d4ed8" } # from get_token
# …repeat, or use a batch call (below)
→ a human reviews & merges "darken-brand-ramp" in StudioPrefer create_branch before writing. If you write without a branchId you target main, which protectMain will usually reject. Write-time policy (naming rules, required descriptions, semantic-must-alias) is enforced on every create/update — an invalid token returns a structured 422 you can read and correct.
Bulk edits — one call, all-or-nothing
Pushing a palette or moving a selection? Use the batch tools rather than a loop of single calls — one request, atomic, and it won’t trip the rate limit. If any entry fails (policy, a dependency, a wrong id) nothing is written.
| Instead of… | Use | Behaviour |
|---|---|---|
| N × create_token | create_tokens { tokens: [...] } | Up to 200 tokens in one call. Every entry passes policy + tier ownership or none are created. |
| N × delete_token | delete_tokens { tokenIds: [...] } | Up to 200. Dependents inside the batch don’t block it; a token referenced from outside the batch fails the whole call (409). |
# "Create a blue palette" (11 shades) — one request, not eleven
create_tokens {
projectId, branchId,
tokens: [
{ name: "color/blue/50", type: "color", tier: "primitive", value: "#eff6ff", groupPath: "color/blue" },
{ name: "color/blue/100", type: "color", tier: "primitive", value: "#dbeafe", groupPath: "color/blue" },
… 9 more …
]
}
→ { created: 11, tokens: [ … ] }Managing themes#
A theme is an overlay, not a change/branch. One base value lives on the token; a theme swaps in a different value for the tokens it overrides. Themes live on a branch — created on main (the default), a theme is exactly what the Studio theme switcher shows and what products inherit. These are the same themes the token editor, Figma plugin, and export all use.
Don’t model themes as separate change branches. Seven branches each setting the same token to a different value would collide at merge (one token holds one value; last-merge-wins flattens the rest). The override overlay exists precisely to avoid that.
# "Add a dark theme where the background is near-black"
create_theme { projectId, name: "dark" } # defaults to main; pass branchId to target another
→ { id: themeId }
set_theme_overrides {
projectId, themeId,
overrides: [
{ tokenName: "color/background", value: "#0b0b0f" },
{ tokenName: "color/text", value: "#f5f5f7" }
]
# mode defaults to "merge" — updates only these tokens, keeps the theme's others.
# mode: "replace" makes the listed set the theme's ENTIRE overlay.
}
→ { themeId, overrideCount }Overrides are given by token name (resolved to ids for you), and the whole overlay is written in a single call — applying a themed set is one request, not one per token. Use get_theme to inspect what a theme changes before editing it, and delete_theme to remove an overlay (base values are untouched).
Managing design-system structure#
The same surfaces Studio exposes for shaping a design system are available as tools — project-scoped, gated by tokens:write / tokens:delete.
| Surface | Tools | What it is |
|---|---|---|
| Token types | list / create / update / delete_token_type | Built-in and custom token types, each scoped to a tier (primitive/semantic/…). |
| Token groups | list / create / delete_token_group | Named sub-namespaces within a type (e.g. a "brand" group under color). |
| Platforms | list / create / update / delete_platform | Web/iOS/… targets semantic tokens vary across on a multi-level project. May need the Business plan. |
| Contrast pairs | list / create / delete_contrast_pair | Foreground/background token pairs the a11y gate checks — created by token name. |
Understand before you change#
Reads that make an agent’s edits safer. Reach for these before a risky change and to answer “is this a good idea?” without writing anything.
| Question | Tool |
|---|---|
| What breaks if I change this token? | get_impact — affected tokens, the components that bind it, and a11y risk. |
| Is this colour pair readable? | check_contrast — WCAG ratio + AA/AA-large/AAA between two named tokens. |
| How healthy is the token system? | get_score (scalability) · get_coverage (bound/unbound, orphans). |
| What’s the governance state? | get_governance_status — lead time, review coverage, gate blocks, stale changes. |
| How does a token resolve in a theme? | resolve_theme — the value a token takes inside a named theme. |
Permissions & plans#
An access token can never exceed the permissions of the person who created it, and its scope is enforced on every call — exactly like Studio. Two things decide whether a call succeeds:
| Gate | What it means for an agent |
|---|---|
| Read vs read-write token | Every read tool works with a read token. The write tools (create/update/delete of tokens, branches, themes, types, groups, platforms, contrast pairs) need a read-write token — a read-only one returns 403 "This access token is read-only." |
| Project allowlist | A token scoped to specific projects returns PROJECT_NOT_IN_SCOPE for any other project. |
| Plan | MCP access needs the Team plan or above. Some features (knowledge-graph, platforms/multi-level) need Business — those return a clear "upgrade required" (FEATURE_GATE). |
| protectMain | Writing to a protected main is refused (MAIN_PROTECTED). Open a Change and write there. |
| org-level actions | Organisation management (e.g. linking foundation/brand modules) is never available to an access token — those stay in Studio with an interactive session. |
Guardrails & good patterns#
| Do | Why |
|---|---|
| Resolve the project by name + kind first | list_projects returns kind/description so you pick the right project even when names are similar. |
| Open a Change before writing | Keeps edits off main, gives reviewers a titled diff, and avoids MAIN_PROTECTED. |
| Batch when you can | create_tokens / delete_tokens are one atomic request — faster, and they won’t exhaust the rate limit like a loop of single calls. |
| Work in names, not ids | Theme overrides and contrast pairs take token names and resolve them for you; get ids from get_token/find_tokens when a tool needs one. |
| Read impact before a big change | get_impact / check_contrast tell you what a change affects before you make it. |
| Surface the server’s error verbatim | A 422 policy violation or 403 lists exactly what to fix — pass it on rather than guessing. |
Reading errors#
Tool errors are actionable — the message says what to do. The common ones:
| Error | What to do |
|---|---|
| This access token is read-only | The token lacks write permission. Create a read-write access token in Studio (Settings → Access Tokens) and reconnect. |
| MAIN_PROTECTED | You tried to write to a protected main. Call create_branch and write on that branch. |
| POLICY_VIOLATION (422) | A token breaks the project’s write-time policy (naming, required description, semantic-must-alias). The response lists each rule — fix and retry. |
| PROJECT_NOT_IN_SCOPE | The token isn’t scoped to this project. Use a token that covers it, or pick a project it does. |
| FEATURE_GATE / plan limit | The feature needs a higher plan. Nothing to retry until the org upgrades. |
| HAS_DEPENDENTS (409) | A delete is blocked because other tokens alias it or a component binds it. Remove those references first (or include them in a batch delete). |
For connection-level problems (401, tools not appearing, connector won’t connect) see the troubleshooting section on the Connect AI agents page.