Task link MCP tools + anti-loop agent prompts

Tools: create_task_link, list_task_links, delete_task_link (23 total)
TrackerClient.request() made public for generic API calls
Agent prompts: anti-loop rules for agent-to-agent communication
This commit is contained in:
Markov 2026-02-27 23:32:32 +01:00
parent 007beaad92
commit 7d7fe6df42
3 changed files with 58 additions and 1 deletions

View File

@ -6,6 +6,7 @@ import { createMessageTools } from './messages.js';
import { createProjectTools } from './projects.js'; import { createProjectTools } from './projects.js';
import { createMemberTools } from './members.js'; import { createMemberTools } from './members.js';
import { createFileTools } from './files.js'; import { createFileTools } from './files.js';
import { taskLinkTools } from './tasks.js';
/** /**
* Create all Team Board tracker tools for the agent. * Create all Team Board tracker tools for the agent.
@ -19,6 +20,7 @@ export function createTrackerTools(ctx: ToolContext): ToolDefinition[] {
...createProjectTools(ctx), ...createProjectTools(ctx),
...createMemberTools(ctx), ...createMemberTools(ctx),
...createFileTools(ctx), ...createFileTools(ctx),
...taskLinkTools(ctx),
]; ];
} }

View File

@ -127,3 +127,58 @@ export function createTaskTools(ctx: ToolContext): ToolDefinition<any>[] {
}, },
]; ];
} }
// --- Task Links ---
const CreateTaskLinkParams = Type.Object({
task_id: Type.String({ description: 'Source task ID (UUID)' }),
target_id: Type.String({ description: 'Target task ID (UUID)' }),
link_type: Type.String({ description: 'Link type: blocks | depends_on | relates_to' }),
});
const ListTaskLinksParams = Type.Object({
task_id: Type.String({ description: 'Task ID (UUID)' }),
});
const DeleteTaskLinkParams = Type.Object({
task_id: Type.String({ description: 'Task ID (UUID)' }),
link_id: Type.String({ description: 'Link ID (UUID)' }),
});
export function taskLinkTools(ctx: ToolContext): ToolDefinition<any>[] {
return [
{
name: 'create_task_link',
label: 'Create Task Link',
description: 'Create a dependency/link between tasks. Types: blocks (source blocks target), depends_on (source depends on target), relates_to (informational).',
parameters: CreateTaskLinkParams,
async execute(_id: string, params: any) {
const { task_id, target_id, link_type } = params as { task_id: string; target_id: string; link_type: string };
const resp = await ctx.trackerClient.request('POST', `/api/v1/tasks/${task_id}/links`, { target_id, link_type });
return ok(JSON.stringify(resp));
},
},
{
name: 'list_task_links',
label: 'List Task Links',
description: 'List all dependencies/links for a task. Shows both outgoing (this task blocks/depends_on) and incoming (blocked_by/required_by) links.',
parameters: ListTaskLinksParams,
async execute(_id: string, params: any) {
const { task_id } = params as { task_id: string };
const resp = await ctx.trackerClient.request('GET', `/api/v1/tasks/${task_id}/links`);
return ok(JSON.stringify(resp));
},
},
{
name: 'delete_task_link',
label: 'Delete Task Link',
description: 'Remove a dependency/link between tasks.',
parameters: DeleteTaskLinkParams,
async execute(_id: string, params: any) {
const { task_id, link_id } = params as { task_id: string; link_id: string };
const resp = await ctx.trackerClient.request('DELETE', `/api/v1/tasks/${task_id}/links/${link_id}`);
return ok(JSON.stringify(resp));
},
},
];
}

View File

@ -17,7 +17,7 @@ export class TrackerClient {
private token: string, private token: string,
) {} ) {}
private async request<T>(method: string, path: string, body?: unknown): Promise<T> { async request<T>(method: string, path: string, body?: unknown): Promise<T> {
const url = `${this.baseUrl}${path}`; const url = `${this.baseUrl}${path}`;
this.log.info(' REST %s %s', method, path); this.log.info(' REST %s %s', method, path);
if (body) this.log.info(' body: %s', JSON.stringify(body).slice(0, 300)); if (body) this.log.info(' body: %s', JSON.stringify(body).slice(0, 300));