Skip to main content
Glama

MCP Ассистент задач

Обучающий проект, работающий как один сервис Docker Compose: отправляет сообщения пользователя LLM в Groq, позволяя модели управлять списком задач в памяти (in-memory) с помощью пяти MCP-инструментов (list_tasks, list_tasks_by_priority, create_task, update_task, delete_task). Каждая задача имеет поле priority (срочность: низкий/средний/высокий).

Что он делает?

Вы отправляете сообщение на естественном языке на эндпоинт POST /chat (например, «Отметь задачу Docker как выполненную»). Чат-сервер передаёт это сообщение в Groq вместе со схемами 5 доступных MCP-инструментов. При необходимости модель может вызывать инструменты последовательно (например, сначала list_tasks, чтобы найти id); каждый вызов проверяется на соответствие JSON Schema, выполняется через реальный MCP-сервер, а результат снова показывается модели. В итоге возвращается ответ на естественном языке и видимый след (trace) всего процесса.

Related MCP server: MCP Project Manager

Архитектура

Два отдельных процесса Node.js, в одном контейнере, общаются через stdio по JSON-RPC:

[app sureci]                          [mcp-server sureci]
Express (/chat)                       (child process, stdio ile baslatiliyor)
  |- groq/           --HTTP-->  Groq API
  `- mcp-client/     --stdio/JSON-RPC-->  mcp-server/  -->  task-store/

Файл

Ответственность

src/task-store

CRUD задач, хранилище в памяти на основе Map, начальные данные

src/mcp-server

Превращает task-store в 5 MCP-инструментов с JSON Schema, слушает stdio+JSON-RPC

src/mcp-client

Запускает mcp-server как дочерний процесс, держит единственное (singleton) соединение

src/groq

Отправляет запросы в Groq, преобразование MCP-схем в формат инструментов Groq

src/app

Эндпоинт /chat, цикл вызова инструментов, проверка через ajv, генерация trace

Установка и запуск

1) Получите API-ключ Groq

  1. Перейдите на https://console.groq.com/keys и войдите.

  2. Нажмите «Create API Key», чтобы создать новый ключ, назовите его как угодно (например, mcp-gorev-asistani).

  3. Скопируйте показанный ключ (gsk_...) — он больше не будет показан.

2) Создайте файл .env

cp .env.example .env

Откройте файл .env и вставьте свой ключ в конец строки GROQ_API_KEY=.

Примечание: значение GROQ_MODEL со временем может меняться — Groq периодически удаляет и добавляет модели. Чтобы увидеть актуальный список: curl -s https://api.groq.com/openai/v1/models -H "Authorization: Bearer $GROQ_API_KEY"

3) Запуск через Docker Compose

docker compose up --build -d

Для просмотра логов:

docker compose logs -f

Когда увидите строку Chat sunucusu http://localhost:3000 adresinde calisiyor. — всё готово (порт 3000 внутри контейнера, наружу открыт через compose.yaml как 3001 — если порт 3000 на вашей машине занят, можно изменить строку ports в compose.yaml).

Для остановки:

docker compose down

Тестовые сообщения

curl -X POST http://localhost:3001/chat -H "Content-Type: application/json" \
  -d '{"message": "Hangi görevlerim var?"}'

curl -X POST http://localhost:3001/chat -H "Content-Type: application/json" \
  -d '{"message": "JSON Schema öğrenmek için bir görev ekle."}'

curl -X POST http://localhost:3001/chat -H "Content-Type: application/json" \
  -d '{"message": "Docker görevini tamamlandı olarak işaretle."}'

curl -X POST http://localhost:3001/chat -H "Content-Type: application/json" \
  -d '{"message": "Tamamlanan görevi sil."}'

Пример ответа (3-е сообщение — обратите внимание, что id сначала находится через list_tasks, а затем передаётся в update_task):

{
  "answer": "\"Docker Compose kur\" görevi tamamlandı olarak işaretlendi.",
  "trace": [
    { "tool": "list_tasks", "arguments": {}, "validation": "passed",
      "result": { "tasks": [ { "id": 1, "title": "MCP sartnamesini oku", "completed": false },
        { "id": 2, "title": "Docker Compose kur", "completed": false },
        { "id": 3, "title": "Groq API anahtarini al", "completed": true } ] } },
    { "tool": "update_task", "arguments": { "completed": true, "id": 2 }, "validation": "passed",
      "result": { "id": 2, "title": "Docker Compose kur", "completed": true } }
  ]
}

4-е сообщение («Удали выполненную задачу.») во время тестирования показало интересное поведение: поскольку в начальных данных уже была выполненная задача (id=3), а после 5-го сообщения появилась ещё одна выполненная задача (id=2), модель оказалась перед выбором из двух вариантов и вместо того, чтобы угадывать, спросила пользователя, какую именно он имеет в виду — не вызвав ни одного инструмента. Это ожидаемое/желаемое поведение проекта (не удалить не ту задачу), а не ошибка.

Часто задаваемые вопросы

Почему добавление нового инструмента (например, list_tasks_by_priority) потребовало изменения только 2 файлов? Потому что слои app, mcp-client и groq вообще не хардкодят инструменты — app при каждом запросе спрашивает mcp-server через listMcpTools() «что у тебя есть» и передаёт полученный список в Groq как есть. То есть для определения нового инструмента достаточно добавить (1) логику в task-store и (2) схему в mcp-server — всё остальное подхватывается автоматически. Это конкретное воплощение решения «разделить ответственности» из Шага 1.

Почему нет базы данных, а используются данные в памяти? Техническое задание сознательно этого требует: проект нацелен на обучение протоколу MCP и потоку вызова инструментов, а постоянное хранение (persistence) — отдельная тема, которая добавила бы лишнюю сложность. Map + начальные данные дают поведение «начинай с чистого состояния при каждом запуске» бесплатно.

Почему Docker Compose, разве одной команды node было бы недостаточно? Docker устраняет проблему «у меня работало» и гарантирует, что проект работает одинаково на любой машине. Compose же делает сервисы (даже если здесь сервис один) стандартными и запускаемыми одной командой — это упражнение, приближенное к реальным продакшн-установкам.

Зачем нужна проверка JSON Schema, разве нельзя было довериться Groq? Вывод LLM недетерминирован — модель иногда может генерировать аргументы с пропусками или неверными типами. Если идти напрямую в task-store без проверки через ajv, это может привести к неожиданным ошибкам или несогласованным данным. Проверка — это кодовая реализация принципа «не доверяй LLM, проверяй».

Почему определения инструментов помещаются в поле tools, а не в системное сообщение? Поле tools — это структурированный контракт в API Groq/OpenAI: модель воспринимает их как реальные, вызываемые функции и генерирует ответ в структурированном формате tool_calls. Если бы мы написали их простым текстом в системном сообщении, модель читала бы их лишь как контекст, без гарантии и структуры вызова.

Почему mcp-client не перезапускает mcp-server при каждом запросе? task-store живёт в памяти процесса mcp-server. Если бы при каждом запросе запускался новый процесс, данные каждый раз сбрасывались бы к начальным — изменения, сделанные в предыдущем сообщении, терялись бы. Поэтому mcp-client поддерживает ЕДИНСТВЕННОЕ соединение с mcp-server (singleton), пока жив процесс app.

Почему одного вызова Groq недостаточно, зачем нужен цикл (loop)? Когда пользователь говорит «Отметь задачу Docker», модель не знает её id:

  • сначала нужно вызвать list_tasks, чтобы найти правильный id, а затем вызвать инструмент, выполняющий основное действие, с этим id. Это означает несколько последовательных вызовов инструментов в рамках одного запроса; фиксированный поток «спроси-выполни-расскажи» этого не поддерживает, нужен настоящий цикл.

Известные недостатки / моменты, не готовые к продакшену

  • Нет постоянства: Если контейнер перезапускается (или падает / передеплоивается), все данные задач теряются. Для реального использования нужна база данных (Postgres, SQLite и т.п.).

  • Нет разделения пользователей/сессий: Все пользователи используют один и тот же task-store; изоляции между пользователями (multi-tenancy) нет.

  • Нет памяти разговора: Каждый запрос /chat начинается независимо. Пользователь не может ссылаться на предыдущие сообщения («удали и её тоже») — контекст сохраняется только в пределах цикла инструментов одного запроса.

  • Только один одновременный вызов инструмента: Даже если модель запрашивает несколько инструментов в одном ходе (параллельные tool_calls), обрабатывается только первый.

  • Нет аутентификации/авторизации: Эндпоинт /chat открыт для всех, никакого контроля доступа нет.

  • Нет ограничения размера ввода / лимита скорости (rate limiting): Злонамеренные или ошибочные клиенты могут отправлять неограниченное количество запросов, и счёт Groq может соответственно вырасти.

  • Схема Ajv перекомпилируется при каждом запросе: ajv.compile(...) можно было бы кэшировать для производительности (в малых масштабах это незаметно).

  • Название модели может устареть со временем: Каталог моделей Groq меняется (в ходе этого проекта llama-3.3-70b-versatile была удалена) — GROQ_MODEL следует периодически проверять.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    A simple, powerful Todo list manager for Claude Desktop and other MCP-compatible AI assistants. Organize your tasks across different projects with priorities and never lose track of what needs to be done!
    15 npm
    2
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    A task manager MCP server that demonstrates all three MCP primitives (tools, resources, prompts). Enables users to manage tasks, read task summaries and details, and run structured planning/review prompts through natural language.
    -