jira_list_sprints
Retrieve and filter current Jira sprints by board, project, or state to track agile development progress and manage workflow visibility.
Instructions
Lists current sprints in Jira with filtering options
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| jiraHost | No | The Jira host URL (e.g., 'your-domain.atlassian.net') | |
| No | Email address associated with the Jira account | ||
| apiToken | No | API token for Jira authentication | |
| boardId | No | Optional Jira board ID to filter sprints by a specific board | |
| projectKey | No | Optional project key to find sprints associated with the project | |
| state | No | Sprint state to filter by (active, future, closed, or all) | active |
Implementation Reference
- src/tools/listSprints.ts:66-285 (handler)Core handler function that validates input, fetches Jira boards and sprints using Agile API, retrieves issues per sprint, sorts and formats a comprehensive Markdown report with tables and links.export async function listSprints(args: any) { const validatedArgs = await JiraSprintRequestSchema.validate(args); const jiraHost = validatedArgs.jiraHost || process.env.JIRA_HOST; const email = validatedArgs.email || process.env.JIRA_EMAIL; const apiToken = validatedArgs.apiToken || process.env.JIRA_API_TOKEN; const boardId = validatedArgs.boardId; const projectKey = validatedArgs.projectKey; const state = validatedArgs.state || 'active'; if (!jiraHost || !email || !apiToken) { throw new Error('Missing required authentication credentials. Please provide jiraHost, email, and apiToken.'); } validateCredentials(jiraHost, email, apiToken); const authHeader = createAuthHeader(email, apiToken); try { let boardIds: string[] = []; if (boardId) { boardIds.push(boardId); } else if (projectKey) { const boardResponse = await axios.get(`https://${jiraHost}/rest/agile/1.0/board`, { params: { projectKeyOrId: projectKey }, headers: { 'Authorization': authHeader, 'Accept': 'application/json', }, }); if (boardResponse.data.values && boardResponse.data.values.length > 0) { boardIds = boardResponse.data.values.map((board: any) => board.id); } else { return { content: [{ type: "text", text: `# No Boards Found\n\nNo boards were found for project key: ${projectKey}` }], isError: false, }; } } else { const boardResponse = await axios.get(`https://${jiraHost}/rest/agile/1.0/board`, { headers: { 'Authorization': authHeader, 'Accept': 'application/json', }, }); if (boardResponse.data.values && boardResponse.data.values.length > 0) { boardIds = boardResponse.data.values.slice(0, 5).map((board: any) => board.id); } else { return { content: [{ type: "text", text: `# No Boards Found\n\nNo boards were found in your Jira instance.` }], isError: false, }; } } let allSprints: any[] = []; for (const bId of boardIds) { try { const sprintParams: any = {}; if (state !== 'all') { sprintParams.state = state; } const sprintResponse = await axios.get(`https://${jiraHost}/rest/agile/1.0/board/${bId}/sprint`, { params: sprintParams, headers: { 'Authorization': authHeader, 'Accept': 'application/json', }, }); if (sprintResponse.data.values && sprintResponse.data.values.length > 0) { const sprintsWithBoardInfo = sprintResponse.data.values.map((sprint: any) => ({ ...sprint, boardId: bId })); allSprints = [...allSprints, ...sprintsWithBoardInfo]; } } catch (error) { console.warn(`Could not fetch sprints for board ${bId}`); } } if (allSprints.length === 0) { let message = `# No Sprints Found\n\n`; if (state !== 'all') { message += `No ${state} sprints were found`; } else { message += `No sprints were found`; } if (boardId) { message += ` for board ID: ${boardId}`; } else if (projectKey) { message += ` for project key: ${projectKey}`; } return { content: [{ type: "text", text: message }], isError: false, }; } allSprints.sort((a, b) => { const dateA = a.startDate ? new Date(a.startDate).getTime() : 0; const dateB = b.startDate ? new Date(b.startDate).getTime() : 0; return dateB - dateA; }); let formattedResponse = `# ${state.charAt(0).toUpperCase() + state.slice(1)} Sprints\n\n`; formattedResponse += `| ID | Name | Board | Status | Start Date | End Date |\n`; formattedResponse += `|----|------|-------|--------|------------|----------|\n`; allSprints.forEach(sprint => { const startDate = sprint.startDate ? new Date(sprint.startDate).toLocaleDateString() : 'Not started'; const endDate = sprint.endDate ? new Date(sprint.endDate).toLocaleDateString() : 'Not set'; const status = sprint.state.charAt(0).toUpperCase() + sprint.state.slice(1); formattedResponse += `| ${sprint.id} | ${sprint.name} | ${sprint.boardId} | ${status} | ${startDate} | ${endDate} |\n`; }); formattedResponse += `\n## Sprint Details\n\n`; for (const sprint of allSprints) { formattedResponse += `### ${sprint.name} (ID: ${sprint.id})\n\n`; formattedResponse += `**Board ID:** ${sprint.boardId}\n`; formattedResponse += `**Status:** ${sprint.state.charAt(0).toUpperCase() + sprint.state.slice(1)}\n`; if (sprint.startDate) { formattedResponse += `**Start Date:** ${new Date(sprint.startDate).toLocaleString()}\n`; } if (sprint.endDate) { formattedResponse += `**End Date:** ${new Date(sprint.endDate).toLocaleString()}\n`; } if (sprint.goal) { formattedResponse += `**Goal:** ${sprint.goal}\n`; } formattedResponse += `**View in Jira:** https://${jiraHost}/jira/software/projects/${projectKey || 'browse'}/boards/${sprint.boardId}/sprints/${sprint.id}\n\n`; try { const issuesResponse = await axios.get(`https://${jiraHost}/rest/agile/1.0/sprint/${sprint.id}/issue`, { params: { fields: 'summary,status,assignee,issuetype' }, headers: { 'Authorization': authHeader, 'Accept': 'application/json', }, }); if (issuesResponse.data.issues && issuesResponse.data.issues.length > 0) { const issues = issuesResponse.data.issues; formattedResponse += `#### Issues (${issues.length})\n\n`; formattedResponse += `| Key | Summary | Type | Status | Assignee |\n`; formattedResponse += `|-----|---------|------|--------|----------|\n`; issues.forEach((issue: any) => { const assignee = issue.fields.assignee ? issue.fields.assignee.displayName : 'Unassigned'; const status = issue.fields.status ? issue.fields.status.name : 'Unknown'; const type = issue.fields.issuetype ? issue.fields.issuetype.name : 'Task'; formattedResponse += `| [${issue.key}](https://${jiraHost}/browse/${issue.key}) | ${issue.fields.summary} | ${type} | ${status} | ${assignee} |\n`; }); } else { formattedResponse += `No issues found in this sprint.\n`; } } catch (error) { formattedResponse += `Could not fetch issues for this sprint.\n`; } formattedResponse += `\n---\n\n`; } return { content: [{ type: "text", text: formattedResponse }], isError: false, }; } catch (error: any) { let errorMsg = "An error occurred while retrieving sprints."; if (error.response) { errorMsg = `Error ${error.response.status}: ${JSON.stringify(error.response.data) || error.message}`; } else if (error.message) { errorMsg = error.message; } return { content: [{ type: "text", text: `# Error\n\n${errorMsg}` }], isError: true, }; } }
- src/validators/index.ts:146-163 (schema)Yup validation schema for jira_list_sprints input parameters, extending base JiraApiRequestSchema with optional boardId, projectKey, and state.export const JiraSprintRequestSchema = JiraApiRequestSchema.shape({ boardId: yup.string() .optional() .test( 'valid-board-id', 'Board ID must be numeric', (value) => !value || /^\d+$/.test(value) ), projectKey: yup.string() .optional() .matches(/^[A-Z][A-Z0-9_]+$/, "Invalid project key format. Only uppercase letters, numbers, and underscores are allowed"), state: yup.string() .oneOf( ['active', 'future', 'closed', 'all'], "Sprint state must be one of: active, future, closed, all" ) .default('active'), });
- src/handlers/handlerTools.ts:27-56 (registration)Tool registration in handlerTools.ts where jira_list_sprints is mapped to its schema and handler function for execution via handleCallTool.const toolConfigs: Record<string, ToolConfig> = { jira_list_projects: { schema: JiraApiRequestSchema, handler: listProjects }, jira_get_issue: { schema: JiraIssueRequestSchema, handler: getIssue }, jira_search_issues: { schema: JiraSearchIssuesRequestSchema, handler: searchIssues }, jira_list_project_members: { schema: JiraProjectMembersRequestSchema, handler: listProjectMembers }, jira_check_user_issues: { schema: JiraCheckUserIssuesRequestSchema, handler: checkUserIssues }, jira_create_issue: { schema: JiraCreateIssueRequestSchema, handler: createIssue }, jira_list_sprints: { schema: JiraSprintRequestSchema, handler: listSprints } };
- src/handlers/handlerListTools.ts:33-182 (registration)Registration of jira_list_sprints tool description and inline inputSchema for the MCP listTools endpoint.export async function handleListTools(request: any) { return { tools: [ { name: "jira_list_projects", description: "Lists all Jira projects the user has access to", inputSchema: { type: "object", properties: { ...getCommonJiraProperties(), }, required: [], }, }, { name: "jira_get_issue", description: "Retrieves details of a specific Jira issue by key", inputSchema: { type: "object", properties: { ...getCommonJiraProperties(), issueKey: { type: "string", description: "The Jira issue key (e.g., 'PROJECT-123')", }, }, required: ["issueKey"], }, }, { name: "jira_search_issues", description: "Searches for Jira issues by project and assignee", inputSchema: { type: "object", properties: { ...getCommonJiraProperties(), projectKey: { type: "string", description: "The Jira project key (e.g., 'PROJECT')", }, assigneeName: { type: "string", description: "The display name of the assignee to filter by (e.g., 'John Doe')", }, }, required: ["projectKey"], }, }, { name: "jira_list_project_members", description: "Lists all members of a specific Jira project", inputSchema: { type: "object", properties: { ...getCommonJiraProperties(), projectKey: { type: "string", description: "The Jira project key (e.g., 'PROJECT')", }, }, required: ["projectKey"], }, }, { name: "jira_check_user_issues", description: "Checks if a user is a member of a project and lists their assigned issues", inputSchema: { type: "object", properties: { ...getCommonJiraProperties(), projectKey: { type: "string", description: "The Jira project key (e.g., 'PROJECT')", }, userName: { type: "string", description: "The display name of the user to check for in the project", }, }, required: ["projectKey", "userName"], }, }, { name: "jira_create_issue", description: "Creates a new issue in a Jira project with specified details", inputSchema: { type: "object", properties: { ...getCommonJiraProperties(), projectKey: { type: "string", description: "The Jira project key (e.g., 'PROJECT')", }, summary: { type: "string", description: "The title/summary of the issue", }, description: { type: "object", description: "Issue description in ADF (Atlassian Document Format). REQUIRED: Must be an object with structure: {\"type\": \"doc\", \"version\": 1, \"content\": [{\"type\": \"paragraph\", \"content\": [{\"type\": \"text\", \"text\": \"Your description text\"}]}]}", }, issueType: { type: "string", description: "Type of issue (e.g., 'Task', 'Bug', 'Story')", default: "Task", }, assigneeName: { type: "string", description: "The display name of the person to assign the issue to", }, reporterName: { type: "string", description: "The display name of the person reporting the issue", }, sprintId: { type: "string", description: "ID of the sprint to add the issue to", }, }, required: ["projectKey", "summary", "description"], }, }, { name: "jira_list_sprints", description: "Lists current sprints in Jira with filtering options", inputSchema: { type: "object", properties: { ...getCommonJiraProperties(), boardId: { type: "string", description: "Optional Jira board ID to filter sprints by a specific board", }, projectKey: { type: "string", description: "Optional project key to find sprints associated with the project", }, state: { type: "string", description: "Sprint state to filter by (active, future, closed, or all)", default: "active", enum: ["active", "future", "closed", "all"] }, }, required: [], }, }, ], }; }
- src/tools/listSprints.ts:12-50 (schema)JSON schema description for the jira_list_sprints tool, exported and re-exported via tools/index.ts.export const listSprintsToolDescription = { name: "jira_list_sprints", description: "Lists current sprints in Jira with filtering options", inputSchema: { type: "object", properties: { jiraHost: { type: "string", description: "The Jira host URL (e.g., 'your-domain.atlassian.net')", default: process.env.JIRA_HOST || "", }, email: { type: "string", description: "Email address associated with the Jira account", default: process.env.JIRA_EMAIL || "", }, apiToken: { type: "string", description: "API token for Jira authentication", default: process.env.JIRA_API_TOKEN || "", }, boardId: { type: "string", description: "Optional Jira board ID to filter sprints by a specific board", }, projectKey: { type: "string", description: "Optional project key to find sprints associated with the project", }, state: { type: "string", description: "Sprint state to filter by (active, future, closed, or all)", default: "active", enum: ["active", "future", "closed", "all"] }, }, required: [], }, };