Przeglądaj źródła

新增task-manager MCP服务器

maxiaolong 1 tydzień temu
rodzic
commit
561521199b
41 zmienionych plików z 1817 dodań i 0 usunięć
  1. 9 0
      .cursor/mcp.json
  2. 3 0
      BUSINESS_RULES.md
  3. 3 0
      CHECK_API_DIAGNOSTIC_REPORT.md
  4. 3 0
      FIELD_STANDARDIZATION_REPORT.md
  5. 3 0
      IMPLEMENTATION_CHECKLIST.md
  6. 3 0
      IMPLEMENTATION_SUMMARY.md
  7. 3 0
      NEO4J_FIELD_STANDARDIZATION_SUMMARY.md
  8. 3 0
      README_METRIC_CHECK.md
  9. 3 0
      REMOVAL_SUMMARY_CLEAN_LIST.md
  10. 3 0
      TEST_REPORT_218.md
  11. 47 0
      database/create_task_list_table.sql
  12. 3 0
      docs/diagrams/metric-check-flow.md
  13. 3 0
      docs/examples/metric-check-examples.md
  14. 3 0
      docs/features/metric-formula-check.md
  15. 3 0
      docs/n8n_chat_trigger_error_diagnosis.md
  16. 3 0
      docs/n8n_chat_workflow_quickstart.md
  17. 3 0
      docs/n8n_deepseek_upgrade.md
  18. 3 0
      docs/n8n_improved_workflow_design.md
  19. 3 0
      docs/n8n_internal_error_fix.md
  20. 3 0
      docs/n8n_tools_added_status.md
  21. 3 0
      docs/n8n_workflow_data_governance.md
  22. 3 0
      docs/n8n_workflow_enhancement_summary.md
  23. 3 0
      docs/n8n_workflow_test_report.md
  24. 3 0
      docs/n8n_workflow_test_success.md
  25. 3 0
      explore_api_218.py
  26. 5 0
      mcp-servers/task-manager/.gitignore
  27. 243 0
      mcp-servers/task-manager/README.md
  28. 6 0
      mcp-servers/task-manager/config.json
  29. 230 0
      mcp-servers/task-manager/database.js
  30. 42 0
      mcp-servers/task-manager/get_config.py
  31. 533 0
      mcp-servers/task-manager/index.js
  32. 133 0
      mcp-servers/task-manager/logger.js
  33. 318 0
      mcp-servers/task-manager/package-lock.json
  34. 26 0
      mcp-servers/task-manager/package.json
  35. 138 0
      mcp-servers/task-manager/task-processor.js
  36. 3 0
      test_check_218.py
  37. 3 0
      test_check_api.py
  38. 3 0
      test_check_interface_only.py
  39. 3 0
      test_metadata_workflow.py
  40. 3 0
      tests/test_metric_check.py
  41. 3 0
      verify_check_api.md

+ 9 - 0
.cursor/mcp.json

@@ -10,6 +10,15 @@
         "N8N_API_URL": "https://n8n.citupro.com",
         "N8N_API_KEY": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkODgwYjljNS1jZjJmLTRhZDUtYjQ0NS1kYzNjMTQyZGU1NTMiLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzYxNzk1MDQzfQ.--JirJJvWqva7tvIpyHfwmvAubqlXxY2QWsfuJCXr48"
       }
+    },
+    "task-manager": {
+      "command": "node",
+      "args": ["mcp-servers/task-manager/index.js"],
+      "env": {
+        "POLL_INTERVAL": "300000",
+        "LOG_LEVEL": "info",
+        "AUTO_START_POLLING": "true"
+      }
     }
   }
 }

+ 3 - 0
BUSINESS_RULES.md

@@ -329,3 +329,6 @@ LOG_FORMAT = '%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(line
 
 
 
+
+
+

+ 3 - 0
CHECK_API_DIAGNOSTIC_REPORT.md

@@ -306,3 +306,6 @@ Host: 192.168.3.143:5000
 
 
 
+
+
+

+ 3 - 0
FIELD_STANDARDIZATION_REPORT.md

@@ -247,3 +247,6 @@ n.en_name as en_name → n.name_en as en_name
 
 
 
+
+
+

+ 3 - 0
IMPLEMENTATION_CHECKLIST.md

@@ -331,3 +331,6 @@
 
 
 
+
+
+

+ 3 - 0
IMPLEMENTATION_SUMMARY.md

@@ -361,3 +361,6 @@ python -m pytest tests/test_metric_check.py --cov=app.core.data_metric --cov-rep
 
 
 
+
+
+

+ 3 - 0
NEO4J_FIELD_STANDARDIZATION_SUMMARY.md

@@ -342,3 +342,6 @@ git commit -m "refactor: 统一Neo4j字段命名规范
 
 
 
+
+
+

+ 3 - 0
README_METRIC_CHECK.md

@@ -279,3 +279,6 @@ python -m pytest tests/test_metric_check.py --cov=app.core.data_metric --cov-rep
 
 
 
+
+
+

+ 3 - 0
REMOVAL_SUMMARY_CLEAN_LIST.md

@@ -256,3 +256,6 @@ git commit -m "refactor: 移除 clean-list 接口及相关代码
 
 
 
+
+
+

+ 3 - 0
TEST_REPORT_218.md

@@ -298,3 +298,6 @@ python test_check_218.py
 
 
 
+
+
+

+ 47 - 0
database/create_task_list_table.sql

@@ -0,0 +1,47 @@
+-- 创建任务列表表
+-- 用于存储需要开发的任务列表
+
+CREATE TABLE IF NOT EXISTS public.task_list (
+    task_id SERIAL PRIMARY KEY,
+    task_name VARCHAR(255) NOT NULL,
+    task_description TEXT NOT NULL,
+    status VARCHAR(20) NOT NULL DEFAULT 'pending',
+    code_name VARCHAR(255),
+    code_path VARCHAR(500),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    create_by VARCHAR(100) NOT NULL
+);
+
+-- 添加表注释
+COMMENT ON TABLE public.task_list IS '任务列表表,用于存储需要开发的任务信息';
+COMMENT ON COLUMN public.task_list.task_id IS '任务ID,主键,自增';
+COMMENT ON COLUMN public.task_list.task_name IS '任务名称';
+COMMENT ON COLUMN public.task_list.task_description IS '任务描述,markdown格式';
+COMMENT ON COLUMN public.task_list.status IS '任务状态:pending(待处理), processing(处理中), completed(已完成), failed(失败)';
+COMMENT ON COLUMN public.task_list.code_name IS '生成的代码文件名';
+COMMENT ON COLUMN public.task_list.code_path IS '代码文件路径';
+COMMENT ON COLUMN public.task_list.create_time IS '创建时间';
+COMMENT ON COLUMN public.task_list.update_time IS '更新时间';
+COMMENT ON COLUMN public.task_list.create_by IS '创建者';
+
+-- 创建索引以提高查询性能
+CREATE INDEX IF NOT EXISTS idx_task_list_status ON public.task_list(status);
+CREATE INDEX IF NOT EXISTS idx_task_list_create_time ON public.task_list(create_time);
+
+-- 创建更新时间自动更新的触发器函数
+CREATE OR REPLACE FUNCTION update_task_list_update_time()
+RETURNS TRIGGER AS $$
+BEGIN
+    NEW.update_time = CURRENT_TIMESTAMP;
+    RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+-- 创建触发器
+DROP TRIGGER IF EXISTS trigger_update_task_list_time ON public.task_list;
+CREATE TRIGGER trigger_update_task_list_time
+    BEFORE UPDATE ON public.task_list
+    FOR EACH ROW
+    EXECUTE FUNCTION update_task_list_update_time();
+

+ 3 - 0
docs/diagrams/metric-check-flow.md

@@ -366,3 +366,6 @@
 
 
 
+
+
+

+ 3 - 0
docs/examples/metric-check-examples.md

@@ -572,3 +572,6 @@ if __name__ == '__main__':
 
 
 
+
+
+

+ 3 - 0
docs/features/metric-formula-check.md

@@ -250,3 +250,6 @@ python -m pytest tests/test_metric_check.py -v
 
 
 
+
+
+

+ 3 - 0
docs/n8n_chat_trigger_error_diagnosis.md

@@ -375,3 +375,6 @@ pm2 logs n8n | grep -i "error\|chat\|webhook"
 
 
 
+
+
+

+ 3 - 0
docs/n8n_chat_workflow_quickstart.md

@@ -378,3 +378,6 @@ export default DataGovernanceChat;
 
 
 
+
+
+

+ 3 - 0
docs/n8n_deepseek_upgrade.md

@@ -251,3 +251,6 @@ DeepSeek Chat Model → AI Agent (ai_languageModel 连接)
 
 
 
+
+
+

+ 3 - 0
docs/n8n_improved_workflow_design.md

@@ -462,3 +462,6 @@ def add_metadata():
 
 
 
+
+
+

+ 3 - 0
docs/n8n_internal_error_fix.md

@@ -376,3 +376,6 @@ Chat Trigger → Set (静态消息) → 返回
 
 
 
+
+
+

+ 3 - 0
docs/n8n_tools_added_status.md

@@ -309,3 +309,6 @@ AI: "✅ 元数据创建成功!
 
 
 
+
+
+

+ 3 - 0
docs/n8n_workflow_data_governance.md

@@ -338,3 +338,6 @@
 
 
 
+
+
+

+ 3 - 0
docs/n8n_workflow_enhancement_summary.md

@@ -488,3 +488,6 @@ AI Agent 处理响应
 
 
 
+
+
+

+ 3 - 0
docs/n8n_workflow_test_report.md

@@ -456,3 +456,6 @@ curl -X POST http://localhost:5000/api/meta/add \
 
 
 
+
+
+

+ 3 - 0
docs/n8n_workflow_test_success.md

@@ -321,3 +321,6 @@ AI:   感谢您提供的信息!我已经收到了您要创建的元数据详
 
 
 
+
+
+

+ 3 - 0
explore_api_218.py

@@ -75,3 +75,6 @@ print("="*70)
 
 
 
+
+
+

+ 5 - 0
mcp-servers/task-manager/.gitignore

@@ -0,0 +1,5 @@
+node_modules/
+.env
+*.log
+.DS_Store
+

+ 243 - 0
mcp-servers/task-manager/README.md

@@ -0,0 +1,243 @@
+# Task Manager MCP Server
+
+任务管理MCP服务器,用于从PostgreSQL数据库读取任务,通过MCP协议与Cursor交互执行代码开发任务。
+
+## 功能特性
+
+- 从PostgreSQL数据库的`task_list`表读取待处理任务
+- 通过MCP协议将任务发送给Cursor执行代码开发
+- 自动更新任务状态(pending → processing → completed/failed)
+- 支持自动轮询,每5分钟检查一次新任务
+- 完整的错误处理和日志记录
+
+## 安装
+
+1. 安装依赖:
+
+```bash
+cd mcp-servers/task-manager
+npm install
+```
+
+2. 创建数据库表:
+
+```bash
+# 在PostgreSQL数据库中执行
+psql -U postgres -d dataops -f ../../database/create_task_list_table.sql
+```
+
+3. 数据库配置:
+
+**重要**:MCP服务器从独立的配置文件`mcp-servers/task-manager/config.json`中读取数据库URI。
+
+编辑`mcp-servers/task-manager/config.json`文件,设置正确的PostgreSQL连接URI:
+
+```json
+{
+  "database": {
+    "uri": "postgresql://postgres:dataOps@192.168.3.143:5432/dataops"
+  }
+}
+```
+
+如果需要覆盖,可以在`.cursor/mcp.json`中设置`DATABASE_URL`环境变量。
+
+其他环境变量配置(在`.cursor/mcp.json`中):
+```
+POLL_INTERVAL=300000  # 轮询间隔(毫秒),默认5分钟
+LOG_LEVEL=info        # 日志级别
+AUTO_START_POLLING=true  # 自动启动轮询
+```
+
+## 配置Cursor
+
+MCP服务器已配置在`.cursor/mcp.json`中。确保:
+
+1. `mcp-servers/task-manager/config.json`中配置了正确的PostgreSQL数据库URI
+2. Node.js版本 >= 18.0.0
+3. 已安装所有依赖
+
+## 使用方法
+
+### 1. 创建任务
+
+在PostgreSQL数据库中插入任务记录:
+
+```sql
+INSERT INTO task_list (task_name, task_description, status, create_by)
+VALUES (
+    '开发用户登录功能',
+    '# 任务描述\n\n开发一个用户登录功能,包括:\n\n1. 用户认证API\n2. JWT token生成\n3. 密码加密存储\n\n## 数据结构\n\n- users表:id, username, password, email',
+    'pending',
+    'admin'
+);
+```
+
+### 2. 通过Cursor执行任务
+
+在Cursor中,可以使用以下MCP工具:
+
+#### 获取待处理任务列表
+
+```
+调用工具: get_pending_tasks
+```
+
+#### 执行单个任务
+
+```
+调用工具: execute_task
+参数: { "task_id": 1 }
+```
+
+#### 批量处理所有任务
+
+```
+调用工具: process_all_tasks
+参数: { "auto_poll": true }  // 启用自动轮询
+```
+
+#### 更新任务状态
+
+```
+调用工具: update_task_status
+参数: {
+    "task_id": 1,
+    "status": "completed",
+    "code_name": "auth.py",
+    "code_path": "app/core/auth.py"
+}
+```
+
+### 3. 自动轮询
+
+当调用`process_all_tasks`并设置`auto_poll: true`时,服务器会:
+
+1. 处理所有待处理任务
+2. 每5分钟自动检查一次新任务
+3. 发现新任务时自动处理
+
+## 任务状态
+
+- `pending` - 待处理
+- `processing` - 处理中
+- `completed` - 已完成
+- `failed` - 失败
+
+## 数据库表结构
+
+```sql
+CREATE TABLE task_list (
+    task_id SERIAL PRIMARY KEY,
+    task_name VARCHAR(255) NOT NULL,
+    task_description TEXT NOT NULL,
+    status VARCHAR(20) NOT NULL DEFAULT 'pending',
+    code_name VARCHAR(255),
+    code_path VARCHAR(500),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    create_by VARCHAR(100) NOT NULL
+);
+```
+
+## 任务描述格式
+
+任务描述应为markdown格式,包含:
+
+1. **功能要求**:详细描述需要开发的功能
+2. **数据结构**:描述涉及的数据表结构或数据格式
+3. **技术要求**:如有特殊技术要求,应明确说明
+
+示例:
+
+```markdown
+# 开发用户登录功能
+
+## 功能要求
+
+开发一个用户登录功能,包括:
+
+1. 用户认证API端点
+2. JWT token生成和验证
+3. 密码加密存储(使用bcrypt)
+
+## 数据结构
+
+users表结构:
+- id: INTEGER PRIMARY KEY
+- username: VARCHAR(50) UNIQUE
+- password: VARCHAR(255)  # 加密后的密码
+- email: VARCHAR(100)
+- created_at: TIMESTAMP
+
+## API端点
+
+POST /api/auth/login
+请求体: { "username": "string", "password": "string" }
+响应: { "token": "string", "user": {...} }
+```
+
+## 工作流程
+
+1. **创建任务**:在数据库中插入任务记录,状态为`pending`
+2. **获取任务**:MCP服务器从数据库读取`pending`状态的任务
+3. **执行任务**:通过MCP协议将任务描述发送给Cursor
+4. **开发代码**:Cursor根据任务描述开发代码
+5. **更新状态**:任务完成后,更新状态为`completed`,并记录代码文件名和路径
+6. **轮询检查**:如果启用自动轮询,每5分钟检查一次新任务
+
+## 故障处理
+
+### 任务执行失败
+
+如果任务执行失败,状态会更新为`failed`。可以:
+
+1. 检查任务描述是否清晰完整
+2. 查看错误日志
+3. 修改任务描述后,将状态改回`pending`重新处理
+
+### 数据库连接失败
+
+确保:
+
+1. PostgreSQL服务正在运行
+2. `mcp-servers/task-manager/config.json`中的`database.uri`配置正确
+3. 数据库用户有足够的权限
+4. 如果使用环境变量,确保`DATABASE_URL`或`SQLALCHEMY_DATABASE_URI`已正确设置
+
+### MCP服务器无法启动
+
+检查:
+
+1. Node.js版本 >= 18.0.0
+2. 所有依赖已安装:`npm install`
+3. 文件权限正确
+
+## 开发说明
+
+### 项目结构
+
+```
+mcp-servers/task-manager/
+├── index.js              # MCP服务器主文件
+├── database.js           # 数据库操作模块
+├── task-processor.js     # 任务处理逻辑
+├── config.json           # 数据库配置文件(独立配置)
+├── package.json          # 项目配置
+└── README.md            # 本文档
+```
+
+### 扩展功能
+
+可以扩展的功能:
+
+1. 任务优先级支持
+2. 任务依赖关系
+3. 任务执行历史记录
+4. 任务执行时间统计
+5. 邮件/通知功能
+
+## 许可证
+
+MIT
+

+ 6 - 0
mcp-servers/task-manager/config.json

@@ -0,0 +1,6 @@
+{
+  "database": {
+    "uri": "postgresql://postgres:dataOps@192.168.3.143:5432/dataops"
+  }
+}
+

+ 230 - 0
mcp-servers/task-manager/database.js

@@ -0,0 +1,230 @@
+import pg from 'pg';
+import dotenv from 'dotenv';
+import { readFileSync } from 'fs';
+import { fileURLToPath } from 'url';
+import { dirname, join } from 'path';
+import { info, error } from './logger.js';
+
+dotenv.config();
+
+const { Pool } = pg;
+
+let pool = null;
+
+/**
+ * 从task-manager独立配置文件中读取数据库URI
+ */
+function getDatabaseUrlFromConfig() {
+    try {
+        // 获取当前文件的目录
+        const __filename = fileURLToPath(import.meta.url);
+        const __dirname = dirname(__filename);
+        
+        // 配置文件路径
+        const configPath = join(__dirname, 'config.json');
+        
+        // 读取配置文件
+        const configFile = readFileSync(configPath, 'utf-8');
+        const config = JSON.parse(configFile);
+        
+        // 获取数据库URI
+        const databaseUrl = config?.database?.uri;
+        
+        if (databaseUrl) {
+            info('Database URL loaded from task-manager config.json: ' + databaseUrl.replace(/:[^:@]+@/, ':****@'));
+            return databaseUrl;
+        } else {
+            throw new Error('database.uri not found in config.json');
+        }
+    } catch (err) {
+        error('Error reading config.json: ' + err.message);
+        // 如果读取失败,回退到环境变量
+        return null;
+    }
+}
+
+/**
+ * 初始化数据库连接池
+ */
+function initPool() {
+    if (pool) {
+        return pool;
+    }
+
+    // 优先从task-manager独立配置文件读取
+    let databaseUrl = getDatabaseUrlFromConfig();
+    
+    // 如果配置文件读取失败,尝试从环境变量读取
+    if (!databaseUrl) {
+        databaseUrl = process.env.DATABASE_URL || process.env.SQLALCHEMY_DATABASE_URI;
+        if (databaseUrl) {
+            info('Database URL loaded from environment variable');
+        }
+    }
+    
+    if (!databaseUrl) {
+        throw new Error('Database URL not found. Please check mcp-servers/task-manager/config.json or set DATABASE_URL environment variable');
+    }
+
+    // 记录实际使用的数据库URI(隐藏密码)
+    const maskedUrl = databaseUrl.replace(/:[^:@]+@/, ':****@');
+    info('Initializing database pool with URI: ' + maskedUrl);
+
+    pool = new Pool({
+        connectionString: databaseUrl,
+        max: 10,
+        idleTimeoutMillis: 30000,
+        connectionTimeoutMillis: 2000,
+    });
+
+    pool.on('error', (err) => {
+        error('Unexpected error on idle client: ' + err.message);
+    });
+
+    return pool;
+}
+
+/**
+ * 获取数据库连接池
+ */
+export function getPool() {
+    if (!pool) {
+        return initPool();
+    }
+    return pool;
+}
+
+/**
+ * 获取所有待处理的任务
+ */
+export async function getPendingTasks() {
+    info('[Task Manager] Starting to read task_list table...');
+    const startTime = Date.now();
+    
+    const client = await getPool().connect();
+    try {
+        const result = await client.query(
+            `SELECT task_id, task_name, task_description, status, code_name, code_path, 
+                    create_time, update_time, create_by
+             FROM task_list 
+             WHERE status = 'pending' 
+             ORDER BY create_time ASC`
+        );
+        
+        const taskCount = result.rows.length;
+        const duration = Date.now() - startTime;
+        info(`[Task Manager] Finished reading task_list table. Found ${taskCount} pending task(s) in ${duration}ms`);
+        
+        return result.rows;
+    } finally {
+        client.release();
+    }
+}
+
+/**
+ * 根据ID获取任务
+ */
+export async function getTaskById(taskId) {
+    const client = await getPool().connect();
+    try {
+        const result = await client.query(
+            `SELECT task_id, task_name, task_description, status, code_name, code_path, 
+                    create_time, update_time, create_by
+             FROM task_list 
+             WHERE task_id = $1`,
+            [taskId]
+        );
+        return result.rows[0] || null;
+    } finally {
+        client.release();
+    }
+}
+
+/**
+ * 更新任务状态
+ */
+export async function updateTaskStatus(taskId, status, codeName = null, codePath = null) {
+    const client = await getPool().connect();
+    try {
+        const updates = ['status = $1', 'update_time = CURRENT_TIMESTAMP'];
+        const values = [status];
+        let paramIndex = 1;
+
+        if (codeName !== null) {
+            paramIndex++;
+            updates.push(`code_name = $${paramIndex}`);
+            values.push(codeName);
+        }
+
+        if (codePath !== null) {
+            paramIndex++;
+            updates.push(`code_path = $${paramIndex}`);
+            values.push(codePath);
+        }
+
+        // taskId is always the last parameter
+        paramIndex++;
+        values.push(taskId);
+
+        const query = `
+            UPDATE task_list 
+            SET ${updates.join(', ')}
+            WHERE task_id = $${paramIndex}
+            RETURNING task_id, task_name, status, update_time
+        `;
+
+        const result = await client.query(query, values);
+        return result.rows[0];
+    } finally {
+        client.release();
+    }
+}
+
+/**
+ * 创建新任务
+ */
+export async function createTask(taskName, taskDescription, createBy) {
+    const client = await getPool().connect();
+    try {
+        const result = await client.query(
+            `INSERT INTO task_list (task_name, task_description, status, create_by)
+             VALUES ($1, $2, 'pending', $3)
+             RETURNING task_id, task_name, task_description, status, create_time, create_by`,
+            [taskName, taskDescription, createBy]
+        );
+        return result.rows[0];
+    } finally {
+        client.release();
+    }
+}
+
+/**
+ * 获取所有任务(用于调试)
+ */
+export async function getAllTasks(limit = 100) {
+    const client = await getPool().connect();
+    try {
+        const result = await client.query(
+            `SELECT task_id, task_name, task_description, status, code_name, code_path, 
+                    create_time, update_time, create_by
+             FROM task_list 
+             ORDER BY create_time DESC
+             LIMIT $1`,
+            [limit]
+        );
+        return result.rows;
+    } finally {
+        client.release();
+    }
+}
+
+/**
+ * 关闭数据库连接池
+ */
+export async function closePool() {
+    if (pool) {
+        await pool.end();
+        pool = null;
+    }
+}
+

+ 42 - 0
mcp-servers/task-manager/get_config.py

@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+"""
+读取Flask配置文件中的production配置
+用于MCP服务器获取数据库连接信息
+"""
+import sys
+import os
+import json
+
+# 添加项目根目录到Python路径
+project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))
+sys.path.insert(0, project_root)
+
+try:
+    from app.config.config import config
+    
+    # 从config字典中获取production配置类(注意:这是类,不是实例)
+    ProductionConfig = config['production']
+    
+    # 直接访问类的属性(类属性,不需要实例化)
+    database_url = ProductionConfig.SQLALCHEMY_DATABASE_URI
+    
+    # 输出数据库URI
+    result = {
+        'database_url': database_url,
+        'status': 'success'
+    }
+    
+    print(json.dumps(result))
+    
+except Exception as e:
+    # 如果读取失败,返回错误信息
+    result = {
+        'status': 'error',
+        'error': str(e),
+        'database_url': None
+    }
+    print(json.dumps(result))
+    sys.exit(1)
+
+
+

+ 533 - 0
mcp-servers/task-manager/index.js

@@ -0,0 +1,533 @@
+#!/usr/bin/env node
+
+import { Server } from '@modelcontextprotocol/sdk/server/index.js';
+import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
+import {
+    CallToolRequestSchema,
+    ListToolsRequestSchema,
+} from '@modelcontextprotocol/sdk/types.js';
+import {
+    getPendingTasks,
+    getTaskById,
+    updateTaskStatus,
+    createTask,
+    getAllTasks,
+    closePool,
+} from './database.js';
+import { processTask } from './task-processor.js';
+import { info, error, setMcpServer } from './logger.js';
+
+/**
+ * Task Manager MCP Server
+ * 
+ * 提供任务管理功能,从PostgreSQL数据库读取任务,
+ * 通过MCP协议与Cursor交互执行代码开发任务。
+ */
+
+class TaskManagerServer {
+    constructor() {
+        this.server = new Server(
+            {
+                name: 'task-manager',
+                version: '1.0.0',
+            },
+            {
+                capabilities: {
+                    tools: {},
+                    logging: {}, // 启用日志通知功能
+                },
+            }
+        );
+
+        this.setupHandlers();
+        this.pollingInterval = null;
+        this.isProcessing = false;
+        
+        // 将 Server 实例传递给 logger,以便通过 MCP 协议发送日志
+        setMcpServer(this.server);
+    }
+
+    setupHandlers() {
+        // 列出所有可用工具
+        this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
+            tools: [
+                {
+                    name: 'get_pending_tasks',
+                    description: '获取所有待处理的任务列表',
+                    inputSchema: {
+                        type: 'object',
+                        properties: {},
+                    },
+                },
+                {
+                    name: 'get_task_by_id',
+                    description: '根据任务ID获取任务详情',
+                    inputSchema: {
+                        type: 'object',
+                        properties: {
+                            task_id: {
+                                type: 'number',
+                                description: '任务ID',
+                            },
+                        },
+                        required: ['task_id'],
+                    },
+                },
+                {
+                    name: 'execute_task',
+                    description: '执行单个任务,将任务描述发送给Cursor进行代码开发',
+                    inputSchema: {
+                        type: 'object',
+                        properties: {
+                            task_id: {
+                                type: 'number',
+                                description: '要执行的任务ID',
+                            },
+                        },
+                        required: ['task_id'],
+                    },
+                },
+                {
+                    name: 'update_task_status',
+                    description: '更新任务状态',
+                    inputSchema: {
+                        type: 'object',
+                        properties: {
+                            task_id: {
+                                type: 'number',
+                                description: '任务ID',
+                            },
+                            status: {
+                                type: 'string',
+                                enum: ['pending', 'processing', 'completed', 'failed'],
+                                description: '新状态',
+                            },
+                            code_name: {
+                                type: 'string',
+                                description: '生成的代码文件名(可选)',
+                            },
+                            code_path: {
+                                type: 'string',
+                                description: '代码文件路径(可选)',
+                            },
+                        },
+                        required: ['task_id', 'status'],
+                    },
+                },
+                {
+                    name: 'process_all_tasks',
+                    description: '批量处理所有待处理的任务',
+                    inputSchema: {
+                        type: 'object',
+                        properties: {
+                            auto_poll: {
+                                type: 'boolean',
+                                description: '是否启用自动轮询(每5分钟检查一次新任务)',
+                                default: false,
+                            },
+                        },
+                    },
+                },
+                {
+                    name: 'create_task',
+                    description: '创建新任务',
+                    inputSchema: {
+                        type: 'object',
+                        properties: {
+                            task_name: {
+                                type: 'string',
+                                description: '任务名称',
+                            },
+                            task_description: {
+                                type: 'string',
+                                description: '任务描述(markdown格式)',
+                            },
+                            create_by: {
+                                type: 'string',
+                                description: '创建者',
+                            },
+                        },
+                        required: ['task_name', 'task_description', 'create_by'],
+                    },
+                },
+                {
+                    name: 'get_all_tasks',
+                    description: '获取所有任务(用于调试)',
+                    inputSchema: {
+                        type: 'object',
+                        properties: {
+                            limit: {
+                                type: 'number',
+                                description: '返回的任务数量限制',
+                                default: 100,
+                            },
+                        },
+                    },
+                },
+            ],
+        }));
+
+        // 处理工具调用
+        this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
+            const { name, arguments: args } = request.params;
+
+            try {
+                switch (name) {
+                    case 'get_pending_tasks': {
+                        const tasks = await getPendingTasks();
+                        return {
+                            content: [
+                                {
+                                    type: 'text',
+                                    text: JSON.stringify(tasks, null, 2),
+                                },
+                            ],
+                        };
+                    }
+
+                    case 'get_task_by_id': {
+                        const task = await getTaskById(args.task_id);
+                        if (!task) {
+                            return {
+                                content: [
+                                    {
+                                        type: 'text',
+                                        text: `Task with ID ${args.task_id} not found`,
+                                    },
+                                ],
+                                isError: true,
+                            };
+                        }
+                        return {
+                            content: [
+                                {
+                                    type: 'text',
+                                    text: JSON.stringify(task, null, 2),
+                                },
+                            ],
+                        };
+                    }
+
+                    case 'execute_task': {
+                        info(`[Task Manager] Executing task ${args.task_id}...`);
+                        const task = await getTaskById(args.task_id);
+                        if (!task) {
+                            error(`[Task Manager] Task ${args.task_id} not found`);
+                            return {
+                                content: [
+                                    {
+                                        type: 'text',
+                                        text: `Task with ID ${args.task_id} not found`,
+                                    },
+                                ],
+                                isError: true,
+                            };
+                        }
+
+                        if (task.status !== 'pending') {
+                            error(`[Task Manager] Task ${args.task_id} is not in pending status. Current status: ${task.status}`);
+                            return {
+                                content: [
+                                    {
+                                        type: 'text',
+                                        text: `Task ${args.task_id} is not in pending status. Current status: ${task.status}`,
+                                    },
+                                ],
+                                isError: true,
+                            };
+                        }
+
+                        // 更新状态为处理中
+                        await updateTaskStatus(args.task_id, 'processing');
+                        info(`[Task Manager] Task ${args.task_id} status updated to 'processing'`);
+
+                        // 处理任务
+                        const result = await processTask(task);
+                        
+                        if (result.success) {
+                            info(`[Task Manager] Task ${args.task_id} executed successfully. Code: ${result.code_name || 'N/A'}, Path: ${result.code_path || 'N/A'}`);
+                        } else {
+                            error(`[Task Manager] Task ${args.task_id} execution failed: ${result.message || 'Unknown error'}`);
+                        }
+
+                        return {
+                            content: [
+                                {
+                                    type: 'text',
+                                    text: JSON.stringify(result, null, 2),
+                                },
+                            ],
+                        };
+                    }
+
+                    case 'update_task_status': {
+                        const updated = await updateTaskStatus(
+                            args.task_id,
+                            args.status,
+                            args.code_name || null,
+                            args.code_path || null
+                        );
+                        return {
+                            content: [
+                                {
+                                    type: 'text',
+                                    text: JSON.stringify(updated, null, 2),
+                                },
+                            ],
+                        };
+                    }
+
+                    case 'process_all_tasks': {
+                        const result = await this.processAllTasks(args.auto_poll || false);
+                        return {
+                            content: [
+                                {
+                                    type: 'text',
+                                    text: JSON.stringify(result, null, 2),
+                                },
+                            ],
+                        };
+                    }
+
+                    case 'create_task': {
+                        const task = await createTask(
+                            args.task_name,
+                            args.task_description,
+                            args.create_by
+                        );
+                        return {
+                            content: [
+                                {
+                                    type: 'text',
+                                    text: JSON.stringify(task, null, 2),
+                                },
+                            ],
+                        };
+                    }
+
+                    case 'get_all_tasks': {
+                        const tasks = await getAllTasks(args.limit || 100);
+                        return {
+                            content: [
+                                {
+                                    type: 'text',
+                                    text: JSON.stringify(tasks, null, 2),
+                                },
+                            ],
+                        };
+                    }
+
+                    default:
+                        return {
+                            content: [
+                                {
+                                    type: 'text',
+                                    text: `Unknown tool: ${name}`,
+                                },
+                            ],
+                            isError: true,
+                        };
+                }
+            } catch (error) {
+                return {
+                    content: [
+                        {
+                            type: 'text',
+                            text: `Error executing tool ${name}: ${error.message}\n${error.stack}`,
+                        },
+                    ],
+                    isError: true,
+                };
+            }
+        });
+    }
+
+    /**
+     * 处理所有待处理的任务
+     */
+    async processAllTasks(autoPoll = false) {
+        if (this.isProcessing) {
+            info('[Task Manager] Task processing is already in progress, skipping...');
+            return {
+                success: false,
+                message: 'Task processing is already in progress',
+            };
+        }
+
+        info('[Task Manager] Starting to process all pending tasks...');
+        const processStartTime = Date.now();
+        
+        this.isProcessing = true;
+        const results = {
+            processed: 0,
+            succeeded: 0,
+            failed: 0,
+            tasks: [],
+        };
+
+        try {
+            const tasks = await getPendingTasks();
+            
+            if (tasks.length === 0) {
+                info('[Task Manager] No pending tasks found, skipping processing.');
+                return {
+                    success: true,
+                    message: 'No pending tasks to process',
+                    ...results,
+                };
+            }
+
+            info(`[Task Manager] Processing ${tasks.length} task(s)...`);
+            
+            for (const task of tasks) {
+                try {
+                    info(`[Task Manager] Processing task ${task.task_id}: ${task.task_name}`);
+                    
+                    // 更新状态为处理中
+                    await updateTaskStatus(task.task_id, 'processing');
+
+                    // 处理任务
+                    const result = await processTask(task);
+                    
+                    results.processed++;
+                    if (result.success) {
+                        results.succeeded++;
+                        await updateTaskStatus(
+                            task.task_id,
+                            'completed',
+                            result.code_name,
+                            result.code_path
+                        );
+                        info(`[Task Manager] Task ${task.task_id} completed successfully. Code: ${result.code_name || 'N/A'}, Path: ${result.code_path || 'N/A'}`);
+                    } else {
+                        results.failed++;
+                        await updateTaskStatus(task.task_id, 'failed');
+                        error(`[Task Manager] Task ${task.task_id} failed: ${result.message || 'Unknown error'}`);
+                    }
+
+                    results.tasks.push({
+                        task_id: task.task_id,
+                        task_name: task.task_name,
+                        success: result.success,
+                        message: result.message,
+                    });
+                } catch (err) {
+                    results.failed++;
+                    await updateTaskStatus(task.task_id, 'failed');
+                    error(`[Task Manager] Task ${task.task_id} failed with error: ${err.message}`);
+                    results.tasks.push({
+                        task_id: task.task_id,
+                        task_name: task.task_name,
+                        success: false,
+                        error: err.message,
+                    });
+                }
+            }
+
+            const processDuration = Date.now() - processStartTime;
+            info(`[Task Manager] Task processing completed. Processed: ${results.processed}, Succeeded: ${results.succeeded}, Failed: ${results.failed}, Duration: ${processDuration}ms`);
+
+            // 如果启用自动轮询,启动轮询机制
+            if (autoPoll && !this.pollingInterval) {
+                this.startPolling();
+            }
+
+            return {
+                success: true,
+                message: `Processed ${results.processed} tasks. Succeeded: ${results.succeeded}, Failed: ${results.failed}`,
+                ...results,
+            };
+        } finally {
+            this.isProcessing = false;
+        }
+    }
+
+    /**
+     * 启动轮询机制
+     */
+    startPolling() {
+        const pollInterval = parseInt(process.env.POLL_INTERVAL || '300000', 10); // 默认5分钟
+
+        if (this.pollingInterval) {
+            clearInterval(this.pollingInterval);
+        }
+
+        this.pollingInterval = setInterval(async () => {
+            if (!this.isProcessing) {
+                try {
+                    const tasks = await getPendingTasks();
+                    if (tasks.length > 0) {
+                        info(`Found ${tasks.length} new pending tasks, processing...`);
+                        await this.processAllTasks(false);
+                    }
+                } catch (err) {
+                    error('Error during polling: ' + err.message);
+                }
+            }
+        }, pollInterval);
+
+        info(`Polling started with interval: ${pollInterval}ms (${pollInterval / 1000}s)`);
+    }
+
+    /**
+     * 停止轮询机制
+     */
+    stopPolling() {
+        if (this.pollingInterval) {
+            clearInterval(this.pollingInterval);
+            this.pollingInterval = null;
+            info('Polling stopped');
+        }
+    }
+
+    /**
+     * 启动服务器
+     */
+    async start() {
+        const transport = new StdioServerTransport();
+        await this.server.connect(transport);
+        info('Task Manager MCP Server started');
+        
+        // 自动启动轮询机制(如果环境变量 AUTO_START_POLLING 为 true 或未设置)
+        const autoStartPolling = process.env.AUTO_START_POLLING !== 'false';
+        if (autoStartPolling) {
+            // 延迟启动,等待数据库连接就绪
+            setTimeout(() => {
+                this.startPolling();
+                // 立即检查一次待处理任务
+                this.processAllTasks(false).catch((err) => {
+                    error('Error during initial task processing: ' + err.message);
+                });
+            }, 2000); // 2秒后启动
+        }
+    }
+
+    /**
+     * 关闭服务器
+     */
+    async close() {
+        this.stopPolling();
+        await closePool();
+        // MCP SDK会自动处理连接关闭
+    }
+}
+
+// 启动服务器
+const server = new TaskManagerServer();
+
+// 处理进程退出
+process.on('SIGINT', async () => {
+    await server.close();
+    process.exit(0);
+});
+
+process.on('SIGTERM', async () => {
+    await server.close();
+    process.exit(0);
+});
+
+server.start().catch((err) => {
+    error('Failed to start server: ' + err.message);
+    process.exit(1);
+});
+

+ 133 - 0
mcp-servers/task-manager/logger.js

@@ -0,0 +1,133 @@
+/**
+ * 日志工具模块
+ * 
+ * 支持不同级别的日志记录(info, error, warn, debug)
+ * 优先通过 MCP 协议的日志通知功能发送日志,如果 MCP Server 不可用则回退到 console.error
+ */
+
+const LOG_LEVELS = {
+    DEBUG: 0,
+    INFO: 1,
+    WARN: 2,
+    ERROR: 3,
+};
+
+// MCP 日志级别映射
+const MCP_LOG_LEVELS = {
+    DEBUG: 'debug',
+    INFO: 'info',
+    WARN: 'warning',
+    ERROR: 'error',
+};
+
+// 从环境变量获取日志级别,默认为 INFO
+const getLogLevel = () => {
+    const level = process.env.LOG_LEVEL?.toUpperCase() || 'INFO';
+    return LOG_LEVELS[level] ?? LOG_LEVELS.INFO;
+};
+
+const currentLogLevel = getLogLevel();
+
+// 全局 MCP Server 实例(由 index.js 设置)
+let mcpServer = null;
+
+/**
+ * 设置 MCP Server 实例,用于发送日志通知
+ */
+export function setMcpServer(server) {
+    mcpServer = server;
+}
+
+/**
+ * 通过 MCP 协议发送日志通知
+ * 根据 MCP 协议规范,日志通知应该通过 stdout 以 JSON-RPC 通知消息的形式发送
+ * 同时也会输出到 stderr 作为备份,确保日志始终可见
+ */
+function sendLogToMcp(level, message) {
+    if (mcpServer) {
+        try {
+            // MCP 协议的日志通知格式:通过 stdout 发送 JSON-RPC 通知消息
+            // 格式:{"jsonrpc": "2.0", "method": "notifications/message", "params": {"level": "info", "message": "..."}}
+            const logNotification = {
+                jsonrpc: '2.0',
+                method: 'notifications/message',
+                params: {
+                    level: MCP_LOG_LEVELS[level] || 'info',
+                    message: message,
+                },
+            };
+            // 输出 JSON-RPC 格式的日志通知到 stdout(MCP 协议的标准方式)
+            // 注意:必须以换行符结尾,并且是有效的 JSON
+            console.log(JSON.stringify(logNotification));
+            // 同时输出到 stderr 作为备份,确保日志始终可见
+            // 这样即使 MCP 客户端没有正确处理通知,日志也能在 stderr 中看到
+            console.error(formatLogMessage(level, message));
+            return true;
+        } catch (error) {
+            // 如果发送失败,回退到 stderr 输出(普通格式)
+            console.error(`[${level}] Failed to send log via MCP: ${error.message}`);
+            return false;
+        }
+    }
+    return false;
+}
+
+/**
+ * 格式化日志消息
+ * 不包含时间戳,因为 Cursor 会自动添加
+ */
+function formatLogMessage(level, message) {
+    return `[${level}] ${message}`;
+}
+
+/**
+ * 记录 INFO 级别的日志(正常操作信息)
+ * 优先通过 MCP 协议格式发送,同时也会输出到 stderr 确保可见
+ */
+export function info(message) {
+    if (currentLogLevel <= LOG_LEVELS.INFO) {
+        // 尝试通过 MCP 协议格式发送日志(同时会输出到 stderr 作为备份)
+        const sent = sendLogToMcp('INFO', message);
+        if (!sent) {
+            // 如果 MCP Server 不可用,回退到 stderr 输出(普通格式)
+            console.error(formatLogMessage('INFO', message));
+        }
+    }
+}
+
+/**
+ * 记录 ERROR 级别的日志(错误信息)
+ */
+export function error(message) {
+    if (currentLogLevel <= LOG_LEVELS.ERROR) {
+        const sent = sendLogToMcp('ERROR', message);
+        if (!sent) {
+            console.error(formatLogMessage('ERROR', message));
+        }
+    }
+}
+
+/**
+ * 记录 WARN 级别的日志(警告信息)
+ */
+export function warn(message) {
+    if (currentLogLevel <= LOG_LEVELS.WARN) {
+        const sent = sendLogToMcp('WARN', message);
+        if (!sent) {
+            console.error(formatLogMessage('WARN', message));
+        }
+    }
+}
+
+/**
+ * 记录 DEBUG 级别的日志(调试信息)
+ */
+export function debug(message) {
+    if (currentLogLevel <= LOG_LEVELS.DEBUG) {
+        const sent = sendLogToMcp('DEBUG', message);
+        if (!sent) {
+            console.error(formatLogMessage('DEBUG', message));
+        }
+    }
+}
+

+ 318 - 0
mcp-servers/task-manager/package-lock.json

@@ -0,0 +1,318 @@
+{
+  "name": "task-manager-mcp-server",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "task-manager-mcp-server",
+      "version": "1.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "@modelcontextprotocol/sdk": "^0.5.0",
+        "dotenv": "^16.3.1",
+        "pg": "^8.11.3"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/@modelcontextprotocol/sdk": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.5.0.tgz",
+      "integrity": "sha512-RXgulUX6ewvxjAG0kOpLMEdXXWkzWgaoCGaA2CwNW7cQCIphjpJhjpHSiaPdVCnisjRF/0Cm9KWHUuIoeiAblQ==",
+      "license": "MIT",
+      "dependencies": {
+        "content-type": "^1.0.5",
+        "raw-body": "^3.0.0",
+        "zod": "^3.23.8"
+      }
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "16.6.1",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+      "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "depd": "2.0.0",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "toidentifier": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
+      "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/pg": {
+      "version": "8.16.3",
+      "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
+      "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
+      "license": "MIT",
+      "dependencies": {
+        "pg-connection-string": "^2.9.1",
+        "pg-pool": "^3.10.1",
+        "pg-protocol": "^1.10.3",
+        "pg-types": "2.2.0",
+        "pgpass": "1.0.5"
+      },
+      "engines": {
+        "node": ">= 16.0.0"
+      },
+      "optionalDependencies": {
+        "pg-cloudflare": "^1.2.7"
+      },
+      "peerDependencies": {
+        "pg-native": ">=3.0.1"
+      },
+      "peerDependenciesMeta": {
+        "pg-native": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pg-cloudflare": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
+      "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/pg-connection-string": {
+      "version": "2.9.1",
+      "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
+      "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
+      "license": "MIT"
+    },
+    "node_modules/pg-int8": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+      "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/pg-pool": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
+      "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
+      "license": "MIT",
+      "peerDependencies": {
+        "pg": ">=8.0"
+      }
+    },
+    "node_modules/pg-protocol": {
+      "version": "1.10.3",
+      "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
+      "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==",
+      "license": "MIT"
+    },
+    "node_modules/pg-types": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+      "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+      "license": "MIT",
+      "dependencies": {
+        "pg-int8": "1.0.1",
+        "postgres-array": "~2.0.0",
+        "postgres-bytea": "~1.0.0",
+        "postgres-date": "~1.0.4",
+        "postgres-interval": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/pgpass": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+      "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+      "license": "MIT",
+      "dependencies": {
+        "split2": "^4.1.0"
+      }
+    },
+    "node_modules/postgres-array": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+      "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postgres-bytea": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+      "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/postgres-date": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+      "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/postgres-interval": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+      "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+      "license": "MIT",
+      "dependencies": {
+        "xtend": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz",
+      "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.7.0",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "license": "ISC"
+    },
+    "node_modules/split2": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+      "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">= 10.x"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/zod": {
+      "version": "3.25.76",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+      "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
+    }
+  }
+}

+ 26 - 0
mcp-servers/task-manager/package.json

@@ -0,0 +1,26 @@
+{
+  "name": "task-manager-mcp-server",
+  "version": "1.0.0",
+  "description": "MCP server for managing development tasks from PostgreSQL database",
+  "main": "index.js",
+  "type": "module",
+  "scripts": {
+    "start": "node index.js"
+  },
+  "keywords": [
+    "mcp",
+    "task-management",
+    "postgresql"
+  ],
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "@modelcontextprotocol/sdk": "^0.5.0",
+    "pg": "^8.11.3",
+    "dotenv": "^16.3.1"
+  },
+  "engines": {
+    "node": ">=18.0.0"
+  }
+}
+

+ 138 - 0
mcp-servers/task-manager/task-processor.js

@@ -0,0 +1,138 @@
+/**
+ * 任务处理模块
+ * 
+ * 负责处理单个任务,将任务描述发送给Cursor执行代码开发
+ */
+
+import { info, error } from './logger.js';
+
+/**
+ * 处理任务
+ * 
+ * 这个函数将任务描述格式化后返回,供Cursor通过MCP协议接收并执行
+ * 
+ * @param {Object} task - 任务对象
+ * @param {number} task.task_id - 任务ID
+ * @param {string} task.task_name - 任务名称
+ * @param {string} task.task_description - 任务描述(markdown格式)
+ * @returns {Promise<Object>} 处理结果
+ */
+export async function processTask(task) {
+    info(`[Task Processor] Processing task ${task.task_id}: ${task.task_name}`);
+    const startTime = Date.now();
+    
+    try {
+        // 构建任务执行消息
+        // 这个消息将通过MCP协议返回给Cursor,Cursor会解析并执行
+        const taskMessage = {
+            type: 'development_task',
+            task_id: task.task_id,
+            task_name: task.task_name,
+            description: task.task_description,
+            timestamp: new Date().toISOString(),
+        };
+
+        // 格式化任务描述,确保是有效的markdown
+        const formattedDescription = formatTaskDescription(task);
+        info(`[Task Processor] Task ${task.task_id} description formatted successfully (${formattedDescription.length} characters)`);
+
+        // 返回任务信息,供Cursor处理
+        // Cursor会接收到这个信息,并根据描述执行代码开发
+        const result = {
+            success: true,
+            message: `Task ${task.task_id} prepared for execution`,
+            task_id: task.task_id,
+            task_name: task.task_name,
+            formatted_description: formattedDescription,
+            // 这些字段将在任务完成后更新到数据库
+            code_name: null,
+            code_path: null,
+            execution_instructions: `
+请根据以下任务描述进行代码开发:
+
+## 任务信息
+- 任务ID: ${task.task_id}
+- 任务名称: ${task.task_name}
+
+## 任务描述
+${formattedDescription}
+
+## 执行要求
+1. 仔细阅读任务描述,理解需求
+2. 根据描述开发相应的代码
+3. 确保代码符合项目规范
+4. 完成后,请提供生成的代码文件名和路径信息
+            `,
+        };
+        
+        const duration = Date.now() - startTime;
+        info(`[Task Processor] Task ${task.task_id} processed successfully in ${duration}ms`);
+        
+        return result;
+    } catch (err) {
+        const duration = Date.now() - startTime;
+        error(`[Task Processor] Task ${task.task_id} processing failed after ${duration}ms: ${err.message}`);
+        return {
+            success: false,
+            message: `Failed to process task: ${err.message}`,
+            error: err.stack,
+        };
+    }
+}
+
+/**
+ * 格式化任务描述
+ * 
+ * @param {Object} task - 任务对象
+ * @returns {string} 格式化后的任务描述
+ */
+function formatTaskDescription(task) {
+    let description = task.task_description || '';
+
+    // 确保描述是有效的markdown格式
+    // 如果描述不是以markdown格式开头,添加一些基本格式
+    if (!description.trim().startsWith('#')) {
+        description = `# ${task.task_name}\n\n${description}`;
+    }
+
+    return description;
+}
+
+/**
+ * 验证任务描述格式
+ * 
+ * @param {string} description - 任务描述
+ * @returns {boolean} 是否为有效的markdown格式
+ */
+export function validateTaskDescription(description) {
+    if (!description || typeof description !== 'string') {
+        return false;
+    }
+
+    // 基本验证:检查是否包含一些内容
+    if (description.trim().length === 0) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * 提取任务中的关键信息
+ * 
+ * @param {Object} task - 任务对象
+ * @returns {Object} 提取的关键信息
+ */
+export function extractTaskInfo(task) {
+    const description = task.task_description || '';
+    
+    // 尝试提取代码文件名和路径(如果任务描述中包含)
+    const codeNameMatch = description.match(/代码文件名[::]\s*([^\n]+)/i);
+    const codePathMatch = description.match(/代码路径[::]\s*([^\n]+)/i);
+
+    return {
+        code_name: codeNameMatch ? codeNameMatch[1].trim() : null,
+        code_path: codePathMatch ? codePathMatch[1].trim() : null,
+    };
+}
+

+ 3 - 0
test_check_218.py

@@ -244,3 +244,6 @@ if __name__ == "__main__":
 
 
 
+
+
+

+ 3 - 0
test_check_api.py

@@ -154,3 +154,6 @@ if __name__ == "__main__":
 
 
 
+
+
+

+ 3 - 0
test_check_interface_only.py

@@ -233,3 +233,6 @@ if __name__ == "__main__":
 
 
 
+
+
+

+ 3 - 0
test_metadata_workflow.py

@@ -187,3 +187,6 @@ if __name__ == "__main__":
 
 
 
+
+
+

+ 3 - 0
tests/test_metric_check.py

@@ -237,3 +237,6 @@ if __name__ == '__main__':
 
 
 
+
+
+

+ 3 - 0
verify_check_api.md

@@ -306,3 +306,6 @@ GET http://192.168.3.143:5000/api/meta/check?name_zh=其他费用定额
 
 
 
+
+
+