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>
This commit is contained in:
13
src/index.ts
13
src/index.ts
@@ -22,6 +22,7 @@ import { taskAdd, taskList, taskShow, taskClose, taskUpdate } from './tools/crud
|
|||||||
import { taskSimilar, taskContext } from './tools/search.js';
|
import { taskSimilar, taskContext } from './tools/search.js';
|
||||||
import { taskLink, checklistAdd, checklistToggle, taskResolveDuplicate } from './tools/relations.js';
|
import { taskLink, checklistAdd, checklistToggle, taskResolveDuplicate } from './tools/relations.js';
|
||||||
import { epicAdd, epicList, epicShow, epicAssign } from './tools/epics.js';
|
import { epicAdd, epicList, epicShow, epicAssign } from './tools/epics.js';
|
||||||
|
import { taskDelegations, taskDelegationQuery } from './tools/delegations.js';
|
||||||
|
|
||||||
// Create MCP server
|
// Create MCP server
|
||||||
const server = new Server(
|
const server = new Server(
|
||||||
@@ -147,6 +148,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|||||||
});
|
});
|
||||||
break;
|
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:
|
default:
|
||||||
throw new Error(`Unknown tool: ${name}`);
|
throw new Error(`Unknown tool: ${name}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { query, queryOne, execute, getNextTaskId, getProjectKey } from '../db.js';
|
import { query, queryOne, execute, getNextTaskId, getProjectKey } from '../db.js';
|
||||||
import { getEmbedding, formatEmbedding } from '../embeddings.js';
|
import { getEmbedding, formatEmbedding } from '../embeddings.js';
|
||||||
import type { Task, ChecklistItem, TaskLink } from '../types.js';
|
import type { Task, ChecklistItem, TaskLink } from '../types.js';
|
||||||
|
import { getRecentDelegations } from './delegations.js';
|
||||||
|
|
||||||
interface TaskAddArgs {
|
interface TaskAddArgs {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -225,6 +226,12 @@ export async function taskShow(id: string): Promise<string> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get recent delegations
|
||||||
|
const delegationHistory = await getRecentDelegations(id);
|
||||||
|
if (delegationHistory) {
|
||||||
|
output += delegationHistory;
|
||||||
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
163
src/tools/delegations.ts
Normal file
163
src/tools/delegations.ts
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// Delegation tracking tools for task-mcp
|
||||||
|
// Session 374: Task-MCP and Delegation System Integration
|
||||||
|
|
||||||
|
import { query } from '../db.js';
|
||||||
|
|
||||||
|
interface TaskDelegation {
|
||||||
|
delegation_id: string;
|
||||||
|
backend: string;
|
||||||
|
quality_score: number | null;
|
||||||
|
status: string;
|
||||||
|
input_tokens: number;
|
||||||
|
output_tokens: number;
|
||||||
|
cost_usd: number;
|
||||||
|
started_at: string;
|
||||||
|
completed_at: string | null;
|
||||||
|
error_message: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TaskDelegationWithTask extends TaskDelegation {
|
||||||
|
task_id: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DelegationListArgs {
|
||||||
|
task_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DelegationQueryArgs {
|
||||||
|
status?: string;
|
||||||
|
backend?: string;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List delegations for a specific task
|
||||||
|
*/
|
||||||
|
export async function taskDelegations(args: DelegationListArgs): Promise<string> {
|
||||||
|
const { task_id } = args;
|
||||||
|
|
||||||
|
const delegations = await query<TaskDelegation>(
|
||||||
|
`SELECT delegation_id, backend, quality_score, status,
|
||||||
|
input_tokens, output_tokens, cost_usd,
|
||||||
|
to_char(started_at, 'YYYY-MM-DD HH24:MI') as started_at,
|
||||||
|
to_char(completed_at, 'YYYY-MM-DD HH24:MI') as completed_at,
|
||||||
|
error_message
|
||||||
|
FROM task_delegations
|
||||||
|
WHERE task_id = $1
|
||||||
|
ORDER BY started_at DESC
|
||||||
|
LIMIT 20`,
|
||||||
|
[task_id]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (delegations.length === 0) {
|
||||||
|
return `No delegations found for ${task_id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = `Delegations for ${task_id}:\n\n`;
|
||||||
|
|
||||||
|
for (const d of delegations) {
|
||||||
|
const icon = d.status === 'success' ? '✓' : d.status === 'failed' ? '✗' : '○';
|
||||||
|
const quality = d.quality_score !== null ? `Q:${d.quality_score}` : 'Q:-';
|
||||||
|
const tokens = d.input_tokens + d.output_tokens;
|
||||||
|
const cost = d.cost_usd > 0 ? ` $${d.cost_usd.toFixed(4)}` : '';
|
||||||
|
|
||||||
|
output += ` ${icon} ${d.delegation_id.substring(0, 8)} [${d.backend}] ${quality} (${d.started_at})`;
|
||||||
|
if (tokens > 0) output += ` ${tokens}tok`;
|
||||||
|
output += cost;
|
||||||
|
output += '\n';
|
||||||
|
|
||||||
|
if (d.error_message) {
|
||||||
|
output += ` Error: ${d.error_message.substring(0, 60)}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query delegations across all tasks with filters
|
||||||
|
*/
|
||||||
|
export async function taskDelegationQuery(args: DelegationQueryArgs): Promise<string> {
|
||||||
|
const { status, backend, limit = 20 } = args;
|
||||||
|
|
||||||
|
let whereClause = 'WHERE 1=1';
|
||||||
|
const params: unknown[] = [];
|
||||||
|
let paramIndex = 1;
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
whereClause += ` AND d.status = $${paramIndex++}`;
|
||||||
|
params.push(status);
|
||||||
|
}
|
||||||
|
if (backend) {
|
||||||
|
whereClause += ` AND d.backend = $${paramIndex++}`;
|
||||||
|
params.push(backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
params.push(limit);
|
||||||
|
|
||||||
|
const delegations = await query<TaskDelegationWithTask>(
|
||||||
|
`SELECT d.delegation_id, d.task_id, t.title, d.backend,
|
||||||
|
d.quality_score, d.status,
|
||||||
|
d.input_tokens, d.output_tokens, d.cost_usd,
|
||||||
|
to_char(d.started_at, 'YYYY-MM-DD HH24:MI') as started_at,
|
||||||
|
d.error_message
|
||||||
|
FROM task_delegations d
|
||||||
|
JOIN tasks t ON t.id = d.task_id
|
||||||
|
${whereClause}
|
||||||
|
ORDER BY d.started_at DESC
|
||||||
|
LIMIT $${paramIndex}`,
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
if (delegations.length === 0) {
|
||||||
|
return `No delegations found${status ? ` with status '${status}'` : ''}${backend ? ` for backend '${backend}'` : ''}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = `Delegations${status ? ` (${status})` : ''}${backend ? ` [${backend}]` : ''}:\n\n`;
|
||||||
|
|
||||||
|
for (const d of delegations) {
|
||||||
|
const icon = d.status === 'success' ? '✓' : d.status === 'failed' ? '✗' : '○';
|
||||||
|
const quality = d.quality_score !== null ? `Q:${d.quality_score}` : 'Q:-';
|
||||||
|
const titleShort = d.title.length > 30 ? d.title.substring(0, 27) + '...' : d.title;
|
||||||
|
|
||||||
|
output += `${icon} ${d.task_id}: ${titleShort}\n`;
|
||||||
|
output += ` ${d.delegation_id.substring(0, 8)} [${d.backend}] ${quality} (${d.started_at})\n`;
|
||||||
|
|
||||||
|
if (d.error_message) {
|
||||||
|
output += ` Error: ${d.error_message.substring(0, 50)}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get recent delegations for a task (for embedding in taskShow)
|
||||||
|
* Returns formatted string or empty string if no delegations
|
||||||
|
*/
|
||||||
|
export async function getRecentDelegations(taskId: string): Promise<string> {
|
||||||
|
const delegations = await query<TaskDelegation>(
|
||||||
|
`SELECT delegation_id, backend, quality_score, status,
|
||||||
|
to_char(started_at, 'YYYY-MM-DD HH24:MI') as started_at
|
||||||
|
FROM task_delegations
|
||||||
|
WHERE task_id = $1
|
||||||
|
ORDER BY started_at DESC
|
||||||
|
LIMIT 5`,
|
||||||
|
[taskId]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (delegations.length === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = '\n**Recent Delegations:**\n';
|
||||||
|
|
||||||
|
for (const d of delegations) {
|
||||||
|
const icon = d.status === 'success' ? '✓' : d.status === 'failed' ? '✗' : '○';
|
||||||
|
const quality = d.quality_score !== null ? `Q:${d.quality_score}` : 'Q:-';
|
||||||
|
output += ` ${icon} ${d.delegation_id.substring(0, 8)} [${d.backend}] ${quality} (${d.started_at})\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
@@ -197,4 +197,29 @@ export const toolDefinitions = [
|
|||||||
required: ['task_id', 'epic_id'],
|
required: ['task_id', 'epic_id'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Delegation Tools
|
||||||
|
{
|
||||||
|
name: 'task_delegations',
|
||||||
|
description: 'List delegations for a specific task (quality scores, backends, status)',
|
||||||
|
inputSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
task_id: { type: 'string', description: 'Task ID (e.g., ST-123)' },
|
||||||
|
},
|
||||||
|
required: ['task_id'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'task_delegation_query',
|
||||||
|
description: 'Query delegations across tasks (filter by status, backend)',
|
||||||
|
inputSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
status: { type: 'string', enum: ['pending', 'success', 'failed', 'partial'], description: 'Filter by delegation status' },
|
||||||
|
backend: { type: 'string', description: 'Filter by backend (e.g., grok, haiku)' },
|
||||||
|
limit: { type: 'number', description: 'Max results (default: 20)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user