Prompts

A prompt is a starting point a person chooses – from a menu, a slash command, a palette. It expands into one or more messages. See Tools, resources, and prompts.

This is where a server offers its own expertise about how to ask: the phrasing that gets good results, the context worth including, the shape of a useful conversation. A tool is what the model decides to do; a prompt is what the person decides to start.

from pydantic import BaseModel, Field

from aiohttp_tiny_mcp import MemoryHub, MemorySessionStore, Registry

registry = Registry("review", "1.0", hub=MemoryHub(), session_store=MemorySessionStore())

Declaring one

class Review(BaseModel):
    language: str = Field(description="The language the code is written in.")
    strictness: str = "normal"


@registry.prompt
async def code_review(args: Review) -> str:
    """Review a piece of code for correctness and clarity."""
    return (
        f"Review the following {args.language} code. "
        f"Be {args.strictness} about correctness, naming and error handling."
    )

The argument model becomes the prompt’s declared arguments. Each field’s description reaches the person filling it in, and a field without a default is marked required.

from aiohttp_tiny_mcp import Client
from aiohttp_tiny_mcp.protocol.selection import AdapterSet

url = await serve(registry)
async with Client(url, AdapterSet.default().by_version["2026-07-28"]) as client:
    await client.initialize()
    listed = await client.list_prompts()
    result = await client.get_prompt("code_review", {"language": "python"})

declared = {argument.name: argument.required for argument in listed[0].arguments}
assert declared == {"language": True, "strictness": False}
assert "python code" in result.messages[0].content.text

Prompt arguments are strings on the wire, whatever the model says, because a person types them into a box. Keep them to strings, or accept that pydantic will coerce.

Returning several messages

A string becomes one user message. Return a list to shape a conversation – several turns, or an assistant turn that primes the answer.

from aiohttp_tiny_mcp.models import PromptMessage, TextContent


class Explain(BaseModel):
    topic: str


@registry.prompt
async def explain(args: Explain) -> list[PromptMessage]:
    """Explain something, starting from what the reader already knows."""
    return [
        PromptMessage(
            role="user",
            content=TextContent(text=f"Explain {args.topic}."),
        ),
        PromptMessage(
            role="assistant",
            content=TextContent(text="First, what do you already know about it?"),
        ),
    ]

Return a GetPromptResult where the prompt needs a description of its own alongside the messages.

Names and titles

class Nothing(BaseModel):
    pass


@registry.prompt(name="summarise", title="Summarise the conversation")
async def summarize_conversation(args: Nothing) -> str:
    """Condense what has been said so far."""
    return "Summarise the conversation so far in five bullet points."

name is the identifier a client uses; title is what a person reads in the menu. Where a title is absent the name is shown, so name prompts for people.

Suggesting argument values

A person filling in language should not have to guess what is accepted. See Completion.

A prompt may ask, too

A prompt handler receives the Exchange like any other, so it can ask the user something while it builds the messages. Whether that is a good idea depends on your host – a prompt is already a person interacting – but nothing stops it. See Asking the user.