Vervollständigungen
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 Client, der eine UI auf deinem Server aufbaut, möchte Argumentwerte automatisch vervollständigen, während die Person tippt: Sprachnamen, Repository-Namen, Dateipfade.
Mit Vervollständigungen liefert dein Server diese Vorschläge.
Etwas zum Vervollständigen
Vervollständigungen gibt es für genau zwei Dinge: die Argumente eines Prompts und die Parameter eines Ressourcen-Templates. Beginne also mit einem Server, der von beidem eines hat:
from mcp.server import MCPServer
mcp = MCPServer("GitHub Explorer")
@mcp.resource("github://repos/{owner}/{repo}")
def github_repo(owner: str, repo: str) -> str:
"""A GitHub repository."""
return f"Repository: {owner}/{repo}"
@mcp.prompt()
def review_code(language: str, code: str) -> str:
"""Review a snippet of code."""
return f"Review this {language} code:\n{code}"
Noch hat hier nichts mit Vervollständigungen zu tun.
review_codenimmt einelanguageentgegen. Niemand sollte raten müssen, welche Schreibweisen du akzeptierst.github_reponimmt einenownerund einrepoentgegen. Freitextfelder für beide ergeben ein schlechtes Formular.
Der Vervollständigungs-Handler
Füge eine mit @mcp.completion() dekorierte Funktion hinzu:
from mcp.server import MCPServer
from mcp.types import Completion, CompletionArgument, CompletionContext, PromptReference, ResourceTemplateReference
mcp = MCPServer("GitHub Explorer")
LANGUAGES = ["go", "javascript", "python", "rust", "typescript"]
@mcp.resource("github://repos/{owner}/{repo}")
def github_repo(owner: str, repo: str) -> str:
"""A GitHub repository."""
return f"Repository: {owner}/{repo}"
@mcp.prompt()
def review_code(language: str, code: str) -> str:
"""Review a snippet of code."""
return f"Review this {language} code:\n{code}"
@mcp.completion()
async def handle_completion(
ref: PromptReference | ResourceTemplateReference,
argument: CompletionArgument,
context: CompletionContext | None,
) -> Completion | None:
if isinstance(ref, PromptReference) and argument.name == "language":
return Completion(values=[lang for lang in LANGUAGES if lang.startswith(argument.value)])
return None
- Es gibt einen Handler pro Server. Jeder Vervollständigungs-Request landet hier, und du verzweigst danach, was gerade vervollständigt wird.
- Er muss mit
async defdefiniert sein: Das SDK wartet per await auf ihn. - Er erhält drei Argumente:
ref: um welchen Prompt oder welches Ressourcen-Template es geht, alsPromptReferenceoderResourceTemplateReference. Mitisinstanceunterscheidest du die beiden.argument:argument.nameist das Argument, das vervollständigt wird,argument.valuedas, was die Person bisher getippt hat.context: die bereits aufgelösten Argumente. Ignoriere es vorerst.- Du gibst eine
Completion(values=[...])zurück, oderNone, wenn du nichts anzubieten hast.
Tip
argument.value ist das Präfix, das die Person getippt hat. Das SDK filtert nicht für dich: Was
immer du in values packst, zeigt die UI an. Das startswith schreibst du selbst.
Ausprobieren
Steuere ihn mit dem In-Memory-Client aus Testen an. Rufe
client.complete() mit ref=PromptReference(name="review_code") und
argument={"name": "language", "value": "py"} auf:
result.completion.values # ['python']
refist derselbe Referenztyp, den dein Handler erhält.argumentist ein einfaches dict mit genau zwei Schlüsseln,nameundvalue.
Schickst du ein leeres value, bekommst du die ganze Liste zurück. lang.startswith("") ist für jede Sprache wahr:
result.completion.values # ['go', 'javascript', 'python', 'rust', 'typescript']
Fragst du nach code (einem Argument, das dein Handler nicht kennt), gibt er None zurück, was das SDK in eine leere Liste verwandelt:
result.completion.values # []
None bedeutet „keine Vorschläge“, nie einen Fehler. Eine UI fällt auf ein einfaches Textfeld zurück.
Eine Capability, die du nie deklariert hast
Den Handler zu registrieren ist die Deklaration. Verbinde einen Client und sieh nach:
client.server_capabilities.completions # CompletionsCapability()
Du hast completions nirgends aufgeführt. Das SDK hat den Handler gesehen und die Capability für dich deklariert. Jede optionale Capability funktioniert so: Der Handler ist die Deklaration. (Die drei Primitive sind nicht optional: MCPServer deklariert sie immer, mit oder ohne Handler.)
Check
Geh zurück zur ersten server.py (der ohne Handler) und frage trotzdem. Der Aufruf schlägt
mit einem JSON-RPC-Fehler fehl:
Method not found
Und client.server_capabilities.completions ist None. Genau dafür ist die Capability da: Ein
Client, der sich korrekt verhält, prüft sie und schickt den Request, den du nicht beantworten kannst, gar nicht erst.
Abhängige Argumente
github://repos/{owner}/{repo} hat zwei Parameter, und die sinnvollen Werte für repo hängen davon ab, welcher owner zuerst gewählt wurde.
Dafür ist context da. Es trägt die Argumente, die die Person bereits aufgelöst hat:
from mcp.server import MCPServer
from mcp.types import Completion, CompletionArgument, CompletionContext, PromptReference, ResourceTemplateReference
mcp = MCPServer("GitHub Explorer")
LANGUAGES = ["go", "javascript", "python", "rust", "typescript"]
REPOS_BY_OWNER = {
"modelcontextprotocol": ["python-sdk", "typescript-sdk", "inspector"],
"pydantic": ["pydantic", "pydantic-ai", "logfire"],
}
@mcp.resource("github://repos/{owner}/{repo}")
def github_repo(owner: str, repo: str) -> str:
"""A GitHub repository."""
return f"Repository: {owner}/{repo}"
@mcp.prompt()
def review_code(language: str, code: str) -> str:
"""Review a snippet of code."""
return f"Review this {language} code:\n{code}"
@mcp.completion()
async def handle_completion(
ref: PromptReference | ResourceTemplateReference,
argument: CompletionArgument,
context: CompletionContext | None,
) -> Completion | None:
if isinstance(ref, PromptReference) and argument.name == "language":
return Completion(values=[lang for lang in LANGUAGES if lang.startswith(argument.value)])
if isinstance(ref, ResourceTemplateReference) and argument.name == "repo":
if context is None or context.arguments is None:
return None
repos = REPOS_BY_OWNER.get(context.arguments.get("owner", ""), [])
return Completion(values=[repo for repo in repos if repo.startswith(argument.value)])
return None
- Der neue Zweig greift beim Parameter
repodes Templates. context.argumentsist eindict[str, str] | Nonemit den bisher gewählten Werten (hierowner).- Noch kein
ownerbedeutet keine sinnvollen Vorschläge, also gibt der HandlerNonezurück.
Der Client schickt diese aufgelösten Werte mit context_arguments=. Diesmal ist ref eine
ResourceTemplateReference(uri="github://repos/{owner}/{repo}"). Frage mit leerem
value nach repo und übergib context_arguments={"owner": "modelcontextprotocol"}:
result.completion.values # ['python-sdk', 'typescript-sdk', 'inspector']
Lässt du context_arguments= weg, gibt derselbe Aufruf [] zurück. Der Handler kann nicht wissen, welche Repos er anbieten soll, solange er den Owner nicht kennt.
Info
Completion nimmt außerdem total= und has_more= entgegen. Setze sie, wenn values ein Ausschnitt einer
längeren Liste ist, damit eine UI „und 200 weitere“ anzeigen kann. Die meisten Handler brauchen sie nie.
Zusammenfassung
- Vervollständigungen sind Vorschläge für Prompt-Argumente und Parameter von Ressourcen-Templates. Sonst nichts.
@mcp.completion()registriert den einen Handler. Er istasync def (ref, argument, context) -> Completion | None.- Verzweige nach
isinstance(ref, ...)und nachargument.name. Filtere selbst nachargument.value. Nonewird zu einer leeren Liste. Es ist nie ein Fehler.context.argumentsenthält die bereits aufgelösten Werte; der Client liefert sie alscontext_arguments=.- Die Capability
completionserscheint, sobald du den Handler registrierst. Ohne ihn endet der Request mitMethod not found.
Vorschläge helfen, solange die Person einen Prompt oder ein Template noch ausfüllt; um ihr mitten in einem Tool-Aufruf eine Frage zu stellen, brauchst du Elicitation (Rückfrage bei der Person am Host). Alles, was ein Tool außer Text zurückgeben kann, steht in Bilder, Audio und Icons.