MCP Prompts Primitive
Implements MCP 2025-03-26 — Prompts for dcc-mcp-core. Issues #351 and #355.
The prompts primitive is the third MCP surface McpHttpServer advertises, alongside tools and resources. It gives AI clients a way to discover reusable prompt templates — natural-language instructions parameterised with arguments — that a skill author has hand-crafted to elicit the right behavioural chain from the model.
Unlike tools/call (which executes side effects) and resources/read (which returns opaque bytes), prompts/get returns a rendered message array the client can splice straight into the conversation, preserving the skill author's intent without the model having to guess the right phrasing.
Wire protocol
When enabled, the server advertises the primitive in initialize:
{
"capabilities": {
"prompts": { "listChanged": true }
}
}Three JSON-RPC methods are exposed:
| Method | Purpose |
|---|---|
prompts/list | Return every registered prompt — name, description, argument schema. |
prompts/get | Render one prompt by name with caller-supplied arguments. |
notifications/prompts/list_changed | Server-pushed SSE event whenever a skill is loaded / unloaded. |
Source: explicit sibling prompts.yaml
Following the project-wide sibling-file rule (#356), hand-authored prompt templates never inline into SKILL.md. The skill author references a sibling file from the metadata.dcc-mcp namespace:
---
name: maya-geometry
description: "Maya geometry primitives and editing."
metadata:
dcc-mcp:
dcc: maya
prompts: prompts.yaml # single file, or
# prompts: prompts/*.prompt.yaml # glob, one file per prompt
---prompts.yaml contains two top-level lists — both optional:
prompts:
- name: bevel_all_edges
description: "Bevel every selected edge with a consistent chamfer width."
arguments:
- name: chamfer_width
description: "Chamfer width in Maya units."
required: true
- name: segments
description: "Number of chamfer segments (default 2)."
required: false
template: |
Use `maya_geometry__select_edges` to capture the current selection,
then call `maya_geometry__bevel_edges` with width={{chamfer_width}}
and segments={{segments}}. Verify the result with
`dcc_diagnostics__screenshot` before saving.
workflows:
- file: workflows/bake_proxies.workflow.yaml
prompt_name: bake_proxies_summary # optional renameExplicit prompts
Each entry under prompts: is a PromptSpec:
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | ✅ | Unique within the skill. Fully-qualified when served as MCP. |
description | string | ✅ | One-line summary the client shows to the user. |
arguments | list[ArgumentSpec] | ❌ | Typed placeholders for the template. |
template | string | ✅ | placeholders resolved against call-site arguments. |
ArgumentSpec fields: name, description, required (default false).
Workflow-derived prompts
The workflows: list auto-generates a summary prompt per referenced workflow. This is a minimal behavioural chain hint — "here are the steps that workflow runs, in order" — suitable for agents that want to narrate the workflow before (or instead of) executing it:
workflows:
- file: workflows/bake_proxies.workflow.yamlNo template is needed; the registry summarises the workflow's description + step list into a user-role message. Use prompt_name to override the default auto-generated name.
Single file vs glob
Both forms are accepted in metadata.dcc-mcp.prompts:
prompts.yaml— single file withprompts:+workflows:lists.prompts/*.prompt.yaml— glob, one file per prompt. Each file has the same shape as a single entry inprompts:.
Parsing is lazy: the path is recorded at scan / load time; the file contents are only read when the server handles prompts/list or prompts/get.
Derived prompts
If a loaded skill does not declare metadata.dcc-mcp.prompts, the registry tries conservative, explainable derivation from prompt-worthy metadata:
| Metadata | Source files | Derived prompt name |
|---|---|---|
metadata.dcc-mcp.examples | Markdown/text examples such as references/EXAMPLES.md or examples/*.md | <skill>.examples or <skill>.examples.<file-stem> |
metadata.dcc-mcp.recipes | Markdown, YAML, TOML, or text recipe references | <skill>.recipes or <skill>.recipes.<file-stem> |
metadata.dcc-mcp.workflows | *.workflow.yaml workflow specs | <skill>.<workflow-name> |
Derived prompts are meant to make prompts/list useful for adapters that already ship examples, recipes, or workflow metadata. They are intentionally plain: the rendered prompt includes the referenced guidance or a workflow step summary and tells the agent to prefer the skill's declared MCP tools.
Use explicit prompts.yaml when you need argument schemas, carefully worded model instructions, stable prompt names, or curated UX copy. Use derived prompts as a zero-adapter-code fallback for existing examples and workflow guidance.
Each prompt entry carries source metadata:
{
"_meta": {
"dcc.prompt_source": {
"skill": "maya-geometry",
"source": "examples"
}
}
}The gateway preserves that metadata while also adding _instance_id, _instance_short, and _dcc_type for the backend that supplied the prompt.
Templating engine
The rendering engine is intentionally minimal — one token only:
{{placeholder}}.
- Whitespace inside braces is trimmed:
=={{ foo }}.{{foo}} - An undeclared required argument raises
INVALID_PARAMS: missing required argument: <name>. - Brace content that isn't a bare identifier (
) is left untouched — the engine never evaluates expressions.{{ 1 + 1 }} - Unclosed
with no matching{{}}is emitted verbatim.
Keep templates small and declarative. If a template needs loops, conditionals, or data fetches, author it as a workflow (issue #348) instead and reference it from the workflows: list.
Server configuration
The primitive is enabled by default. Disable it globally with McpHttpConfig.enable_prompts = false:
from dcc_mcp_core import McpHttpConfig, create_skill_server
cfg = McpHttpConfig(port=8765)
cfg.enable_prompts = False # opt out — capability vanishes from initialize
server = create_skill_server("maya", cfg)
server.start()When disabled, the server omits the prompts capability and rejects prompts/list / prompts/get with Method not found.
Diagnostics
An empty prompts/list response includes a diagnostic path in _meta so clients can distinguish "no loaded skills" from "loaded skills have no prompt metadata" and "prompt-capable metadata failed to load":
{
"prompts": [],
"_meta": {
"dcc.prompt_diagnostics": {
"enabled": true,
"loaded_skill_count": 1,
"prompt_count": 0,
"prompt_capable_skill_count": 0,
"notes": [
"Loaded skills did not declare metadata.dcc-mcp.prompts, examples, recipes, or workflows."
]
}
}
}The REST GET /v1/prompts surface exposes the same information under diagnostics. Gateway aggregation adds backend-level diagnostics under _meta["dcc.prompt_diagnostics"].backends, including per-backend prompt counts and transport/protocol errors, while still returning healthy backend prompts.
Dynamic registration from Python
Python adapters can also register prompt templates directly before or after server start. server.prompts() returns a PromptHandle; register_prompt upserts into that handle and does not return a new handle.
from dcc_mcp_core import McpHttpConfig, McpHttpServer, ToolRegistry
registry = ToolRegistry()
server = McpHttpServer(registry, McpHttpConfig(port=8765))
prompts = server.prompts()
prompts.register_prompt(
name="bake_animation",
description="Guide an agent through baking animation keys.",
arguments=[
{"name": "frame_start", "description": "First frame", "required": True},
{"name": "frame_end", "description": "Last frame", "required": True},
],
template="Bake the active animation from {{frame_start}} to {{frame_end}}.",
)
# Later dynamic updates are visible to subsequent prompts/list calls.
prompts.unregister_prompt("bake_animation")
prompts.clear()Argument names must be non-empty and unique. Prefer sibling prompts.yaml for packaged skills; use dynamic registration for adapter-owned prompts that depend on runtime capabilities.
list_changed invariants
notifications/prompts/list_changedfires whenever the set of loaded skills changes (skills/load,skills/unload, hot-reload).- The registry's internal cache is invalidated in the same critical section that emits the notification — clients may call
prompts/listimmediately after and will observe the new set. - The notification fans out to every active SSE session; there is no per-session subscription model for prompts.
Guidance for skill authors
- Keep
templatebodies under 50 lines. Longer guidance belongs inreferences/and should be pulled in by the workflow layer. - Prefer fully-qualified tool names inside templates (
maya_geometry__bevel_edges) — the agent then doesn't have to guess. - If the behavioural chain is more than 3-4 tool calls, extract it to a
workflow.yamland let the server auto-generate the summary prompt. - Don't put secrets or environment-specific paths in templates — prompts are surfaced verbatim to the model.
- Treat derived prompts as a fallback. Once an example or recipe becomes a product-facing workflow, promote it to an explicit
prompts.yamlentry with a stable name and argument declarations.