MCP Prompts
MCP prompts are reusable, operator-authored templates that AI clients discover via prompts/list and invoke via prompts/get. Each prompt lives as an object in the mcp-prompt reserved collection. The body is a Twig template — the full cms.* function library is available alongside declared argument values.
Use prompts when you want to ship a starting workflow to an AI agent — a house-style blog scaffold, a brand-voice rewrite template, or a site-wide content audit outline. For data retrieval, use Saved-Query Tools instead.
| Prompts | Saved-Query Tools | |
|---|---|---|
| Returns | Rendered text (one user-role message) | Filtered collection data |
| AI agent uses it as | A starting point or instruction | A data query |
| Defined in | mcp-prompt collection | Collection schema MCP tab |
| Twig in body | Yes — full cms.* available | No |
Quick start
Section titled “Quick start”- In the admin, open MCP → Prompts and click Add Prompt.
- Fill in Name (snake_case, e.g.
draft_blog_post), Description, and Body. - Save. The prompt appears in
prompts/listimmediately — no rebuild, no restart.
Name: draft_blog_postDescription: Draft a new blog post in our house style.Body: Write a {{ args.tone | default('professional') }} blog post titled "{{ args.title }}". Use H2 subheadings, keep paragraphs under 5 sentences, and end with a call to action.Args: title (required), tone (optional)An AI client invokes it as:
{ "method": "prompts/get", "params": { "name": "draft_blog_post", "arguments": { "title": "10 Tips for Summer Photography", "tone": "conversational" } }}The server renders the Twig body with the supplied arguments and returns one user-role message.
Worked examples
Section titled “Worked examples”House-style scaffold
Section titled “House-style scaffold”Return a structured writing brief that matches editorial standards:
You are writing for {{ cms.config('displayName') }}.
Write a {{ args.format | default('long-form') }} article titled: {{ args.title }}
House rules:- Sentences: ≤ 25 words.- Tone: {{ args.tone | default('professional') }}.- Always include a tldr summary in the opening paragraph.- End with a bulleted "Key Takeaways" section.
Begin the article now.Args: title (required), format (optional), tone (optional).
Object-aware workflow
Section titled “Object-aware workflow”Pull live collection data into the prompt body using cms.collection.object():
{% set post = cms.collection.object('blog', args.post_id) %}You are editing an existing blog post.
Title: {{ post.title }}Published: {{ post.date }}Current body:{{ post.body | striptags }}
Rewrite the body in a {{ args.tone | default('professional') }} tone.Keep the same structure. Return only the rewritten body, no title.Args: post_id (required), tone (optional).
The agent passes the post’s ID; the prompt fetches the live record and injects it. Editors can trigger a rewrite by invoking prompts/get from Claude Desktop, Cursor, or any MCP client.
Site-wide aggregation
Section titled “Site-wide aggregation”Give the agent a cross-collection overview to answer high-level questions:
Site: {{ cms.config('displayName') }}Date: {{ "now" | date("Y-m-d") }}
Content summary:{% for collection in cms.collections %}- {{ collection.labelPlural }}: {{ collection.count }} objects{% endfor %}
Your task: {{ args.task }}
Focus on any content gaps, outdated objects (older than {{ args.months | default(6) }} months),or collections with fewer than 5 objects.Args: task (required), months (optional).
Twig context reference
Section titled “Twig context reference”The body template receives these variables at render time:
| Variable | Type | Notes |
|---|---|---|
args.* | string (or typed if declared) | Caller-supplied argument values. Only declared args are injected — undeclared keys are silently dropped. |
cms.* | object | Full T3 Twig adapter: cms.object(), cms.collection(), cms.config(), cms.collections, cms.env, etc. |
| Twig builtins | — | All Twig filters and functions: date, default, striptags, upper, for, if, etc. |
Arguments with required: true are validated before rendering. If a required argument is absent, the server returns a JSON-RPC error — the body template is never called.
Access model
Section titled “Access model”Each prompt has an Access setting:
| Setting | Who can call the prompt |
|---|---|
(inherit from target) | Inherits the MCP access of the collection or Data View named in Target. A blank target defaults to admin. |
Admin only | API-key authenticated callers only. |
Authenticated | OAuth Bearer token callers (and admin). |
Public | Anonymous AI agents (and all above). |
Targeting a collection or a Data View
Section titled “Targeting a collection or a Data View”Target accepts either, in one picker — entries are labelled with their kind, because the two are separate namespaces and an id can exist in both.
When an id does exist as both, it resolves to the collection. That order is deliberate rather than alphabetical: collections are filtered by access group, and Data Views are not — a view’s own mcp.access is its entire gate, by design. Resolving a colliding id to the collection lands on the stricter of the two models; the other order would let a view named after a collection silently widen a prompt’s reach.
That difference is worth knowing whenever you inherit from a view. authenticated on a collection means “signed-in callers whose groups grant access to it”; authenticated on a view means “any signed-in caller”, with no group filtering. If a prompt’s reach should respect access groups, target a collection or set Access explicitly.
One subtlety: whether the target exists as a collection decides which branch runs, not whether that collection sets mcp.access. A real collection with no MCP access configured resolves to admin and stops — it does not fall through to a same-named view.
Persona enforcement runs at two points:
prompts/list— inaccessible prompts are filtered out entirely. A public caller never sees anadminprompt in the list.prompts/get— the handler re-checks access at call time. A caller who guesses an admin prompt name receives a JSON-RPC error, not the rendered content.
Argument declaration
Section titled “Argument declaration”Arguments declared in the Arguments card field are typed and validated:
| Field | Notes |
|---|---|
| Name | Snake_case (^[a-z][a-z0-9_]*$). Referenced in the body as {{ args.name }}. |
| Description | Shown to AI agents in the prompts/list response so they know what to pass. |
| Required | Toggle. Missing required args return a JSON-RPC error before the body renders. |
Optional arguments with no default in the body render as an empty string when absent. Use Twig’s default filter: {{ args.tone | default('professional') }}.
Only declared argument names are injected into the template context. Extra keys passed by the caller are silently dropped.
Code-defined prompts (extensions)
Section titled “Code-defined prompts (extensions)”Extensions can ship code-defined prompts alongside the collection-stored ones this page covers. They share the same prompts/list and prompts/get surface; the difference is authoring path — PHP in an extension vs. JSON object in the admin.
See Extending MCP → Registering code-defined prompts for the full guide: signature, when-to-use comparison vs. collection-stored, worked examples, access tiers, and collision policy.
ID field and snake_case
Section titled “ID field and snake_case”The id field on the mcp-prompt schema is readonly and auto-generated from the Name you enter. It follows the snakeCase ID setting — see ID Field for details. The name field is the canonical identifier AI clients use to call the prompt; keep it stable once published.
Limitations (v1)
Section titled “Limitations (v1)”- Single user-role message. Each prompt returns one
user-rolePromptMessage. Multi-turn scaffolds (system + user) are a v2 target. - Synchronous render. The body is rendered inline during
prompts/get. Long-runningcms.*calls (e.g. fetching many objects) block the response. Keep the body lightweight. - No per-prompt persona override. Access is set at the prompt level. Collection inheritance is coarse-grained — there is no field-level or argument-level access control.
- No listChanged push in v1. Clients that have called
prompts/listare not notified when a new prompt is added until they re-list (e.g. on reconnect).
Reference
Section titled “Reference”- MCP Server — personas, transport, core tool catalog, collection MCP settings.
- Saved-Query Tools — parameterised data queries; the right tool when you need filtered collection content rather than a workflow template.
- Extending MCP —
registerMcpPrompt()in the full context of the extension MCP API surface.