This guide provides essential information for developers working with the Jira Cloud MCP Server.
- Node.js 20 or higher
- npm 8 or higher
- Git
- Clone the repository:
git clone https://github.com/aaronsb/jira-cloud.git
cd jira-cloud
- Install dependencies:
npm install
- Build the project:
npm run build
- Run in development mode:
npm run dev
src/
├── client/ # Core Jira API client
├── handlers/ # MCP tool handlers
├── schemas/ # JSON schemas for tools
├── types/ # TypeScript definitions
├── utils/ # Utility functions
└── index.ts # Server entry point
Run the test suite:
npm test
Run tests with coverage:
npm run test:coverage
- Define the tool schema in
src/schemas/tool-schemas.ts
- Implement the handler in the appropriate file in
src/handlers/
- Add any necessary methods to the Jira client in
src/client/jira-client.ts
- Register the tool in
src/index.ts
Here's an example of adding a new tool called get_jira_issue_links
:
- Define the tool schema:
// src/schemas/tool-schemas.ts
export const GetJiraIssueLinksSchema = {
name: 'get_jira_issue_links',
description: 'Get links between Jira issues',
inputSchema: {
type: 'object',
properties: {
issueKey: {
type: 'string',
description: 'The Jira issue key (e.g., PROJ-123)'
}
},
required: ['issueKey']
}
};
- Implement the handler:
// src/handlers/issue-handlers.ts
export async function handleGetJiraIssueLinks(
request: CallToolRequest,
jiraClient: JiraClient
): Promise<CallToolResponse> {
const { issueKey } = request.params.arguments;
if (!issueKey) {
throw new McpError(ErrorCode.InvalidParams, 'issueKey is required');
}
try {
const issueLinks = await jiraClient.getIssueLinks(issueKey);
return {
content: [
{
type: 'text',
text: JSON.stringify(issueLinks, null, 2)
}
]
};
} catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError(
ErrorCode.InternalError,
`Error getting issue links: ${error.message}`
);
}
}
- Add to JiraClient:
// src/client/jira-client.ts
async getIssueLinks(issueKey: string): Promise<any> {
const endpoint = `/rest/api/3/issue/${issueKey}`;
const params = { fields: 'issuelinks' };
const response = await this.makeRequest('GET', endpoint, params);
return response.data.fields.issuelinks;
}
- Register the tool:
// src/index.ts
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
// Existing tools...
GetJiraIssueLinksSchema
]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
// Existing tool handling...
if (request.params.name === 'get_jira_issue_links') {
return handleGetJiraIssueLinks(request, jiraClient);
}
// Error handling for unknown tools...
});
- Follow TypeScript best practices
- Use async/await for asynchronous code
- Add proper error handling
- Include JSDoc comments for public APIs
- Use McpError for all errors returned to clients
- Include meaningful error messages
- Categorize errors with appropriate error codes
- Log detailed error information for debugging
- Write unit tests for all new functionality
- Test both success and error cases
- Mock external dependencies
- Aim for high test coverage
Build a local Docker image:
./scripts/build-local.sh
Run the local Docker image:
docker run -i \
-e JIRA_API_TOKEN=your_api_token \
-e JIRA_EMAIL=your_email \
-e JIRA_HOST=your-instance.atlassian.net \
jira-cloud:local