@hasna/todos 0.9.40 → 0.9.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli/index.js +1538 -1383
  2. package/dist/mcp/index.js +1538 -1383
  3. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -9039,6 +9039,13 @@ var init_zod = __esm(() => {
9039
9039
  var exports_mcp = {};
9040
9040
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9041
9041
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9042
+ function shouldRegisterTool(name) {
9043
+ if (TODOS_PROFILE === "minimal")
9044
+ return MINIMAL_TOOLS.has(name);
9045
+ if (TODOS_PROFILE === "standard")
9046
+ return !STANDARD_EXCLUDED.has(name);
9047
+ return true;
9048
+ }
9042
9049
  function formatError(error) {
9043
9050
  if (error instanceof VersionConflictError) {
9044
9051
  return JSON.stringify({ code: VersionConflictError.code, message: error.message, suggestion: VersionConflictError.suggestion });
@@ -9135,7 +9142,7 @@ async function main() {
9135
9142
  const transport = new StdioServerTransport;
9136
9143
  await server.connect(transport);
9137
9144
  }
9138
- var server;
9145
+ var server, TODOS_PROFILE, MINIMAL_TOOLS, STANDARD_EXCLUDED;
9139
9146
  var init_mcp = __esm(() => {
9140
9147
  init_zod();
9141
9148
  init_tasks();
@@ -9153,1596 +9160,1744 @@ var init_mcp = __esm(() => {
9153
9160
  name: "todos",
9154
9161
  version: "0.9.35"
9155
9162
  });
9156
- server.tool("create_task", "Create a new task", {
9157
- title: exports_external.string(),
9158
- description: exports_external.string().optional(),
9159
- project_id: exports_external.string().optional(),
9160
- parent_id: exports_external.string().optional(),
9161
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
9162
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
9163
- agent_id: exports_external.string().optional(),
9164
- assigned_to: exports_external.string().optional(),
9165
- session_id: exports_external.string().optional(),
9166
- working_dir: exports_external.string().optional(),
9167
- plan_id: exports_external.string().optional(),
9168
- task_list_id: exports_external.string().optional(),
9169
- tags: exports_external.array(exports_external.string()).optional(),
9170
- metadata: exports_external.record(exports_external.unknown()).optional(),
9171
- estimated_minutes: exports_external.number().optional(),
9172
- requires_approval: exports_external.boolean().optional(),
9173
- recurrence_rule: exports_external.string().optional()
9174
- }, async (params) => {
9175
- try {
9176
- const resolved = { ...params };
9177
- if (resolved.project_id)
9178
- resolved.project_id = resolveId(resolved.project_id, "projects");
9179
- if (resolved.parent_id)
9180
- resolved.parent_id = resolveId(resolved.parent_id);
9181
- if (resolved.plan_id)
9182
- resolved.plan_id = resolveId(resolved.plan_id, "plans");
9183
- if (resolved.task_list_id)
9184
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
9185
- const task = createTask(resolved);
9186
- return { content: [{ type: "text", text: `created: ${formatTask(task)}` }] };
9187
- } catch (e) {
9188
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9189
- }
9190
- });
9191
- server.tool("list_tasks", "List tasks with optional filters and pagination.", {
9192
- project_id: exports_external.string().optional(),
9193
- status: exports_external.union([
9194
- exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
9195
- exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
9196
- ]).optional(),
9197
- priority: exports_external.union([
9198
- exports_external.enum(["low", "medium", "high", "critical"]),
9199
- exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
9200
- ]).optional(),
9201
- assigned_to: exports_external.string().optional(),
9202
- tags: exports_external.array(exports_external.string()).optional(),
9203
- plan_id: exports_external.string().optional(),
9204
- task_list_id: exports_external.string().optional(),
9205
- has_recurrence: exports_external.boolean().optional(),
9206
- limit: exports_external.number().optional(),
9207
- offset: exports_external.number().optional()
9208
- }, async (params) => {
9209
- try {
9210
- const resolved = { ...params };
9211
- if (resolved.project_id)
9212
- resolved.project_id = resolveId(resolved.project_id, "projects");
9213
- if (resolved.plan_id)
9214
- resolved.plan_id = resolveId(resolved.plan_id, "plans");
9215
- if (resolved.task_list_id)
9216
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
9217
- const tasks = listTasks(resolved);
9218
- const { limit: _limit, offset: _offset, ...countFilter } = resolved;
9219
- const total = countTasks(countFilter);
9220
- if (tasks.length === 0) {
9221
- return { content: [{ type: "text", text: total > 0 ? `No tasks in this page (total: ${total}).` : "No tasks found." }] };
9222
- }
9223
- const text = tasks.map((t) => {
9224
- const lock = t.locked_by ? ` [locked by ${t.locked_by}]` : "";
9225
- const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
9226
- return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned}${lock}`;
9227
- }).join(`
9163
+ TODOS_PROFILE = (process.env["TODOS_PROFILE"] || "full").toLowerCase();
9164
+ MINIMAL_TOOLS = new Set([
9165
+ "claim_next_task",
9166
+ "complete_task",
9167
+ "fail_task",
9168
+ "get_status",
9169
+ "get_task",
9170
+ "start_task",
9171
+ "add_comment",
9172
+ "get_next_task"
9173
+ ]);
9174
+ STANDARD_EXCLUDED = new Set([
9175
+ "get_org_chart",
9176
+ "set_reports_to",
9177
+ "rename_agent",
9178
+ "delete_agent",
9179
+ "create_webhook",
9180
+ "list_webhooks",
9181
+ "delete_webhook",
9182
+ "create_template",
9183
+ "list_templates",
9184
+ "create_task_from_template",
9185
+ "delete_template",
9186
+ "approve_task"
9187
+ ]);
9188
+ if (shouldRegisterTool("create_task")) {
9189
+ server.tool("create_task", "Create a new task", {
9190
+ title: exports_external.string(),
9191
+ description: exports_external.string().optional(),
9192
+ project_id: exports_external.string().optional(),
9193
+ parent_id: exports_external.string().optional(),
9194
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
9195
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
9196
+ agent_id: exports_external.string().optional(),
9197
+ assigned_to: exports_external.string().optional(),
9198
+ session_id: exports_external.string().optional(),
9199
+ working_dir: exports_external.string().optional(),
9200
+ plan_id: exports_external.string().optional(),
9201
+ task_list_id: exports_external.string().optional(),
9202
+ tags: exports_external.array(exports_external.string()).optional(),
9203
+ metadata: exports_external.record(exports_external.unknown()).optional(),
9204
+ estimated_minutes: exports_external.number().optional(),
9205
+ requires_approval: exports_external.boolean().optional(),
9206
+ recurrence_rule: exports_external.string().optional()
9207
+ }, async (params) => {
9208
+ try {
9209
+ const resolved = { ...params };
9210
+ if (resolved.project_id)
9211
+ resolved.project_id = resolveId(resolved.project_id, "projects");
9212
+ if (resolved.parent_id)
9213
+ resolved.parent_id = resolveId(resolved.parent_id);
9214
+ if (resolved.plan_id)
9215
+ resolved.plan_id = resolveId(resolved.plan_id, "plans");
9216
+ if (resolved.task_list_id)
9217
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
9218
+ const task = createTask(resolved);
9219
+ return { content: [{ type: "text", text: `created: ${formatTask(task)}` }] };
9220
+ } catch (e) {
9221
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9222
+ }
9223
+ });
9224
+ }
9225
+ if (shouldRegisterTool("list_tasks")) {
9226
+ server.tool("list_tasks", "List tasks with optional filters and pagination.", {
9227
+ project_id: exports_external.string().optional(),
9228
+ status: exports_external.union([
9229
+ exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
9230
+ exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
9231
+ ]).optional(),
9232
+ priority: exports_external.union([
9233
+ exports_external.enum(["low", "medium", "high", "critical"]),
9234
+ exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
9235
+ ]).optional(),
9236
+ assigned_to: exports_external.string().optional(),
9237
+ tags: exports_external.array(exports_external.string()).optional(),
9238
+ plan_id: exports_external.string().optional(),
9239
+ task_list_id: exports_external.string().optional(),
9240
+ has_recurrence: exports_external.boolean().optional(),
9241
+ limit: exports_external.number().optional(),
9242
+ offset: exports_external.number().optional()
9243
+ }, async (params) => {
9244
+ try {
9245
+ const resolved = { ...params };
9246
+ if (resolved.project_id)
9247
+ resolved.project_id = resolveId(resolved.project_id, "projects");
9248
+ if (resolved.plan_id)
9249
+ resolved.plan_id = resolveId(resolved.plan_id, "plans");
9250
+ if (resolved.task_list_id)
9251
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
9252
+ const tasks = listTasks(resolved);
9253
+ const { limit: _limit, offset: _offset, ...countFilter } = resolved;
9254
+ const total = countTasks(countFilter);
9255
+ if (tasks.length === 0) {
9256
+ return { content: [{ type: "text", text: total > 0 ? `No tasks in this page (total: ${total}).` : "No tasks found." }] };
9257
+ }
9258
+ const text = tasks.map((t) => {
9259
+ const lock = t.locked_by ? ` [locked by ${t.locked_by}]` : "";
9260
+ const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
9261
+ return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned}${lock}`;
9262
+ }).join(`
9228
9263
  `);
9229
- const pagination = resolved.limit ? `
9264
+ const pagination = resolved.limit ? `
9230
9265
  (showing ${tasks.length} of ${total}, offset: ${resolved.offset || 0})` : "";
9231
- return { content: [{ type: "text", text: `${tasks.length} task(s):
9266
+ return { content: [{ type: "text", text: `${tasks.length} task(s):
9232
9267
  ${text}${pagination}` }] };
9233
- } catch (e) {
9234
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9235
- }
9236
- });
9237
- server.tool("get_task", "Get full task details with subtasks, deps, and comments.", {
9238
- id: exports_external.string()
9239
- }, async ({ id }) => {
9240
- try {
9241
- const resolvedId = resolveId(id);
9242
- const task = getTaskWithRelations(resolvedId);
9243
- if (!task)
9244
- return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
9245
- const parts = [formatTaskDetail(task)];
9246
- if (task.subtasks.length > 0) {
9247
- parts.push(`
9268
+ } catch (e) {
9269
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9270
+ }
9271
+ });
9272
+ }
9273
+ if (shouldRegisterTool("get_task")) {
9274
+ server.tool("get_task", "Get full task details with subtasks, deps, and comments.", {
9275
+ id: exports_external.string()
9276
+ }, async ({ id }) => {
9277
+ try {
9278
+ const resolvedId = resolveId(id);
9279
+ const task = getTaskWithRelations(resolvedId);
9280
+ if (!task)
9281
+ return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
9282
+ const parts = [formatTaskDetail(task)];
9283
+ if (task.subtasks.length > 0) {
9284
+ parts.push(`
9248
9285
  Subtasks (${task.subtasks.length}):`);
9249
- for (const st of task.subtasks) {
9250
- parts.push(` [${st.status}] ${st.id.slice(0, 8)} | ${st.title}`);
9286
+ for (const st of task.subtasks) {
9287
+ parts.push(` [${st.status}] ${st.id.slice(0, 8)} | ${st.title}`);
9288
+ }
9251
9289
  }
9252
- }
9253
- if (task.dependencies.length > 0) {
9254
- parts.push(`
9290
+ if (task.dependencies.length > 0) {
9291
+ parts.push(`
9255
9292
  Depends on (${task.dependencies.length}):`);
9256
- for (const dep of task.dependencies) {
9257
- parts.push(` [${dep.status}] ${dep.id.slice(0, 8)} | ${dep.title}`);
9293
+ for (const dep of task.dependencies) {
9294
+ parts.push(` [${dep.status}] ${dep.id.slice(0, 8)} | ${dep.title}`);
9295
+ }
9258
9296
  }
9259
- }
9260
- if (task.blocked_by.length > 0) {
9261
- parts.push(`
9297
+ if (task.blocked_by.length > 0) {
9298
+ parts.push(`
9262
9299
  Blocks (${task.blocked_by.length}):`);
9263
- for (const b of task.blocked_by) {
9264
- parts.push(` [${b.status}] ${b.id.slice(0, 8)} | ${b.title}`);
9300
+ for (const b of task.blocked_by) {
9301
+ parts.push(` [${b.status}] ${b.id.slice(0, 8)} | ${b.title}`);
9302
+ }
9265
9303
  }
9266
- }
9267
- if (task.comments.length > 0) {
9268
- parts.push(`
9304
+ if (task.comments.length > 0) {
9305
+ parts.push(`
9269
9306
  Comments (${task.comments.length}):`);
9270
- for (const c of task.comments) {
9271
- const agent = c.agent_id ? `[${c.agent_id}] ` : "";
9272
- parts.push(` ${agent}${c.created_at}: ${c.content}`);
9307
+ for (const c of task.comments) {
9308
+ const agent = c.agent_id ? `[${c.agent_id}] ` : "";
9309
+ parts.push(` ${agent}${c.created_at}: ${c.content}`);
9310
+ }
9273
9311
  }
9274
- }
9275
- if (task.parent) {
9276
- parts.push(`
9312
+ if (task.parent) {
9313
+ parts.push(`
9277
9314
  Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
9278
- }
9279
- return { content: [{ type: "text", text: parts.join(`
9315
+ }
9316
+ return { content: [{ type: "text", text: parts.join(`
9280
9317
  `) }] };
9281
- } catch (e) {
9282
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9283
- }
9284
- });
9285
- server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
9286
- id: exports_external.string(),
9287
- version: exports_external.number(),
9288
- title: exports_external.string().optional(),
9289
- description: exports_external.string().optional(),
9290
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
9291
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
9292
- assigned_to: exports_external.string().optional(),
9293
- tags: exports_external.array(exports_external.string()).optional(),
9294
- metadata: exports_external.record(exports_external.unknown()).optional(),
9295
- plan_id: exports_external.string().optional(),
9296
- task_list_id: exports_external.string().optional()
9297
- }, async ({ id, ...rest }) => {
9298
- try {
9299
- const resolvedId = resolveId(id);
9300
- const task = updateTask(resolvedId, rest);
9301
- return { content: [{ type: "text", text: `updated: ${formatTask(task)}` }] };
9302
- } catch (e) {
9303
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9304
- }
9305
- });
9306
- server.tool("delete_task", "Delete a task permanently. Subtasks cascade-deleted.", {
9307
- id: exports_external.string()
9308
- }, async ({ id }) => {
9309
- try {
9310
- const resolvedId = resolveId(id);
9311
- const deleted = deleteTask(resolvedId);
9312
- return {
9313
- content: [{
9314
- type: "text",
9315
- text: deleted ? `Task ${id} deleted.` : `Task ${id} not found.`
9316
- }]
9317
- };
9318
- } catch (e) {
9319
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9320
- }
9321
- });
9322
- server.tool("start_task", "Claim, lock, and set task to in_progress.", {
9323
- id: exports_external.string(),
9324
- agent_id: exports_external.string()
9325
- }, async ({ id, agent_id }) => {
9326
- try {
9327
- const resolvedId = resolveId(id);
9328
- const task = startTask(resolvedId, agent_id);
9329
- return { content: [{ type: "text", text: `started: ${formatTask(task)}` }] };
9330
- } catch (e) {
9331
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9332
- }
9333
- });
9334
- server.tool("complete_task", "Complete a task. For recurring tasks, auto-spawns next instance.", {
9335
- id: exports_external.string(),
9336
- agent_id: exports_external.string().optional(),
9337
- skip_recurrence: exports_external.boolean().optional(),
9338
- files_changed: exports_external.array(exports_external.string()).optional().describe("List of files changed as part of completing this task"),
9339
- test_results: exports_external.string().optional().describe("Summary of test results"),
9340
- commit_hash: exports_external.string().optional().describe("Git commit hash associated with this completion"),
9341
- notes: exports_external.string().optional().describe("Notes about the completion"),
9342
- attachment_ids: exports_external.array(exports_external.string()).optional().describe("IDs of attachments uploaded via @hasna/attachments to link as evidence")
9343
- }, async ({ id, agent_id, skip_recurrence, files_changed, test_results, commit_hash, notes, attachment_ids }) => {
9344
- try {
9345
- const resolvedId = resolveId(id);
9346
- const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
9347
- const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, ...evidence });
9348
- let text = `completed: ${formatTask(task)}`;
9349
- if (task.metadata._next_recurrence) {
9350
- const next = task.metadata._next_recurrence;
9351
- text += `
9318
+ } catch (e) {
9319
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9320
+ }
9321
+ });
9322
+ }
9323
+ if (shouldRegisterTool("update_task")) {
9324
+ server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
9325
+ id: exports_external.string(),
9326
+ version: exports_external.number(),
9327
+ title: exports_external.string().optional(),
9328
+ description: exports_external.string().optional(),
9329
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
9330
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
9331
+ assigned_to: exports_external.string().optional(),
9332
+ tags: exports_external.array(exports_external.string()).optional(),
9333
+ metadata: exports_external.record(exports_external.unknown()).optional(),
9334
+ plan_id: exports_external.string().optional(),
9335
+ task_list_id: exports_external.string().optional()
9336
+ }, async ({ id, ...rest }) => {
9337
+ try {
9338
+ const resolvedId = resolveId(id);
9339
+ const task = updateTask(resolvedId, rest);
9340
+ return { content: [{ type: "text", text: `updated: ${formatTask(task)}` }] };
9341
+ } catch (e) {
9342
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9343
+ }
9344
+ });
9345
+ }
9346
+ if (shouldRegisterTool("delete_task")) {
9347
+ server.tool("delete_task", "Delete a task permanently. Subtasks cascade-deleted.", {
9348
+ id: exports_external.string()
9349
+ }, async ({ id }) => {
9350
+ try {
9351
+ const resolvedId = resolveId(id);
9352
+ const deleted = deleteTask(resolvedId);
9353
+ return {
9354
+ content: [{
9355
+ type: "text",
9356
+ text: deleted ? `Task ${id} deleted.` : `Task ${id} not found.`
9357
+ }]
9358
+ };
9359
+ } catch (e) {
9360
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9361
+ }
9362
+ });
9363
+ }
9364
+ if (shouldRegisterTool("start_task")) {
9365
+ server.tool("start_task", "Claim, lock, and set task to in_progress.", {
9366
+ id: exports_external.string(),
9367
+ agent_id: exports_external.string()
9368
+ }, async ({ id, agent_id }) => {
9369
+ try {
9370
+ const resolvedId = resolveId(id);
9371
+ const task = startTask(resolvedId, agent_id);
9372
+ return { content: [{ type: "text", text: `started: ${formatTask(task)}` }] };
9373
+ } catch (e) {
9374
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9375
+ }
9376
+ });
9377
+ }
9378
+ if (shouldRegisterTool("complete_task")) {
9379
+ server.tool("complete_task", "Complete a task. For recurring tasks, auto-spawns next instance.", {
9380
+ id: exports_external.string(),
9381
+ agent_id: exports_external.string().optional(),
9382
+ skip_recurrence: exports_external.boolean().optional(),
9383
+ files_changed: exports_external.array(exports_external.string()).optional().describe("List of files changed as part of completing this task"),
9384
+ test_results: exports_external.string().optional().describe("Summary of test results"),
9385
+ commit_hash: exports_external.string().optional().describe("Git commit hash associated with this completion"),
9386
+ notes: exports_external.string().optional().describe("Notes about the completion"),
9387
+ attachment_ids: exports_external.array(exports_external.string()).optional().describe("IDs of attachments uploaded via @hasna/attachments to link as evidence")
9388
+ }, async ({ id, agent_id, skip_recurrence, files_changed, test_results, commit_hash, notes, attachment_ids }) => {
9389
+ try {
9390
+ const resolvedId = resolveId(id);
9391
+ const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
9392
+ const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, ...evidence });
9393
+ let text = `completed: ${formatTask(task)}`;
9394
+ if (task.metadata._next_recurrence) {
9395
+ const next = task.metadata._next_recurrence;
9396
+ text += `
9352
9397
  next: ${next.short_id || next.id.slice(0, 8)} due ${next.due_at}`;
9398
+ }
9399
+ return { content: [{ type: "text", text }] };
9400
+ } catch (e) {
9401
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9353
9402
  }
9354
- return { content: [{ type: "text", text }] };
9355
- } catch (e) {
9356
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9357
- }
9358
- });
9359
- server.tool("lock_task", "Acquire exclusive lock. Expires after 30 min. Idempotent per agent.", {
9360
- id: exports_external.string(),
9361
- agent_id: exports_external.string()
9362
- }, async ({ id, agent_id }) => {
9363
- try {
9364
- const resolvedId = resolveId(id);
9365
- const result = lockTask(resolvedId, agent_id);
9366
- if (result.success) {
9367
- return { content: [{ type: "text", text: `Lock acquired by ${agent_id} at ${result.locked_at}` }] };
9403
+ });
9404
+ }
9405
+ if (shouldRegisterTool("lock_task")) {
9406
+ server.tool("lock_task", "Acquire exclusive lock. Expires after 30 min. Idempotent per agent.", {
9407
+ id: exports_external.string(),
9408
+ agent_id: exports_external.string()
9409
+ }, async ({ id, agent_id }) => {
9410
+ try {
9411
+ const resolvedId = resolveId(id);
9412
+ const result = lockTask(resolvedId, agent_id);
9413
+ if (result.success) {
9414
+ return { content: [{ type: "text", text: `Lock acquired by ${agent_id} at ${result.locked_at}` }] };
9415
+ }
9416
+ return { content: [{ type: "text", text: `Lock failed: ${result.error}` }], isError: true };
9417
+ } catch (e) {
9418
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9368
9419
  }
9369
- return { content: [{ type: "text", text: `Lock failed: ${result.error}` }], isError: true };
9370
- } catch (e) {
9371
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9372
- }
9373
- });
9374
- server.tool("unlock_task", "Release exclusive lock on a task.", {
9375
- id: exports_external.string(),
9376
- agent_id: exports_external.string().optional()
9377
- }, async ({ id, agent_id }) => {
9378
- try {
9379
- const resolvedId = resolveId(id);
9380
- unlockTask(resolvedId, agent_id);
9381
- return { content: [{ type: "text", text: `Lock released on task ${id}` }] };
9382
- } catch (e) {
9383
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9384
- }
9385
- });
9386
- server.tool("add_dependency", "Add a dependency. Prevents cycles via BFS detection.", {
9387
- task_id: exports_external.string(),
9388
- depends_on: exports_external.string()
9389
- }, async ({ task_id, depends_on }) => {
9390
- try {
9391
- const resolvedTaskId = resolveId(task_id);
9392
- const resolvedDepsOn = resolveId(depends_on);
9393
- addDependency(resolvedTaskId, resolvedDepsOn);
9394
- return { content: [{ type: "text", text: `Dependency added: ${task_id} depends on ${depends_on}` }] };
9395
- } catch (e) {
9396
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9397
- }
9398
- });
9399
- server.tool("remove_dependency", "Remove a dependency link between two tasks.", {
9400
- task_id: exports_external.string(),
9401
- depends_on: exports_external.string()
9402
- }, async ({ task_id, depends_on }) => {
9403
- try {
9404
- const resolvedTaskId = resolveId(task_id);
9405
- const resolvedDepsOn = resolveId(depends_on);
9406
- const removed = removeDependency(resolvedTaskId, resolvedDepsOn);
9407
- return {
9408
- content: [{
9409
- type: "text",
9410
- text: removed ? `Dependency removed.` : `Dependency not found.`
9411
- }]
9412
- };
9413
- } catch (e) {
9414
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9415
- }
9416
- });
9417
- server.tool("add_comment", "Add a comment or note to a task. Comments are append-only.", {
9418
- task_id: exports_external.string(),
9419
- content: exports_external.string(),
9420
- agent_id: exports_external.string().optional(),
9421
- session_id: exports_external.string().optional()
9422
- }, async ({ task_id, ...rest }) => {
9423
- try {
9424
- const resolvedId = resolveId(task_id);
9425
- const comment = addComment({ task_id: resolvedId, ...rest });
9426
- return { content: [{ type: "text", text: `Comment added (${comment.id.slice(0, 8)}) at ${comment.created_at}` }] };
9427
- } catch (e) {
9428
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9429
- }
9430
- });
9431
- server.tool("list_projects", "List all registered projects", {}, async () => {
9432
- try {
9433
- const projects = listProjects();
9434
- if (projects.length === 0) {
9435
- return { content: [{ type: "text", text: "No projects registered." }] };
9436
- }
9437
- const text = projects.map((p) => {
9438
- const taskList = p.task_list_id ? ` [${p.task_list_id}]` : "";
9439
- return `${p.id.slice(0, 8)} | ${p.name} | ${p.path}${taskList}${p.description ? ` - ${p.description}` : ""}`;
9440
- }).join(`
9420
+ });
9421
+ }
9422
+ if (shouldRegisterTool("unlock_task")) {
9423
+ server.tool("unlock_task", "Release exclusive lock on a task.", {
9424
+ id: exports_external.string(),
9425
+ agent_id: exports_external.string().optional()
9426
+ }, async ({ id, agent_id }) => {
9427
+ try {
9428
+ const resolvedId = resolveId(id);
9429
+ unlockTask(resolvedId, agent_id);
9430
+ return { content: [{ type: "text", text: `Lock released on task ${id}` }] };
9431
+ } catch (e) {
9432
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9433
+ }
9434
+ });
9435
+ }
9436
+ if (shouldRegisterTool("add_dependency")) {
9437
+ server.tool("add_dependency", "Add a dependency. Prevents cycles via BFS detection.", {
9438
+ task_id: exports_external.string(),
9439
+ depends_on: exports_external.string()
9440
+ }, async ({ task_id, depends_on }) => {
9441
+ try {
9442
+ const resolvedTaskId = resolveId(task_id);
9443
+ const resolvedDepsOn = resolveId(depends_on);
9444
+ addDependency(resolvedTaskId, resolvedDepsOn);
9445
+ return { content: [{ type: "text", text: `Dependency added: ${task_id} depends on ${depends_on}` }] };
9446
+ } catch (e) {
9447
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9448
+ }
9449
+ });
9450
+ }
9451
+ if (shouldRegisterTool("remove_dependency")) {
9452
+ server.tool("remove_dependency", "Remove a dependency link between two tasks.", {
9453
+ task_id: exports_external.string(),
9454
+ depends_on: exports_external.string()
9455
+ }, async ({ task_id, depends_on }) => {
9456
+ try {
9457
+ const resolvedTaskId = resolveId(task_id);
9458
+ const resolvedDepsOn = resolveId(depends_on);
9459
+ const removed = removeDependency(resolvedTaskId, resolvedDepsOn);
9460
+ return {
9461
+ content: [{
9462
+ type: "text",
9463
+ text: removed ? `Dependency removed.` : `Dependency not found.`
9464
+ }]
9465
+ };
9466
+ } catch (e) {
9467
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9468
+ }
9469
+ });
9470
+ }
9471
+ if (shouldRegisterTool("add_comment")) {
9472
+ server.tool("add_comment", "Add a comment or note to a task. Comments are append-only.", {
9473
+ task_id: exports_external.string(),
9474
+ content: exports_external.string(),
9475
+ agent_id: exports_external.string().optional(),
9476
+ session_id: exports_external.string().optional()
9477
+ }, async ({ task_id, ...rest }) => {
9478
+ try {
9479
+ const resolvedId = resolveId(task_id);
9480
+ const comment = addComment({ task_id: resolvedId, ...rest });
9481
+ return { content: [{ type: "text", text: `Comment added (${comment.id.slice(0, 8)}) at ${comment.created_at}` }] };
9482
+ } catch (e) {
9483
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9484
+ }
9485
+ });
9486
+ }
9487
+ if (shouldRegisterTool("list_projects")) {
9488
+ server.tool("list_projects", "List all registered projects", {}, async () => {
9489
+ try {
9490
+ const projects = listProjects();
9491
+ if (projects.length === 0) {
9492
+ return { content: [{ type: "text", text: "No projects registered." }] };
9493
+ }
9494
+ const text = projects.map((p) => {
9495
+ const taskList = p.task_list_id ? ` [${p.task_list_id}]` : "";
9496
+ return `${p.id.slice(0, 8)} | ${p.name} | ${p.path}${taskList}${p.description ? ` - ${p.description}` : ""}`;
9497
+ }).join(`
9441
9498
  `);
9442
- return { content: [{ type: "text", text: `${projects.length} project(s):
9499
+ return { content: [{ type: "text", text: `${projects.length} project(s):
9443
9500
  ${text}` }] };
9444
- } catch (e) {
9445
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9446
- }
9447
- });
9448
- server.tool("create_project", "Register a new project with auto-generated task prefix.", {
9449
- name: exports_external.string(),
9450
- path: exports_external.string(),
9451
- description: exports_external.string().optional(),
9452
- task_list_id: exports_external.string().optional()
9453
- }, async (params) => {
9454
- try {
9455
- const project = createProject(params);
9456
- const taskList = project.task_list_id ? ` [${project.task_list_id}]` : "";
9457
- return {
9458
- content: [{
9459
- type: "text",
9460
- text: `Project created: ${project.id.slice(0, 8)} | ${project.name} | ${project.path}${taskList}`
9461
- }]
9462
- };
9463
- } catch (e) {
9464
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9465
- }
9466
- });
9467
- server.tool("create_plan", "Create a plan to group related tasks.", {
9468
- name: exports_external.string(),
9469
- project_id: exports_external.string().optional(),
9470
- description: exports_external.string().optional(),
9471
- status: exports_external.enum(["active", "completed", "archived"]).optional(),
9472
- task_list_id: exports_external.string().optional(),
9473
- agent_id: exports_external.string().optional()
9474
- }, async (params) => {
9475
- try {
9476
- const resolved = { ...params };
9477
- if (resolved.project_id)
9478
- resolved.project_id = resolveId(resolved.project_id, "projects");
9479
- if (resolved.task_list_id)
9480
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
9481
- const plan = createPlan(resolved);
9482
- return {
9483
- content: [{
9484
- type: "text",
9485
- text: `Plan created: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
9486
- }]
9487
- };
9488
- } catch (e) {
9489
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9490
- }
9491
- });
9492
- server.tool("list_plans", "List all plans, optionally filtered by project.", {
9493
- project_id: exports_external.string().optional()
9494
- }, async ({ project_id }) => {
9495
- try {
9496
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
9497
- const plans = listPlans(resolvedProjectId);
9498
- if (plans.length === 0) {
9499
- return { content: [{ type: "text", text: "No plans found." }] };
9500
- }
9501
- const text = plans.map((p) => {
9502
- const project = p.project_id ? ` (project: ${p.project_id.slice(0, 8)})` : "";
9503
- return `[${p.status}] ${p.id.slice(0, 8)} | ${p.name}${project}`;
9504
- }).join(`
9501
+ } catch (e) {
9502
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9503
+ }
9504
+ });
9505
+ }
9506
+ if (shouldRegisterTool("create_project")) {
9507
+ server.tool("create_project", "Register a new project with auto-generated task prefix.", {
9508
+ name: exports_external.string(),
9509
+ path: exports_external.string(),
9510
+ description: exports_external.string().optional(),
9511
+ task_list_id: exports_external.string().optional()
9512
+ }, async (params) => {
9513
+ try {
9514
+ const project = createProject(params);
9515
+ const taskList = project.task_list_id ? ` [${project.task_list_id}]` : "";
9516
+ return {
9517
+ content: [{
9518
+ type: "text",
9519
+ text: `Project created: ${project.id.slice(0, 8)} | ${project.name} | ${project.path}${taskList}`
9520
+ }]
9521
+ };
9522
+ } catch (e) {
9523
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9524
+ }
9525
+ });
9526
+ }
9527
+ if (shouldRegisterTool("create_plan")) {
9528
+ server.tool("create_plan", "Create a plan to group related tasks.", {
9529
+ name: exports_external.string(),
9530
+ project_id: exports_external.string().optional(),
9531
+ description: exports_external.string().optional(),
9532
+ status: exports_external.enum(["active", "completed", "archived"]).optional(),
9533
+ task_list_id: exports_external.string().optional(),
9534
+ agent_id: exports_external.string().optional()
9535
+ }, async (params) => {
9536
+ try {
9537
+ const resolved = { ...params };
9538
+ if (resolved.project_id)
9539
+ resolved.project_id = resolveId(resolved.project_id, "projects");
9540
+ if (resolved.task_list_id)
9541
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
9542
+ const plan = createPlan(resolved);
9543
+ return {
9544
+ content: [{
9545
+ type: "text",
9546
+ text: `Plan created: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
9547
+ }]
9548
+ };
9549
+ } catch (e) {
9550
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9551
+ }
9552
+ });
9553
+ }
9554
+ if (shouldRegisterTool("list_plans")) {
9555
+ server.tool("list_plans", "List all plans, optionally filtered by project.", {
9556
+ project_id: exports_external.string().optional()
9557
+ }, async ({ project_id }) => {
9558
+ try {
9559
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
9560
+ const plans = listPlans(resolvedProjectId);
9561
+ if (plans.length === 0) {
9562
+ return { content: [{ type: "text", text: "No plans found." }] };
9563
+ }
9564
+ const text = plans.map((p) => {
9565
+ const project = p.project_id ? ` (project: ${p.project_id.slice(0, 8)})` : "";
9566
+ return `[${p.status}] ${p.id.slice(0, 8)} | ${p.name}${project}`;
9567
+ }).join(`
9505
9568
  `);
9506
- return { content: [{ type: "text", text: `${plans.length} plan(s):
9569
+ return { content: [{ type: "text", text: `${plans.length} plan(s):
9507
9570
  ${text}` }] };
9508
- } catch (e) {
9509
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9510
- }
9511
- });
9512
- server.tool("get_plan", "Get plan details including status and timestamps.", {
9513
- id: exports_external.string()
9514
- }, async ({ id }) => {
9515
- try {
9516
- const resolvedId = resolveId(id, "plans");
9517
- const plan = getPlan(resolvedId);
9518
- if (!plan)
9519
- return { content: [{ type: "text", text: `Plan not found: ${id}` }], isError: true };
9520
- const parts = [
9521
- `ID: ${plan.id}`,
9522
- `Name: ${plan.name}`,
9523
- `Status: ${plan.status}`
9524
- ];
9525
- if (plan.description)
9526
- parts.push(`Description: ${plan.description}`);
9527
- if (plan.project_id)
9528
- parts.push(`Project: ${plan.project_id}`);
9529
- parts.push(`Created: ${plan.created_at}`);
9530
- parts.push(`Updated: ${plan.updated_at}`);
9531
- return { content: [{ type: "text", text: parts.join(`
9571
+ } catch (e) {
9572
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9573
+ }
9574
+ });
9575
+ }
9576
+ if (shouldRegisterTool("get_plan")) {
9577
+ server.tool("get_plan", "Get plan details including status and timestamps.", {
9578
+ id: exports_external.string()
9579
+ }, async ({ id }) => {
9580
+ try {
9581
+ const resolvedId = resolveId(id, "plans");
9582
+ const plan = getPlan(resolvedId);
9583
+ if (!plan)
9584
+ return { content: [{ type: "text", text: `Plan not found: ${id}` }], isError: true };
9585
+ const parts = [
9586
+ `ID: ${plan.id}`,
9587
+ `Name: ${plan.name}`,
9588
+ `Status: ${plan.status}`
9589
+ ];
9590
+ if (plan.description)
9591
+ parts.push(`Description: ${plan.description}`);
9592
+ if (plan.project_id)
9593
+ parts.push(`Project: ${plan.project_id}`);
9594
+ parts.push(`Created: ${plan.created_at}`);
9595
+ parts.push(`Updated: ${plan.updated_at}`);
9596
+ return { content: [{ type: "text", text: parts.join(`
9532
9597
  `) }] };
9533
- } catch (e) {
9534
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9535
- }
9536
- });
9537
- server.tool("update_plan", "Update plan fields (name, description, status).", {
9538
- id: exports_external.string(),
9539
- name: exports_external.string().optional(),
9540
- description: exports_external.string().optional(),
9541
- status: exports_external.enum(["active", "completed", "archived"]).optional(),
9542
- task_list_id: exports_external.string().optional(),
9543
- agent_id: exports_external.string().optional()
9544
- }, async ({ id, ...rest }) => {
9545
- try {
9546
- const resolvedId = resolveId(id, "plans");
9547
- const resolved = { ...rest };
9548
- if (resolved.task_list_id)
9549
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
9550
- const plan = updatePlan(resolvedId, resolved);
9551
- return {
9552
- content: [{
9553
- type: "text",
9554
- text: `Plan updated: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
9555
- }]
9556
- };
9557
- } catch (e) {
9558
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9559
- }
9560
- });
9561
- server.tool("delete_plan", "Delete a plan. Tasks in the plan are orphaned (not deleted).", {
9562
- id: exports_external.string()
9563
- }, async ({ id }) => {
9564
- try {
9565
- const resolvedId = resolveId(id, "plans");
9566
- const deleted = deletePlan(resolvedId);
9567
- return {
9568
- content: [{
9569
- type: "text",
9570
- text: deleted ? `Plan ${id} deleted.` : `Plan ${id} not found.`
9571
- }]
9572
- };
9573
- } catch (e) {
9574
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9575
- }
9576
- });
9577
- server.tool("search_tasks", "Full-text search across tasks with filters.", {
9578
- query: exports_external.string(),
9579
- project_id: exports_external.string().optional(),
9580
- task_list_id: exports_external.string().optional(),
9581
- status: exports_external.union([
9582
- exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
9583
- exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
9584
- ]).optional(),
9585
- priority: exports_external.union([
9586
- exports_external.enum(["low", "medium", "high", "critical"]),
9587
- exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
9588
- ]).optional(),
9589
- assigned_to: exports_external.string().optional(),
9590
- agent_id: exports_external.string().optional(),
9591
- created_after: exports_external.string().optional(),
9592
- updated_after: exports_external.string().optional(),
9593
- has_dependencies: exports_external.boolean().optional(),
9594
- is_blocked: exports_external.boolean().optional()
9595
- }, async ({ query, project_id, task_list_id, ...filters }) => {
9596
- try {
9597
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
9598
- const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
9599
- const tasks = searchTasks({
9600
- query,
9601
- project_id: resolvedProjectId,
9602
- task_list_id: resolvedTaskListId,
9603
- ...filters
9604
- });
9605
- if (tasks.length === 0) {
9606
- return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
9598
+ } catch (e) {
9599
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9607
9600
  }
9608
- const text = tasks.map((t) => `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}`).join(`
9609
- `);
9610
- return { content: [{ type: "text", text: `${tasks.length} result(s) for "${query}":
9611
- ${text}` }] };
9612
- } catch (e) {
9613
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9614
- }
9615
- });
9616
- server.tool("sync", "Sync tasks between local DB and agent task list.", {
9617
- task_list_id: exports_external.string().optional(),
9618
- agent: exports_external.string().optional(),
9619
- all_agents: exports_external.boolean().optional(),
9620
- project_id: exports_external.string().optional(),
9621
- direction: exports_external.enum(["push", "pull", "both"]).optional(),
9622
- prefer: exports_external.enum(["local", "remote"]).optional()
9623
- }, async ({ task_list_id, agent, all_agents, project_id, direction, prefer }) => {
9624
- try {
9625
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
9626
- const project = resolvedProjectId ? getProject(resolvedProjectId) : undefined;
9627
- const dir = direction ?? "both";
9628
- const options = { prefer: prefer ?? "remote" };
9629
- let result;
9630
- if (all_agents) {
9631
- const agents = defaultSyncAgents();
9632
- result = syncWithAgents(agents, (a) => resolveTaskListId(a, task_list_id || project?.task_list_id || undefined), resolvedProjectId, dir, options);
9633
- } else {
9634
- const resolvedAgent = agent || "claude";
9635
- const taskListId = resolveTaskListId(resolvedAgent, task_list_id || project?.task_list_id || undefined);
9636
- if (!taskListId) {
9637
- return {
9638
- content: [{
9639
- type: "text",
9640
- text: `Could not determine task list ID for ${resolvedAgent}. Provide task_list_id or set task_list_id on the project.`
9641
- }],
9642
- isError: true
9643
- };
9644
- }
9645
- result = syncWithAgent(resolvedAgent, taskListId, resolvedProjectId, dir, options);
9601
+ });
9602
+ }
9603
+ if (shouldRegisterTool("update_plan")) {
9604
+ server.tool("update_plan", "Update plan fields (name, description, status).", {
9605
+ id: exports_external.string(),
9606
+ name: exports_external.string().optional(),
9607
+ description: exports_external.string().optional(),
9608
+ status: exports_external.enum(["active", "completed", "archived"]).optional(),
9609
+ task_list_id: exports_external.string().optional(),
9610
+ agent_id: exports_external.string().optional()
9611
+ }, async ({ id, ...rest }) => {
9612
+ try {
9613
+ const resolvedId = resolveId(id, "plans");
9614
+ const resolved = { ...rest };
9615
+ if (resolved.task_list_id)
9616
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
9617
+ const plan = updatePlan(resolvedId, resolved);
9618
+ return {
9619
+ content: [{
9620
+ type: "text",
9621
+ text: `Plan updated: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
9622
+ }]
9623
+ };
9624
+ } catch (e) {
9625
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9646
9626
  }
9647
- const parts = [];
9648
- if (result.pulled > 0)
9649
- parts.push(`Pulled ${result.pulled} task(s).`);
9650
- if (result.pushed > 0)
9651
- parts.push(`Pushed ${result.pushed} task(s).`);
9652
- if (result.pulled === 0 && result.pushed === 0 && result.errors.length === 0) {
9653
- parts.push("Nothing to sync.");
9627
+ });
9628
+ }
9629
+ if (shouldRegisterTool("delete_plan")) {
9630
+ server.tool("delete_plan", "Delete a plan. Tasks in the plan are orphaned (not deleted).", {
9631
+ id: exports_external.string()
9632
+ }, async ({ id }) => {
9633
+ try {
9634
+ const resolvedId = resolveId(id, "plans");
9635
+ const deleted = deletePlan(resolvedId);
9636
+ return {
9637
+ content: [{
9638
+ type: "text",
9639
+ text: deleted ? `Plan ${id} deleted.` : `Plan ${id} not found.`
9640
+ }]
9641
+ };
9642
+ } catch (e) {
9643
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9654
9644
  }
9655
- for (const err of result.errors) {
9656
- parts.push(`Error: ${err}`);
9645
+ });
9646
+ }
9647
+ if (shouldRegisterTool("search_tasks")) {
9648
+ server.tool("search_tasks", "Full-text search across tasks with filters.", {
9649
+ query: exports_external.string(),
9650
+ project_id: exports_external.string().optional(),
9651
+ task_list_id: exports_external.string().optional(),
9652
+ status: exports_external.union([
9653
+ exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
9654
+ exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
9655
+ ]).optional(),
9656
+ priority: exports_external.union([
9657
+ exports_external.enum(["low", "medium", "high", "critical"]),
9658
+ exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
9659
+ ]).optional(),
9660
+ assigned_to: exports_external.string().optional(),
9661
+ agent_id: exports_external.string().optional(),
9662
+ created_after: exports_external.string().optional(),
9663
+ updated_after: exports_external.string().optional(),
9664
+ has_dependencies: exports_external.boolean().optional(),
9665
+ is_blocked: exports_external.boolean().optional()
9666
+ }, async ({ query, project_id, task_list_id, ...filters }) => {
9667
+ try {
9668
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
9669
+ const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
9670
+ const tasks = searchTasks({
9671
+ query,
9672
+ project_id: resolvedProjectId,
9673
+ task_list_id: resolvedTaskListId,
9674
+ ...filters
9675
+ });
9676
+ if (tasks.length === 0) {
9677
+ return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
9678
+ }
9679
+ const text = tasks.map((t) => `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}`).join(`
9680
+ `);
9681
+ return { content: [{ type: "text", text: `${tasks.length} result(s) for "${query}":
9682
+ ${text}` }] };
9683
+ } catch (e) {
9684
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9657
9685
  }
9658
- return { content: [{ type: "text", text: parts.join(`
9686
+ });
9687
+ }
9688
+ if (shouldRegisterTool("sync")) {
9689
+ server.tool("sync", "Sync tasks between local DB and agent task list.", {
9690
+ task_list_id: exports_external.string().optional(),
9691
+ agent: exports_external.string().optional(),
9692
+ all_agents: exports_external.boolean().optional(),
9693
+ project_id: exports_external.string().optional(),
9694
+ direction: exports_external.enum(["push", "pull", "both"]).optional(),
9695
+ prefer: exports_external.enum(["local", "remote"]).optional()
9696
+ }, async ({ task_list_id, agent, all_agents, project_id, direction, prefer }) => {
9697
+ try {
9698
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
9699
+ const project = resolvedProjectId ? getProject(resolvedProjectId) : undefined;
9700
+ const dir = direction ?? "both";
9701
+ const options = { prefer: prefer ?? "remote" };
9702
+ let result;
9703
+ if (all_agents) {
9704
+ const agents = defaultSyncAgents();
9705
+ result = syncWithAgents(agents, (a) => resolveTaskListId(a, task_list_id || project?.task_list_id || undefined), resolvedProjectId, dir, options);
9706
+ } else {
9707
+ const resolvedAgent = agent || "claude";
9708
+ const taskListId = resolveTaskListId(resolvedAgent, task_list_id || project?.task_list_id || undefined);
9709
+ if (!taskListId) {
9710
+ return {
9711
+ content: [{
9712
+ type: "text",
9713
+ text: `Could not determine task list ID for ${resolvedAgent}. Provide task_list_id or set task_list_id on the project.`
9714
+ }],
9715
+ isError: true
9716
+ };
9717
+ }
9718
+ result = syncWithAgent(resolvedAgent, taskListId, resolvedProjectId, dir, options);
9719
+ }
9720
+ const parts = [];
9721
+ if (result.pulled > 0)
9722
+ parts.push(`Pulled ${result.pulled} task(s).`);
9723
+ if (result.pushed > 0)
9724
+ parts.push(`Pushed ${result.pushed} task(s).`);
9725
+ if (result.pulled === 0 && result.pushed === 0 && result.errors.length === 0) {
9726
+ parts.push("Nothing to sync.");
9727
+ }
9728
+ for (const err of result.errors) {
9729
+ parts.push(`Error: ${err}`);
9730
+ }
9731
+ return { content: [{ type: "text", text: parts.join(`
9659
9732
  `) }] };
9660
- } catch (e) {
9661
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9662
- }
9663
- });
9664
- server.tool("register_agent", "Register an agent (idempotent by name). Updates last_seen_at.", {
9665
- name: exports_external.string(),
9666
- description: exports_external.string().optional()
9667
- }, async ({ name, description }) => {
9668
- try {
9669
- const agent = registerAgent({ name, description });
9670
- return {
9671
- content: [{
9672
- type: "text",
9673
- text: `Agent registered:
9733
+ } catch (e) {
9734
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9735
+ }
9736
+ });
9737
+ }
9738
+ if (shouldRegisterTool("register_agent")) {
9739
+ server.tool("register_agent", "Register an agent (idempotent by name). Updates last_seen_at.", {
9740
+ name: exports_external.string(),
9741
+ description: exports_external.string().optional()
9742
+ }, async ({ name, description }) => {
9743
+ try {
9744
+ const agent = registerAgent({ name, description });
9745
+ return {
9746
+ content: [{
9747
+ type: "text",
9748
+ text: `Agent registered:
9674
9749
  ID: ${agent.id}
9675
9750
  Name: ${agent.name}${agent.description ? `
9676
9751
  Description: ${agent.description}` : ""}
9677
9752
  Created: ${agent.created_at}
9678
9753
  Last seen: ${agent.last_seen_at}`
9679
- }]
9680
- };
9681
- } catch (e) {
9682
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9683
- }
9684
- });
9685
- server.tool("list_agents", "List all registered agents", {}, async () => {
9686
- try {
9687
- const agents = listAgents();
9688
- if (agents.length === 0) {
9689
- return { content: [{ type: "text", text: "No agents registered." }] };
9754
+ }]
9755
+ };
9756
+ } catch (e) {
9757
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9690
9758
  }
9691
- const text = agents.map((a) => {
9692
- return `${a.id} | ${a.name}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
9693
- }).join(`
9759
+ });
9760
+ }
9761
+ if (shouldRegisterTool("list_agents")) {
9762
+ server.tool("list_agents", "List all registered agents", {}, async () => {
9763
+ try {
9764
+ const agents = listAgents();
9765
+ if (agents.length === 0) {
9766
+ return { content: [{ type: "text", text: "No agents registered." }] };
9767
+ }
9768
+ const text = agents.map((a) => {
9769
+ return `${a.id} | ${a.name}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
9770
+ }).join(`
9694
9771
  `);
9695
- return { content: [{ type: "text", text: `${agents.length} agent(s):
9772
+ return { content: [{ type: "text", text: `${agents.length} agent(s):
9696
9773
  ${text}` }] };
9697
- } catch (e) {
9698
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9699
- }
9700
- });
9701
- server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
9702
- id: exports_external.string().optional(),
9703
- name: exports_external.string().optional()
9704
- }, async ({ id, name }) => {
9705
- try {
9706
- if (!id && !name) {
9707
- return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
9708
- }
9709
- const agent = id ? getAgent(id) : getAgentByName(name);
9710
- if (!agent) {
9711
- return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
9774
+ } catch (e) {
9775
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9712
9776
  }
9713
- const parts = [
9714
- `ID: ${agent.id}`,
9715
- `Name: ${agent.name}`
9716
- ];
9717
- if (agent.description)
9718
- parts.push(`Description: ${agent.description}`);
9719
- if (Object.keys(agent.metadata).length > 0)
9720
- parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
9721
- parts.push(`Created: ${agent.created_at}`);
9722
- parts.push(`Last seen: ${agent.last_seen_at}`);
9723
- return { content: [{ type: "text", text: parts.join(`
9777
+ });
9778
+ }
9779
+ if (shouldRegisterTool("get_agent")) {
9780
+ server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
9781
+ id: exports_external.string().optional(),
9782
+ name: exports_external.string().optional()
9783
+ }, async ({ id, name }) => {
9784
+ try {
9785
+ if (!id && !name) {
9786
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
9787
+ }
9788
+ const agent = id ? getAgent(id) : getAgentByName(name);
9789
+ if (!agent) {
9790
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
9791
+ }
9792
+ const parts = [
9793
+ `ID: ${agent.id}`,
9794
+ `Name: ${agent.name}`
9795
+ ];
9796
+ if (agent.description)
9797
+ parts.push(`Description: ${agent.description}`);
9798
+ if (Object.keys(agent.metadata).length > 0)
9799
+ parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
9800
+ parts.push(`Created: ${agent.created_at}`);
9801
+ parts.push(`Last seen: ${agent.last_seen_at}`);
9802
+ return { content: [{ type: "text", text: parts.join(`
9724
9803
  `) }] };
9725
- } catch (e) {
9726
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9727
- }
9728
- });
9729
- server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
9730
- id: exports_external.string().optional(),
9731
- name: exports_external.string().optional(),
9732
- new_name: exports_external.string()
9733
- }, async ({ id, name, new_name }) => {
9734
- try {
9735
- if (!id && !name) {
9736
- return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
9737
- }
9738
- const agent = id ? getAgent(id) : getAgentByName(name);
9739
- if (!agent) {
9740
- return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
9804
+ } catch (e) {
9805
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9741
9806
  }
9742
- const updated = updateAgent(agent.id, { name: new_name });
9743
- return {
9744
- content: [{
9745
- type: "text",
9746
- text: `Agent renamed: ${agent.name} -> ${updated.name}
9807
+ });
9808
+ }
9809
+ if (shouldRegisterTool("rename_agent")) {
9810
+ server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
9811
+ id: exports_external.string().optional(),
9812
+ name: exports_external.string().optional(),
9813
+ new_name: exports_external.string()
9814
+ }, async ({ id, name, new_name }) => {
9815
+ try {
9816
+ if (!id && !name) {
9817
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
9818
+ }
9819
+ const agent = id ? getAgent(id) : getAgentByName(name);
9820
+ if (!agent) {
9821
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
9822
+ }
9823
+ const updated = updateAgent(agent.id, { name: new_name });
9824
+ return {
9825
+ content: [{
9826
+ type: "text",
9827
+ text: `Agent renamed: ${agent.name} -> ${updated.name}
9747
9828
  ID: ${updated.id}`
9748
- }]
9749
- };
9750
- } catch (e) {
9751
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9752
- }
9753
- });
9754
- server.tool("delete_agent", "Delete an agent permanently. Resolve by id or name.", {
9755
- id: exports_external.string().optional(),
9756
- name: exports_external.string().optional()
9757
- }, async ({ id, name }) => {
9758
- try {
9759
- if (!id && !name) {
9760
- return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
9829
+ }]
9830
+ };
9831
+ } catch (e) {
9832
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9761
9833
  }
9762
- const agent = id ? getAgent(id) : getAgentByName(name);
9763
- if (!agent) {
9764
- return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
9834
+ });
9835
+ }
9836
+ if (shouldRegisterTool("delete_agent")) {
9837
+ server.tool("delete_agent", "Delete an agent permanently. Resolve by id or name.", {
9838
+ id: exports_external.string().optional(),
9839
+ name: exports_external.string().optional()
9840
+ }, async ({ id, name }) => {
9841
+ try {
9842
+ if (!id && !name) {
9843
+ return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
9844
+ }
9845
+ const agent = id ? getAgent(id) : getAgentByName(name);
9846
+ if (!agent) {
9847
+ return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
9848
+ }
9849
+ const deleted = deleteAgent(agent.id);
9850
+ return {
9851
+ content: [{
9852
+ type: "text",
9853
+ text: deleted ? `Agent deleted: ${agent.name} (${agent.id})` : `Failed to delete agent: ${agent.name}`
9854
+ }],
9855
+ isError: !deleted
9856
+ };
9857
+ } catch (e) {
9858
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9765
9859
  }
9766
- const deleted = deleteAgent(agent.id);
9767
- return {
9768
- content: [{
9769
- type: "text",
9770
- text: deleted ? `Agent deleted: ${agent.name} (${agent.id})` : `Failed to delete agent: ${agent.name}`
9771
- }],
9772
- isError: !deleted
9773
- };
9774
- } catch (e) {
9775
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9776
- }
9777
- });
9778
- server.tool("create_task_list", "Create a task list container for organizing tasks.", {
9779
- name: exports_external.string(),
9780
- slug: exports_external.string().optional(),
9781
- project_id: exports_external.string().optional(),
9782
- description: exports_external.string().optional()
9783
- }, async (params) => {
9784
- try {
9785
- const resolved = { ...params };
9786
- if (resolved.project_id)
9787
- resolved.project_id = resolveId(resolved.project_id, "projects");
9788
- const list = createTaskList(resolved);
9789
- return {
9790
- content: [{
9791
- type: "text",
9792
- text: `Task list created:
9860
+ });
9861
+ }
9862
+ if (shouldRegisterTool("create_task_list")) {
9863
+ server.tool("create_task_list", "Create a task list container for organizing tasks.", {
9864
+ name: exports_external.string(),
9865
+ slug: exports_external.string().optional(),
9866
+ project_id: exports_external.string().optional(),
9867
+ description: exports_external.string().optional()
9868
+ }, async (params) => {
9869
+ try {
9870
+ const resolved = { ...params };
9871
+ if (resolved.project_id)
9872
+ resolved.project_id = resolveId(resolved.project_id, "projects");
9873
+ const list = createTaskList(resolved);
9874
+ return {
9875
+ content: [{
9876
+ type: "text",
9877
+ text: `Task list created:
9793
9878
  ID: ${list.id}
9794
9879
  Name: ${list.name}
9795
9880
  Slug: ${list.slug}${list.project_id ? `
9796
9881
  Project: ${list.project_id}` : ""}${list.description ? `
9797
9882
  Description: ${list.description}` : ""}`
9798
- }]
9799
- };
9800
- } catch (e) {
9801
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9802
- }
9803
- });
9804
- server.tool("list_task_lists", "List all task lists, optionally filtered by project.", {
9805
- project_id: exports_external.string().optional()
9806
- }, async ({ project_id }) => {
9807
- try {
9808
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
9809
- const lists = listTaskLists(resolvedProjectId);
9810
- if (lists.length === 0) {
9811
- return { content: [{ type: "text", text: "No task lists found." }] };
9812
- }
9813
- const text = lists.map((l) => {
9814
- const project = l.project_id ? ` (project: ${l.project_id.slice(0, 8)})` : "";
9815
- return `${l.id.slice(0, 8)} | ${l.name} [${l.slug}]${project}${l.description ? ` - ${l.description}` : ""}`;
9816
- }).join(`
9883
+ }]
9884
+ };
9885
+ } catch (e) {
9886
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9887
+ }
9888
+ });
9889
+ }
9890
+ if (shouldRegisterTool("list_task_lists")) {
9891
+ server.tool("list_task_lists", "List all task lists, optionally filtered by project.", {
9892
+ project_id: exports_external.string().optional()
9893
+ }, async ({ project_id }) => {
9894
+ try {
9895
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
9896
+ const lists = listTaskLists(resolvedProjectId);
9897
+ if (lists.length === 0) {
9898
+ return { content: [{ type: "text", text: "No task lists found." }] };
9899
+ }
9900
+ const text = lists.map((l) => {
9901
+ const project = l.project_id ? ` (project: ${l.project_id.slice(0, 8)})` : "";
9902
+ return `${l.id.slice(0, 8)} | ${l.name} [${l.slug}]${project}${l.description ? ` - ${l.description}` : ""}`;
9903
+ }).join(`
9817
9904
  `);
9818
- return { content: [{ type: "text", text: `${lists.length} task list(s):
9905
+ return { content: [{ type: "text", text: `${lists.length} task list(s):
9819
9906
  ${text}` }] };
9820
- } catch (e) {
9821
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9822
- }
9823
- });
9824
- server.tool("get_task_list", "Get task list details including slug and metadata.", {
9825
- id: exports_external.string()
9826
- }, async ({ id }) => {
9827
- try {
9828
- const resolvedId = resolveId(id, "task_lists");
9829
- const list = getTaskList(resolvedId);
9830
- if (!list) {
9831
- return { content: [{ type: "text", text: `Task list not found: ${id}` }], isError: true };
9832
- }
9833
- const parts = [
9834
- `ID: ${list.id}`,
9835
- `Name: ${list.name}`,
9836
- `Slug: ${list.slug}`
9837
- ];
9838
- if (list.project_id)
9839
- parts.push(`Project: ${list.project_id}`);
9840
- if (list.description)
9841
- parts.push(`Description: ${list.description}`);
9842
- if (Object.keys(list.metadata).length > 0)
9843
- parts.push(`Metadata: ${JSON.stringify(list.metadata)}`);
9844
- parts.push(`Created: ${list.created_at}`);
9845
- parts.push(`Updated: ${list.updated_at}`);
9846
- return { content: [{ type: "text", text: parts.join(`
9907
+ } catch (e) {
9908
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9909
+ }
9910
+ });
9911
+ }
9912
+ if (shouldRegisterTool("get_task_list")) {
9913
+ server.tool("get_task_list", "Get task list details including slug and metadata.", {
9914
+ id: exports_external.string()
9915
+ }, async ({ id }) => {
9916
+ try {
9917
+ const resolvedId = resolveId(id, "task_lists");
9918
+ const list = getTaskList(resolvedId);
9919
+ if (!list) {
9920
+ return { content: [{ type: "text", text: `Task list not found: ${id}` }], isError: true };
9921
+ }
9922
+ const parts = [
9923
+ `ID: ${list.id}`,
9924
+ `Name: ${list.name}`,
9925
+ `Slug: ${list.slug}`
9926
+ ];
9927
+ if (list.project_id)
9928
+ parts.push(`Project: ${list.project_id}`);
9929
+ if (list.description)
9930
+ parts.push(`Description: ${list.description}`);
9931
+ if (Object.keys(list.metadata).length > 0)
9932
+ parts.push(`Metadata: ${JSON.stringify(list.metadata)}`);
9933
+ parts.push(`Created: ${list.created_at}`);
9934
+ parts.push(`Updated: ${list.updated_at}`);
9935
+ return { content: [{ type: "text", text: parts.join(`
9847
9936
  `) }] };
9848
- } catch (e) {
9849
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9850
- }
9851
- });
9852
- server.tool("update_task_list", "Update a task list's name or description.", {
9853
- id: exports_external.string(),
9854
- name: exports_external.string().optional(),
9855
- description: exports_external.string().optional()
9856
- }, async ({ id, ...rest }) => {
9857
- try {
9858
- const resolvedId = resolveId(id, "task_lists");
9859
- const list = updateTaskList(resolvedId, rest);
9860
- return {
9861
- content: [{
9862
- type: "text",
9863
- text: `Task list updated:
9937
+ } catch (e) {
9938
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9939
+ }
9940
+ });
9941
+ }
9942
+ if (shouldRegisterTool("update_task_list")) {
9943
+ server.tool("update_task_list", "Update a task list's name or description.", {
9944
+ id: exports_external.string(),
9945
+ name: exports_external.string().optional(),
9946
+ description: exports_external.string().optional()
9947
+ }, async ({ id, ...rest }) => {
9948
+ try {
9949
+ const resolvedId = resolveId(id, "task_lists");
9950
+ const list = updateTaskList(resolvedId, rest);
9951
+ return {
9952
+ content: [{
9953
+ type: "text",
9954
+ text: `Task list updated:
9864
9955
  ID: ${list.id}
9865
9956
  Name: ${list.name}
9866
9957
  Slug: ${list.slug}`
9867
- }]
9868
- };
9869
- } catch (e) {
9870
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9871
- }
9872
- });
9873
- server.tool("delete_task_list", "Delete a task list. Tasks are orphaned, not deleted.", {
9874
- id: exports_external.string()
9875
- }, async ({ id }) => {
9876
- try {
9877
- const resolvedId = resolveId(id, "task_lists");
9878
- const deleted = deleteTaskList(resolvedId);
9879
- return {
9880
- content: [{
9881
- type: "text",
9882
- text: deleted ? `Task list ${id} deleted.` : `Task list ${id} not found.`
9883
- }]
9884
- };
9885
- } catch (e) {
9886
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9887
- }
9888
- });
9889
- server.tool("get_task_history", "Get audit log \u2014 field changes with timestamps and actors.", {
9890
- task_id: exports_external.string()
9891
- }, async ({ task_id }) => {
9892
- try {
9893
- const resolvedId = resolveId(task_id);
9894
- const { getTaskHistory: getTaskHistory2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
9895
- const history = getTaskHistory2(resolvedId);
9896
- if (history.length === 0)
9897
- return { content: [{ type: "text", text: "No history for this task." }] };
9898
- const text = history.map((h) => `${h.created_at} | ${h.action}${h.field ? ` ${h.field}` : ""}${h.old_value ? ` from "${h.old_value}"` : ""}${h.new_value ? ` to "${h.new_value}"` : ""}${h.agent_id ? ` by ${h.agent_id}` : ""}`).join(`
9958
+ }]
9959
+ };
9960
+ } catch (e) {
9961
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9962
+ }
9963
+ });
9964
+ }
9965
+ if (shouldRegisterTool("delete_task_list")) {
9966
+ server.tool("delete_task_list", "Delete a task list. Tasks are orphaned, not deleted.", {
9967
+ id: exports_external.string()
9968
+ }, async ({ id }) => {
9969
+ try {
9970
+ const resolvedId = resolveId(id, "task_lists");
9971
+ const deleted = deleteTaskList(resolvedId);
9972
+ return {
9973
+ content: [{
9974
+ type: "text",
9975
+ text: deleted ? `Task list ${id} deleted.` : `Task list ${id} not found.`
9976
+ }]
9977
+ };
9978
+ } catch (e) {
9979
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9980
+ }
9981
+ });
9982
+ }
9983
+ if (shouldRegisterTool("get_task_history")) {
9984
+ server.tool("get_task_history", "Get audit log \u2014 field changes with timestamps and actors.", {
9985
+ task_id: exports_external.string()
9986
+ }, async ({ task_id }) => {
9987
+ try {
9988
+ const resolvedId = resolveId(task_id);
9989
+ const { getTaskHistory: getTaskHistory2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
9990
+ const history = getTaskHistory2(resolvedId);
9991
+ if (history.length === 0)
9992
+ return { content: [{ type: "text", text: "No history for this task." }] };
9993
+ const text = history.map((h) => `${h.created_at} | ${h.action}${h.field ? ` ${h.field}` : ""}${h.old_value ? ` from "${h.old_value}"` : ""}${h.new_value ? ` to "${h.new_value}"` : ""}${h.agent_id ? ` by ${h.agent_id}` : ""}`).join(`
9899
9994
  `);
9900
- return { content: [{ type: "text", text: `${history.length} change(s):
9995
+ return { content: [{ type: "text", text: `${history.length} change(s):
9901
9996
  ${text}` }] };
9902
- } catch (e) {
9903
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9904
- }
9905
- });
9906
- server.tool("get_recent_activity", "Get recent task changes \u2014 global activity feed.", {
9907
- limit: exports_external.number().optional()
9908
- }, async ({ limit }) => {
9909
- try {
9910
- const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
9911
- const activity = getRecentActivity2(limit || 50);
9912
- if (activity.length === 0)
9913
- return { content: [{ type: "text", text: "No recent activity." }] };
9914
- const text = activity.map((h) => `${h.created_at} | ${h.task_id.slice(0, 8)} | ${h.action}${h.field ? ` ${h.field}` : ""}${h.agent_id ? ` by ${h.agent_id}` : ""}`).join(`
9997
+ } catch (e) {
9998
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
9999
+ }
10000
+ });
10001
+ }
10002
+ if (shouldRegisterTool("get_recent_activity")) {
10003
+ server.tool("get_recent_activity", "Get recent task changes \u2014 global activity feed.", {
10004
+ limit: exports_external.number().optional()
10005
+ }, async ({ limit }) => {
10006
+ try {
10007
+ const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
10008
+ const activity = getRecentActivity2(limit || 50);
10009
+ if (activity.length === 0)
10010
+ return { content: [{ type: "text", text: "No recent activity." }] };
10011
+ const text = activity.map((h) => `${h.created_at} | ${h.task_id.slice(0, 8)} | ${h.action}${h.field ? ` ${h.field}` : ""}${h.agent_id ? ` by ${h.agent_id}` : ""}`).join(`
9915
10012
  `);
9916
- return { content: [{ type: "text", text: `${activity.length} recent change(s):
10013
+ return { content: [{ type: "text", text: `${activity.length} recent change(s):
9917
10014
  ${text}` }] };
9918
- } catch (e) {
9919
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9920
- }
9921
- });
9922
- server.tool("create_webhook", "Register a webhook for task change events.", {
9923
- url: exports_external.string(),
9924
- events: exports_external.array(exports_external.string()).optional(),
9925
- secret: exports_external.string().optional()
9926
- }, async (params) => {
9927
- try {
9928
- const { createWebhook: createWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
9929
- const wh = createWebhook2(params);
9930
- return { content: [{ type: "text", text: `Webhook created: ${wh.id.slice(0, 8)} | ${wh.url} | events: ${wh.events.length === 0 ? "all" : wh.events.join(",")}` }] };
9931
- } catch (e) {
9932
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9933
- }
9934
- });
9935
- server.tool("list_webhooks", "List all registered webhooks", {}, async () => {
9936
- try {
9937
- const { listWebhooks: listWebhooks2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
9938
- const webhooks = listWebhooks2();
9939
- if (webhooks.length === 0)
9940
- return { content: [{ type: "text", text: "No webhooks registered." }] };
9941
- const text = webhooks.map((w) => `${w.id.slice(0, 8)} | ${w.active ? "active" : "inactive"} | ${w.url} | events: ${w.events.length === 0 ? "all" : w.events.join(",")}`).join(`
10015
+ } catch (e) {
10016
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10017
+ }
10018
+ });
10019
+ }
10020
+ if (shouldRegisterTool("create_webhook")) {
10021
+ server.tool("create_webhook", "Register a webhook for task change events.", {
10022
+ url: exports_external.string(),
10023
+ events: exports_external.array(exports_external.string()).optional(),
10024
+ secret: exports_external.string().optional()
10025
+ }, async (params) => {
10026
+ try {
10027
+ const { createWebhook: createWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
10028
+ const wh = createWebhook2(params);
10029
+ return { content: [{ type: "text", text: `Webhook created: ${wh.id.slice(0, 8)} | ${wh.url} | events: ${wh.events.length === 0 ? "all" : wh.events.join(",")}` }] };
10030
+ } catch (e) {
10031
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10032
+ }
10033
+ });
10034
+ }
10035
+ if (shouldRegisterTool("list_webhooks")) {
10036
+ server.tool("list_webhooks", "List all registered webhooks", {}, async () => {
10037
+ try {
10038
+ const { listWebhooks: listWebhooks2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
10039
+ const webhooks = listWebhooks2();
10040
+ if (webhooks.length === 0)
10041
+ return { content: [{ type: "text", text: "No webhooks registered." }] };
10042
+ const text = webhooks.map((w) => `${w.id.slice(0, 8)} | ${w.active ? "active" : "inactive"} | ${w.url} | events: ${w.events.length === 0 ? "all" : w.events.join(",")}`).join(`
9942
10043
  `);
9943
- return { content: [{ type: "text", text: `${webhooks.length} webhook(s):
10044
+ return { content: [{ type: "text", text: `${webhooks.length} webhook(s):
9944
10045
  ${text}` }] };
9945
- } catch (e) {
9946
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9947
- }
9948
- });
9949
- server.tool("delete_webhook", "Delete a webhook by ID.", {
9950
- id: exports_external.string()
9951
- }, async ({ id }) => {
9952
- try {
9953
- const { deleteWebhook: deleteWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
9954
- const deleted = deleteWebhook2(id);
9955
- return { content: [{ type: "text", text: deleted ? "Webhook deleted." : "Webhook not found." }] };
9956
- } catch (e) {
9957
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9958
- }
9959
- });
9960
- server.tool("create_template", "Create a reusable task template.", {
9961
- name: exports_external.string(),
9962
- title_pattern: exports_external.string(),
9963
- description: exports_external.string().optional(),
9964
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
9965
- tags: exports_external.array(exports_external.string()).optional(),
9966
- project_id: exports_external.string().optional(),
9967
- plan_id: exports_external.string().optional()
9968
- }, async (params) => {
9969
- try {
9970
- const { createTemplate: createTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
9971
- const t = createTemplate2(params);
9972
- return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"` }] };
9973
- } catch (e) {
9974
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9975
- }
9976
- });
9977
- server.tool("list_templates", "List all task templates", {}, async () => {
9978
- try {
9979
- const { listTemplates: listTemplates2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
9980
- const templates = listTemplates2();
9981
- if (templates.length === 0)
9982
- return { content: [{ type: "text", text: "No templates." }] };
9983
- const text = templates.map((t) => `${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}" | ${t.priority}`).join(`
10046
+ } catch (e) {
10047
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10048
+ }
10049
+ });
10050
+ }
10051
+ if (shouldRegisterTool("delete_webhook")) {
10052
+ server.tool("delete_webhook", "Delete a webhook by ID.", {
10053
+ id: exports_external.string()
10054
+ }, async ({ id }) => {
10055
+ try {
10056
+ const { deleteWebhook: deleteWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
10057
+ const deleted = deleteWebhook2(id);
10058
+ return { content: [{ type: "text", text: deleted ? "Webhook deleted." : "Webhook not found." }] };
10059
+ } catch (e) {
10060
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10061
+ }
10062
+ });
10063
+ }
10064
+ if (shouldRegisterTool("create_template")) {
10065
+ server.tool("create_template", "Create a reusable task template.", {
10066
+ name: exports_external.string(),
10067
+ title_pattern: exports_external.string(),
10068
+ description: exports_external.string().optional(),
10069
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
10070
+ tags: exports_external.array(exports_external.string()).optional(),
10071
+ project_id: exports_external.string().optional(),
10072
+ plan_id: exports_external.string().optional()
10073
+ }, async (params) => {
10074
+ try {
10075
+ const { createTemplate: createTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
10076
+ const t = createTemplate2(params);
10077
+ return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"` }] };
10078
+ } catch (e) {
10079
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10080
+ }
10081
+ });
10082
+ }
10083
+ if (shouldRegisterTool("list_templates")) {
10084
+ server.tool("list_templates", "List all task templates", {}, async () => {
10085
+ try {
10086
+ const { listTemplates: listTemplates2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
10087
+ const templates = listTemplates2();
10088
+ if (templates.length === 0)
10089
+ return { content: [{ type: "text", text: "No templates." }] };
10090
+ const text = templates.map((t) => `${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}" | ${t.priority}`).join(`
9984
10091
  `);
9985
- return { content: [{ type: "text", text: `${templates.length} template(s):
10092
+ return { content: [{ type: "text", text: `${templates.length} template(s):
9986
10093
  ${text}` }] };
9987
- } catch (e) {
9988
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
9989
- }
9990
- });
9991
- server.tool("create_task_from_template", "Create a task from a template with optional overrides.", {
9992
- template_id: exports_external.string(),
9993
- title: exports_external.string().optional(),
9994
- description: exports_external.string().optional(),
9995
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
9996
- assigned_to: exports_external.string().optional(),
9997
- project_id: exports_external.string().optional()
9998
- }, async (params) => {
9999
- try {
10000
- const { taskFromTemplate: taskFromTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
10001
- const input = taskFromTemplate2(params.template_id, {
10002
- title: params.title,
10003
- description: params.description,
10004
- priority: params.priority,
10005
- assigned_to: params.assigned_to,
10006
- project_id: params.project_id
10007
- });
10008
- const task = createTask(input);
10009
- return { content: [{ type: "text", text: `Task created from template:
10094
+ } catch (e) {
10095
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10096
+ }
10097
+ });
10098
+ }
10099
+ if (shouldRegisterTool("create_task_from_template")) {
10100
+ server.tool("create_task_from_template", "Create a task from a template with optional overrides.", {
10101
+ template_id: exports_external.string(),
10102
+ title: exports_external.string().optional(),
10103
+ description: exports_external.string().optional(),
10104
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
10105
+ assigned_to: exports_external.string().optional(),
10106
+ project_id: exports_external.string().optional()
10107
+ }, async (params) => {
10108
+ try {
10109
+ const { taskFromTemplate: taskFromTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
10110
+ const input = taskFromTemplate2(params.template_id, {
10111
+ title: params.title,
10112
+ description: params.description,
10113
+ priority: params.priority,
10114
+ assigned_to: params.assigned_to,
10115
+ project_id: params.project_id
10116
+ });
10117
+ const task = createTask(input);
10118
+ return { content: [{ type: "text", text: `Task created from template:
10010
10119
  ${task.id.slice(0, 8)} | ${task.priority} | ${task.title}` }] };
10011
- } catch (e) {
10012
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10013
- }
10014
- });
10015
- server.tool("delete_template", "Delete a task template by ID.", { id: exports_external.string() }, async ({ id }) => {
10016
- try {
10017
- const { deleteTemplate: deleteTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
10018
- const deleted = deleteTemplate2(id);
10019
- return { content: [{ type: "text", text: deleted ? "Template deleted." : "Template not found." }] };
10020
- } catch (e) {
10021
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10022
- }
10023
- });
10024
- server.tool("approve_task", "Approve a task with requires_approval=true.", {
10025
- id: exports_external.string(),
10026
- agent_id: exports_external.string().optional()
10027
- }, async ({ id, agent_id }) => {
10028
- try {
10029
- const resolvedId = resolveId(id);
10030
- const task = getTaskWithRelations(resolvedId);
10031
- if (!task)
10032
- return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
10033
- if (!task.requires_approval)
10034
- return { content: [{ type: "text", text: `Task ${id} does not require approval.` }] };
10035
- if (task.approved_by)
10036
- return { content: [{ type: "text", text: `Task already approved by ${task.approved_by}.` }] };
10037
- const updated = updateTask(resolvedId, { approved_by: agent_id || "system", version: task.version });
10038
- return { content: [{ type: "text", text: `Task approved by ${agent_id || "system"}: ${updated.id.slice(0, 8)} | ${updated.title}` }] };
10039
- } catch (e) {
10040
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10041
- }
10042
- });
10043
- server.tool("fail_task", "Mark a task as failed with structured reason and optional auto-retry.", {
10044
- id: exports_external.string(),
10045
- agent_id: exports_external.string().optional(),
10046
- reason: exports_external.string().optional(),
10047
- error_code: exports_external.string().optional(),
10048
- retry: exports_external.boolean().optional(),
10049
- retry_after: exports_external.string().optional()
10050
- }, async ({ id, agent_id, reason, error_code, retry, retry_after }) => {
10051
- try {
10052
- const resolvedId = resolveId(id);
10053
- const result = failTask(resolvedId, agent_id, reason, { retry, retry_after, error_code });
10054
- let text = `failed: ${formatTask(result.task)}`;
10055
- if (reason)
10056
- text += `
10120
+ } catch (e) {
10121
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10122
+ }
10123
+ });
10124
+ }
10125
+ if (shouldRegisterTool("delete_template")) {
10126
+ server.tool("delete_template", "Delete a task template by ID.", { id: exports_external.string() }, async ({ id }) => {
10127
+ try {
10128
+ const { deleteTemplate: deleteTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
10129
+ const deleted = deleteTemplate2(id);
10130
+ return { content: [{ type: "text", text: deleted ? "Template deleted." : "Template not found." }] };
10131
+ } catch (e) {
10132
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10133
+ }
10134
+ });
10135
+ }
10136
+ if (shouldRegisterTool("approve_task")) {
10137
+ server.tool("approve_task", "Approve a task with requires_approval=true.", {
10138
+ id: exports_external.string(),
10139
+ agent_id: exports_external.string().optional()
10140
+ }, async ({ id, agent_id }) => {
10141
+ try {
10142
+ const resolvedId = resolveId(id);
10143
+ const task = getTaskWithRelations(resolvedId);
10144
+ if (!task)
10145
+ return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
10146
+ if (!task.requires_approval)
10147
+ return { content: [{ type: "text", text: `Task ${id} does not require approval.` }] };
10148
+ if (task.approved_by)
10149
+ return { content: [{ type: "text", text: `Task already approved by ${task.approved_by}.` }] };
10150
+ const updated = updateTask(resolvedId, { approved_by: agent_id || "system", version: task.version });
10151
+ return { content: [{ type: "text", text: `Task approved by ${agent_id || "system"}: ${updated.id.slice(0, 8)} | ${updated.title}` }] };
10152
+ } catch (e) {
10153
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10154
+ }
10155
+ });
10156
+ }
10157
+ if (shouldRegisterTool("fail_task")) {
10158
+ server.tool("fail_task", "Mark a task as failed with structured reason and optional auto-retry.", {
10159
+ id: exports_external.string(),
10160
+ agent_id: exports_external.string().optional(),
10161
+ reason: exports_external.string().optional(),
10162
+ error_code: exports_external.string().optional(),
10163
+ retry: exports_external.boolean().optional(),
10164
+ retry_after: exports_external.string().optional()
10165
+ }, async ({ id, agent_id, reason, error_code, retry, retry_after }) => {
10166
+ try {
10167
+ const resolvedId = resolveId(id);
10168
+ const result = failTask(resolvedId, agent_id, reason, { retry, retry_after, error_code });
10169
+ let text = `failed: ${formatTask(result.task)}`;
10170
+ if (reason)
10171
+ text += `
10057
10172
  Reason: ${reason}`;
10058
- if (result.retryTask) {
10059
- text += `
10173
+ if (result.retryTask) {
10174
+ text += `
10060
10175
  Retry task created: ${formatTask(result.retryTask)}`;
10176
+ }
10177
+ return { content: [{ type: "text", text }] };
10178
+ } catch (e) {
10179
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10061
10180
  }
10062
- return { content: [{ type: "text", text }] };
10063
- } catch (e) {
10064
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10065
- }
10066
- });
10067
- server.tool("get_my_tasks", "Get tasks assigned to/created by an agent with stats.", {
10068
- agent_name: exports_external.string()
10069
- }, async ({ agent_name }) => {
10070
- try {
10071
- const agent = registerAgent({ name: agent_name });
10072
- const tasks = listTasks({});
10073
- const myTasks = tasks.filter((t) => t.assigned_to === agent_name || t.assigned_to === agent.id || t.agent_id === agent.id || t.agent_id === agent_name);
10074
- const pending = myTasks.filter((t) => t.status === "pending");
10075
- const inProgress = myTasks.filter((t) => t.status === "in_progress");
10076
- const completed = myTasks.filter((t) => t.status === "completed");
10077
- const rate = myTasks.length > 0 ? Math.round(completed.length / myTasks.length * 100) : 0;
10078
- const lines = [
10079
- `Agent: ${agent.name} (${agent.id})`,
10080
- `Tasks: ${myTasks.length} total, ${pending.length} pending, ${inProgress.length} active, ${completed.length} done (${rate}%)`
10081
- ];
10082
- if (pending.length > 0) {
10083
- lines.push(`
10181
+ });
10182
+ }
10183
+ if (shouldRegisterTool("get_my_tasks")) {
10184
+ server.tool("get_my_tasks", "Get tasks assigned to/created by an agent with stats.", {
10185
+ agent_name: exports_external.string()
10186
+ }, async ({ agent_name }) => {
10187
+ try {
10188
+ const agent = registerAgent({ name: agent_name });
10189
+ const tasks = listTasks({});
10190
+ const myTasks = tasks.filter((t) => t.assigned_to === agent_name || t.assigned_to === agent.id || t.agent_id === agent.id || t.agent_id === agent_name);
10191
+ const pending = myTasks.filter((t) => t.status === "pending");
10192
+ const inProgress = myTasks.filter((t) => t.status === "in_progress");
10193
+ const completed = myTasks.filter((t) => t.status === "completed");
10194
+ const rate = myTasks.length > 0 ? Math.round(completed.length / myTasks.length * 100) : 0;
10195
+ const lines = [
10196
+ `Agent: ${agent.name} (${agent.id})`,
10197
+ `Tasks: ${myTasks.length} total, ${pending.length} pending, ${inProgress.length} active, ${completed.length} done (${rate}%)`
10198
+ ];
10199
+ if (pending.length > 0) {
10200
+ lines.push(`
10084
10201
  Pending:`);
10085
- for (const t of pending.slice(0, 10))
10086
- lines.push(` [${t.priority}] ${t.id.slice(0, 8)} | ${t.title}`);
10087
- }
10088
- if (inProgress.length > 0) {
10089
- lines.push(`
10202
+ for (const t of pending.slice(0, 10))
10203
+ lines.push(` [${t.priority}] ${t.id.slice(0, 8)} | ${t.title}`);
10204
+ }
10205
+ if (inProgress.length > 0) {
10206
+ lines.push(`
10090
10207
  In Progress:`);
10091
- for (const t of inProgress)
10092
- lines.push(` ${t.id.slice(0, 8)} | ${t.title}`);
10093
- }
10094
- return { content: [{ type: "text", text: lines.join(`
10208
+ for (const t of inProgress)
10209
+ lines.push(` ${t.id.slice(0, 8)} | ${t.title}`);
10210
+ }
10211
+ return { content: [{ type: "text", text: lines.join(`
10095
10212
  `) }] };
10096
- } catch (e) {
10097
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10098
- }
10099
- });
10100
- server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy.", {}, async () => {
10101
- try {
10102
- let render = function(nodes, indent = 0) {
10103
- return nodes.map((n) => {
10104
- const prefix = " ".repeat(indent);
10105
- const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
10106
- const level = n.agent.level ? ` (${n.agent.level})` : "";
10107
- const line = `${prefix}${n.agent.name}${title}${level}`;
10108
- const children = n.reports.length > 0 ? `
10213
+ } catch (e) {
10214
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10215
+ }
10216
+ });
10217
+ }
10218
+ if (shouldRegisterTool("get_org_chart")) {
10219
+ server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy.", {}, async () => {
10220
+ try {
10221
+ let render = function(nodes, indent = 0) {
10222
+ return nodes.map((n) => {
10223
+ const prefix = " ".repeat(indent);
10224
+ const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
10225
+ const level = n.agent.level ? ` (${n.agent.level})` : "";
10226
+ const line = `${prefix}${n.agent.name}${title}${level}`;
10227
+ const children = n.reports.length > 0 ? `
10109
10228
  ` + render(n.reports, indent + 1) : "";
10110
- return line + children;
10111
- }).join(`
10229
+ return line + children;
10230
+ }).join(`
10112
10231
  `);
10113
- };
10114
- const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
10115
- const tree = getOrgChart2();
10116
- const text = tree.length > 0 ? render(tree) : "No agents registered.";
10117
- return { content: [{ type: "text", text }] };
10118
- } catch (e) {
10119
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10120
- }
10121
- });
10122
- server.tool("set_reports_to", "Set agent reporting relationship in org chart.", {
10123
- agent_name: exports_external.string(),
10124
- manager_name: exports_external.string().optional()
10125
- }, async ({ agent_name, manager_name }) => {
10126
- try {
10127
- const agent = getAgentByName(agent_name);
10128
- if (!agent)
10129
- return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
10130
- let managerId = null;
10131
- if (manager_name) {
10132
- const manager = getAgentByName(manager_name);
10133
- if (!manager)
10134
- return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
10135
- managerId = manager.id;
10136
- }
10137
- const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
10138
- updateAgent2(agent.id, { reports_to: managerId });
10139
- const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
10140
- return { content: [{ type: "text", text: result }] };
10141
- } catch (e) {
10142
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10143
- }
10144
- });
10145
- server.tool("bulk_update_tasks", "Update multiple tasks at once with the same changes.", {
10146
- task_ids: exports_external.array(exports_external.string()),
10147
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
10148
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
10149
- assigned_to: exports_external.string().optional(),
10150
- tags: exports_external.array(exports_external.string()).optional()
10151
- }, async ({ task_ids, ...updates }) => {
10152
- try {
10153
- const resolvedIds = task_ids.map((id) => resolveId(id));
10154
- const result = bulkUpdateTasks(resolvedIds, updates);
10155
- const parts = [`Updated ${result.updated} task(s).`];
10156
- if (result.failed.length > 0) {
10157
- parts.push(`Failed ${result.failed.length}:`);
10158
- for (const f of result.failed)
10159
- parts.push(` ${f.id.slice(0, 8)}: ${f.error}`);
10160
- }
10161
- return { content: [{ type: "text", text: parts.join(`
10232
+ };
10233
+ const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
10234
+ const tree = getOrgChart2();
10235
+ const text = tree.length > 0 ? render(tree) : "No agents registered.";
10236
+ return { content: [{ type: "text", text }] };
10237
+ } catch (e) {
10238
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10239
+ }
10240
+ });
10241
+ }
10242
+ if (shouldRegisterTool("set_reports_to")) {
10243
+ server.tool("set_reports_to", "Set agent reporting relationship in org chart.", {
10244
+ agent_name: exports_external.string(),
10245
+ manager_name: exports_external.string().optional()
10246
+ }, async ({ agent_name, manager_name }) => {
10247
+ try {
10248
+ const agent = getAgentByName(agent_name);
10249
+ if (!agent)
10250
+ return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
10251
+ let managerId = null;
10252
+ if (manager_name) {
10253
+ const manager = getAgentByName(manager_name);
10254
+ if (!manager)
10255
+ return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
10256
+ managerId = manager.id;
10257
+ }
10258
+ const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
10259
+ updateAgent2(agent.id, { reports_to: managerId });
10260
+ const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
10261
+ return { content: [{ type: "text", text: result }] };
10262
+ } catch (e) {
10263
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10264
+ }
10265
+ });
10266
+ }
10267
+ if (shouldRegisterTool("bulk_update_tasks")) {
10268
+ server.tool("bulk_update_tasks", "Update multiple tasks at once with the same changes.", {
10269
+ task_ids: exports_external.array(exports_external.string()),
10270
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
10271
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
10272
+ assigned_to: exports_external.string().optional(),
10273
+ tags: exports_external.array(exports_external.string()).optional()
10274
+ }, async ({ task_ids, ...updates }) => {
10275
+ try {
10276
+ const resolvedIds = task_ids.map((id) => resolveId(id));
10277
+ const result = bulkUpdateTasks(resolvedIds, updates);
10278
+ const parts = [`Updated ${result.updated} task(s).`];
10279
+ if (result.failed.length > 0) {
10280
+ parts.push(`Failed ${result.failed.length}:`);
10281
+ for (const f of result.failed)
10282
+ parts.push(` ${f.id.slice(0, 8)}: ${f.error}`);
10283
+ }
10284
+ return { content: [{ type: "text", text: parts.join(`
10162
10285
  `) }] };
10163
- } catch (e) {
10164
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10165
- }
10166
- });
10167
- server.tool("clone_task", "Duplicate a task with optional field overrides.", {
10168
- task_id: exports_external.string(),
10169
- title: exports_external.string().optional(),
10170
- description: exports_external.string().optional(),
10171
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
10172
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
10173
- project_id: exports_external.string().optional(),
10174
- plan_id: exports_external.string().optional(),
10175
- task_list_id: exports_external.string().optional(),
10176
- assigned_to: exports_external.string().optional(),
10177
- tags: exports_external.array(exports_external.string()).optional(),
10178
- estimated_minutes: exports_external.number().optional()
10179
- }, async ({ task_id, ...overrides }) => {
10180
- try {
10181
- const resolvedId = resolveId(task_id);
10182
- const resolved = { ...overrides };
10183
- if (resolved.project_id)
10184
- resolved.project_id = resolveId(resolved.project_id, "projects");
10185
- if (resolved.plan_id)
10186
- resolved.plan_id = resolveId(resolved.plan_id, "plans");
10187
- if (resolved.task_list_id)
10188
- resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
10189
- const task = cloneTask(resolvedId, resolved);
10190
- return { content: [{ type: "text", text: `cloned: ${formatTask(task)}` }] };
10191
- } catch (e) {
10192
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10193
- }
10194
- });
10195
- server.tool("get_task_stats", "Get task analytics: counts by status, priority, agent.", {
10196
- project_id: exports_external.string().optional(),
10197
- task_list_id: exports_external.string().optional(),
10198
- agent_id: exports_external.string().optional()
10199
- }, async ({ project_id, task_list_id, agent_id }) => {
10200
- try {
10201
- const filters = {};
10202
- if (project_id)
10203
- filters.project_id = resolveId(project_id, "projects");
10204
- if (task_list_id)
10205
- filters.task_list_id = resolveId(task_list_id, "task_lists");
10206
- if (agent_id)
10207
- filters.agent_id = agent_id;
10208
- const stats = getTaskStats(Object.keys(filters).length > 0 ? filters : undefined);
10209
- return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
10210
- } catch (e) {
10211
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10212
- }
10213
- });
10214
- server.tool("get_task_graph", "Get full dependency tree for a task.", {
10215
- id: exports_external.string(),
10216
- direction: exports_external.enum(["up", "down", "both"]).optional()
10217
- }, async ({ id, direction }) => {
10218
- try {
10219
- let formatNode = function(node, indent) {
10220
- const prefix = " ".repeat(indent);
10221
- const idLabel = node.task.short_id || node.task.id.slice(0, 8);
10222
- const blocked = node.task.is_blocked ? " (blocked: yes)" : "";
10223
- let out = `${prefix}[${node.task.status}] ${idLabel} | ${node.task.title}${blocked}
10286
+ } catch (e) {
10287
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10288
+ }
10289
+ });
10290
+ }
10291
+ if (shouldRegisterTool("clone_task")) {
10292
+ server.tool("clone_task", "Duplicate a task with optional field overrides.", {
10293
+ task_id: exports_external.string(),
10294
+ title: exports_external.string().optional(),
10295
+ description: exports_external.string().optional(),
10296
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
10297
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
10298
+ project_id: exports_external.string().optional(),
10299
+ plan_id: exports_external.string().optional(),
10300
+ task_list_id: exports_external.string().optional(),
10301
+ assigned_to: exports_external.string().optional(),
10302
+ tags: exports_external.array(exports_external.string()).optional(),
10303
+ estimated_minutes: exports_external.number().optional()
10304
+ }, async ({ task_id, ...overrides }) => {
10305
+ try {
10306
+ const resolvedId = resolveId(task_id);
10307
+ const resolved = { ...overrides };
10308
+ if (resolved.project_id)
10309
+ resolved.project_id = resolveId(resolved.project_id, "projects");
10310
+ if (resolved.plan_id)
10311
+ resolved.plan_id = resolveId(resolved.plan_id, "plans");
10312
+ if (resolved.task_list_id)
10313
+ resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
10314
+ const task = cloneTask(resolvedId, resolved);
10315
+ return { content: [{ type: "text", text: `cloned: ${formatTask(task)}` }] };
10316
+ } catch (e) {
10317
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10318
+ }
10319
+ });
10320
+ }
10321
+ if (shouldRegisterTool("get_task_stats")) {
10322
+ server.tool("get_task_stats", "Get task analytics: counts by status, priority, agent.", {
10323
+ project_id: exports_external.string().optional(),
10324
+ task_list_id: exports_external.string().optional(),
10325
+ agent_id: exports_external.string().optional()
10326
+ }, async ({ project_id, task_list_id, agent_id }) => {
10327
+ try {
10328
+ const filters = {};
10329
+ if (project_id)
10330
+ filters.project_id = resolveId(project_id, "projects");
10331
+ if (task_list_id)
10332
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
10333
+ if (agent_id)
10334
+ filters.agent_id = agent_id;
10335
+ const stats = getTaskStats(Object.keys(filters).length > 0 ? filters : undefined);
10336
+ return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
10337
+ } catch (e) {
10338
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10339
+ }
10340
+ });
10341
+ }
10342
+ if (shouldRegisterTool("get_task_graph")) {
10343
+ server.tool("get_task_graph", "Get full dependency tree for a task.", {
10344
+ id: exports_external.string(),
10345
+ direction: exports_external.enum(["up", "down", "both"]).optional()
10346
+ }, async ({ id, direction }) => {
10347
+ try {
10348
+ let formatNode = function(node, indent) {
10349
+ const prefix = " ".repeat(indent);
10350
+ const idLabel = node.task.short_id || node.task.id.slice(0, 8);
10351
+ const blocked = node.task.is_blocked ? " (blocked: yes)" : "";
10352
+ let out = `${prefix}[${node.task.status}] ${idLabel} | ${node.task.title}${blocked}
10224
10353
  `;
10225
- if (node.depends_on.length > 0) {
10226
- out += `${prefix} Depends on:
10354
+ if (node.depends_on.length > 0) {
10355
+ out += `${prefix} Depends on:
10227
10356
  `;
10228
- for (const dep of node.depends_on) {
10229
- out += formatNode(dep, indent + 2);
10357
+ for (const dep of node.depends_on) {
10358
+ out += formatNode(dep, indent + 2);
10359
+ }
10230
10360
  }
10231
- }
10232
- if (node.blocks.length > 0) {
10233
- out += `${prefix} Blocks:
10361
+ if (node.blocks.length > 0) {
10362
+ out += `${prefix} Blocks:
10234
10363
  `;
10235
- for (const dep of node.blocks) {
10236
- out += formatNode(dep, indent + 2);
10364
+ for (const dep of node.blocks) {
10365
+ out += formatNode(dep, indent + 2);
10366
+ }
10237
10367
  }
10238
- }
10239
- return out;
10240
- };
10241
- const taskId = resolveId(id, "tasks");
10242
- const graph = getTaskGraph(taskId, direction || "both");
10243
- let text = `Task: ${formatNode(graph, 0)}`;
10244
- if (graph.depends_on.length > 0) {
10245
- text += `
10368
+ return out;
10369
+ };
10370
+ const taskId = resolveId(id, "tasks");
10371
+ const graph = getTaskGraph(taskId, direction || "both");
10372
+ let text = `Task: ${formatNode(graph, 0)}`;
10373
+ if (graph.depends_on.length > 0) {
10374
+ text += `
10246
10375
  Depends on:
10247
10376
  `;
10248
- for (const dep of graph.depends_on) {
10249
- text += formatNode(dep, 1);
10377
+ for (const dep of graph.depends_on) {
10378
+ text += formatNode(dep, 1);
10379
+ }
10250
10380
  }
10251
- }
10252
- if (graph.blocks.length > 0) {
10253
- text += `
10381
+ if (graph.blocks.length > 0) {
10382
+ text += `
10254
10383
  Blocks:
10255
10384
  `;
10256
- for (const dep of graph.blocks) {
10257
- text += formatNode(dep, 1);
10385
+ for (const dep of graph.blocks) {
10386
+ text += formatNode(dep, 1);
10387
+ }
10258
10388
  }
10389
+ return { content: [{ type: "text", text }] };
10390
+ } catch (e) {
10391
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10259
10392
  }
10260
- return { content: [{ type: "text", text }] };
10261
- } catch (e) {
10262
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10263
- }
10264
- });
10265
- server.tool("bulk_create_tasks", "Create multiple tasks atomically with dependency support.", {
10266
- tasks: exports_external.array(exports_external.object({
10267
- temp_id: exports_external.string().optional(),
10268
- title: exports_external.string(),
10269
- description: exports_external.string().optional(),
10270
- priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
10271
- status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
10393
+ });
10394
+ }
10395
+ if (shouldRegisterTool("bulk_create_tasks")) {
10396
+ server.tool("bulk_create_tasks", "Create multiple tasks atomically with dependency support.", {
10397
+ tasks: exports_external.array(exports_external.object({
10398
+ temp_id: exports_external.string().optional(),
10399
+ title: exports_external.string(),
10400
+ description: exports_external.string().optional(),
10401
+ priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
10402
+ status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
10403
+ project_id: exports_external.string().optional(),
10404
+ plan_id: exports_external.string().optional(),
10405
+ task_list_id: exports_external.string().optional(),
10406
+ agent_id: exports_external.string().optional(),
10407
+ assigned_to: exports_external.string().optional(),
10408
+ tags: exports_external.array(exports_external.string()).optional(),
10409
+ estimated_minutes: exports_external.number().optional(),
10410
+ depends_on_temp_ids: exports_external.array(exports_external.string()).optional()
10411
+ })),
10272
10412
  project_id: exports_external.string().optional(),
10273
10413
  plan_id: exports_external.string().optional(),
10274
- task_list_id: exports_external.string().optional(),
10275
- agent_id: exports_external.string().optional(),
10276
- assigned_to: exports_external.string().optional(),
10277
- tags: exports_external.array(exports_external.string()).optional(),
10278
- estimated_minutes: exports_external.number().optional(),
10279
- depends_on_temp_ids: exports_external.array(exports_external.string()).optional()
10280
- })),
10281
- project_id: exports_external.string().optional(),
10282
- plan_id: exports_external.string().optional(),
10283
- task_list_id: exports_external.string().optional()
10284
- }, async ({ tasks, project_id, plan_id, task_list_id }) => {
10285
- try {
10286
- const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
10287
- const resolvedPlanId = plan_id ? resolveId(plan_id, "plans") : undefined;
10288
- const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
10289
- const enrichedTasks = tasks.map((t) => ({
10290
- ...t,
10291
- project_id: t.project_id || resolvedProjectId,
10292
- plan_id: t.plan_id || resolvedPlanId,
10293
- task_list_id: t.task_list_id || resolvedTaskListId
10294
- }));
10295
- const result = bulkCreateTasks(enrichedTasks);
10296
- const lines = result.created.map((t) => {
10297
- const tid = t.temp_id ? `[${t.temp_id}] ` : "";
10298
- const sid = t.short_id || t.id.slice(0, 8);
10299
- return ` ${tid}${sid} | ${t.title}`;
10300
- });
10301
- return { content: [{ type: "text", text: `Created ${result.created.length} task(s):
10414
+ task_list_id: exports_external.string().optional()
10415
+ }, async ({ tasks, project_id, plan_id, task_list_id }) => {
10416
+ try {
10417
+ const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
10418
+ const resolvedPlanId = plan_id ? resolveId(plan_id, "plans") : undefined;
10419
+ const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
10420
+ const enrichedTasks = tasks.map((t) => ({
10421
+ ...t,
10422
+ project_id: t.project_id || resolvedProjectId,
10423
+ plan_id: t.plan_id || resolvedPlanId,
10424
+ task_list_id: t.task_list_id || resolvedTaskListId
10425
+ }));
10426
+ const result = bulkCreateTasks(enrichedTasks);
10427
+ const lines = result.created.map((t) => {
10428
+ const tid = t.temp_id ? `[${t.temp_id}] ` : "";
10429
+ const sid = t.short_id || t.id.slice(0, 8);
10430
+ return ` ${tid}${sid} | ${t.title}`;
10431
+ });
10432
+ return { content: [{ type: "text", text: `Created ${result.created.length} task(s):
10302
10433
  ${lines.join(`
10303
10434
  `)}` }] };
10304
- } catch (e) {
10305
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10306
- }
10307
- });
10308
- server.tool("move_task", "Move a task to a different list, project, or plan.", {
10309
- task_id: exports_external.string(),
10310
- task_list_id: exports_external.string().nullable().optional(),
10311
- project_id: exports_external.string().nullable().optional(),
10312
- plan_id: exports_external.string().nullable().optional()
10313
- }, async ({ task_id, ...target }) => {
10314
- try {
10315
- const resolvedId = resolveId(task_id);
10316
- const resolvedTarget = {};
10317
- if (target.task_list_id !== undefined)
10318
- resolvedTarget.task_list_id = target.task_list_id ? resolveId(target.task_list_id, "task_lists") : null;
10319
- if (target.project_id !== undefined)
10320
- resolvedTarget.project_id = target.project_id ? resolveId(target.project_id, "projects") : null;
10321
- if (target.plan_id !== undefined)
10322
- resolvedTarget.plan_id = target.plan_id ? resolveId(target.plan_id, "plans") : null;
10323
- const task = moveTask(resolvedId, resolvedTarget);
10324
- return { content: [{ type: "text", text: `moved: ${formatTask(task)}` }] };
10325
- } catch (e) {
10326
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10327
- }
10328
- });
10329
- server.tool("get_next_task", "Get the best pending task to work on next.", {
10330
- agent_id: exports_external.string().optional(),
10331
- project_id: exports_external.string().optional(),
10332
- task_list_id: exports_external.string().optional(),
10333
- plan_id: exports_external.string().optional(),
10334
- tags: exports_external.array(exports_external.string()).optional()
10335
- }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
10336
- try {
10337
- const filters = {};
10338
- if (project_id)
10339
- filters.project_id = resolveId(project_id, "projects");
10340
- if (task_list_id)
10341
- filters.task_list_id = resolveId(task_list_id, "task_lists");
10342
- if (plan_id)
10343
- filters.plan_id = resolveId(plan_id, "plans");
10344
- if (tags)
10345
- filters.tags = tags;
10346
- const task = getNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
10347
- if (!task) {
10348
- return { content: [{ type: "text", text: "No tasks available \u2014 all pending tasks are blocked, locked, or none exist." }] };
10349
- }
10350
- return { content: [{ type: "text", text: `next: ${formatTask(task)}
10435
+ } catch (e) {
10436
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10437
+ }
10438
+ });
10439
+ }
10440
+ if (shouldRegisterTool("move_task")) {
10441
+ server.tool("move_task", "Move a task to a different list, project, or plan.", {
10442
+ task_id: exports_external.string(),
10443
+ task_list_id: exports_external.string().nullable().optional(),
10444
+ project_id: exports_external.string().nullable().optional(),
10445
+ plan_id: exports_external.string().nullable().optional()
10446
+ }, async ({ task_id, ...target }) => {
10447
+ try {
10448
+ const resolvedId = resolveId(task_id);
10449
+ const resolvedTarget = {};
10450
+ if (target.task_list_id !== undefined)
10451
+ resolvedTarget.task_list_id = target.task_list_id ? resolveId(target.task_list_id, "task_lists") : null;
10452
+ if (target.project_id !== undefined)
10453
+ resolvedTarget.project_id = target.project_id ? resolveId(target.project_id, "projects") : null;
10454
+ if (target.plan_id !== undefined)
10455
+ resolvedTarget.plan_id = target.plan_id ? resolveId(target.plan_id, "plans") : null;
10456
+ const task = moveTask(resolvedId, resolvedTarget);
10457
+ return { content: [{ type: "text", text: `moved: ${formatTask(task)}` }] };
10458
+ } catch (e) {
10459
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10460
+ }
10461
+ });
10462
+ }
10463
+ if (shouldRegisterTool("get_next_task")) {
10464
+ server.tool("get_next_task", "Get the best pending task to work on next.", {
10465
+ agent_id: exports_external.string().optional(),
10466
+ project_id: exports_external.string().optional(),
10467
+ task_list_id: exports_external.string().optional(),
10468
+ plan_id: exports_external.string().optional(),
10469
+ tags: exports_external.array(exports_external.string()).optional()
10470
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
10471
+ try {
10472
+ const filters = {};
10473
+ if (project_id)
10474
+ filters.project_id = resolveId(project_id, "projects");
10475
+ if (task_list_id)
10476
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
10477
+ if (plan_id)
10478
+ filters.plan_id = resolveId(plan_id, "plans");
10479
+ if (tags)
10480
+ filters.tags = tags;
10481
+ const task = getNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
10482
+ if (!task) {
10483
+ return { content: [{ type: "text", text: "No tasks available \u2014 all pending tasks are blocked, locked, or none exist." }] };
10484
+ }
10485
+ return { content: [{ type: "text", text: `next: ${formatTask(task)}
10351
10486
  ${formatTaskDetail(task)}` }] };
10352
- } catch (e) {
10353
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10354
- }
10355
- });
10356
- server.tool("get_active_work", "See all in-progress tasks and who is working on them.", {
10357
- project_id: exports_external.string().optional(),
10358
- task_list_id: exports_external.string().optional()
10359
- }, async ({ project_id, task_list_id }) => {
10360
- try {
10361
- const filters = {};
10362
- if (project_id)
10363
- filters.project_id = resolveId(project_id, "projects");
10364
- if (task_list_id)
10365
- filters.task_list_id = resolveId(task_list_id, "task_lists");
10366
- const work = getActiveWork(Object.keys(filters).length > 0 ? filters : undefined);
10367
- if (work.length === 0) {
10368
- return { content: [{ type: "text", text: "No active work \u2014 no tasks are currently in progress." }] };
10369
- }
10370
- const text = work.map((w) => {
10371
- const id = w.short_id || w.id.slice(0, 8);
10372
- const agent = w.assigned_to || w.locked_by || "unassigned";
10373
- const since = w.updated_at;
10374
- return `${agent.padEnd(12)} | ${w.priority.padEnd(8)} | ${id} | ${w.title} (since ${since})`;
10375
- }).join(`
10487
+ } catch (e) {
10488
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10489
+ }
10490
+ });
10491
+ }
10492
+ if (shouldRegisterTool("get_active_work")) {
10493
+ server.tool("get_active_work", "See all in-progress tasks and who is working on them.", {
10494
+ project_id: exports_external.string().optional(),
10495
+ task_list_id: exports_external.string().optional()
10496
+ }, async ({ project_id, task_list_id }) => {
10497
+ try {
10498
+ const filters = {};
10499
+ if (project_id)
10500
+ filters.project_id = resolveId(project_id, "projects");
10501
+ if (task_list_id)
10502
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
10503
+ const work = getActiveWork(Object.keys(filters).length > 0 ? filters : undefined);
10504
+ if (work.length === 0) {
10505
+ return { content: [{ type: "text", text: "No active work \u2014 no tasks are currently in progress." }] };
10506
+ }
10507
+ const text = work.map((w) => {
10508
+ const id = w.short_id || w.id.slice(0, 8);
10509
+ const agent = w.assigned_to || w.locked_by || "unassigned";
10510
+ const since = w.updated_at;
10511
+ return `${agent.padEnd(12)} | ${w.priority.padEnd(8)} | ${id} | ${w.title} (since ${since})`;
10512
+ }).join(`
10376
10513
  `);
10377
- return { content: [{ type: "text", text: `${work.length} active task(s):
10514
+ return { content: [{ type: "text", text: `${work.length} active task(s):
10378
10515
  ${text}` }] };
10379
- } catch (e) {
10380
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10381
- }
10382
- });
10383
- server.tool("get_tasks_changed_since", "Get tasks modified after a timestamp for incremental sync.", {
10384
- since: exports_external.string(),
10385
- project_id: exports_external.string().optional(),
10386
- task_list_id: exports_external.string().optional()
10387
- }, async ({ since, project_id, task_list_id }) => {
10388
- try {
10389
- const filters = {};
10390
- if (project_id)
10391
- filters.project_id = resolveId(project_id, "projects");
10392
- if (task_list_id)
10393
- filters.task_list_id = resolveId(task_list_id, "task_lists");
10394
- const tasks = getTasksChangedSince(since, Object.keys(filters).length > 0 ? filters : undefined);
10395
- if (tasks.length === 0) {
10396
- return { content: [{ type: "text", text: `No tasks changed since ${since}.` }] };
10397
- }
10398
- const text = tasks.map((t) => {
10399
- const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
10400
- return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned} (updated: ${t.updated_at})`;
10401
- }).join(`
10516
+ } catch (e) {
10517
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10518
+ }
10519
+ });
10520
+ }
10521
+ if (shouldRegisterTool("get_tasks_changed_since")) {
10522
+ server.tool("get_tasks_changed_since", "Get tasks modified after a timestamp for incremental sync.", {
10523
+ since: exports_external.string(),
10524
+ project_id: exports_external.string().optional(),
10525
+ task_list_id: exports_external.string().optional()
10526
+ }, async ({ since, project_id, task_list_id }) => {
10527
+ try {
10528
+ const filters = {};
10529
+ if (project_id)
10530
+ filters.project_id = resolveId(project_id, "projects");
10531
+ if (task_list_id)
10532
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
10533
+ const tasks = getTasksChangedSince(since, Object.keys(filters).length > 0 ? filters : undefined);
10534
+ if (tasks.length === 0) {
10535
+ return { content: [{ type: "text", text: `No tasks changed since ${since}.` }] };
10536
+ }
10537
+ const text = tasks.map((t) => {
10538
+ const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
10539
+ return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned} (updated: ${t.updated_at})`;
10540
+ }).join(`
10402
10541
  `);
10403
- return { content: [{ type: "text", text: `${tasks.length} task(s) changed since ${since}:
10542
+ return { content: [{ type: "text", text: `${tasks.length} task(s) changed since ${since}:
10404
10543
  ${text}` }] };
10405
- } catch (e) {
10406
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10407
- }
10408
- });
10409
- server.tool("claim_next_task", "Atomically claim, lock, and start the best pending task.", {
10410
- agent_id: exports_external.string(),
10411
- project_id: exports_external.string().optional(),
10412
- task_list_id: exports_external.string().optional(),
10413
- plan_id: exports_external.string().optional(),
10414
- tags: exports_external.array(exports_external.string()).optional()
10415
- }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
10416
- try {
10417
- const filters = {};
10418
- if (project_id)
10419
- filters.project_id = resolveId(project_id, "projects");
10420
- if (task_list_id)
10421
- filters.task_list_id = resolveId(task_list_id, "task_lists");
10422
- if (plan_id)
10423
- filters.plan_id = resolveId(plan_id, "plans");
10424
- if (tags)
10425
- filters.tags = tags;
10426
- const task = claimNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
10427
- if (!task) {
10428
- return { content: [{ type: "text", text: "No tasks available to claim." }] };
10429
- }
10430
- return { content: [{ type: "text", text: `claimed: ${formatTask(task)}
10544
+ } catch (e) {
10545
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10546
+ }
10547
+ });
10548
+ }
10549
+ if (shouldRegisterTool("claim_next_task")) {
10550
+ server.tool("claim_next_task", "Atomically claim, lock, and start the best pending task.", {
10551
+ agent_id: exports_external.string(),
10552
+ project_id: exports_external.string().optional(),
10553
+ task_list_id: exports_external.string().optional(),
10554
+ plan_id: exports_external.string().optional(),
10555
+ tags: exports_external.array(exports_external.string()).optional()
10556
+ }, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
10557
+ try {
10558
+ const filters = {};
10559
+ if (project_id)
10560
+ filters.project_id = resolveId(project_id, "projects");
10561
+ if (task_list_id)
10562
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
10563
+ if (plan_id)
10564
+ filters.plan_id = resolveId(plan_id, "plans");
10565
+ if (tags)
10566
+ filters.tags = tags;
10567
+ const task = claimNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
10568
+ if (!task) {
10569
+ return { content: [{ type: "text", text: "No tasks available to claim." }] };
10570
+ }
10571
+ return { content: [{ type: "text", text: `claimed: ${formatTask(task)}
10431
10572
  ${formatTaskDetail(task)}` }] };
10432
- } catch (e) {
10433
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10434
- }
10435
- });
10436
- server.tool("get_stale_tasks", "Find stale in_progress tasks with no recent activity.", {
10437
- stale_minutes: exports_external.number().optional(),
10438
- project_id: exports_external.string().optional(),
10439
- task_list_id: exports_external.string().optional()
10440
- }, async ({ stale_minutes, project_id, task_list_id }) => {
10441
- try {
10442
- const filters = {};
10443
- if (project_id)
10444
- filters.project_id = resolveId(project_id, "projects");
10445
- if (task_list_id)
10446
- filters.task_list_id = resolveId(task_list_id, "task_lists");
10447
- const tasks = getStaleTasks(stale_minutes || 30, Object.keys(filters).length > 0 ? filters : undefined);
10448
- if (tasks.length === 0) {
10449
- return { content: [{ type: "text", text: "No stale tasks found." }] };
10450
- }
10451
- const text = tasks.map((t) => {
10452
- const id = t.short_id || t.id.slice(0, 8);
10453
- const agent = t.locked_by || t.assigned_to || "unknown";
10454
- const staleFor = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
10455
- return `${id} | ${agent} | ${t.title} (stale ${staleFor}min)`;
10456
- }).join(`
10573
+ } catch (e) {
10574
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10575
+ }
10576
+ });
10577
+ }
10578
+ if (shouldRegisterTool("get_stale_tasks")) {
10579
+ server.tool("get_stale_tasks", "Find stale in_progress tasks with no recent activity.", {
10580
+ stale_minutes: exports_external.number().optional(),
10581
+ project_id: exports_external.string().optional(),
10582
+ task_list_id: exports_external.string().optional()
10583
+ }, async ({ stale_minutes, project_id, task_list_id }) => {
10584
+ try {
10585
+ const filters = {};
10586
+ if (project_id)
10587
+ filters.project_id = resolveId(project_id, "projects");
10588
+ if (task_list_id)
10589
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
10590
+ const tasks = getStaleTasks(stale_minutes || 30, Object.keys(filters).length > 0 ? filters : undefined);
10591
+ if (tasks.length === 0) {
10592
+ return { content: [{ type: "text", text: "No stale tasks found." }] };
10593
+ }
10594
+ const text = tasks.map((t) => {
10595
+ const id = t.short_id || t.id.slice(0, 8);
10596
+ const agent = t.locked_by || t.assigned_to || "unknown";
10597
+ const staleFor = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
10598
+ return `${id} | ${agent} | ${t.title} (stale ${staleFor}min)`;
10599
+ }).join(`
10457
10600
  `);
10458
- return { content: [{ type: "text", text: `${tasks.length} stale task(s):
10601
+ return { content: [{ type: "text", text: `${tasks.length} stale task(s):
10459
10602
  ${text}` }] };
10460
- } catch (e) {
10461
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10462
- }
10463
- });
10464
- server.tool("get_status", "Get a full project health snapshot \u2014 counts, active work, next task, stale/overdue summary.", {
10465
- agent_id: exports_external.string().optional(),
10466
- project_id: exports_external.string().optional(),
10467
- task_list_id: exports_external.string().optional()
10468
- }, async ({ agent_id, project_id, task_list_id }) => {
10469
- try {
10470
- const filters = {};
10471
- if (project_id)
10472
- filters.project_id = resolveId(project_id, "projects");
10473
- if (task_list_id)
10474
- filters.task_list_id = resolveId(task_list_id, "task_lists");
10475
- const status = getStatus(Object.keys(filters).length > 0 ? filters : undefined, agent_id);
10476
- const lines = [
10477
- `Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done | ${status.total} total`
10478
- ];
10479
- if (status.stale_count > 0)
10480
- lines.push(`\u26A0\uFE0F ${status.stale_count} stale (stuck in_progress)`);
10481
- if (status.overdue_recurring > 0)
10482
- lines.push(`\uD83D\uDD01 ${status.overdue_recurring} overdue recurring`);
10483
- if (status.active_work.length > 0) {
10484
- lines.push(`
10603
+ } catch (e) {
10604
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10605
+ }
10606
+ });
10607
+ }
10608
+ if (shouldRegisterTool("get_status")) {
10609
+ server.tool("get_status", "Get a full project health snapshot \u2014 counts, active work, next task, stale/overdue summary.", {
10610
+ agent_id: exports_external.string().optional(),
10611
+ project_id: exports_external.string().optional(),
10612
+ task_list_id: exports_external.string().optional()
10613
+ }, async ({ agent_id, project_id, task_list_id }) => {
10614
+ try {
10615
+ const filters = {};
10616
+ if (project_id)
10617
+ filters.project_id = resolveId(project_id, "projects");
10618
+ if (task_list_id)
10619
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
10620
+ const status = getStatus(Object.keys(filters).length > 0 ? filters : undefined, agent_id);
10621
+ const lines = [
10622
+ `Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done | ${status.total} total`
10623
+ ];
10624
+ if (status.stale_count > 0)
10625
+ lines.push(`\u26A0\uFE0F ${status.stale_count} stale (stuck in_progress)`);
10626
+ if (status.overdue_recurring > 0)
10627
+ lines.push(`\uD83D\uDD01 ${status.overdue_recurring} overdue recurring`);
10628
+ if (status.active_work.length > 0) {
10629
+ lines.push(`
10485
10630
  Active (${status.active_work.length}):`);
10486
- for (const w of status.active_work.slice(0, 5)) {
10487
- const id = w.short_id || w.id.slice(0, 8);
10488
- lines.push(` ${id} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
10631
+ for (const w of status.active_work.slice(0, 5)) {
10632
+ const id = w.short_id || w.id.slice(0, 8);
10633
+ lines.push(` ${id} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
10634
+ }
10489
10635
  }
10490
- }
10491
- if (status.next_task) {
10492
- lines.push(`
10636
+ if (status.next_task) {
10637
+ lines.push(`
10493
10638
  Next up:`);
10494
- lines.push(` ${formatTask(status.next_task)}`);
10495
- } else {
10496
- lines.push(`
10639
+ lines.push(` ${formatTask(status.next_task)}`);
10640
+ } else {
10641
+ lines.push(`
10497
10642
  No pending tasks available.`);
10498
- }
10499
- return { content: [{ type: "text", text: lines.join(`
10643
+ }
10644
+ return { content: [{ type: "text", text: lines.join(`
10500
10645
  `) }] };
10501
- } catch (e) {
10502
- return { content: [{ type: "text", text: formatError(e) }], isError: true };
10503
- }
10504
- });
10505
- server.tool("search_tools", "List all tool names, optionally filtered by substring.", { query: exports_external.string().optional() }, async ({ query }) => {
10506
- const all = [
10507
- "create_task",
10508
- "list_tasks",
10509
- "get_task",
10510
- "update_task",
10511
- "delete_task",
10512
- "start_task",
10513
- "complete_task",
10514
- "fail_task",
10515
- "lock_task",
10516
- "unlock_task",
10517
- "approve_task",
10518
- "add_dependency",
10519
- "remove_dependency",
10520
- "add_comment",
10521
- "create_project",
10522
- "list_projects",
10523
- "create_plan",
10524
- "list_plans",
10525
- "get_plan",
10526
- "update_plan",
10527
- "delete_plan",
10528
- "register_agent",
10529
- "list_agents",
10530
- "get_agent",
10531
- "rename_agent",
10532
- "delete_agent",
10533
- "get_my_tasks",
10534
- "get_org_chart",
10535
- "set_reports_to",
10536
- "create_task_list",
10537
- "list_task_lists",
10538
- "get_task_list",
10539
- "update_task_list",
10540
- "delete_task_list",
10541
- "search_tasks",
10542
- "sync",
10543
- "clone_task",
10544
- "move_task",
10545
- "get_next_task",
10546
- "claim_next_task",
10547
- "get_task_history",
10548
- "get_recent_activity",
10549
- "create_webhook",
10550
- "list_webhooks",
10551
- "delete_webhook",
10552
- "create_template",
10553
- "list_templates",
10554
- "create_task_from_template",
10555
- "delete_template",
10556
- "bulk_update_tasks",
10557
- "bulk_create_tasks",
10558
- "get_task_stats",
10559
- "get_task_graph",
10560
- "get_active_work",
10561
- "get_tasks_changed_since",
10562
- "get_stale_tasks",
10563
- "get_status",
10564
- "search_tools",
10565
- "describe_tools"
10566
- ];
10567
- const q = query?.toLowerCase();
10568
- const matches = q ? all.filter((n) => n.includes(q)) : all;
10569
- return { content: [{ type: "text", text: matches.join(", ") }] };
10570
- });
10571
- server.tool("describe_tools", "Get detailed parameter info for specific tools by name.", { names: exports_external.array(exports_external.string()) }, async ({ names }) => {
10572
- const descriptions = {
10573
- create_task: `Create a new task.
10646
+ } catch (e) {
10647
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10648
+ }
10649
+ });
10650
+ }
10651
+ if (shouldRegisterTool("search_tools")) {
10652
+ server.tool("search_tools", "List all tool names, optionally filtered by substring.", { query: exports_external.string().optional() }, async ({ query }) => {
10653
+ const all = [
10654
+ "create_task",
10655
+ "list_tasks",
10656
+ "get_task",
10657
+ "update_task",
10658
+ "delete_task",
10659
+ "start_task",
10660
+ "complete_task",
10661
+ "fail_task",
10662
+ "lock_task",
10663
+ "unlock_task",
10664
+ "approve_task",
10665
+ "add_dependency",
10666
+ "remove_dependency",
10667
+ "add_comment",
10668
+ "create_project",
10669
+ "list_projects",
10670
+ "create_plan",
10671
+ "list_plans",
10672
+ "get_plan",
10673
+ "update_plan",
10674
+ "delete_plan",
10675
+ "register_agent",
10676
+ "list_agents",
10677
+ "get_agent",
10678
+ "rename_agent",
10679
+ "delete_agent",
10680
+ "get_my_tasks",
10681
+ "get_org_chart",
10682
+ "set_reports_to",
10683
+ "create_task_list",
10684
+ "list_task_lists",
10685
+ "get_task_list",
10686
+ "update_task_list",
10687
+ "delete_task_list",
10688
+ "search_tasks",
10689
+ "sync",
10690
+ "clone_task",
10691
+ "move_task",
10692
+ "get_next_task",
10693
+ "claim_next_task",
10694
+ "get_task_history",
10695
+ "get_recent_activity",
10696
+ "create_webhook",
10697
+ "list_webhooks",
10698
+ "delete_webhook",
10699
+ "create_template",
10700
+ "list_templates",
10701
+ "create_task_from_template",
10702
+ "delete_template",
10703
+ "bulk_update_tasks",
10704
+ "bulk_create_tasks",
10705
+ "get_task_stats",
10706
+ "get_task_graph",
10707
+ "get_active_work",
10708
+ "get_tasks_changed_since",
10709
+ "get_stale_tasks",
10710
+ "get_status",
10711
+ "search_tools",
10712
+ "describe_tools"
10713
+ ].filter((name) => shouldRegisterTool(name));
10714
+ const q = query?.toLowerCase();
10715
+ const matches = q ? all.filter((n) => n.includes(q)) : all;
10716
+ return { content: [{ type: "text", text: matches.join(", ") }] };
10717
+ });
10718
+ }
10719
+ if (shouldRegisterTool("describe_tools")) {
10720
+ server.tool("describe_tools", "Get detailed parameter info for specific tools by name.", { names: exports_external.array(exports_external.string()) }, async ({ names }) => {
10721
+ const descriptions = {
10722
+ create_task: `Create a new task.
10574
10723
  Params: title(string, req), description(string), priority(low|medium|high|critical, default:medium), status(pending|in_progress|completed|failed|cancelled, default:pending), project_id(string), parent_id(string \u2014 creates subtask), plan_id(string), task_list_id(string), agent_id(string), assigned_to(string), tags(string[]), metadata(object), estimated_minutes(number), requires_approval(boolean), recurrence_rule(string \u2014 e.g. 'every day', 'every weekday', 'every 2 weeks', 'every monday'), session_id(string), working_dir(string)
10575
10724
  Example: {title: 'Daily standup', recurrence_rule: 'every weekday', priority: 'medium'}`,
10576
- list_tasks: `List tasks with optional filters. Supports pagination.
10725
+ list_tasks: `List tasks with optional filters. Supports pagination.
10577
10726
  Params: status(string|string[]), priority(string|string[]), project_id(string), plan_id(string), task_list_id(string), assigned_to(string), tags(string[]), has_recurrence(boolean \u2014 true=only recurring, false=only non-recurring), limit(number), offset(number)
10578
10727
  Example: {status: ['pending', 'in_progress'], has_recurrence: true, limit: 20}`,
10579
- get_task: `Get full task details with subtasks, deps, and comments.
10728
+ get_task: `Get full task details with subtasks, deps, and comments.
10580
10729
  Params: id(string, req \u2014 task ID, short_id like 'APP-00001', or partial ID)
10581
10730
  Example: {id: 'a1b2c3d4'}`,
10582
- update_task: `Update task fields. Requires version for optimistic locking (get it from get_task first).
10731
+ update_task: `Update task fields. Requires version for optimistic locking (get it from get_task first).
10583
10732
  Params: id(string, req), version(number, req), title(string), description(string), status(pending|in_progress|completed|failed|cancelled), priority(low|medium|high|critical), assigned_to(string), tags(string[]), metadata(object), plan_id(string), task_list_id(string)
10584
10733
  Example: {id: 'a1b2c3d4', version: 3, status: 'completed'}`,
10585
- delete_task: `Delete a task permanently. Subtasks cascade-delete. Dependencies removed.
10734
+ delete_task: `Delete a task permanently. Subtasks cascade-delete. Dependencies removed.
10586
10735
  Params: id(string, req)
10587
10736
  Example: {id: 'a1b2c3d4'}`,
10588
- start_task: `Claim, lock, and set task status to in_progress in one call.
10737
+ start_task: `Claim, lock, and set task status to in_progress in one call.
10589
10738
  Params: id(string, req), agent_id(string, req \u2014 your 8-char agent ID)
10590
10739
  Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
10591
- complete_task: `Mark task completed, release lock, set completed_at timestamp. For recurring tasks, auto-spawns next instance unless skip_recurrence is true.
10740
+ complete_task: `Mark task completed, release lock, set completed_at timestamp. For recurring tasks, auto-spawns next instance unless skip_recurrence is true.
10592
10741
  Params: id(string, req), agent_id(string, optional \u2014 required if locked by different agent), skip_recurrence(boolean \u2014 set true to prevent auto-creating next recurring instance)
10593
10742
  Example: {id: 'a1b2c3d4', skip_recurrence: false}`,
10594
- lock_task: `Acquire exclusive lock on a task. Locks auto-expire after 30 min. Re-locking by same agent is idempotent.
10743
+ lock_task: `Acquire exclusive lock on a task. Locks auto-expire after 30 min. Re-locking by same agent is idempotent.
10595
10744
  Params: id(string, req), agent_id(string, req)
10596
10745
  Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
10597
- unlock_task: `Release exclusive lock on a task.
10746
+ unlock_task: `Release exclusive lock on a task.
10598
10747
  Params: id(string, req), agent_id(string, optional \u2014 omit to force-unlock)
10599
10748
  Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
10600
- approve_task: `Approve a task with requires_approval=true. Must be approved before completion.
10749
+ approve_task: `Approve a task with requires_approval=true. Must be approved before completion.
10601
10750
  Params: id(string, req), agent_id(string, optional \u2014 defaults to 'system')
10602
10751
  Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
10603
- fail_task: `Mark a task as failed with structured reason and optional auto-retry. Stores failure info in metadata._failure, releases lock.
10752
+ fail_task: `Mark a task as failed with structured reason and optional auto-retry. Stores failure info in metadata._failure, releases lock.
10604
10753
  Params: id(string, req), agent_id(string, optional), reason(string), error_code(string \u2014 e.g. 'TIMEOUT'), retry(boolean \u2014 create new pending copy), retry_after(ISO date)
10605
10754
  Example: {id: 'a1b2c3d4', reason: 'Build timeout', error_code: 'TIMEOUT', retry: true}`,
10606
- add_dependency: `Add a dependency: task_id depends on depends_on. Prevents cycles via BFS.
10755
+ add_dependency: `Add a dependency: task_id depends on depends_on. Prevents cycles via BFS.
10607
10756
  Params: task_id(string, req), depends_on(string, req)
10608
10757
  Example: {task_id: 'abc12345', depends_on: 'def67890'}`,
10609
- remove_dependency: `Remove a dependency link between two tasks.
10758
+ remove_dependency: `Remove a dependency link between two tasks.
10610
10759
  Params: task_id(string, req), depends_on(string, req)
10611
10760
  Example: {task_id: 'abc12345', depends_on: 'def67890'}`,
10612
- add_comment: `Add a comment/note to a task. Comments are append-only.
10761
+ add_comment: `Add a comment/note to a task. Comments are append-only.
10613
10762
  Params: task_id(string, req), content(string, req), agent_id(string), session_id(string)
10614
10763
  Example: {task_id: 'a1b2c3d4', content: 'Blocked by API rate limit'}`,
10615
- create_project: `Register a new project. Auto-generates task prefix for short IDs (e.g. APP-00001).
10764
+ create_project: `Register a new project. Auto-generates task prefix for short IDs (e.g. APP-00001).
10616
10765
  Params: name(string, req), path(string, req \u2014 unique absolute path), description(string), task_list_id(string)
10617
10766
  Example: {name: 'my-app', path: '/Users/dev/my-app'}`,
10618
- list_projects: "List all registered projects. No params.",
10619
- create_plan: `Create a plan to group related tasks.
10767
+ list_projects: "List all registered projects. No params.",
10768
+ create_plan: `Create a plan to group related tasks.
10620
10769
  Params: name(string, req), project_id(string), description(string), status(active|completed|archived, default:active), task_list_id(string), agent_id(string)
10621
10770
  Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
10622
- list_plans: `List all plans, optionally filtered by project.
10771
+ list_plans: `List all plans, optionally filtered by project.
10623
10772
  Params: project_id(string)
10624
10773
  Example: {project_id: 'a1b2c3d4'}`,
10625
- get_plan: `Get plan details (name, status, description, timestamps).
10774
+ get_plan: `Get plan details (name, status, description, timestamps).
10626
10775
  Params: id(string, req)
10627
10776
  Example: {id: 'a1b2c3d4'}`,
10628
- update_plan: `Update plan fields.
10777
+ update_plan: `Update plan fields.
10629
10778
  Params: id(string, req), name(string), description(string), status(active|completed|archived), task_list_id(string), agent_id(string)
10630
10779
  Example: {id: 'a1b2c3d4', status: 'completed'}`,
10631
- delete_plan: `Delete a plan. Tasks in the plan are orphaned, not deleted.
10780
+ delete_plan: `Delete a plan. Tasks in the plan are orphaned, not deleted.
10632
10781
  Params: id(string, req)
10633
10782
  Example: {id: 'a1b2c3d4'}`,
10634
- register_agent: `Register an agent (idempotent by name). Returns existing agent if name matches.
10783
+ register_agent: `Register an agent (idempotent by name). Returns existing agent if name matches.
10635
10784
  Params: name(string, req \u2014 e.g. 'maximus'), description(string)
10636
10785
  Example: {name: 'maximus', description: 'Backend developer'}`,
10637
- list_agents: "List all registered agents with IDs, names, and last seen timestamps. No params.",
10638
- get_agent: `Get agent details by ID or name. Provide one of id or name.
10786
+ list_agents: "List all registered agents with IDs, names, and last seen timestamps. No params.",
10787
+ get_agent: `Get agent details by ID or name. Provide one of id or name.
10639
10788
  Params: id(string), name(string)
10640
10789
  Example: {name: 'maximus'}`,
10641
- rename_agent: `Rename an agent. Resolve by id or current name.
10790
+ rename_agent: `Rename an agent. Resolve by id or current name.
10642
10791
  Params: id(string), name(string \u2014 current name), new_name(string, req)
10643
10792
  Example: {name: 'old-name', new_name: 'new-name'}`,
10644
- delete_agent: `Delete an agent permanently. Resolve by id or name.
10793
+ delete_agent: `Delete an agent permanently. Resolve by id or name.
10645
10794
  Params: id(string), name(string)
10646
10795
  Example: {name: 'maximus'}`,
10647
- get_my_tasks: `Get all tasks assigned to/created by an agent, with stats (pending/active/done/rate).
10796
+ get_my_tasks: `Get all tasks assigned to/created by an agent, with stats (pending/active/done/rate).
10648
10797
  Params: agent_name(string, req)
10649
10798
  Example: {agent_name: 'maximus'}`,
10650
- get_org_chart: "Get agent org chart showing reporting hierarchy. No params.",
10651
- set_reports_to: `Set who an agent reports to in the org chart. Omit manager_name for top-level.
10799
+ get_org_chart: "Get agent org chart showing reporting hierarchy. No params.",
10800
+ set_reports_to: `Set who an agent reports to in the org chart. Omit manager_name for top-level.
10652
10801
  Params: agent_name(string, req), manager_name(string, optional)
10653
10802
  Example: {agent_name: 'brutus', manager_name: 'maximus'}`,
10654
- create_task_list: `Create a task list \u2014 a container/folder for organizing tasks.
10803
+ create_task_list: `Create a task list \u2014 a container/folder for organizing tasks.
10655
10804
  Params: name(string, req), slug(string \u2014 auto-generated if omitted), project_id(string), description(string)
10656
10805
  Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
10657
- list_task_lists: `List all task lists, optionally filtered by project.
10806
+ list_task_lists: `List all task lists, optionally filtered by project.
10658
10807
  Params: project_id(string)
10659
10808
  Example: {project_id: 'a1b2c3d4'}`,
10660
- get_task_list: `Get task list details (name, slug, project, metadata).
10809
+ get_task_list: `Get task list details (name, slug, project, metadata).
10661
10810
  Params: id(string, req)
10662
10811
  Example: {id: 'a1b2c3d4'}`,
10663
- update_task_list: `Update a task list's name or description.
10812
+ update_task_list: `Update a task list's name or description.
10664
10813
  Params: id(string, req), name(string), description(string)
10665
10814
  Example: {id: 'a1b2c3d4', name: 'Sprint 2'}`,
10666
- delete_task_list: `Delete a task list. Tasks are orphaned (not deleted).
10815
+ delete_task_list: `Delete a task list. Tasks are orphaned (not deleted).
10667
10816
  Params: id(string, req)
10668
10817
  Example: {id: 'a1b2c3d4'}`,
10669
- search_tasks: `Full-text search across task titles, descriptions, and tags. Supports filters.
10818
+ search_tasks: `Full-text search across task titles, descriptions, and tags. Supports filters.
10670
10819
  Params: query(string, req), project_id(string), task_list_id(string), status(string|string[]), priority(string|string[]), assigned_to(string), agent_id(string), created_after(ISO date), updated_after(ISO date), has_dependencies(boolean), is_blocked(boolean)
10671
10820
  Example: {query: 'auth bug', status: 'pending'}`,
10672
- get_next_task: `Get the optimal next task to work on \u2014 finds highest-priority pending task that is not blocked or locked. Prefers tasks assigned to the given agent.
10821
+ get_next_task: `Get the optimal next task to work on \u2014 finds highest-priority pending task that is not blocked or locked. Prefers tasks assigned to the given agent.
10673
10822
  Params: agent_id(string \u2014 prefers your tasks), project_id(string), task_list_id(string), plan_id(string), tags(string[])
10674
10823
  Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
10675
- claim_next_task: `Atomically find the best pending task, lock it, and start it \u2014 one call instead of get_next_task + start_task. Eliminates race conditions between agents.
10824
+ claim_next_task: `Atomically find the best pending task, lock it, and start it \u2014 one call instead of get_next_task + start_task. Eliminates race conditions between agents.
10676
10825
  Params: agent_id(string, req \u2014 used for lock and assignment), project_id(string), task_list_id(string), plan_id(string), tags(string[])
10677
10826
  Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
10678
- sync: `Sync tasks between local DB and agent task list (e.g. Claude Code).
10827
+ sync: `Sync tasks between local DB and agent task list (e.g. Claude Code).
10679
10828
  Params: agent(string, default:'claude'), task_list_id(string), all_agents(boolean), project_id(string), direction(push|pull|both, default:both), prefer(local|remote, default:remote)
10680
10829
  Example: {agent: 'claude', direction: 'push'}`,
10681
- clone_task: `Duplicate a task with optional field overrides. Creates new independent copy.
10830
+ clone_task: `Duplicate a task with optional field overrides. Creates new independent copy.
10682
10831
  Params: task_id(string, req), title(string), description(string), priority(low|medium|high|critical), status(pending|in_progress|completed|failed|cancelled), project_id(string), plan_id(string), task_list_id(string), assigned_to(string), tags(string[]), estimated_minutes(number)
10683
10832
  Example: {task_id: 'a1b2c3d4', title: 'Cloned task', assigned_to: 'brutus'}`,
10684
- move_task: `Move a task to a different list, project, or plan.
10833
+ move_task: `Move a task to a different list, project, or plan.
10685
10834
  Params: task_id(string, req), task_list_id(string|null), project_id(string|null), plan_id(string|null)
10686
10835
  Example: {task_id: 'a1b2c3d4', task_list_id: 'e5f6g7h8'}`,
10687
- bulk_update_tasks: `Update multiple tasks at once with the same changes.
10836
+ bulk_update_tasks: `Update multiple tasks at once with the same changes.
10688
10837
  Params: task_ids(string[], req), status(pending|in_progress|completed|failed|cancelled), priority(low|medium|high|critical), assigned_to(string), tags(string[])
10689
10838
  Example: {task_ids: ['abc12345', 'def67890'], status: 'completed'}`,
10690
- bulk_create_tasks: `Create multiple tasks atomically. Supports inter-task dependencies via temp_id references.
10839
+ bulk_create_tasks: `Create multiple tasks atomically. Supports inter-task dependencies via temp_id references.
10691
10840
  Params: tasks(array, req \u2014 [{temp_id, title, description, priority, status, project_id, plan_id, task_list_id, agent_id, assigned_to, tags, estimated_minutes, depends_on_temp_ids}]), project_id(string \u2014 default for all), plan_id(string \u2014 default for all), task_list_id(string \u2014 default for all)
10692
10841
  Example: {tasks: [{temp_id: 'a', title: 'First'}, {temp_id: 'b', title: 'Second', depends_on_temp_ids: ['a']}]}`,
10693
- get_task_stats: `Get task analytics: counts by status, priority, agent, and completion rate. All via SQL.
10842
+ get_task_stats: `Get task analytics: counts by status, priority, agent, and completion rate. All via SQL.
10694
10843
  Params: project_id(string), task_list_id(string), agent_id(string)
10695
10844
  Example: {project_id: 'a1b2c3d4'}`,
10696
- get_task_graph: `Get full dependency tree for a task \u2014 upstream blockers and downstream dependents.
10845
+ get_task_graph: `Get full dependency tree for a task \u2014 upstream blockers and downstream dependents.
10697
10846
  Params: id(string, req), direction(up|down|both, default:both)
10698
10847
  Example: {id: 'a1b2c3d4', direction: 'up'}`,
10699
- get_task_history: `Get audit log for a task \u2014 all field changes with timestamps and actors.
10848
+ get_task_history: `Get audit log for a task \u2014 all field changes with timestamps and actors.
10700
10849
  Params: task_id(string, req)
10701
10850
  Example: {task_id: 'a1b2c3d4'}`,
10702
- get_recent_activity: `Get recent task changes across all tasks \u2014 global activity feed.
10851
+ get_recent_activity: `Get recent task changes across all tasks \u2014 global activity feed.
10703
10852
  Params: limit(number, default:50)
10704
10853
  Example: {limit: 20}`,
10705
- create_webhook: `Register a webhook for task change events.
10854
+ create_webhook: `Register a webhook for task change events.
10706
10855
  Params: url(string, req), events(string[] \u2014 empty=all), secret(string \u2014 HMAC signing)
10707
10856
  Example: {url: 'https://example.com/hook', events: ['task.created', 'task.completed']}`,
10708
- list_webhooks: "List all registered webhooks. No params.",
10709
- delete_webhook: `Delete a webhook by ID.
10857
+ list_webhooks: "List all registered webhooks. No params.",
10858
+ delete_webhook: `Delete a webhook by ID.
10710
10859
  Params: id(string, req)
10711
10860
  Example: {id: 'a1b2c3d4'}`,
10712
- create_template: `Create a reusable task template.
10861
+ create_template: `Create a reusable task template.
10713
10862
  Params: name(string, req), title_pattern(string, req \u2014 e.g. 'Fix: {description}'), description(string), priority(low|medium|high|critical), tags(string[]), project_id(string), plan_id(string)
10714
10863
  Example: {name: 'Bug Report', title_pattern: 'Bug: {description}', priority: 'high', tags: ['bug']}`,
10715
- list_templates: "List all task templates. No params.",
10716
- create_task_from_template: `Create a task from a template with optional overrides.
10864
+ list_templates: "List all task templates. No params.",
10865
+ create_task_from_template: `Create a task from a template with optional overrides.
10717
10866
  Params: template_id(string, req), title(string), description(string), priority(low|medium|high|critical), assigned_to(string), project_id(string)
10718
10867
  Example: {template_id: 'a1b2c3d4', assigned_to: 'maximus'}`,
10719
- delete_template: `Delete a task template.
10868
+ delete_template: `Delete a task template.
10720
10869
  Params: id(string, req)
10721
10870
  Example: {id: 'a1b2c3d4'}`,
10722
- get_active_work: `See all in-progress tasks and who is working on them.
10871
+ get_active_work: `See all in-progress tasks and who is working on them.
10723
10872
  Params: project_id(string, optional), task_list_id(string, optional)
10724
10873
  Example: {project_id: 'a1b2c3d4'}`,
10725
- get_tasks_changed_since: `Get tasks modified after a timestamp \u2014 incremental delta sync.
10874
+ get_tasks_changed_since: `Get tasks modified after a timestamp \u2014 incremental delta sync.
10726
10875
  Params: since(string, req \u2014 ISO date), project_id(string, optional), task_list_id(string, optional)
10727
10876
  Example: {since: '2026-03-14T10:00:00Z'}`,
10728
- get_stale_tasks: `Find stale in_progress tasks with no recent activity.
10877
+ get_stale_tasks: `Find stale in_progress tasks with no recent activity.
10729
10878
  Params: stale_minutes(number, default:30), project_id(string, optional), task_list_id(string, optional)
10730
10879
  Example: {stale_minutes: 60, project_id: 'a1b2c3d4'}`,
10731
- get_status: `Get a full project health snapshot \u2014 pending/in_progress/completed counts, active work, next recommended task, stale task count, overdue recurring tasks. Saves 4+ round trips at session start.
10880
+ get_status: `Get a full project health snapshot \u2014 pending/in_progress/completed counts, active work, next recommended task, stale task count, overdue recurring tasks. Saves 4+ round trips at session start.
10732
10881
  Params: agent_id(string, optional \u2014 prefers tasks assigned to this agent for next_task), project_id(string, optional), task_list_id(string, optional)
10733
10882
  Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
10734
- search_tools: `List all tool names or filter by substring.
10883
+ search_tools: `List all tool names or filter by substring.
10735
10884
  Params: query(string, optional)
10736
10885
  Example: {query: 'task'}`,
10737
- describe_tools: `Get detailed descriptions and parameter info for tools by name.
10886
+ describe_tools: `Get detailed descriptions and parameter info for tools by name.
10738
10887
  Params: names(string[], req)
10739
10888
  Example: {names: ['create_task', 'update_task']}`
10740
- };
10741
- const result = names.map((n) => `${n}: ${descriptions[n] || "Unknown tool. Use search_tools to list available tools."}`).join(`
10889
+ };
10890
+ const allToolNames = Object.keys(descriptions);
10891
+ const registeredCount = allToolNames.filter((n) => shouldRegisterTool(n)).length;
10892
+ const profileLine = `Profile: ${TODOS_PROFILE} (${registeredCount} tools active)
10893
+
10894
+ `;
10895
+ const result = names.map((n) => `${n}: ${descriptions[n] || "Unknown tool. Use search_tools to list available tools."}`).join(`
10742
10896
 
10743
10897
  `);
10744
- return { content: [{ type: "text", text: result }] };
10745
- });
10898
+ return { content: [{ type: "text", text: profileLine + result }] };
10899
+ });
10900
+ }
10746
10901
  server.resource("tasks", "todos://tasks", { description: "All active tasks", mimeType: "application/json" }, async () => {
10747
10902
  const tasks = listTasks({ status: ["pending", "in_progress"] });
10748
10903
  return { contents: [{ uri: "todos://tasks", text: JSON.stringify(tasks, null, 2), mimeType: "application/json" }] };