docs/MCP-TOOLS-ARCHITECTURE.md

23 KiB
Raw Blame History

MCP Tools Architecture — Team Board

Версия: 1.0
Дата: 2026-02-23
Автор: Winston (BMAD)


1. Исследование MCP

Что такое MCP

Model Context Protocol (MCP) — открытый стандарт для подключения AI приложений к внешним системам. MCP работает как "USB-C для AI" — стандартизированный способ подключения ИИ к данным, инструментам и рабочим процессам.

Архитектура клиент-сервер:

  • MCP Host (AI приложение): Управляет одним или множеством MCP клиентов
  • MCP Client: Поддерживает соединение с MCP сервером и получает контекст
  • MCP Server: Программа, предоставляющая контекст MCP клиентам

Двухуровневая архитектура:

  1. Data layer (Протокол данных):

    • JSON-RPC 2.0 для коммуникации клиент-сервер
    • Управление жизненным циклом, capabilities negotiation
    • Основные примитивы: Tools, Resources, Prompts
  2. Transport layer (Транспорт):

    • STDIO: Прямое соединение между процессами (локальные серверы)
    • HTTP Streamable: HTTP POST с Server-Sent Events (удаленные серверы)

Ключевые концепции MCP

Серверные примитивы:

  • Tools: Выполняемые функции (например, API вызовы, операции с файлами)
  • Resources: Источники данных для контекста (содержимое файлов, записи БД)
  • Prompts: Шаблоны для взаимодействия с языковыми моделями

Клиентские примитивы:

  • Sampling: Запрос language model completion от клиента
  • Elicitation: Запрос дополнительной информации от пользователя
  • Logging: Отправка логов для отладки

Lifecycle Management:

  1. Инициализация соединения
  2. Negotiation capabilities через handshake
  3. Динамическое обнаружение tools через tools/list
  4. Выполнение tools через tools/call
  5. Real-time уведомления о изменениях

2. Анализ текущей архитектуры Picogent

Как работает сейчас

Picogent — агент для Team Board с архитектурой:

Picogent Agent
├── WebSocket Transport    (WsClientTransport)
├── REST Client           (TrackerClient)
├── Event Router          (EventRouter)
└── Claude API Integration

Текущий стек:

  • Transport: WebSocket для событий + REST для мутаций
  • Events: task.assigned, message.new → обработка через EventRouter
  • Actions: Только REST API через TrackerClient
  • LLM: Прямая интеграция с Claude API (Anthropic SDK)

Интеграционные точки

  1. Agent.json конфигурация:

    {
      "capabilities": ["coding", "review"],
      "chat_listen": "mentions",
      "task_listen": "assigned"
    }
    
  2. EventRouter обрабатывает:

    • task.assigned → запускает runAgent()
    • message.new → отвечает если упомянут
  3. TrackerClient предоставляет REST методы:

    • Tasks: listTasks(), takeTask(), updateTask()
    • Messages: sendMessage(), listMessages()
    • Projects: listProjects(), getProject()

Ограничения текущего подхода

  • Монолитный EventRouter: Вся логика в одном классе
  • Hardcoded инструменты: REST методы зашиты в код агента
  • Нет tool discovery: ИИ не знает какие действия доступны
  • Сложное расширение: Добавление новых действий требует изменения кода

3. Решение: MCP vs Function Calling

Сравнение подходов

Критерий MCP Server Function Calling Рекомендация
Стандартизация Открытый стандарт Зависит от LLM провайдера MCP
Совместимость Любые MCP hosts Только Claude/OpenAI MCP
Tool Discovery Динамический tools/list Статичные definitions MCP
Real-time updates Notifications Нет MCP
Сложность реализации ⚠️ Средняя (протокол) Простая (JSON schema) Function Calling
Готовые SDK Python, TypeScript Самописные MCP

Гибридный подход — рекомендация

Используем Function Calling с MCP-совместимой архитектурой:

  1. Tool definitions в формате совместимом с MCP
  2. Простая реализация без полного MCP сервера
  3. Готовность к миграции на полный MCP в будущем

Обоснование:

  • Прагматизм: Function calling проще реализовать и отладить
  • Быстрый результат: Не нужен отдельный MCP сервер
  • Совместимость: Структуры данных готовы для MCP
  • Эволюция: Легко мигрировать на полный MCP позже

4. Каталог инструментов

4.1 Tasks (Задачи)

Tool Name Description Parameters Returns
list_tasks Список задач с фильтрацией project_id?, status?, assignee?, labels? TaskOut[]
get_task Получить задачу по ID task_id: string TaskOut
create_task Создать новую задачу project_slug: string, title: string, description?, type?, priority?, assignee_slug? TaskOut
update_task Обновить поля задачи task_id: string, title?, status?, priority?, assignee_slug? TaskOut
take_task Взять задачу себе (атомарно) task_id: string, agent_slug: string TaskOut
reject_task Отклонить задачу с причиной task_id: string, reason: string {ok: true, reason: string}
assign_task Назначить задачу другому task_id: string, assignee_slug: string TaskOut
watch_task Подписаться на уведомления task_id: string, agent_slug: string {ok: true, watchers: string[]}

Пример tool definition:

{
  "name": "take_task",
  "title": "Взять задачу в работу",
  "description": "Атомарно назначить задачу агенту, если она свободна",
  "inputSchema": {
    "type": "object",
    "properties": {
      "task_id": {"type": "string", "description": "ID или ключ задачи (TE-15)"},
      "agent_slug": {"type": "string", "description": "Slug агента"}
    },
    "required": ["task_id", "agent_slug"]
  }
}

4.2 Steps (Этапы задач)

Tool Name Description Parameters Returns
list_steps Этапы задачи task_id: string StepOut[]
add_step Добавить этап в чеклист task_id: string, title: string StepOut
complete_step Отметить этап выполненным task_id: string, step_id: string StepOut
update_step Обновить название этапа task_id: string, step_id: string, title?: string, done?: boolean StepOut

4.3 Messages (Сообщения и комментарии)

Tool Name Description Parameters Returns
send_message Отправить сообщение в чат или комментарий к задаче content: string, chat_id?: string, task_id?: string, mentions?: string[] MessageOut
list_messages Получить сообщения chat_id?: string, task_id?: string, limit?: number MessageOut[]
reply_message Ответить в треде parent_id: string, content: string MessageOut

Unified Message модель — одна сущность для:

  • Chat messages: chat_id заполнен
  • Task comments: task_id заполнен
  • Thread replies: parent_id заполнен

4.4 Projects (Проекты)

Tool Name Description Parameters Returns
list_projects Список активных проектов - ProjectOut[]
get_project Информация о проекте slug: string ProjectOut

4.5 Members (Участники)

Tool Name Description Parameters Returns
list_members Участники проекта - MemberOut[]
get_member Информация об участнике slug: string MemberOut
update_my_status Обновить свой статус status: string {status: string}

4.6 Files (Файлы) — будущие

Tool Name Description Parameters Returns
upload_file Загрузить файл к сообщению message_id: string, filename: string, content: base64 AttachmentOut
list_files Файлы задачи/проекта task_id?: string, project_id?: string AttachmentOut[]
download_file Скачать файл attachment_id: string {filename: string, content: base64}

5. Архитектура реализации

5.1 Компонентная структура

picogent/
├── src/
│   ├── tools/                    # MCP-совместимые tools
│   │   ├── registry.ts          # Реестр всех tools
│   │   ├── tasks.ts             # Task tools
│   │   ├── messages.ts          # Message tools  
│   │   ├── projects.ts          # Project tools
│   │   └── members.ts           # Member tools
│   ├── mcp/
│   │   ├── types.ts            # MCP-совместимые типы
│   │   ├── tool-executor.ts    # Выполнение tools
│   │   └── tool-builder.ts     # Построение tool definitions
│   ├── agent.ts                # Интеграция с Claude + tools
│   └── router.ts               # Обновлённый EventRouter

5.2 Tool Registry (Центральный реестр)

// tools/registry.ts
export interface ToolDefinition {
  name: string;
  title: string;
  description: string;
  inputSchema: JSONSchema;
  handler: (params: any, context: ToolContext) => Promise<any>;
}

export interface ToolContext {
  trackerClient: TrackerClient;
  agentSlug: string;
  transport: WsClientTransport;
}

export class ToolRegistry {
  private tools = new Map<string, ToolDefinition>();

  register(tool: ToolDefinition): void {
    this.tools.set(tool.name, tool);
  }

  list(): ToolDefinition[] {
    return Array.from(this.tools.values());
  }

  async execute(name: string, params: any, context: ToolContext): Promise<any> {
    const tool = this.tools.get(name);
    if (!tool) throw new Error(`Tool not found: ${name}`);
    return tool.handler(params, context);
  }
}

5.3 Task Tools Implementation

// tools/tasks.ts
import { ToolDefinition } from './registry.js';

export const takeTaskTool: ToolDefinition = {
  name: 'take_task',
  title: 'Взять задачу в работу',
  description: 'Атомарно назначить задачу агенту, если она свободна',
  inputSchema: {
    type: 'object',
    properties: {
      task_id: { type: 'string', description: 'ID задачи' },
      agent_slug: { type: 'string', description: 'Slug агента' }
    },
    required: ['task_id', 'agent_slug']
  },
  async handler(params, context) {
    try {
      const result = await context.trackerClient.takeTask(
        params.task_id, 
        params.agent_slug
      );
      return {
        content: [{ 
          type: 'text', 
          text: `✅ Задача ${result.key} взята в работу`
        }],
        structuredContent: result
      };
    } catch (error) {
      return {
        content: [{ 
          type: 'text', 
          text: `❌ Ошибка: ${error.message}`
        }],
        isError: true
      };
    }
  }
};

export const listTasksTool: ToolDefinition = {
  name: 'list_tasks',
  title: 'Список задач',
  description: 'Получить список задач с фильтрацией',
  inputSchema: {
    type: 'object',
    properties: {
      project_id: { type: 'string', description: 'ID проекта' },
      status: { type: 'string', enum: ['backlog', 'todo', 'in_progress', 'in_review', 'done'] },
      assignee: { type: 'string', description: 'Slug исполнителя' }
    }
  },
  async handler(params, context) {
    const tasks = await context.trackerClient.listTasks(params);
    return {
      content: [{ 
        type: 'text', 
        text: `Найдено задач: ${tasks.length}\n${tasks.map(t => `- ${t.key}: ${t.title} (${t.status})`).join('\n')}`
      }],
      structuredContent: tasks
    };
  }
};

5.4 Интеграция с Claude

// agent.ts (модифицированный)
import { ToolRegistry } from './tools/registry.js';

export async function* runAgent(prompt: string, options: AgentOptions): AsyncIterator<AgentMessage> {
  const toolRegistry = new ToolRegistry();
  
  // Регистрируем все tools
  registerAllTools(toolRegistry);
  
  // Создаём tool definitions для Claude
  const tools = toolRegistry.list().map(tool => ({
    name: tool.name,
    description: tool.description,
    input_schema: tool.inputSchema
  }));

  const response = await anthropic.messages.create({
    model: options.model,
    messages: [{ role: 'user', content: prompt }],
    tools,
    system: options.systemPrompt
  });

  // Обрабатываем tool calls
  for (const content of response.content) {
    if (content.type === 'tool_use') {
      const result = await toolRegistry.execute(
        content.name, 
        content.input,
        { trackerClient: options.trackerClient, agentSlug: options.agentSlug }
      );
      
      yield {
        type: 'text',
        content: result.content?.[0]?.text || 'Tool executed',
        sessionId: options.sessionId
      };
    } else {
      yield {
        type: 'text',
        content: content.text,
        sessionId: options.sessionId
      };
    }
  }
}

5.5 Обновлённый EventRouter

// router.ts (упрощённый)
export class EventRouter {
  constructor(
    private config: AgentConfig,
    private toolRegistry: ToolRegistry,
    private taskTracker: TaskTracker,
  ) {}

  async handleEvent(event: TrackerEvent): Promise<void> {
    const context = {
      trackerClient: new TrackerClient(this.config.trackerUrl, this.config.token),
      agentSlug: this.config.slug,
      transport: this.taskTracker
    };

    switch (event.event) {
      case 'task.assigned':
        await this.handleTaskAssigned(event.data, context);
        break;
      case 'message.new':
        await this.handleMessageNew(event.data, context);
        break;
    }
  }

  private async handleTaskAssigned(data: any, context: ToolContext): Promise<void> {
    const task = data.task || data;
    
    // Строим промпт с доступными tools
    const toolsDescription = this.toolRegistry.list()
      .map(t => `- ${t.name}: ${t.description}`)
      .join('\n');
    
    const prompt = `
# Задача: ${task.key}${task.title}

${task.description || ''}

## Доступные инструменты:
${toolsDescription}

Выполни задачу используя доступные инструменты. 
Начни с обновления статуса на "in_progress".
`;

    // Запускаем агента с tools
    for await (const msg of runAgent(prompt, {
      toolRegistry: this.toolRegistry,
      agentSlug: this.config.slug,
      trackerClient: context.trackerClient
    })) {
      // Логируем результат
      console.log(`Agent output: ${msg.content}`);
    }
  }
}

5.6 Структура файлов

picogent/
├── src/
│   ├── tools/
│   │   ├── registry.ts          # Центральный реестр tools
│   │   ├── tasks.ts             # 8 task tools
│   │   ├── steps.ts             # 4 step tools  
│   │   ├── messages.ts          # 3 message tools
│   │   ├── projects.ts          # 2 project tools
│   │   ├── members.ts           # 3 member tools
│   │   └── index.ts             # Export всех tools
│   ├── mcp/
│   │   ├── types.ts             # MCP-совместимые типы
│   │   └── executor.ts          # Tool execution engine
│   ├── tracker/
│   │   ├── client.ts            # REST client (существующий)
│   │   └── types.ts             # Tracker API types
│   ├── transport/
│   │   └── ws-client.ts         # WebSocket transport (существующий)
│   ├── agent.ts                 # Модифицированный для tools
│   ├── router.ts                # Упрощённый EventRouter  
│   └── index.ts                 # Точка входа
├── package.json                 # + @anthropic-ai/sdk dependency
└── agent.json                   # Конфигурация (существующая)

6. План реализации

Phase 1: Базовая инфраструктура tools (Неделя 1)

День 1-2: Создание Tool Registry

  • src/tools/registry.ts — интерфейсы и реестр
  • src/mcp/types.ts — MCP-совместимые типы
  • Тесты для registry

День 3-4: Интеграция с Claude

  • Модификация agent.ts для поддержки tools
  • Tool execution pipeline
  • Error handling для tool calls

День 5-7: Базовые Task Tools

  • take_task, list_tasks, get_task
  • Интеграция с существующим TrackerClient
  • Тестирование на простых задачах

Phase 2: Полный набор tools (Неделя 2)

День 1-3: Все Task и Step Tools

  • Реализация всех 8 task tools
  • 4 step tools для чеклистов
  • Валидация параметров и ошибок

День 4-5: Message Tools

  • send_message, list_messages, reply_message
  • Поддержка unified модели (чат + комментарии)

День 6-7: Project и Member Tools

  • Project tools: list_projects, get_project
  • Member tools: list_members, update_my_status
  • Интеграционные тесты

Phase 3: Продвинутые возможности (Неделя 3)

День 1-2: Обновлённый EventRouter

  • Рефакторинг router.ts под tools
  • Улучшенная обработка task.assigned
  • Dynamic tool discovery

День 3-4: Error Handling и Reliability

  • Robust error handling в tools
  • Retry logic для API calls
  • Graceful degradation

День 5-7: Документация и тесты

  • Tool documentation
  • Integration tests
  • Performance тесты

Phase 4: Готовность к MCP миграции (Неделя 4)

День 1-3: MCP Compatibility Layer

  • Full MCP tool definitions format
  • MCP server skeleton (не активный)
  • Migration path documentation

День 4-5: Production Deploy

  • Deploy обновлённого Picogent
  • Monitoring и логирование
  • Performance optimization

День 6-7: Итерации на основе feedback

  • Bug fixes
  • UX improvements
  • Documentation updates

Критерии готовности

Phase 1 Complete:

  • Агент может взять задачу используя take_task tool
  • Список задач через list_tasks tool работает
  • Error handling для недоступных tools

Phase 2 Complete:

  • Все 20 tools реализованы и работают
  • Агент может выполнить полный цикл: взять задачу → добавить этапы → обновить статус → прокомментировать

Phase 3 Complete:

  • Production-ready качество кода
  • Comprehensive error handling
  • Full integration test suite

Phase 4 Complete:

  • MCP-compatible архитектура
  • Готовность к миграции на полный MCP сервер
  • Документация для developers

Заключение

Выбранный подход — гибридная архитектура с Function Calling в MCP-совместимом формате — даёт нам:

  1. Быстрый результат: Можно реализовать за 3-4 недели
  2. Простота: Не требует отдельного MCP сервера
  3. Гибкость: 20+ tools покрывают все операции Tracker API
  4. Масштабируемость: Tool Registry легко расширяется
  5. Будущее: Готовность к миграции на полный MCP

Ключевые преимущества:

  • Dynamic tool discovery: ИИ знает какие действия доступны
  • Structured error handling: Понятные ошибки для self-correction
  • Unified architecture: Все операции через единый интерфейс
  • MCP compatibility: Готовность к стандарту будущего

Team Board получает полнофункциональных агентов с богатым набором инструментов для автономной работы с задачами, проектами и командой.