@xiaotaitech/tempo-mcp-server
Version:
MCP server for managing Tempo worklogs in Jira
131 lines (130 loc) • 5.97 kB
JavaScript
/**
* 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);
});