Metadata-Version: 2.5
Name: scrumdo-mcp
Version: 0.6.0
Summary: MCP server for ScrumDo boards — connect any AI tool directly to your board
Project-URL: Homepage, https://www.scrumdo.com
Project-URL: Documentation, https://stage.scrumdo.com/byoa/mcp/
Project-URL: Support, https://www.scrumdo.com/contact/
Author-email: ScrumDo LLC <support@scrumdo.com>
License: MIT
License-File: LICENSE
Keywords: agile,ai,claude,cursor,kanban,llm,mcp,model-context-protocol,scrum,scrumdo,windsurf
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Scheduling
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: httpx==0.27.2
Requires-Dist: mcp[cli]==1.29.1
Requires-Dist: pydantic==2.12.5
Requires-Dist: python-dotenv==1.2.3
Requires-Dist: pyyaml==6.0.1
Provides-Extra: dev
Requires-Dist: build==1.3.0; extra == 'dev'
Requires-Dist: cryptography==50.0.0; extra == 'dev'
Requires-Dist: detect-secrets==1.5.0; extra == 'dev'
Requires-Dist: pip-audit==2.9.0; extra == 'dev'
Requires-Dist: pyjwt==2.13.0; extra == 'dev'
Requires-Dist: pytest-asyncio==1.4.0; extra == 'dev'
Requires-Dist: pytest==9.0.3; extra == 'dev'
Requires-Dist: respx==0.23.1; extra == 'dev'
Requires-Dist: twine==7.0.0; extra == 'dev'
Description-Content-Type: text/markdown

# scrumdo-mcp

Connect Claude Code, Codex, Cursor, OpenCode, Antigravity, GitHub Copilot, and
other MCP hosts to your
[ScrumDo](https://www.scrumdo.com) / [Spryng](https://app.spryng.io) boards
over **local stdio**.

The default connection is a **personal `smcp_` token** and the **collaborate**
tool profile: read, comment, propose, and follow events as you. A broad
organization API key is **not** equivalent human identity.

Full setup and host-specific guidance: [ScrumDo MCP documentation](https://stage.scrumdo.com/byoa/mcp/).

---

## Installation

```bash
pip install scrumdo-mcp
```

Requires Python 3.11+. The process speaks MCP over **stdio** (`scrumdo-mcp`).
That local path is the supported compatibility floor.

The hosted Django endpoint
`POST https://app.spryng.io/api/scrumdo/organizations/<org>/mcp/rpc/` is a
**transitional bearer beta**. It is **not** OAuth and is **not** official
Streamable HTTP general availability. Prefer stdio until remote GA.

---

## Get a personal token

1. Sign in at [https://app.spryng.io](https://app.spryng.io).
2. Open your organization → **Settings → MCP Tokens**.
3. Create a named personal connection and copy the secret once.

Personal tokens start with `smcp_`. Do not use **Settings → API Tokens**
(OrgAPIKey) for human AI-client collaboration.

Your org and project slugs are the path segments in
`app.spryng.io/`**`my-company`**`/`**`engineering`**.

---

## Local stdio (supported)

Replace the placeholder. Never commit a real token.

### Cursor — `~/.cursor/mcp.json`

```json
{
  "mcpServers": {
    "scrumdo": {
      "command": "scrumdo-mcp",
      "env": {
        "SCRUMDO_TOKEN": "smcp_YOUR_PERSONAL_TOKEN",
        "SCRUMDO_ORG": "your-org-slug",
        "SCRUMDO_PROJECT": "your-default-project-slug",
        "SCRUMDO_BASE_URL": "https://app.spryng.io",
        "SCRUMDO_MCP_PROFILE": "collaborate",
        "SCRUMDO_CLIENT_NAME": "cursor"
      }
    }
  }
}
```

### Claude Code — `~/.claude.json`

Same `mcpServers` shape as Cursor. Prefer the CLI so you do not edit
`~/.claude/claude.json` by mistake:

```bash
claude mcp add scrumdo \
  -e SCRUMDO_TOKEN=smcp_YOUR_PERSONAL_TOKEN \
  -e SCRUMDO_ORG=your-org-slug \
  -e SCRUMDO_PROJECT=your-default-project-slug \
  -e SCRUMDO_BASE_URL=https://app.spryng.io \
  -e SCRUMDO_MCP_PROFILE=collaborate \
  -e SCRUMDO_CLIENT_NAME=claude-code \
  -- scrumdo-mcp
```

### Codex — `~/.codex/config.toml`

```toml
[mcp_servers.scrumdo]
command = "scrumdo-mcp"

[mcp_servers.scrumdo.env]
SCRUMDO_TOKEN = "smcp_YOUR_PERSONAL_TOKEN"
SCRUMDO_ORG = "your-org-slug"
SCRUMDO_PROJECT = "your-default-project-slug"
SCRUMDO_BASE_URL = "https://app.spryng.io"
SCRUMDO_MCP_PROFILE = "collaborate"
SCRUMDO_CLIENT_NAME = "codex"
```

Restart Codex after changing the token so the stdio process picks up the new env.

### OpenCode — `opencode.json`

OpenCode uses a top-level `mcp` object (not `mcpServers`):

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "scrumdo": {
      "type": "local",
      "command": ["scrumdo-mcp"],
      "enabled": true,
      "environment": {
        "SCRUMDO_TOKEN": "smcp_YOUR_PERSONAL_TOKEN",
        "SCRUMDO_ORG": "your-org-slug",
        "SCRUMDO_PROJECT": "your-default-project-slug",
        "SCRUMDO_BASE_URL": "https://app.spryng.io",
        "SCRUMDO_MCP_PROFILE": "collaborate",
        "SCRUMDO_CLIENT_NAME": "opencode"
      }
    }
  }
}
```

### Antigravity

Use the same `mcpServers` shape as Cursor and set
`SCRUMDO_CLIENT_NAME=antigravity`. The connector still enforces the personal
`smcp_` credential and `collaborate` profile server-side.

### GitHub Copilot Chat — `.vscode/mcp.json`

Use Copilot Chat in Agent mode with a local stdio server:

```json
{
  "servers": {
    "scrumdo": {
      "type": "stdio",
      "command": "scrumdo-mcp",
      "env": {
        "SCRUMDO_TOKEN": "smcp_YOUR_PERSONAL_TOKEN",
        "SCRUMDO_ORG": "your-org-slug",
        "SCRUMDO_PROJECT": "your-default-project-slug",
        "SCRUMDO_BASE_URL": "https://app.spryng.io",
        "SCRUMDO_MCP_PROFILE": "collaborate",
        "SCRUMDO_CLIENT_NAME": "github-copilot-vscode"
      }
    }
  }
}
```

### GitHub Copilot CLI — `~/.copilot/mcp-config.json`

```json
{
  "mcpServers": {
    "scrumdo": {
      "type": "stdio",
      "command": "scrumdo-mcp",
      "args": [],
      "env": {
        "SCRUMDO_TOKEN": "smcp_YOUR_PERSONAL_TOKEN",
        "SCRUMDO_ORG": "your-org-slug",
        "SCRUMDO_PROJECT": "your-default-project-slug",
        "SCRUMDO_BASE_URL": "https://app.spryng.io",
        "SCRUMDO_MCP_PROFILE": "collaborate",
        "SCRUMDO_CLIENT_NAME": "github-copilot-cli"
      }
    }
  }
}
```

GitHub Copilot cloud agent is not this personal-machine flow. Do not put a
person's `smcp_` token into a shared repository or organization Agent secret.

---

## Hosted remote (beta, not GA)

You can point a remote-capable host at:

`https://app.spryng.io/api/scrumdo/organizations/<org>/mcp/rpc/`

with `Authorization: Bearer smcp_…`.

This is the current Workbench bearer JSON-RPC POST. It is **not** OAuth 2.1,
Protected Resource Metadata, or full Streamable HTTP GA. Treat it as beta.
Local stdio remains the compatibility floor.

Microsoft 365 Copilot through Copilot Studio is not supported by this beta
endpoint. It requires the planned standards-conformant Streamable HTTP endpoint
and delegated per-user OAuth; a shared API key is not equivalent human identity.

---

## Tool profiles

| Profile | Env | What the model sees |
|---------|-----|---------------------|
| **collaborate** (default) | `SCRUMDO_MCP_PROFILE=collaborate` or unset | Identity/capability discovery; card search/read and handoff brief; comments and blackboard notes; spec read and publish-as-proposal; proposal read/revise/request-changes; decision inbox and challenge status; run read/status; `events.wait` / `events.ack` |
| **compat** | `SCRUMDO_MCP_PROFILE=compat` | All registered tools, including card mutation, deletes, webhooks, inbox `wait_for_notifications`, direct spec writes, and run/loop writes. Aliases: `full`, `legacy`, `all`. |

Call `get_mcp_capabilities()` for the exact `tool_count` and names on your
process. This package version registers **56** tools on `collaborate` and
**135** on `compat` (the previous 133 plus `events.wait` / `events.ack`).
Compat restores the large surface **only** when you opt in.

Deprecated / inbox-coupled: `wait_for_notifications` stays in **compat**
only. New clients should use `events.wait` / `events.ack` (connection cursor,
independent of the web inbox).

---

## Identity

- Personal `smcp_` token → `credential_kind=personal_mcp`, human collaboration.
- Broad OrgAPIKey → `org_api_key`. The server **refuses to start** unless you
  set `SCRUMDO_ALLOW_ORG_API_KEY=1`. That flag is an explicit, documented
  compatibility mode. It does **not** make the key equivalent to a person.
- `SCRUMDO_AGENT_RUN_ID` describes a run-scoped session, not human identity.

---

## Events

`events.wait(after?, timeout_s, limit)` and `events.ack(cursor)` follow the
frozen connection-cursor contract. Envelopes are `schema_version` 1 with a
`safe_summary` only. If the cursor is behind the 30-day retention floor the
adapter returns `cursor_expired` plus a handoff instruction.

---

## Security

- Create and revoke tokens at **Settings → MCP Tokens**, not API Tokens.
- The secret is shown once. Do not put a real token in chat, git, tests, or docs.
- Default collaborate omits deletes, webhook admin, org config, secrets,
  deployment, and direct accepted-spec writes.
- Remote API origins must use HTTPS. Plain HTTP is accepted only for localhost
  and loopback addresses used during local development.
- Compat-profile local attachment uploads are disabled until
  `SCRUMDO_ATTACHMENT_ROOTS` explicitly allows one or more directories. Resolved
  paths outside those roots, including symlink escapes, are rejected.
- Concurrent writes that this package translates carry `Idempotency-Key` /
  `expected_version` (and `If-Match`) when callers supply them. Closed error
  codes include `not_found_or_denied`, `wrong_organization`,
  `connection_revoked`, `grant_required`, `human_step_up_required`,
  `stale_resource`, `duplicate_command`, `rate_limited`, `cursor_expired`,
  `unsupported_client_capability`.
- Revoke a connection if a machine is lost. The next operation fails.

---

## Environment variables

| Variable | Default | Description |
|----------|---------|-------------|
| `SCRUMDO_TOKEN` | — | **Required.** Personal `smcp_` token from Settings → MCP Tokens |
| `SCRUMDO_ORG` | — | Organization slug |
| `SCRUMDO_PROJECT` | — | Default project slug |
| `SCRUMDO_BASE_URL` | `https://app.spryng.io` | API base URL |
| `SCRUMDO_MCP_PROFILE` | `collaborate` | `collaborate` or `compat` |
| `SCRUMDO_ALLOW_ORG_API_KEY` | unset | Deprecated opt-in to start with an OrgAPIKey |
| `SCRUMDO_AGENT_RUN_ID` | — | Optional run attribution header |
| `SCRUMDO_CLIENT_NAME` | `mcp` | Host name (`codex`, `claude-code`, `cursor`, `opencode`, `antigravity`, `github-copilot-vscode`, or `github-copilot-cli`) |
| `SCRUMDO_ATTACHMENT_ROOTS` | unset | Compat-only local attachment allowlist; use the platform path separator for multiple directories |
| `SPRYNG_LOOP_ID` | — | Optional loop attribution |

Legacy `SPRYNG_*` names are still accepted as aliases.

---

## What you can do (collaborate)

```
"What cards need my attention?"
"Read ENG-42 and the handoff brief"
"Comment on ENG-42 that the proposal is up"
"Publish this spec as a proposal on ENG-42"
"Wait for the next board event for this connection"
```

Direct column moves, webhook admin, and accepted-spec overwrites are not on
the default profile.

---

## Live conformance (opt-in)

The package includes a stdio-protocol harness (`python -m spryng_mcp.conformance`).
The default `hermetic` mode launches two real `scrumdo-mcp` processes against an
in-process HTTP fixture. It does **not** talk to Django.

The two-user **live** run is opt-in and fail-closed. It never reads tokens from
source or fixtures. Export personal `smcp_` values only in the process
environment (never commit them):

| Variable | Required for live | Purpose |
|----------|-------------------|---------|
| `SCRUMDO_LIVE_CONFORMANCE` | yes (`1`) | Opt-in gate |
| `SCRUMDO_ALICE_TOKEN` | yes | Personal connection A |
| `SCRUMDO_BOB_TOKEN` | yes | Personal connection B |
| `SCRUMDO_READONLY_TOKEN` | yes | Read-only / guest control |
| `SCRUMDO_UNRELATED_TOKEN` | yes | Unrelated-room control |
| `SCRUMDO_REVOKED_TOKEN` | yes | Already-revoked personal token |
| `SCRUMDO_TARGET_RUN_ID` | yes | AgentRun id under test (do not reuse `SCRUMDO_AGENT_RUN_ID`) |
| `SCRUMDO_BASE_URL` | no | Default `http://127.0.0.1:8018` |
| `SCRUMDO_ORG` | no | Default `test-account-a` |
| `SCRUMDO_PROJECT` | no | Default room project slug |
| `SCRUMDO_CARD_REF` | no | Default `AG-2` |
| `SCRUMDO_FOREIGN_ORG` | no | Other-org slug for closed denial |
| `SCRUMDO_ALICE_CLIENT` / `SCRUMDO_BOB_CLIENT` | no | Distinct client names |
| `SCRUMDO_COPILOT_TOKEN` | no | Defaults to Alice; use with Copilot client name |
| `SCRUMDO_ORG_API_KEY` | no | Negative OrgAPIKey case |
| `SCRUMDO_WEB_SESSIONID` | no | Django session cookie for s8 web-inbox independence |
| `SCRUMDO_WEB_CSRFTOKEN` | no | CSRF token paired with the web session |
| `SCRUMDO_WEB_SESSION_COOKIE_NAME` | no | Session cookie name; defaults to `sessionid` (Spryng uses `sprsession`) |

```bash
python -m spryng_mcp.conformance hermetic
SCRUMDO_LIVE_CONFORMANCE=1 python -m spryng_mcp.conformance live
```

If a required live name is missing, the command exits 2 with a missing-fixture
message that lists **names only**.

---

## Remote MCP conformance (AG-07C)

Hermetic delegated-OAuth and hosted Streamable HTTP proof lives in this
package. It uses official MCP Inspector, NAV `mock-oauth2-server`, and
`oidc-provider` as **test-only** processes. It is **not** the future
real-Microsoft-tenant AG-07D gate, and it does not enable Microsoft 365
Copilot.

See [docs/AG-07C-RUNBOOK.md](docs/AG-07C-RUNBOOK.md). One command:

```bash
./scripts/ag07c-gate
```

---

## Development (maintainers)

```bash
pip install -e ".[dev]"
pytest tests/ -v
python -m spryng_mcp.conformance hermetic
```

Source access is restricted to maintainers. Package users should install the
public wheel from PyPI and use the public documentation linked above.

---

## License

MIT
