23 KiB
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 клиентам
Двухуровневая архитектура:
-
Data layer (Протокол данных):
- JSON-RPC 2.0 для коммуникации клиент-сервер
- Управление жизненным циклом, capabilities negotiation
- Основные примитивы: Tools, Resources, Prompts
-
Transport layer (Транспорт):
- STDIO: Прямое соединение между процессами (локальные серверы)
- HTTP Streamable: HTTP POST с Server-Sent Events (удаленные серверы)
Ключевые концепции MCP
Серверные примитивы:
- Tools: Выполняемые функции (например, API вызовы, операции с файлами)
- Resources: Источники данных для контекста (содержимое файлов, записи БД)
- Prompts: Шаблоны для взаимодействия с языковыми моделями
Клиентские примитивы:
- Sampling: Запрос language model completion от клиента
- Elicitation: Запрос дополнительной информации от пользователя
- Logging: Отправка логов для отладки
Lifecycle Management:
- Инициализация соединения
- Negotiation capabilities через handshake
- Динамическое обнаружение tools через
tools/list - Выполнение tools через
tools/call - 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)
Интеграционные точки
-
Agent.json конфигурация:
{ "capabilities": ["coding", "review"], "chat_listen": "mentions", "task_listen": "assigned" } -
EventRouter обрабатывает:
task.assigned→ запускаетrunAgent()message.new→ отвечает если упомянут
-
TrackerClient предоставляет REST методы:
- Tasks:
listTasks(),takeTask(),updateTask() - Messages:
sendMessage(),listMessages() - Projects:
listProjects(),getProject()
- Tasks:
Ограничения текущего подхода
- Монолитный 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-совместимой архитектурой:
- Tool definitions в формате совместимом с MCP
- Простая реализация без полного MCP сервера
- Готовность к миграции на полный 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_tasktool - ✅ Список задач через
list_taskstool работает - ✅ 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-совместимом формате — даёт нам:
- Быстрый результат: Можно реализовать за 3-4 недели
- Простота: Не требует отдельного MCP сервера
- Гибкость: 20+ tools покрывают все операции Tracker API
- Масштабируемость: Tool Registry легко расширяется
- Будущее: Готовность к миграции на полный MCP
Ключевые преимущества:
- Dynamic tool discovery: ИИ знает какие действия доступны
- Structured error handling: Понятные ошибки для self-correction
- Unified architecture: Все операции через единый интерфейс
- MCP compatibility: Готовность к стандарту будущего
Team Board получает полнофункциональных агентов с богатым набором инструментов для автономной работы с задачами, проектами и командой.