npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tugudush/jira-mcp

v1.1.1

Published

A focused, Jira-only Model Context Protocol server with bitbucket-mcp-grade developer ergonomics. Works today — no OAuth admin gating required.

Readme

Jira MCP Server

A focused, Jira-only Model Context Protocol (MCP) server with bitbucket-mcp-grade developer ergonomics. Talks to the Jira Cloud REST API v3 directly using an Atlassian email + API token — no OAuth dance, no admin gating, works today.

🎯 36 tools (35 read + 1 scoped issue text update) · ✅ Read-only by default · 🏗️ Modern TS 6 + ESM · 📦 TOON / JSON / text output formats

npm version License: MIT Node ≥ 20

Status:v1.0.0 released — see CHANGELOG.md and docs/plan.md for the full roadmap and competitors.md for the research that informed it.


Why?

The official Atlassian MCP (Rovo) requires twg:* OAuth scopes that need tenant-admin approval — and the legacy SSE method is being deprecated on June 30, 2026. This server is a drop-in alternative: same MCP UX, none of the gating. Talk to Jira directly with email + API token.


Requirements

  • Node.js ≥ 20.0.0 (see .nvmrc)
  • A Jira Cloud site (e.g. https://your-domain.atlassian.net)
  • An API token is required. Create a classic (unscoped) API token at https://id.atlassian.com/manage-profile/security/api-tokens:
    • Important: Click "Create API token" (do NOT use "Create API token with scopes", as scoped tokens can fail to authorize various Jira platform endpoints correctly).
    • Make sure your Atlassian account has active read permissions for the target Jira project. The optional issue text update also requires Jira edit permission, and this server still refuses the update unless the authenticated account is the issue reporter or assignee.

Installation

Option 1 — npm global install (recommended)

npm install -g @tugudush/jira-mcp

Update:

npm update -g @tugudush/jira-mcp

Option 2 — npx (no global install)

npx -y @tugudush/jira-mcp

Option 3 — build from source

git clone https://github.com/tugudush/jira-mcp.git
cd jira-mcp
npm install
npm run build
node dist/index.js

Configuration

VS Code / GitHub Copilot — .vscode/mcp.json

After global install:

{
  "servers": {
    "jira-mcp": {
      "type": "stdio",
      "command": "jira-mcp",
      "env": {
        "JIRA_BASE_URL": "https://your-domain.atlassian.net",
        "JIRA_EMAIL": "[email protected]",
        "JIRA_API_TOKEN": "your-api-token",
      },
    },
  },
}

Or via npx (no global install):

{
  "mcpServers": {
    "jira-mcp": {
      "command": "npx",
      "args": ["-y", "@tugudush/jira-mcp"],
      "env": {
        "JIRA_BASE_URL": "https://your-domain.atlassian.net",
        "JIRA_EMAIL": "[email protected]",
        "JIRA_API_TOKEN": "your-api-token",
      },
    },
  },
}

Or from a local build (after npm install && npm run build):

{
  "servers": {
    "jira-mcp": {
      "type": "stdio",
      "command": "node",
      "args": ["/path/to/jira-mcp/dist/index.js"],
      "env": {
        "JIRA_BASE_URL": "https://your-domain.atlassian.net",
        "JIRA_EMAIL": "[email protected]",
        "JIRA_API_TOKEN": "your-api-token",
      },
    },
  },
}

Tip: paste the actual values in the env block. If you don't want to commit secrets, keep .vscode/mcp.json in your global gitignore.


Environment Variables

| Variable | Required | Default | Description | | -------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------- | | JIRA_BASE_URL | Yes | — | Your Jira Cloud site URL (no trailing slash) | | JIRA_EMAIL | Yes | — | Atlassian account email | | JIRA_API_TOKEN | Yes | — | API token from https://id.atlassian.com/manage-profile/security/api-tokens | | JIRA_ALLOW_ISSUE_UPDATES | No | false | Set to true to enable only jira_update_issue_text; updates still require reporter/assignee identity match | | JIRA_DEBUG | No | false | Verbose stderr logging (URLs + status codes only — no bodies, no headers) | | JIRA_DEFAULT_FORMAT | No | text | Default output format: text / json / toon | | JIRA_REQUEST_TIMEOUT_MS | No | 30000 | Per-request timeout in milliseconds |

No secrets are ever logged. JIRA_DEBUG=true only logs URLs and status codes.


Usage Examples

Once jira-mcp is wired into your MCP client (Copilot, Cursor, Claude Desktop, etc.), the model can call any of the 36 tools. A few typical invocations:

Search issues by JQL

Tool: jira_search_issues
Args:
  jql:          "project = ENG AND status != Done ORDER BY updated DESC"
  maxResults:   10
  output_format: toon

Use the new /rest/api/3/search/jql endpoint (POST). The older /rest/api/3/search is deprecated and not exposed by this server.

Fetch one issue

Tool: jira_get_issue
Args:
  issueIdOrKey:  "ENG-1234"
  fields:        ["summary", "status", "assignee", "reporter", "priority", "updated"]
  output_format: text

Extract image attachments from an issue

Tool: jira_get_issue
Args:
  issueIdOrKey:        "ENG-1234"
  include_attachments: true
  output_format:       text

When include_attachments: true, jira_get_issue auto-requests the attachment field, downloads every image attachment (PNG/JPEG/GIF/WebP), and returns them as MCP image content blocks alongside the text summary. Defaults to false (no download, no context cost). See Image Attachments for limits and caveats.

List agile boards and their sprints

Tool: jira_list_boards
Args:
  projectKeyOrId: "ENG"
  output_format:  text
Tool: jira_get_board_sprints
Args:
  boardId: 42
  state:   "active"

Filter a response with JMESPath

Every tool accepts a filter parameter (JMESPath expression) to project the response down to just the fields you need. Example:

Tool: jira_search_issues
Args:
  jql:          "project = ENG ORDER BY updated DESC"
  maxResults:   20
  filter:       "issues[].{key: key, summary: fields.summary, status: fields.status.name}"
  output_format: json

See jmespath.org for the full expression syntax.


Enabling Writes — jira_update_issue_text

Opt-in. Off by default. The 35 read tools are always available. This single scoped write tool requires two safeguards:

  1. JIRA_ALLOW_ISSUE_UPDATES=true in the MCP env block, and
  2. The authenticated Atlassian account is the reporter or assignee of the target issue.

What it can change

Only an issue's title (summary) and/or plain-text description. It cannot change status, assignee, priority, labels, comments, worklogs, sprints, links, or any other field. The handler converts your plain text description to Atlassian Document Format (ADF) and PUTs to /rest/api/3/issue/{key}.

Step 1 — set the env var

In .vscode/mcp.json (or the equivalent in your MCP client):

{
  "servers": {
    "jira-mcp": {
      "type": "stdio",
      "command": "jira-mcp",
      "env": {
        "JIRA_BASE_URL": "https://your-domain.atlassian.net",
        "JIRA_EMAIL": "[email protected]",
        "JIRA_API_TOKEN": "your-api-token",
        "JIRA_ALLOW_ISSUE_UPDATES": "true",
      },
    },
  },
}

Step 2 — confirm the identity match

Open the target Jira issue and check the Reporter and Assignee fields. The email in JIRA_EMAIL must match one of them. The server reads /rest/api/3/myself first and compares the account IDs.

Step 3 — call the tool

Tool: jira_update_issue_text
Args:
  issueIdOrKey:  "ENG-1234"
  title:         "Refactor onboarding wizard to async stepper"
  description:   "Step 1: profile, Step 2: workspace, Step 3: invite teammates."
  output_format: text

What you see when it works

Updated ENG-1234 successfully.

What you see when it doesn't

| Condition | Tool response | | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | JIRA_ALLOW_ISSUE_UPDATES not set / "false" | IssueUpdateDisabledError: Issue text updates are disabled. To enable, set JIRA_ALLOW_ISSUE_UPDATES=true. | | Account is neither reporter nor assignee | IssueUpdatePermissionError: Only the issue reporter or assignee may update the title/description. | | Both title and description are omitted | Zod validation: Either title or description must be provided. | | Jira returns 401 / 403 | AuthenticationError or ForbiddenError — check token + email | | Jira returns 429 | RateLimitError — server retries with exponential backoff |

Caveats

  • Token scope. Use a classic (unscoped) API token. Scoped tokens created via "Create API token with scopes" can fail to authorize various Jira endpoints correctly — including the issue update endpoint.
  • Plain text only. The description argument is plain text. Multi-line strings are accepted; markdown is not converted. If you need rich formatting, file an issue and we'll add an adf_json argument in v1.1.
  • No idempotency token. A second call with the same args immediately after the first will succeed again and bump the updated timestamp. Jira Cloud does not support If-Match on this endpoint.

Image Attachments

Opt-in. Off by default. Setting include_attachments: true on jira_get_issue causes the server to download every image attachment on the issue and return them as MCP image content blocks alongside the regular text summary. Image-capable MCP clients (Copilot, Claude Desktop, Cursor, etc.) will render them inline so the model can actually see the mockup, screenshot, or error capture.

What counts as "an image"

Only the following MIME types are downloaded:

  • image/png
  • image/jpeg
  • image/gif
  • image/webp

PDFs, zips, logs, Office docs, etc. are listed in the text output but not fetched — the model has no way to consume them as images.

Safety limits

| Limit | Default | What happens when exceeded | | ---------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------- | | Attachments per issue | 10 | additional ones are skipped with skipped: per-issue attachment cap reached | | Per-attachment size | 2 MB | oversized attachments are skipped with skipped: too large (per-attachment cap) | | Total extracted per call | 5 MB | once the running total crosses the budget, attachments are skipped with skipped: would exceed total extraction budget | | Download failure | (per attachment) | the failure is recorded as skipped: download failed and the rest of the batch continues |

Limits are per call (each jira_get_issue invocation starts fresh). The default of include_attachments: false performs zero downloads and emits zero image blocks — behavior is unchanged for callers who don't opt in.

Output format gates

Image content blocks are only emitted when both of the following are true:

  • output_format is text (the default), and
  • no JMESPath filter is supplied.

json and toon are structured-data modes; embedding base64 would balloon the response. A JMESPath filter means the consumer asked for a projected slice of the response — images have no data-side representation, so they are skipped.

Example output (text mode, two PNGs)

ENG-1234 — Add onboarding wizard stepper
- Status:    In Progress
- Assignee:  Jerome Gomez
- …
- Attachments (2):
  - mockup-step-1.png (148 KB, image/png)
  - mockup-step-2.png (164 KB, image/png)
  [Extracted 2 image(s) into the response]

— ATTACHMENT EXTRACTION REPORT —
  - mockup-step-1.png (image/png) — extracted
  - mockup-step-2.png (image/png) — extracted

The two PNGs are returned as MCP image content blocks (one block each), which the MCP client renders inline.

Skipped attachments are never silent

Every attachment on the issue gets a line in the text report — either extracted or one of the skip reasons listed above. You can always tell exactly which attachments made it into the response and why the others didn't.

Caveats

  • Read-only. The download is a GET; this server still has no write surface for uploads.
  • Not cached. Each call re-downloads attachments. Repeated calls on the same issue will refetch the same images — see the open question in docs/features/image-extraction.md §5 for the in-memory LRU idea (out of scope for v1.1).
  • Base64 inflation. Binary → base64 adds ~33%. A 2 MB PNG becomes ~2.67 MB of string data in the LLM context. If your context is tight, call jira_get_issue once with include_attachments: false first to read the description, then again with include_attachments: true only when the model actually needs to see the images.
  • Not available on jira_search_jql — only jira_get_issue accepts the flag. Extracting images for every issue in a JQL result would blow the context window for any non-trivial search.

Every tool accepts two optional parameters:

| Parameter | Values | Effect | | --------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------ | | output_format | text / json / toon | Default text (human-readable markdown); json is pretty-printed; toon saves 30–60% of tokens for lists of > 5 items | | filter | JMESPath expression | Projects the response down to just the fields you need |

Set JIRA_DEFAULT_FORMAT=toon in env to make TOON the default for every tool. The model can still override per-call with output_format=....


Project-level AGENTS.md defaults

Want the model to stop rediscovering your project key on every session? Drop docs/AGENTS.md.example into your repo's AGENTS.md and edit the YOURPROJ placeholder. See docs/agents-guide.md for the full how-to. Pattern lifted from the official atlassian/atlassian-mcp-server (UX4).


Features

  • Issue search with JQL via the new /search/jql endpoint (POST)
  • Issue detail — transitions, changelog, comments, worklogs, watchers, links
  • Opt-in image attachment extraction on jira_get_issue via include_attachments: true — downloads PNG/JPEG/GIF/WebP attachments and returns them as MCP image content blocks (10 / 2 MB / 5 MB defaults)
  • Project listing, components, versions, statuses
  • Agile boards, sprints, backlog issues
  • Users (current user, search, assignable)
  • Saved filters, dashboards, workflows, fields + issue types
  • Opt-in scoped issue text update behind JIRA_ALLOW_ISSUE_UPDATES=true: update only issue title (summary) and description, only as reporter or assignee
  • TOON output format (30–60% token savings) with text / json fallback
  • JMESPath filter parameter on every tool
  • Response truncation with raw file logging when payloads exceed ~10k tokens (aashari pattern)
  • Project-level AGENTS.md defaults so the model doesn't re-discover your project every session (atlassian/Rovo pattern)
  • Per-tool description strings teach usage — embed deprecation notes, parameter hints, and gotchas inline (aashari pattern)
  • Retry with exponential backoff on transient errors (429 / 5xx / network cuts)
  • Configurable per-request timeout via JIRA_REQUEST_TIMEOUT_MS (default 30 s)

Development

See docs/plan.md for the full phased implementation plan and CHANGELOG.md for the release history.

git clone https://github.com/tugudush/jira-mcp.git
cd jira-mcp
npm install
npm run dev          # tsx watch src/index.ts

For workspace-level testing inside VS Code/GitHub Copilot, a local configuration is available at .vscode/mcp.json (gitignored). You can configure your credentials there to load either:

  • jira-mcp-dev — Runs directly from TS source via tsx (convenient for active development).
  • jira-mcp-dist — Runs the compiled production code from the dist/ directory.

You can export the jira-mcp-dist server's env block to your shell with:

node scripts/dump-mcp-env.cjs > .mcp-env.sh && source .mcp-env.sh

Scripts

| Script | What it does | | ----------------------- | -------------------------------------------------------------------------------------- | | npm run dev | Watch mode via tsx | | npm run build | prebuild (scripts/sync-version.ts) → tscdist/ | | npm run type-check | tsc --noEmit | | npm run lint | ESLint (flat config + sonarjs), max-warnings 0 | | npm run format | Prettier write | | npm run format:check | Prettier check (no modifications, exits non-zero on diff) | | npm test | Vitest (single run, 96+ tests) | | npm run test:watch | Vitest watch mode | | npm run test:coverage | Vitest with v8 coverage | | npm run ltfb | lint → type-check → format → build | | npm run phase5:smoke | Run the stdio smoke driver against a sandbox tenant | | npm run image:smoke | Manual image-extraction smoke against SSP-2313 (or any issue with image attachments) | | npm start | Boot the built server (node dist/index.js) |

No automated CI is configured for this repo. The maintainer runs npm run ltfb && npm test locally on Node 20 and Node 22 before merging each PR. If you fork the repo and want CI, see the GitHub Actions Node.js workflow template.


Troubleshooting

"Authentication failed" / 401 errors

  • Check that JIRA_EMAIL matches the email on the Atlassian account that created the API token.
  • Check that JIRA_API_TOKEN is a classic (unscoped) token from https://id.atlassian.com/manage-profile/security/api-tokens. Scoped tokens created via the newer "Create API token with scopes" flow can fail authorization on various Jira platform endpoints.
  • Make sure the token has not been revoked.

"Forbidden" / 403 errors

  • The Atlassian account must have read permission to the Jira project being queried. Ask your Jira admin to grant access, or use a different account.

"Not Found" / 404 errors

  • Verify JIRA_BASE_URL matches your Jira Cloud site URL exactly (no trailing slash, https://, not http://).
  • Verify the issue key exists (PROJ-1234 format — must be uppercase project key + dash + number).

"Issue text updates are disabled"

  • Set JIRA_ALLOW_ISSUE_UPDATES=true in the MCP env block and restart the MCP client. See Enabling Writes.

"Only the issue reporter or assignee may update the title/description"

  • The Atlassian account you're authenticated as is neither the reporter nor the assignee on the target issue. Either:
    • have the reporter/assignee make the change, or
    • have a Jira admin change the issue's reporter/assignee to you, or
    • use a different Atlassian account whose email matches the reporter or assignee on the issue.

Tool never returns / times out

  • Default per-request timeout is 30 s (override with JIRA_REQUEST_TIMEOUT_MS).
  • Set JIRA_DEBUG=true to see URL + status code lines on stderr.
  • Jira Cloud's rate limit is ~10 req/s per tenant. The server retries with exponential backoff on 429/5xx. If you're seeing frequent timeouts, slow down the model's request rate.

"Cannot find module '@modelcontextprotocol/sdk/...'" after install

  • Run npm ci to do a clean install from package-lock.json. If the issue persists, ensure your Node version is ≥ 20 (node --version).

Server shows Starting → Running → Stopped with no output (global install / npx)

You started the server via the global bin ("command": "jira-mcp") or npx, and the VS Code MCP panel goes Starting → Running → Stopped — with no [server stderr] lines in the Output channel. The initialize handshake either times out or the process exits immediately.

This affected @tugudush/[email protected] only: an entry-point guard in src/index.ts only started the server when process.argv[1] string-matched the module path, which fails when Node resolves a symlinked/junctioned install (nvm-for-windows, the npx cache, Homebrew, Linux /usr/local/bin/…, etc.). The same code runs fine when invoked directly from a non-symlinked source build.

  • Upgrade to ≥ 1.0.1 — the entry-point guard was rewritten to use realpathSync() on both sides, which is symlink- and slash-agnostic. After upgrading, both "command": "jira-mcp" and the npx config work on every platform.

  • Immediate workaround without upgrading: run the server from a local source build (a real path, NOT through any symlink/junction). Clone, build, and point .vscode/mcp.json at the local entry:

    {
      "servers": {
        "jira-mcp": {
          "type": "stdio",
          "command": "node",
          "args": ["C:/path/to/cloned/jira-mcp/dist/index.js"],
          "env": {
            "JIRA_BASE_URL": "…",
            "JIRA_EMAIL": "…",
            "JIRA_API_TOKEN": "…",
          },
        },
      },
    }

    See docs/bugs/unable-to-start-via-global-npm/findings.md for the full investigation.

Local development in this repo

  • The phase5:smoke script runs an end-to-end round-trip (jira_get_current_userjira_get_issuejira_update_issue_textjira_get_issue) against the built dist/index.js. It needs a Jira Cloud sandbox tenant. Set JIRA_ALLOW_ISSUE_UPDATES=true and a test issue key (default KAN-1) in your environment before running it. The script is manual — it is not part of any automated pipeline.
  • The image:smoke script calls jira_get_issue with include_attachments: true against a real issue that has image attachments (default SSP-2313) and prints the resulting MCP content blocks (text + each image block's mimeType and base64 length). Useful for verifying the Image Attachments feature against your own tenant before tagging a release. The script is also manual — it is not part of any automated pipeline.

Security

  • Read-only by default. Only the scoped issue text update tool can mutate Jira, and only after JIRA_ALLOW_ISSUE_UPDATES=true and an identity check.
  • No broad write mode. Method-locked to PUT; path-locked to /rest/api/3/issue/{key}. Even if a bug tried to call the write helper incorrectly, the API layer rejects it.
  • No secrets logged. JIRA_DEBUG=true logs URLs + status codes only.
  • No startup network calls. The server is fully offline until a tool fires.
  • See docs/plan.md §11 Security Posture for the full threat model.

License

MIT — see LICENSE.