Sub-issues let you break down work into hierarchical tasks. Each parent issue can have up to 100 sub-issues, nested up to 8 levels deep. Sub-issues can span repositories within the same owner.
The simplest way to create a sub-issue is two steps: create the issue, then link it.
# Step 1: Create the issue and capture its numeric ID
ISSUE_ID=$(gh api repos/{owner}/{repo}/issues \
-X POST \
-f title="Sub-task title" \
-f body="Description" \
--jq '.id')
# Step 2: Link it as a sub-issue of the parent
# IMPORTANT: sub_issue_id must be an integer. Use --input (not -f) to send JSON.
echo "{\"sub_issue_id\": $ISSUE_ID}" | gh api repos/{owner}/{repo}/issues/{parent_number}/sub_issues -X POST --input -Why --input instead of -f? The gh api -f flag sends all values as strings, but the API requires sub_issue_id as an integer. Using -f sub_issue_id=12345 will return a 422 error.
Alternatively, use GraphQL createIssue with parentIssueId to do it in one step (see GraphQL section below).
List sub-issues:
Call mcp__github__issue_read with method: "get_sub_issues", owner, repo, and issue_number.
Create an issue as a sub-issue: There is no MCP tool for creating sub-issues directly. Use the workflow above or GraphQL.
List sub-issues:
gh api repos/{owner}/{repo}/issues/{issue_number}/sub_issuesGet parent issue:
gh api repos/{owner}/{repo}/issues/{issue_number}/parentAdd an existing issue as a sub-issue:
# sub_issue_id is the numeric issue ID (not the issue number)
# Get it from the .id field when creating or fetching an issue
echo '{"sub_issue_id": 12345}' | gh api repos/{owner}/{repo}/issues/{parent_number}/sub_issues -X POST --input -To move a sub-issue that already has a parent, add "replace_parent": true to the JSON body.
Remove a sub-issue:
echo '{"sub_issue_id": 12345}' | gh api repos/{owner}/{repo}/issues/{parent_number}/sub_issue -X DELETE --input -Reprioritize a sub-issue:
echo '{"sub_issue_id": 6, "after_id": 5}' | gh api repos/{owner}/{repo}/issues/{parent_number}/sub_issues/priority -X PATCH --input -Use after_id or before_id to position the sub-issue relative to another.
Read parent and sub-issues:
{
repository(owner: "OWNER", name: "REPO") {
issue(number: 123) {
parent { number title }
subIssues(first: 50) {
nodes { number title state }
}
subIssuesSummary { total completed percentCompleted }
}
}
}Add a sub-issue:
mutation {
addSubIssue(input: {
issueId: "PARENT_NODE_ID"
subIssueId: "CHILD_NODE_ID"
}) {
issue { id }
subIssue { id number title }
}
}You can also use subIssueUrl instead of subIssueId (pass the issue's HTML URL). Add replaceParent: true to move a sub-issue from another parent.
Create an issue directly as a sub-issue:
mutation {
createIssue(input: {
repositoryId: "REPO_NODE_ID"
title: "Implement login validation"
parentIssueId: "PARENT_NODE_ID"
}) {
issue { id number }
}
}Remove a sub-issue:
mutation {
removeSubIssue(input: {
issueId: "PARENT_NODE_ID"
subIssueId: "CHILD_NODE_ID"
}) {
issue { id }
}
}Reprioritize a sub-issue:
mutation {
reprioritizeSubIssue(input: {
issueId: "PARENT_NODE_ID"
subIssueId: "CHILD_NODE_ID"
afterId: "OTHER_CHILD_NODE_ID"
}) {
issue { id }
}
}Use afterId or beforeId to position relative to another sub-issue.