项目介绍
技术文档摘要
Taiga MCP 服务器
项目概述
- 实现了一个基于Starlette的模型上下文协议(MCP)服务器,该服务器暴露了用于ChatGPT和其他符合MCP标准客户端的Server-Sent Events(SSE)和可流式传输HTTP传输。
- 担任ChatGPT与Taiga项目管理API之间的桥梁,当前包括一个用于传输验证的
echo工具,并正在进行Taiga特定操作的集成。
- 部署为容器工作负载到Azure容器应用,并通过GitHub容器注册表(GHCR)打包分发。
配置
- 将
.env.example复制为.env(或直接导出变量),并填写你的Taiga实例、凭据和部署设置的值。
- 不要提交
.env文件或秘密;项目.gitignore默认排除常见模式。
- 本地脚本和辅助应用程序读取相同的变量(如
TAIGA_BASE_URL、TAIGA_USERNAME、TAIGA_PASSWORD、ACTION_PROXY_API_KEY、MCP_URL、TAIGA_PROXY_BASE_URL等),因此更新环境可以保持整个工具链的一致性。
运行时架构
app.py实例化带有Taiga MCP身份的FastMCP,在/sse/挂载SSE,在/mcp/挂载可流式传输HTTP,并禁用尾随斜杠重定向以保留MCP会话头。
- 自定义中间件将裸
/mcp请求重写为/mcp/,并在子应用程序内部规范化空白路径,以避免先前导致MCP会话头丢失的307重定向。
- FastMCP会话管理器通过Starlette的生命周期钩子启动,确保流式传输会话在长时间运行的ChatGPT对话中保持活跃。
- 健康检查:
GET / → 简单文本“Taiga MCP up”,用于快速可用性探测。
GET /healthz → 最小健康端点,供容器编排器使用。
端点总结
/ — 根状态页面。
/healthz — 由Azure容器应用使用的存活探测。
/sse/ — Server-Sent Events传输;需要Accept: text/event-stream,并在第一个事件有效载荷中返回消息发布端点。
/sse/sse/messages/ — SSE消息提交的目标(在SSE endpoint事件中返回)。
/mcp/ — 可流式传输HTTP传输;客户端必须发送Accept: application/json, text/event-stream以满足协议协商。
MCP工具
echo(message) — 诊断助手,返回提供的消息。
taiga.projects.list(search?) — 列出服务账户成员的项目(可选大小写不敏感的子字符串搜索,范围限定于认证用户ID)。
taiga.projects.get(project_id?, slug?) — 通过数字标识符或slug获取项目记录(两个输入中的一个必须提供)。
taiga.epics.list(project_id) — 列出项目的史诗,包括id/ref/subject/status元数据。
taiga.stories.list(project_id, search?, epic_id?, tags?, page?, page_size?) — 列出项目的用户故事,可选过滤器包括文本搜索、史诗成员资格、标签和分页。
taiga.stories.create(project_id, subject, description?, status?, tags?, assigned_to?) — 创建Taiga用户故事;status接受id或状态名称/别名。
taiga.stories.update(user_story_id, subject?, description?, status?, tags?, assigned_to?, epic_id?, milestone_id?, custom_attributes?, version?) — 使用乐观并发检查和服务器端状态解析更新现有故事。
taiga.epics.add_user_story(epic_id, user_story_id) — 将用户故事链接到史诗。
taiga.tasks.create(user_story_id, subject, description?, assigned_to?, status?, tags?, due_date?, idempotency_key?) — 在故事下创建Taiga任务,应用状态查找和幂等保护。
taiga.tasks.update(task_id, subject?, description?, assigned_to?, status?, tags?, due_date?, version?) — 使用与故事相同的乐观并发处理更新任务。
taiga.tasks.list(project_id?, user_story_id?, assigned_to?, search?, status?, page?, page_size?) — 列出任务,具有灵活的过滤器和分页元数据,当提供project_id时解析状态名称。
taiga.users.list(project_id?, search?) — 根据项目成员资格查找Taiga用户,可选子字符串匹配全名、用户名或电子邮件。
taiga.milestones.list(project_id, search?) — 返回项目的里程碑/冲刺,允许可选的名称/别名过滤。
动作代理表面
- 目的:为Taiga自动化提供轻量级HTTP桥接,同时MCP写入工具保持白名单。
- 认证:每个请求提供
X-Api-Key;值必须与ACTION_PROXY_API_KEY环境变量匹配(缺少/无效的键返回401,未配置的键返回503)。
- 端点:
GET /actions/list_projects?search=foo → { "projects": [...] },可选大小写不敏感的名称过滤器(自动设置member=<service-account-id>,除非覆盖member查询参数)。
GET /actions/get_project?project_id=123 → { "project": {...} },返回给定ID的完整Taiga项目负载。
GET /actions/get_project_by_slug?slug=acme-backlog → { "project": {...} },通过slug解析项目。
GET /actions/list_epics?project_id=111&project_id=222 → { "epics": [...] },包含每个史诗的原始project_id。
GET /actions/list_stories?project_id=123&epic_id=456&search=prior+art&tag=ip → { "stories": [...] },按项目、史诗、关键词、标签和可选分页(page/page_size)过滤。
GET /actions/statuses?project_id=123 → { "statuses": [...] },驱动状态选择器。
POST /actions/create_story → { "story": {...} };接受与MCP工具相同的负载,并解析状态别名/名称。
POST /actions/update_story → { "story": {...} };接受story_id加上任何组合的project_id、subject、description、status、tags、assigned_to(状态字符串自动解析为id)。
POST /actions/delete_story → { "deleted": {"story_id": ...} }。
POST /actions/add_story_to_epic → { "link": {...} },在链接故事到史诗后。
POST /actions/create_epic / update_epic / delete_epic → 管理史诗(project_id、subject、可选description、status、assigned_to、tags、color)。
POST /actions/create_task / update_task / delete_task → 管理任务(project_id、subject、可选description、status、assigned_to、tags、user_story_id)。
POST /actions/create_issue / update_issue / delete_issue → 管理问题(project_id、subject、可选description、status、priority、severity、type、assigned_to、tags)。
- 错误模型:JSON
{ "error": "..." }负载,4xx用于验证/Taiga错误,500用于意外失败(也记录在服务器端)。
- 辅助脚本(需要设置
ACTION_PROXY_API_KEY和TAIGA_PROXY_BASE_URL):
\.\.chat-venv\Scripts\python.exe scripts/actions_proxy_client.py --pretty list-projects
\.\.chat-venv\Scripts\python.exe scripts/actions_proxy_client.py get-project --project-id <ID>
\.\.chat-venv\Scripts\python.exe scripts/actions_proxy_client.py get-project-by-slug --slug <slug>
powershell.exe -File scripts/actions-proxy.ps1 list-projects
- 原始curl示例:
curl.exe -H "X-Api-Key: $env:ACTION_PROXY_API_KEY" "$env:TAIGA_PROXY_BASE_URL/actions/list_projects?search=beta"
curl.exe -H "X-Api-Key: $env:ACTION_PROXY_API_KEY" -H "Content-Type: application/json" -d "{\"project_id\":123,\"subject\":\"Story\"}" "$env:TAIGA_PROXY_BASE_URL/actions/create_story"
本地开发工作流程
- 前提条件
- Python 3.11(项目默认使用
.chat-venv虚拟环境)。
- Docker Desktop用于容器构建。
- Azure CLI用于部署自动化。
- GHCR认证(
docker login ghcr.io)。
- 安装依赖项
python -m venv .chat-venv
.\.chat-venv\Scripts\python.exe -m pip install -r requirements.txt
- 配置环境
copy .env.example .env(PowerShell: Copy-Item .env.example .env),并更新值指向你的Taiga实例。
- 本地运行服务器
.\.chat-venv\Scripts\uvicorn.exe app:app --host 127.0.0.1 --port 8010
- 流式传输探测:
.\.chat-venv\Scripts\python.exe streamable_client.py http://127.0.0.1:8010/mcp --message "hello local"
- 手动测试SSE
curl.exe -sN -H "Accept: text/event-stream" http://127.0.0.1:8010/sse/
容器构建与发布
- 选择镜像名称(默认部署使用
ghcr.io/johnwblack/taiga-mcp),并设置:
- PowerShell:
$env:CONTAINER_IMAGE = 'ghcr.io/johnwblack/taiga-mcp' 和 $env:IMAGE_TAG = 'v0.0.29'
- Bash:
export CONTAINER_IMAGE=ghcr.io/johnwblack/taiga-mcp 和 export IMAGE_TAG=v0.0.29
- 构建标记的镜像(交叉构建时设置
--platform):
docker build -t "$CONTAINER_IMAGE:$IMAGE_TAG" -t "$CONTAINER_IMAGE:latest" .
- 推送到你的注册表:
docker push "$CONTAINER_IMAGE:$IMAGE_TAG"
docker push "$CONTAINER_IMAGE:latest"
Azure容器应用部署
- 导出以下环境变量(更新名称以匹配你的订阅):
$env:AZURE_RESOURCE_GROUP = 'your-resource-group'
$env:AZURE_CONTAINER_APP = 'taiga-mcp'
- 部署新版本(推送镜像后):
az containerapp update -g $env:AZURE_RESOURCE_GROUP -n $env:AZURE_CONTAINER_APP --image "$CONTAINER_IMAGE:$IMAGE_TAG"
- 或运行辅助脚本(构建、推送和部署一步完成;尊重相同的环境变量并接受覆盖):
python scripts/deploy_to_azure.py --image "$env:CONTAINER_IMAGE" --tag "$env:IMAGE_TAG" --resource-group "$env:AZURE_RESOURCE_GROUP" --container-app "$env:AZURE_CONTAINER_APP"
- 可选标志:
--skip-build、--skip-push、--latest-tag帮助热修复重新部署
- 有用的Windows设置(避免WinError 5权限问题):
$env:AZURE_EXTENSION_DIR = Join-Path $HOME '.az-extensions'
$env:AZURE_CONFIG_DIR = Join-Path $HOME '.az-cli'
Taiga凭证的秘密管理
- 将Taiga凭证和代理密钥存储在你选择的秘密存储中(例如Azure容器应用秘密、Kubernetes秘密等),并在容器内作为环境变量呈现。
- 示例(Azure容器应用):
az containerapp secret set --resource-group $env:AZURE_RESOURCE_GROUP --name $env:AZURE_CONTAINER_APP --secrets taiga-username="<USERNAME>" taiga-password="<PASSWORD>"
az containerapp update --resource-group $env:AZURE_RESOURCE_GROUP --name $env:AZURE_CONTAINER_APP --set-env-vars TAIGA_USERNAME=secretref:taiga-username TAIGA_PASSWORD=secretref:taiga-password
- 以相同方式更新或轮换动作代理密钥:
az containerapp secret set --resource-group $env:AZURE_RESOURCE_GROUP --name $env:AZURE_CONTAINER_APP --secrets action-proxy-api-key="<RANDOM_TOKEN>"
az containerapp update --resource-group $env:AZURE_RESOURCE_GROUP --name $env:AZURE_CONTAINER_APP --set-env-vars ACTION_PROXY_API_KEY=secretref:action-proxy-api-key
- 容器期望以下环境变量存在:
TAIGA_BASE_URL — Taiga API的基本URL(通常由秘密引用支持)。
TAIGA_USERNAME — 服务账户用户名。
TAIGA_PASSWORD — 服务账户密码。
ACTION_PROXY_API_KEY — /actions/*端点使用的共享秘密。
- 定期轮换凭证并重新部署,以便新版本能够获取更改。
连接MCP客户端
- 你应部署并托管自己的MCP服务器实例(例如在Azure容器应用、Docker Desktop或其他Python托管目标上)。此仓库不公开共享公共端点。
- ChatGPT自定义GPT设置:
- 打开GPT Builder UI,选择创建,然后在模型上下文协议工具部分下选择配置→添加。
- 输入已部署的可流式传输HTTP URL(例如
https://your-domain.example/mcp)作为端点,并在没有代理的情况下留空头部/正文。
- 保存GPT并测试
echo工具以确认连接。会话将在每次调用时重复使用你的部署服务器。
- 开源MCP客户端(如
mcp Python CLI或Claude Desktop)可以针对相同的/mcp端点;在运行辅助脚本如streamable_client.py之前,将MCP_URL设置为你的部署URL。
- 如果重新生成容器镜像,现有的环境变量和秘密引用仍附加到容器应用。只有在引入新的键/变量或凭证轮换时才需再次设置它们。
验证检查清单
- 可流式传输HTTP烟雾测试:
\.\.chat-venv\Scripts\python.exe streamable_client.py $env:MCP_URL --message "ping"
- SSE可用性测试:
curl.exe -sN -H "Accept: text/event-stream" "$env:TAIGA_PROXY_BASE_URL/sse/" --max-time 5
- Azure日志审查(如果部署到ACA):
az containerapp logs show -g $env:AZURE_RESOURCE_GROUP -n $env:AZURE_CONTAINER_APP --tail 50
- ChatGPT连接器验证:
- 使用你的部署MCP端点配置ChatGPT或其他MCP客户端,并确认
echo工具响应。
- 端到端地练习项目、史诗和故事的工作流程,以确保Taiga凭证和权限正确。
故障排除说明
Not Acceptable: Client must accept text/event-stream — 确保客户端在调用/mcp/时发送Accept: application/json, text/event-stream。
Session terminated错误通常表示重定向;验证请求命中/mcp/(带尾随斜杠),并且代理没有重写头部。
- 解决Azure CLI
WinError 5权限问题的方法是将AZURE_EXTENSION_DIR和AZURE_CONFIG_DIR设置为用户可写的目录。
- 更新秘密后,Azure容器应用会重启修订版;在重新测试端点前,请等待1-2分钟。
测试
- 安装开发依赖项:
python -m pip install -r requirements.txt pytest(从.chat-venv环境)。
- 运行Python单元套件:
pytest(涵盖/actions/*认证、验证和通过假数据的Taiga错误处理)。
- 对本地辅助CLI进行烟雾测试:
.\.chat-venv\Scripts\python.exe scripts/actions_proxy_client.py --help。
- PowerShell验证:
powershell.exe -File scripts/actions-proxy.ps1 list-projects -BaseUrl http://127.0.0.1:8010 -ApiKey local-test,当服务器使用临时密钥运行时。
请求有效负载参考
用户故事
- 创建(
POST /actions/create_story):{ "project_id": int, "subject": str, "description"?: str, "status"?: int|str, "tags"?: [str], "assigned_to"?: int }
- 更新(
POST /actions/update_story):{ "story_id": int, "project_id"?: int, "subject"?: str, "description"?: str, "status"?: int|str, "tags"?: [str], "assigned_to"?: int }
- 删除(
POST /actions/delete_story):{ "story_id": int }
史诗
- 创建(
POST /actions/create_epic):{ "project_id": int, "subject": str, "description"?: str, "status"?: int, "assigned_to"?: int, "tags"?: [str], "color"?: str }
- 更新(
POST /actions/update_epic):`{ "epic_id": int, "subject"?: str, "description"?: str,