Prompts¶
Prompts are templated instructions for a model. Unlike tools (which execute) and resources (which return data), a prompt returns a list of messages the client feeds back into an LLM. The plugin supports two registration paths:
Standalone prompt functions — plain (async) callables decorated with
mcp_prompt()and passed toLitestarMCP(prompts=[...]). These are not routed under HTTP; they are reachable only throughprompts/get.Handler-based prompts — Litestar route handlers tagged with
mcp_prompt="<name>". The handler stays a normal HTTP endpoint and is published throughprompts/get, with full access to DI, guards, and signature validation.
The task-manager demo registers a single standalone prompt that summarises the current task list:
docs/examples/task_manager/main.py - register_prompts¶def register_prompts(store: "dict[int, Task]") -> "list[Any]":
"""Return standalone MCP prompts bound to ``store``.
Prompts are templated instructions for an LLM. Unlike tools (which execute)
and resources (which return data), prompts return a list of messages the
client feeds back into the model. Returning a plain ``str`` is the
simplest form — the plugin wraps it as a single user-role text message.
"""
# start-example
@mcp_prompt(
name="summarize_tasks",
title="Summarize tasks",
description="Ask the model to summarize the current task list in a chosen style.",
)
async def summarize_tasks(style: str = "concise") -> str:
"""Build a prompt summarizing the task store.
Args:
style: Summary style hint, e.g. ``concise``, ``detailed``,
``bullet-points``. Used verbatim in the prompt body.
"""
lines = [f"- [{'x' if task.completed else ' '}] {task.title}: {task.description}" for task in store.values()]
body = "\n".join(lines) if lines else "(no tasks)"
return f"Summarise the following tasks in a {style} style:\n\n{body}"
# end-example
return [summarize_tasks]
See Marking Routes for the handler-based opt-key form and the full
mcp_prompt* opt-key reference.
Return Value Normalisation¶
A prompt's return value is normalised to a list of MCP PromptMessage
dicts before it goes on the wire:
str→ single user-role text message.dictwith arolekey and a recognised content block (text/image/audio/resource_link/resource) is wrapped in a list and used as-is.listitems follow the same dict rules.Anything else is coerced to
str(result)with a warning log.
In practice this means the simplest possible prompt body — returning a
single string — Just Works, and richer multi-message replies use the
dict / list form. The advertised arguments list is derived from the
function signature (or the handler's parsed_fn_signature for the opt-key
form), enriched with Google-style docstring descriptions when present.
Capability Gating¶
The prompts capability is only advertised — both in initialize's
capability response and in /.well-known/mcp-server.json — when at
least one visible prompt is registered. This matches the MCP spec's
recommendation that servers only declare capabilities for primitives
they actually expose. The same per-tag and per-operation include/exclude
filters that gate tools and resources also gate prompt visibility.
JSON-RPC Round-Trip¶
After initialize, clients drive prompts via prompts/list and
prompts/get:
# List every prompt the server publishes
curl -sS -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"prompts/list","params":{}}'
# Render a specific prompt (task-manager demo)
curl -sS -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"prompts/get",
"params":{"name":"summarize_tasks",
"arguments":{"style":"detailed"}}}'
A missing required argument surfaces as JSON-RPC INVALID_PARAMS
(-32602); an unknown prompt name and an unknown argument name do the
same — the executor validates the argument set against the handler's
parsed_fn_signature before dispatch. If a handler-based prompt fails
during execution, the error maps to INTERNAL_ERROR (-32603)
with the handler's HTTP status preserved in data.statusCode — the
same contract resources use (see Resources). The JSON-RPC code
reflects the primitive-level error class, not the raw HTTP status.