Jira MCP Server
by janbaer
README.md
# Jira MCP Server
An MCP (Model Context Protocol) server for creating and fetching Jira issues via the REST API. Built with [Bun](https://bun.sh).
## Features
- Fetch and update existing Jira issues
- Create Jira issues with a single tool call
- Support for all standard issue fields (summary, description, type, priority, labels)
- Flexible description input:
- Plain text → automatic conversion to basic ADF paragraphs
- Pre-formatted ADF objects → full control over formatting (panels, headings, lists, etc.)
- Runtime ADF validation with Zod schemas
- Standalone executable (no runtime dependencies)
- `--help` flag for quick setup reference
## Prerequisites
- [Bun](https://bun.sh) v1.0+ — install with `curl -fsSL https://bun.sh/install | bash`
- Or use [devbox](https://www.jetify.com/devbox) for an isolated environment without a permanent Bun installation (see below)
- A Jira Cloud instance
- Jira API token (see [Atlassian API tokens](https://id.atlassian.com/manage-profile/security/api-tokens))
## Installation
```bash
git clone <your-repo-url>
cd jira-mcp-server
bun install
```
### Using devbox (no permanent Bun installation)
If you prefer not to install Bun globally, [devbox](https://www.jetify.com/devbox) provides an isolated shell with Bun available:
```bash
# Install devbox (one-time)
curl -fsSL https://get.jetify.com/devbox | bash
# Enter the devbox shell (installs Bun automatically)
devbox shell
# Now use bun as normal
bun install
bun run build
```
Run `exit` to leave the devbox shell. Bun is not added to your system outside of it.
## Configuration
The server requires these environment variables:
| Variable | Description | Example |
| ---------------- | ------------------------------------------ | ----------------------------------- |
| `JIRA_URL` | Your Jira instance URL | `https://your-domain.atlassian.net` |
| `JIRA_EMAIL` | Your Jira account email | `user@example.com` |
| `JIRA_API_TOKEN` | Your Jira API token | `your-api-token` |
| `JIRA_PROJECT` | Default project key for issue creation | `PROJ` |
## Usage
```bash
# Build standalone executable
bun run build
# Build and deploy to ~/bin
bun run deploy
# Build, deploy, and sign (required on macOS — see note below)
bun run deploy-and-sign
# Run from source (development)
bun run dev
# Inspect with MCP Inspector
bun run mcp-inspect
# Display setup instructions
./dist/jira-mcp-server --help
```
### macOS: use `deploy-and-sign`
On macOS, use `bun run deploy-and-sign` instead of `bun run deploy`. When `bun build --compile` produces a binary, it embeds a code signature. Copying the file with `cp` invalidates that signature because macOS recalculates file hashes on copy. Gatekeeper then kills the binary with `SIGKILL` when you try to run it from the new location.
`deploy-and-sign` adds an ad-hoc re-signing step (`codesign -s -`) after the copy, which satisfies macOS without requiring an Apple Developer certificate.
## Integration with MCP Clients
### Claude Desktop
Add to your config file:
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"jira": {
"command": "/absolute/path/to/jira-mcp-server/dist/jira-mcp-server",
"env": {
"JIRA_URL": "https://your-domain.atlassian.net",
"JIRA_EMAIL": "user@example.com",
"JIRA_API_TOKEN": "your-token",
"JIRA_PROJECT": "PROJ"
}
}
}
}
```
For development (running from source), replace `command` with `"bun"` and add `"args": ["run", "/absolute/path/to/jira-mcp-server/src/index.ts"]`.
### Cursor
1. Open Cursor Settings → Features → MCP
2. Add a new MCP server:
- **Transport type:** stdio
- **Command:** `/absolute/path/to/jira-mcp-server/dist/jira-mcp-server`
3. Set the environment variables in Cursor's settings
### Claude Code
```bash
claude mcp add jira /absolute/path/to/jira-mcp-server/dist/jira-mcp-server
```
## Tool Reference
### `jira-search-issues`
Searches for Jira issues using a JQL query. Returns a list of matching issues with key fields.
| Parameter | Type | Required | Description |
| ------------ | -------- | -------- | --------------------------------------------------- |
| `jql` | `string` | Yes | JQL query string |
| `maxResults` | `number` | No | Maximum results to return (1–100, default 20) |
**JQL examples:**
```
project = MYPROJ AND status = "In Progress"
assignee = currentUser() AND sprint in openSprints()
project = MYPROJ AND labels = "backend" ORDER BY created DESC
```
**Example response:**
```json
{
"success": true,
"total": 42,
"returned": 20,
"issues": [
{
"key": "PROJ-123",
"summary": "Fix login button on mobile",
"status": "In Progress",
"priority": "High",
"issueType": "Bug",
"assignee": "Jane Doe",
"reporter": "John Smith",
"labels": ["mobile", "frontend"],
"created": "2026-02-19T10:00:00.000+0000",
"updated": "2026-02-19T12:30:00.000+0000",
"url": "https://your-domain.atlassian.net/browse/PROJ-123"
}
]
}
```
### `jira-get-issue`
Fetches an existing Jira issue by its key.
| Parameter | Type | Required | Description |
| ---------- | -------- | -------- | ------------------------------- |
| `issueKey` | `string` | Yes | The issue key (e.g. "PROJ-123") |
**Example response:**
```json
{
"success": true,
"issue": {
"key": "PROJ-123",
"summary": "Fix login button on mobile",
"status": "In Progress",
"priority": "High",
"issueType": "Bug",
"assignee": "Jane Doe",
"reporter": "John Smith",
"labels": ["mobile", "frontend"],
"description": { "type": "doc", "version": 1, "content": [] },
"created": "2026-02-19T10:00:00.000+0000",
"updated": "2026-02-19T12:30:00.000+0000",
"url": "https://your-domain.atlassian.net/browse/PROJ-123"
}
}
```
### `jira-update-issue`
Updates an existing Jira issue's summary and/or description. At least one field must be provided.
| Parameter | Type | Required | Description |
| ------------- | ----------------- | -------- | ---------------------------------- |
| `issueKey` | `string` | Yes | The issue key (e.g. "PROJ-123") |
| `summary` | `string` | No | New summary/title for the issue |
| `description` | `string` or `ADF` | No | Plain text or pre-formatted ADF |
**Example response:**
```json
{
"success": true,
"issue": {
"key": "PROJ-123",
"url": "https://your-domain.atlassian.net/browse/PROJ-123"
},
"message": "Successfully updated issue PROJ-123"
}
```
### `jira-create-issue`
Creates a new issue in Jira.
| Parameter | Type | Required | Description |
| ------------- | ----------------- | -------- | ------------------------------------------------------------- |
| `summary` | `string` | Yes | Summary/title of the issue |
| `description` | `string` or `ADF` | No | Plain text or pre-formatted ADF object |
| `issueType` | `string` | No | Issue type (default: "Task") |
| `priority` | `string` | No | Priority level ("Highest", "High", "Medium", "Low", "Lowest") |
| `labels` | `string[]` | No | Labels to add to the issue |
| `projectKey` | `string` | No | Override the default project key |
| `parentKey` | `string` | No | Parent issue key (e.g. "PROJ-10") — creates as a child issue |
**Description formatting:**
Plain text is converted to ADF paragraphs automatically:
```
"description": "Main description.\n\nAnother paragraph."
```
For full formatting control, pass a pre-formatted ADF object:
```json
"description": {
"type": "doc",
"version": 1,
"content": [
{ "type": "paragraph", "content": [{ "type": "text", "text": "Main description" }] },
{ "type": "heading", "attrs": { "level": 3 }, "content": [{ "type": "text", "text": "TODO" }] },
{ "type": "panel", "attrs": { "panelType": "error" }, "content": [
{ "type": "paragraph", "content": [{ "type": "text", "text": "First task" }] }
]}
]
}
```
Panel types: `error` (red), `info` (blue), `warning` (yellow), `success` (green), `note` (purple).
**Example response:**
```json
{
"success": true,
"issue": {
"id": "10001",
"key": "PROJ-123",
"url": "https://your-domain.atlassian.net/browse/PROJ-123"
},
"message": "Successfully created issue PROJ-123"
}
```
## Example Commands
### `jira-ticket`
An interactive command for drafting and creating Jira tickets with proper ADF formatting. Supports Stories, Bugs, and Maintenance tasks with a smart question flow and automatic ADF construction.
**Location:** `command/jira-ticket.md`
```bash
/jira-ticket [optional summary]
```
## Project Structure
```
jira-mcp-server/
├── src/
│ ├── index.ts # Entry point, orchestrates startup
│ ├── config.ts # Environment variable validation
│ ├── server.ts # MCP server setup and tool registration
│ ├── jira-client.ts # Jira REST API client
│ ├── adf-schema.ts # Zod schema for ADF validation
│ ├── adf-schema.test.ts # ADF validation tests
│ ├── types.ts # TypeScript type definitions
│ ├── help.ts # Help text display
│ └── version.ts # Version from package.json
├── dist/
│ └── jira-mcp-server # Standalone executable (after build)
├── command/
│ └── jira-ticket.md # Interactive ticket creation command
├── biome.json # Linter/formatter configuration
└── package.json
```
## Available Scripts
| Script | Description |
| ------------- | ----------------------------------------------------- |
| `build` | Build standalone executable with Bun runtime included |
| `deploy` | Build and copy executable to ~/bin directory |
| `deploy-and-sign` | Build, copy, and ad-hoc sign (required on macOS) |
| `dev` | Run source directly with Bun (for development) |
| `test` | Run tests with Bun |
| `lint` | Check code with Biome (lint + format) |
| `lint:fix` | Auto-fix lint and format issues |
| `mcp-inspect` | Build and launch MCP Inspector for manual testing |
## Future Improvements
- [ ] Add more Jira operations (delete, search, comment, transitions)
- [ ] Support for custom fields
- [ ] Support for attachments
- [ ] Caching of project/issue type metadata
- [ ] Support for Jira Server/Data Center (different auth method)
- [ ] Add assignee field support
- [ ] Add component support
## License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues