mcp-coffee

mcp-coffee

Enables coffee shop order management via MCP, including menu lookup, order creation, and status tracking.

Category
访问服务器

README

☕ MCP Coffee — учебный проект по Model Context Protocol

Telegram-бот кофейни, который принимает заказы в свободной форме («два капучино и латте»), записывает их в Excel и сообщает время готовности. Главная цель проекта — разобраться, как устроен MCP изнутри: и сервер, и клиент написаны вручную, tool use реализован ручной петлёй поверх OpenAI, без готовых агентских врапперов.

Что демонстрирует проект

  • MCP-сервер на TypeScript — три инструмента, транспорт Streamable HTTP, сессии по заголовку mcp-session-id.
  • MCP-клиент — подключение, tools/list, tools/call.
  • Ручная петля tool use — модель сама извлекает структуру заказа из текста; мы перехватываем вызовы инструментов, исполняем их через MCP и возвращаем результат в диалог, пока модель не ответит текстом.
  • Разделение слоёв — хранение (Excel) отделено от бизнес-логики (инструменты) и от «мозга» (бот).

Архитектура

Система состоит из трёх независимых частей:

Часть Файл Ответственность
MCP-сервер src/index.ts 3 инструмента + Express/Streamable HTTP на :3000/mcp. Единственный, кто трогает Excel.
Слой хранения src/excel.ts Чтение menu.xlsx, запись/чтение orders.xlsx. Не знает бизнес-правил.
MCP-клиент src/mcpClient.ts connect / listTools / callTool.
Бот src/bot.ts Telegram + OpenAI + ручная петля tool use.

Ключевой принцип: сервер не знает про OpenAI, а OpenAI не знает про Excel. Бот посередине — переводчик между ними.

Поток данных

Пользователь в Telegram: "два капучино"
        │
        ▼
   [bot.ts] получает текст
        │
        ▼
   [bot.ts] → OpenAI: сообщение + список инструментов (function calling)
        │
        ▼
   OpenAI решает вызвать create_order({items:[{drink:"капучино",qty:2}]})
        │
        ▼
   [bot.ts] перехватывает вызов          ← РУЧНАЯ ПЕТЛЯ
        │
        ▼
   [mcpClient] → [index.ts] tools/call create_order(...)
        │
        ▼
   [index.ts] читает menu.xlsx (время), пишет строку в orders.xlsx
        │
        ▼
   результат {id, totalMinutes} → обратно в OpenAI
        │
        ▼
   OpenAI формулирует ответ: "Заказ №7 принят, готовность ~8 мин"
        │
        ▼
   [bot.ts] → Telegram

Инструменты MCP

Инструмент Аргументы Возвращает
get_menu — список напитков {drink, price, minutes, composition}
search_menu query напитки, у которых ключевое слово есть в составе/названии
create_order items: [{drink, qty}] {id, totalMinutes}
get_order_status orderId {status, minutesLeft, items}

Описания инструментов и схемы аргументов — на английском: это контракт для модели, по которому она выбирает инструмент и заполняет аргументы.

Технологии

TypeScript · @modelcontextprotocol/sdk · Express · OpenAI SDK · Telegraf · ExcelJS · zod · Node.js 24

Запуск

npm install
cp .env.example .env   # впиши OPENAI_API_KEY и TELEGRAM_BOT_TOKEN

.env:

OPENAI_API_KEY=sk-...
TELEGRAM_BOT_TOKEN=123456:ABC-...
OPENAI_MODEL=gpt-4o-mini
MCP_URL=http://localhost:3000/mcp

Два терминала:

npm run dev       # 1-й: MCP-сервер на :3000
npm run dev:bot   # 2-й: Telegram-бот

Дальше пиши боту: «два капучино и латте» → получишь номер и время; «когда готов заказ 3?» → остаток минут до готовности.

Команды

Команда Что делает
npm run dev MCP-сервер (tsx watch)
npm run dev:bot Telegram-бот (tsx watch)
npm run build сборка в dist/
npm start запуск собранного сервера

Структура

src/
├── index.ts      MCP-сервер: инструменты + HTTP-транспорт
├── excel.ts      слой хранения (ExcelJS)
├── mcpClient.ts  MCP-клиент
└── bot.ts        Telegram + OpenAI + ручная петля tool use
data/
└── menu.xlsx     меню (напиток | цена | время_приготовления_мин | состав)
                  orders.xlsx создаётся при первом заказе

Принятые решения и почему

Этот раздел — самое важное в проекте: он показывает понимание, а не только код.

  • Ручная петля tool use вместо готового агента. Цель — увидеть механику: как модель возвращает tool_calls, как результат с ролью tool и tool_call_id привязывается к конкретному вызову, как цикл повторяется. Это понимание дороже, чем «подключил библиотеку».

  • Поиск по составу — это retrieval, а не исправление опечаток. Нормализацию текста («КАПУЧино», «капуччино») делает сама LLM — дублировать её на сервере бессмысленно. А вот состав напитков лежит в Excel, и в контексте модели его нет: по названию «Розовая волна» не угадать, что там арбузный сироп. Поэтому search_menu оправдан: LLM извлекает ключевое слово («арбуз»), а сервер находит напитки по данным, которых у модели нет. Это тот же паттерн, что и поиск по большому каталогу, который не помещается в окно контекста.

  • Расчёт времени — в инструменте, не в слое хранения. Чтобы посчитать общее время, нужно сопоставить заказ с меню — это та же операция, что и валидация «существует ли напиток». Логично держать в одном месте. excel.ts остаётся тупым хранилищем: поменяем формулу — слой хранения не трогаем.

  • Общее время = сумма время × количество. Сознательное упрощение для модели «один бариста по очереди». Легко заменить на максимум (параллельная готовка) — правка в одном месте, в обработчике create_order.

  • Описания инструментов на английском, общение — на русском. Описания и .describe() — контракт для модели (выбор инструмента, заполнение аргументов): на английском надёжнее для function calling и экономнее по токенам. Тексты ошибок — на русском: они возвращаются модели как данные, и она пересказывает их пользователю.

  • Язык ответа задан в системном промпте. gpt-4o-mini иногда вставляет иноязычные слова. Явное требование «отвечай только по-русски» в системном промпте убирает большинство таких сбоев; при необходимости можно дополнительно понизить temperature.

  • Сессии MCP по mcp-session-id. Один разговор клиента с сервером = одна сессия со своим транспортом; так несколько клиентов не мешают друг другу.

  • Управление окном контекста — обрезка по границам ходов. История растёт с каждым сообщением и упёрлась бы в лимит токенов. Обрезаем до MAX_TURNS последних ходов, но не как попало: у tool-use есть инвариант — assistant с tool_calls обязан сопровождаться tool-ответами с теми же tool_call_id. Поэтому окно всегда начинается с сообщения user и системное сообщение сохраняется — иначе «осиротевший» tool дал бы ошибку 400.

Известные ограничения

  • История диалога хранится в памяти процесса — перезапуск бота её обнуляет.
  • Обрезка контекста — по числу ходов (MAX_TURNS), а не по токенам. Точнее было бы считать токены (tiktoken), но для учебного проекта порога по ходам достаточно.
  • Нет автотестов и CI.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选