Skip to main content
Glama
formonkey

jira-issue-context-mcp

by formonkey
README.md
# jira-issue-context-mcp

Read-only MCP server that gives Codex, Claude, Cursor, or any MCP client Jira issue context and JQL-based task discovery.

It intentionally stays narrow: fetch a single issue by key or list issues from a JQL query without performing Jira writes.

## Features

- Self-describing helper tool: `jira_issue_help`
- Issue context read-only tool: `jira_issue_context`
- Workflow/router triage read-only tool: `jira_issue_workflow`
- JQL read-only task discovery tool: `jira_issue_search`
- No board listing, sprint browsing, transitions, comment creation, or writes
- Converts Jira ADF descriptions/comments into plain Markdown-ish text
- Paginates all issue comments
- Validates JQL before search by default
- Reads available transitions, issue links, subtasks, and recent changelog for router triage
- Returns image attachments as MCP `image` content
- Supports Atlassian Cloud API token auth, bearer auth, or browser/session cookie auth
- Optional Playwright login helper for cookie-based SSO setups

## Tools

### `jira_issue_help`

Explains what tools this MCP exposes, when to use each one, and example arguments. It does not call Jira and does not require Jira authentication.

Inputs:

- `topic`: optional help topic. Supported values: `overview`, `router`, `config`, `jira_issue_search`, `jira_issue_workflow`, `jira_issue_context`.

Use this tool when the model is unsure whether it should search by JQL, triage workflow state, or fetch full issue context.

### `jira_issue_context`

Fetches the context for one issue key: title, description, comments, attachment summary, and image attachments.

### `jira_issue_workflow`

Fetches a read-only workflow view for one issue key so routers can decide whether to create agent work.

Inputs:

- `issueKey`: Jira issue key. Optional if the MCP was started with `--issue=KEY` or `JIRA_ISSUE_KEY`.
- `maxChangelogEntries`: maximum recent changelog entries to return. Defaults to `5`, maximum `20`.

The output includes:

- current status and status category
- available transitions for the authenticated user
- subtasks and their statuses
- issue links and linked issue statuses
- recent changelog entries
- a stable `Router Payload` JSON block with `taskKey`, `routerAction`, `availableTransitions`, and workflow signals

Possible `routerAction` values:

- `ready_for_agent_work`: issue is not done and transitions are available
- `review_no_available_transitions`: issue is not done, but Jira returned no transitions for this user/status
- `skip_done`: issue is already in a Done status category

### `jira_issue_search`

Runs a read-only JQL query and returns a compact task list for routers plus issue metadata.

Inputs:

- `jql`: JQL query to execute.
- `maxIssues`: maximum issues to return. Defaults to `JIRA_DEFAULT_JQL_MAX_ISSUES` or `5`, maximum `200`.
- `validateJql`: validates the JQL before searching. Defaults to `true`.

The output includes a stable `Router Payload` JSON block with `taskKeys`, `issueCount`, `limit`, and `isTruncated`. If `isTruncated` is `true`, the JQL matched more issues than `maxIssues`.

Example `Router Payload`:

```json
{
  "taskKeys": ["XXXX-123", "XXXX-345"],
  "issueCount": 2,
  "limit": 5,
  "isTruncated": false
}
```

Example router prompt:

```text
Actua como router. Ejecuta este JQL y trabaja en las tareas en paralelo:

project = XXXX AND statusCategory != Done ORDER BY priority DESC, updated ASC

Primero usa jira_issue_search para obtener las claves. Despues delega a jira-pm para
revisar cada issue con jira_issue_workflow. Si routerAction es ready_for_agent_work,
recoge contexto por issue con jira_issue_context y crea una conversacion por tarea.
No trabajes mas de 5 issues por ejecucion salvo que yo indique otro maxIssues.
```

For a daily Codex automation, use the same router prompt and keep `maxIssues` low. The MCP only discovers and reads Jira issues; branch creation, worktrees, delegation, commits, and pull requests belong to the Codex router/agents that consume the returned task keys.

## Install

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

## Development

Run the full local validation:

```bash
npm test
```

The test suite builds TypeScript and runs Node's built-in test runner. Current coverage includes:

- config loading, auth selection, numeric defaults, and issue key validation
- Jira issue field requests and HTTP error reporting
- ADF-to-text conversion
- image attachment download/skip behavior
- JQL search pagination, truncation, and validation
- helper guidance topics and examples
- stable router payload formatting
- workflow triage payloads, transitions, links, subtasks, and changelog formatting

## Authentication

Set `JIRA_BASE_URL` plus one auth method.

Optional safety limit for JQL task discovery:

```env
JIRA_DEFAULT_JQL_MAX_ISSUES=5
```

You can put these variables in the shell environment or in a `.env` file in the directory where the MCP command is started. The package also checks its own local `.env` as a fallback.

If your MCP client starts the server from a different working directory, pass `--env-file` or set `JIRA_ENV_PATH` so the server reads the intended project `.env` explicitly:

```env
JIRA_ENV_PATH=/absolute/path/to/your/project/.env
```

```bash
jira-issue-mcp --env-file=/absolute/path/to/your/project/.env
```

For Codex project config, prefer `--env-file` in `args` because it avoids relying on the parent Codex process environment.

Recommended for Atlassian Cloud:

```env
JIRA_BASE_URL=https://your-domain.atlassian.net
JIRA_EMAIL=you@example.com
JIRA_API_TOKEN=your-atlassian-api-token
```

Bearer token:

```env
JIRA_BASE_URL=https://your-domain.atlassian.net
JIRA_BEARER_TOKEN=your-token
```

Cookie/session token:

```env
JIRA_BASE_URL=https://your-domain.atlassian.net
JIRA_SESSION_TOKEN=your-cookie-value
JIRA_COOKIE_NAME=tenant.session.token
```

For SSO environments where API tokens are not available, the login helper can capture a browser cookie:

```bash
npx -y --package github:formonkey/jira-issue-context-mcp#main jira-issue-login \
  --base-url=https://your-domain.atlassian.net \
  --cookie-name=tenant.session.token \
  --storage-state=.auth/jira-storage-state.json
```

Run it from your project root. The first successful run writes `JIRA_SESSION_TOKEN` to `.env` and saves Playwright auth state to `.auth/jira-storage-state.json`. Later runs try that stored auth state first, refresh `.env`, and only open the browser again if the state is missing or expired.

`--force-login=false` is the normal mode. It does not disable login. It means:

1. Try the stored Playwright state first.
2. If the stored state is still valid, write a fresh `JIRA_SESSION_TOKEN` to `.env` and exit.
3. If the stored state is missing or expired, open the login browser, let the user complete SSO, capture the cookie, and update `.env`.

Use `--force-login=true` only when you explicitly want to ignore the stored state and force a fresh browser login.

If your Atlassian organization only shows SSO from a specific Jira application login URL, pass it explicitly:

```bash
npx -y --package github:formonkey/jira-issue-context-mcp#main jira-issue-login \
  --base-url=https://your-domain.atlassian.net \
  --cookie-name=tenant.session.token \
  --login-url="https://id.atlassian.com/login?...&application=jira&orgId=..."
```

Or let the helper build the Atlassian login URL:

```bash
npx -y --package github:formonkey/jira-issue-context-mcp#main jira-issue-login \
  --base-url=https://your-domain.atlassian.net \
  --cookie-name=tenant.session.token \
  --atlassian-site-id=your-site-id \
  --atlassian-org-id=your-org-id \
  --login-application=jira \
  --login-continue=https://your-domain.atlassian.net/browse/PROJ-123
```

If Microsoft SSO blocks the automated browser, start Edge yourself with remote debugging and let the helper connect to that real Edge session:

```bash
npx -y --package github:formonkey/jira-issue-context-mcp#main jira-issue-login \
  --base-url=https://your-domain.atlassian.net \
  --cookie-name=tenant.session.token \
  --launch-browser=msedge \
  --cdp-port=9222 \
  --default-profile=true \
  --close-existing-browser=true
```

This closes existing Edge processes, launches a real Edge process with the installed browser's default profile, connects to it over CDP, waits for you to finish Microsoft SSO, captures the Jira session cookie, and writes it to `.env`.

Advanced manual mode:

```powershell
start msedge --remote-debugging-port=9222
```

Then run:

```bash
npx -y --package github:formonkey/jira-issue-context-mcp#main jira-issue-login \
  --base-url=https://your-domain.atlassian.net \
  --cookie-name=tenant.session.token \
  --cdp-url=http://127.0.0.1:9222
```

Both CDP modes are useful for corporate SSO because the browser is launched as a normal installed browser instead of a Playwright-managed browser.

## Recommended Project Makefile

For project usage, wrap the long login command in `make`. The important commands are:

```bash
make jira-login
make jira-check
```

`make jira-login` should be the single normal login command. It tries the stored auth state first and only opens the SSO browser when required.

`make jira-check` should verify that the generated `.env` contains a valid Jira session before starting Codex or another MCP client.

Example for a project that vendors this MCP under `mcps/jira`:

```makefile
JIRA_BASE_URL ?= https://your-domain.atlassian.net
JIRA_COOKIE_NAME ?= tenant.session.token
JIRA_STORAGE_STATE ?= .auth/jira-storage-state.json
JIRA_FORCE_LOGIN ?= false
JIRA_LOGIN_URL ?= https://id.atlassian.com/login?...&application=jira&orgId=...

.PHONY: jira-build jira-install-browser jira-login jira-check
jira-build: ; npm --prefix mcps/jira run build
jira-install-browser: ; npm --prefix mcps/jira exec playwright install chromium
jira-login: ; node mcps/jira/dist/login-cli.js --base-url="$(JIRA_BASE_URL)" --cookie-name="$(JIRA_COOKIE_NAME)" --storage-state="$(JIRA_STORAGE_STATE)" --login-url="$(JIRA_LOGIN_URL)" --force-login="$(JIRA_FORCE_LOGIN)"
jira-check: ; node mcps/jira/dist/check-cli.js
```

Equivalent `make jira-login` command when using the package directly from GitHub:

```makefile
JIRA_BASE_URL ?= https://your-domain.atlassian.net
JIRA_COOKIE_NAME ?= tenant.session.token
JIRA_STORAGE_STATE ?= .auth/jira-storage-state.json
JIRA_FORCE_LOGIN ?= false
JIRA_LOGIN_URL ?= https://id.atlassian.com/login?...&application=jira&orgId=...

.PHONY: jira-login jira-check
jira-login: ; npx -y --package github:formonkey/jira-issue-context-mcp#main jira-issue-login --base-url="$(JIRA_BASE_URL)" --cookie-name="$(JIRA_COOKIE_NAME)" --storage-state="$(JIRA_STORAGE_STATE)" --login-url="$(JIRA_LOGIN_URL)" --force-login="$(JIRA_FORCE_LOGIN)"
jira-check: ; npx -y --package github:formonkey/jira-issue-context-mcp#main jira-issue-check --base-url="$(JIRA_BASE_URL)" --cookie-name="$(JIRA_COOKIE_NAME)"
```

If Playwright reports that its browser executable is missing, run the install target once:

```bash
make jira-install-browser
```

For Atlassian SSO, prefer a login URL whose query contains `application=jira`. Some Atlassian admin/user-access URLs use application values such as `admin--...`; those flows may hide the Microsoft SSO option even though the normal Jira application flow works.

After `make jira-login`, verify the session:

```bash
make jira-check
```

The check should call Jira `/rest/api/3/myself` with:

```http
Cookie: tenant.session.token=<JIRA_SESSION_TOKEN>
```

and return `HTTP 200 OK`. Once this works, restart the MCP client so it reads the updated `.env`.

## MCP Client Configuration

This server uses the MCP `stdio` transport. Every MCP client needs to start a local command and pass the Jira environment file:

```bash
node /absolute/path/to/jira-issue-context-mcp/dist/index.js --env-file=/absolute/path/to/your/project/.env
```

Recommended setup:

1. Clone or vendor this repository.
2. Run `npm install` and `npm run build`.
3. Create a project `.env` with `JIRA_BASE_URL` and one auth method.
4. Configure your MCP client to run `node dist/index.js --env-file=...`.
5. Restart or reload the client and check that the tools are visible.

Keep secrets in `.env`, shell env, keychains, or client secret stores. Do not commit Jira tokens.

### Tool Selection Guidance

Tell the agent this mapping so it uses the right tool:

- Use `jira_issue_help` when unsure which Jira MCP tool fits the request.
- Use `jira_issue_search` when the user gives a JQL or asks for pending Jira tasks.
- Use `jira_issue_workflow` before opening implementation work for an issue.
- Use `jira_issue_context` when the agent needs the full description, comments, attachments, and images for one issue.

Good router prompt:

```text
Actua como router. Ejecuta este JQL y trabaja en las tareas en paralelo:

project = XXXX AND statusCategory != Done ORDER BY priority DESC, updated ASC

Usa jira_issue_search con maxIssues=5 para obtener las claves.
Para cada clave, usa jira_issue_workflow.
Si routerAction es ready_for_agent_work, delega a jira-pm para obtener contexto con jira_issue_context.
Crea una conversacion o rama por tarea siguiendo el patron feature/XXXX-123.
No hagas escrituras en Jira desde este MCP; es read-only.
```

### Codex

Project-level config goes in `.codex/config.toml`.

Local checkout:

```toml
[mcp_servers.jira_issue]
command = "node"
args = [
  "/absolute/path/to/jira-issue-context-mcp/dist/index.js",
  "--env-file=/absolute/path/to/your/project/.env"
]
cwd = "/absolute/path/to/your/project"
enabled = true
startup_timeout_sec = 20
tool_timeout_sec = 90
default_tools_approval_mode = "auto"
```

Directly from GitHub with `npx`:

```toml
[mcp_servers.jira_issue]
command = "npx"
args = [
  "-y",
  "--package",
  "github:formonkey/jira-issue-context-mcp#main",
  "jira-issue-mcp",
  "--env-file=/absolute/path/to/your/project/.env"
]
cwd = "/absolute/path/to/your/project"
enabled = true
startup_timeout_sec = 20
tool_timeout_sec = 90
default_tools_approval_mode = "auto"
```

Codex project instructions can include:

```text
When Jira context is needed, use the jira_issue MCP server.
For JQL task discovery use jira_issue_search.
For router triage use jira_issue_workflow.
For full issue context use jira_issue_context.
```

### Claude Code

Claude Code can load MCP servers from a project `.mcp.json`, user scope, or local scope. For team-shared configuration, put this in `.mcp.json` at the project root:

```json
{
  "mcpServers": {
    "jira-issue": {
      "type": "stdio",
      "command": "node",
      "args": [
        "/absolute/path/to/jira-issue-context-mcp/dist/index.js",
        "--env-file=/absolute/path/to/your/project/.env"
      ],
      "env": {}
    }
  }
}
```

Equivalent CLI command:

```bash
claude mcp add --transport stdio --scope project jira-issue -- \
  node /absolute/path/to/jira-issue-context-mcp/dist/index.js \
  --env-file=/absolute/path/to/your/project/.env
```

Verify in Claude Code:

```bash
claude mcp list
```

Inside Claude Code, run:

```text
/mcp
```

If you already configured the server in Claude Desktop, Claude Code can import compatible servers on supported platforms:

```bash
claude mcp add-from-claude-desktop
```

### Claude Desktop

Claude Desktop local MCP configuration is separate from Claude Code. Add this to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "jira-issue": {
      "type": "stdio",
      "command": "node",
      "args": [
        "/absolute/path/to/jira-issue-context-mcp/dist/index.js",
        "--env-file=/absolute/path/to/your/project/.env"
      ],
      "env": {}
    }
  }
}
```

Restart Claude Desktop after editing the file. If tools do not appear, confirm that `npm run build` has produced `dist/index.js` and that the `.env` path is absolute.

### GitHub Copilot in VS Code

VS Code MCP configuration uses a top-level `servers` object. For a workspace-shared setup, create `.vscode/mcp.json`:

```json
{
  "servers": {
    "jira-issue": {
      "type": "stdio",
      "command": "node",
      "args": [
        "/absolute/path/to/jira-issue-context-mcp/dist/index.js",
        "--env-file=/absolute/path/to/your/project/.env"
      ]
    }
  }
}
```

Then:

1. Reload VS Code.
2. Open Copilot Chat.
3. Use Agent mode.
4. Open the tools picker and confirm `jira-issue` tools are listed.

For personal VS Code configuration, use the command palette action `MCP: Open User Configuration` and add the same server under `servers`.

### GitHub Copilot CLI

Copilot CLI uses `mcpServers`. Add a user-level server:

```bash
copilot mcp add jira-issue --type stdio -- \
  node /absolute/path/to/jira-issue-context-mcp/dist/index.js \
  --env-file=/absolute/path/to/your/project/.env
```

Verify:

```bash
copilot mcp list
copilot mcp get jira-issue
```

Workspace-level Copilot CLI configs can be stored in `.mcp.json` or `.github/mcp.json`:

```json
{
  "mcpServers": {
    "jira-issue": {
      "type": "stdio",
      "command": "node",
      "args": [
        "/absolute/path/to/jira-issue-context-mcp/dist/index.js",
        "--env-file=/absolute/path/to/your/project/.env"
      ]
    }
  }
}
```

Copilot CLI requires tool-call permission even for read-only MCP tools.

### GitHub Copilot Custom Agent

For a Copilot custom agent profile, configure this MCP under `mcp-servers` and list the tools the agent may use:

```yaml
---
name: jira-router
description: Routes Jira issues into implementation work.
tools:
  - jira-issue/jira_issue_search
  - jira-issue/jira_issue_workflow
  - jira-issue/jira_issue_context
mcp-servers:
  jira-issue:
    type: local
    command: node
    args:
      - /absolute/path/to/jira-issue-context-mcp/dist/index.js
      - --env-file=/absolute/path/to/your/project/.env
    tools: ["*"]
---

Actua como router de Jira. Usa jira_issue_search para JQL, jira_issue_workflow
para triage y jira_issue_context para contexto completo.
```

For cloud agents, configure Jira secrets as repository or organization agent secrets/variables instead of hardcoding them.

### Gemini CLI

Gemini CLI uses `mcpServers` in `settings.json`. Project-level config goes in `.gemini/settings.json`; user-level config goes in `~/.gemini/settings.json`.

```json
{
  "mcpServers": {
    "jira-issue": {
      "command": "node",
      "args": [
        "/absolute/path/to/jira-issue-context-mcp/dist/index.js",
        "--env-file=/absolute/path/to/your/project/.env"
      ],
      "timeout": 30000,
      "trust": false
    }
  }
}
```

Gemini CLI also supports adding the server from the command line:

```bash
gemini mcp add jira-issue node /absolute/path/to/jira-issue-context-mcp/dist/index.js -- \
  --env-file=/absolute/path/to/your/project/.env
```

Verify:

```bash
gemini mcp list
```

Inside Gemini CLI:

```text
/mcp
```

Avoid underscores in Gemini MCP server aliases; use `jira-issue` instead of `jira_issue`.

### Cursor

Cursor uses `mcpServers`. Project-level config goes in `.cursor/mcp.json`; global config goes in `~/.cursor/mcp.json`.

```json
{
  "mcpServers": {
    "jira-issue": {
      "command": "node",
      "args": [
        "/absolute/path/to/jira-issue-context-mcp/dist/index.js",
        "--env-file=/absolute/path/to/your/project/.env"
      ],
      "env": {}
    }
  }
}
```

Use Cursor Agent/Composer and ask for the tool by purpose:

```text
Use the Jira MCP to fetch workflow triage for XXXX-123 before implementing.
```

### Continue

Continue can load MCP server blocks from `.continue/mcpServers/`. Create `.continue/mcpServers/jira-issue.yaml`:

```yaml
name: Jira Issue MCP
version: 1.0.0
schema: v1
mcpServers:
  - name: Jira Issue
    type: stdio
    command: node
    args:
      - /absolute/path/to/jira-issue-context-mcp/dist/index.js
      - --env-file=/absolute/path/to/your/project/.env
```

Continue also supports copying JSON-style MCP config files into `.continue/mcpServers/`, so the generic `mcpServers` JSON block below also works there.

### Windsurf / Cascade

Windsurf uses a global `mcp_config.json`. Open Cascade's MCP settings, choose the raw config, and add:

```json
{
  "mcpServers": {
    "jira-issue": {
      "command": "node",
      "args": [
        "/absolute/path/to/jira-issue-context-mcp/dist/index.js",
        "--env-file=/absolute/path/to/your/project/.env"
      ]
    }
  }
}
```

After saving, refresh MCP servers from the Windsurf/Cascade MCP toolbar. If your Windsurf version does not interpolate env vars, keep using `--env-file` as shown.

### Generic MCP Clients

Most MCP clients accept a config shaped like this:

```json
{
  "mcpServers": {
    "jira-issue": {
      "type": "stdio",
      "command": "node",
      "args": [
        "/absolute/path/to/jira-issue-context-mcp/dist/index.js",
        "--env-file=/absolute/path/to/your/project/.env"
      ],
      "env": {}
    }
  }
}
```

Some clients use `servers` instead of `mcpServers` and some call `stdio` `local`. Keep the command and args the same.

### Troubleshooting MCP Clients

- Run `npm run build` before configuring clients; they execute `dist/index.js`.
- Use an absolute `--env-file` path.
- Restart or reload the client after changing MCP config or `.env`.
- Check that `JIRA_BASE_URL` and exactly one auth method are valid.
- Run `node dist/check-cli.js --env-file=/absolute/path/to/your/project/.env` if auth fails.
- If the client shows the server but no tools, inspect the client's MCP logs and confirm stdout is not used for logs. This server writes diagnostics to stderr.
- If a JQL returns too many issues, lower `maxIssues` or set `JIRA_DEFAULT_JQL_MAX_ISSUES=5`.

### Reference Docs

- Codex uses project `.codex/config.toml` for MCP server configuration.
- Claude Code MCP configuration: https://code.claude.com/docs/en/mcp
- Claude Desktop MCP support: https://support.anthropic.com/en/articles/10949351-getting-started-with-local-mcp-servers-on-claude-desktop
- VS Code MCP configuration: https://code.visualstudio.com/docs/agents/reference/mcp-configuration
- GitHub Copilot CLI MCP configuration: https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-command-reference
- GitHub Copilot custom agents with MCP: https://docs.github.com/en/copilot/reference/custom-agents-configuration
- Gemini CLI MCP configuration: https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md
- Cursor MCP configuration: https://docs.cursor.com/context/model-context-protocol
- Continue MCP configuration: https://docs.continue.dev/customize/deep-dives/mcp
- Windsurf MCP configuration example: https://learn.microsoft.com/en-us/azure/developer/azure-mcp-server/get-started/tools/windsurf

TDQS

A4.1/5.0

Scored across 1 tool

Disambiguation5/5

Only one tool exists, so there is no possibility of confusion or overlap.

Naming Consistency5/5

With a single tool, naming is trivially consistent.

Tool Count2/5

A single tool for a Jira domain is insufficient; typical Jira servers have multiple tools for CRUD and search operations.

Completeness1/5

Only read-only context is provided; missing essential operations like listing issues, updating, or creating issues, making the surface severely incomplete.

Maintenance

ActivitySlowing
ResponsivenessNo issues