T
Taiga MCP
作者 @JohnWBlack
MCP server by JohnWBlack
创建于 10/30/2025
更新于 about 2 months ago
README
Repository documentation and setup instructions
Taiga MCP Server
Project Overview
- Implements a Starlette-based Model Context Protocol (MCP) server that exposes both Server-Sent Events (SSE) and Streamable HTTP transports for ChatGPT and other MCP compliant clients.
- Serves as a bridge between ChatGPT and the Taiga project management API, currently including an
echotool for transport validation with ongoing integration of Taiga-specific actions. - Deployed as a container workload to Azure Container Apps and packaged for distribution through GitHub Container Registry (GHCR).
Configuration
- Copy
.env.exampleto.env(or export the variables directly) and fill in values for your Taiga instance, credentials, and deployment settings. - Never commit
.envfiles or secrets; the project.gitignoreexcludes common patterns by default. - Local scripts and helper apps read the same variables (
TAIGA_BASE_URL,TAIGA_USERNAME,TAIGA_PASSWORD,ACTION_PROXY_API_KEY,MCP_URL,TAIGA_PROXY_BASE_URL, and friends), so updating the environment keeps the entire toolchain aligned.
Runtime Architecture
app.pyinstantiatesFastMCPwith the Taiga MCP identity, mounts SSE at/sse/and Streamable HTTP at/mcp/, and disables trailing-slash redirects to preserve MCP session headers.- A custom middleware rewrites bare
/mcprequests to/mcp/and normalizes blank paths inside the sub-application to avoid 307 redirects that previously dropped MCP session headers. - The FastMCP session manager is started via Starlette's lifespan hook to ensure streamable sessions stay active for long-running ChatGPT conversations.
- Health checks:
GET /→ plain text "Taiga MCP up" for quick availability probes.GET /healthz→ minimal health endpoint for container orchestrators.
Endpoints Summary
/— Root status page./healthz— Liveness probe used by Azure Container Apps./sse/— Server-Sent Events transport; requiresAccept: text/event-streamand returns the message posting endpoint in the first event payload./sse/sse/messages/— POST target for SSE message submission (returned in SSEendpointevent)./mcp/— Streamable HTTP transport; clients must sendAccept: application/json, text/event-streamto satisfy protocol negotiation.
MCP Tools
echo(message)— diagnostic helper that returns the provided message.taiga.projects.list(search?)— lists projects where the service account is a member (optional case-insensitive substring search, scoped by the authenticated user id).taiga.projects.get(project_id?, slug?)— fetches a project record either by numeric identifier or slug (one of the two inputs is required).taiga.epics.list(project_id)— lists epics for a project, including id/ref/subject/status metadata.taiga.stories.list(project_id, search?, epic_id?, tags?, page?, page_size?)— lists user stories for a project with optional filters for text search, epic membership, tags, and pagination.taiga.stories.create(project_id, subject, description?, status?, tags?, assigned_to?)— creates a Taiga user story;statusaccepts either an id or status name/slug.taiga.stories.update(user_story_id, subject?, description?, status?, tags?, assigned_to?, epic_id?, milestone_id?, custom_attributes?, version?)— updates an existing story with optimistic concurrency checks and server-side status resolution.taiga.epics.add_user_story(epic_id, user_story_id)— links a user story to an epic.taiga.tasks.create(user_story_id, subject, description?, assigned_to?, status?, tags?, due_date?, idempotency_key?)— creates a Taiga task under a story, applying status lookups and idempotency safeguards.taiga.tasks.update(task_id, subject?, description?, assigned_to?, status?, tags?, due_date?, version?)— updates a task with the same optimistic concurrency handling as stories.taiga.tasks.list(project_id?, user_story_id?, assigned_to?, search?, status?, page?, page_size?)— lists tasks with flexible filters and pagination metadata, resolving status names when aproject_idis supplied.taiga.users.list(project_id?, search?)— finds Taiga users by project membership with optional substring matching across full name, username, or email.taiga.milestones.list(project_id, search?)— returns milestones/sprints for a project, allowing optional name/slug filtering.
Action Proxy Surface
- Purpose: provide a lightweight HTTP bridge for Taiga automation while MCP write tools stay allowlisted.
- Auth: every request supplies
X-Api-Key; the value must match theACTION_PROXY_API_KEYenvironment variable (missing/invalid keys return 401, unconfigured key returns 503). - Endpoints:
GET /actions/list_projects?search=foo→{ "projects": [...] }with optional case-insensitive name filter (automatically setsmember=<service-account-id>unless you override thememberquery parameter).GET /actions/get_project?project_id=123→{ "project": {...} }returning the full Taiga project payload for the given id.GET /actions/get_project_by_slug?slug=acme-backlog→{ "project": {...} }resolving the project via slug.GET /actions/list_epics?project_id=123&project_id=456→{ "epics": [...] }including the originatingproject_idfor each epic.GET /actions/list_stories?project_id=123&epic_id=456&search=prior+art&tag=ip→{ "stories": [...] }filtered by project, epic, keywords, tags, and optional pagination (page/page_size).GET /actions/statuses?project_id=123→{ "statuses": [...] }to drive status pickers.POST /actions/create_story→{ "story": {...} }; accepts the same payload as the MCP tool and resolves status slugs/names.POST /actions/update_story→{ "story": {...} }; acceptsstory_idplus any combination ofproject_id,subject,description,status,tags,assigned_to(status strings resolve to ids automatically).POST /actions/delete_story→{ "deleted": {"story_id": ...} }.POST /actions/add_story_to_epic→{ "link": {...} }after linking a story to an epic.POST /actions/create_epic/update_epic/delete_epic→ manage epics (project_id,subject, optionaldescription,status,assigned_to,tags,color).POST /actions/create_task/update_task/delete_task→ manage tasks (project_id,subject, optionaldescription,status,assigned_to,tags,user_story_id).POST /actions/create_issue/update_issue/delete_issue→ manage issues (project_id,subject, optionaldescription,status,priority,severity,type,assigned_to,tags).
- Error model: JSON
{ "error": "..." }payloads with 4xx for validation/Taiga errors and 500 for unexpected failures (also logged server-side). - Helper scripts (require
ACTION_PROXY_API_KEYandTAIGA_PROXY_BASE_URLto be set):\.\.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
- Raw curl samples:
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"
Local Development Workflow
- Prerequisites
- Python 3.11 (project uses a
.chat-venvvirtual environment by default). - Docker Desktop for container builds.
- Azure CLI for deployment automation.
- GHCR authentication (
docker login ghcr.io).
- Python 3.11 (project uses a
- Install dependencies
python -m venv .chat-venv.\.chat-venv\Scripts\python.exe -m pip install -r requirements.txt
- Configure environment
copy .env.example .env(PowerShell:Copy-Item .env.example .env) and update the values to point at your Taiga instance.
- Run the server locally
.\.chat-venv\Scripts\uvicorn.exe app:app --host 127.0.0.1 --port 8010- Streamable probe:
.\.chat-venv\Scripts\python.exe streamable_client.py http://127.0.0.1:8010/mcp --message "hello local"
- SSE manual test
curl.exe -sN -H "Accept: text/event-stream" http://127.0.0.1:8010/sse/
Container Build & Publish
- Pick an image name (default deployment uses
ghcr.io/johnwblack/taiga-mcp) and set:- PowerShell:
$env:CONTAINER_IMAGE = 'ghcr.io/johnwblack/taiga-mcp'and$env:IMAGE_TAG = 'v0.0.29' - Bash:
export CONTAINER_IMAGE=ghcr.io/johnwblack/taiga-mcpandexport IMAGE_TAG=v0.0.29
- PowerShell:
- Build tagged images (set
--platformwhen cross-building):docker build -t "$CONTAINER_IMAGE:$IMAGE_TAG" -t "$CONTAINER_IMAGE:latest" .
- Push to your registry:
docker push "$CONTAINER_IMAGE:$IMAGE_TAG"docker push "$CONTAINER_IMAGE:latest"
Azure Container Apps Deployment
- Export the following environment variables (update names to match your subscription):
$env:AZURE_RESOURCE_GROUP = 'your-resource-group'$env:AZURE_CONTAINER_APP = 'taiga-mcp'
- Deploy the new revision (after pushing the image):
az containerapp update -g $env:AZURE_RESOURCE_GROUP -n $env:AZURE_CONTAINER_APP --image "$CONTAINER_IMAGE:$IMAGE_TAG"
- Or run the helper script (builds, pushes, and deploys in one step; honours the same env vars and accepts overrides):
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"- Optional flags:
--skip-build,--skip-push,--latest-taghelp with hotfix redeploys
- Helpful Windows settings (avoid WinError 5 permission issues):
$env:AZURE_EXTENSION_DIR = Join-Path $HOME '.az-extensions'$env:AZURE_CONFIG_DIR = Join-Path $HOME '.az-cli'
Secret Management for Taiga Credentials
- Store Taiga credentials and proxy keys in your secret store of choice (Azure Container Apps secrets, Kubernetes secrets, etc.) and surface them as environment variables inside the container.
- Example (Azure Container Apps):
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
- Update or rotate the action proxy key the same way:
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
- The container expects the following environment variables to be present:
TAIGA_BASE_URL— base URL for the Taiga API (typically backed by a secret reference).TAIGA_USERNAME— service account username.TAIGA_PASSWORD— service account password.ACTION_PROXY_API_KEY— shared secret used by the/actions/*endpoints.
- Rotate credentials regularly and redeploy so that new revisions pick up the changes.
Connecting MCP Clients
- You are expected to deploy and host your own MCP server instance (for example on Azure Container Apps, Docker Desktop, or any other Python hosting target). This repository does not expose a shared public endpoint.
- ChatGPT Custom GPT setup:
- Open the GPT Builder UI, choose Create, and select Configure → Add under the Model Context Protocol tools section.
- Enter your deployed Streamable HTTP URL (for example
https://your-domain.example/mcp) as the endpoint and leave headers/body blank unless you have protected the route behind a proxy. - Save the GPT and test the
echotool to confirm connectivity. The session will reuse your deployed server on each invocation.
- Open-source MCP clients (such as the
mcpPython CLI or Claude Desktop) can target the same/mcpendpoint; setMCP_URLto your deployed URL before running helper scripts likestreamable_client.py. - If you regenerate the container image, existing environment variables and secret references remain attached to the Container App. Only set them again when new keys/variables are introduced or when credentials rotate.
Verification Checklist
- Streamable HTTP smoke test:
\.\.chat-venv\Scripts\python.exe streamable_client.py $env:MCP_URL --message "ping"
- SSE availability test:
curl.exe -sN -H "Accept: text/event-stream" "$env:TAIGA_PROXY_BASE_URL/sse/" --max-time 5
- Azure logs review (if deployed to ACA):
az containerapp logs show -g $env:AZURE_RESOURCE_GROUP -n $env:AZURE_CONTAINER_APP --tail 50
- ChatGPT connector validation:
- Configure ChatGPT or another MCP client with your deployed MCP endpoint and confirm the
echotool responds. - Exercise project, epic, and story workflows end-to-end to ensure Taiga credentials and permissions are correct.
- Configure ChatGPT or another MCP client with your deployed MCP endpoint and confirm the
Troubleshooting Notes
Not Acceptable: Client must accept text/event-stream— Ensure clients sendAccept: application/json, text/event-streamwhen calling/mcp/.Session terminatederrors usually indicate a redirect; verify the request hits/mcp/(with trailing slash) and that proxies are not rewriting headers.- Azure CLI
WinError 5permission issues are resolved by settingAZURE_EXTENSION_DIRandAZURE_CONFIG_DIRto user-writable locations. - After updating secrets, Azure Container Apps restarts the revision; allow 1–2 minutes before retesting endpoints.
Testing
- Install dev dependencies:
python -m pip install -r requirements.txt pytest(from the.chat-venvenvironment). - Run the Python unit suite:
pytest(covers/actions/*auth, validation, and Taiga error handling via fakes). - Smoke test the helper CLI locally:
.\.chat-venv\Scripts\python.exe scripts/actions_proxy_client.py --help. - PowerShell validation:
powershell.exe -File scripts/actions-proxy.ps1 list-projects -BaseUrl http://127.0.0.1:8010 -ApiKey local-testwhen the server is running with a dummy key.
Request Payload Reference
Stories
- Create (
POST /actions/create_story):{ "project_id": int, "subject": str, "description"?: str, "status"?: int|str, "tags"?: [str], "assigned_to"?: int } - Update (
POST /actions/update_story):{ "story_id": int, "project_id"?: int, "subject"?: str, "description"?: str, "status"?: int|str, "tags"?: [str], "assigned_to"?: int } - Delete (
POST /actions/delete_story):{ "story_id": int }
Epics
- Create (
POST /actions/create_epic):{ "project_id": int, "subject": str, "description"?: str, "status"?: int, "assigned_to"?: int, "tags"?: [str], "color"?: str } - Update (
POST /actions/update_epic):{ "epic_id": int, "subject"?: str, "description"?: str, "status"?: int, "assigned_to"?: int, "tags"?: [str], "color"?: str } - Delete (
POST /actions/delete_epic):{ "epic_id": int }
Tasks
- Create (
POST /actions/create_task):{ "project_id": int, "subject": str, "description"?: str, "status"?: int, "assigned_to"?: int, "tags"?: [str], "user_story_id"?: int } - Update (
POST /actions/update_task):{ "task_id": int, "subject"?: str, "description"?: str, "status"?: int, "assigned_to"?: int, "tags"?: [str], "user_story_id"?: int } - Delete (
POST /actions/delete_task):{ "task_id": int }
Issues
- Create (
POST /actions/create_issue):{ "project_id": int, "subject": str, "description"?: str, "status"?: int, "priority"?: int, "severity"?: int, "type"?: int, "assigned_to"?: int, "tags"?: [str] } - Update (
POST /actions/update_issue):{ "issue_id": int, "subject"?: str, "description"?: str, "status"?: int, "priority"?: int, "severity"?: int, "type"?: int, "assigned_to"?: int, "tags"?: [str] } - Delete (
POST /actions/delete_issue):{ "issue_id": int }
Azure AI Fallback (Future Option)
- Idea: expose Taiga access through an Azure OpenAI Assistants tool while the ChatGPT MCP allowlist is pending.
- Components: Azure Functions (or Container App) hosting the same FastMCP logic, Azure OpenAI Assistant registered with HTTPS tool endpoints, and service principal credentials stored in Key Vault.
- Status: deferred; revisit if the OpenAI allowlist remains blocked after proxy rollout. Keep scripts modular so the same helper payloads feed both the MCP proxy and any future Azure AI adapter.
Project History (October 2025 Highlights)
- Refactored Starlette routing to eliminate automatic slash redirects that stripped MCP session headers on Azure.
- Added middleware to normalize Streamable HTTP paths and ensure compatibility with Azure ingress rewrite behavior.
- Built and published container versions
v0.0.4throughv0.0.10, withv0.0.10deployed as revisiontaiga-mcp--0000016. - Validated SSE and Streamable HTTP transports using local clients,
curl, and the ChatGPT MCP connector. - Stored Taiga service account credentials securely in Azure Container Apps secrets to enable future write access to Taiga.
快速设置
此服务器的安装指南
安装包 (如果需要)
uvx taiga-mcp
Cursor 配置 (mcp.json)
{
"mcpServers": {
"johnwblack-taiga-mcp": {
"command": "uvx",
"args": [
"taiga-mcp"
]
}
}
}