Каждый новый сервис, который вы хотите дать агенту, до сих пор означал новую обёртку: функция с @tool, запрос к API, обработка ошибок. Для трёх сервисов это терпимо. Но если агенту нужны трекер задач, файлы, календарь и корпоративная вики, а таких агентов в компании пять, одинаковые обёртки начинают писать снова и снова в разных проектах.
MCP (Model Context Protocol) решает эту проблему стандартом. Сервис один раз оформляется как MCP-сервер, и подключить его может любое приложение, которое говорит на этом протоколе: ваш агент на LangChain, редактор кода, настольный ассистент. Многие компании уже публикуют готовые MCP-серверы для своих продуктов. После урока вы сможете написать собственный MCP-сервер на Python, подключить к агенту локальные и удалённые серверы и понимать, каким из них можно доверять.
Что уже умеем
Вы создавали инструменты через @tool и знаете, что модель выбирает их по имени, описанию и аргументам. В прошлом уроке вы подключали к главному агенту целых агентов как инструменты. Сегодня инструменты придут к агенту из отдельной программы — MCP-сервера, а LangChain сам превратит их в привычные инструменты.
Что вы узнаете в рамках статьи
- что такое MCP и из каких ролей он состоит;
- как написать MCP-сервер на Python с несколькими инструментами;
- чем отличаются транспорты stdio и Streamable HTTP;
- как получить инструменты сервера через
MCPAdapterи отдать их агенту; - почему агента с MCP-инструментами запускают асинхронно и как выбирать надёжные серверы.
Ключевые термины
- MCP (Model Context Protocol) — открытый протокол, по которому AI-приложения получают инструменты и данные от внешних программ.
- MCP-сервер — программа, которая объявляет инструменты и выполняет их по запросу.
- MCP-клиент — часть приложения, которая подключается к серверу, получает список инструментов и вызывает их.
- Транспорт — способ связи клиента и сервера: stdio для локального процесса или Streamable HTTP для сервера в сети.
- Асинхронный код — функции с
async def, которые вызываются черезawaitи не блокируют программу во время ожидания ответа.
Как устроен MCP
В MCP две стороны. Сервер знает, как работать с конкретным сервисом, и публикует список своих инструментов: имя, описание, схему аргументов. Клиент подключается к серверу, запрашивает этот список и по запросу вызывает инструменты. Агент при этом ничего не знает о внутреннем устройстве сервиса: для него это те же инструменты, что и созданные через @tool.
Главная выгода — повторное использование. Без стандарта пять приложений и четыре сервиса требуют до двадцати отдельных интеграций. С MCP каждый сервис оформляется сервером один раз, а каждое приложение один раз учится быть клиентом. Кроме инструментов MCP-сервер может отдавать ресурсы (данные для чтения) и готовые шаблоны промптов, но для агентов главное — инструменты.
Пишем свой MCP-сервер
Для серверов есть официальный Python SDK: pip install "mcp[cli]". Сервер — это обычный скрипт, в котором функции помечаются декоратором @mcp.tool(). Подсказки типов становятся схемой аргументов, а докстринг — описанием инструмента.
# notes_server.py
from mcp.server import MCPServer
mcp = MCPServer("Notes")
notes: dict[str, str] = {}
@mcp.tool()
def add_note(title: str, text: str) -> str:
"""Сохраняет заметку под заданным заголовком."""
notes[title] = text
return f"Заметка «{title}» сохранена"
@mcp.tool()
def list_notes() -> list[str]:
"""Возвращает заголовки всех сохранённых заметок."""
return list(notes)
if __name__ == "__main__":
mcp.run() # без аргументов используется транспорт stdio
Если запустить скрипт напрямую, в консоли ничего не появится: сервер ждёт сообщений клиента на стандартном вводе. Так и должно быть — запускать его будет клиент. Проверить сервер вручную можно командой mcp dev notes_server.py, которая откроет инспектор с формами для вызова инструментов. Обратите внимание: в старых руководствах класс назывался FastMCP и импортировался из mcp.server.fastmcp. Во второй версии SDK этот модуль удалён, используйте MCPServer.
Транспорты: stdio и Streamable HTTP
Транспорт stdio подходит для локальных серверов. Клиент сам запускает скрипт сервера как дочерний процесс и обменивается с ним сообщениями через стандартный ввод и вывод. Ничего не нужно разворачивать, сервер живёт ровно столько, сколько клиент.
Streamable HTTP нужен, когда сервер работает отдельно: на другой машине, в облаке или как общий сервис для нескольких команд. Сервер запускается заранее, а клиент подключается к нему по адресу.
if __name__ == "__main__":
mcp.run(transport="streamable-http", port=8000)
Что увидим в консоли: сообщение о запуске сервера, после чего он доступен по адресу http://127.0.0.1:8000/mcp. Путь /mcp используется по умолчанию.
Подключаем MCP-серверы к агенту
Клиентская часть встроена в LangChain: pip install "langchain[mcp]". Класс MCPAdapter подключается к серверу и превращает его инструменты в инструменты LangChain. Цель подключения определяет транспорт: объект Path запускает локальный скрипт через stdio, строка с адресом http(s) подключается по Streamable HTTP. Для нескольких серверов передаётся словарь конфигурации.
import asyncio
from langchain.agents import create_agent
from langchain.mcp import MCPAdapter
config = {
"mcpServers": {
"notes": {"command": "python", "args": ["/home/anna/mcp/notes_server.py"]},
"weather": {"url": "<http://127.0.0.1:8000/mcp>"},
}
}
async def main():
async with MCPAdapter(config) as adapter:
tools = await adapter.list_tools()
print([t.name for t in tools])
agent = create_agent(model="openai:gpt-6-luna", tools=tools)
result = await agent.ainvoke({"messages": [{
"role": "user",
"content": "Запиши заметку «Купить» с текстом «молоко, хлеб» и покажи все заметки",
}]})
print(result["messages"][-1].content)
asyncio.run(main())
Что увидим: сначала список вроде ['notes_add_note', 'notes_list_notes', 'weather_get_forecast'] — при нескольких серверах к имени инструмента добавляется ключ сервера, чтобы одинаковые имена не конфликтовали. Затем ответ агента: «Заметка сохранена. Сейчас у вас одна заметка: Купить».
Почему всё асинхронно? Общение с сервером — это ожидание ответа процесса или сети, и MCP-инструменты в LangChain создаются асинхронными. Поэтому код помещают в async def, ждут результатов через await, агента вызывают через ainvoke, а запускают всё через asyncio.run(main()). Блок async with открывает соединение с серверами и закрывает его по выходе. Пространство имён langchain.mcp пока помечено как бета, поэтому при первом импорте вы можете увидеть предупреждение — это нормально.
К MCP-инструментам применимо всё, что вы уже знаете: их можно передать субагенту, а опасные из них поставить под подтверждение через HumanInTheLoopMiddleware.
Частые ошибки
- Относительный путь к скрипту сервера. Клиент запускает процесс из своей рабочей папки, и относительный путь может не найтись. Указывайте полный путь.
- Вызов
invokeвместоainvoke. MCP-инструменты асинхронные; запускайте агента черезawait agent.ainvoke(...)внутриasync def. - Подключение непроверенных серверов. Описания инструментов попадают прямо в промпт модели, а сам сервер выполняет действия от вашего имени. Подключайте серверы только из надёжных источников.
- Подключить всё сразу. Каждый инструмент добавляет своё описание в каждый запрос к модели. Десятки лишних инструментов увеличивают стоимость и путают модель.
Наиболее частые вопросы
- Чем MCP-инструмент отличается от
@tool? — Для агента ничем: после загрузки это обычные инструменты LangChain. Разница в том, что код живёт в отдельном сервере, который можно переиспользовать в разных приложениях. - Можно ли писать сервер не на Python? — Да, протокол не зависит от языка. Агенту на Python всё равно, на чём написан сервер.
- Что такое
MultiServerMCPClientиз старых примеров? — Это клиент из пакетаlangchain-mcp-adapters, который больше не развивается. Его заменилMCPAdapterизlangchain.mcp. - Нужен ли интернет для stdio-сервера? — Нет, сервер работает на вашей машине. Интернет нужен, только если сам сервер обращается к внешнему API.
Итоги
Поздравляем, вы освоили стандартный способ расширять возможности агентов! Теперь вы понимаете, как устроен MCP, умеете написать собственный сервер с инструментами на MCPServer, запустить его через stdio или Streamable HTTP и подключить к агенту один или несколько серверов через MCPAdapter. Вы знаете, зачем здесь асинхронный код и почему к выбору серверов нужно подходить так же внимательно, как к установке любых сторонних программ.
Агент стал мощнее, а значит, тем важнее понимать, что происходит у него внутри. В следующем уроке вы научитесь отслеживать каждый шаг агента и измерять качество его ответов на наборе тестовых вопросов.