LangGraph MCP as Node

Паттерн из практики C6: MCP-сервер подключается как отдельный узел графа, а не как инструмent, который LLM вызывает сама. Вызов решается программно (условным ребром), виден на диаграмме и не тратит токены на «решение модели». Типичное применение — fallback-цепочка RAG→MCP.

Суть

Обычно MCP-инструмент дёргает сама LLM через tool calling (Tool Calling). Здесь иначе: mcp_node — узел графа, в который ведёт условное ребро. Решение «звать MCP» принимает код (по состоянию), а не модель.

Зачем это нужно

  • Детерминизм и контроль: переход в MCP определяется явным критерием в state, а не «настроением» модели.
  • Экономия токенов: модель не тратит вызов на то, чтобы решить, нужен ли инструмент.
  • Наблюдаемость: MCP-вызов виден как узел на диаграмме графа — это часть бизнес-процесса, а не скрытая деталь внутри RAG-узла.

Как работает

  • Fallback-цепочка: rag_node делает retrieval и оценивает, нашёл ли ответ. Учебный критерий — overlap токенов вопроса и лучшего документа: если overlap < MIN_OVERLAP, ставит needs_mcp=True.
  • Условный переход: rag → final (нашли) или rag → mcp_node → final (не нашли). В проде вместо overlap — score/threshold, reranker, self-check/LLM-judge (Agentic RAG).
  • mcp_node: делает HTTP-запрос к MCP-серверу и кладёт ответ (mcp_answer) в state.
  • Это и есть «MCP как узел» в отличие от «MCP как tool»: оба валидны, выбор зависит от того, нужен ли программный контроль над вызовом.

Пример

rag_node ─(needs_mcp == False)→ final
         └(needs_mcp == True) → mcp_node → final     # внешний сервер как узел графа

Связано с

  • MCP — здесь MCP подаётся как узел графа, а не как LLM-вызываемый инструмент
  • Tool Calling — альтернатива: MCP как tool, который выбирает сама модель
  • Agentic RAG — fallback «не нашли в базе → внешний источник» = шаг к agentic-retrieval
  • LangGraph Intent Router — MCP-узел = второй уровень маршрутизации из RAG-ветки

Открытые вопросы

  • когда MCP лучше как узел (контроль), а когда как tool (гибкость выбора моделью)
  • продакшн-критерий «нашёл/не нашёл» вместо учебного MIN_OVERLAP (reranker, LLM-judge)