任务管理MCP服务实现指南
April 14, 2025 · View on GitHub
1. 项目结构
实际项目结构如下:
task-manager-mcp/
├── src/ # 源代码目录
│ ├── server.py # 主程序入口 (MCP服务定义)
│ ├── config.py # 配置模块 (LLM客户端工厂等)
│ ├── models/
│ │ └── task.py # 任务数据模型
│ ├── services/
│ │ ├── task_service.py # 任务管理核心逻辑
│ │ └── prd_parser.py # PRD解析服务
│ ├── storage/
│ │ └── task_storage.py # 任务存储实现
│ ├── llm/ # LLM集成模块
│ │ ├── base.py # LLM接口抽象基类
│ │ └── gemini.py # Google Gemini实现
│ └── utils/ # 工具函数
│ ├── logging_config.py # 日志配置
│ ├── file_operations.py # 文件操作
│ └── task_utils.py # 任务工具函数
└── docs/
├── design.md # 设计文档
├── mcp-rules.md # MCP调用规则
└── getting-started.md # 快速入门指南
2. 核心组件实现
2.1 服务器入口 (src/server.py)
服务器入口定义了MCP服务所有接口,包括PRD解析、任务管理等功能:
from mcp.server.fastmcp import FastMCP
from services.task_service import TaskService
from config import get_llm_client
import mcp.types as types
import logging
import os
import json
# 设置日志
logger = setup_logging(log_file_path, logging.INFO)
# 初始化LLM客户端
llm_client = get_llm_client() # 从环境变量获取配置
# 创建MCP实例
mcp = FastMCP("task-manager-mcp")
# 创建任务服务实例,注入LLM客户端
task_service = TaskService(llm_client=llm_client)
@mcp.tool("decompose_prd")
async def decompose_prd(prd_content: str) -> list[types.TextContent]:
"""解析PRD文档,自动拆解为主任务列表
Args:
prd_content: PRD文档内容或文件路径,支持直接文本或以file://开头的文件路径
Returns:
List: 包含提取的主任务列表的格式化响应
"""
# 实现略...
@mcp.tool("add_task")
async def add_task(
name: str,
description: str = "",
id: str = "",
priority: str = "medium",
tags: str = "",
assigned_to: str = "",
estimated_hours: str = "",
dependencies: str = ""
) -> list[types.TextContent]:
"""创建新任务"""
# 实现略...
@mcp.tool("update_task")
async def update_task(
task_id: str,
name: str = "",
description: str = "",
status: str = "",
priority: str = "",
tags: str = "",
assigned_to: str = "",
estimated_hours: str = "",
actual_hours: str = "",
dependencies: str = ""
) -> list[types.TextContent]:
"""更新现有任务信息"""
# 实现略...
@mcp.tool("get_task")
async def get_task(task_id: str) -> list[types.TextContent]:
"""获取任务详情"""
# 实现略...
@mcp.tool("get_task_list")
async def get_task_list(
status: str = "",
priority: str = "",
tag: str = "",
assigned_to: str = "",
page: str = "1",
page_size: str = "100"
) -> list[types.TextContent]:
"""获取任务列表"""
# 实现略...
@mcp.tool("get_next_executable_task")
async def get_next_executable_task(limit: str = "5") -> list[types.TextContent]:
"""获取下一个可执行任务"""
# 实现略...
@mcp.tool("expand_task")
async def expand_task(task_id: str, num_subtasks: str = "5") -> list[types.TextContent]:
"""展开任务为子任务"""
# 实现略...
@mcp.tool("update_task_code_references")
async def update_task_code_references(task_id: str, code_files: str) -> list[types.TextContent]:
"""更新任务实现的代码文件引用"""
# 实现略...
if __name__ == "__main__":
import uvicorn
port = int(os.environ.get("MCP_SERVICE_PORT", 8000))
uvicorn.run("server:mcp", host="0.0.0.0", port=port, reload=True)
2.2 任务模型 (src/models/task.py)
任务模型定义了系统中的核心数据结构:
from enum import Enum
from typing import Dict, List, Optional, Set, Union, Any
from datetime import datetime
from pydantic import BaseModel, Field
class TaskStatus(str, Enum):
"""任务状态枚举"""
TODO = "todo" # 待办
IN_PROGRESS = "in_progress" # 进行中
DONE = "done" # 已完成
BLOCKED = "blocked" # 被阻塞
CANCELLED = "cancelled" # 已取消
class TaskPriority(str, Enum):
"""任务优先级枚举"""
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
class TaskComplexity(str, Enum):
"""任务复杂度枚举"""
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
class Task(BaseModel):
"""任务数据模型"""
id: str = Field(..., description="任务唯一标识符")
name: str = Field(..., description="任务名称")
description: str = Field("", description="任务详细描述")
status: TaskStatus = Field(default=TaskStatus.TODO, description="任务状态")
priority: TaskPriority = Field(default=TaskPriority.MEDIUM, description="任务优先级")
complexity: TaskComplexity = Field(default=TaskComplexity.MEDIUM, description="任务复杂度")
dependencies: Set[str] = Field(default_factory=set, description="依赖任务的ID集合")
blocked_by: Set[str] = Field(default_factory=set, description="阻塞该任务的任务ID集合")
created_at: datetime = Field(default_factory=datetime.now, description="创建时间")
updated_at: datetime = Field(default_factory=datetime.now, description="最后更新时间")
completed_at: Optional[datetime] = Field(None, description="完成时间")
tags: List[str] = Field(default_factory=list, description="任务标签")
assigned_to: Optional[str] = Field(None, description="分配给谁")
metadata: Dict[str, Union[str, int, float, bool]] = Field(
default_factory=dict, description="附加元数据"
)
estimated_hours: Optional[float] = Field(None, description="预估工时")
actual_hours: Optional[float] = Field(None, description="实际工时")
code_references: List[str] = Field(default_factory=list, description="关联的代码引用")
parent_task_id: Optional[str] = Field(None, description="父任务ID")
subtasks: List[Dict[str, Any]] = Field(default_factory=list, description="子任务列表")
class Config:
use_enum_values = True
# 方法略...
2.3 任务存储 (src/storage/task_storage.py)
任务存储提供了对任务数据的CRUD操作和依赖关系管理:
from typing import Dict, List, Optional, Set, Tuple, Union
from datetime import datetime
import logging
from ..models.task import Task, TaskStatus, TaskPriority, TaskComplexity
class TaskStorage:
"""任务内存存储类"""
def __init__(self):
"""初始化内存存储"""
self.tasks: Dict[str, Task] = {}
# 依赖关系图(任务ID -> 被该任务阻塞的任务ID集合)
self.dependency_graph: Dict[str, Set[str]] = {}
def create_task(
self,
name: str,
description: str = "",
id: Optional[str] = None,
priority: TaskPriority = TaskPriority.MEDIUM,
complexity: TaskComplexity = TaskComplexity.MEDIUM,
tags: List[str] = None,
assigned_to: Optional[str] = None,
estimated_hours: Optional[float] = None,
dependencies: List[str] = None,
code_references: List[str] = None
) -> Task:
"""创建新任务"""
# 实现略...
def get_task(self, task_id: str) -> Optional[Task]:
"""获取任务信息"""
return self.tasks.get(task_id)
def update_task(self, task_id: str, **kwargs) -> Optional[Task]:
"""更新任务信息"""
# 实现略...
def set_task_dependency(self, task_id: str, depends_on_id: str) -> Tuple[bool, Optional[str]]:
"""设置任务依赖关系"""
# 实现略...
def remove_task_dependency(self, task_id: str, depends_on_id: str) -> bool:
"""移除任务依赖关系"""
# 实现略...
def _would_create_cycle(self, task_id: str, depends_on_id: str) -> bool:
"""检查添加依赖是否会导致循环依赖"""
# 实现略...
def clear_all_tasks(self) -> None:
"""清空所有任务"""
self.tasks.clear()
self.dependency_graph.clear()
3. 核心服务实现
3.1 任务服务 (src/services/task_service.py)
任务服务是系统核心业务逻辑的实现,提供任务管理的所有功能:
from typing import Dict, List, Optional, Any, Tuple
import logging
from datetime import datetime
from ..models.task import Task, TaskStatus, TaskPriority
from ..storage.task_storage import TaskStorage
from ..services.prd_parser import PrdParser
from ..llm.base import LLMInterface
class TaskService:
"""任务服务类,提供任务管理的业务逻辑"""
def __init__(self, storage: Optional[TaskStorage] = None, llm_client: Optional[LLMInterface] = None):
"""
初始化任务服务
Args:
storage: 任务存储实例,如果为None则创建新实例。
llm_client: 可选的 LLM 客户端实例,用于 PRD 解析。
"""
self.storage = storage or TaskStorage()
self.prd_parser = PrdParser(storage=self.storage, llm_client=llm_client)
async def decompose_prd(self, prd_content: str) -> Dict[str, Any]:
"""
解析PRD文档,自动拆解为任务列表
Args:
prd_content: PRD文档内容或文件路径
Returns:
Dict: 包含提取任务信息的响应
"""
# 处理文件路径和内容
# 调用 PrdParser 进行解析
# 返回格式化响应
def add_task(self, name: str, description: str = "", **kwargs) -> Dict[str, Any]:
"""
创建新任务
Args:
name: 任务名称
description: 任务描述
**kwargs: 其他任务属性
Returns:
Dict: 包含新创建任务信息的响应
"""
# 实现任务创建
def update_task(self, task_id: str, **kwargs) -> Dict[str, Any]:
"""
更新现有任务信息,包括依赖关系
Args:
task_id: 任务ID
**kwargs: 要更新的任务属性
Returns:
Dict: 包含更新后任务信息的响应
"""
# 处理子任务更新和普通任务更新
# 管理任务依赖关系
def _sync_parent_task_status(self, parent_task_id: str) -> None:
"""
根据子任务状态同步更新父任务状态
Args:
parent_task_id: 父任务ID
"""
# 实现父任务状态同步
def get_task_list(self, status: Optional[str] = None, priority: Optional[str] = None,
tag: Optional[str] = None, assigned_to: Optional[str] = None,
page: int = 1, page_size: int = 100) -> Dict[str, Any]:
"""
获取任务列表,支持各种筛选条件
Args:
多种筛选条件和分页参数
Returns:
Dict: 包含任务列表的分页响应
"""
# 实现任务列表查询和分页
def get_next_executable_task(self, limit: int = 5) -> Dict[str, Any]:
"""
获取下一个可执行任务
逻辑顺序:
1. 首先查找状态为"in_progress"的任务
2. 如果没有进行中的任务,查找"todo"状态且依赖已满足的任务
3. 按优先级排序(critical > high > medium > low)
4. 同等优先级下,被更多任务依赖的排前面
5. 同等条件下,创建时间早的排前面
Args:
limit: 内部排序时考虑的任务数量限制
Returns:
Dict: 包含下一个可执行任务的响应
"""
# 实现获取下一个任务的复杂逻辑
async def expand_task(self, task_id: str, num_subtasks: int = 5,
project_context: str = "") -> Dict[str, Any]:
"""
为指定任务生成子任务
Args:
task_id: 要展开的任务ID
num_subtasks: 希望生成的子任务数量
project_context: 项目相关上下文
Returns:
Dict: 包含生成的子任务信息的响应
"""
# 调用LLM生成子任务
# 处理子任务层级标识与依赖关系
3.2 PRD解析服务 (src/services/prd_parser.py)
PRD解析服务负责智能解析产品需求文档,提取任务和依赖关系:
import re
import os
import json
import google.generativeai as genai
from typing import List, Dict, Optional, Any, Tuple
import logging
from ..models.task import Task, TaskStatus, TaskPriority
from ..storage.task_storage import TaskStorage
from ..llm.base import LLMInterface
class PrdParser:
"""PRD文档解析器,使用注入的LLM客户端(如果提供)"""
def __init__(self, storage: TaskStorage, llm_client: Optional[LLMInterface] = None):
"""
初始化PRD解析器。
Args:
storage: 任务存储后端实例。
llm_client: 一个实现了LLMInterface的可选客户端实例。
如果提供,将用于解析PRD;否则,将回退到基本解析。
"""
self.storage = storage
self.llm_client = llm_client
async def parse(self, content: str) -> Tuple[List[Task], Optional[str]]:
"""
解析PRD文档内容,提取任务和依赖关系
Args:
content: PRD文档内容
Returns:
Tuple[List[Task], Optional[str]]: 提取的任务列表和LLM解析错误信息
"""
# 优先使用LLM进行解析
# 发生错误时回退到基础解析方法
async def parse_with_llm(self, prd_content: str) -> List[Task]:
"""
使用LLM客户端解析PRD文档
Args:
prd_content: PRD文档内容
Returns:
List[Task]: 提取并转换为Task对象的任务列表
"""
# 第一次LLM调用提取任务
# 第二次LLM调用分析依赖关系
# 创建任务对象并建立依赖关系
3.3 LLM接口实现 (src/llm)
LLM集成模块提供了与大语言模型交互的抽象接口和具体实现:
3.3.1 LLM接口基类 (src/llm/base.py)
from abc import ABC, abstractmethod
from typing import Any, Dict, Optional, List
import logging
class LLMInterface(ABC):
"""大语言模型调用的抽象基类接口"""
@abstractmethod
async def generate_text_async(
self,
prompt: str,
temperature: float = 0.7,
max_tokens: Optional[int] = None,
**kwargs: Any
) -> str:
"""生成文本内容 (异步)"""
pass
@abstractmethod
async def generate_structured_content_async(
self,
prompt: str,
schema: Dict[str, Any],
temperature: float = 0.1,
**kwargs: Any
) -> Any:
"""根据提供的Schema生成结构化内容 (异步)"""
pass
@abstractmethod
async def parse_prd_to_tasks_async(
self,
prd_content: str,
**kwargs: Any
) -> List[Dict[str, Any]]:
"""解析PRD文档内容,提取结构化的任务列表 (异步)"""
pass
@abstractmethod
async def generate_subtasks_for_task_async(
self,
task_info: Dict[str, Any],
num_subtasks: int = 5,
temperature: float = 0.2,
**kwargs: Any
) -> List[Dict[str, Any]]:
"""为指定的任务生成子任务列表 (异步)"""
pass
3.3.2 Gemini实现 (src/llm/gemini.py)
import os
import json
import logging
from typing import Any, Dict, Optional, List
import google.generativeai as genai
from google.generativeai.types import GenerationConfig
from google.api_core.exceptions import GoogleAPIError
from .base import LLMInterface
class GeminiLLM(LLMInterface):
"""Google Gemini LLM implementation using the google-generativeai SDK."""
def __init__(self, api_key: Optional[str] = None, config: Optional[Dict[str, Any]] = None):
"""初始化Gemini LLM客户端"""
# 配置Gemini SDK和模型
async def generate_text_async(self, prompt: str, temperature: float = 0.7,
max_tokens: Optional[int] = None, **kwargs: Any) -> str:
"""生成文本内容"""
# 实现文本生成
async def generate_structured_content_async(self, prompt: str, schema: Dict[str, Any],
temperature: float = 0.1, **kwargs: Any) -> Any:
"""生成结构化内容"""
# 实现结构化内容生成
async def parse_prd_to_tasks_async(self, prd_content: str, **kwargs: Any) -> List[Dict[str, Any]]:
"""解析PRD文档为任务列表"""
# 构建PRD解析的提示词
# 定义输出模式
# 调用结构化内容生成
async def generate_subtasks_for_task_async(self, task_info: Dict[str, Any],
num_subtasks: int = 5, **kwargs: Any) -> List[Dict[str, Any]]:
"""为任务生成子任务"""
# 实现子任务生成
4. 测试策略
4.1 单元测试
为核心功能编写单元测试,特别是:
- 任务依赖关系检查和循环依赖检测
- 任务状态转换和状态同步
- get_next_executable_task 优先级排序逻辑
# tests/test_task_service.py 示例
import unittest
from unittest.mock import MagicMock, patch
from src.storage.task_storage import TaskStorage
from src.services.task_service import TaskService
from src.models.task import Task, TaskStatus, TaskPriority
class TestTaskService(unittest.TestCase):
def setUp(self):
"""测试前准备"""
self.storage = TaskStorage()
self.service = TaskService(storage=self.storage)
def test_get_next_executable_task(self):
"""测试获取下一个可执行任务的逻辑"""
# 创建测试任务
task1 = self.storage.create_task(id="1", name="Task 1", priority=TaskPriority.HIGH)
task2 = self.storage.create_task(id="2", name="Task 2", priority=TaskPriority.MEDIUM)
task3 = self.storage.create_task(id="3", name="Task 3", priority=TaskPriority.HIGH)
# 设置依赖关系
self.storage.set_task_dependency("2", "1") # Task 2 依赖于 Task 1
# 测试返回优先级高的没有依赖的任务
result = self.service.get_next_executable_task()
self.assertEqual(result["task"]["id"], "3") # Task 3 优先级高且无依赖
# 将任务3标记为进行中
self.storage.update_task("3", status=TaskStatus.IN_PROGRESS)
# 测试返回进行中的任务
result = self.service.get_next_executable_task()
self.assertEqual(result["task"]["id"], "3") # 进行中的任务优先
# 将任务3标记为完成
self.storage.update_task("3", status=TaskStatus.DONE)
# 测试返回无依赖且优先级高的任务
result = self.service.get_next_executable_task()
self.assertEqual(result["task"]["id"], "1") # Task 1 无依赖可执行
# 将任务1标记为完成
self.storage.update_task("1", status=TaskStatus.DONE)
# 测试依赖已满足的任务
result = self.service.get_next_executable_task()
self.assertEqual(result["task"]["id"], "2") # Task 2 依赖已满足
4.2 集成测试
集成测试主要测试PRD解析和LLM集成的端到端流程:
# tests/test_prd_parser_integration.py 示例
import asyncio
import unittest
from unittest.mock import MagicMock, patch
from src.storage.task_storage import TaskStorage
from src.services.prd_parser import PrdParser
from src.llm.gemini import GeminiLLM
class TestPrdParserIntegration(unittest.TestCase):
def setUp(self):
"""测试前准备"""
self.storage = TaskStorage()
self.mock_llm = MagicMock(spec=GeminiLLM)
self.parser = PrdParser(storage=self.storage, llm_client=self.mock_llm)
def test_prd_parsing_with_llm(self):
"""测试使用LLM解析PRD"""
# 模拟LLM返回的任务数据
mock_tasks = [
{"id": "1", "name": "任务1", "description": "描述1", "priority": "high"},
{"id": "2", "name": "任务2", "description": "描述2", "priority": "medium"}
]
# 模拟依赖分析结果
mock_dependencies = [
{"task_id": "2", "depends_on_id": "1"}
]
# 设置模拟函数的返回值
async def mock_parse_prd(*args, **kwargs):
return mock_tasks
async def mock_generate_structured(*args, **kwargs):
return mock_dependencies
self.mock_llm.parse_prd_to_tasks_async.side_effect = mock_parse_prd
self.mock_llm.generate_structured_content_async.side_effect = mock_generate_structured
# 执行测试
sample_prd = "# 标题1\n## 子标题1\n# 标题2"
tasks, error = asyncio.run(self.parser.parse(sample_prd))
# 验证结果
self.assertEqual(len(tasks), 2)
self.assertIsNone(error)
self.assertEqual(tasks[0].id, "1")
self.assertEqual(tasks[0].name, "任务1")
self.assertEqual(tasks[1].id, "2")
# 验证依赖关系
task2 = self.storage.get_task("2")
self.assertEqual(task2.dependencies, {"1"})
5. 部署指南
5.1 环境变量配置
服务运行需要以下环境变量:
# LLM配置
export GEMINI_API_KEY="your-api-key" # Gemini API密钥
export MODEL_NAME="gemini-1.5-flash" # 使用的模型
export LLM_PROVIDER="gemini" # LLM提供商 (gemini 或 openai)
# 代理设置(可选)
export HTTP_PROXY="http://127.0.0.1:7890" # HTTP代理
export HTTPS_PROXY="http://127.0.0.1:7890" # HTTPS代理
# 输出目录(可选)
export MCP_OUTPUT_DIR="/path/to/output" # 输出文件保存路径
5.2 使用 uv 运行
推荐使用 uv 运行服务,支持基于 PEP 735 的子解释器隔离:
# 安装依赖
pip install uv
# 运行服务
uv run --with fastmcp fastmcp run /path/to/server.py
5.3 Docker部署
FROM python:3.10-slim
WORKDIR /app
# 安装UV
RUN pip install uv
# 拷贝项目文件
COPY requirements.txt .
COPY src/ src/
COPY docs/ docs/
# 创建输出目录
RUN mkdir -p /app/output/tasks /app/output/md /app/output/logs
# 设置环境变量
ENV PYTHONPATH=/app
ENV MCP_OUTPUT_DIR=/app/output
# 暴露端口
EXPOSE 8000
# 使用UV运行服务
CMD ["uv", "run", "--with", "fastmcp", "fastmcp", "run", "/app/src/server.py"]
5.4 运行命令
# 构建镜像
docker build -t task-manager-mcp .
# 运行容器
docker run -d \
-p 8000:8000 \
-e GEMINI_API_KEY=your-api-key \
-e MODEL_NAME=gemini-1.5-flash \
-e LLM_PROVIDER=gemini \
--name task-manager-mcp \
task-manager-mcp