Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ jobs:
uses: actions/cache@v6
with:
path: .tox
# No restore-keys fallback: a partial match would restore a .tox env built
# against an older setup.cfg, whose dependencies tox won't re-resolve on a
# plain run (it only reinstalls deps when their own declaration text changes,
# not when setup.cfg's extras do) - a cache miss should mean a clean install,
# not a stale/broken one.
key: ${{ runner.os }}-lint-${{ matrix.toxenv }}-${{ hashFiles('setup.cfg') }}
restore-keys: |
${{ runner.os }}-lint-${{ matrix.toxenv }}-
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools tox>4
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ jobs:
uses: actions/cache@v6
with:
path: .tox
# No restore-keys fallback: a partial match would restore a .tox env built
# against an older setup.cfg, whose dependencies tox won't re-resolve on a
# plain run (it only reinstalls deps when their own declaration text changes,
# not when setup.cfg's extras do) - a cache miss should mean a clean install,
# not a stale/broken one.
key: ${{ runner.os }}-tox-${{ format('{{py{0}}}', matrix.python-version) }}-${{ hashFiles('setup.cfg') }}
restore-keys: |
${{ runner.os }}-tox-${{ format('{{py{0}}}', matrix.python-version) }}-
- name: Install dependencies
run: |
sudo apt-get install gettext
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ debian/files
debian/python-taiga*
debian/python3-taiga*
.ruff_cache
.venv
*.egg-link
.superpowers
112 changes: 112 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Agent instructions

This file gives coding agents (Claude Code and similar) step-by-step
instructions for tasks specific to this repository. Human-facing docs live in
``README.rst`` and ``docs/``.

## Registering the Taiga MCP server in the user's global Claude config

This repo ships an MCP server (`taiga/mcp_server/`) that exposes the Taiga
REST API as tools over stdio, via the `taiga-mcp-server` console script
(installed by the `mcp` extra: `pip install -e .[mcp]` from this repo, or
`pip install python-taiga[mcp]` from PyPI).

When asked to "add the Taiga MCP server to Claude" / "register taiga-mcp
globally" / "add it to my user-wide config", follow this procedure:

1. **Confirm before acting.** Registering at user scope changes the user's
global Claude Code config (`~/.claude.json`), applying to every project,
not just this repo. Confirm the target Taiga instance and scope with the
user before running the command, unless they've already given explicit
go-ahead in this conversation.

2. **Get a stable `taiga-mcp-server` binary.** Don't point the MCP config at
a project-local `.venv` — Claude Code launches MCP server commands without
inheriting an activated venv, and the binary disappears if that venv is
ever recreated. Install it somewhere durable instead. There are several
equally valid ways to do this; pick whichever fits the user's toolchain,
asking if it's unclear, and default to `pip install --user` since it needs
nothing beyond a reasonably modern Python:
```bash
# default: pip install --user (works with any modern Python/pip)
pip install --user "python-taiga[mcp]" # from PyPI
pip install --user -e ".[mcp]" # from this checkout

# pipx (isolated venv per tool, one binary on PATH)
pipx install "python-taiga[mcp]" # from PyPI
pipx install --editable ".[mcp]" # from this checkout

# uvx (no persistent install; uv manages an ephemeral/cached env)
# here the *registered command* becomes `uvx --from "python-taiga[mcp]" taiga-mcp-server`
# instead of a resolved path — see the uvx example in step 4.
```
After a `pip --user`/`pipx` install, resolve the resulting path and use it
verbatim in step 4:
```bash
command -v taiga-mcp-server
```

3. **Collect credentials.** Ask the user for:
- `TAIGA_HOST` — the Taiga site root, e.g. `https://my.taiga.com`.
For self-hosted instances this is *not* an `api.` subdomain and has no
`/api` suffix — the client appends `/api/v1` itself.
- Either `TAIGA_TOKEN` (pre-issued API token), or both
`TAIGA_USERNAME` and `TAIGA_PASSWORD`. A token takes precedence if both
are configured.
- Optional: `TAIGA_TOKEN_TYPE` (default `Bearer`), `TAIGA_TLS_VERIFY`
(default `true`).

Never pass `--token`/`--password` as CLI arguments — they'd be visible in
the process list. Always pass credentials as environment variables.

**Default to username/password over a token, unless the instance has a
real personal-access-token feature.** Stock Taiga (checked against
`https://my.taiga.com`) has no self-service PAT: the only tokens it
issues are (a) short-lived JWTs from `POST /api/v1/auth` — on that
instance, a 24h access token / 8-day refresh token — and (b) OAuth-style
"Application" tokens, which require an admin-registered app and a
consent/`auth_code` flow (`client.auth_app()`), not something a regular
user can self-serve. This server's `auth.py`/CLI has no refresh-token
support, so a manually-generated `TAIGA_TOKEN` will just silently stop
working after ~24h with no renewal — worse than username/password, which
re-authenticates fresh on every server start. Only reach for `TAIGA_TOKEN`
when the target instance genuinely offers a durable personal token (e.g.
a Taiga Enterprise/hosted deployment with PAT support) — verify that
before recommending it, don't assume it exists.

4. **Register at user scope** with `claude mcp add`, using `-e` for every
credential env var and the resolved binary (or `uvx` invocation) from
step 2:
```bash
claude mcp add --scope user taiga \
-e TAIGA_HOST=https://my.taiga.com \
-e TAIGA_USERNAME=<username> \
-e TAIGA_PASSWORD=<password> \
-- /absolute/path/to/taiga-mcp-server
```
or, with a token instead of username/password:
```bash
claude mcp add --scope user taiga \
-e TAIGA_HOST=https://my.taiga.com \
-e TAIGA_TOKEN=<token> \
-- /absolute/path/to/taiga-mcp-server
```
With `uvx` there's no path to resolve — pass the `uvx` invocation itself
as the command:
```bash
claude mcp add --scope user taiga \
-e TAIGA_HOST=https://my.taiga.com \
-e TAIGA_TOKEN=<token> \
-- uvx --from "python-taiga[mcp]" taiga-mcp-server
```
`--scope user` (not `local`/`project`) is what makes it "user-wide" —
available in every project for that user, stored outside this repo.

5. **Verify** with `claude mcp list` (look for `taiga` ... `✔ Connected`) and
`claude mcp get taiga`. If it fails to connect, re-check the resolved
binary/command from step 2 and that `TAIGA_HOST` is the site root, not an
API subdomain.

6. **Don't persist secrets in the repo.** Credentials belong only in the
`claude mcp add -e ...` invocation (stored in the user's own
`~/.claude.json`) — never write them into files inside this repository.
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
include AGENTS.md
include AUTHORS
include LICENSE
include README.rst
include CONTRIBUTING.rst
include HISTORY.rst
include requirements.txt
include requirements-tests.txt
include requirements-test.txt
recursive-include taiga *.html *.png *.gif *js *jpg *jpeg *svg *py *mo *po
42 changes: 42 additions & 0 deletions artifacts/activity-log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Activity Log

## 2026-08-24 — Swapped fastmcp for the official mcp SDK in the Taiga MCP server
**What:** Rewrote `taiga/mcp_server/server.py` to build on the official MCP Python
SDK's `MCPServer` (`mcp.server.mcpserver`, `mcp~=2.0`) instead of the third-party
`fastmcp` package; updated the `mcp` extra in `setup.cfg` and the `docs/mcp.rst`
dependency mention accordingly. On `feature/issue-267-add-mcp`, as a follow-up to
the MCP server added earlier on that same branch.
**Why:** User asked to rewrite the MCP server on the official SDK instead of the
`fastmcp` wrapper, specifically pinned to `mcp~=2.0`.
**Decisions:**
- Classified as a *bounded* change (brainstorming skill) — existing flow, small
mechanical diff — so no spec/plan artifact, direct implementation after in-chat
design approval.
- Confirmed by installing `mcp~=2.0` in a scratch venv: mcp 2.0 renamed
`fastmcp.FastMCP`/`mcp.server.fastmcp.FastMCP` to `mcp.server.mcpserver.MCPServer`
(no back-compat alias), and requires the `@mcp.tool()` call form — bare
`@mcp.tool` raises `TypeError` at import time.
- Renamed to `MCPServer` throughout (chose over aliasing to `FastMCP`) to match
upstream naming exactly, per user preference.
- Stayed on the existing `feature/issue-267-add-mcp` branch rather than cutting a
new one — this is a continuation of the same feature, not new scope.
- Left the working tree uncommitted (per chosen commit strategy) pending user
review before splitting into commits.
**Agent usage:**

| Stage | Agent/skill | Tokens | Time |
|---|---|---|---|
| Review | general-purpose (requesting-code-review) | ~82k | ~4m |
| Review | nephila-core-conventions:code-eval | ~5k | ~2m |
| Review | nephila-core-conventions:doc-sync | ~3k | ~1m |

**Considered & dropped:** low-level `mcp.server.lowlevel.Server` rewrite (hand-rolled
schemas/dispatch) — rejected as unnecessary boilerplate once the official SDK's
own FastMCP-equivalent (`MCPServer`) covered the same decorator ergonomics.
Aliasing the new class as `FastMCP` to minimize diff size — rejected in favor of
the real name for clarity to future readers.
**Follow-ups:** `docs/mcp.rst` was updated for the dependency description; no other
doc/config files referenced `fastmcp` by name. Optional (not done): an explicit
tool-count/import smoke test for the SDK swap, and a towncrier fragment for the
dependency change (feature is still unreleased on this branch, so not required).
**Refs:** #267. Eval: 87% — artifacts/evaluations/2026-08-24-mcp-sdk-rewrite.md
26 changes: 26 additions & 0 deletions artifacts/evaluations/2026-08-24-mcp-sdk-rewrite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Evaluation — mcp-sdk-rewrite

- **Date:** 2026-08-24
- **Branch:** feature/issue-267-add-mcp (working tree, uncommitted)
- **Task:** #267 (follow-up: swap `fastmcp` for the official `mcp` SDK, `mcp~=2.0`)
- **Coverage:** partial — scoped to this task's diff only (`setup.cfg`, `taiga/mcp_server/server.py`, 2 files / 37+37 lines). Excludes the rest of the already-committed MCP feature on this branch, which was a separate prior deliverable.

## Priority findings
- Documentation ≤ 2: `docs/mcp.rst:24-25` still describes `fastmcp` as the pulled-in dependency, contradicting the code now on `mcp~=2.0` — fix is queued in the immediately-following doc-sync step.

## Scores
| Dimension | Score | Weight | Key evidence |
|---|---|---|---|
| Functionality | 5 | 20 | 66/66 tests pass against real `mcp~=2.0` in a scratch venv; stdio smoke test lists all 34 tools with instructions preserved verbatim. |
| Testing | 4 | 15 | Existing suite exercises every tool function directly and would fail at import if `MCPServer`/decorator form were wrong (reviewer confirmed); no explicit assertion of tool count/import success as a named test. |
| Security | 4 | 15 | No new input handling introduced; diff is import/class-name/decorator-form only (server.py:9,14,57...). |
| Code quality & best practices | 5 | 15 | Mechanical, minimal diff matching stated intent exactly; no stray bare `@mcp.tool` or leftover `fastmcp` refs (verified via grep). |
| Maintainability & flexibility | 5 | 15 | Matches upstream naming (`MCPServer`) rather than aliasing; drops one third-party dependency. |
| Error handling | N/A | 10 | Diff touches no error-handling paths (`auth.py`/`ConfigError` untouched). |
| Documentation | 2 | 10 | `docs/mcp.rst` still names `fastmcp` as the dependency (see priority finding above). |

## Recommendations
- Documentation: run doc-sync now to update `docs/mcp.rst`'s install-extra description and the `pypi.org/project/fastmcp` link.

## Total
**87%** — Clean, correctly-verified mechanical swap; the only real gap is a stale doc line already queued for the next step.
1 change: 1 addition & 0 deletions changes/267.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add MCP server exposing Taiga projects, user stories, tasks, issues, epics, milestones and wiki pages as tools for AI agents
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Welcome to python-taiga's documentation!
:maxdepth: 3

usage
mcp
api
models
development
Expand Down
Loading
Loading