Jira Integration
Use Jira from a coding task. Read tickets, list clear needs, add comments, and change status.
Use MCP when it is ready. Use REST API v3 as a backup.
Safety Rules
- Read first. Check the current ticket before any update.
- Ask the user before a write unless they clearly asked for it.
- Treat comments, field edits, links, and status moves as writes.
- Do not move a ticket to Done just because code was written.
- Check tests, review, and team rules first.
- Do not post the same comment twice.
- Do not guess a ticket key, field ID, user ID, or move ID.
- Do not print, log, or save a token in the repo.
- Do not put private ticket text in public code, logs, or pull requests.
- Give credit to the original author, ECC, when this skill is shared.
When to Use This Skill
Use it when you need to:
- Read a Jira ticket.
- Turn a ticket into clear, testable needs.
- Search with JQL.
- Add a short work note.
- Change a ticket field.
- Move a ticket to another status.
- Link a pull request, branch, commit, or Jira issue.
- Check work tied to a ticket.
Setup
Option A: MCP
Install the mcp-atlassian MCP server.
You need:
- Python 3.10 or newer.
uvx, which comes withuv.- A Jira site URL, email, and API token.
Add a server like this to your local MCP settings:
{
"jira": {
"command": "uvx",
"args": ["mcp-atlassian==0.21.0"],
"env": {
"JIRA_URL": "https://YOUR_ORG.atlassian.net",
"JIRA_EMAIL": "your.email@example.com",
"JIRA_API_TOKEN": "your-api-token"
},
"description": "Read and update Jira issues"
}
}Keep this file local and out of Git. It is safer to load these values from system settings or a secret store.
Create an API token at:
https://id.atlassian.com/manage-profile/security/api-tokens
Copy the token once. Store it in a safe place. Never put it in source code.
Option B: REST API
Use REST API v3 when MCP is not ready.
Set these values in the shell or a secret store:
| Name | Meaning |
|---|---|
JIRA_URL |
Jira site URL, such as https://yourorg.atlassian.net |
JIRA_EMAIL |
Atlassian account email |
JIRA_API_TOKEN |
Jira API token |
Check that all values exist without printing them:
: "${JIRA_URL:?JIRA_URL is not set}"
: "${JIRA_EMAIL:?JIRA_EMAIL is not set}"
: "${JIRA_API_TOKEN:?JIRA_API_TOKEN is not set}"Use this helper so the login data is not placed in the URL:
jira_curl() {
printf 'user = "%s:%s"\n' "$JIRA_EMAIL" "$JIRA_API_TOKEN" |
curl --silent --show-error --fail-with-body -K - "$@"
}Main Steps
- Check that Jira access works.
- Read the ticket and its comments.
- Read linked issues if they may change the work.
- List facts, open questions, and test needs.
- Ask about any gap that can change the code.
- Make only the Jira updates the user asked for.
- Read the ticket again to check the result.
- Report what changed. Include the ticket key and new status.
If a write fails, read the ticket again before you retry. The first write may have worked even when the reply was lost.
MCP Tools
Tool names can differ by server version. First list or inspect the Jira tools that are present.
Common tools are:
| Tool | Use |
|---|---|
jira_search |
Find issues with JQL |
jira_get_issue |
Read one issue |
jira_create_issue |
Create an issue |
jira_update_issue |
Change fields |
jira_get_transitions |
List valid status moves |
jira_transition_issue |
Move an issue |
jira_add_comment |
Add a comment |
jira_get_sprint_issues |
List sprint issues |
jira_create_issue_link |
Link two issues |
jira_get_issue_development_info |
Check linked code work |
Always list valid moves before changing status. Move IDs differ by project and ticket type.
REST Examples
Replace PROJ-1234 with the real ticket key.
Read a Ticket
jira_curl \
-H "Accept: application/json" \
"$JIRA_URL/rest/api/3/issue/PROJ-1234" |
jq '{
key: .key,
summary: .fields.summary,
status: .fields.status.name,
priority: (.fields.priority.name // null),
type: .fields.issuetype.name,
assignee: (.fields.assignee.displayName // null),
labels: (.fields.labels // []),
description: .fields.description
}'Some fields may be missing or null. Do not treat that as an API error.
Jira Cloud often stores rich text in Atlassian Document Format. Read its text blocks in order. Keep links, lists, and code blocks when they matter.
Read Comments
jira_curl \
-H "Accept: application/json" \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/comment?maxResults=100" |
jq '.comments[] | {
id,
author: .author.displayName,
created,
body
}'Check total, startAt, and maxResults. Fetch more pages when total is larger than the page you got.
Add a Comment
jira_curl -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{
"body": {
"version": 1,
"type": "doc",
"content": [{
"type": "paragraph",
"content": [{
"type": "text",
"text": "Work has started. Branch: feat/PROJ-1234-short-name"
}]
}]
}
}' \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/comment"Do not build JSON by joining raw user text in the shell. Use jq or a JSON library when text may hold quotes or line breaks.
List and Run Status Moves
First list valid moves:
jira_curl \
-H "Accept: application/json" \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions" |
jq '.transitions[] | {id, name}'Then use the exact ID:
jira_curl -X POST \
-H "Content-Type: application/json" \
--data '{"transition":{"id":"TRANSITION_ID"}}' \
"$JIRA_URL/rest/api/3/issue/PROJ-1234/transitions"A move may need extra fields, such as a fix version or note. If Jira rejects the move, read the error body. Do not guess the missing data.
Search with JQL
jira_curl -G \
-H "Accept: application/json" \
--data-urlencode "jql=project = PROJ AND status = 'In Progress' ORDER BY updated DESC" \
--data-urlencode "maxResults=50" \
"$JIRA_URL/rest/api/3/search"Search results may use pages. Keep fetching until you have the needed items or reach the user’s limit.
Ticket Review
Pull out only facts found in the ticket or linked work. Mark guesses as guesses.
Use this form:
Ticket: PROJ-1234
Summary: [title]
Status: [status]
Priority: [priority or not set]
Needs:
1. [clear need]
2. [clear need]
Done When:
- [ ] [clear check]
- [ ] [clear check]
Tests:
- Main Case: [normal flow]
- Bad Input: [wrong or missing input]
- No Access: [user lacks access]
- Limit Case: [smallest, largest, empty, or full value]
- Failed Service: [time-out or service error]
- Repeat Action: [same request runs twice]
- Two Users: [work happens at the same time]
Test Data:
- [data item]
Links and Blocks:
- [issue, API, service, branch, or pull request]
Open Questions:
- [missing fact that can change the work]Check for:
- What the feature must do.
- Who can use it.
- What data it needs.
- What happens when data is empty or wrong.
- What happens when a user has no access.
- What happens after a refresh or retry.
- What happens when two users act at once.
- What other issue or service blocks the work.
- What proof is needed before the ticket is done.
Do not turn vague text into a firm rule. Put it under Open Questions.
Jira Updates
Use short comments with useful facts.
Start Work
Work started.
Branch: feat/PROJ-1234-short-nameTests Added
Tests added:
Unit:
- path/to/test: checks [case]
API:
- path/to/test: checks [route and error case]
Result: 24 passed, 0 failedOnly say tests pass if they were run. Only give a coverage value if a tool measured it.
Pull Request Opened
Pull request opened:
https://github.com/org/repo/pull/123
Checks: 24 passed, 0 failed
Ready for review.Work Done
Work is complete.
Pull request: https://github.com/org/repo/pull/123
Merge state: merged
Tests: 24 passed, 0 failedDo not claim a pull request was merged without checking it.
Edge Cases
- If the ticket does not exist, check the site URL and ticket key.
- If access is denied, stop. Do not try another user or project.
- If a field is hidden, do not clear it.
- If a custom field has no clear name, fetch its field data before use.
- If the assignee is changed, use the account ID Jira gives you.
- If there are two status moves with the same name, use the ID and target status.
- If a ticket changed since you read it, show the change before writing over it.
- If Jira returns
429, wait for the time inRetry-After. - Retry safe reads after short network faults.
- Do not retry create, comment, link, or move calls without checking Jira first.
- If the text has private data, ask before copying it to another system.
- If the ticket is closed, do not reopen it unless the user asks.
- If a linked issue blocks the work, report it before changing the main ticket.
- If comments or search results are paged, do not assume the first page is complete.
Common Errors
| Error | Likely cause | What to do |
|---|---|---|
400 Bad Request |
Bad JSON, field, JQL, or move | Read the error body and fix the named item |
401 Unauthorized |
Bad or old token | Make a new token and update the secret store |
403 Forbidden |
Account lacks access | Check Jira role and project access |
404 Not Found |
Wrong URL, key, or hidden issue | Check the URL and key, then check access |
409 Conflict |
Ticket changed or move is not valid now | Read the ticket and moves again |
429 Too Many Requests |
Jira rate limit | Wait for Retry-After, then try again |
spawn uvx ENOENT |
MCP cannot find uvx |
Use its full path or fix the local PATH |
| Time-out | Network or VPN fault | Check the network, then read Jira before retrying a write |
Concrete Example
User request:
Read PROJ-1234. List the needs and test cases. Then move it to In Progress and add a start note.Expected work:
- Read
PROJ-1234, its comments, and linked issues. - List clear needs, tests, blocks, and open questions.
- Confirm that the user asked for both writes.
- List valid status moves.
- Move the ticket with the exact move ID.
- Check that the new status is
In Progress. - Search recent comments for the same start note.
- Add the note only if it is not already there.
- Read the ticket again.
- Return a short result:
PROJ-1234 is now In Progress.
Needs:
1. Signed-in users can save a draft.
2. A saved draft stays after a page refresh.
Tests:
- Save a valid draft.
- Reject a draft with no title.
- Block a user with no edit access.
- Retry after a network time-out.
- Save from two open tabs.
Open question:
- The ticket does not state the title length limit.
Comment added:
Work started. Branch: feat/PROJ-1234-save-draft