index.ts•12.4 kB
#!/usr/bin/env node
/**
* Eureka Labo MCP Server
* Task management with automated change tracking
*/
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 { getConfig } from './config.js';
import {
listTasks,
getTask,
createTask,
updateTask,
listProjectMembers,
uploadTaskAttachment,
} from './tools/task-tools.js';
import {
startWorkOnTask,
completeTaskWork,
getActiveSessions,
cancelWorkSession,
} from './tools/work-session.js';
import {
listBranchTasks,
createPullRequest,
} from './tools/pr-tools.js';
// Initialize MCP server
const server = new Server(
{
name: 'eurekalabo-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// ===== Tool Definitions =====
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
// Task Management Tools
{
name: 'list_tasks',
description:
'List tasks for the project. Optionally filter by status, assignee, or search term.',
inputSchema: {
type: 'object',
properties: {
status: {
type: 'string',
description: 'Filter by status (todo, in_progress, done, cancelled)',
},
assigneeId: {
type: 'string',
description: 'Filter by assignee user ID',
},
search: {
type: 'string',
description: 'Search in task title and description',
},
limit: {
type: 'number',
description: 'Maximum number of tasks to return',
},
},
},
},
{
name: 'get_task',
description:
'Get detailed information about a specific task, including change history.',
inputSchema: {
type: 'object',
properties: {
taskId: {
type: 'string',
description: 'Task ID',
},
},
required: ['taskId'],
},
},
{
name: 'create_task',
description: 'Create a new task in the project.',
inputSchema: {
type: 'object',
properties: {
title: {
type: 'string',
description: 'Task title',
},
description: {
type: 'string',
description: 'Task description',
},
status: {
type: 'string',
description: 'Initial status (default: todo)',
},
priority: {
type: 'string',
description: 'Priority level (low, medium, high, critical)',
},
assigneeId: {
type: 'string',
description: 'User ID to assign task to',
},
dueDate: {
type: 'string',
description: 'Due date (ISO 8601 format)',
},
},
required: ['title'],
},
},
{
name: 'update_task',
description: 'Update an existing task.',
inputSchema: {
type: 'object',
properties: {
taskId: {
type: 'string',
description: 'Task ID',
},
title: {
type: 'string',
description: 'New task title',
},
description: {
type: 'string',
description: 'New task description',
},
status: {
type: 'string',
description: 'New status',
},
priority: {
type: 'string',
description: 'New priority',
},
assigneeId: {
type: 'string',
description: 'New assignee user ID',
},
},
required: ['taskId'],
},
},
// Work Session Tools
{
name: 'start_work_on_task',
description:
'Begin working on a task. Captures git baseline for change tracking. Requires clean working directory (no uncommitted changes).',
inputSchema: {
type: 'object',
properties: {
taskId: {
type: 'string',
description: 'Task ID to start working on',
},
},
required: ['taskId'],
},
},
{
name: 'complete_task_work',
description:
'Complete work on a task. Captures all git changes since work started and logs them to the task.',
inputSchema: {
type: 'object',
properties: {
taskId: {
type: 'string',
description: 'Task ID to complete',
},
summary: {
type: 'string',
description: 'Brief summary of work completed',
},
},
required: ['taskId', 'summary'],
},
},
{
name: 'get_active_sessions',
description: 'List all active work sessions.',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'cancel_work_session',
description:
'Cancel an active work session without logging changes.',
inputSchema: {
type: 'object',
properties: {
taskId: {
type: 'string',
description: 'Task ID to cancel session for',
},
},
required: ['taskId'],
},
},
// Utility Tools
{
name: 'list_project_members',
description:
'List all members of the project (for task assignment). Project is automatically determined from API key.',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'upload_task_attachment',
description: 'Upload a file attachment to a task.',
inputSchema: {
type: 'object',
properties: {
taskId: {
type: 'string',
description: 'Task ID',
},
filePath: {
type: 'string',
description: 'Local file path to upload',
},
},
required: ['taskId', 'filePath'],
},
},
// PR Integration Tools
{
name: 'list_branch_tasks',
description:
'List all tasks worked on in the current git branch. Shows tasks that are part of the branch session.',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'create_pull_request',
description:
'Create a GitHub pull request for all tasks in the current branch. Generates PR description from work sessions. Requires GitHub integration configured for the project.',
inputSchema: {
type: 'object',
properties: {
title: {
type: 'string',
description: 'PR title (optional - will auto-generate from tasks if not provided)',
},
baseBranch: {
type: 'string',
description: 'Base branch to merge into (default: main)',
},
},
},
},
],
};
});
// ===== Tool Execution Handler =====
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const { name, arguments: args } = request.params;
switch (name) {
// Task Management
case 'list_tasks':
return {
content: [
{
type: 'text',
text: JSON.stringify(await listTasks(args as any), null, 2),
},
],
};
case 'get_task':
return {
content: [
{
type: 'text',
text: JSON.stringify(await getTask(args.taskId as string), null, 2),
},
],
};
case 'create_task':
return {
content: [
{
type: 'text',
text: JSON.stringify(await createTask(args as any), null, 2),
},
],
};
case 'update_task':
return {
content: [
{
type: 'text',
text: JSON.stringify(
await updateTask(args.taskId as string, args as any),
null,
2
),
},
],
};
// Work Sessions
case 'start_work_on_task':
return {
content: [
{
type: 'text',
text: JSON.stringify(
await startWorkOnTask(args.taskId as string),
null,
2
),
},
],
};
case 'complete_task_work':
return {
content: [
{
type: 'text',
text: JSON.stringify(
await completeTaskWork(
args.taskId as string,
args.summary as string
),
null,
2
),
},
],
};
case 'get_active_sessions':
return {
content: [
{
type: 'text',
text: JSON.stringify(
{ sessions: getActiveSessions() },
null,
2
),
},
],
};
case 'cancel_work_session':
return {
content: [
{
type: 'text',
text: JSON.stringify(
await cancelWorkSession(args.taskId as string),
null,
2
),
},
],
};
// Utilities
case 'list_project_members':
return {
content: [
{
type: 'text',
text: JSON.stringify(
await listProjectMembers(),
null,
2
),
},
],
};
case 'upload_task_attachment':
return {
content: [
{
type: 'text',
text: JSON.stringify(
await uploadTaskAttachment(
args.taskId as string,
args.filePath as string
),
null,
2
),
},
],
};
// PR Integration
case 'list_branch_tasks':
return {
content: [
{
type: 'text',
text: JSON.stringify(
await listBranchTasks(),
null,
2
),
},
],
};
case 'create_pull_request':
return {
content: [
{
type: 'text',
text: JSON.stringify(
await createPullRequest(args as any),
null,
2
),
},
],
};
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error: any) {
return {
content: [
{
type: 'text',
text: JSON.stringify(
{
success: false,
error: error.message,
},
null,
2
),
},
],
isError: true,
};
}
});
// ===== Start Server =====
async function main() {
try {
// Validate configuration
const config = getConfig();
console.error(`[MCP] Starting Eureka Labo MCP Server`);
console.error(`[MCP] API URL: ${config.apiUrl}`);
console.error(`[MCP] Workspace: ${config.workspacePath}`);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('[MCP] Server ready');
} catch (error: any) {
console.error('[MCP] Failed to start server:', error.message);
process.exit(1);
}
}
main();