Первые шаги
Машинный перевод
Эта страница переведена с английской документации автоматически, и основной версией остаётся английская страница. Если что-то читается неправильно, на странице Переводы объясняется, как об этом сообщить.
Главная страница идёт быстро: написать сервер, запустить его, вызвать инструмент.
Эта страница идёт не спеша: все три вида того, что сервер может предоставлять, и название для всего, что встретится по дороге.
Хост, клиент и сервер
Три слова, которые с этого момента будут встречаться на каждой странице:
- Хост — это LLM-приложение: Claude, IDE, среда выполнения агентов. Это то, с чем разговаривает пользователь.
- Клиент живёт внутри хоста и говорит на MCP. Хост запускает по одному клиенту на каждый сервер, к которому подключён.
- Сервер — это то, что вы строите с помощью этого SDK. Он предоставляет клиентам разные вещи. С моделью напрямую он никогда не общается.
Вы пишете сервер. Хосты — это чужой продукт. SDK также даёт класс Client. Он пригодится для тестирования серверов и появится ниже на этой странице.
Три примитива
Сервер предоставляет ровно три вида сущностей. Различает их то, кто решает ими воспользоваться:
| Примитив | Кто управляет | Что это такое | Пример |
|---|---|---|---|
| Инструменты | Модель | Функция, которую модель вызывает, чтобы совершить действие | Вызов API, запись в базу данных |
| Ресурсы | Приложение | Данные, которые хост загружает в контекст модели | Содержимое файла, ответ API |
| Промпты | Пользователь | Многоразовый шаблон сообщения, который пользователь вызывает по имени | Слэш-команда, пункт меню |
«Кто управляет» — в этом весь смысл разделения. Инструмент запускается, потому что его решила вызвать модель. Ресурс прикрепляется, потому что приложение решило, что он нужен модели. Промпт запускается, потому что его выбрал пользователь.
Info
Если вы уже строили веб-API, интуиция у вас по большей части есть: ресурс — это GET
(загружает данные и ничего не меняет), а инструмент — это POST (делает работу и может
иметь побочные эффекты). У промпта нет аналога в HTTP; он ближе к сохранённому запросу,
который пользователь запускает по имени.
Один сервер, все три
from mcp.server import MCPServer
mcp = MCPServer("Demo")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
@mcp.prompt()
def summarize(text: str) -> str:
"""Summarize a piece of text in one sentence."""
return f"Summarize the following text in one sentence:\n\n{text}"
Три обычные функции, три декоратора. Каждый декоратор — это и есть вся регистрация:
@mcp.tool()делаетaddинструментом.@mcp.resource("greeting://{name}")делаетgreetingшаблоном ресурса:{name}в URI — это параметр функции.@mcp.prompt()делаетsummarizeпромптом. Строка, которую она возвращает, становится сообщением пользователя.
Всё остальное (имя, описание, схему аргументов) SDK считывает из самой функции: её имени, строки документации, аннотаций типов. Ничего из этого вы отдельно не объявляли.
Tip
У двух половин SDK два пути импорта: from mcp import Client и
from mcp.server import MCPServer. Варианта from mcp import MCPServer нет.
Попробуйте сами
Запустите сервер в MCP Inspector:
uv run mcp dev server.py
Откройте URL, который он напечатает. В Inspector по одной вкладке на каждый примитив; пройдитесь по ним по порядку.
Tools. Одна запись: add с описанием Add two numbers. В форме обязательное целочисленное поле для a и ещё одно для b. Заполните их, вызовите инструмент — результат 3. Inspector построил эту форму по a: int, b: int. Так же поступает любой другой клиент.
Resources. Список Resources пуст. greeting находится в разделе Resource Templates, потому что в greeting://{name} есть параметр: пока кто-нибудь не укажет name, конкретного ресурса для списка нет. Передайте World и прочитайте его:
Hello, World!
Prompts. Одна запись: summarize с единственным обязательным аргументом text. Получите его с каким-нибудь текстом — придёт одно сообщение с role: user и вашей готовой строкой в качестве содержимого. Вот и всё, что такое промпт: функция, которая собирает сообщения.
Inspector запустил ваш сервер через stdio — один из транспортов, на которых может говорить MCP-сервер. Выбирать транспорт пока не нужно; этому посвящена страница Запуск сервера.
Возможности
В Inspector вы видели три вкладки. Откуда он узнал, что их три?
Когда клиент подключается, сервер объявляет свои возможности: на какие семейства запросов он будет отвечать. По этому объявлению клиент решает, о чём вообще имеет смысл спрашивать. Вы его не писали; MCPServer объявляет его за вас.
Посмотрите сами. Класс Client из SDK принимает объект сервера напрямую и подключается к нему в памяти (без подпроцесса, без порта):
import asyncio
from mcp import Client
from server import mcp
async def main() -> None:
async with Client(mcp) as client:
print(client.server_capabilities.model_dump(exclude_none=True))
asyncio.run(main())
{'prompts': {'list_changed': True}, 'resources': {'subscribe': True, 'list_changed': True}, 'tools': {'list_changed': True}}
Этот словарь — объявленные возможности вашего сервера. Это первое, что узнаёт каждый подключающийся клиент:
| Возможность | Теперь клиент может вызывать |
|---|---|
tools |
tools/list, tools/call |
resources |
resources/list, resources/templates/list, resources/read |
prompts |
prompts/list, prompts/get |
MCPServer обслуживает все три примитива, поэтому все три возможности объявлены всегда.
Обратите внимание на то, чего здесь нет. completions (автодополнение аргументов для шаблонов ресурсов и промптов) требует обработчика, который пишете вы; у этого сервера его нет, поэтому возможность отсутствует, и корректный клиент о ней не спросит. Таково правило для всего необязательного: зарегистрируйте нужное — и возможность появится; страница Автодополнение это демонстрирует.
Info
Client(mcp) — тот самый клиент в памяти, которым тестируется каждый пример в этой
документации, и именно так вы будете тестировать свои. Ему отведена целая страница:
Тестирование.
Чего вы не писали
Оглянитесь на эту страницу. Вы написали три маленькие функции на Python. Вы не писали:
- JSON Schema.
a: int, b: int— это и есть схема дляadd. - Обработчик запросов.
tools/list,resources/read,prompts/get— всё это обслуживается за вас. - Объявление возможностей.
MCPServerсоставил его за вас. - Ни строчки протокола. Согласование версии, обрамление JSON-RPC, обмен возможностями — всё это произошло внутри
mcp devиClient(mcp), и вы этого не видели.
В этом соотношении — весь смысл SDK.
Итоги
- Хост — это LLM-приложение, клиент — его половина, говорящая на MCP, сервер — то, что строите вы.
- Инструментами управляет модель, ресурсами — приложение, промптами — пользователь.
- По одному декоратору на примитив:
@mcp.tool(),@mcp.resource(uri),@mcp.prompt(). Имя, описание и схема берутся из функции. - URI с
{param}создаёт шаблон ресурса, который отображается отдельно от конкретных ресурсов. - Возможности сервера объявляются за вас, а клиент спрашивает только о том, что сервер объявил.
Client(mcp)подключается к объекту сервера в памяти: ваш тестовый стенд с первого дня.
Дальше — Подключение к настоящему хосту: этот же сервер внутри Claude Desktop или IDE, по-настоящему. Затем Тестирование: одна страница, один клиент в памяти — и больше не придётся гадать, работает ли оно. После этого каждому примитиву отведена своя страница, начиная с того, которым управляет модель: Инструменты.