UNPKG

@xiaotaitech/tempo-mcp-server

Version:

MCP server for managing Tempo worklogs in Jira

131 lines (130 loc) 5.97 kB
#!/usr/bin/env node /** * Tempo MCP Server * * A simple Model Context Protocol server for managing Tempo worklogs with TypeScript. */ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import config from './config.js'; import * as tools from './tools.js'; import { retrieveWorklogsSchema, createWorklogSchema, bulkCreateWorklogsSchema, editWorklogSchema, deleteWorklogSchema } from './types.js'; // Create MCP server instance const server = new McpServer({ name: config.server.name, version: config.server.version, }); // Tool: retrieveWorklogs - fetch worklogs between two dates server.tool('retrieveWorklogs', retrieveWorklogsSchema.shape, { description: 'Retrieve worklogs for a specific date range. Returns logged time entries with issue keys, dates, hours, activities, and descriptions.' }, async ({ startDate, endDate }) => { try { const result = await tools.retrieveWorklogs(startDate, endDate); return { content: result.content }; } catch (error) { console.error(`[ERROR] retrieveWorklogs failed: ${error instanceof Error ? error.message : String(error)}`); return { content: [{ type: 'text', text: `Error retrieving worklogs: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }); // Tool: createWorklog - create a single worklog entry server.tool('createWorklog', createWorklogSchema.shape, { description: 'Create a single worklog entry for a Jira issue. Specify issue key, time spent in hours, date, optional description, start time, and activity type.' }, async ({ issueKey, timeSpentHours, date, description, startTime, activity }) => { try { // Create attributes array from activity const attributes = activity ? [{ key: '_Activity_', value: activity }] : undefined; const result = await tools.createWorklog(issueKey, timeSpentHours, date, description, startTime, attributes); return { content: result.content }; } catch (error) { console.error(`[ERROR] createWorklog failed: ${error instanceof Error ? error.message : String(error)}`); return { content: [{ type: 'text', text: `Error creating worklog: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }); // Tool: bulkCreateWorklogs - create multiple worklog entries at once server.tool('bulkCreateWorklogs', bulkCreateWorklogsSchema.shape, { description: 'Create multiple worklog entries at once for batch time logging. Provide an array of worklog entries (each with issue key, time, date, etc.) and a common activity type.' }, async ({ worklogEntries, activity }) => { try { // Create attributes array from activity const attributes = [{ key: '_Activity_', value: activity }]; const result = await tools.bulkCreateWorklogs(worklogEntries, attributes); return { content: result.content }; } catch (error) { console.error(`[ERROR] bulkCreateWorklogs failed: ${error instanceof Error ? error.message : String(error)}`); return { content: [{ type: 'text', text: `Error creating multiple worklogs: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }); // Tool: editWorklog - modify an existing worklog entry server.tool('editWorklog', editWorklogSchema.shape, { description: 'Edit an existing worklog entry by worklog ID. Update time spent, description, date, start time, or activity type. All fields except worklogId and timeSpentHours are optional.' }, async ({ worklogId, timeSpentHours, description, date, startTime, activity }) => { try { // Create attributes array from activity if provided const defaultAttributes = activity ? [{ key: '_Activity_', value: activity }] : undefined; const result = await tools.editWorklog(worklogId, timeSpentHours, description || null, date || null, startTime || undefined, defaultAttributes); return { content: result.content }; } catch (error) { console.error(`[ERROR] editWorklog failed: ${error instanceof Error ? error.message : String(error)}`); return { content: [{ type: 'text', text: `Error editing worklog: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }); // Tool: deleteWorklog - remove an existing worklog entry server.tool('deleteWorklog', deleteWorklogSchema.shape, { description: 'Delete an existing worklog entry by its worklog ID. This action cannot be undone.' }, async ({ worklogId }) => { try { const result = await tools.deleteWorklog(worklogId); return { content: result.content }; } catch (error) { console.error(`[ERROR] deleteWorklog failed: ${error instanceof Error ? error.message : String(error)}`); return { content: [{ type: 'text', text: `Error deleting worklog: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }); async function startServer() { try { const transport = new StdioServerTransport(); await server.connect(transport); console.error('[INFO] MCP Server started successfully'); } catch (error) { console.error(`[ERROR] Failed to start MCP Server: ${error instanceof Error ? error.message : String(error)}`); if (error instanceof Error && error.stack) { console.error(`[ERROR] Stack trace: ${error.stack}`); } process.exit(1); } } startServer().catch(error => { console.error(`[ERROR] Unhandled exception: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); });