Skip to main content
Glama
Hopcos
by Hopcos

Jira MCP Server

Production-ready Jira MCP server exposing Jira projects, issues, sprints, users, attachments, and worklogs as Model Context Protocol tools, resources, and prompts — to any MCP client (Claude Desktop, Claude Code, Cursor, VS Code) over stdio (local) or HTTP/SSE / Streamable HTTP (remote or team-shared).

┌──────────────────────────────────────────────────────────────────┐
│                         MCP Client                                │
│   Claude Desktop / Claude Code / Cursor / VS Code / custom        │
└───────────────┬───────────────────────────────────┬──────────────┘
                │ stdio (JSON-RPC over stdin/stdout) │   HTTP (SSE/Streamable)
                ▼                                    ▼
        ┌──────────────────┐              ┌──────────────────────┐
        │   stdio transport │              │   HTTP transport     │
        │   (default)       │              │   /sse /mcp /health  │
        └───────┬──────────┘              └──────────┬───────────┘
                │       same registered server        │
                ▼                                     ▼
        ┌──────────────────────────────────────────────────────────┐
        │               jira_mcp_server.server.create_server()      │
        │   27 tools · 4 resources · 4 prompts (transport-agnostic) │
        └───────────────────────────────┬──────────────────────────┘
                                        ▼
                       ┌────────────────────────────────┐
                       │  JiraClient (httpx async)       │
                       │  auth · rate-limit · retry      │
                       └────────────────┬───────────────┘
                                        ▼
                          Jira REST API / Agile API (HTTPS)

Features

  • Dual transports — stdio for local integration; HTTP with SSE and Streamable HTTP for remote/team deployments, switchable via CLI flag or MCP_TRANSPORT.

  • Dual Jira auth — Jira Cloud (basic: email + API token) and Jira Data Center / Server (Bearer PAT).

  • Security first — HTTPS-only (JIRA_BASE_URL refuses plaintext), no credentials in logs (token masking), client-side token-bucket rate limiting, project whitelist enforcement, tool-level CRUD permission control (JIRA_TOOLS), confirm guard on deletion, attachment size limits, optional static Bearer auth for the HTTP transport, CORS control.

  • Automatic retry + pagination — 429/5xx retry with exponential backoff; list endpoints paginate automatically.

  • ADF support — plain-text → Atlassian Document Format conversion for descriptions/comments; ADF → text rendering for reads.

  • Type-safe — Pydantic-validated tool schemas and config.

  • Docker readyDockerfile + docker-compose.yml.

Related MCP server: Jira MCP Server

Requirements

  • Python 3.11+ (3.12 recommended).

  • A Jira instance:

    • Jira Cloud — an API token (email + token for Basic auth), and the user should have at least: Browse projects, Create issues, Edit issues, Manage attachments, Manage worklogs, and (for board/sprint tools) access to the relevant boards.

    • Jira Data Center / Server — a Personal Access Token, and a TLS-terminated HTTPS endpoint (the server refuses plaintext).

Token management: create tokens per environment, scope them to the least privilege you need, and rotate them regularly. Never commit .env. If a token must be granted broad Jira permissions, pair it with JIRA_TOOLS (see Tool permissions) to restrict which of those permissions MCP clients can actually drive.

Installation

1. pip

pip install jira-mcp-server

2. uv

uv pip install jira-mcp-server

3. From source

git clone https://github.com/your-org/jira-mcp-server.git
cd jira-mcp-server
python -m venv .venv
source .venv/bin/activate      # Linux / macOS
pip install -e ".[dev]"

On Windows, use PowerShell instead:

git clone https://github.com/your-org/jira-mcp-server.git
cd jira-mcp-server
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"

or (CMD):

git clone https://github.com/your-org/jira-mcp-server.git
cd jira-mcp-server
python -m venv .venv
.\.venv\Scripts\activate.bat
python -m pip install -e ".[dev]"

A successful editable install automatically writes the jira-mcp-server console entry point — and on Windows the jira-mcp-server.exe launcher — into the active environment's Scripts directory. See Building the jira-mcp-server.exe console script.

4. Docker (HTTP mode)

docker run -p 8080:8080 \
  -e JIRA_BASE_URL=https://your-domain.atlassian.net \
  -e JIRA_API_TOKEN=your-token \
  -e JIRA_USER_EMAIL=your@email.com \
  -e JIRA_TOOLS=read,create,update \
  jira-mcp-server:latest

See Docker deployment for full details.

Quick start (3 minutes)

stdio mode — Claude Desktop

Add this to Claude Desktop's claude_desktop_config.json:

{
  "mcpServers": {
    "jira": {
      "command": "jira-mcp-server",
      "args": ["--transport", "stdio"],
      "env": {
        "JIRA_BASE_URL": "https://your-domain.atlassian.net",
        "JIRA_AUTH_METHOD": "basic",
        "JIRA_USER_EMAIL": "your-email@example.com",
        "JIRA_API_TOKEN": "your-api-token",
        "JIRA_TOOLS": "read,create,update"
      }
    }
  }
}

Restart Claude Desktop, open a conversation, and try:

"List my Jira projects." → the server calls jira_list_projects. "Create a Bug in PROJ titled 'Login fails on Safari'."jira_create_issue.

stdio mode — Cursor / VS Code

The same mcpServers block goes into the MCP configuration of your editor (~/.cursor/mcp.json for Cursor, or the VS Code MCP panel). The command stays jira-mcp-server with the same env.

HTTP/SSE mode

Start the service on port 8080:

jira-mcp-server --transport http --port 8080
# or using env vars
export MCP_TRANSPORT=http
export MCP_HOST=127.0.0.1
export MCP_PORT=8080
export JIRA_TOOLS=read,create,update
jira-mcp-server

Connect from any SSE-capable MCP client:

{
  "mcpServers": {
    "jira": {
      "url": "http://127.0.0.1:8080/sse",
      "headers": { "Authorization": "Bearer your-secret-token" }
    }
  }
}

Check it is up:

curl http://127.0.0.1:8080/health
# {"status":"healthy","server":"jira-mcp-server","version":"0.1.0","transport":"http","jira_configured":true}

To require a client token (recommended for anything beyond localhost), start with:

jira-mcp-server --transport http --port 8080 --auth-mode token --server-token your-secret-token

The token protects who can connect to the MCP server; the Jira credentials (JIRA_API_TOKEN) still govern what the server can do in Jira.

Client token (HTTP 认证)

In HTTP/SSE mode you can require clients to authenticate with a static Client token before the MCP session is allowed. Think of it as the server's "front door" password: it decides who may connect, whereas JIRA_API_TOKEN decides what Jira operations the server may perform.

Create a client token

Any reasonably long random string works (the server compares the presented token by exact string equality). Generate one with secrets, so it cannot be guessed:

# Python is cross-platform and already available (the project requires it).
python -c "import secrets; print(secrets.token_urlsafe(32))"
# e.g. 9xKp2vY6QwE3rT8zU1iA0sD5fG7hJ4kN6lM9oP2qR5uW8

Configure the server side

Enable token auth by supplying the same client token through one of:

# Option A — CLI flag (highest precedence).
jira-mcp-server --transport http --port 8080 \
  --auth-mode token --server-token 9xKp2vY6QwE3rT8zU1iA0sD5fG7hJ4kN6lM9oP2qR5uW8
# Option B — environment variables (equivalent; avoids the token in shell history).
export MCP_AUTH_MODE=token
export MCP_SERVER_TOKEN=9xKp2vY6QwE3rT8zU1iA0sD5fG7hJ4kN6lM9oP2qR5uW8
jira-mcp-server --transport http --port 8080
# Option C — docker-compose (environment passthrough, already wired).
environment:
  JIRA_TOOLS: ${JIRA_TOOLS:-}
  MCP_TRANSPORT: http
  MCP_AUTH_MODE: token
  MCP_SERVER_TOKEN: ${MCP_SERVER_TOKEN:-}

Set both MCP_AUTH_MODE=token and MCP_SERVER_TOKEN together. Note the exact behavior when one is missing:

  • MCP_AUTH_MODE=token and no MCP_SERVER_TOKEN → the server logs a warning at startup and serves unauthenticated (no auth middleware is attached). This is a footgun: if you intend to require a client token, set the token too.

  • MCP_AUTH_MODE=none (or unset) → no client auth at all, regardless of MCP_SERVER_TOKEN.

Configure the client side

The client sends the same token in an Authorization: Bearer … header:

{
  "mcpServers": {
    "jira": {
      "url": "http://127.0.0.1:8080/sse",
      "headers": { "Authorization": "Bearer 9xKp2vY6QwE3rT8zU1iA0sD5fG7hJ4kN6lM9oP2qR5uW8" }
    }
  }
}

Claude Code uses --header; Claude Desktop and Cursor/VS Code use the headers map above:

claude mcp add jira --scope project \
  --url http://127.0.0.1:8080/sse \
  --header "Authorization: Bearer 9xKp2vY6QwE3rT8zU1iA0sD5fG7hJ4kN6lM9oP2qR5uW8"

Sanity-check both sides with curl:

curl -i http://127.0.0.1:8080/health | head -1        # 401 without a token
curl -i -H "Authorization: Bearer 9xKp2vY6QwE3rT8zU1iA0sD5fG7hJ4kN6lM9oP2qR5uW8" \
  http://127.0.0.1:8080/health | head -1              # 200 with the token

Rotate the token

Generate a new token (see above), restart the server with it, and update every client config. The two-layer layout (server process holds the Jira credentials, clients only hold the client token) means rotating the client token does not require reissuing Jira tokens.

Connecting to Claude Code

Claude Code registers local MCP servers as stdio commands or remote HTTP URLs. With the package installed (so jira-mcp-server is on PATH — on Windows that means D:\develop\Python312\Scripts\jira-mcp-server.exe exists). If the executable is not on your machine yet, generate it from source first (see Building the jira-mcp-server.exe console script), then return here. From the project root:

claude mcp add jira --scope project \
  --env JIRA_BASE_URL=https://your-domain.atlassian.net \
  --env JIRA_USER_EMAIL=you@email.com \
  --env JIRA_AUTH_METHOD=basic \
  --env JIRA_API_TOKEN=ATATT3YOUR_REAL_TOKEN \
  --env JIRA_TOOLS=read,create,update \
  jira-mcp-server
  • JIRA_API_TOKEN is required: the server refuses to start with JIRA_AUTH_METHOD=basic unless an API token is present. Include it in the command (or see the safer alternatives below).

  • Omitting --scope project writes to the global config ~/.claude/mcp.json (all projects); --scope project writes a project-level .mcp.json so credentials are not shared across projects.

  • You can also pass --scope local to store the entry in ~/.claude.json for the current user only (3.x).

  • Where does the token come from? The server reads credentials from the environment that the stdio child process inherits when Claude Code starts it. Two ways to supply it:

    1. --env JIRA_API_TOKEN=... in the command above (easiest; token is stored in plain text in .mcp.json/mcp.json).

    2. Keep it out of the config by exporting it in the shell that launches claude (then omit the --env JIRA_API_TOKEN line). The child process inherits the exported variable:

      export JIRA_BASE_URL=https://your-domain.atlassian.net
      export JIRA_USER_EMAIL=you@email.com
      export JIRA_API_TOKEN=ATATT3YOUR_REAL_TOKEN
      export JIRA_AUTH_METHOD=basic
      export JIRA_TOOLS=read,create,update
      claude mcp add jira --scope project \
        --env JIRA_BASE_URL="$JIRA_BASE_URL" \
        --env JIRA_USER_EMAIL="$JIRA_USER_EMAIL" \
        --env JIRA_AUTH_METHOD=basic \
        --env JIRA_TOOLS="$JIRA_TOOLS" \
        jira-mcp-server
    3. A project-root .env file is not relied upon: the server tries to load it lazily, but only when a code layout allows it, and that is unreliable across installs. Use options 1 or 2.

Verify:

claude mcp list            # jira: Command - ✔ Connected
claude mcp get jira        # shows the resolved command + env

Restart Claude Code (or run /mcp to check connection status) and start a new session — the jira_* tools will be available.

If the server fails to start with Jira API Token is required when JIRA_AUTH_METHOD=basic, the most likely cause is that JIRA_API_TOKEN was not visible to the stdio child process. Either add it with --env JIRA_API_TOKEN=... or export it before launching claude.

HTTP/SSE (team-shared / multi-client)

# terminal 1: start the service (it holds the Jira credentials)
export JIRA_BASE_URL=https://your-domain.atlassian.net
export JIRA_USER_EMAIL=you@email.com
export JIRA_API_TOKEN=<token>
export JIRA_TOOLS=read,create,update
jira-mcp-server --transport http --host 127.0.0.1 --port 8080 \
  --auth-mode token --server-token <client-token>

# terminal 2: register the URL in Claude Code
claude mcp add jira --scope project \
  --url http://127.0.0.1:8080/sse \
  --header "Authorization: Bearer <client-token>"

This gives two-layer auth: <client-token> guards who may connect to the MCP server, JIRA_API_TOKEN governs what it can do in Jira. The <client-token> placeholder is the shared secret you generate yourself (see Client token (HTTP 认证)) — it is not the Jira API token.

Because the Jira credentials live in the server process (terminal 1), they never appear in Claude Code's mcp.json/.mcp.json — an advantage of the HTTP layout when you want to keep JIRA_API_TOKEN out of the client config.

Environment variables

Variable

Type

Required

Default

Mode

Description

Example

JIRA_BASE_URL

str

yes

all

Jira instance URL; must be https://

https://acme.atlassian.net

JIRA_AUTH_METHOD

enum

yes

basic

all

basic (Cloud) or bearer (Data Center)

basic

JIRA_USER_EMAIL

str

if basic

all

Account email for Cloud Basic auth

dev@acme.com

JIRA_API_TOKEN

str

yes

all

Jira API token (Cloud) or PAT (Server)

ATATT3xxxx…

JIRA_PROJECT_KEYS

str

no

all

Comma-separated allowlist; reads scoped & writes blocked outside it

ENG,SALES

JIRA_TOOLS

str

no

all

Tool permission allowlist (read/create/update/delete/write or exact tool names); empty ⇒ all

read,create,update

JIRA_RATE_LIMIT

int

no

100

all

Client-side requests/minute (token bucket)

200

JIRA_REQUEST_TIMEOUT

float

no

30

all

Per-request timeout (s)

30

JIRA_CONNECT_TIMEOUT

float

no

10

all

Connection-establishment timeout (s)

10

HTTPS_PROXY

str

no

all

Outbound proxy (honored by httpx trust_env)

https://proxy:8080

MCP_TRANSPORT

enum

no

stdio

both

stdio, http (SSE), or http-streamable

http

MCP_HOST

str

no

127.0.0.1

http

Bind address (use 127.0.0.1 + reverse proxy in prod)

0.0.0.0

MCP_PORT

int

no

8080

http

TCP port

8080

MCP_AUTH_MODE

enum

no

none

http

none or token

token

MCP_SERVER_TOKEN

str

if token

http

Bearer token clients must send

change-me

MCP_CORS_ORIGINS

str

no

*

http

Comma-separated allowed origins

https://app.example.com

MCP_LOG_LEVEL

enum

no

INFO

all

DEBUG/INFO/WARNING/ERROR

DEBUG

MCP_LOG_FILE

str

no

all

Log file; empty ⇒ stderr (never stdout)

/var/log/jira-mcp.log

Copy .env.example.env for local dev. CLI flags take precedence over environment variables.

CLI reference

jira-mcp-server [OPTIONS]

Options:
  --transport [stdio|http|http-streamable]  Transport mode (default: stdio; env MCP_TRANSPORT)
  --host TEXT                HTTP bind host (default: 127.0.0.1; env MCP_HOST)
  --port INTEGER             HTTP bind port (default: 8080; env MCP_PORT)
  --auth-mode [none|token]   HTTP client auth (default: none; env MCP_AUTH_MODE)
  --server-token TEXT        Bearer token for client connections (env MCP_SERVER_TOKEN)
  --cors-origins TEXT        CORS origins, comma-separated (default: *)
  --log-level [DEBUG|INFO|WARNING|ERROR]  Logging level (env MCP_LOG_LEVEL)
  --log-file TEXT            Optional log file (env MCP_LOG_FILE)
  --version                  Print version and exit
  --help                     Show help

Transports

Characteristic

stdio

HTTP/SSE

Best for

single local user

remote / team-shared / CI

Deployment

spawned by the client

standalone service

Clients

Claude Desktop, Cursor, VS Code

any MCP SSE/Streamable client

Concurrency

one client

many clients

Client auth

per-process env

Bearer token (MCP_SERVER_TOKEN)

Network

local only

network reachable

Endpoint

stdin/stdout

/sse + /messages/ (SSE) or /mcp (streamable); /health, /

Tools

All tools return JSON text. On failure they return {"isError": true, "content": [{"type": "text", "text": "Jira API Error [403]: …"}]}.

Issues

Tool

Description

Key params

jira_create_issue

Create an issue (auto-converts description to ADF)

project_key, summary, issue_type; optional description, priority, assignee_account_id, labels, components, custom_fields, parent_key

jira_update_issue

Update fields

issue_key, fields

jira_get_issue

Get full issue

issue_key, fields*, expand*

jira_delete_issue

Delete (guarded)

issue_key, confirm (must be true), delete_subtasks*

jira_transition_issue

Transition by id or status name

issue_key, transition_id*/target_status*, comment*, fields*

jira_get_transitions

List available transitions

issue_key

jira_add_comment

Add a comment

issue_key, body, visibility*, visibility_value*

jira_get_comments

List comments

issue_key, max_results*, start_at*

jira_link_issues

Create a link between issues

inward_issue_key, outward_issue_key, link_type, comment*

jira_get_issue_links

List links

issue_key

jira_search_issues

JQL search (whitelist-scoped)

jql, max_results*, start_at*, fields*, expand*

jira_search_issues_jql_only

Compact JQL search (key/summary/status/assignee)

jql, max_results* (default 20)

Sprints / Boards

Tool

Description

Key params

jira_list_boards

List boards

project_key*, board_type*

jira_list_sprints

List sprints

board_id, state*, max_results*

jira_get_sprint_issues

Issues in a sprint

sprint_id

jira_move_issues_to_sprint

Move issues to a sprint

sprint_id, issue_keys

Projects

Tool

Description

Key params

jira_list_projects

List accessible projects

max_results*

jira_get_project

Get project detail

project_key

jira_get_project_versions

List project versions

project_key

Users

Tool

Description

Key params

jira_search_users

Search users (privacy-filtered)

query, max_results*

jira_get_myself

Authenticated user info / debug auth

Attachments

Tool

Description

Key params

jira_add_attachment

Upload a file (≤ 10 MiB)

issue_key, file_path

jira_list_attachments

List attachments

issue_key

Worklogs

Tool

Description

Key params

jira_add_worklog

Log time

issue_key, time_spent (e.g. 2h 30m), comment*, started*

jira_get_worklogs

List worklogs

issue_key

Example return value

jira_create_issue(project_key="PROJ", summary="Login fails", issue_type="Bug")

{"issue":{"id":"10004","key":"PROJ-9","self":"https://acme.atlassian.net/rest/api/3/issue/10004"}}

Resources

MCP resources expose read-only context the model can reference:

URI (template)

Content

jira://projects

Static listing of accessible projects (key – name).

jira://project/{project_key}/meta

Create-issue metadata: issue types + editable fields.

jira://issue/{issue_key}

Live snapshot of a single issue.

jira://issue/{issue_key}/transitions

Available workflow transitions.

Reading jira://project/PROJ/meta, for example, returns JSON such as:

{"projects": [{"key": "PROJ", "issuetypes": [{"name": "Bug", "fields": {"summary": {"required": true}}}]}]}

Prompts

Prompts guide the model through structured workflows:

Prompt

Description

Variables

create_bug_report

Draft a structured Bug + create it

project_key, summary, steps_to_reproduce, expected_behavior, actual_behavior, severity

sprint_review_summary

Summarize a sprint for review

sprint_id

triage_issue

Recommend priority/component/assignee

issue_key

daily_standup_report

Group last-24h issue changes by people

project_key, sprint_id (optional)

Example — after invoking create_bug_report the model will assemble the details and call jira_create_issue automatically.

Tool permissions (CRUD control)

A Jira API token is often granted broad rights (e.g. create, edit, delete), but a given deployment may only need a subset of them. Rather than maintaining a separate token per workflow, the server can restrict which tools it exposes via JIRA_TOOLS. Disabled tools are never registered, so clients cannot see them in tools/list — an over-permissioned token can still only drive the tools you opt in to.

JIRA_TOOLS is a comma-separated allowlist of category keywords and/or exact tool names. Empty or unset keeps today's behavior (all tools enabled).

Keyword

Effect

read

All read-only tools: jira_get_*, jira_list_*, jira_search_*, jira_get_*_meta. No mutating operations.

create

Create issues, add comments / attachments / worklogs, link issues, move issues to sprints.

update

Update issue fields and transition issues.

delete

jira_delete_issue.

write

Shorthand for create,update,delete.

Examples:

# Read-only deployment (the Jira token may be full-admin; clients can only read).
JIRA_TOOLS=read

# Everything except deletion.
JIRA_TOOLS=read,create,update

# Read + exactly one extra tool.
JIRA_TOOLS=read,jira_add_comment

Notes:

  • Values are case-insensitive; entries must be a known keyword or a real tool name. A typo (e.g. JIRA_TOOLS=rede) fails startup instead of silently dropping tools.

  • The current tool-category mapping is:

    Category

    Tools

    read

    jira_get_issue, jira_get_transitions, jira_get_comments, jira_get_issue_links, jira_get_issue_meta, jira_get_project_meta, jira_search_issues, jira_search_issues_jql_only, jira_list_projects, jira_get_project, jira_get_project_versions, jira_list_boards, jira_list_sprints, jira_get_sprint_issues, jira_list_attachments, jira_get_worklogs, jira_search_users, jira_get_myself

    create

    jira_create_issue, jira_add_comment, jira_add_attachment, jira_add_worklog, jira_link_issues, jira_move_issues_to_sprint

    update

    jira_update_issue, jira_transition_issue

    delete

    jira_delete_issue

This layer protects the tool surface, not Jira itself. Authorization also follows JIRA_PROJECT_KEYS: combine both to scope by operation and by project (e.g. JIRA_TOOLS=read JIRA_PROJECT_KEYS=ENG = read-only on one project).

Security & best practices

  • HTTPS onlyJIRA_BASE_URL must start with https://; plaintext URLs are refused at startup.

  • No hardcoded credentials — everything comes from the environment.

  • Token masking — tokens are logged as ATATT****, never in full.

  • Project whitelist — set JIRA_PROJECT_KEYS=ENG,SALES to scope all JQL searches and block writes to other projects.

  • Tool whitelistJIRA_TOOLS restricts which tools are exposed (see Tool permissions); use it to keep an over-permissioned token from driving destructive tools.

  • Deletion guardjira_delete_issue requires confirm=true.

  • Attachment guard — uploads over 10 MiB are rejected.

  • Rate limiting — client-side token bucket (default 100 req/min).

  • HTTP transport auth — enable --auth-mode token and a strong MCP_SERVER_TOKEN for anything networked.

  • Production HTTP — bind 127.0.0.1 and put the server behind a reverse proxy (nginx/Caddy) that terminates TLS; restrict the port to the proxy and approved clients; enable request/audit logging.

  • Rotate tokens — use distinct tokens per environment, rotate quarterly (or on any suspected leak), and remove users from Jira when they leave.

  • Least privilege — give the token user only the Jira permissions the workflows need (browse + create/edit specific projects; not Global Admin).

Docker deployment

Dockerfile (included)

FROM python:3.12-slim
WORKDIR /app
COPY pyproject.toml README.md ./
COPY src/ src/
RUN pip install --no-cache-dir .
ENV MCP_TRANSPORT=http MCP_HOST=0.0.0.0 MCP_PORT=8080 JIRA_AUTH_METHOD=basic JIRA_TOOLS=read,create,update
EXPOSE 8080
CMD ["jira-mcp-server", "--transport", "http", "--host", "0.0.0.0", "--port", "8080"]

docker-compose.yml (included)

services:
  jira-mcp:
    build: .
    ports: ["8080:8080"]
    environment:
      JIRA_BASE_URL: ${JIRA_BASE_URL}
      JIRA_API_TOKEN: ${JIRA_API_TOKEN}
      JIRA_USER_EMAIL: ${JIRA_USER_EMAIL}
      JIRA_PROJECT_KEYS: ${JIRA_PROJECT_KEYS:-}
      JIRA_TOOLS: ${JIRA_TOOLS:-}
      MCP_TRANSPORT: http
      MCP_HOST: 0.0.0.0
      MCP_PORT: 8080
      MCP_AUTH_MODE: ${MCP_AUTH_MODE:-none}
      MCP_SERVER_TOKEN: ${MCP_SERVER_TOKEN:-}
    restart: unless-stopped
docker compose up -d                        # reads .env for JIRA_* / MCP_SERVER_TOKEN
curl http://127.0.0.1:8080/health

Kubernetes (example)

apiVersion: apps/v1
kind: Deployment
metadata: { name: jira-mcp }
spec:
  replicas: 2
  template:
    metadata: { labels: { app: jira-mcp } }
    spec:
      containers:
        - name: jira-mcp
          image: your-registry/jira-mcp-server:latest
          ports: [{ containerPort: 8080 }]
          envFrom: [{ secretRef: { name: jira-mcp-secrets } }]
          readinessProbe:
            httpGet: { path: /health, port: 8080 }
---
apiVersion: v1
kind: Service
metadata: { name: jira-mcp }
spec:
  selector: { app: jira-mcp }
  ports: [{ port: 8080, targetPort: 8080 }]

Troubleshooting

Symptom

Likely cause

Fix

Jira API Error [401]

Jira token missing/wrong/expired

Regenerate at the API-token page; re-run

Jira API Error [403]

Token lacks permission for the operation

Grant the user the Jira permission or widen API token scope

Jira API Error [404]

Wrong issue/project key

Verify the key; check the project exists and is accessible

Jira API Error [400]

Invalid field/value for the transition

Read the message; adjust fields

Jira API Error [429]

Jira's own rate limit

Slow down; honor retry-after; raise JIRA_RATE_LIMIT

Connection timed out

Wrong URL / blocked egress / proxy

Check JIRA_BASE_URL, outbound network, HTTPS_PROXY

Request validation failed on /sse

DNS-rebinding protection from the SDK

Use a real Host header; the SDK allows 127.0.0.1/localhost as default. For proxies, pass --host 0.0.0.0 or a configured host

SSE drops then reconnects

Idle/long-lived connection timeout on the network

Reconnect is automatic in MCP clients; check proxies for < SSE > 60s timeouts

401 on the HTTP endpoint

--auth-mode token wrong/absent Authorization

Send Authorization: Bearer <MCP_SERVER_TOKEN>

Diagnosing Jira auth quickly: run

jira-mcp-server --transport stdio
# and in a client, call jira_get_myself — it returns the Jira user the token resolves to.

Rate limiting: the client limiter blocks the calling task instead of erroring, so batch prompts just run a little slower. Jira's own limits are upstream of the token; space out CI loops and use max_results liberally.

Development

Add a new tool

  1. Open src/jira_mcp_server/tools/issues.py (or the matching module).

  2. Inside the existing register(registry) function add:

@registry.tool(name="my_new_tool", title="...", description="...")
async def my_new_tool(
    ctx: Context,
    param1: Annotated[str, Field(description="...")],
    optional: Annotated[int | None, Field(default=None, description="...")] = None,
) -> Any:
    """Docstring used as the default description."""
    client = get_client()
    try:
        data = await client.some_endpoint(param1)
        return dict_result(data, label="result")
    except Exception as exc:
        return error_result(exc)
  1. Add the matching client method in src/jira_mcp_server/client.py.

  2. Run the tests.

Add a new resource

In src/jira_mcp_server/tools/resources.py, inside register():

@registry.resource("jira://issue/{issue_key}/comments")
def comments_for_issue(issue_key: str) -> str:
    return json_dumps(get_client().get_comments_blocking_for_resource(issue_key))

Tests

pytest                                # full suite
pytest -m "not network"               # offline unit + integration tests
pytest tests/test_transport_http.py   # HTTP/SSE app tests

The suite uses pytest + pytest-asyncio; HTTP tests run against an in-process Starlette app via httpx.ASGITransport (no live network).

Style

ruff (lint + format) and mypy are configured in pyproject.toml:

ruff check src tests
ruff format --check src tests
mypy src

Commit messages: conventional-commit style (e.g. feat: add jira_export_issues). Changes land via PR; each PR must pass lint, type checks, and the test suite.

Building the jira-mcp-server.exe console script

There is no separate "build" step for the executable — on Windows it is created automatically when you install the project, from the [project.scripts] entry point in pyproject.toml:

[project.scripts]
jira-mcp-server = "jira_mcp_server.cli:app"

What the entry point produces

pip (via the setuptools backend) generates a small launcher:

  • On Windows: jira-mcp-server.exe inside the Python environment's Scripts folder (e.g. D:\develop\Python312\Scripts\jira-mcp-server.exe, or .venv\Scripts\jira-mcp-server.exe when using a virtual env).

  • On macOS/Linux: a jira-mcp-server shell script on PATH.

The launcher is a thin shim that imports jira_mcp_server.cli and calls app (a Typer command). All real logic lives in src/; the .exe is just a starter.

Build it from source (Windows)

# 1. Create and activate a virtual environment.
python -m venv .venv
.\.venv\Scripts\Activate.ps1

# 2. Install the project (editable keeps src/ live; plain install copies it).
#    Either is fine. "-e" means you can edit src/ and restart the MCP client
#    without reinstalling.
python -m pip install -e ".[dev]"      # editable, recommended for development
# python -m pip install .              # regular install, recommended for a fixed version

# 3. The .exe is now on PATH inside the venv (or the base Scripts dir).
jira-mcp-server --version              # -> jira-mcp-server 0.1.1

Verify

python -m jira_mcp_server --version   # same behavior as the exe
jira-mcp-server --version             # the generated launcher/exe

If jira-mcp-server is not found, ensure the environment's Scripts directory is on PATH (Python's installer usually adds it). To reinstall after deleting the launcher, re-run pip install -e ..

Editable vs. regular install

  • pip install -e . ("editable") registers the launcher but points it back at your src/ tree. Edit source, restart the MCP client, and the change is picked up without reinstalling — the normal choice while developing.

  • pip install . ("regular") copies the package into the environment's site-packages; the launcher runs that frozen copy. Use it for a fixed version you do not expect to edit.

Both commands produce the same jira-mcp-server.exe; only where the code lives differs.

Build without a virtual environment

If you installed Python globally and cannot or do not want a venv, run pip install -e . directly. The launcher then lands in the base interpreter's Scripts folder (e.g. D:\develop\Python312\Scripts\jira-mcp-server.exe), which must be on PATH for MCP clients to find the jira-mcp-server command.

Architecture / how to extend

src/jira_mcp_server/
├── cli.py            # typer CLI → transport selection
├── server.py         # create_server(): MCPServer + registration
├── config.py         # pydantic-settings (JIRA_* env), validated, lazy
├── safety.py         # credential masking / normalization
├── permissions.py    # JIRA_TOOLS allowlist: CRUD categories → tool sets
├── client.py         # JiraClient: httpx async, retry, pagination, scope
├── auth.py           # Basic / Bearer header construction
├── errors.py         # JiraError hierarchy + status mapping
├── formatters.py     # ADF ⇄ plain text
├── validators.py     # safe JQL building
├── rate_limiter.py   # token bucket
├── middleware.py     # HTTP auth / logging / CORS middleware + /health
├── transport/
│   ├── stdio.py      # stdio runner
│   ├── http.py       # SSE + Streamable HTTP apps (extends SDK app)
│   └── logging.py    # stderr/file logging, sensitive filter
└── tools/
    ├── core.py       # ToolRegistry, register_server, shared client
    ├── serde.py      # CallToolResult helpers
    ├── issues.py     # 14 issue tools
    ├── projects.py   # 3 project tools
    ├── sprints.py    # 4 board/sprint tools
    ├── users.py      # 2 user tools
    ├── attachments.py# 2 attachment tools
    ├── worklog.py    # 2 worklog tools
    ├── resources.py  # 4 MCP resources
    └── prompts.py    # 4 MCP prompts

Planned / extension points

  • Webhook events (the transport layer is decoupled so an HTTP Webhook route can be added without touching the tools).

  • Multi-Jira-instance support (the config layer is a single Settings object; a future MCP_INSTANCES could create one per instance).

  • Per-tool granular Prompts / completion metadata.

Changelog

v0.1.1 (2026-08-06)

  • Tool permission control (JIRA_TOOLS) — restrict which MCP tools are exposed via comma-separated CRUD keywords (read/create/update/ delete/write) or exact tool names. Disabled tools are not registered, so clients never see them; typos fail startup. See Tool permissions.

  • Docs: 补充 基于源码安装(含 Windows PowerShell/CMD 命令)与本地生成 jira-mcp-server.exe 的完整说明;新增 Client token (HTTP 认证) 章节,说明 client-token 的生成、服务端与 客户端两侧的配置方式及旋转方法。

v0.1.0 (2026-08-06)

  • Initial release.


Jira MCP Server is not affiliated with, endorsed by, or sponsored by Atlassian. "Jira" is a trademark of Atlassian Pty Ltd.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Personal assistant MCP server with search, execute, packages, jobs, secrets, and integrations.

  • A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…

  • MCP server for AI access to SmartBear tools, including BugSnag, Reflect, Swagger, PactFlow, QTM4J.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Hopcos/jira-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server