Metadata-Version: 2.5
Name: orkestrr-mcp
Version: 0.5.0
Summary: MCP server exposing the Orkestrr project-management hierarchy (Bundles, Goals, Tasks) to AI agents
Project-URL: Homepage, https://orkestrr.com
Project-URL: Repository, https://github.com/cafadev/orkestrr-mcp
Project-URL: Issues, https://github.com/cafadev/orkestrr-mcp/issues
Author-email: Christopher Flores <christopherflores@moxie-link.com>
License-Expression: MIT
License-File: LICENSE
Keywords: ai-agents,claude,mcp,model-context-protocol,orkestrr,project-management
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: mcp<2,>=1.2.0
Description-Content-Type: text/markdown

# orkestrr-mcp

An [MCP](https://modelcontextprotocol.io) server that lets an AI agent (e.g. Claude Code)
**create, reuse, and manage** the Orkestrr project-management hierarchy — Bundles, Goals, and
Tasks — through the existing Orkestrr REST API.

It is a standalone process that talks to Orkestrr over HTTP, so once registered at **user scope**
it works from **any** project/repo you open, against **local or production** Orkestrr.

## What it exposes

Hierarchy tools (read + write):

| Area | Tools |
| --- | --- |
| Projects | `list_projects`, `get_project`, `list_project_members` |
| Bundles | `list_bundles`, `create_bundle`, `update_bundle` (incl. status & archive) |
| Goals | `list_goals`, `create_goal`, `update_goal` (incl. status) |
| Tasks | `list_tasks`, `get_task`, `create_task`, `update_task` (incl. status & assignment) |

Task details:

| Area | Tools |
| --- | --- |
| Status | `set_task_status` |
| Estimation | `list_estimation_values`, `set_task_estimation` |
| Labels | `list_labels`, `create_label`, `add_task_labels`, `remove_task_labels` |

The list tools return compact summaries so an agent can search and chain cheaply;
`get_task` is the one that returns a task's body — description, dates, people, acceptance
criteria, action items, comments, dependencies and linked PRs. Read it before working a
task: its description is where the agreed spec lives.

Status changes propagate up the hierarchy automatically (Task → Goal → Bundle → Project),
handled by the Orkestrr backend. `add_task_labels` reads the task's current labels and sends
the union (the API replaces the label set on update), so existing labels are preserved.

### Backend quirks worked around (candidates for a future backend fix)

* `POST /labels/` does not return the new label's `id` — `create_label` resolves it by
  re-querying labels by name.
* A task's `estimation_label` is null in list/update payloads (only the nested `estimation`
  object on the detail payload carries the label) — `set_task_estimation` re-fetches the detail
  so it can confirm the value.

## Authentication

You sign in **once**, interactively, in the browser with your EVOHEIM SSO (Authentik):

```bash
okr login     # opens the browser (OAuth device flow); approve the code
okr status    # shows who you're logged in as + token validity
okr logout    # clears the stored session
```

### Several accounts at once (profiles)

One profile = one stored session, so a second Orkestrr account costs no
logout/login round trip:

```bash
okr --profile moxie login     # signs in and stores the session separately
okr --profile moxie status
okr status                    # the default profile, still signed in
```

The flag just sets `ORKESTRR_PROFILE`, which is also what the MCP registration
reads — give each registered server its own profile and both accounts work in
parallel:

```bash
claude mcp add --scope user orkestrr-moxie \
  --env ORKESTRR_BASE_URL=https://api.orkestrr.com \
  --env ORKESTRR_PROFILE=moxie \
  -- okr mcp
```

A profile's session lives in `~/.config/orkestrr-mcp/<profile>/tokens.json`.

### Switching organization within one account

When a single account belongs to several organizations, the org is a property of
the account, not of the session — switch it instead of signing in again:

```bash
okr org list              # '*' marks the selected one
okr org use "Moxie Health"   # name or id
```

The selection is account-wide: the web app follows it too.

`okr login` uses the OAuth 2.0 **Device Authorization Flow** against the public
`orkestrr-cli` Authentik provider. The issued JWT carries your real identity, so tasks and
status changes stay attributed to you. Tokens are stored in
`~/.config/orkestrr-mcp/tokens.json` (mode `0600`) and **auto-refresh** — the MCP server
never needs an interactive prompt. No secrets go into the MCP registration.

The browser is sent to whichever approval URL the identity provider names in its
device-code response, so the flow follows the server rather than a URL built here.

If a tool ever fails with *"Not logged in"* or *"Session expired"*, just run `okr login` again.
Upgrading from 0.3.0 may ask for one such re-login: the CLI now talks to
`auth.orkestrr.com` instead of `auth.evoheim.com`.

## Configuration

| Variable | Example | Notes |
| --- | --- | --- |
| `ORKESTRR_BASE_URL` | *(none — required)* | API root. `https://api.orkestrr.com` in production, `http://localhost:9000` for local. `/api` is appended when absent. |
| `ORKESTRR_MCP_HOME` | `~/.config/orkestrr-mcp` | Optional. Override the directory where the session is stored. |
| `ORKESTRR_PROFILE` | `moxie` | Optional. Keep a separate session per account, under `<home>/<profile>/`. Same as `okr --profile`. |
| `ORKESTRR_AUTH_URL` | `https://auth.orkestrr.com` | Optional. The Authentik that issues and approves the CLI's tokens. |

## Install

From PyPI (recommended — no repo checkout needed):

```bash
uv tool install orkestrr-mcp        # or: pipx install orkestrr-mcp
# installs the `okr` command on your PATH; upgrade with `uv tool upgrade orkestrr-mcp`
```

From source (for development on the server itself):

```bash
git clone git@github.com:cafadev/orkestrr.git
uv tool install --editable ./orkestrr/orkestrr-mcp
```

## Register with Claude Code (user scope = available in every project)

No credentials in the registration — the server command is `okr mcp`, and it uses your
`okr login` session.

```bash
# Local Orkestrr
claude mcp add --scope user orkestrr-local \
  --env ORKESTRR_BASE_URL=http://localhost:9000 \
  -- okr mcp

# Production Orkestrr
claude mcp add --scope user orkestrr \
  --env ORKESTRR_BASE_URL=https://api.orkestrr.com \
  -- okr mcp
```

`ORKESTRR_BASE_URL` is the API root; `/api` is appended when the URL does not
already end in it, so the production host resolves to `https://api.orkestrr.com/api`.

Then run `okr login` once (it works for both environments — same Authentik). Restart Claude
Code and run `/mcp` to confirm the tools are listed.

## Develop

```bash
uv sync          # install deps + this package (editable)
uv run pytest    # run the test suite
```

## Publishing to PyPI (maintainers)

Publishing is automated by GitHub Actions
([`.github/workflows/orkestrr-mcp-publish.yml`](../.github/workflows/orkestrr-mcp-publish.yml))
using **PyPI Trusted Publishing (OIDC)** — no API token is stored anywhere.

**One-time setup on PyPI** (do this before the first release): on
<https://pypi.org/manage/account/publishing/> add a *pending* trusted publisher:

| Field | Value |
| --- | --- |
| PyPI Project Name | `orkestrr-mcp` |
| Owner | `cafadev` |
| Repository name | `orkestrr` |
| Workflow name | `orkestrr-mcp-publish.yml` |
| Environment | *(leave blank)* |

**Each release:**

1. Bump `version` in `pyproject.toml` (PyPI never lets you re-upload the same version) and merge it.
2. Create a GitHub Release whose **tag starts with `orkestrr-mcp-v`** (e.g. `orkestrr-mcp-v0.1.0`).
   The tag prefix is what triggers the workflow (and keeps it monorepo-safe).

The workflow builds the sdist + wheel and publishes them. Teammates then `uv tool install orkestrr-mcp`.

**Manual fallback** (if Actions is unavailable) — needs an API token from
Account settings → API tokens:

```bash
rm -rf dist && uv build
uvx twine check dist/*
UV_PUBLISH_TOKEN=pypi-XXXX uv publish
```

## Out of scope (for now)

Acceptance criteria, action items, comments, blockers, and estimation-scale configuration
(creating/editing the scale itself — only assigning existing values is supported). A dedicated
API-key / OAuth2 machine-to-machine auth layer is also deferred — token auth covers current needs.
