jira-mcp
# jira-mcp
[](https://www.npmjs.com/package/@thammarongg/jira-mcp)
[](LICENSE)
[](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
Scored across 43 tools
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.
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.
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.
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.