Skip to main content
Prompts are user-controlled message templates. Unlike tools (which the LLM calls), prompts are selected by users and rendered into conversation messages. Prompts flow through the MCP protocol like this:
  1. A client discovers prompts via prompts/list (each prompt may include arguments and metadata).
  2. A client renders a prompt via prompts/get with string-valued arguments.
  3. The server executes your prompt renderer.
  4. The server returns a GetPromptResult containing messages (and optional description) per MCP spec.
Define prompts with @prompt(...) and register them with server.collect(...) (or inside with server.binding(): ...).

Decorator signature

  • prompt(...): prompt(name: str, *, description=None, title=None, arguments=None, icons=None, meta=None)
Your renderer is called like:
  • fn(arguments: dict[str, str] | None) → returns messages / mapping / GetPromptResult / None

Basic prompt

from dedalus_mcp import MCPServer, prompt, types

@prompt(
    "code-review",
    description="Review code for issues",
    arguments=[
        types.PromptArgument(name="language", required=False),
        types.PromptArgument(name="focus", required=False),
    ],
)
def code_review(arguments: dict[str, str] | None):
    args = arguments or {}
    language = args.get("language", "python")
    focus = args.get("focus")
    focus_text = f" Focus on: {focus}." if focus else ""

    return [
        ("assistant", "You are a senior code reviewer."),
        ("user", f"Review the following {language} code.{focus_text}"),
    ]

server = MCPServer("assistant")
server.collect(code_review)
The description tells the client/LLM what the prompt is for. arguments=[...] defines what the client should pass to prompts/get.

Arguments (required vs optional)

Dedalus MCP treats arguments as required only if you mark them required=True in the decorator’s arguments=[...].
from dedalus_mcp import MCPServer, prompt, types

@prompt(
    "translate",
    description="Translate text",
    arguments=[
        types.PromptArgument(name="text", required=True),
        types.PromptArgument(name="target_lang", required=False),
    ],
)
def translate(arguments: dict[str, str] | None):
    args = arguments or {}
    text = args["text"]
    target = args.get("target_lang", "English")
    return [("user", f"Translate this to {target}: {text}")]

server = MCPServer("assistant")
server.collect(translate)
If required args are missing, Dedalus raises an MCP error with INVALID_PARAMS.

Complex argument values (lists/dicts)

MCP prompt arguments are strings. If you need structured values, pass JSON strings and parse them yourself:
import json
from dedalus_mcp import prompt, types

@prompt(
    "summarize",
    description="Summarize a document",
    arguments=[types.PromptArgument(name="focus_areas_json", required=False)],
)
def summarize(arguments: dict[str, str] | None):
    args = arguments or {}
    focus_areas = json.loads(args.get("focus_areas_json", "[]"))
    return [("user", f"Summarize this document. Focus on: {focus_areas}")]

Return formats

Prompt renderers can return:
  • A list/iterable of messages, where each item can be:
    • a (role, content) tuple
    • a mapping like {"role": "...", "content": ...}
    • a PromptMessage instance
  • A mapping with explicit control:
    • required: "messages"
    • optional: "description"
  • GetPromptResult
  • None (produces zero messages)
Not supported: returning a raw str (Dedalus raises TypeError so you always provide role + content).

Explicit control (mapping)

from dedalus_mcp import prompt

@prompt("status", description="Daily status template")
def status(arguments: dict[str, str] | None):
    return {
        "description": "Status template",
        "messages": [
            ("assistant", "You summarize daily status reports."),
            ("user", "Write yesterday/today/blockers."),
        ],
    }

Message content

For message content, you can use:
  • a str (auto-coerced to text content)
  • a full content-block mapping (e.g. {"type": "text", "text": "..."})
  • a content-block instance from dedalus_mcp.types (e.g. TextContent, ImageContent, etc.)

Async prompts

from dedalus_mcp import prompt

@prompt("db-summary", description="Summarize database state")
async def db_summary(arguments: dict[str, str] | None):
    # await fetch_db_stats(...)
    return [
        ("assistant", "You analyze database metrics."),
        ("user", "Summarize current DB health and recent anomalies."),
    ]
Prefer async def for I/O.

Decorator options

from dedalus_mcp import prompt

@prompt(
    "analyze",
    description="Analyze code",
    title="Code Analysis",
    icons=[{"type": "url", "url": "https://example.com/icon.png"}],
    meta={"category": "code"},
    arguments=[{"name": "language", "required": False}],
)
def analyze(arguments: dict[str, str] | None):
    language = (arguments or {}).get("language", "python")
    return [("user", f"Analyze this {language} code for bugs, style, and security.")]

Context access

If a prompt is rendered during an MCP request, it can access context via get_context() (for logging, progress, etc.). Note: get_context() only works inside an active MCP request handler; calling it outside a request raises LookupError.
from dedalus_mcp import prompt, get_context

@prompt("generate-report", description="Generate a report request")
async def generate_report(arguments: dict[str, str] | None):
    ctx = get_context()
    await ctx.info("Rendering prompt", data={"args": arguments or {}})
    return [("user", "Generate a concise weekly report for this project.")]

Testing

Test prompt renderers like normal functions:
def test_code_review_prompt():
    msgs = code_review({"language": "python", "focus": "error handling"})
    assert len(msgs) == 2
Integration-style test via the server API (mirrors prompts/get):
import pytest
from dedalus_mcp import MCPServer

@pytest.mark.asyncio
async def test_prompt_rendering():
    server = MCPServer("test")
    server.collect(code_review)

    result = await server.invoke_prompt("code-review", arguments={"language": "rust"})
    assert result.messages