Prompts
Maschinelle Übersetzung
Diese Seite wurde automatisch aus der englischen Dokumentation übersetzt, und die englische Seite ist die maßgebliche Fassung. Wenn sich etwas falsch liest, erklärt Übersetzungen, wie du es melden kannst.
Ein Prompt ist eine Nachrichtenvorlage, die die Person am Host auswählt.
Tools sind für das Modell gedacht. Ein Prompt ist das Gegenteil: Die Person wählt einen aus einem Menü in ihrem Client (ein Slash-Command, ein Button), füllt die Argumente aus, und die gerenderten Nachrichten landen in der Unterhaltung, als hätte sie sie selbst getippt.
Du deklarierst einen, indem du @mcp.prompt() auf eine Funktion setzt, die den Text zurückgibt.
Dein erster Prompt
from mcp.server import MCPServer
mcp = MCPServer("Code Helper")
@mcp.prompt()
def review_code(code: str) -> str:
"""Review a piece of code."""
return f"Please review this code:\n\n{code}"
Das SDK liest dieselben drei Dinge wie bei einem Tool:
- Der Name ist der Funktionsname:
review_code. - Die Beschreibung, die der Client anzeigt, ist der Docstring:
Review a piece of code. - Die Argumente stammen aus den Parametern.
codehat keinen Standardwert, also ist es erforderlich.
Das bekommt ein Client von prompts/list zurück:
{
"name": "review_code",
"description": "Review a piece of code.",
"arguments": [
{"name": "code", "required": true}
]
}
Hier gibt es kein JSON Schema. Prompt-Argumente sind eine flache Liste benannter String-Werte: ein Formular, das eine Person ausfüllt, keine Payload, die ein Modell zusammenbaut.
Rendern
Der Client rendert die Vorlage mit prompts/get und übergibt dabei die Argumente. Deine Funktion läuft, und der str, den du zurückgibst, wird zu einer einzigen User-Nachricht:
{
"description": "Review a piece of code.",
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Please review this code:\n\ndef add(a, b): return a + b"
}
}
],
"resultType": "complete"
}
Das ist der ganze Lebenslauf eines Prompts: unter seinem Namen aufgelistet, bei Bedarf gerendert, in den Chat eingefügt.
Check
required wird durchgesetzt, bevor deine Funktion läuft. Renderst du review_code ohne code,
schlägt der Request selbst mit einem JSON-RPC-Fehler (Code -32603) fehl:
mcp.shared.exceptions.MCPError: Internal server error
Es gibt kein Fehlerergebnis im Stil eines Tools, das man einem Modell zurückgeben könnte, denn es ist
kein Modell beteiligt: Der Aufruf löst eine Exception aus. Der Grund (Missing required arguments: {'code'})
landet im Log deines Servers.
Ausprobieren
Starte den Server mit dem MCP Inspector:
uv run mcp dev server.py
Öffne den Tab Prompts und wähle review_code. Der Inspector zeichnet ein Formular mit einem erforderlichen Feld code. Fülle es aus, rendere es, und du bekommst genau die User-Nachricht von oben zurück.
Mehr als eine Nachricht
Ein Code-Review ist eine Nachricht. Eine Debugging-Sitzung ist eine Unterhaltung, und ein Prompt kann sie komplett anstoßen.
Gib eine Liste von Nachrichten statt eines str zurück:
from mcp.server import MCPServer
from mcp.server.mcpserver.prompts.base import AssistantMessage, Message, UserMessage
mcp = MCPServer("Code Helper")
@mcp.prompt()
def review_code(code: str) -> str:
"""Review a piece of code."""
return f"Please review this code:\n\n{code}"
@mcp.prompt()
def debug_error(error: str) -> list[Message]:
"""Start a debugging conversation."""
return [
UserMessage("I'm seeing this error:"),
UserMessage(error),
AssistantMessage("I'll help debug that. What have you tried so far?"),
]
UserMessageundAssistantMessagekommen ausmcp.server.mcpserver.prompts.base. Übergib ihnen einenstr, und sie verpacken ihn für dich inTextContent. Die Rolle ist der Klassenname.Messageist ihre gemeinsame Basisklasse. Verwende sie als Rückgabeannotation.
Das Rendern von debug_error erzeugt jetzt drei Nachrichten, in dieser Reihenfolge:
{
"description": "Start a debugging conversation.",
"messages": [
{"role": "user", "content": {"type": "text", "text": "I'm seeing this error:"}},
{"role": "user", "content": {"type": "text", "text": "TypeError: 'int' object is not iterable"}},
{
"role": "assistant",
"content": {"type": "text", "text": "I'll help debug that. What have you tried so far?"}
}
],
"resultType": "complete"
}
Beachte die letzte. Einen assistant-Beitrag vorzubelegen ist der Weg, die nächste Antwort des Modells zu lenken, ohne dass die Person die Lenkung selbst tippen muss.
Titel und Argumentbeschreibungen
review_code ist ein Funktionsname, keine Beschriftung. Gib dem Client etwas Besseres für den Button und beschreibe jedes Argument, damit sich das Formular von selbst erklärt:
from typing import Annotated
from pydantic import Field
from mcp.server import MCPServer
mcp = MCPServer("Code Helper")
@mcp.prompt(title="Code review")
def review_code(
code: Annotated[str, Field(description="The code to review.")],
language: Annotated[str, Field(description="The language the code is written in.")] = "python",
) -> str:
"""Review a piece of code."""
return f"Please review this {language} code:\n\n{code}"
title="Code review"ist der menschenlesbare Name, genau wie dastitleeines Tools.Annotated[str, Field(description=...)]ist dasselbe Muster, mit dem Tools die Parameter eines Tools beschreibt. Hier landet die Beschreibung am Argument statt in einem Schema.languagehat einen Standardwert und ist damit nicht mehr erforderlich.
Der prompts/list-Eintrag enthält jetzt alles, was ein Client braucht, um ein gutes Formular zu zeichnen:
{
"name": "review_code",
"title": "Code review",
"description": "Review a piece of code.",
"arguments": [
{"name": "code", "description": "The code to review.", "required": true},
{"name": "language", "description": "The language the code is written in.", "required": false}
]
}
Info
Wenn du Tools gelesen hast, kennst du bis hierher schon alles. Derselbe Dekorator, derselbe
Docstring als Beschreibung, dasselbe Annotated/Field. Das Einzige, was sich ändert: wer
ihn auslöst (die Person) und wohin das Ergebnis geht (in die Unterhaltung).
Mehr als Text
UserMessage und AssistantMessage akzeptieren überall dort, wo sie einen str akzeptieren, auch einen Content-Block oder einen Image-/Audio-Helfer. Zwei Fälle kommen bei Prompts vor: ein Dokument anhängen und ein Bild anhängen.
Eine Datei einbetten
from pathlib import Path
from mcp.server import MCPServer
from mcp.server.mcpserver import Message, UserMessage
from mcp.types import EmbeddedResource, TextResourceContents
mcp = MCPServer("Code Helper")
STYLE_GUIDE_FILE = Path(__file__).parent / "style-guide.md" # or the path to your file on disk
@mcp.resource("style://python", mime_type="text/markdown")
def style_guide() -> str:
"""The team's Python style guide."""
return STYLE_GUIDE_FILE.read_text(encoding="utf-8")
@mcp.prompt()
def review_code(code: str) -> list[Message]:
"""Review a piece of code against the team style guide."""
guide = TextResourceContents(uri="style://python", mime_type="text/markdown", text=style_guide())
return [
UserMessage(EmbeddedResource(resource=guide)),
UserMessage(f"Review this code against the style guide above:\n\n{code}"),
]
- Der Styleguide ist eine Ressource unter
style://python(die behandelt Ressourcen), gelesen aus einerstyle-guide.mdnebenserver.py. Lege dort eine beliebige Markdown-Datei ab. EmbeddedResource(resource=TextResourceContents(...)), beide ausmcp.types, trägt die Datei samt URI und MIME-Typ als erste Nachricht; die Anweisung, die sich darauf bezieht, folgt als reiner Text.- Einbetten, statt den Guide in den f-String einzufügen, erlaubt dem Client, ihn als Anhang zu zeigen und
style://pythonspäter erneut zu öffnen, und das Modell erhält die Datei unverändert. Für eine Binärdatei nimmBlobResourceContentsmit einem base64-kodiertenblob.
Gerendert ist der content der ersten Nachricht ein resource-Block:
{"type": "resource", "resource": {"uri": "style://python", "mimeType": "text/markdown", "text": "* Prefer early returns.\n..."}}
Ein Bild anhängen
from pathlib import Path
from mcp.server import MCPServer
from mcp.server.mcpserver import Image, Message, UserMessage
mcp = MCPServer("Code Helper")
DIAGRAM_FILE = Path(__file__).parent / "architecture.png" # or the path to your file on disk
@mcp.prompt()
def explain_component(component: str) -> list[Message]:
"""Explain one component using the architecture diagram."""
return [
UserMessage(Image(path=DIAGRAM_FILE)),
UserMessage(f"Where does {component} sit in this architecture, and what does it talk to?"),
]
Imageist der Helfer aus Bilder, Audio und Icons.UserMessagewandelt ihn beim Rendern des Prompts in einenImageContent-Block um (die Datei base64-kodiert, der MIME-Typ aus.pngerraten);Audiowird auf dieselbe Weise zu einemAudioContent.- Lege ein beliebiges PNG namens
architecture.pngnebenserver.py. Prompt-Argumente sind Strings, daher kommt das Bild immer vom Server;componentliefert nur die Worte.
{"type": "image", "data": "iVBORw0KGgoAAAANSUhEUg...", "mimeType": "image/png"}
Die Liste zur Laufzeit ändern
Prompts lassen sich hinzufügen, während Clients verbunden sind, z. B. damit eine Person eine Anweisung als eigenen Menüeintrag speichern kann. Registriere den Prompt und benachrichtige dann:
from contextlib import suppress
from mcp.server import MCPServer
from mcp.server.mcpserver import Context
from mcp.server.mcpserver.prompts import Prompt
mcp = MCPServer("Code Helper")
@mcp.prompt()
def review_code(code: str) -> str:
"""Review a piece of code."""
return f"Please review this code:\n\n{code}"
@mcp.tool()
async def save_template(name: str, instruction: str, ctx: Context) -> str:
"""Save an instruction as a prompt the user can pick from the menu."""
def template(code: str) -> str:
return f"{instruction}\n\n{code}"
with suppress(ValueError): # replace an existing entry of the same name
mcp.remove_prompt(name)
mcp.add_prompt(Prompt.from_function(template, name=name, description=instruction))
await ctx.notify_prompts_changed()
await ctx.session.send_prompt_list_changed()
return f"Saved '{name}' to the prompt menu."
mcp.add_prompt(Prompt.from_function(fn, name=..., description=...))registriert eine Funktion genau so, wie@mcp.prompt()es täte, undmcp.remove_prompt(name)ist die Umkehrung.add_promptbehält einen vorhandenen Eintrag gleichen Namens, statt ihn zu überschreiben; deshalb entfernt das Tool zuerst einen etwaigen alten, damit Speichern ein Ersetzen ist.prompts/listspiegelt die Änderung sofort wider.await ctx.notify_prompts_changed()sendetnotifications/prompts/list_changedan jeden2026-07-28-Client, der auf einemsubscriptions/listen-Stream lauscht (Abonnements).await ctx.session.send_prompt_list_changed()sendet sie an den aufrufenden Client, wenn dieser älter als 2026 ist (Legacy-Clients unterstützen). Rufe beide auf; jede tut nichts, wenn es niemanden zu benachrichtigen gibt.- Ein Client, der die Benachrichtigung erhält, ruft
prompts/listerneut auf. Im Python-Clientist dasasync with client.listen(prompts_list_changed=True) as sub:, was einPromptsListChanged-Event liefert.
Zusammenfassung
@mcp.prompt()auf einer Funktion macht sie zu einem Prompt. Der Name kommt von der Funktion, die Beschreibung vom Docstring.- Prompts sind von der Person gesteuert: Der Client listet sie auf, die Person wählt einen und füllt die Argumente aus.
- Argumente sind eine flache Liste benannter Strings (kein Schema). Ein Parameter mit Standardwert ist optional.
- Gibst du einen
strzurück, wird daraus eine User-Nachricht. Gib eine Liste vonUserMessage/AssistantMessagezurück, um eine mehrteilige Unterhaltung anzustoßen. title=undField(description=...)sind das, was ein Client in seiner Oberfläche anzeigt.- Ein fehlendes erforderliches Argument lässt den ganzen Request fehlschlagen. Es gibt kein Fehlerergebnis pro Prompt.
- Verpacke eine
EmbeddedResourceoder einImagein eineUserMessage, um ein Dokument oder ein Bild anzuhängen. - Füge Prompts zur Laufzeit mit
mcp.add_prompt(...)/mcp.remove_prompt(...)hinzu oder entferne sie, dannawait ctx.notify_prompts_changed()undawait ctx.session.send_prompt_list_changed().
Serverseitige Autovervollständigung für die Argumente eines Prompts (oder eines Ressourcen-Templates) ist Vervollständigungen.