Агентный бедлам: почему без MCP вы тонете в инструментальном хаосе
Давайте честно: кто из вас не видел этого? Вы пишете третьего по счёту агента для внутренних нужд. У каждого свой способ дёргать API, своя обёртка для вызова функций, свой встроенный curl. Один агент стучится в Slack через готовый SDK, второй — напрямую к PostgreSQL через psycopg2, третий колотит в Redis по сырому TCP. А когда эти агенты начинают общаться друг с другом, начинается ад.
В итоге — дублирование кода, раздутый контекст, и каждое изменение внешнего сервиса требует правки десятка мест. Это не архитектура, это свалка инструментов. И вот тут появляется MCP (Model Context Protocol). Не панацея, но очень жирная заплатка.
MCP как санитар кода: что это и как работает
MCP — это протокол, который стандартизирует то, как LLM-агенты общаются с инструментами. Вместо того чтобы каждый раз писать свой велосипед, вы определяете сервер, который предоставляет список инструментов и их схемы вызова. Агент просто говорит: «эй, мне нужно отправить сообщение в Slack», а MCP сервер делает всю грязную работу.
Ключевые понятия:
- Transport: как агент и сервер общаются — через stdio (подпроцесс) или HTTP (сетевой вызов).
- Tools: список доступных функций с JSON-схемами входных параметров.
- Resources: статические данные, которые сервер может отдавать агенту (например, документация).
- Prompts: готовые шаблоны запросов, которые помогают LLM правильно использовать инструменты.
На декабрь 2025 MCP уже вошёл в продакшн у десятков компаний. Arcade.dev и LangSmith Fleet предлагают единый gateway к 7500+ инструментам — это серьёзно упрощает жизнь.
LangGraph: дирижёр оркестра, а не просто граф
LangGraph — это фреймворк для построения stateful графов состояний. Вы описываете узлы (nodes) — действия агента, и рёбра (edges) — переходы между ними. Но в отличие от простого pipeline, LangGraph позволяет реализовать циклы, параллельные ветки и, что для нас критично, Human-in-the-loop.
Если вы ещё не развернули LangGraph Server — загляните в построение графовой инфраструктуры LLM с LangGraph Server и LangSmith. Там подробно описано, как поднять сервер для локальных моделей.
А если хотите упаковать агента в Docker — LangGraph Deploy CLI развернёт продакшн одной командой.
Стыковка: как подружить MCP и LangGraph
Теперь к сути. Допустим, у нас есть MCP сервер, который предоставляет инструменты для работы с Jira и Slack. Мы хотим, чтобы LangGraph агент сам решал, какой инструмент вызвать. Для этого нам нужно:
- Поднять MCP сервер (stdio или HTTP).
- В LangGraph узле инициализировать клиент MCP.
- Получить список инструментов, передать их LLM.
- При вызове инструмента — выполнить запрос к MCP серверу.
- Результат вернуть в граф.
1 Пишем MCP сервер (stdio)
Самый простой способ — стартовать с stdio. Сервер запускается как подпроцесс, общается через stdin/stdout. Это удобно для локальной разработки.
# mcp_server.py
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationOptions
import mcp.server.stdio
from mcp.types import Tool, TextContent
async def serve() -> None:
server = Server("demo-tools")
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="say_hello",
description="Поздороваться с пользователем",
inputSchema={
"type": "object",
"properties": {
"name": {"type": "string"}
},
"required": ["name"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "say_hello":
return [TextContent(type="text", text=f"Привет, {arguments['name']}!")]
raise ValueError(f"Unknown tool: {name}")
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="demo-tools",
server_version="0.1.0"
)
)
if __name__ == "__main__":
import asyncio
asyncio.run(serve())
2 А как же HTTP?
Stdio — круто, пока у вас один агент на одной машине. Как только появляется распределённая система или несколько реплик, вам нужен HTTP. MCP для HTTP использует SSE (Server-Sent Events). Сервер слушает порт, агент подключается.
Когда выбирать stdio: разработка, однопоточные агенты, изоляция. Когда HTTP: продакшн, микросервисы, единый gateway для множества агентов.
Пример HTTP сервера с использованием mcp.server.fastmcp:
# http_server.py
from mcp.server.fastmcp import FastMCP
import uvicorn
mcp = FastMCP("demo-http")
@mcp.tool()
def say_hello(name: str) -> str:
return f"Здравствуй, {name}!"
if __name__ == "__main__":
mcp.run(transport="http", port=8000)
Запускаем: python http_server.py. Теперь любой агент может стучаться через HTTP.
3 Интеграция с LangGraph
Создадим граф, который использует MCP-инструменты. Для этого нам нужен клиент MCP внутри узла.
# langgraph_mcp_agent.py
import asyncio
from langgraph.graph import StateGraph, END
from typing import TypedDict, Any
from mcp.client.stdio import stdio_client
from mcp.client import ClientSession
class AgentState(TypedDict):
messages: list
next_node: str
def create_agent() -> StateGraph:
workflow = StateGraph(AgentState)
async def call_mcp(state: AgentState) -> dict:
# Подключаемся к MCP серверу через stdio
server_params = {
"command": "python",
"args": ["mcp_server.py"],
"env": {}
}
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
# Прокидываем инструменты LLM — тут вызов нужной модели
# ... пропускаем выбор инструмента, считается что LLM вернула tool_call
result = await session.call_tool("say_hello", {"name": "DevOps"})
state["messages"].append({"role": "tool", "content": result.content[0].text})
return state
workflow.add_node("mcp_node", call_mcp)
workflow.set_entry_point("mcp_node")
workflow.add_edge("mcp_node", END)
return workflow.compile()
if __name__ == "__main__":
graph = create_agent()
initial = {"messages": [], "next_node": ""}
result = asyncio.run(graph.ainvoke(initial))
print(result["messages"])
Это упрощённый пример. В реальности вам нужно будет передавать LLM список инструментов, обрабатывать её выбор и выполнять вызов. Но принцип именно такой: MCP даёт единый интерфейс, а LangGraph — оркестрацию.
Human-in-the-loop: когда агент просит разрешения
Самая страшная вещь в агентах — автономные действия с непредсказуемыми последствиями. MCP + LangGraph позволяют легко вставить точку принятия решения человеком. В LangGraph это называется interrupt.
Представьте: агент хочет отправить сообщение в Slack. Мы помещаем узел, который перед вызовом MCP инструмента прерывает выполнение и ждёт подтверждения от человека. После подтверждения — продолжает.
from langgraph.checkpoint import MemorySaver
workflow = StateGraph(AgentState)
# ... добавляем узлы
# Перед вызовом инструмента ставим interrupt
@workflow.node("ask_human")
def ask_human(state: AgentState) -> dict:
print("Агент хочет выполнить действие. Подтвердите (y/n):")
answer = input() # в продакшене — через вебхук или Telegram
if answer.lower() != "y":
state["messages"].append({"role": "system", "content": "Действие отменено пользователем"})
return {"next_node": "end"}
return {"next_node": "mcp_call"}
workflow.add_conditional_edges("ask_human", lambda s: s["next_node"])
memory = MemorySaver()
graph = workflow.compile(checkpointer=memory)
Важно: Не используйте input() в продакшене. Реальный Human-in-the-loop требует асинхронного ожидания через каналы связи. Хорошая практика — ставить задачу в очередь и ждать ответа из внешнего event-брокера.
Как НЕ надо: типичные грабли
- Не кладите всё в один MCP сервер. Разделяйте инструменты по доменам: отдельный сервер для базы данных, отдельный для Slack. Иначе конфигурация превращается в монстра.
- Не забывайте про таймауты. LLM может долго думать, а MCP сервер — ждать. Ставьте hard timeout на вызов инструмента, иначе граф зависнет.
- Не игнорируйте ошибки MCP. Если сервер упал, агент должен либо повторить, либо уведомить человека. В LangGraph для этого есть
add_nodeс fallback. - Не используйте stdio в Kubernetes. Контейнер с MCP сервером должен жить в том же pod'е, иначе stdio не пробросить. В K8s лучше HTTP.
Пример из реального кейса: мы строили агента для код-ревью на LangGraph. Изначально всё работало, но после масштабирования до 50+ инструментов начался хаос. Полный разбор ошибок читайте в отдельной статье — там много болевых точек, которые стоит знать заранее.
Бонус: самоподдерживающаяся база знаний через MCP
Интересный паттерн — использовать MCP сервер как прослойку для работы с графовой базой знаний. Агент через MCP может добавлять или искать информацию, а результаты сразу же становятся доступны другим агентам. Мы реализовали такой MCP сервер с графовым хранилищем — он же может работать как единый источник правды для LangGraph.
Вместо заключения: прогноз
К концу 2026 года MCP станет настолько же стандартным, как REST в микросервисах. LangGraph уже сейчас — лучший способ строить production-агенты. Их комбинация даёт то, чего так не хватало индустрии: порядок. Если ваш проект ещё не на MCP — вы отстаёте, но исправить легко. Начните с одного сервера, подцепите к одному графу, и вы увидите, как хаос уходит.
И помните: лучший способ предсказать будущее — сделать его стандартизированным. MCP — ваш инструмент для этого.