Автодоповнення
Машинний переклад
Цю сторінку перекладено автоматично з англомовної документації, і основною версією є англомовна сторінка. Якщо щось читається неправильно, на сторінці Переклади пояснено, як про це повідомити.
Клієнт, що будує інтерфейс поверх вашого сервера, хоче автоматично доповнювати значення аргументів, поки користувач їх вводить: назви мов, назви репозиторіїв, шляхи до файлів.
Автодоповнення (completions) — це спосіб, у який сервер надає такі підказки.
Що варто доповнювати
Автодоповнення стосується рівно двох речей: аргументів промпту і параметрів шаблону ресурсу. Тож почніть із сервера, де є по одному з них:
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}"
Тут поки нічого про автодоповнення.
review_codeприймаєlanguage. Користувач не повинен вгадувати, які варіанти написання ви приймаєте.github_repoприймаєownerіrepo. Два поля вільного введення — це погана форма.
Обробник автодоповнення
Додайте одну функцію з декоратором @mcp.completion():
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
- Обробник один на сервер. Кожен запит на автодоповнення потрапляє сюди, а ви розгалужуєте логіку залежно від того, що саме доповнюється.
- Він має бути
async def: SDK викликає його через await. - Він отримує три аргументи:
ref: який саме промпт або шаблон ресурсу — якPromptReferenceабоResourceTemplateReference. Розрізняють їх черезisinstance.argument:argument.name— аргумент, що доповнюється,argument.value— те, що користувач уже встиг ввести.context: уже визначені аргументи. Поки що ігноруйте його.- Повертаєте
Completion(values=[...])абоNone, коли запропонувати нічого.
Tip
argument.value — це префікс, який ввів користувач. SDK не фільтрує за вас: що покладете
у values, те й покаже інтерфейс. startswith пишете ви самі.
Спробуйте самі
Перевірте його за допомогою Client у пам'яті зі сторінки Тестування. Викличте
client.complete() з ref=PromptReference(name="review_code") і
argument={"name": "language", "value": "py"}:
result.completion.values # ['python']
ref— той самий тип посилання, що його отримує обробник.argument— звичайний словник із рівно двома ключами,nameіvalue.
Надішліть порожнє value — і повернеться весь список. lang.startswith("") істинне для кожної мови:
result.completion.values # ['go', 'javascript', 'python', 'rust', 'typescript']
Запитайте про code (аргумент, якого обробник не знає) — він поверне None, а SDK перетворить його на порожній список:
result.completion.values # []
None означає «підказок немає», а не помилку. Інтерфейс просто показує звичайне текстове поле.
Можливість, яку ви не оголошували
Реєстрація обробника і є оголошенням. Під'єднайте клієнт і погляньте:
client.server_capabilities.completions # CompletionsCapability()
Ви ніде не вказували completions. SDK побачив обробник і оголосив можливість за вас. Так працює кожна необов'язкова можливість: обробник і є оголошенням. (Три примітиви не є необов'язковими: MCPServer оголошує їх завжди, з обробниками чи без.)
Check
Поверніться до першого server.py (того, що без обробника) і все одно надішліть запит. Виклик
завершиться помилкою JSON-RPC:
Method not found
А client.server_capabilities.completions дорівнює None. У цьому й сенс можливості:
коректний клієнт перевіряє її й ніколи не надсилає запит, на який ви не можете відповісти.
Залежні аргументи
github://repos/{owner}/{repo} має два параметри, і корисні значення для repo залежать від того, якого owner обрали спершу.
Саме для цього є context. Він містить аргументи, які користувач уже визначив:
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
- Нова гілка спрацьовує для параметра
repoшаблону. context.arguments— цеdict[str, str] | Noneзі значеннями, вибраними досі (тут —owner).- Немає
owner— немає й осмислених підказок, тож обробник повертаєNone.
Клієнт надсилає ці визначені значення через context_arguments=. Цього разу ref — це
ResourceTemplateReference(uri="github://repos/{owner}/{repo}"). Запитайте repo з
порожнім value і передайте context_arguments={"owner": "modelcontextprotocol"}:
result.completion.values # ['python-sdk', 'typescript-sdk', 'inspector']
Приберіть context_arguments= — і той самий виклик поверне []. Обробник не може знати, які репозиторії пропонувати, доки не знає власника.
Info
Completion також приймає total= і has_more=. Задавайте їх, коли values — лише зріз
довшого списку, щоб інтерфейс міг показати «і ще 200». Більшості обробників вони ніколи не знадобляться.
Підсумки
- Автодоповнення — це підказки для аргументів промптів і параметрів шаблонів ресурсів. Ні для чого іншого.
@mcp.completion()реєструє єдиний обробник. Цеasync def (ref, argument, context) -> Completion | None.- Розгалужуйтеся за
isinstance(ref, ...)та заargument.name. Фільтруйте заargument.valueсамостійно. Noneстає порожнім списком. Це ніколи не помилка.context.argumentsмістить уже визначені значення; клієнт передає їх якcontext_arguments=.- Можливість
completionsз'являється, щойно ви реєструєте обробник. Без нього відповідь на запит —Method not found.
Підказки допомагають, поки користувач ще заповнює промпт чи шаблон; щоб поставити йому запитання посеред виклику інструмента, потрібна Еліцитація (elicitation). Усе, що інструмент може повернути, крім тексту, — на сторінці Зображення, аудіо та значки.