MCP server for the AI Engineering Jira project — rich ADF issue creation and working-day gantt scheduling.
Project description
jira-aie-mcp
An MCP server for the AIE team's Jira instance. Exposes the two things
mcp-atlassian can't do natively:
- Rich Atlassian Document Format (ADF) issue creation/update — info / warning / success / error / note panels, inline status lozenges, date chips, expand/collapse blocks, and task lists. Accepts a Markdown string, a compact block-list DSL, or a pre-built ADF doc.
- Working-day gantt scheduler — reads Blocks dependencies and
Original Estimates from a JQL-scoped set of issues, computes start/end
dates on the Norwegian public-holiday calendar, and optionally writes
the end dates back to each issue's
duedatefield.
Plus a semantic link shortcut (jira_link_issues) that picks the
right inward/outward direction for Blocks, Relates, Duplicates, Clones,
and Causes without forcing the caller to memorise Jira's link model.
mcp-atlassian still handles everything else — search, transitions,
comments, watchers, worklogs, etc. This server is a gap-filler, not
a replacement.
Install
Run it on demand with uvx — no install step needed; uvx fetches the
package from PyPI into an ephemeral environment on first use and caches
it for subsequent runs:
uvx jira-aie-mcp
Or install it as a persistent tool:
uv tool install jira-aie-mcp
jira-aie-mcp
Either form starts the server on stdio, which is what MCP clients expect.
Configure (MCP client)
All configuration lives in the MCP client's server definition — no separate config file. Minimum required env vars:
| Variable | Required | Default |
|---|---|---|
JIRA_BASE_URL |
yes | — |
JIRA_EMAIL |
yes | — |
JIRA_API_TOKEN |
yes | — |
JIRA_DEFAULT_PROJECT_KEY |
no | — (fallback for jira_create_issue when the tool call omits project_key) |
JIRA_WORKSTREAM_FIELD_ID |
no | — (tenant-specific custom field ID, e.g. customfield_10742, that schedule_compute groups by in the Mermaid gantt) |
Generate an API token at https://id.atlassian.com/manage-profile/security/api-tokens.
Cowork / Claude Desktop config
{
"mcpServers": {
"jira-aie": {
"command": "uvx",
"args": ["jira-aie-mcp"],
"env": {
"JIRA_BASE_URL": "https://apply-team.atlassian.net",
"JIRA_EMAIL": "you@apply.no",
"JIRA_API_TOKEN": "atlassian-api-token-here"
}
}
}
}
To pin a specific version (recommended, so upstream releases don't silently change behaviour):
"args": ["jira-aie-mcp==0.1.2"]
Reload the client after editing the config.
⚠️ If you ran 0.1.0 against a real Jira instance with
write_duedate=True, the persisted duedates are inverted — upgrade to ≥0.1.1 and re-runschedule_compute(..., write_duedate=True)once to overwrite them. SeeCHANGELOG.mdfor the fix details.
Tools
| Tool | What it does |
|---|---|
jira_create_issue |
Create one issue. description accepts Markdown, block-DSL, or raw ADF. |
jira_update_issue |
Update summary/labels/fields/description on an existing issue. |
jira_get_issue_raw |
Fetch the raw REST payload (including ADF description). |
jira_link_issues |
Create an issue link. Semantic shortcuts for Blocks/Relates/Duplicates/Clones/Causes. |
jira_batch_ops |
Mixed array of `{op: "create" |
schedule_compute |
Forward-pass gantt from a JQL scope. Format: json / markdown / mermaid. Optional write_duedate. |
schedule_holidays |
Print the Norwegian red-day calendar for a given year. |
Block DSL (descriptions)
[
{"h": 2, "text": "What broke"},
{"p": "Reranker started returning 500s on ~30% of requests at 14:20 UTC."},
{"panel": "warning", "content": [
{"p": "Customer-facing; search relevance degraded but not down."}
]},
{"h": 2, "text": "Timeline"},
{"ul": [
"🔴 14:20 UTC — first 500s observed",
"🟡 14:26 UTC — paged on-call",
"🟢 14:41 UTC — rolled back cross-encoder to v1"
]},
{"h": 2, "text": "Follow-ups"},
{"tasks": [
{"text": "Postmortem", "done": false},
{"text": "Add 5xx alarm to SLO dashboard", "done": false}
]}
]
All block keys (full list):
h — heading (1-6) p — paragraph
ul — bullet list ol — numbered list
code — code block (+ `lang`) quote — blockquote
rule — horizontal rule panel — info|note|success|warning|error
status — inline status lozenge date — inline date chip
expand — collapsible block tasks — task list with done flags
table — {headers, rows} raw — pass-through ADF node
Inline specs (inside p / h text or a list item):
{"text": "bold", "marks": ["strong"]}
{"text": "link", "link": "https://..."}
{"mention": "account-id", "text": "@Alice"}
{"emoji": ":rocket:", "text": "🚀"}
{"status": "In progress", "color": "yellow"}
{"date": "2026-04-20"}
{"hard_break": true}
Scheduler
The forward pass follows Blocks dependencies, uses Jira's
timetracking.originalEstimate (1w 2d 3h 30m syntax) for durations, and
skips weekends and 12 Norwegian public holidays per year (Easter-derived).
schedule_compute(
jql="project = AIE AND \"Epic Link\" = AIE-5 AND statusCategory != Done",
start="2026-05-04",
format="markdown",
write_duedate=False
)
Returns a JSON plan (project_start, project_end, critical_path,
issues[], warnings[]) and, for markdown/mermaid formats, a
rendered string.
Rescheduling is just re-running with overrides:
pins = {"AIE-15": "2026-06-01"} # force start date
delays = {"AIE-7": "3d"} # shift start N working days later
Module layout
src/jira_aie_mcp/
__main__.py # entry point — starts FastMCP over stdio
server.py # MCP tool registrations
config.py # env-var loading → Config dataclass
client.py # httpx-based Jira REST v3 client + link shortcuts
adf.py # block DSL + Markdown → ADF
holidays.py # Easter + Norwegian red days + duration parser
scheduler.py # graph build + forward pass + critical path
formats.py # JSON / Markdown / Mermaid plan output
tests/
test_scheduler.py # Blocks-direction contract + diamond end-to-end
Development
Install with the dev extras and run the test suite:
uv pip install -e ".[dev]"
pytest
The test suite is intentionally small and focused on the pieces most
likely to regress (the Blocks-direction contract, the working-day forward
pass, and the critical path). The REST client and the FastMCP wiring are
exercised via the live smoke tests in the team's Jira — if you want those
to be unit-level too, mock httpx.Client.post / .put.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jira_aie_mcp-0.1.2.tar.gz.
File metadata
- Download URL: jira_aie_mcp-0.1.2.tar.gz
- Upload date:
- Size: 26.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3741b026e42c93491bb273460f46ebc38b3a1f147671dae225b4a51bed781ef5
|
|
| MD5 |
cd3c2a92f6c4884df508a7481e6a2fa5
|
|
| BLAKE2b-256 |
c9d775aa796e399e3a9ddfebbfba21cf1f1753b8be4b32d38708d4ea86b695fc
|
File details
Details for the file jira_aie_mcp-0.1.2-py3-none-any.whl.
File metadata
- Download URL: jira_aie_mcp-0.1.2-py3-none-any.whl
- Upload date:
- Size: 26.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1fd3585e67019119fb11d3f0ecf5bccfbd79848542e7b8d15e34fd54089948f9
|
|
| MD5 |
0451caf2f47dc2f9e38e996ed96b22e6
|
|
| BLAKE2b-256 |
fa6a1546b8f722bba30b5480d51d7f546c901f547cf9fdbe775cb075e73071a6
|