No description
  • Go 99.7%
  • Dockerfile 0.3%
Find a file
2026-06-17 12:19:50 +03:00
cmd Доработка недочетов и ошибок 2026-06-05 11:45:21 +03:00
docs Формирование промптов 2026-06-05 12:47:28 +03:00
internal fix: TestLocalHTTP 2026-06-17 12:11:48 +03:00
.dockerignore Добавление версии 2026-06-04 13:23:49 +03:00
.gitignore Рефакторинг 2026-06-05 09:25:35 +03:00
.golangci.yml Уточнение документации 2026-06-04 10:52:01 +03:00
.woodpecker.yml fix: Удаление мусора 2026-06-17 12:19:50 +03:00
docker-compose.yml Добавление версии 2026-06-04 13:23:49 +03:00
Dockerfile Fix CI 2026-06-05 09:51:38 +03:00
go.mod Обновление модулей 2026-06-17 11:58:01 +03:00
go.sum Обновление модулей 2026-06-17 11:58:01 +03:00
LICENSE Добавление версии 2026-06-04 13:23:49 +03:00
README.md Формирование промптов 2026-06-05 12:47:28 +03:00

MCP Semantic Task Planner

MCP-сервер для управления задачами AI-агентов с построением планов по зависимостям (DAG), семантическим (векторным) поиском и привязкой к фрагментам документации.

Два режима работы:

  • local — однопользовательский: работает с ./docs/roadmap.json (stdio или HTTP)
  • shared — многопользовательский multi-tenant: register/unregister сессий, Lazy Load, fsnotify, раздельные Control и Data Plane

Возможности

  • 4 инструмента (search_tasks, get_tasks, upsert_task, delete_task) с полным inputSchema, enum для статусов, required для обязательных полей
  • Планирование через directed acyclic graph (DAG): зависимости задач, проверка циклов, get_tasks(status: "ready") для выбора следующей задачи
  • Три режима просмотра: плоский список (full), дерево зависимостей (tree), цепочка родителей до корня (ancestors)
  • Семантический (векторный) поиск с настраиваемым порогом min_score (через OpenAI-совместимый embedding API) + text fallback
  • compact=true — режим краткого вывода без длинных полей (description, doc_references и т.д.)
  • Пагинация (limit + offset) в get_tasks и search_tasks
  • Состояния задач: pending → in_progress ⇄ on_hold → completed / failed

Быстрый старт (local mode)

Требования

  • Go 1.26+
  • Embedding-сервер (совместимый с OpenAI API /v1/embeddings) — опционально, без него поиск работает по точному совпадению

Запуск

# из исходников (stdio — для подключения к MCP-агенту)
go run ./cmd/mcp-semantic-task-planner

# HTTP вместо stdio
go run ./cmd/mcp-semantic-task-planner --transport http

# с портом 9090
go run ./cmd/mcp-semantic-task-planner --transport http --port 9090

# с embedding-сервером
EMBEDDING_URL=http://localhost:8000 go run ./cmd/mcp-semantic-task-planner

Сборка

# обычная
go build -o mcp-semantic-task-planner ./cmd/mcp-semantic-task-planner

# с версией
go build -ldflags "-X git.ymnuktech.ru/ymnuk/mcp-semantic-task-planner/internal/version.Version=v0.1.0" \
  -o mcp-semantic-task-planner ./cmd/mcp-semantic-task-planner

Подключение к MCP-агентам (Cline, Roo, Claude Desktop)

mcp.json:

{
  "mcpServers": {
    "task-planner": {
      "command": "/path/to/mcp-semantic-task-planner",
      "args": ["--mode", "local", "--transport", "stdio"],
      "env": {
        "EMBEDDING_URL": "http://localhost:8000"
      }
    }
  }
}

Для разработки:

{
  "mcpServers": {
    "task-planner": {
      "command": "go",
      "args": ["run", "./cmd/mcp-semantic-task-planner", "--mode", "local", "--transport", "stdio"]
    }
  }
}

Архитектура

                          ┌──────────────────────────────────┐
                          │         HTTP Router              │
                          │  (http.ServeMux + middleware)    │
                          │                                  │
                          │  /mcp ────────────────────────────│───▶ Control Plane
                          │  /mcp/{session_id} ──────────────│───▶ Data Plane
                          │  GET /health ────────────────────│───▶ JSON health
                          │  GET /metrics ───────────────────│───▶ Prometheus
                          └──────────────────────────────────┘
                                      │
                     ┌────────────────┼──────────────────┐
                     ▼                                    ▼
        ┌─────────────────────────┐       ┌─────────────────────────┐
        │ Control MCP Server      │       │ Session MCP Server      │
        │ (один на весь сервер)   │       │ (свой для каждой        │
        │                         │       │  зарегистрированной     │
        │ • register              │       │  сессии)                │
        │ • unregister            │       │                         │
        │ • list                  │       │ • search_tasks          │
        └─────────────────────────┘       │ • get_tasks             │
                                          │ • upsert_task           │
                                          │ • delete_task           │
                                          └─────────────────────────┘
                                                     │
                                                     ▼
                                          ┌─────────────────────┐
                                          │  session.Session    │
                                          │  • Load / Unload    │
                                          │  • SearchTasks      │
                                          │  • UpsertTask       │
                                          │  • DAG (deps)       │
                                          │  • fsnotify         │
                                          │  • State Machine    │
                                          └─────────────────────┘

Режимы

local shared
Путь к плану ./docs/roadmap.json (жёстко) --path-root + путь из register
БД SQLite (./docs/planner.db) SQLite (--db-path)
Source of Truth roadmap.json (Git-friendly) roadmap.json (Git-friendly)
Lazy Load Нет (всегда Loaded) Да (Loaded ↔ Unloaded)
fsnotify Да Per-session
Сессии Одна, ID="local" Множественные
Мониторинг GET /health, GET /metrics GET /health, GET /metrics
Транспорт stdio (default) или http Только http

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

Инструмент local shared Эндпоинт (shared) Описание
search_tasks да да POST /mcp/{session_id} Поиск задач по тексту (query обязателен). Векторный поиск при min_score > 0 + embedding сервер, иначе text fallback. Результат: {results: [{task, score}]}
get_tasks да да POST /mcp/{session_id} Получение задач. view: full (список), tree (дерево от task_id), ancestors (цепочка родителей от task_id). compact=true — краткий вывод
upsert_task да да POST /mcp/{session_id} Создание/обновление задачи. task_id пустой → авто-генерация. Статусы: pending, in_progress, completed, failed, on_hold
delete_task да да POST /mcp/{session_id} Soft-delete (archived=true, можно восстановить через upsert_task с archived:false)
register да POST /mcp Создать сессию для проекта (path обязателен)
unregister да POST /mcp Удалить сессию, выгрузка из RAM
list да POST /mcp Список активных сессий

Режимы просмотра (get_tasks view)

Значение Поведение task_id
"" / "full" Плоский список с фильтрацией по status, limit, offset не требуется
"tree" Рекурсивное дерево: кто зависит от этой задачи (fan-out) требуется
"ancestors" Сама задача + цепочка родителей до корня в BFS-порядке (fan-in). Полезно понять, почему задача не в ready требуется

Компактный режим (compact: true)

При compact=true поля description, doc_references, prompt_template, result_summary, created_at, updated_at опускаются из результата. Работает для всех значений view: full, tree, ancestors.

// compact: false (по умолчанию)
{"id":"t1","name":"Setup DNS","status":"completed","description":"Configure SPF and DKIM","created_at":"2026-06-05T10:00:00Z",...}

// compact: true
{"id":"t1","name":"Setup DNS","status":"completed"}

Семантический поиск (search_tasks min_score)

При наличии embedding-сервера (EMBEDDING_URL) search_tasks выполняет векторный поиск по косинусной близости:

// поиск с порогом 0.7
{"query":"настроить DNS","min_score":0.7}

// результат с баллами
{"results": [
  {"task":{"id":"t1","name":"Setup DNS",...}, "score":0.85},
  {"task":{"id":"t2","name":"Configure MX",...}, "score":0.72}
]}

Параметры:

  • min_score: 0.0 (или omit) — используется --min-score из конфига (по умолчанию 0.4)
  • min_score: 0.6 — явный порог
  • Если embedding сервер недоступен — автоматический fallback на текстовый поиск (substring match)

Пагинация

limit + offset работают в search_tasks и get_tasks(view: "full"):

{"query":"DNS","limit":10,"offset":20}

State Machine (shared mode)

[Start] → register() → StateLoaded (RAM + fsnotify + roadmap)
                         │
                    (IDLE_TIMEOUT)
                         ▼
                     StateUnloaded (RAM cleared, fsnotify stopped)
                         │
                   (incoming tool call)
                         ▼
                 Lazy Load → read roadmap.json → StateLoaded

Статусы задач

pending ──→ in_progress ──→ completed
    │            ↑                  
    │            │                   
    └─────→ on_hold                 
                                    
                       └─────→ failed
  • pending — задача создана, ожидает выполнения
  • in_progress — выполняется
  • on_hold — отложена (не блокирует dependants для ready, не возвращается в ready через GetReadyTasks)
  • completed — выполнена (разблокирует dependants)
  • failed — не может быть выполнена

Конфигурация

CLI-флаги

Флаг По умолчанию Описание
--mode local local или shared
--transport stdio stdio или http
--port 8085 HTTP порт
--path-root ./docs Корень для roadmap.json сессий (local: ./docs/roadmap.json)
--db-path ./data/cache.db Файл SQLite (для local по умолчанию ./docs/planner.db)
--idle-timeout 30m Таймаут простоя сессии (shared)
--embedding-url http://localhost:8080 URL embedding-сервера
--embedding-model text-embedding-3-small Модель эмбеддингов
--llm-api-key `` API-ключ для embedding-сервера
--min-score 0.4 Порог схожести по умолчанию (0.11.0)

Все флаги также поддерживаются через переменные окружения (например, MODE=shared, EMBEDDING_URL=http://...).

Docker

# Сборка
docker build --build-arg VERSION=v0.1.0 -t mcp-semantic-task-planner .

# Запуск (local mode, HTTP)
docker run -p 8085:8085 \
  -v ./docs:/docs \
  mcp-semantic-task-planner -- --transport http

# Запуск (shared mode)
docker run -p 8085:8085 \
  -v ./projects:/projects \
  mcp-semantic-task-planner -- \
    --mode shared --transport http

Docker Compose

# Запуск с embedding-сервером
docker compose up

Тесты

# unit + integration
go test -race ./...

# с покрытием
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html

Demo

# генерация 84 задач с DAG, дерево, поиск, on_hold
go run ./cmd/demo

Скрипт:

  1. Создаёт 84 задачи по 11 этапам с cross-stage fan-in зависимостями
  2. Показывает дерево этапов 6 и 10
  3. Выполняет поиск по тексту
  4. Завершает Этап 0 и показывает новые ready
  5. Откладывает Этап 4 (on_hold) и показывает, что ready сократились
  6. Выводит задачи со статусом on_hold

Промпты MCP

Сервер регистрирует MCP-промпты, доступные через prompts/list и prompts/get. Они делятся на статические (инструкции) и динамические (текущее состояние задач).

agent-instructions

Статический промпт с полной инструкцией по использованию data-plane инструментов. Доступен в local mode и в каждой data-plane сессии shared mode.

prompts/get("agent-instructions") → полный текст инструкции (см. ниже)

session-management

Статический промпт для управления сессиями в shared mode. Доступен только на control plane (эндпоинт /mcp).

prompts/get("session-management") → инструкция по register/unregister/list

Содержимое:

Для управления сессиями в shared режиме используй MCP-инструменты сервера mcp-semantic-task-planner-control.

Этот сервер работает в multi-tenant режиме: каждая сессия — изолированное рабочее пространство, привязанное к директории проекта. Все data-plane инструменты (search_tasks, get_tasks, upsert_task, delete_task) вызываются через конкретную сессию.

Управление сессиями

  1. register(path: "project-a", description: "optional description") — создать сессию. Сервер вернёт session_id. Сохрани его для последующих вызовов.
  2. unregister(session_id: "...") — закрыть сессию и освободить ресурсы (RAM, fsnotify, idle timer).
  3. list — показать все активные сессии (id, path, state).

После регистрации используй полученный session_id для data-plane вызовов через эндпоинт /mcp/{session_id}. Внутри сессии доступны инструменты: search_tasks, get_tasks, upsert_task, delete_task. Полное описание этих инструментов — в промпте agent-instructions внутри сессии.

Обработка ошибок Если сессия не найдена — проверь session_id через list и зарегистрируй заново при необходимости.

Завершение работы После завершения работы с проектом всегда вызывай unregister, чтобы освободить ресурсы сервера.

task-status (динамический)

Сводка по проекту: общее количество задач, разбивка по статусам, N первых ready задач. Помогает агенту быстро оценить состояние, не вызывая get_tasks.

prompts/get("task-status", {limit: "5"})

**Сводка проекта**
Всего: 34 задачи
Ready:      5  ← можно брать в работу
In progress: 3
Completed:  21
Failed:      1
On hold:     4

**Ближайшие ready (5):**
  t1  setup-dns        pending
  t2  configure-ssl    pending
  t3  deploy-staging   pending
  t4  smoke-tests      pending
  t5  load-tests       pending

Аргументы:

Аргумент Тип По умолчанию Описание
limit string (int) "5" Сколько ready задач показать в сводке

ready-tasks (динамический)

Плоский пагинируемый список задач, готовых к выполнению (все зависимости completed, статус pending, не on_hold). Позволяет агенту выбирать следующую задачу без вызова get_tasks(status: "ready").

prompts/get("ready-tasks", {limit: "10", offset: "0", compact: "true"})

**Ready tasks (страница 1 из 3, всего 25):**
  t1  setup-dns        pending
  t2  configure-ssl    pending
  t3  deploy-staging   pending

Аргументы:

Аргумент Тип По умолчанию Описание
limit string (int) "10" Сколько ready задач вернуть
offset string (int) "0" Смещение для пагинации
compact string (bool) "false" "true" — только id, name, status

blocked-tasks (динамический)

Задачи со статусом pending, у которых есть незавершённые зависимости (не ready). Помогает агенту понять, что тормозит прогресс и какие зависимости нужно разблокировать.

prompts/get("blocked-tasks", {limit: "10", offset: "0"})

**Blocked tasks (страница 1 из 2, всего 12):**
  t15  deploy-prod        ждёт: setup-dns, configure-ssl
  t20  load-test          ждёт: deploy-staging
  t21  monitoring-setup   ждёт: deploy-staging

Аргументы:

Аргумент Тип По умолчанию Описание
limit string (int) "10" Сколько заблокированных задач вернуть
offset string (int) "0" Смещение для пагинации

Пример использования (для MCP-агентов)

Рекомендуемый рабочий промпт для системной инструкции агента. Содержимое также доступно через prompts/get("agent-instructions"):

Для работы с задачами используй MCP-инструменты сервера mcp-semantic-task-planner:

Обзор 0. Начни с get_tasks(status: "all") — посмотреть все задачи (статусы, прогресс, зависимости). Или сразу get_tasks(status: "ready") — увидеть, что готово к выполнению.

  1. get_tasks(status: "all") — посмотреть все задачи (статусы, прогресс).
  2. get_tasks(status: "ready") — задачи, готовые к выполнению (все зависимости completed, статус pending, не on_hold).

Создание задач 3. Перед созданием новой задачи через upsert_task используй search_tasks, чтобы убедиться, что такая задача ещё не существует. 4. upsert_task(name: "...", description: "...") — создать новую задачу (ID сгенерируется из названия). Статус по умолчанию pending. 5. upsert_task(name: "...", dependencies: ["parent-task-1", "parent-task-2"]) — создать задачу с зависимостями.

Управление статусами 6. upsert_task(task_id: "...", status: "in_progress") — начать выполнение. 7. upsert_task(task_id: "...", status: "completed", result_summary: "...") — завершить (разблокирует dependants). 8. upsert_task(task_id: "...", status: "on_hold") — отложить (не блокирует dependants, задача уходит из ready). 9. upsert_task(task_id: "...", status: "failed", result_summary: "...") — отметить как невыполнимую.

Редактирование 10. При использовании upsert_task передавай только изменяемые поля, чтобы случайно не затереть существующие данные (например, не отправляй пустой description, если меняешь только status). 11. upsert_task(task_id: "...", dependencies: ["new-dep-1"]) — добавить/изменить зависимости существующей задачи. 12. upsert_task(task_id: "...", dependencies: []) — убрать все зависимости (задача станет ready, если статус pending). 13. upsert_task(task_id: "...", name: "...", description: "...") — изменить название или описание.

Диагностика блокировок 14. get_tasks(task_id: "...", view: "ancestors") — цепочка родителей до корня: понять, какие зависимости ещё не выполнены. 15. get_tasks(task_id: "...", view: "tree", compact: true) — дерево dependants: понять, кого затронет изменение статуса (компактно). 16. search_tasks(query: "...") или search_tasks(query: "...", min_score: 0.6) — найти задачи по смыслу.

Удаление и восстановление 17. delete_task(task_id: "...") — soft-delete (archived). Задача скрыта, но данные сохранены. 18. get_tasks(status: "all", include_archived: true) — посмотреть архив. 19. upsert_task(task_id: "...", archived: false) — восстановить из архива.

Работа с большим количеством задач 20. get_tasks(status: "ready", limit: 5, compact: true) — первые 5 готовых задач в кратком виде. 21. Используй compact: true когда нужно только id, name, status, dependencies без описаний и дат. 22. Для параллельной работы — каждая готовая задача из get_tasks(status: "ready") может выполняться в отдельном subagent. 23. После каждого изменения проверяй get_tasks(status: "ready") — возможно, появились новые готовые.

Обработка ошибок 24. Если MCP-инструмент возвращает ошибку, проанализируй сообщение об ошибке, исправь параметры запроса и повтори попытку. Не игнорируй ошибки.

Shared mode 25. Если сервер запущен в режиме shared, предварительно зарегистрируй сессию через register(path: "..."), получи session_id и используй его для всех последующих data-plane вызовов. По окончании — unregister.


Мониторинг

При HTTP-транспорте доступны:

  • GET /health — JSON со статусом, режимом, uptime, количеством сессий, версией
  • GET /metrics — Prometheus-метрики (mcp_http_requests_total, mcp_http_request_duration_seconds, mcp_active_sessions + Go runtime)

Лицензия

MIT