Skip to main content

Jira MCP (Model Context Protocol) server built with FastMCP for Jira Cloud API v3

Project description

Jira MCP Server

A comprehensive MCP (Model Context Protocol) server for Jira Cloud integration, built with FastMCP v3. Provides 8 focused tools for complete Jira issue and project management, with automatic ADF formatting, multi-tenant HTTP support, and interactive Elicitation for guided issue creation.

Version 3.1.0 — Single-server FastMCP v3 rewrite with multi-tenant support, Elicitation, resource templates, and 118 tests. Published on PyPI.

Features

  • 8 Focused Tools for complete Jira issue and project management
  • Interactive Elicitationissue_create and issue_delete interactively request missing or confirmatory input
  • Streamable-HTTP + Stdio Transport — run as an HTTP server or stdio for Claude Desktop
  • Multi-Tenant HTTP — different clients can connect with different Jira credentials per request
  • Smart Field Normalization — case-insensitive types/priorities, display-name assignee resolution, component name-to-ID lookup
  • Automatic ADF Formatting — plain text and Markdown auto-converted to Atlassian Document Format
  • Transition Resolution — use transition names ("In Progress") instead of numeric IDs
  • JQL Search with verbosity control (ids / summary / full)
  • Jira Cloud API v3 compatibility
  • Docker Support with hot-reload development mode
  • 118 Tests — unit tests and FastMCP Client integration tests

Installation

Prerequisites

Install from PyPI (Recommended)

The package is published to PyPI. Choose any Python package manager:

uvx (Run Without Installing)

The fastest way to try it — runs in an ephemeral environment with no permanent install:

uvx atlassian-jira-mcp-server

uv tool (Persistent Install)

Installs the CLI commands (jira-mcp-server, jira-mcp-http) in an isolated environment:

uv tool install atlassian-jira-mcp-server

pipx (Isolated Install)

Same concept as uv tool but using pipx:

pipx install atlassian-jira-mcp-server

pip (Virtual Environment)

pip install atlassian-jira-mcp-server

After any of the above, two CLI commands are available:

Command Transport Use Case
jira-mcp-server stdio Claude Desktop, Claude Code (stdio), any MCP client
jira-mcp-http streamable-http Multi-tenant HTTP deployment, remote access

MCP Client Configuration

Claude Desktop (stdio via PyPI)

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "jira": {
      "command": "jira-mcp-server",
      "env": {
        "JIRA_BASE_URL": "https://your-domain.atlassian.net",
        "JIRA_USERNAME": "your-email@domain.com",
        "JIRA_API_TOKEN": "your-api-token",
        "JIRA_DEFAULT_PROJECT": "PROJ"
      }
    }
  }
}

Claude Desktop (stdio via uvx — no install needed)

{
  "mcpServers": {
    "jira": {
      "command": "uvx",
      "args": ["atlassian-jira-mcp-server"],
      "env": {
        "JIRA_BASE_URL": "https://your-domain.atlassian.net",
        "JIRA_USERNAME": "your-email@domain.com",
        "JIRA_API_TOKEN": "your-api-token",
        "JIRA_DEFAULT_PROJECT": "PROJ"
      }
    }
  }
}

Claude Code (stdio)

claude mcp add jira -- jira-mcp-server

Or with uvx (no install):

claude mcp add jira -- uvx atlassian-jira-mcp-server

Then set environment variables in your shell or .env file.

Claude Code / Any HTTP Client

Start the HTTP server first:

jira-mcp-http
# or: uvx --from atlassian-jira-mcp-server jira-mcp-http

Then configure the client:

{
  "mcpServers": {
    "jira": {
      "url": "http://localhost:8000/mcp",
      "headers": {
        "X-Jira-Base-Url": "https://your-domain.atlassian.net",
        "X-Jira-Username": "your-email@domain.com",
        "X-Jira-Api-Token": "your-api-token"
      }
    }
  }
}

Install from Source (Development)

git clone https://github.com/your-username/atlassian-jira-mcp-server
cd atlassian-jira-mcp-server

python3 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install with dev dependencies
pip install -e ".[dev]"

Or install directly as a uv tool from source:

git clone https://github.com/your-username/atlassian-jira-mcp-server
cd atlassian-jira-mcp-server
uv tool install . --force --reinstall

Docker (HTTP Transport)

git clone https://github.com/your-username/atlassian-jira-mcp-server
cd atlassian-jira-mcp-server

# Create a .env file with your credentials (see Configuration section)
cp .env.example .env  # edit with your values

# Build and start the server
docker-compose up -d

The server will be available at http://localhost:8000/mcp.

For multi-tenant mode, leave Jira credentials out of .env and provide them via headers or query params on each request instead.

Configuration

Environment Variables

Create a .env file in the project root:

# Jira credentials (required for single-tenant / env-based config)
JIRA_BASE_URL=https://your-domain.atlassian.net
JIRA_USERNAME=your-email@domain.com
JIRA_API_TOKEN=your-api-token
JIRA_DEFAULT_PROJECT=PROJ          # Optional default project key

# HTTP server settings (HTTP transport only)
FASTMCP_HOST=0.0.0.0               # Default: 0.0.0.0
FASTMCP_PORT=8000                  # Default: 8000

# Optional bearer-token authentication for the MCP endpoint
MCP_AUTH_TOKEN=your-secure-bearer-token

Multi-Source Credential Resolution (HTTP Transport)

For HTTP deployments, credentials are resolved per request using the following priority (highest to lowest):

  1. Query parameters?jira_base_url=...&jira_username=...&jira_api_token=...
  2. Authorization: Basic header — standard HTTP Basic Auth (username:api_token base64-encoded)
  3. X-Jira-* headersX-Jira-Base-Url, X-Jira-Username, X-Jira-Api-Token, X-Jira-Default-Project
  4. Environment variables — fallback for single-tenant / Docker deployments

This allows a single HTTP server instance to serve multiple Jira tenants simultaneously.

Example: Query Parameter Auth

curl "http://localhost:8000/mcp?jira_base_url=https://your-domain.atlassian.net&jira_username=user@example.com&jira_api_token=your-token"

Example: Basic Auth Header

curl -H "Authorization: Basic $(echo -n 'user@example.com:api-token' | base64)" \
     http://localhost:8000/mcp

Example: X-Jira-* Headers

curl -H "X-Jira-Base-Url: https://your-domain.atlassian.net" \
     -H "X-Jira-Username: your-email@domain.com" \
     -H "X-Jira-Api-Token: your-api-token" \
     http://localhost:8000/mcp

Usage

Running the Server

HTTP Transport

# With Docker (recommended)
docker-compose up -d

# Manually via Python entry point
python -c "from server import run_http; run_http()"

# Or via the installed CLI command
jira-mcp-http

Stdio Transport (Claude Desktop)

# Via installed CLI command
jira-mcp-server

# Or directly
python server.py

Connecting with MCP Clients

See the MCP Client Configuration section under Installation above for Claude Desktop, Claude Code, and HTTP client setup.

For multi-tenant HTTP deployments, configure clients to include credentials per request via headers or query params. The server resolves credentials fresh per request so different clients can target different Jira instances.

Available Tools

Issue Operations

issue_get — Get a single issue

issue_get(
    issue_key="PROJ-123",
    verbosity="full",          # "ids" | "summary" | "full" (default: "full")
    include_transitions=True,
    include_comments=True,
)

Verbosity levels:

  • ids — issue key only (minimal tokens)
  • summary — key fields: summary, status, assignee, priority, dates
  • full — complete details including description, comments, transitions

issue_search — Search with JQL

issue_search(
    jql="project = PROJ AND status = 'In Progress'",
    verbosity="summary",       # default: "summary"
    max_results=50,
)

Common JQL patterns:

# My open issues
issue_search(jql="assignee = currentUser() AND statusCategory != Done")

# High priority bugs
issue_search(jql="priority = High AND issuetype = Bug")

# Sprint backlog
issue_search(jql="sprint in openSprints() AND project = PROJ")

issue_create — Create a new issue

Supports interactive Elicitation for missing required fields (project_key, issue_type, summary).

issue_create(
    project_key="PROJ",
    issue_type="Story",        # case-insensitive; "story", "bug", "task" all work
    summary="Implement login feature",
    description="## Overview\nDetailed description with **markdown** support",
    priority="high",           # aliases: "high"→"High", "critical"→"Highest"
    assignee="John Smith",     # display name, email, or account ID
    components=["frontend"],   # component names auto-resolved to IDs
    labels=["backend", "auth"],
    auto_normalize=True,       # default; normalizes all field values
)

issue_update — Update fields, transition status, and add comments (unified)

# Update fields
issue_update(issue_key="PROJ-123", priority="High", labels=["backend"])

# Transition status (by name or ID)
issue_update(issue_key="PROJ-123", transition="In Progress")

# Add a comment
issue_update(issue_key="PROJ-123", comment="Review complete")

# Combined: transition + assign + comment in one call
issue_update(
    issue_key="PROJ-123",
    transition="In Progress",
    assignee="dev@example.com",
    comment="Starting work on this",
)

issue_delete — Delete an issue

Uses Elicitation to confirm before deleting.

issue_delete(issue_key="PROJ-123", delete_subtasks=False)

issue_comment — Add a comment

issue_comment(
    issue_key="PROJ-123",
    body="Looks good to me — merging.",
    mentions=["user@example.com"],  # optional @mentions
)

Project Operations

project_get — Get project details

project_get(
    project_key="PROJ",
    verbosity="summary",
    include_components=True,
    include_versions=True,
)

Validation

issue_validate_fields — Validate and normalize fields before creation

Use this to preview normalization and catch errors before calling issue_create:

result = issue_validate_fields(
    project_key="PROJ",
    issue_type="bug",
    priority="high",
    assignee="John",
    components=["frontend"],
)

# result["valid"] == True/False
# result["normalized"] == normalized field values
# result["errors"] == list of field errors with suggestions

Resource Endpoints

Resource Templates (Parameterized)

Read any Jira entity by URI — MCP clients discover these via list_resource_templates():

URI Template Description
issue://{issue_key} Get any issue by key (e.g., issue://PROJ-123)
project://{project_key} Get any project by key (e.g., project://PROJ)

Static Resources

Access workspace data through fixed MCP resource URIs:

URI Description
current-user://info Current authenticated Jira user
current-user://issues Issues assigned to the current user
workspace://users Active users in the workspace
workspace://projects Accessible Jira projects
workspace://recent-issues Issues updated in the last 7 days
workspace://verbose-metadata Combined metadata: priorities and statuses
workspace://issue-creation-metadata Issue types per project + priorities + assignees
adf-examples:// ADF formatting examples and reference

ADF (Atlassian Document Format) Support

The server automatically converts plain text and Markdown to ADF format — no manual formatting required.

# Plain text
description = "Simple text description"

# Markdown (headings, bold, lists, code blocks, links all supported)
description = """
## Overview
This issue implements **important** features.

### Acceptance Criteria
- First criterion
- Second criterion

```python
def example():
    return "code block"

"""


## Architecture

Version 3.0.0 uses a single FastMCP instance with direct tool/resource/prompt registration:

atlassian-jira-mcp-server/ ├── server.py # Single FastMCP server entry point ├── src/ │ ├── client/ │ │ ├── jira.py # JiraClient — per-request HTTP client │ │ ├── config.py # TenantConfig — multi-source credential resolution │ │ └── errors.py # Typed error classes (PermanentError, etc.) │ ├── tools/ │ │ ├── issue.py # issue_get, issue_search, issue_create, issue_update, │ │ │ # issue_delete, issue_comment │ │ ├── project.py # project_get │ │ ├── validation.py # issue_validate_fields │ │ └── helpers.py # Shared utilities (get_jira_client, elicit_or_error) │ ├── resources/ │ │ ├── static.py # ADF examples resource │ │ └── workspace.py # Dynamic workspace/user/project resources │ ├── prompts/ │ │ └── workflows.py # Workflow guidance prompts │ ├── middleware/ │ │ ├── auth.py # BearerAuthMiddleware — MCP endpoint token validation │ │ └── tenant.py # TenantMiddleware — per-request credential resolution │ └── utilities/ │ ├── adf.py # Markdown→ADF conversion │ ├── resolvers.py # Component/transition name→ID resolution with caching │ └── formatting.py # Issue/project response formatting + verbosity ├── tests/ # 118 tests (unit + FastMCP Client integration) ├── Dockerfile ├── docker-compose.yml ├── pyproject.toml └── requirements.txt


### Key Design Patterns

1. **Single-Server Architecture** — no mounted sub-servers; tools, resources, and prompts register directly on one `FastMCP` instance
2. **Middleware Stack** — `BearerAuthMiddleware` (endpoint auth) → `TenantMiddleware` (per-request credential injection)
3. **Per-Request JiraClient** — no global HTTP client; each tool call gets a fresh `JiraClient` scoped to the request's `TenantConfig`
4. **Multi-Tenant by Default** — HTTP mode supports any number of Jira tenants with zero configuration changes
5. **Elicitation** — `issue_create` and `issue_delete` use FastMCP v3's `ctx.elicit()` for interactive guidance

## Development

### Running Tests

```bash
# Run full test suite
pytest tests/ -v

# Run a specific test file
pytest tests/test_integration.py -v

# With venv
.venv/bin/pytest tests/ -v

The test suite includes:

  • Unit tests for individual functions (test_client.py, test_config.py, test_errors.py, test_tools_*.py, etc.)
  • FastMCP Client integration tests (test_integration.py) — exercise the full MCP stack in-memory using FastMCP's Client without a running server
  • Middleware tests (test_middleware.py) — validate auth and tenant credential resolution

After Code Changes

# Reinstall uv tool
uv tool uninstall atlassian-jira-mcp-server && uv tool install . --reinstall

# Rebuild Docker image
docker-compose down && docker-compose build && docker-compose up -d

Verifying Imports

python -c "from src.tools import register_tools; print('OK')"
python -c "from server import create_server; print('OK')"

Adding New Tools

  1. Implement in src/tools/ (e.g., src/tools/issue.py):
@mcp.tool
async def issue_new_operation(ctx: Context, issue_key: str) -> dict:
    """Tool description shown to the LLM."""
    async with await get_jira_client(ctx) as client:
        result = await client.request("GET", f"issue/{issue_key}/subtasks")
        return result
  1. Register in src/tools/__init__.py:
from src.tools import new_module
new_module.register(mcp)

Error Handling

The server distinguishes temporary from permanent errors:

Error Type Retry? Example
Connection error Yes "Connection Error: Unable to connect..."
Timeout Yes "Timeout Error: Request timed out..."
Session error Yes "No valid session ID provided"
Permission error (403) No "Permission Error (HTTP 403): ..."
Validation error (400) No "Jira API Error: field 'x' is required"
Not found (404) No Project or issue does not exist

Troubleshooting

"externally-managed-environment" Error (pip)

Use uv tool install (Method 2) or a virtual environment (Method 3) instead of a system pip install.

Python Not Found with pyenv

# Set a global Python version
pyenv global 3.12.11

# Or use python3 explicitly
python3 -m venv .venv

Authentication Failures

  • Verify JIRA_BASE_URL includes https:// and no trailing slash
  • Confirm the API token belongs to the same email as JIRA_USERNAME
  • Check that the token has not expired in Atlassian account settings

Permission Errors (HTTP 403)

  • Confirm the Jira user has Browse Project and the relevant Create/Edit/Delete permissions
  • Check that the issue type is available in the target project

Contributing

Contributions are welcome. Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality (pytest tests/ -v must pass)
  4. Submit a pull request

License

MIT License — see LICENSE file for details.

Support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

atlassian_jira_mcp_server-3.1.0.tar.gz (50.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

atlassian_jira_mcp_server-3.1.0-py3-none-any.whl (45.3 kB view details)

Uploaded Python 3

File details

Details for the file atlassian_jira_mcp_server-3.1.0.tar.gz.

File metadata

File hashes

Hashes for atlassian_jira_mcp_server-3.1.0.tar.gz
Algorithm Hash digest
SHA256 37770ade721d45cee01e1b4621c2fee5febf1146d73aa1e5452d8499e0380d46
MD5 f740d9c7042e9ae2a54614689ce6eca6
BLAKE2b-256 a7d1c7344d8175c1a21c9cc354db8f3cda4a16cc5eafd884be3def1d2565ac64

See more details on using hashes here.

Provenance

The following attestation bundles were made for atlassian_jira_mcp_server-3.1.0.tar.gz:

Publisher: publish.yml on jasonpaulso/jira-openapi-generated

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file atlassian_jira_mcp_server-3.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for atlassian_jira_mcp_server-3.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 456eb4bf20b9f1e704e388917ee3a59c35d189fdef51abe363db01af2163ef59
MD5 fc3cbaeacb0c946bf567ef3d7fdd8524
BLAKE2b-256 4f72dae8151792a7537bf238c097a822139d8e51f8b2f98a111ff3abc1bd4809

See more details on using hashes here.

Provenance

The following attestation bundles were made for atlassian_jira_mcp_server-3.1.0-py3-none-any.whl:

Publisher: publish.yml on jasonpaulso/jira-openapi-generated

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page