@pavi_thran_7/jira-mcp-server
Version:
MCP server for Jira integration with ticket creation and time logging
173 lines (156 loc) • 4.84 kB
JavaScript
/**
* @fileoverview Discovery tools for Jira MCP server
*/
/**
* @typedef {import('../types/jira.js').McpToolResult} McpToolResult
* @typedef {import('../jira-client.js').JiraClient} JiraClient
*/
/**
* List all projects accessible to the user
* @param {JiraClient} jiraClient - Jira client instance
* @returns {Promise<McpToolResult>} Projects list result
*/
export async function listProjects(jiraClient) {
try {
const projectsResult = await jiraClient.getProjects();
if (!projectsResult.success) {
return {
content: [
{
type: 'text',
text: `Error getting projects: ${projectsResult.error}`,
},
],
isError: true,
};
}
const projects = projectsResult.data.values;
if (!projects || projects.length === 0) {
return {
content: [
{
type: 'text',
text: 'No projects found. You may not have access to any projects.',
},
],
isError: false,
};
}
// Format projects list
const projectsList = projects.map(project => {
const projectType = project.projectTypeKey || 'Unknown';
return `- **${project.key}**: ${project.name} (${projectType})`;
}).join('\n');
return {
content: [
{
type: 'text',
text: `📁 **Available Projects (${projects.length})**
${projectsList}
**Usage:**
- Use the project key (e.g., "${projects[0].key}") when creating tickets
- Use list_boards with a project key to see available boards for that project`,
},
],
isError: false,
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error listing projects: ${error.message}`,
},
],
isError: true,
};
}
}
/**
* List boards for a project
* @param {JiraClient} jiraClient - Jira client instance
* @param {Object} params - Parameters
* @param {string} [params.projectKey] - Project key to filter boards
* @returns {Promise<McpToolResult>} Boards list result
*/
export async function listBoards(jiraClient, params = {}) {
try {
const boardsResult = await jiraClient.getBoards(params.projectKey);
if (!boardsResult.success) {
return {
content: [
{
type: 'text',
text: `Error getting boards: ${boardsResult.error}`,
},
],
isError: true,
};
}
const boards = boardsResult.data.values;
if (!boards || boards.length === 0) {
const projectFilter = params.projectKey ? ` for project "${params.projectKey}"` : '';
return {
content: [
{
type: 'text',
text: `No boards found${projectFilter}. You may not have access to any boards or the project may not exist.`,
},
],
isError: false,
};
}
// Group boards by project if no project filter is specified
const boardsByProject = {};
boards.forEach(board => {
const projectKey = board.location?.projectKey || 'Unknown';
if (!boardsByProject[projectKey]) {
boardsByProject[projectKey] = [];
}
boardsByProject[projectKey].push(board);
});
// Format boards list
let boardsList = '';
if (params.projectKey) {
// Single project view
boardsList = boards.map(board => {
const boardType = board.type || 'Unknown';
return `- **${board.id}**: ${board.name} (${boardType})`;
}).join('\n');
} else {
// Multi-project view
boardsList = Object.entries(boardsByProject).map(([projectKey, projectBoards]) => {
const boardItems = projectBoards.map(board => {
const boardType = board.type || 'Unknown';
return ` - **${board.id}**: ${board.name} (${boardType})`;
}).join('\n');
return `**${projectKey}:**\n${boardItems}`;
}).join('\n\n');
}
const projectFilter = params.projectKey ? ` for project "${params.projectKey}"` : '';
const totalBoards = boards.length;
return {
content: [
{
type: 'text',
text: `📋 **Available Boards${projectFilter} (${totalBoards})**
${boardsList}
**Usage:**
- Use the board ID (e.g., "${boards[0].id}") when creating tickets to assign them to a specific board
- Board types: Scrum (for sprint-based work), Kanban (for continuous flow)`,
},
],
isError: false,
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error listing boards: ${error.message}`,
},
],
isError: true,
};
}
}