if args.label_filter:
tasks = [t for t in tasks if args.label_filter in t.get("labels", [])]
Batch Creation with Rate Limiting
Add rate limiting for large batches:
import time
for task in tasks:
result = create_jira_task(auth, epic_key, task)
time.sleep(0.5) # 500ms delay between tasks
Integration with Claude Code
When breaking down epics for Claude Code execution:
Use this skill to create the task structure
Add autonomy levels to descriptions (HIGH/MEDIUM/LOW)
Include Claude Code prompts in each task description
Link related tasks using Jira issue links
Track progress as Claude Code completes tasks
Security Notes
⚠️ Never commit API tokens to version control
⚠️ Use environment variables for sensitive data
⚠️ Rotate tokens regularly (every 90 days recommended)
⚠️ Restrict token permissions to minimum required scope
Success Criteria
You've successfully used this skill when:
✅ All tasks are created in Jira under correct epics
✅ Task descriptions include autonomy levels and prompts
✅ Labels and priorities are set correctly
✅ Time estimates are realistic and useful for planning
✅ No duplicate tasks were created
✅ Error handling caught and reported any failures
Advanced Jira Operations
Beyond bulk task creation, this skill covers all Jira REST API operations. Here are code examples for common workflows:
1. Search Issues with JQL
defsearch_issues(auth, jql, max_results=50):
"""Search Jira issues using JQL (Jira Query Language)."""
params = {
"jql": jql,
"maxResults": max_results,
"fields": "summary,status,assignee,priority,created"
}
response = requests.get(
f"{JIRA_API_URL}/search",
auth=auth,
params=params
)
if response.status_code == 200:
data = response.json()
print(f"[OK] Found {data['total']} issues")
for issue in data['issues']:
print(f" {issue['key']}: {issue['fields']['summary']}")
return data['issues']
else:
print(f"[ERROR] Search failed: {response.status_code}")
return []
# Example usage:# search_issues(auth, "project = DP01 AND status = 'In Progress'")# search_issues(auth, "assignee = currentUser() AND status != Done")# search_issues(auth, "created >= -7d AND labels = Track-3-Platform")
2. Get Issue Details
defget_issue(auth, issue_key):
"""Get complete details of a specific issue."""
response = requests.get(
f"{JIRA_API_URL}/issue/{issue_key}",
auth=auth
)
if response.status_code == 200:
issue = response.json()
print(f"[OK] {issue_key}: {issue['fields']['summary']}")
print(f" Status: {issue['fields']['status']['name']}")
print(f" Assignee: {issue['fields']['assignee']['displayName'] if issue['fields']['assignee'] else'Unassigned'}")
return issue
else:
print(f"[ERROR] Failed to get issue: {response.status_code}")
returnNone
3. Update Issue Fields
defupdate_issue(auth, issue_key, fields):
"""Update fields on an existing issue."""
payload = {"fields": fields}
response = requests.put(
f"{JIRA_API_URL}/issue/{issue_key}",
auth=auth,
headers={"Content-Type": "application/json"},
json=payload
)
if response.status_code == 204:
print(f"[OK] Updated {issue_key}")
returnTrueelse:
print(f"[ERROR] Failed to update: {response.status_code}")
print(f" Response: {response.text}")
returnFalse# Example usage:# update_issue(auth, "DP01-74", {# "summary": "New task title",# "description": {...}, # ADF format# "priority": {"name": "High"},# "labels": ["urgent", "backend"]# })
4. Transition Issue (Change Status)
defget_transitions(auth, issue_key):
"""Get available transitions for an issue."""
response = requests.get(
f"{JIRA_API_URL}/issue/{issue_key}/transitions",
auth=auth
)
if response.status_code == 200:
transitions = response.json()['transitions']
print(f"[OK] Available transitions for {issue_key}:")
for t in transitions:
print(f" {t['id']}: {t['name']}")
return transitions
else:
print(f"[ERROR] Failed to get transitions: {response.status_code}")
return []
deftransition_issue(auth, issue_key, transition_id):
"""Transition an issue to a new status."""
payload = {
"transition": {"id": transition_id}
}
response = requests.post(
f"{JIRA_API_URL}/issue/{issue_key}/transitions",
auth=auth,
headers={"Content-Type": "application/json"},
json=payload
)
if response.status_code == 204:
print(f"[OK] Transitioned {issue_key}")
returnTrueelse:
print(f"[ERROR] Failed to transition: {response.status_code}")
returnFalse# Example workflow:# transitions = get_transitions(auth, "DP01-74")# # Find "In Progress" transition ID from the list# transition_issue(auth, "DP01-74", "21") # ID for "In Progress"
5. Add Comments
defadd_comment(auth, issue_key, comment_text):
"""Add a comment to an issue."""
payload = {
"body": {
"type": "doc",
"version": 1,
"content": [{
"type": "paragraph",
"content": [{
"type": "text",
"text": comment_text
}]
}]
}
}
response = requests.post(
f"{JIRA_API_URL}/issue/{issue_key}/comment",
auth=auth,
headers={"Content-Type": "application/json"},
json=payload
)
if response.status_code == 201:
print(f"[OK] Added comment to {issue_key}")
returnTrueelse:
print(f"[ERROR] Failed to add comment: {response.status_code}")
returnFalse# Example usage:# add_comment(auth, "DP01-74", "Implementation started. Setting up AWS Organizations.")
6. Link Issues
deflink_issues(auth, inward_issue, outward_issue, link_type="Relates"):
"""Create a link between two issues."""
payload = {
"type": {"name": link_type}, # "Relates", "Blocks", "Duplicate", etc."inwardIssue": {"key": inward_issue},
"outwardIssue": {"key": outward_issue}
}
response = requests.post(
f"{JIRA_API_URL}/issueLink",
auth=auth,
headers={"Content-Type": "application/json"},
json=payload
)
if response.status_code == 201:
print(f"[OK] Linked {inward_issue}{link_type}{outward_issue}")
returnTrueelse:
print(f"[ERROR] Failed to link issues: {response.status_code}")
returnFalse# Example usage:# link_issues(auth, "DP01-74", "DP01-75", "Blocks") # DP01-74 blocks DP01-75# link_issues(auth, "DP01-85", "DP01-86", "Relates") # DP01-85 relates to DP01-86
7. Link Issue to Epic
deflink_to_epic(auth, issue_key, epic_key):
"""Link an issue to an epic (parent)."""
payload = {
"fields": {
"parent": {"key": epic_key}
}
}
response = requests.put(
f"{JIRA_API_URL}/issue/{issue_key}",
auth=auth,
headers={"Content-Type": "application/json"},
json=payload
)
if response.status_code == 204:
print(f"[OK] Linked {issue_key} to epic {epic_key}")
returnTrueelse:
print(f"[ERROR] Failed to link to epic: {response.status_code}")
returnFalse
8. Add Work Log (Time Tracking)
defadd_worklog(auth, issue_key, time_spent, comment=""):
"""Log time spent on an issue."""
payload = {
"timeSpent": time_spent, # e.g., "3h 30m", "1d", "45m""comment": {
"type": "doc",
"version": 1,
"content": [{
"type": "paragraph",
"content": [{
"type": "text",
"text": comment
}]
}]
} if comment elseNone
}
response = requests.post(
f"{JIRA_API_URL}/issue/{issue_key}/worklog",
auth=auth,
headers={"Content-Type": "application/json"},
json=payload
)
if response.status_code == 201:
print(f"[OK] Logged {time_spent} on {issue_key}")
returnTrueelse:
print(f"[ERROR] Failed to log work: {response.status_code}")
returnFalse# Example usage:# add_worklog(auth, "DP01-74", "2h 30m", "Configured AWS Organizations")
9. Get All Projects
defget_all_projects(auth):
"""Get list of all accessible Jira projects."""
response = requests.get(
f"{JIRA_API_URL}/project",
auth=auth
)
if response.status_code == 200:
projects = response.json()
print(f"[OK] Found {len(projects)} projects:")
for project in projects:
print(f" {project['key']}: {project['name']}")
return projects
else:
print(f"[ERROR] Failed to get projects: {response.status_code}")
return []
10. Batch Create Issues
defbatch_create_issues(auth, issues):
"""Create multiple issues in a single API call."""
payload = {
"issueUpdates": [
{"fields": issue} for issue in issues
]
}
response = requests.post(
f"{JIRA_API_URL}/issue/bulk",
auth=auth,
headers={"Content-Type": "application/json"},
json=payload
)
if response.status_code == 201:
results = response.json()
print(f"[OK] Created {len(results['issues'])} issues")
for issue in results['issues']:
print(f" {issue['key']}")
return results
else:
print(f"[ERROR] Batch creation failed: {response.status_code}")
returnNone# Example usage:# batch_create_issues(auth, [# {# "project": {"key": "DP01"},# "summary": "Task 1",# "issuetype": {"name": "Task"},# "parent": {"key": "DP01-21"}# },# {# "project": {"key": "DP01"},# "summary": "Task 2",# "issuetype": {"name": "Task"},# "parent": {"key": "DP01-21"}# }# ])
11. Create Sprint
defcreate_sprint(auth, board_id, sprint_name, start_date=None, end_date=None):
"""Create a new sprint for an agile board."""
payload = {
"name": sprint_name,
"originBoardId": board_id,
}
if start_date:
payload["startDate"] = start_date # ISO 8601 format: "2025-01-20T10:00:00.000Z"if end_date:
payload["endDate"] = end_date
response = requests.post(
f"{JIRA_BASE_URL}/rest/agile/1.0/sprint",
auth=auth,
headers={"Content-Type": "application/json"},
json=payload
)
if response.status_code == 201:
sprint = response.json()
print(f"[OK] Created sprint: {sprint['name']} (ID: {sprint['id']})")
return sprint
else:
print(f"[ERROR] Failed to create sprint: {response.status_code}")
returnNone
12. Get Board Issues
defget_board_issues(auth, board_id, jql_filter=""):
"""Get issues from a specific agile board."""
params = {"maxResults": 100}
if jql_filter:
params["jql"] = jql_filter
response = requests.get(
f"{JIRA_BASE_URL}/rest/agile/1.0/board/{board_id}/issue",
auth=auth,
params=params
)
if response.status_code == 200:
data = response.json()
print(f"[OK] Found {data['total']} issues on board")
return data['issues']
else:
print(f"[ERROR] Failed to get board issues: {response.status_code}")
return []
Common JQL Query Examples
Jira Query Language (JQL) is powerful for filtering issues. Here are common queries:
# Issues in specific project"project = DP01"# Issues assigned to you"assignee = currentUser()"# Issues in progress"project = DP01 AND status = 'In Progress'"# Recent issues (last 7 days)"created >= -7d"# High priority bugs"project = DP01 AND issuetype = Bug AND priority in (High, Highest)"# Issues with specific label"labels = Track-3-Platform"# Overdue issues"duedate < now() AND status != Done"# Issues updated recently"updated >= -1d"# Complex query with multiple conditions"project = DP01 AND assignee = currentUser() AND status in ('To Do', 'In Progress') AND labels = urgent ORDER BY priority DESC"# Epic and its children"'Epic Link' = DP01-21"# Unassigned issues in current sprint"sprint in openSprints() AND assignee is EMPTY"# Issues blocking others"issueFunction in linkedIssuesOf('project = DP01', 'blocks')"
Integration with Claude Code Workflows
Automated Task Status Updates
When Claude Code completes a task, automatically update Jira:
defcomplete_claude_task(auth, issue_key, time_spent, implementation_notes):
"""Mark a Claude Code task as complete in Jira."""# 1. Add work log
add_worklog(auth, issue_key, time_spent, "Implementation completed by Claude Code")
# 2. Add comment with results
add_comment(auth, issue_key, f"Implementation complete.\n\n{implementation_notes}")
# 3. Get available transitions
transitions = get_transitions(auth, issue_key)
done_transition = next((t for t in transitions if t['name'].lower() == 'done'), None)
# 4. Transition to Doneif done_transition:
transition_issue(auth, issue_key, done_transition['id'])
print(f"[OK] Task {issue_key} marked as complete")
else:
print(f"[WARN] Could not find 'Done' transition for {issue_key}")
Sprint Planning Automation
Automate sprint creation and issue assignment:
defsetup_sprint(auth, board_id, sprint_name, epic_key, num_days=14):
"""Create sprint and add epic issues to it."""from datetime import datetime, timedelta
# 1. Create sprint
start_date = datetime.now().isoformat() + "Z"
end_date = (datetime.now() + timedelta(days=num_days)).isoformat() + "Z"
sprint = create_sprint(auth, board_id, sprint_name, start_date, end_date)
ifnot sprint:
return# 2. Get issues from epic
jql = f"'Epic Link' = {epic_key} AND status = 'To Do'"
issues = search_issues(auth, jql)
# 3. Move issues to sprintfor issue in issues:
move_to_sprint(auth, issue['key'], sprint['id'])
print(f"[OK] Sprint '{sprint_name}' created with {len(issues)} issues")
Related Skills
brainstorming - Use before this skill to refine epic breakdown
test-driven-development - Use after task creation for implementation
finishing-a-development-branch - Use when completing tasks
Version History
v2.0 (2025-01-14): Expanded to full Jira automation toolkit
Added all MCP-equivalent operations (search, update, transition, comments, links)