jira-server-mcp
Version:
MCP (Model Context Protocol) para integração com Jira Server auto-hospedado. Uso principal via configuração MCP (mcp.json) para automações e integrações.
243 lines (219 loc) • 5.75 kB
JavaScript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import fetch from 'node-fetch';
// Configuração do servidor
const server = new Server(
{
name: 'jira-server-mcp',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Função para carregar a configuração
function loadConfig() {
try {
// Se as variáveis de ambiente foram fornecidas
if (process.env.JIRA_BASE_URL && process.env.JIRA_API_TOKEN) {
return {
baseUrl: process.env.JIRA_BASE_URL,
token: process.env.JIRA_API_TOKEN
};
}
throw new Error('Configuração não encontrada. Defina JIRA_BASE_URL e JIRA_API_TOKEN');
} catch (error) {
console.error('Erro ao carregar configuração:', error);
throw error;
}
}
// Configuração do Jira
const config = loadConfig();
// Classe para interagir com a API do Jira
class JiraAPI {
constructor(baseUrl, token) {
this.baseUrl = baseUrl;
this.token = token;
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const headers = {
'Authorization': `Bearer ${this.token}`,
'Accept': 'application/json',
'Content-Type': 'application/json',
...options.headers
};
try {
const response = await fetch(url, {
...options,
headers
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Jira API error: ${response.status} - ${error}`);
}
return await response.json();
} catch (error) {
console.error('Erro na requisição:', error);
throw error;
}
}
async getIssue(issueKey) {
return this.request(`/rest/api/2/issue/${issueKey}`);
}
async searchIssues(jql) {
return this.request('/rest/api/2/search', {
method: 'POST',
body: JSON.stringify({
jql,
maxResults: 50
})
});
}
async getIssueTransitions(issueKey) {
return this.request(`/rest/api/2/issue/${issueKey}/transitions`);
}
async getMyself() {
return this.request('/rest/api/2/myself');
}
}
// Inicializar API do Jira
const jiraAPI = new JiraAPI(config.baseUrl, config.token);
// Handler para listar ferramentas
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'get_issue',
description: 'Buscar uma issue do Jira por chave',
inputSchema: {
type: 'object',
properties: {
issueKey: {
type: 'string',
description: 'Chave da issue (ex: CLI-123)',
},
},
required: ['issueKey'],
},
},
{
name: 'search_issues',
description: 'Buscar issues usando JQL (Jira Query Language)',
inputSchema: {
type: 'object',
properties: {
jql: {
type: 'string',
description: 'Query JQL para buscar issues',
},
},
required: ['jql'],
},
},
{
name: 'get_transitions',
description: 'Buscar transições disponíveis para uma issue',
inputSchema: {
type: 'object',
properties: {
issueKey: {
type: 'string',
description: 'Chave da issue (ex: CLI-123)',
},
},
required: ['issueKey'],
},
},
{
name: 'test_connection',
description: 'Testar a conexão com o Jira',
inputSchema: {
type: 'object',
properties: {},
},
},
],
};
});
// Handler para executar ferramentas
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'get_issue': {
const issue = await jiraAPI.getIssue(args.issueKey);
return {
content: [
{
type: 'text',
text: JSON.stringify(issue, null, 2),
},
],
};
}
case 'search_issues': {
const results = await jiraAPI.searchIssues(args.jql);
return {
content: [
{
type: 'text',
text: JSON.stringify(results, null, 2),
},
],
};
}
case 'get_transitions': {
const transitions = await jiraAPI.getIssueTransitions(args.issueKey);
return {
content: [
{
type: 'text',
text: JSON.stringify(transitions, null, 2),
},
],
};
}
case 'test_connection': {
const user = await jiraAPI.getMyself();
return {
content: [
{
type: 'text',
text: `Conexão estabelecida com sucesso!\nUsuário: ${user.displayName} (${user.emailAddress})`,
},
],
};
}
default:
throw new Error(`Ferramenta desconhecida: ${name}`);
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Erro: ${error.message}`,
},
],
isError: true,
};
}
});
// Conectar via stdio
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Jira MCP Server iniciado via stdio');
}
main().catch((error) => {
console.error('Erro ao iniciar o servidor:', error);
process.exit(1);
});