Files
session-mcp/src/index.ts
Christian Gick e21072ea54 feat: Add delegation tracking MCP tools
Session 374: Task-MCP and Delegation System Integration (Phase 4 & 6)

- Add task_delegations tool: query delegations for a specific task
- Add task_delegation_query tool: query across all tasks by status/backend
- Enhance taskShow() to display recent delegation history
- New delegations.ts module with getRecentDelegations helper

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 10:43:13 +02:00

212 lines
5.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Task MCP Server
*
* Exposes task management tools via Model Context Protocol.
* Uses PostgreSQL with pgvector for semantic search.
*
* Requires SSH tunnel to docker-host:
* ssh -L 5432:172.27.0.3:5432 docker-host -N &
*/
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 { testConnection, close } from './db.js';
import { toolDefinitions } from './tools/index.js';
import { taskAdd, taskList, taskShow, taskClose, taskUpdate } from './tools/crud.js';
import { taskSimilar, taskContext } from './tools/search.js';
import { taskLink, checklistAdd, checklistToggle, taskResolveDuplicate } from './tools/relations.js';
import { epicAdd, epicList, epicShow, epicAssign } from './tools/epics.js';
import { taskDelegations, taskDelegationQuery } from './tools/delegations.js';
// Create MCP server
const server = new Server(
{ name: 'task-mcp', version: '1.0.0' },
{ capabilities: { tools: {} } }
);
// Register tool list handler
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: toolDefinitions,
}));
// Register tool call handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
let result: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const a = args as any;
switch (name) {
// CRUD
case 'task_add':
result = await taskAdd({
title: a.title,
project: a.project,
type: a.type,
priority: a.priority,
description: a.description,
});
break;
case 'task_list':
result = await taskList({
project: a.project,
status: a.status,
type: a.type,
priority: a.priority,
limit: a.limit,
});
break;
case 'task_show':
result = await taskShow(a.id);
break;
case 'task_close':
result = await taskClose(a.id);
break;
case 'task_update':
result = await taskUpdate({
id: a.id,
status: a.status,
priority: a.priority,
type: a.type,
title: a.title,
});
break;
// Search
case 'task_similar':
result = await taskSimilar({
query: a.query,
project: a.project,
limit: a.limit,
});
break;
case 'task_context':
result = await taskContext({
description: a.description,
project: a.project,
limit: a.limit,
});
break;
// Relations
case 'task_link':
result = await taskLink({
from_id: a.from_id,
to_id: a.to_id,
link_type: a.link_type,
});
break;
case 'task_checklist_add':
result = await checklistAdd({
task_id: a.task_id,
item: a.item,
});
break;
case 'task_checklist_toggle':
result = await checklistToggle({
item_id: a.item_id,
checked: a.checked,
});
break;
case 'task_resolve_duplicate':
result = await taskResolveDuplicate({
duplicate_id: a.duplicate_id,
dominant_id: a.dominant_id,
});
break;
// Epics
case 'epic_add':
result = await epicAdd({
title: a.title,
project: a.project,
description: a.description,
});
break;
case 'epic_list':
result = await epicList({
project: a.project,
status: a.status,
limit: a.limit,
});
break;
case 'epic_show':
result = await epicShow(a.id);
break;
case 'epic_assign':
result = await epicAssign({
task_id: a.task_id,
epic_id: a.epic_id,
});
break;
// Delegations
case 'task_delegations':
result = await taskDelegations({ task_id: a.task_id });
break;
case 'task_delegation_query':
result = await taskDelegationQuery({
status: a.status,
backend: a.backend,
limit: a.limit,
});
break;
default:
throw new Error(`Unknown tool: ${name}`);
}
return {
content: [{ type: 'text', text: result }],
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return {
content: [{ type: 'text', text: `Error: ${message}` }],
isError: true,
};
}
});
// Main entry point
async function main() {
// Test database connection
const connected = await testConnection();
if (!connected) {
console.error('Failed to connect to database. Ensure SSH tunnel is active:');
console.error(' ssh -L 5432:172.27.0.3:5432 docker-host -N &');
process.exit(1);
}
console.error('task-mcp: Connected to database');
// Set up cleanup
process.on('SIGINT', async () => {
await close();
process.exit(0);
});
process.on('SIGTERM', async () => {
await close();
process.exit(0);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('task-mcp: Server started');
}
main().catch((error) => {
console.error('task-mcp: Fatal error:', error);
process.exit(1);
});