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.0.0 — Single-server FastMCP v3 rewrite with multi-tenant support, Elicitation, and 104 tests.

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
  • 104 Tests — unit tests and FastMCP Client integration tests

Installation

Prerequisites

  • Python 3.10+
  • Jira Cloud account with an API token (create one here)
  • uv (recommended) or pip
  • Docker (optional, for containerized HTTP deployment)

Method 1: Docker (Recommended for HTTP Transport)

# Clone repository
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.

To pass Jira credentials at runtime (multi-tenant mode), leave them out of .env and provide them via headers or query params on each request instead.

Method 2: UV Tool (Recommended for Stdio / Claude Desktop)

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

Test the installation:

# Starts stdio mode (will block waiting for MCP client)
jira-mcp-server

Method 3: Virtual Environment (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]"

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

Claude Desktop (Stdio)

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 Code / HTTP Client (Single-Tenant)

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

Multi-Tenant HTTP (per-request credentials)

Configure the MCP client to include credentials in each request URL or headers. The server resolves credentials fresh per request so different clients can target different Jira instances.

{
  "mcpServers": {
    "jira": {
      "url": "http://localhost:8000/mcp?jira_base_url=https://your-domain.atlassian.net",
      "headers": {
        "Authorization": "Basic base64(user@example.com:api-token)"
      }
    }
  }
}

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

Access workspace data through 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/ # 104 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.0.0.tar.gz (48.8 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.0.0-py3-none-any.whl (44.3 kB view details)

Uploaded Python 3

File details

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

File metadata

File hashes

Hashes for atlassian_jira_mcp_server-3.0.0.tar.gz
Algorithm Hash digest
SHA256 88dd0201a23180b2a29f60e47274a361a892e16a555bb40c587529528f4e6118
MD5 479d93f1a2127084ea36eaf83c76bd60
BLAKE2b-256 ce3697d8c68806342b604320cd866b11dafbc01c67b1e614da0e11ae83e692d1

See more details on using hashes here.

Provenance

The following attestation bundles were made for atlassian_jira_mcp_server-3.0.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.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for atlassian_jira_mcp_server-3.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3afe818193b65d18d7dd7266d4854de5c4a099f04d5041542113e67d37483a6e
MD5 77a3f1d0227a17560e2bfcd3146afa96
BLAKE2b-256 b538bc45b6b8a2a97fdaa8367a1568d0b591f18a253b46d6c53a1ff236c9c6f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for atlassian_jira_mcp_server-3.0.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