Skip to main content
Glama
panitw

jira-pm-mcp

by panitw
README.md
# jira-pm-mcp

An MCP server that wraps the JIRA Cloud REST API for AI-driven agile project management. Designed to be run as a daily batch by an AI agent (e.g. via OpenClaw) to monitor sprint health, sync labels, flag risks, and write audit receipts.

## Requirements

- Node.js 20+
- JIRA Cloud instance
- API token with edit-issues and add-comments permissions on the target projects

## Setup

```bash
npm install
npm run build
```

### Environment variables

| Variable | Required | Description |
|---|---|---|
| `JIRA_BASE_URL` | yes | Base URL of your JIRA instance (e.g. `https://yourcompany.atlassian.net`) |
| `JIRA_EMAIL` | yes | Email address tied to the API token |
| `JIRA_API_TOKEN` | yes | JIRA Cloud API token |
| `PORT` | no | When set, starts an HTTP/SSE server on this port instead of stdio |

### Transport modes

The server supports two transport modes selected at startup.

#### stdio (default)

The server process is spawned and managed by the MCP client. Env vars are passed through the client config. Best for local single-user setups.

```json
{
  "mcpServers": {
    "jira-pm": {
      "command": "node",
      "args": ["/path/to/jira-pm-mcp/dist/index.js"],
      "env": {
        "JIRA_BASE_URL": "https://yourcompany.atlassian.net",
        "JIRA_EMAIL": "you@company.com",
        "JIRA_API_TOKEN": "your-token"
      }
    }
  }
}
```

#### HTTP/SSE

The server runs as a persistent HTTP process. The MCP client connects to it via SSE. Best for running the server as a system service or Docker container shared across multiple clients.

**Start the server:**

```bash
export JIRA_BASE_URL=https://yourcompany.atlassian.net
export JIRA_EMAIL=you@company.com
export JIRA_API_TOKEN=your-token
export PORT=3000
node dist/index.js
```

**Claude Desktop config (`claude_desktop_config.json`):**

```json
{
  "mcpServers": {
    "jira-pm": {
      "url": "http://localhost:3000/sse"
    }
  }
}
```

**Endpoints:**

| Endpoint | Method | Description |
|---|---|---|
| `/sse` | GET | SSE stream — MCP client connects here |
| `/message?sessionId=<id>` | POST | MCP message delivery (used internally by client) |
| `/health` | GET | Liveness probe — returns `{"status":"ok","sessions":<n>}` |

---

## Tools

### `setup_project`

Configure a project for PM Agent monitoring. Must be run before any other tools. Validates connectivity and write permissions, then stores configuration as a YAML comment on the Root Epic and registers the project locally.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `project_key` | string | yes | JIRA project key (e.g. `PROJ`) |
| `root_epic_key` | string | yes | Key of the root epic that parents all cross-project tasks (e.g. `PROJ-1`) |
| `board_id` | number | no | JIRA board ID (optional, for validation only) |
| `epic_link_field` | string | no | JQL field for child tasks. `"Epic Link"` (default, classic projects) or `"parent"` (next-gen/team-managed) |
| `active_statuses` | string[] | no | Status names that count as in-progress. Default: `In Progress, In Review, Code Review, In Testing` |
| `committed_date` | string | no | Project delivery deadline `YYYY-MM-DD`. Enables delivery risk and velocity tracking. |
| `story_points_field` | string | no | Custom field key for story points. Default: `customfield_10016` |

**Returns:** A checklist of validation results (`root_epic`, `fix_versions`, `write_permissions`, `config_written`) plus the config comment ID.

---

### `list_projects`

List all projects registered with the PM Agent, with their configuration summary.

No parameters.

**Returns:** Array of projects with `project_key`, `root_epic_key`, `committed_date`, `epic_link_field`, `active_statuses`, and `config_available`.

---

### `list_fix_versions`

List all fixVersions (sprints) for a project. Identifies the currently active sprint and how far through it we are.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `project_key` | string | yes | JIRA project key |

**Returns:** Array of versions with `name`, `label_equivalent` (spaces replaced with hyphens), `start_date`, `release_date`, `is_active`, `is_released`, and `elapsed_pct`.

---

### `get_sprint_health`

Read-only full sprint health snapshot. Builds the full dependency graph — stories → blocking tasks → dates and status — without making any changes to JIRA. Safe to call at any time.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `project_key` | string | yes | JIRA project key |
| `fix_version` | string | yes | fixVersion name identifying the sprint |

**Returns:**
- Sprint metadata (dates, `elapsed_pct`, `committed_date`)
- Summary counts (`total_stories`, `stories_with_no_tasks`, `stories_past_sprint_end`, `tasks_missing_dates`, `orphaned_tasks`)
- Per-story breakdown: `effective_completion_date` (max end date across blocking tasks), `past_sprint_end`, `past_committed_date`, `has_date_gap`, and the full list of blocking tasks with their dates, assignees, and current `pm-*` labels
- `orphaned_tasks`: tasks under the root epic that don't block any story in this sprint

---

### `run_sprint_audit`

Run the daily PM audit for a sprint. This is the main write tool — it applies all label and date changes to JIRA and writes an audit receipt to the Root Epic.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `project_key` | string | yes | JIRA project key |
| `fix_version` | string | yes | fixVersion name identifying the sprint |
| `dry_run` | boolean | no | If `true`, compute all changes but do not write to JIRA (default: `false`) |

**What it does (in order):**

1. **Sync sprint labels** — adds the sprint label (fixVersion with spaces → hyphens) to all blocking tasks in the sprint
2. **Compute task flags** — evaluates each blocking task and applies `pm-flag-*` labels:
   - Date slip: end date past sprint boundary (`pm-flag-critical-date-slip`, `pm-flag-warn-date-approaching`)
   - Commitment risk: end date past `committed_date` (`pm-flag-task-beyond-commitment`)
   - Not started: task in Backlog/Todo at mid-sprint (`pm-flag-critical-not-started`, `pm-flag-warn-not-started`)
   - Stalled: no status change for more than `stalled_threshold_days` (`pm-flag-warn-stalled`)
3. **Compute story flags** — evaluates each story:
   - Stories whose `effective_completion_date` slips past the sprint end or `committed_date` (`pm-flag-story-at-risk`, `pm-flag-story-beyond-commitment`)
   - Stories with missing blocking tasks (`pm-flag-missing-tasks`)
   - Stories with date gaps between blocking tasks (`pm-flag-date-gap`)
   - Stories recommended for deferral (`pm-flag-recommend-defer`)
4. **Update story due dates** — sets each story's `duedate` to its `effective_completion_date` (max end date of blocking tasks)
5. **Velocity calculation** — if `committed_date` is set, samples up to 5 past released sprints, computes average velocity, and projects expected completion date
6. **Write audit receipt** — posts a YAML receipt comment to the Root Epic

Tasks with `pm-acknowledged` are skipped entirely (human override).

**Returns:** Summary of changes made, `label_changes`, `story_date_changes`, `tasks_needing_dates`, `defer_recommendations`, `velocity` result, and `receipt_comment_id`.

---

### `rollback_audit`

Reverse the label and story date changes made by a previous `run_sprint_audit`. Reads the audit receipt from the Root Epic and applies the inverse diff. Never removes `pm-acknowledged` labels.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `project_key` | string | yes | JIRA project key |
| `audit_timestamp` | string | no | Timestamp from the receipt to roll back (e.g. `2026-02-22T06-00-00-000Z`). Defaults to the most recent audit. |

**Returns:** `rolled_back_audit`, `changes_reversed`, `errors`, and `rollback_receipt_comment_id`.

---

### `search_issues`

Escape hatch for ad-hoc JQL queries. Returns a page of matching issues with key, summary, status, assignee, labels, fixVersions, and due date. Use the high-level tools (`get_sprint_health`, `run_sprint_audit`) for routine PM operations.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `jql` | string | yes | JQL query string |
| `start_at` | number | no | Pagination offset (default: `0`) |
| `max_results` | number | no | Results per page, max 100 (default: `50`) |
| `fields` | string[] | no | Specific field keys to return |

**Returns:** `total`, `start_at`, `max_results`, `returned`, and `issues` array.

---

## pm-* label taxonomy

The agent owns the full `pm-*` label namespace. All managed labels are cleared and recomputed on each audit run. The one exception is `pm-acknowledged` — set by a human to suppress all agent flags on that issue; the agent never removes it.

| Label | Applied to | Meaning |
|---|---|---|
| `pm-acknowledged` | Task or Story | Human override — agent skips this issue entirely |
| `pm-flag-critical-date-slip` | Task | End date is past the sprint boundary |
| `pm-flag-warn-date-approaching` | Task | End date is within the last 20% of the sprint |
| `pm-flag-task-beyond-commitment` | Task | End date is past `committed_date` |
| `pm-flag-critical-not-started` | Task | Still in Backlog/Todo past the 50% sprint mark |
| `pm-flag-warn-not-started` | Task | Still in Backlog/Todo past the 25% sprint mark |
| `pm-flag-warn-stalled` | Task | No status change for more than `stalled_threshold_days` |
| `pm-flag-shared-task` | Task | Task blocks stories in multiple projects |
| `pm-flag-story-at-risk` | Story | `effective_completion_date` is past sprint end |
| `pm-flag-story-beyond-commitment` | Story | `effective_completion_date` is past `committed_date` |
| `pm-flag-delivery-at-risk` | Story | Velocity projection shows project won't meet `committed_date` |
| `pm-flag-missing-tasks` | Story | Story has no blocking tasks linked |
| `pm-flag-date-gap` | Story | Gap exists between consecutive blocking task date windows |
| `pm-flag-recommend-defer` | Story | Story is unlikely to complete in time; defer recommended |

Sprint labels (e.g. `SIT-27-02-2026`) are also applied to blocking tasks by the audit — these are derived from fixVersion names with spaces replaced by hyphens.

---

## Authority model

| Action | Who |
|---|---|
| Set task start/end dates | Human only |
| Set story due date | PM Agent (auto-set to `effective_completion_date`) |
| Add/remove `pm-flag-*` labels | PM Agent |
| Add sprint label to tasks | PM Agent |
| Add `pm-acknowledged` | Human only |
| Remove `pm-acknowledged` | Human only |
| Create fixVersions | Human only |
| Sprint planning (assign stories to fixVersion) | Human only |

## Architecture

```
src/
  server.ts           — MCP server entry point, registers all tools
  tools/              — One file per MCP tool
  jira/               — JIRA REST API client wrappers (client, search, issues, versions, comments)
  audit/              — Core audit logic (graph, flags, velocity, receipt)
  config/             — Project config schema, JIRA-backed storage, local registry
  utils/              — Labels, dates, JQL builder
```

Configuration is stored as a YAML comment on the Root Epic in JIRA (prefixed with `## pm-agent-config`), making it portable and version-controlled alongside the project. A local `.jira-pm-registry.json` maps `project_key → root_epic_key` to bootstrap config reads without a prior JIRA call.

TDQS

A4.3/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: setup_project for configuration, run_sprint_audit for comprehensive sprint audit, sync_sprint_tasks for blocking task synchronization, and search_issues as an ad-hoc JQL escape hatch. No two tools overlap in functionality.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern using snake_case (setup_project, run_sprint_audit, sync_sprint_tasks, search_issues), making the naming predictable and easy to understand.

Tool Count5/5

With 4 tools, the server is well-scoped for its purpose as a PM monitoring assistant. Each tool serves a necessary function (setup, audit, sync, search), and the count fits comfortably within the typical 3-15 range.

Completeness4/5

The tool set covers the core PM workflow: configuration, audit, task synchronization, and ad-hoc queries. However, it references a 'get_sprint_health' tool that is not provided, and lacks tools for direct issue creation or manual updates, leaving minor gaps.

Maintenance

ActivityInactive
ResponsivenessNo issues