Skip to main content
Glama

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
NameRequiredDescriptionDefault
jiraHostNoThe Jira host URL (e.g., 'your-domain.atlassian.net')
emailNoEmail address associated with the Jira account
apiTokenNoAPI token for Jira authentication
boardIdNoOptional Jira board ID to filter sprints by a specific board
projectKeyNoOptional project key to find sprints associated with the project
stateNoSprint state to filter by (active, future, closed, or all)active

Implementation Reference

  • 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,
            };
        }
    }
  • 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'),
    });
  • 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
        }
    };
  • 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: [],
                    },
                },
            ],
        };
    }
  • 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: [],
        },
    };
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions 'filtering options' but doesn't explain key behaviors: whether this is a read-only operation (implied by 'Lists'), what authentication is required (hinted by parameters but not stated), how results are returned (e.g., pagination, format), or any rate limits. For a tool with authentication parameters and no annotations, this is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core purpose ('Lists current sprints in Jira') and adds a brief qualifier ('with filtering options'). There is no wasted language or redundancy, making it appropriately sized for the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (6 parameters, no output schema, no annotations), the description is incomplete. It doesn't address authentication requirements, result format, error handling, or how filtering parameters interact. Without annotations or output schema, the agent lacks sufficient context to use this tool effectively beyond basic parameter passing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds minimal value beyond the input schema, which has 100% coverage. It mentions 'filtering options,' which aligns with parameters like 'boardId,' 'projectKey,' and 'state,' but doesn't provide additional semantics (e.g., how filters combine or default behaviors). With high schema coverage, the baseline is 3, as the schema already documents parameters well.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Lists') and resource ('current sprints in Jira'), making the purpose understandable. However, it doesn't explicitly differentiate this tool from sibling tools like 'jira_list_projects' or 'jira_list_project_members', which reduces specificity. The mention of 'filtering options' adds some context but doesn't fully distinguish it from other list operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., authentication setup), compare it to sibling tools like 'jira_search_issues' for sprint-related queries, or specify scenarios where filtering by board or project is appropriate. This leaves the agent without clear usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/samuelrizzo/jira-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server