280 lines
8.1 KiB
Python
280 lines
8.1 KiB
Python
|
|
"""
|
|||
|
|
项目相关MCP工具
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from typing import Dict, List, Optional, Any
|
|||
|
|
from fastmcp import FastMCP
|
|||
|
|
from .adapter import adapter, APIError
|
|||
|
|
|
|||
|
|
# --- 模块级核心逻辑函数 ---
|
|||
|
|
|
|||
|
|
def get_projects_logic() -> List[Dict[str, Any]]:
|
|||
|
|
"""
|
|||
|
|
获取所有项目列表 (逻辑部分)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
项目列表 (包含 id, name, color, sortOrder, sortType, modifiedTime)
|
|||
|
|
"""
|
|||
|
|
projects = adapter.list_projects()
|
|||
|
|
# 直接返回官方结构的精简版
|
|||
|
|
result: List[Dict[str, Any]] = []
|
|||
|
|
for p in projects:
|
|||
|
|
result.append({k: v for k, v in p.items() if v is not None})
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
def create_project_logic(
|
|||
|
|
name: str,
|
|||
|
|
color: Optional[str] = None,
|
|||
|
|
view_mode: Optional[str] = None,
|
|||
|
|
kind: Optional[str] = None,
|
|||
|
|
sort_order: Optional[int] = None
|
|||
|
|
) -> Dict[str, Any]:
|
|||
|
|
"""
|
|||
|
|
创建新项目 (逻辑部分)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
name: 项目名称
|
|||
|
|
color: 项目颜色,如 "#FF0000" 表示红色
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
创建的项目信息 (API 原始响应)
|
|||
|
|
"""
|
|||
|
|
payload = {"name": name, "color": color}
|
|||
|
|
if view_mode is not None:
|
|||
|
|
payload["viewMode"] = view_mode
|
|||
|
|
if kind is not None:
|
|||
|
|
payload["kind"] = kind
|
|||
|
|
if sort_order is not None:
|
|||
|
|
payload["sortOrder"] = sort_order
|
|||
|
|
payload = {k: v for k, v in payload.items() if v is not None}
|
|||
|
|
return adapter.create_project(**payload)
|
|||
|
|
|
|||
|
|
def update_project_logic(
|
|||
|
|
project_id_or_name: str,
|
|||
|
|
name: Optional[str] = None,
|
|||
|
|
color: Optional[str] = None,
|
|||
|
|
view_mode: Optional[str] = None,
|
|||
|
|
kind: Optional[str] = None,
|
|||
|
|
sort_order: Optional[int] = None
|
|||
|
|
) -> Dict[str, Any]:
|
|||
|
|
"""
|
|||
|
|
更新项目信息 (逻辑部分)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
project_id_or_name: 项目ID或项目名称
|
|||
|
|
name: 新项目名称
|
|||
|
|
color: 新项目颜色
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
更新操作的结果字典 (包含 success, info, data)
|
|||
|
|
"""
|
|||
|
|
# 获取项目信息
|
|||
|
|
projects = get_projects_logic()
|
|||
|
|
|
|||
|
|
# 查找项目
|
|||
|
|
project = None
|
|||
|
|
project_id = None
|
|||
|
|
# 先尝试按ID查找
|
|||
|
|
for p in projects:
|
|||
|
|
if p.get('id') == project_id_or_name:
|
|||
|
|
project = p
|
|||
|
|
project_id = p.get('id')
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
# 如果没找到,按名称查找
|
|||
|
|
if not project:
|
|||
|
|
for p in projects:
|
|||
|
|
if p.get('name') == project_id_or_name:
|
|||
|
|
project = p
|
|||
|
|
project_id = p.get('id')
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
if not project or not project_id:
|
|||
|
|
return {
|
|||
|
|
"success": False,
|
|||
|
|
"info": f"未找到ID或名称为 '{project_id_or_name}' 的项目",
|
|||
|
|
"data": None
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 准备更新数据
|
|||
|
|
update_data = {
|
|||
|
|
"id": project_id,
|
|||
|
|
"name": name if name is not None else project.get('name'),
|
|||
|
|
"color": color if color is not None else project.get('color')
|
|||
|
|
}
|
|||
|
|
if view_mode is not None:
|
|||
|
|
update_data["viewMode"] = view_mode
|
|||
|
|
if kind is not None:
|
|||
|
|
update_data["kind"] = kind
|
|||
|
|
if sort_order is not None:
|
|||
|
|
update_data["sortOrder"] = sort_order
|
|||
|
|
# 移除 name 和 color 为 None 的情况,避免 API 报错
|
|||
|
|
update_data = {k:v for k,v in update_data.items() if v is not None}
|
|||
|
|
|
|||
|
|
# 发送更新请求
|
|||
|
|
response = adapter.update_project(project_id, name=name, color=color)
|
|||
|
|
updated_project_data = response if isinstance(response, dict) else {**update_data, "id": project_id}
|
|||
|
|
updated_project_data['id'] = project_id # 确保 ID 在返回数据中
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"success": True,
|
|||
|
|
"info": "项目更新成功",
|
|||
|
|
"data": updated_project_data
|
|||
|
|
}
|
|||
|
|
except Exception as e:
|
|||
|
|
# 更新项目失败(禁用print避免干扰JSONRPC)
|
|||
|
|
pass
|
|||
|
|
return {
|
|||
|
|
"success": False,
|
|||
|
|
"info": f"更新项目失败: {str(e)}",
|
|||
|
|
"data": None
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def delete_project_logic(project_id_or_name: str) -> Dict[str, Any]:
|
|||
|
|
"""
|
|||
|
|
删除项目 (逻辑部分)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
project_id_or_name: 项目ID或项目名称
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
删除操作的响应字典 (包含 success, info, data)
|
|||
|
|
"""
|
|||
|
|
# 获取项目信息
|
|||
|
|
projects = get_projects_logic()
|
|||
|
|
|
|||
|
|
# 查找项目
|
|||
|
|
project = None
|
|||
|
|
project_id = None
|
|||
|
|
# 先尝试按ID查找
|
|||
|
|
for p in projects:
|
|||
|
|
if p.get('id') == project_id_or_name:
|
|||
|
|
project = p
|
|||
|
|
project_id = p.get('id')
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
# 如果没找到,按名称查找
|
|||
|
|
if not project:
|
|||
|
|
for p in projects:
|
|||
|
|
if p.get('name') == project_id_or_name:
|
|||
|
|
project = p
|
|||
|
|
project_id = p.get('id')
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
if not project or not project_id:
|
|||
|
|
return {
|
|||
|
|
"success": False,
|
|||
|
|
"info": f"未找到ID或名称为 '{project_id_or_name}' 的项目",
|
|||
|
|
"data": None
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 发送删除请求
|
|||
|
|
# 滴答清单删除项目通常不需要请求体,直接调用DELETE方法
|
|||
|
|
adapter.delete_project(project_id)
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"success": True,
|
|||
|
|
"info": f"成功删除项目 '{project.get('name')}'",
|
|||
|
|
"data": project # 返回被删除项目的信息
|
|||
|
|
}
|
|||
|
|
except Exception as e:
|
|||
|
|
# 删除项目失败(禁用print避免干扰JSONRPC)
|
|||
|
|
pass
|
|||
|
|
return {
|
|||
|
|
"success": False,
|
|||
|
|
"info": f"删除项目失败: {str(e)}",
|
|||
|
|
"data": None
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# --- MCP工具注册 ---
|
|||
|
|
|
|||
|
|
def register_project_tools(server: FastMCP, auth_info: Dict[str, Any]):
|
|||
|
|
"""
|
|||
|
|
注册项目相关工具到MCP服务器
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
server: MCP服务器实例
|
|||
|
|
auth_info: 认证信息字典,包含token或email/password
|
|||
|
|
"""
|
|||
|
|
# 适配层按需初始化,无需在此显式初始化
|
|||
|
|
|
|||
|
|
@server.tool()
|
|||
|
|
def get_projects() -> List[Dict[str, Any]]:
|
|||
|
|
"""
|
|||
|
|
获取所有项目列表
|
|||
|
|
(调用模块级逻辑函数)
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
项目列表
|
|||
|
|
"""
|
|||
|
|
return get_projects_logic()
|
|||
|
|
|
|||
|
|
@server.tool()
|
|||
|
|
def create_project(
|
|||
|
|
name: str,
|
|||
|
|
color: Optional[str] = None,
|
|||
|
|
view_mode: Optional[str] = None,
|
|||
|
|
kind: Optional[str] = None,
|
|||
|
|
sort_order: Optional[int] = None
|
|||
|
|
) -> Dict[str, Any]:
|
|||
|
|
"""
|
|||
|
|
创建新项目
|
|||
|
|
(调用模块级逻辑函数)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
name: 项目名称
|
|||
|
|
color: 项目颜色,如 "#FF0000" 表示红色
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
创建的项目信息 (API 原始响应)
|
|||
|
|
"""
|
|||
|
|
return create_project_logic(name=name, color=color, view_mode=view_mode, kind=kind, sort_order=sort_order)
|
|||
|
|
|
|||
|
|
@server.tool()
|
|||
|
|
def update_project(
|
|||
|
|
project_id_or_name: str,
|
|||
|
|
name: Optional[str] = None,
|
|||
|
|
color: Optional[str] = None,
|
|||
|
|
view_mode: Optional[str] = None,
|
|||
|
|
kind: Optional[str] = None,
|
|||
|
|
sort_order: Optional[int] = None
|
|||
|
|
) -> Dict[str, Any]:
|
|||
|
|
"""
|
|||
|
|
更新项目信息
|
|||
|
|
(调用模块级逻辑函数)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
project_id_or_name: 项目ID或项目名称
|
|||
|
|
name: 新项目名称
|
|||
|
|
color: 新项目颜色
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
更新操作的结果字典 (包含 success, info, data)
|
|||
|
|
"""
|
|||
|
|
return update_project_logic(project_id_or_name=project_id_or_name, name=name, color=color, view_mode=view_mode, kind=kind, sort_order=sort_order)
|
|||
|
|
|
|||
|
|
@server.tool()
|
|||
|
|
def delete_project(project_id_or_name: str) -> Dict[str, Any]:
|
|||
|
|
"""
|
|||
|
|
删除项目
|
|||
|
|
(调用模块级逻辑函数)
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
project_id_or_name: 项目ID或项目名称
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
删除操作的响应字典 (包含 success, info, data)
|
|||
|
|
"""
|
|||
|
|
return delete_project_logic(project_id_or_name=project_id_or_name)
|
|||
|
|
|
|||
|
|
# 导出可供外部引用的函数
|
|||
|
|
__all__ = [
|
|||
|
|
'get_projects_logic',
|
|||
|
|
'create_project_logic',
|
|||
|
|
'update_project_logic',
|
|||
|
|
'delete_project_logic',
|
|||
|
|
'register_project_tools'
|
|||
|
|
]
|