Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Coffee

FreeNot checked

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

GitHubEmbed

About

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

README

CI

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 запуск собранного сервера
npm test юнит-тесты (встроенный node:test)

Структура

src/
├── index.ts          точка входа: MCP-сервер (инструменты + HTTP-транспорт)
├── bot.ts            точка входа: Telegram + OpenAI + ручная петля tool use
├── config.ts         настройки в одном месте (модель, порт, лимиты)
├── domain/           ЧИСТАЯ логика без I/O (тестируется без запуска сервера)
│   ├── search.ts       поиск по составу
│   ├── orders.ts       расчёт заказа: проверка меню, время
│   └── history.ts      обрезка окна контекста
├── infra/            адаптеры к внешнему миру
│   ├── excel.ts        слой хранения (ExcelJS)
│   └── mcpClient.ts    MCP-клиент (с авто-переподключением)
└── tests/            юнит-тесты к чистой логике
data/
└── menu.xlsx         меню (напиток | цена | время_приготовления_мин | состав)
                      orders.xlsx создаётся при первом заказе

Архитектура: точки входа наверху, чистое ядро (domain/) отделено от адаптеров к внешнему миру (infra/). Благодаря этому логику можно тестировать, не запуская сервер и бота.

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

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

  • Ручная петля 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. Один разговор клиента с сервером = одна сессия со своим транспортом; так несколько клиентов не мешают друг другу.

  • Автопереподключение к MCP. Сессии живут в памяти сервера: после его рестарта mcp-session-id бота протухает, и сервер отвечает 400. Чтобы бот не падал на каждом сообщении до перезапуска, McpClient при ошибке запроса один раз переподключается (новая сессия) и повторяет вызов.

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

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

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

from github.com/Ira-Ivaa/mcp-coffee

Installing Coffee

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/Ira-Ivaa/mcp-coffee

FAQ

Is Coffee MCP free?

Yes, Coffee MCP is free — one-click install via Unyly at no cost.

Does Coffee need an API key?

No, Coffee runs without API keys or environment variables.

Is Coffee hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Coffee in Claude Desktop, Claude Code or Cursor?

Open Coffee on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Coffee with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All development MCPs