Skip to main content
Glama
README.md
# jira-mcp

[![npm version](https://img.shields.io/npm/v/@thammarongg/jira-mcp.svg)](https://www.npmjs.com/package/@thammarongg/jira-mcp)
[![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![node](https://img.shields.io/node/v/@thammarongg/jira-mcp.svg)](https://nodejs.org)

An [MCP](https://modelcontextprotocol.io) (Model Context Protocol) server that exposes the Jira REST API to AI assistants — boards, sprints, epics, issues, JQL search, workflow transitions, comments, worklogs, projects, users, plus a generic passthrough tool for **any** Jira REST endpoint.

Works with both **Jira Cloud** (REST API v3) and **Jira Data Center** (REST API v2), auto-detected from the base URL.

## Install (one-click)

Once published to npm, any MCP client can run it via `npx` — no local build needed.

### Interactive installer (all agents at once)

```bash
npx -y @thammarongg/jira-mcp@latest install
```

Shows a menu — Select All or pick agents (Claude Code, OpenCode, Codex, Cursor,
Claude Desktop, Gemini CLI) — then asks for the Jira base URL and credentials
(token entry is masked on a TTY). If a selected agent already has a `jira`
entry, the installer offers to reuse its base URL and account without asking
for the credentials again. Existing config files are backed up to `.bak`
before any modification, and re-running updates the `jira` entry in place
instead of duplicating it.

With `--yes` and no credential flags, a selected existing entry's credentials
are reused without prompting.

Non-interactive (CI / scripting):

```bash
# Jira Cloud
npx -y @thammarongg/jira-mcp@latest install --agents all \
  --base-url https://your-org.atlassian.net \
  --email you@example.com --token xxx --yes

# Jira Data Center (subset of agents)
npx -y @thammarongg/jira-mcp@latest install --agents claude-code,codex \
  --base-url https://jira.yourcompany.com \
  --username you --token xxx --yes
```

Flags: `--agents all` or comma-separated ids/numbers (`claude-code`,
`opencode`, `codex`, `cursor`, `claude-desktop`, `gemini-cli`), `--base-url`,
`--email` (Cloud) / `--username` (Data Center), `--token` (API token or PAT),
`--password` (DC app password), `--yes` (skip the confirm prompt).

### CLI commands

```bash
npx -y @thammarongg/jira-mcp@latest update                 # refresh configured agents
npx -y @thammarongg/jira-mcp@latest update --agents codex
npx -y @thammarongg/jira-mcp@latest uninstall              # choose configured agents interactively
npx -y @thammarongg/jira-mcp@latest uninstall --agents all --yes
npx -y @thammarongg/jira-mcp@latest --version
npx -y @thammarongg/jira-mcp@latest --help
```

`update` is non-interactive and repairs every existing `jira` entry from that
agent's own credentials; agents without an entry are skipped. `uninstall`
removes only the `jira` entry, preserving the rest of each agent configuration;
it accepts the same `--agents` selection grammar as `install` and asks for
confirmation unless `--yes` is supplied.

Running `npx -y @thammarongg/jira-mcp@latest` bare in a terminal prints a status card
with the installed version, configured agents, and available commands. MCP
clients pipe stdio, so the same bare command starts the MCP server for them;
it expects `JIRA_*` environment variables, which is why running it bare
without those variables reports a missing `JIRA_BASE_URL`.

### Claude Code (manual one-liner)

```bash
claude mcp add jira --env JIRA_BASE_URL=https://your-org.atlassian.net \
  --env JIRA_EMAIL=you@example.com --env JIRA_API_TOKEN=xxx \
  -- npx -y @thammarongg/jira-mcp@latest
```

```json
// Claude Desktop / any MCP client (mcpServers)
{
  "mcpServers": {
    "jira": {
      "command": "npx",
      "args": ["-y", "@thammarongg/jira-mcp@latest"],
      "env": {
        "JIRA_BASE_URL": "https://your-org.atlassian.net",
        "JIRA_EMAIL": "you@example.com",
        "JIRA_API_TOKEN": "xxx"
      }
    }
  }
}
```

```json
// opencode (opencode.json)
{
  "mcp": {
    "jira": {
      "type": "local",
      "command": ["npx", "-y", "@thammarongg/jira-mcp@latest"],
      "environment": {
        "JIRA_BASE_URL": "https://your-org.atlassian.net",
        "JIRA_EMAIL": "you@example.com",
        "JIRA_API_TOKEN": "xxx"
      },
      "enabled": true
    }
  }
}
```

### Publishing

```bash
npm login
npm publish          # prepublishOnly runs build + smoke test first
```

### Local development (no publish)

```bash
npm install
npm run build
# run directly: node dist/index.js  (or `npm run dev` via tsx)
```

### Configuration (environment variables)

| Variable | Required | Description |
| --- | --- | --- |
| `JIRA_BASE_URL` | yes | `https://your-org.atlassian.net` (Cloud) or `https://jira.yourcompany.com` (DC); a DC context path such as `/jira` is preserved |
| `JIRA_EMAIL` | Cloud | Your Atlassian account email |
| `JIRA_API_TOKEN` | yes | API token (Cloud: id.atlassian.com → Security → API tokens; DC: personal access token) |
| `JIRA_USERNAME` | DC | Username (alternative to `JIRA_EMAIL`) |
| `JIRA_PASSWORD` | DC | App password (alternative to `JIRA_API_TOKEN`) |
| `JIRA_API_VERSION` | no | Force `2` or `3`. Default: auto (Cloud → 3, DC → 2) |
| `JIRA_TIMEOUT_MS` | no | Request timeout, default `30000` |

Quick auth check:

```bash
JIRA_BASE_URL=... JIRA_EMAIL=... JIRA_API_TOKEN=... node dist/index.js
```

Then call the `get_current_user` tool from your MCP client — it verifies credentials.

## Using a local checkout

Prefer not to publish? Point the client at the built file instead of `npx` —
same env vars as above:

```bash
claude mcp add jira --env JIRA_BASE_URL=https://your-org.atlassian.net \
  --env JIRA_EMAIL=you@example.com --env JIRA_API_TOKEN=xxx \
  -- node /absolute/path/to/jira-mcp/dist/index.js
```

```json
// opencode (opencode.json)
{
  "mcp": {
    "jira": {
      "type": "local",
      "command": ["node", "/absolute/path/to/jira-mcp/dist/index.js"],
      "environment": {
        "JIRA_BASE_URL": "https://your-org.atlassian.net",
        "JIRA_EMAIL": "you@example.com",
        "JIRA_API_TOKEN": "xxx"
      },
      "enabled": true
    }
  }
}
```

## Agent skill

A ready-made agent skill (setup + workflow guidance for the tools) ships in
[`skill/SKILL.md`](skill/SKILL.md). Install it by copying to your skills
directory:

```bash
mkdir -p ~/.claude/skills/jira && cp skill/SKILL.md ~/.claude/skills/jira/
# or for opencode / shared agents:
mkdir -p ~/.agents/skills/jira && cp skill/SKILL.md ~/.agents/skills/jira/
```

## Tools

### Boards & sprints (Agile + Software APIs)

| Tool | Description |
| --- | --- |
| `list_boards` | List boards (filter by type/name/project key or ID) |
| `get_board` | Board details incl. projects |
| `list_sprints` | Sprints on a board (state: active/closed/future) |
| `get_sprint` | Sprint details |
| `create_sprint` | New sprint on a board |
| `update_sprint` | Rename, reschedule, change goal/state |
| `close_sprint` | Close a sprint |
| `get_sprint_issues` | Issues in a sprint (Cloud cursor or DC offset paging) |
| `get_sprint_view` | Full UI-like sprint view (board + sprint + issues in one call) |
| `get_backlog` | Board backlog (Cloud Software enhanced or DC Agile, rank-ordered) |

### Epics

| Tool | Description |
| --- | --- |
| `list_epics` | Epics on a board (optionally filtered by `done`) |
| `get_epic` / `get_epic_issues` | Read an epic and its children (works on team-managed projects) |
| `create_epic` | New epic in a project |
| `move_issue_to_epic` | Add issues to an epic (sets `parent` on team-managed) |
| `get_epic_meta` | Epic-level issue types available in a project |

### Issues

| Tool | Description |
| --- | --- |
| `get_issue` | Issue by key |
| `create_issue` | Create (supports custom fields) |
| `update_issue` | Set fields and/or relative `update` ops |
| `delete_issue` | Delete |
| `search_issues` | **JQL search** — enhanced search (`/search/jql`) on Cloud, legacy `/search` on Data Center |
| `get_issue_create_meta` | Discover projects/types/required fields (current per-project metadata endpoints) |
| `get_issue_transitions` / `transition_issue` | Workflow transitions |
| `assign_issue` | Assign/unassign |
| `add_comment` / `list_comments` / `delete_comment` | Comments (plain text in, auto-converted to ADF on Cloud/v3) |
| `get_issue_worklogs` / `add_worklog` | Time tracking |

### Projects, users, meta

`list_projects`, `get_project`, `get_project_components`, `create_project_component`, `get_project_issue_types`, `get_project_roles`, `get_project_versions`, `get_current_user`, `find_users`, `get_user`, `get_fields`, `get_issue_types`

### Escape hatch

| Tool | Description |
| --- | --- |
| `jira_api` | Raw call to any `/rest/...` endpoint (method, path, query, body) — covers the full Jira REST API |

## Typical agent workflow

1. `list_boards` → pick a board
2. `list_sprints` (state: active) → pick a sprint
3. `get_sprint_issues` or `get_sprint_view` → see the work
4. `search_issues` with JQL for anything custom
5. `create_issue` / `transition_issue` / `add_comment` to act
6. Anything else → `jira_api`

## Rich text and ADF

Jira Cloud REST v3 stores descriptions, comment bodies and worklog comments as
[Atlassian Document Format](https://developer.atlassian.com/cloud/jira/platform/apis/document/structure/),
not plain strings. Pass plain text to `create_issue`, `create_epic`,
`update_issue`, `transition_issue`, `add_comment` and `add_worklog` — the server
wraps it in ADF when `JIRA_API_VERSION` is `3`, and leaves it alone on v2 (Data
Center), where wiki markup applies. Blank lines start a new paragraph and single
newlines become line breaks; **Markdown is not rendered** (`**bold**` shows
literally). Already-built ADF objects are passed through untouched.

## Development

```bash
npm run dev        # run from source via tsx
npm run build      # compile to dist/
npm run typecheck  # tsc --noEmit
node scripts/smoke.mjs   # stdio handshake + tools/list smoke test
```

## Notes & limitations

- Auth is HTTP Basic (email+token for Cloud, username+token/password for DC) — the standard for Jira REST.
- `JIRA_BASE_URL` may include a Data Center context path (for example `https://jira.example.com/jira`); it is retained when resolving `/rest/...` endpoints.
- Pagination: most list tools return Jira's native `startAt`/`maxResults`/`total`; pass `startAt` to page.
- Platform routing: Cloud-only enhanced Software reads use `/rest/software/1.0` for backlog, sprint issues, and epic issues (cursor `nextPageToken`); Data Center uses the corresponding Agile 1.0 endpoints with `startAt`/`total`. `list_boards` passes its project filter as the Agile API's singular `projectKeyOrId` parameter.
- `create_sprint` uses the documented top-level Agile `POST /rest/agile/1.0/sprint` route and sends the selected board as `originBoardId`.
- JQL search on **Jira Cloud** uses `/rest/api/3/search/jql`, since Atlassian removed `GET /rest/api/{2,3}/search` on 2025-05-01 ([CHANGE-2046](https://developer.atlassian.com/changelog/#CHANGE-2046) — the old endpoint now returns HTTP 410). Consequences for `search_issues` on Cloud: the JQL must be **bounded** (include a restriction such as `project`, `assignee`, or `key`), the response carries **no `total`**, and paging is by cursor — pass the returned `nextPageToken` back and stop when `isLast` is true. `startAt` is rejected there rather than silently ignored, and `includeApproximateTotal: true` adds an approximate match count via `/search/approximate-count`.
- Jira **Data Center** keeps the legacy `/search` endpoint with `startAt`/`total`; if a Cloud site on a custom domain is misdetected as DC, a 410 from `/search` transparently retries against `/search/jql`.
- `get_issue_create_meta` composes the current per-project and per-issue-type metadata endpoints (`/issue/createmeta/{project}/issuetypes` and `/issue/createmeta/{project}/issuetypes/{issueType}`), because the old `/issue/createmeta` query form is deprecated on Cloud and removed from Jira Data Center 9.0+. Its output keeps the familiar `projects[].issuetypes[].fields` shape. Pass `projectKeys` when fields are expanded (the default); omitting it is allowed only with `expand: "projects.issuetypes"` for bounded issue-type summaries. Project metadata is fetched with a fixed concurrency of four, and issue-type field metadata is likewise limited to four concurrent requests.
- `get_backlog`, `get_sprint_issues`, and `get_epic_issues` use Cloud's current Software enhanced endpoints with cursor paging; pass `nextPageToken` for the next page. Data Center keeps Agile 1.0 and `startAt`/`total`. The older Cloud Agile issue-list endpoints remain available but are deprecated.
- Rapid view IDs are computed as `boardId * 10^13 + sprintId` (Jira's documented convention).
- Comment bodies use the `body` field on both Cloud (v3) and Data Center (v2).
- User references use `accountId` on API v3/Cloud and `name` on API v2/Data Center. `get_user` calls `/user?accountId=...` on v3; the v3 path form `/user/{accountId}` is not a current endpoint. `create_issue`'s `dueDate` input is sent as Jira's `duedate` field; `add_worklog` accepts an optional `started` timestamp and Jira defaults it to now when omitted.
- Epics: the Agile epic API (`/rest/agile/1.0/epic/...`) only understands company-managed epics and returns HTTP 400 on team-managed ("next-gen") projects. `get_epic`, `get_epic_issues`, and `move_issue_to_epic` detect that and fall back to the issue/search APIs, where an epic is an ordinary issue linked to its children by `parent`.
- Jira Data Center exposes platform REST API v2/latest, not `/rest/api/3`. Leave `JIRA_API_VERSION` at its default (`2`) for DC; forcing `3` against a DC host is unsupported and causes the server to request unavailable `/rest/api/3/...` routes. Cloud-vs-DC endpoint routing is based on the detected deployment (`*.atlassian.net` is Cloud), while field representations are based on the selected API version.
- `jira_api` paths must resolve under `/rest/` — paths that would escape it (e.g. via `..` segments) are rejected, and `?`/`#` must be passed via `query`.

## License

[MIT](LICENSE) © Thammarong G.

TDQS

B3.3/5.0

Scored across 43 tools

Disambiguation4/5

Most tools target a distinct resource and action, and descriptions clarify likely variants like get_sprint, get_sprint_issues, and get_sprint_view. A few close pairs exist—get_issue_types vs get_project_issue_types and update_issue vs move_issue_to_epic—but the resource scopes and descriptions make misselection unlikely.

Naming Consistency3/5

The set mostly follows verb_noun, but it mixes get_ and list_ for collections, uses create_ for most resources but add_ for comments and worklogs, and uses search_issues versus find_users. jira_api also breaks the pattern, making the naming readable but not consistently structured.

Tool Count2/5

43 tools is well beyond the 25+ threshold, and the surface includes many fine-grained getters plus a catch-all jira_api tool. While Jira is a broad domain, the large count makes agent tool selection heavier than necessary, and some variants could reasonably be consolidated.

Completeness4/5

Core Jira workflows—issues, sprints, epics, projects, comments, worklogs, and users—are well covered with appropriate create/read/update/delete operations where relevant. Minor gaps exist, such as no update_comment, no delete_sprint, or no direct add-issue-to-sprint tool, but jira_api and update_issue provide usable workarounds.

Maintenance

ActivityMaintained
ResponsivenessNo issues