/**
* MCP 工具注册 - 使用最新的MCP SDK模式
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
// 禅道客户端和缓存
import { ZenTaoClient } from '../client';
import { LRUCache } from '../utils/cache';
import { Logger } from '../utils/logger';
// 工具实现
import { getProjectsHandler } from './getProjects';
import { getProjectByIdHandler } from './getProjectById';
import { createProjectHandler } from './createProject';
import { getTasksHandler } from './getTasks';
import { createTaskHandler } from './createTask';
import { updateTaskStatusHandler } from './updateTaskStatus';
import { batchCreateTasksHandler } from './batchCreateTasks';
// Zod 模式定义
const ProjectQuerySchema = z.object({
page: z.number().int().min(1).optional().default(1).describe('页码,默认为1'),
limit: z.number().int().min(1).max(100).optional().default(50).describe('每页数量,默认为50'),
status: z.enum(['wait', 'doing', 'done', 'suspended', 'closed']).optional().describe('项目状态'),
orderBy: z.string().optional().describe('排序字段')
});
const ProjectByIdSchema = z.object({
projectId: z.number().int().positive().describe('项目ID')
});
const CreateProjectSchema = z.object({
name: z.string().min(1).max(100).describe('项目名称'),
code: z.string().min(1).max(20).describe('项目代码(唯一)'),
begin: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe('项目开始时间 (YYYY-MM-DD)'),
end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe('项目结束时间 (YYYY-MM-DD)'),
acl: z.enum(['private', 'open', 'extend']).optional().default('private').describe('访问控制')
});
const TaskQuerySchema = z.object({
projectId: z.number().int().positive().optional().describe('项目ID过滤'),
productId: z.number().int().positive().optional().describe('产品ID过滤'),
storyId: z.number().int().positive().optional().describe('需求ID过滤'),
status: z.enum(['wait', 'doing', 'done', 'blocked', 'cancel', 'closed']).optional().describe('任务状态'),
assignedTo: z.string().optional().describe('负责人账号'),
pri: z.number().int().min(1).max(4).optional().describe('优先级 (1-4, 1最高)'),
page: z.number().int().min(1).optional().default(1).describe('页码'),
limit: z.number().int().min(1).max(100).optional().default(50).describe('每页数量')
});
const CreateTaskSchema = z.object({
name: z.string().min(1).max(200).describe('任务名称'),
project: z.number().int().positive().describe('所属项目ID'),
story: z.number().int().positive().optional().describe('关联需求ID'),
assignedTo: z.string().optional().describe('负责人账号'),
pri: z.number().int().min(1).max(4).optional().default(3).describe('优先级 (1-4, 1最高)'),
estimate: z.number().positive().optional().describe('预计工时 (小时)'),
deadline: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe('截止时间 (YYYY-MM-DD)')
});
const UpdateTaskStatusSchema = z.object({
taskId: z.number().int().positive().describe('任务ID'),
status: z.enum(['wait', 'doing', 'done', 'blocked', 'cancel', 'closed']).describe('任务状态')
});
const TaskItemSchema = z.object({
name: z.string().min(1).max(200).describe('任务名称'),
project: z.number().int().positive().describe('项目ID'),
assignedTo: z.string().optional().describe('负责人账号'),
pri: z.number().int().min(1).max(4).optional().default(3).describe('优先级'),
estimate: z.number().positive().optional().describe('预计工时')
});
const BatchCreateTasksSchema = z.object({
items: z.array(TaskItemSchema).min(1).max(50).describe('任务列表(最多50个)'),
options: z.object({
continueOnError: z.boolean().optional().default(true).describe('出错时是否继续'),
reportMode: z.enum(['summary', 'detailed']).optional().default('summary').describe('报告模式'),
maxConcurrency: z.number().int().min(1).max(20).optional().default(10).describe('最大并发数')
}).optional().default({})
});
// 输出模式定义
const ProjectOutputSchema = z.object({
id: z.number(),
name: z.string(),
code: z.string(),
status: z.string(),
begin: z.string(),
end: z.string().nullable(),
acl: z.string().optional()
});
const TaskOutputSchema = z.object({
id: z.number(),
name: z.string(),
project: z.number(),
status: z.string(),
assignedTo: z.string().nullable(),
pri: z.number(),
estimate: z.number().nullable(),
deadline: z.string().nullable()
});
const BatchResultSchema = z.object({
total: z.number(),
successful: z.number(),
failed: z.number(),
errors: z.array(z.object({
index: z.number(),
error: z.string()
})).optional()
});
/**
* 注册所有工具到MCP服务器
*/
export function registerAllTools(
server: McpServer,
client: ZenTaoClient,
cache: LRUCache<string, any>,
logger: Logger
): void {
// 项目管理工具
// 获取项目列表
server.registerTool(
'zendao_list_projects',
{
title: '获取禅道项目列表',
description: '获取禅道系统中的项目列表,支持分页和状态过滤',
inputSchema: ProjectQuerySchema,
outputSchema: z.object({
projects: z.array(ProjectOutputSchema),
total: z.number(),
page: z.number(),
limit: z.number()
})
},
async (args) => {
logger.info('获取项目列表', args);
try {
const result = await getProjectsHandler(client, cache, args);
logger.info('获取项目列表成功');
return result;
} catch (error: any) {
logger.error('获取项目列表失败', error);
throw error;
}
}
);
// 根据ID获取项目详情
server.registerTool(
'zendao_get_project',
{
title: '获取项目详情',
description: '根据项目ID获取禅道项目的详细信息',
inputSchema: ProjectByIdSchema,
outputSchema: ProjectOutputSchema
},
async (args) => {
logger.info('获取项目详情', args);
try {
const result = await getProjectByIdHandler(client, cache, args.projectId);
logger.info('获取项目详情成功');
return result;
} catch (error: any) {
logger.error('获取项目详情失败', error);
throw error;
}
}
);
// 创建新项目
server.registerTool(
'zendao_create_project',
{
title: '创建新项目',
description: '在禅道系统中创建一个新的项目',
inputSchema: CreateProjectSchema,
outputSchema: ProjectOutputSchema,
annotations: {
destructiveHint: false,
idempotentHint: false
}
},
async (args) => {
logger.info('创建项目', args);
try {
const result = await createProjectHandler(client, cache, args);
logger.info('创建项目成功');
return result;
} catch (error: any) {
logger.error('创建项目失败', error);
throw error;
}
}
);
// 任务管理工具
// 获取任务列表
server.registerTool(
'zendao_list_tasks',
{
title: '获取任务列表',
description: '获取禅道系统中的任务列表,支持多种过滤条件',
inputSchema: TaskQuerySchema,
outputSchema: z.object({
tasks: z.array(TaskOutputSchema),
total: z.number(),
page: z.number(),
limit: z.number()
})
},
async (args) => {
logger.info('获取任务列表', args);
try {
const result = await getTasksHandler(client, cache, args);
logger.info('获取任务列表成功');
return result;
} catch (error: any) {
logger.error('获取任务列表失败', error);
throw error;
}
}
);
// 创建新任务
server.registerTool(
'zendao_create_task',
{
title: '创建新任务',
description: '在禅道系统中创建一个新的任务',
inputSchema: CreateTaskSchema,
outputSchema: TaskOutputSchema,
annotations: {
destructiveHint: false,
idempotentHint: false
}
},
async (args) => {
logger.info('创建任务', args);
try {
const result = await createTaskHandler(client, cache, args);
logger.info('创建任务成功');
return result;
} catch (error: any) {
logger.error('创建任务失败', error);
throw error;
}
}
);
// 更新任务状态
server.registerTool(
'zendao_update_task_status',
{
title: '更新任务状态',
description: '更新指定任务的状态',
inputSchema: UpdateTaskStatusSchema,
outputSchema: TaskOutputSchema,
annotations: {
destructiveHint: false,
idempotentHint: true
}
},
async (args) => {
logger.info('更新任务状态', args);
try {
const result = await updateTaskStatusHandler(client, cache, args.taskId, args.status);
logger.info('更新任务状态成功');
return result;
} catch (error: any) {
logger.error('更新任务状态失败', error);
throw error;
}
}
);
// 批量操作工具
// 批量创建任务
server.registerTool(
'zendao_batch_create_tasks',
{
title: '批量创建任务',
description: '一次性创建多个任务,支持并发处理和错误恢复',
inputSchema: BatchCreateTasksSchema,
outputSchema: BatchResultSchema,
annotations: {
destructiveHint: false,
idempotentHint: false
}
},
async (args) => {
logger.info('批量创建任务', { itemCount: args.items.length });
try {
const result = await batchCreateTasksHandler(client, cache, args.items, args.options);
logger.info('批量创建任务完成', {
total: result.total,
successful: result.successful,
failed: result.failed
});
return result;
} catch (error: any) {
logger.error('批量创建任务失败', error);
throw error;
}
}
);
logger.info('所有工具注册完成');
}