@hasna/todos 0.9.40 → 0.9.42
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.
- package/dist/cli/index.js +1614 -1396
- package/dist/db/tasks.d.ts +14 -0
- package/dist/db/tasks.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -0
- package/dist/mcp/index.js +1613 -1396
- package/dist/server/index.js +31 -0
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -5991,6 +5991,36 @@ function getStatus(filters, agentId, db) {
|
|
|
5991
5991
|
overdue_recurring: overdueRow.count
|
|
5992
5992
|
};
|
|
5993
5993
|
}
|
|
5994
|
+
function decomposeTasks(parentId, subtasks, options, db) {
|
|
5995
|
+
const d = db || getDatabase();
|
|
5996
|
+
const parent = getTask(parentId, d);
|
|
5997
|
+
if (!parent)
|
|
5998
|
+
throw new TaskNotFoundError(parentId);
|
|
5999
|
+
const created = [];
|
|
6000
|
+
const tx = d.transaction(() => {
|
|
6001
|
+
for (const input of subtasks) {
|
|
6002
|
+
const task = createTask({
|
|
6003
|
+
title: input.title,
|
|
6004
|
+
description: input.description,
|
|
6005
|
+
priority: input.priority || parent.priority,
|
|
6006
|
+
parent_id: parentId,
|
|
6007
|
+
project_id: parent.project_id || undefined,
|
|
6008
|
+
plan_id: parent.plan_id || undefined,
|
|
6009
|
+
task_list_id: parent.task_list_id || undefined,
|
|
6010
|
+
assigned_to: input.assigned_to || parent.assigned_to || undefined,
|
|
6011
|
+
estimated_minutes: input.estimated_minutes,
|
|
6012
|
+
tags: input.tags
|
|
6013
|
+
}, d);
|
|
6014
|
+
if (options?.depends_on_prev && created.length > 0) {
|
|
6015
|
+
const prev = created[created.length - 1];
|
|
6016
|
+
addDependency(task.id, prev.id, d);
|
|
6017
|
+
}
|
|
6018
|
+
created.push(task);
|
|
6019
|
+
}
|
|
6020
|
+
});
|
|
6021
|
+
tx();
|
|
6022
|
+
return { parent, subtasks: created };
|
|
6023
|
+
}
|
|
5994
6024
|
function wouldCreateCycle(taskId, dependsOn, db) {
|
|
5995
6025
|
const visited = new Set;
|
|
5996
6026
|
const queue = [dependsOn];
|
|
@@ -6855,6 +6885,38 @@ var server = new McpServer({
|
|
|
6855
6885
|
name: "todos",
|
|
6856
6886
|
version: "0.9.35"
|
|
6857
6887
|
});
|
|
6888
|
+
var TODOS_PROFILE = (process.env["TODOS_PROFILE"] || "full").toLowerCase();
|
|
6889
|
+
var MINIMAL_TOOLS = new Set([
|
|
6890
|
+
"claim_next_task",
|
|
6891
|
+
"complete_task",
|
|
6892
|
+
"fail_task",
|
|
6893
|
+
"get_status",
|
|
6894
|
+
"get_task",
|
|
6895
|
+
"start_task",
|
|
6896
|
+
"add_comment",
|
|
6897
|
+
"get_next_task"
|
|
6898
|
+
]);
|
|
6899
|
+
var STANDARD_EXCLUDED = new Set([
|
|
6900
|
+
"get_org_chart",
|
|
6901
|
+
"set_reports_to",
|
|
6902
|
+
"rename_agent",
|
|
6903
|
+
"delete_agent",
|
|
6904
|
+
"create_webhook",
|
|
6905
|
+
"list_webhooks",
|
|
6906
|
+
"delete_webhook",
|
|
6907
|
+
"create_template",
|
|
6908
|
+
"list_templates",
|
|
6909
|
+
"create_task_from_template",
|
|
6910
|
+
"delete_template",
|
|
6911
|
+
"approve_task"
|
|
6912
|
+
]);
|
|
6913
|
+
function shouldRegisterTool(name) {
|
|
6914
|
+
if (TODOS_PROFILE === "minimal")
|
|
6915
|
+
return MINIMAL_TOOLS.has(name);
|
|
6916
|
+
if (TODOS_PROFILE === "standard")
|
|
6917
|
+
return !STANDARD_EXCLUDED.has(name);
|
|
6918
|
+
return true;
|
|
6919
|
+
}
|
|
6858
6920
|
function formatError(error) {
|
|
6859
6921
|
if (error instanceof VersionConflictError) {
|
|
6860
6922
|
return JSON.stringify({ code: VersionConflictError.code, message: error.message, suggestion: VersionConflictError.suggestion });
|
|
@@ -6947,1596 +7009,1751 @@ function formatTaskDetail(task) {
|
|
|
6947
7009
|
return parts.join(`
|
|
6948
7010
|
`);
|
|
6949
7011
|
}
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
}
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
exports_external.
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7012
|
+
if (shouldRegisterTool("create_task")) {
|
|
7013
|
+
server.tool("create_task", "Create a new task", {
|
|
7014
|
+
title: exports_external.string(),
|
|
7015
|
+
description: exports_external.string().optional(),
|
|
7016
|
+
project_id: exports_external.string().optional(),
|
|
7017
|
+
parent_id: exports_external.string().optional(),
|
|
7018
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
7019
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
7020
|
+
agent_id: exports_external.string().optional(),
|
|
7021
|
+
assigned_to: exports_external.string().optional(),
|
|
7022
|
+
session_id: exports_external.string().optional(),
|
|
7023
|
+
working_dir: exports_external.string().optional(),
|
|
7024
|
+
plan_id: exports_external.string().optional(),
|
|
7025
|
+
task_list_id: exports_external.string().optional(),
|
|
7026
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
7027
|
+
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
7028
|
+
estimated_minutes: exports_external.number().optional(),
|
|
7029
|
+
requires_approval: exports_external.boolean().optional(),
|
|
7030
|
+
recurrence_rule: exports_external.string().optional()
|
|
7031
|
+
}, async (params) => {
|
|
7032
|
+
try {
|
|
7033
|
+
const resolved = { ...params };
|
|
7034
|
+
if (resolved.project_id)
|
|
7035
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
7036
|
+
if (resolved.parent_id)
|
|
7037
|
+
resolved.parent_id = resolveId(resolved.parent_id);
|
|
7038
|
+
if (resolved.plan_id)
|
|
7039
|
+
resolved.plan_id = resolveId(resolved.plan_id, "plans");
|
|
7040
|
+
if (resolved.task_list_id)
|
|
7041
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
7042
|
+
const task = createTask(resolved);
|
|
7043
|
+
return { content: [{ type: "text", text: `created: ${formatTask(task)}` }] };
|
|
7044
|
+
} catch (e) {
|
|
7045
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7046
|
+
}
|
|
7047
|
+
});
|
|
7048
|
+
}
|
|
7049
|
+
if (shouldRegisterTool("list_tasks")) {
|
|
7050
|
+
server.tool("list_tasks", "List tasks with optional filters and pagination.", {
|
|
7051
|
+
project_id: exports_external.string().optional(),
|
|
7052
|
+
status: exports_external.union([
|
|
7053
|
+
exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
|
|
7054
|
+
exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
|
|
7055
|
+
]).optional(),
|
|
7056
|
+
priority: exports_external.union([
|
|
7057
|
+
exports_external.enum(["low", "medium", "high", "critical"]),
|
|
7058
|
+
exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
|
|
7059
|
+
]).optional(),
|
|
7060
|
+
assigned_to: exports_external.string().optional(),
|
|
7061
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
7062
|
+
plan_id: exports_external.string().optional(),
|
|
7063
|
+
task_list_id: exports_external.string().optional(),
|
|
7064
|
+
has_recurrence: exports_external.boolean().optional(),
|
|
7065
|
+
limit: exports_external.number().optional(),
|
|
7066
|
+
offset: exports_external.number().optional()
|
|
7067
|
+
}, async (params) => {
|
|
7068
|
+
try {
|
|
7069
|
+
const resolved = { ...params };
|
|
7070
|
+
if (resolved.project_id)
|
|
7071
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
7072
|
+
if (resolved.plan_id)
|
|
7073
|
+
resolved.plan_id = resolveId(resolved.plan_id, "plans");
|
|
7074
|
+
if (resolved.task_list_id)
|
|
7075
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
7076
|
+
const tasks = listTasks(resolved);
|
|
7077
|
+
const { limit: _limit, offset: _offset, ...countFilter } = resolved;
|
|
7078
|
+
const total = countTasks(countFilter);
|
|
7079
|
+
if (tasks.length === 0) {
|
|
7080
|
+
return { content: [{ type: "text", text: total > 0 ? `No tasks in this page (total: ${total}).` : "No tasks found." }] };
|
|
7081
|
+
}
|
|
7082
|
+
const text = tasks.map((t) => {
|
|
7083
|
+
const lock = t.locked_by ? ` [locked by ${t.locked_by}]` : "";
|
|
7084
|
+
const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
|
|
7085
|
+
return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned}${lock}`;
|
|
7086
|
+
}).join(`
|
|
7022
7087
|
`);
|
|
7023
|
-
|
|
7088
|
+
const pagination = resolved.limit ? `
|
|
7024
7089
|
(showing ${tasks.length} of ${total}, offset: ${resolved.offset || 0})` : "";
|
|
7025
|
-
|
|
7090
|
+
return { content: [{ type: "text", text: `${tasks.length} task(s):
|
|
7026
7091
|
${text}${pagination}` }] };
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
});
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
parts
|
|
7092
|
+
} catch (e) {
|
|
7093
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7094
|
+
}
|
|
7095
|
+
});
|
|
7096
|
+
}
|
|
7097
|
+
if (shouldRegisterTool("get_task")) {
|
|
7098
|
+
server.tool("get_task", "Get full task details with subtasks, deps, and comments.", {
|
|
7099
|
+
id: exports_external.string()
|
|
7100
|
+
}, async ({ id }) => {
|
|
7101
|
+
try {
|
|
7102
|
+
const resolvedId = resolveId(id);
|
|
7103
|
+
const task = getTaskWithRelations(resolvedId);
|
|
7104
|
+
if (!task)
|
|
7105
|
+
return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
|
|
7106
|
+
const parts = [formatTaskDetail(task)];
|
|
7107
|
+
if (task.subtasks.length > 0) {
|
|
7108
|
+
parts.push(`
|
|
7042
7109
|
Subtasks (${task.subtasks.length}):`);
|
|
7043
|
-
|
|
7044
|
-
|
|
7110
|
+
for (const st of task.subtasks) {
|
|
7111
|
+
parts.push(` [${st.status}] ${st.id.slice(0, 8)} | ${st.title}`);
|
|
7112
|
+
}
|
|
7045
7113
|
}
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
parts.push(`
|
|
7114
|
+
if (task.dependencies.length > 0) {
|
|
7115
|
+
parts.push(`
|
|
7049
7116
|
Depends on (${task.dependencies.length}):`);
|
|
7050
|
-
|
|
7051
|
-
|
|
7117
|
+
for (const dep of task.dependencies) {
|
|
7118
|
+
parts.push(` [${dep.status}] ${dep.id.slice(0, 8)} | ${dep.title}`);
|
|
7119
|
+
}
|
|
7052
7120
|
}
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
parts.push(`
|
|
7121
|
+
if (task.blocked_by.length > 0) {
|
|
7122
|
+
parts.push(`
|
|
7056
7123
|
Blocks (${task.blocked_by.length}):`);
|
|
7057
|
-
|
|
7058
|
-
|
|
7124
|
+
for (const b of task.blocked_by) {
|
|
7125
|
+
parts.push(` [${b.status}] ${b.id.slice(0, 8)} | ${b.title}`);
|
|
7126
|
+
}
|
|
7059
7127
|
}
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
parts.push(`
|
|
7128
|
+
if (task.comments.length > 0) {
|
|
7129
|
+
parts.push(`
|
|
7063
7130
|
Comments (${task.comments.length}):`);
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7131
|
+
for (const c of task.comments) {
|
|
7132
|
+
const agent = c.agent_id ? `[${c.agent_id}] ` : "";
|
|
7133
|
+
parts.push(` ${agent}${c.created_at}: ${c.content}`);
|
|
7134
|
+
}
|
|
7067
7135
|
}
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
parts.push(`
|
|
7136
|
+
if (task.parent) {
|
|
7137
|
+
parts.push(`
|
|
7071
7138
|
Parent: ${task.parent.id.slice(0, 8)} | ${task.parent.title}`);
|
|
7072
|
-
|
|
7073
|
-
|
|
7139
|
+
}
|
|
7140
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
7074
7141
|
`) }] };
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
}
|
|
7078
|
-
});
|
|
7079
|
-
server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
|
|
7080
|
-
id: exports_external.string(),
|
|
7081
|
-
version: exports_external.number(),
|
|
7082
|
-
title: exports_external.string().optional(),
|
|
7083
|
-
description: exports_external.string().optional(),
|
|
7084
|
-
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
7085
|
-
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
7086
|
-
assigned_to: exports_external.string().optional(),
|
|
7087
|
-
tags: exports_external.array(exports_external.string()).optional(),
|
|
7088
|
-
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
7089
|
-
plan_id: exports_external.string().optional(),
|
|
7090
|
-
task_list_id: exports_external.string().optional()
|
|
7091
|
-
}, async ({ id, ...rest }) => {
|
|
7092
|
-
try {
|
|
7093
|
-
const resolvedId = resolveId(id);
|
|
7094
|
-
const task = updateTask(resolvedId, rest);
|
|
7095
|
-
return { content: [{ type: "text", text: `updated: ${formatTask(task)}` }] };
|
|
7096
|
-
} catch (e) {
|
|
7097
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7098
|
-
}
|
|
7099
|
-
});
|
|
7100
|
-
server.tool("delete_task", "Delete a task permanently. Subtasks cascade-deleted.", {
|
|
7101
|
-
id: exports_external.string()
|
|
7102
|
-
}, async ({ id }) => {
|
|
7103
|
-
try {
|
|
7104
|
-
const resolvedId = resolveId(id);
|
|
7105
|
-
const deleted = deleteTask(resolvedId);
|
|
7106
|
-
return {
|
|
7107
|
-
content: [{
|
|
7108
|
-
type: "text",
|
|
7109
|
-
text: deleted ? `Task ${id} deleted.` : `Task ${id} not found.`
|
|
7110
|
-
}]
|
|
7111
|
-
};
|
|
7112
|
-
} catch (e) {
|
|
7113
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7114
|
-
}
|
|
7115
|
-
});
|
|
7116
|
-
server.tool("start_task", "Claim, lock, and set task to in_progress.", {
|
|
7117
|
-
id: exports_external.string(),
|
|
7118
|
-
agent_id: exports_external.string()
|
|
7119
|
-
}, async ({ id, agent_id }) => {
|
|
7120
|
-
try {
|
|
7121
|
-
const resolvedId = resolveId(id);
|
|
7122
|
-
const task = startTask(resolvedId, agent_id);
|
|
7123
|
-
return { content: [{ type: "text", text: `started: ${formatTask(task)}` }] };
|
|
7124
|
-
} catch (e) {
|
|
7125
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7126
|
-
}
|
|
7127
|
-
});
|
|
7128
|
-
server.tool("complete_task", "Complete a task. For recurring tasks, auto-spawns next instance.", {
|
|
7129
|
-
id: exports_external.string(),
|
|
7130
|
-
agent_id: exports_external.string().optional(),
|
|
7131
|
-
skip_recurrence: exports_external.boolean().optional(),
|
|
7132
|
-
files_changed: exports_external.array(exports_external.string()).optional().describe("List of files changed as part of completing this task"),
|
|
7133
|
-
test_results: exports_external.string().optional().describe("Summary of test results"),
|
|
7134
|
-
commit_hash: exports_external.string().optional().describe("Git commit hash associated with this completion"),
|
|
7135
|
-
notes: exports_external.string().optional().describe("Notes about the completion"),
|
|
7136
|
-
attachment_ids: exports_external.array(exports_external.string()).optional().describe("IDs of attachments uploaded via @hasna/attachments to link as evidence")
|
|
7137
|
-
}, async ({ id, agent_id, skip_recurrence, files_changed, test_results, commit_hash, notes, attachment_ids }) => {
|
|
7138
|
-
try {
|
|
7139
|
-
const resolvedId = resolveId(id);
|
|
7140
|
-
const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
|
|
7141
|
-
const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, ...evidence });
|
|
7142
|
-
let text = `completed: ${formatTask(task)}`;
|
|
7143
|
-
if (task.metadata._next_recurrence) {
|
|
7144
|
-
const next = task.metadata._next_recurrence;
|
|
7145
|
-
text += `
|
|
7146
|
-
next: ${next.short_id || next.id.slice(0, 8)} due ${next.due_at}`;
|
|
7142
|
+
} catch (e) {
|
|
7143
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7147
7144
|
}
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7145
|
+
});
|
|
7146
|
+
}
|
|
7147
|
+
if (shouldRegisterTool("update_task")) {
|
|
7148
|
+
server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
|
|
7149
|
+
id: exports_external.string(),
|
|
7150
|
+
version: exports_external.number(),
|
|
7151
|
+
title: exports_external.string().optional(),
|
|
7152
|
+
description: exports_external.string().optional(),
|
|
7153
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
7154
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
7155
|
+
assigned_to: exports_external.string().optional(),
|
|
7156
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
7157
|
+
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
7158
|
+
plan_id: exports_external.string().optional(),
|
|
7159
|
+
task_list_id: exports_external.string().optional()
|
|
7160
|
+
}, async ({ id, ...rest }) => {
|
|
7161
|
+
try {
|
|
7162
|
+
const resolvedId = resolveId(id);
|
|
7163
|
+
const task = updateTask(resolvedId, rest);
|
|
7164
|
+
return { content: [{ type: "text", text: `updated: ${formatTask(task)}` }] };
|
|
7165
|
+
} catch (e) {
|
|
7166
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7162
7167
|
}
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
}
|
|
7180
|
-
server.tool("add_dependency", "Add a dependency. Prevents cycles via BFS detection.", {
|
|
7181
|
-
task_id: exports_external.string(),
|
|
7182
|
-
depends_on: exports_external.string()
|
|
7183
|
-
}, async ({ task_id, depends_on }) => {
|
|
7184
|
-
try {
|
|
7185
|
-
const resolvedTaskId = resolveId(task_id);
|
|
7186
|
-
const resolvedDepsOn = resolveId(depends_on);
|
|
7187
|
-
addDependency(resolvedTaskId, resolvedDepsOn);
|
|
7188
|
-
return { content: [{ type: "text", text: `Dependency added: ${task_id} depends on ${depends_on}` }] };
|
|
7189
|
-
} catch (e) {
|
|
7190
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7191
|
-
}
|
|
7192
|
-
});
|
|
7193
|
-
server.tool("remove_dependency", "Remove a dependency link between two tasks.", {
|
|
7194
|
-
task_id: exports_external.string(),
|
|
7195
|
-
depends_on: exports_external.string()
|
|
7196
|
-
}, async ({ task_id, depends_on }) => {
|
|
7197
|
-
try {
|
|
7198
|
-
const resolvedTaskId = resolveId(task_id);
|
|
7199
|
-
const resolvedDepsOn = resolveId(depends_on);
|
|
7200
|
-
const removed = removeDependency(resolvedTaskId, resolvedDepsOn);
|
|
7201
|
-
return {
|
|
7202
|
-
content: [{
|
|
7203
|
-
type: "text",
|
|
7204
|
-
text: removed ? `Dependency removed.` : `Dependency not found.`
|
|
7205
|
-
}]
|
|
7206
|
-
};
|
|
7207
|
-
} catch (e) {
|
|
7208
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7209
|
-
}
|
|
7210
|
-
});
|
|
7211
|
-
server.tool("add_comment", "Add a comment or note to a task. Comments are append-only.", {
|
|
7212
|
-
task_id: exports_external.string(),
|
|
7213
|
-
content: exports_external.string(),
|
|
7214
|
-
agent_id: exports_external.string().optional(),
|
|
7215
|
-
session_id: exports_external.string().optional()
|
|
7216
|
-
}, async ({ task_id, ...rest }) => {
|
|
7217
|
-
try {
|
|
7218
|
-
const resolvedId = resolveId(task_id);
|
|
7219
|
-
const comment = addComment({ task_id: resolvedId, ...rest });
|
|
7220
|
-
return { content: [{ type: "text", text: `Comment added (${comment.id.slice(0, 8)}) at ${comment.created_at}` }] };
|
|
7221
|
-
} catch (e) {
|
|
7222
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7223
|
-
}
|
|
7224
|
-
});
|
|
7225
|
-
server.tool("list_projects", "List all registered projects", {}, async () => {
|
|
7226
|
-
try {
|
|
7227
|
-
const projects = listProjects();
|
|
7228
|
-
if (projects.length === 0) {
|
|
7229
|
-
return { content: [{ type: "text", text: "No projects registered." }] };
|
|
7230
|
-
}
|
|
7231
|
-
const text = projects.map((p) => {
|
|
7232
|
-
const taskList = p.task_list_id ? ` [${p.task_list_id}]` : "";
|
|
7233
|
-
return `${p.id.slice(0, 8)} | ${p.name} | ${p.path}${taskList}${p.description ? ` - ${p.description}` : ""}`;
|
|
7234
|
-
}).join(`
|
|
7235
|
-
`);
|
|
7236
|
-
return { content: [{ type: "text", text: `${projects.length} project(s):
|
|
7237
|
-
${text}` }] };
|
|
7238
|
-
} catch (e) {
|
|
7239
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7240
|
-
}
|
|
7241
|
-
});
|
|
7242
|
-
server.tool("create_project", "Register a new project with auto-generated task prefix.", {
|
|
7243
|
-
name: exports_external.string(),
|
|
7244
|
-
path: exports_external.string(),
|
|
7245
|
-
description: exports_external.string().optional(),
|
|
7246
|
-
task_list_id: exports_external.string().optional()
|
|
7247
|
-
}, async (params) => {
|
|
7248
|
-
try {
|
|
7249
|
-
const project = createProject(params);
|
|
7250
|
-
const taskList = project.task_list_id ? ` [${project.task_list_id}]` : "";
|
|
7251
|
-
return {
|
|
7252
|
-
content: [{
|
|
7253
|
-
type: "text",
|
|
7254
|
-
text: `Project created: ${project.id.slice(0, 8)} | ${project.name} | ${project.path}${taskList}`
|
|
7255
|
-
}]
|
|
7256
|
-
};
|
|
7257
|
-
} catch (e) {
|
|
7258
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7259
|
-
}
|
|
7260
|
-
});
|
|
7261
|
-
server.tool("create_plan", "Create a plan to group related tasks.", {
|
|
7262
|
-
name: exports_external.string(),
|
|
7263
|
-
project_id: exports_external.string().optional(),
|
|
7264
|
-
description: exports_external.string().optional(),
|
|
7265
|
-
status: exports_external.enum(["active", "completed", "archived"]).optional(),
|
|
7266
|
-
task_list_id: exports_external.string().optional(),
|
|
7267
|
-
agent_id: exports_external.string().optional()
|
|
7268
|
-
}, async (params) => {
|
|
7269
|
-
try {
|
|
7270
|
-
const resolved = { ...params };
|
|
7271
|
-
if (resolved.project_id)
|
|
7272
|
-
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
7273
|
-
if (resolved.task_list_id)
|
|
7274
|
-
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
7275
|
-
const plan = createPlan(resolved);
|
|
7276
|
-
return {
|
|
7277
|
-
content: [{
|
|
7278
|
-
type: "text",
|
|
7279
|
-
text: `Plan created: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
|
|
7280
|
-
}]
|
|
7281
|
-
};
|
|
7282
|
-
} catch (e) {
|
|
7283
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7284
|
-
}
|
|
7285
|
-
});
|
|
7286
|
-
server.tool("list_plans", "List all plans, optionally filtered by project.", {
|
|
7287
|
-
project_id: exports_external.string().optional()
|
|
7288
|
-
}, async ({ project_id }) => {
|
|
7289
|
-
try {
|
|
7290
|
-
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
7291
|
-
const plans = listPlans(resolvedProjectId);
|
|
7292
|
-
if (plans.length === 0) {
|
|
7293
|
-
return { content: [{ type: "text", text: "No plans found." }] };
|
|
7294
|
-
}
|
|
7295
|
-
const text = plans.map((p) => {
|
|
7296
|
-
const project = p.project_id ? ` (project: ${p.project_id.slice(0, 8)})` : "";
|
|
7297
|
-
return `[${p.status}] ${p.id.slice(0, 8)} | ${p.name}${project}`;
|
|
7298
|
-
}).join(`
|
|
7299
|
-
`);
|
|
7300
|
-
return { content: [{ type: "text", text: `${plans.length} plan(s):
|
|
7301
|
-
${text}` }] };
|
|
7302
|
-
} catch (e) {
|
|
7303
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7304
|
-
}
|
|
7305
|
-
});
|
|
7306
|
-
server.tool("get_plan", "Get plan details including status and timestamps.", {
|
|
7307
|
-
id: exports_external.string()
|
|
7308
|
-
}, async ({ id }) => {
|
|
7309
|
-
try {
|
|
7310
|
-
const resolvedId = resolveId(id, "plans");
|
|
7311
|
-
const plan = getPlan(resolvedId);
|
|
7312
|
-
if (!plan)
|
|
7313
|
-
return { content: [{ type: "text", text: `Plan not found: ${id}` }], isError: true };
|
|
7314
|
-
const parts = [
|
|
7315
|
-
`ID: ${plan.id}`,
|
|
7316
|
-
`Name: ${plan.name}`,
|
|
7317
|
-
`Status: ${plan.status}`
|
|
7318
|
-
];
|
|
7319
|
-
if (plan.description)
|
|
7320
|
-
parts.push(`Description: ${plan.description}`);
|
|
7321
|
-
if (plan.project_id)
|
|
7322
|
-
parts.push(`Project: ${plan.project_id}`);
|
|
7323
|
-
parts.push(`Created: ${plan.created_at}`);
|
|
7324
|
-
parts.push(`Updated: ${plan.updated_at}`);
|
|
7325
|
-
return { content: [{ type: "text", text: parts.join(`
|
|
7326
|
-
`) }] };
|
|
7327
|
-
} catch (e) {
|
|
7328
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7329
|
-
}
|
|
7330
|
-
});
|
|
7331
|
-
server.tool("update_plan", "Update plan fields (name, description, status).", {
|
|
7332
|
-
id: exports_external.string(),
|
|
7333
|
-
name: exports_external.string().optional(),
|
|
7334
|
-
description: exports_external.string().optional(),
|
|
7335
|
-
status: exports_external.enum(["active", "completed", "archived"]).optional(),
|
|
7336
|
-
task_list_id: exports_external.string().optional(),
|
|
7337
|
-
agent_id: exports_external.string().optional()
|
|
7338
|
-
}, async ({ id, ...rest }) => {
|
|
7339
|
-
try {
|
|
7340
|
-
const resolvedId = resolveId(id, "plans");
|
|
7341
|
-
const resolved = { ...rest };
|
|
7342
|
-
if (resolved.task_list_id)
|
|
7343
|
-
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
7344
|
-
const plan = updatePlan(resolvedId, resolved);
|
|
7345
|
-
return {
|
|
7346
|
-
content: [{
|
|
7347
|
-
type: "text",
|
|
7348
|
-
text: `Plan updated: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
|
|
7349
|
-
}]
|
|
7350
|
-
};
|
|
7351
|
-
} catch (e) {
|
|
7352
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7353
|
-
}
|
|
7354
|
-
});
|
|
7355
|
-
server.tool("delete_plan", "Delete a plan. Tasks in the plan are orphaned (not deleted).", {
|
|
7356
|
-
id: exports_external.string()
|
|
7357
|
-
}, async ({ id }) => {
|
|
7358
|
-
try {
|
|
7359
|
-
const resolvedId = resolveId(id, "plans");
|
|
7360
|
-
const deleted = deletePlan(resolvedId);
|
|
7361
|
-
return {
|
|
7362
|
-
content: [{
|
|
7363
|
-
type: "text",
|
|
7364
|
-
text: deleted ? `Plan ${id} deleted.` : `Plan ${id} not found.`
|
|
7365
|
-
}]
|
|
7366
|
-
};
|
|
7367
|
-
} catch (e) {
|
|
7368
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7369
|
-
}
|
|
7370
|
-
});
|
|
7371
|
-
server.tool("search_tasks", "Full-text search across tasks with filters.", {
|
|
7372
|
-
query: exports_external.string(),
|
|
7373
|
-
project_id: exports_external.string().optional(),
|
|
7374
|
-
task_list_id: exports_external.string().optional(),
|
|
7375
|
-
status: exports_external.union([
|
|
7376
|
-
exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
|
|
7377
|
-
exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
|
|
7378
|
-
]).optional(),
|
|
7379
|
-
priority: exports_external.union([
|
|
7380
|
-
exports_external.enum(["low", "medium", "high", "critical"]),
|
|
7381
|
-
exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
|
|
7382
|
-
]).optional(),
|
|
7383
|
-
assigned_to: exports_external.string().optional(),
|
|
7384
|
-
agent_id: exports_external.string().optional(),
|
|
7385
|
-
created_after: exports_external.string().optional(),
|
|
7386
|
-
updated_after: exports_external.string().optional(),
|
|
7387
|
-
has_dependencies: exports_external.boolean().optional(),
|
|
7388
|
-
is_blocked: exports_external.boolean().optional()
|
|
7389
|
-
}, async ({ query, project_id, task_list_id, ...filters }) => {
|
|
7390
|
-
try {
|
|
7391
|
-
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
7392
|
-
const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
|
|
7393
|
-
const tasks = searchTasks({
|
|
7394
|
-
query,
|
|
7395
|
-
project_id: resolvedProjectId,
|
|
7396
|
-
task_list_id: resolvedTaskListId,
|
|
7397
|
-
...filters
|
|
7398
|
-
});
|
|
7399
|
-
if (tasks.length === 0) {
|
|
7400
|
-
return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
|
|
7168
|
+
});
|
|
7169
|
+
}
|
|
7170
|
+
if (shouldRegisterTool("delete_task")) {
|
|
7171
|
+
server.tool("delete_task", "Delete a task permanently. Subtasks cascade-deleted.", {
|
|
7172
|
+
id: exports_external.string()
|
|
7173
|
+
}, async ({ id }) => {
|
|
7174
|
+
try {
|
|
7175
|
+
const resolvedId = resolveId(id);
|
|
7176
|
+
const deleted = deleteTask(resolvedId);
|
|
7177
|
+
return {
|
|
7178
|
+
content: [{
|
|
7179
|
+
type: "text",
|
|
7180
|
+
text: deleted ? `Task ${id} deleted.` : `Task ${id} not found.`
|
|
7181
|
+
}]
|
|
7182
|
+
};
|
|
7183
|
+
} catch (e) {
|
|
7184
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7401
7185
|
}
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
}
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
}
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7186
|
+
});
|
|
7187
|
+
}
|
|
7188
|
+
if (shouldRegisterTool("start_task")) {
|
|
7189
|
+
server.tool("start_task", "Claim, lock, and set task to in_progress.", {
|
|
7190
|
+
id: exports_external.string(),
|
|
7191
|
+
agent_id: exports_external.string()
|
|
7192
|
+
}, async ({ id, agent_id }) => {
|
|
7193
|
+
try {
|
|
7194
|
+
const resolvedId = resolveId(id);
|
|
7195
|
+
const task = startTask(resolvedId, agent_id);
|
|
7196
|
+
return { content: [{ type: "text", text: `started: ${formatTask(task)}` }] };
|
|
7197
|
+
} catch (e) {
|
|
7198
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7199
|
+
}
|
|
7200
|
+
});
|
|
7201
|
+
}
|
|
7202
|
+
if (shouldRegisterTool("complete_task")) {
|
|
7203
|
+
server.tool("complete_task", "Complete a task. For recurring tasks, auto-spawns next instance.", {
|
|
7204
|
+
id: exports_external.string(),
|
|
7205
|
+
agent_id: exports_external.string().optional(),
|
|
7206
|
+
skip_recurrence: exports_external.boolean().optional(),
|
|
7207
|
+
files_changed: exports_external.array(exports_external.string()).optional().describe("List of files changed as part of completing this task"),
|
|
7208
|
+
test_results: exports_external.string().optional().describe("Summary of test results"),
|
|
7209
|
+
commit_hash: exports_external.string().optional().describe("Git commit hash associated with this completion"),
|
|
7210
|
+
notes: exports_external.string().optional().describe("Notes about the completion"),
|
|
7211
|
+
attachment_ids: exports_external.array(exports_external.string()).optional().describe("IDs of attachments uploaded via @hasna/attachments to link as evidence")
|
|
7212
|
+
}, async ({ id, agent_id, skip_recurrence, files_changed, test_results, commit_hash, notes, attachment_ids }) => {
|
|
7213
|
+
try {
|
|
7214
|
+
const resolvedId = resolveId(id);
|
|
7215
|
+
const evidence = files_changed || test_results || commit_hash || notes || attachment_ids ? { files_changed, test_results, commit_hash, notes, attachment_ids } : undefined;
|
|
7216
|
+
const task = completeTask(resolvedId, agent_id, undefined, { skip_recurrence, ...evidence });
|
|
7217
|
+
let text = `completed: ${formatTask(task)}`;
|
|
7218
|
+
if (task.metadata._next_recurrence) {
|
|
7219
|
+
const next = task.metadata._next_recurrence;
|
|
7220
|
+
text += `
|
|
7221
|
+
next: ${next.short_id || next.id.slice(0, 8)} due ${next.due_at}`;
|
|
7438
7222
|
}
|
|
7439
|
-
|
|
7223
|
+
return { content: [{ type: "text", text }] };
|
|
7224
|
+
} catch (e) {
|
|
7225
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7440
7226
|
}
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7227
|
+
});
|
|
7228
|
+
}
|
|
7229
|
+
if (shouldRegisterTool("lock_task")) {
|
|
7230
|
+
server.tool("lock_task", "Acquire exclusive lock. Expires after 30 min. Idempotent per agent.", {
|
|
7231
|
+
id: exports_external.string(),
|
|
7232
|
+
agent_id: exports_external.string()
|
|
7233
|
+
}, async ({ id, agent_id }) => {
|
|
7234
|
+
try {
|
|
7235
|
+
const resolvedId = resolveId(id);
|
|
7236
|
+
const result = lockTask(resolvedId, agent_id);
|
|
7237
|
+
if (result.success) {
|
|
7238
|
+
return { content: [{ type: "text", text: `Lock acquired by ${agent_id} at ${result.locked_at}` }] };
|
|
7239
|
+
}
|
|
7240
|
+
return { content: [{ type: "text", text: `Lock failed: ${result.error}` }], isError: true };
|
|
7241
|
+
} catch (e) {
|
|
7242
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7448
7243
|
}
|
|
7449
|
-
|
|
7450
|
-
|
|
7244
|
+
});
|
|
7245
|
+
}
|
|
7246
|
+
if (shouldRegisterTool("unlock_task")) {
|
|
7247
|
+
server.tool("unlock_task", "Release exclusive lock on a task.", {
|
|
7248
|
+
id: exports_external.string(),
|
|
7249
|
+
agent_id: exports_external.string().optional()
|
|
7250
|
+
}, async ({ id, agent_id }) => {
|
|
7251
|
+
try {
|
|
7252
|
+
const resolvedId = resolveId(id);
|
|
7253
|
+
unlockTask(resolvedId, agent_id);
|
|
7254
|
+
return { content: [{ type: "text", text: `Lock released on task ${id}` }] };
|
|
7255
|
+
} catch (e) {
|
|
7256
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7451
7257
|
}
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
content: [{
|
|
7466
|
-
type: "text",
|
|
7467
|
-
text: `Agent registered:
|
|
7468
|
-
ID: ${agent.id}
|
|
7469
|
-
Name: ${agent.name}${agent.description ? `
|
|
7470
|
-
Description: ${agent.description}` : ""}
|
|
7471
|
-
Created: ${agent.created_at}
|
|
7472
|
-
Last seen: ${agent.last_seen_at}`
|
|
7473
|
-
}]
|
|
7474
|
-
};
|
|
7475
|
-
} catch (e) {
|
|
7476
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7477
|
-
}
|
|
7478
|
-
});
|
|
7479
|
-
server.tool("list_agents", "List all registered agents", {}, async () => {
|
|
7480
|
-
try {
|
|
7481
|
-
const agents = listAgents();
|
|
7482
|
-
if (agents.length === 0) {
|
|
7483
|
-
return { content: [{ type: "text", text: "No agents registered." }] };
|
|
7258
|
+
});
|
|
7259
|
+
}
|
|
7260
|
+
if (shouldRegisterTool("add_dependency")) {
|
|
7261
|
+
server.tool("add_dependency", "Add a dependency. Prevents cycles via BFS detection.", {
|
|
7262
|
+
task_id: exports_external.string(),
|
|
7263
|
+
depends_on: exports_external.string()
|
|
7264
|
+
}, async ({ task_id, depends_on }) => {
|
|
7265
|
+
try {
|
|
7266
|
+
const resolvedTaskId = resolveId(task_id);
|
|
7267
|
+
const resolvedDepsOn = resolveId(depends_on);
|
|
7268
|
+
addDependency(resolvedTaskId, resolvedDepsOn);
|
|
7269
|
+
return { content: [{ type: "text", text: `Dependency added: ${task_id} depends on ${depends_on}` }] };
|
|
7270
|
+
} catch (e) {
|
|
7271
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7484
7272
|
}
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7273
|
+
});
|
|
7274
|
+
}
|
|
7275
|
+
if (shouldRegisterTool("remove_dependency")) {
|
|
7276
|
+
server.tool("remove_dependency", "Remove a dependency link between two tasks.", {
|
|
7277
|
+
task_id: exports_external.string(),
|
|
7278
|
+
depends_on: exports_external.string()
|
|
7279
|
+
}, async ({ task_id, depends_on }) => {
|
|
7280
|
+
try {
|
|
7281
|
+
const resolvedTaskId = resolveId(task_id);
|
|
7282
|
+
const resolvedDepsOn = resolveId(depends_on);
|
|
7283
|
+
const removed = removeDependency(resolvedTaskId, resolvedDepsOn);
|
|
7284
|
+
return {
|
|
7285
|
+
content: [{
|
|
7286
|
+
type: "text",
|
|
7287
|
+
text: removed ? `Dependency removed.` : `Dependency not found.`
|
|
7288
|
+
}]
|
|
7289
|
+
};
|
|
7290
|
+
} catch (e) {
|
|
7291
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7292
|
+
}
|
|
7293
|
+
});
|
|
7294
|
+
}
|
|
7295
|
+
if (shouldRegisterTool("add_comment")) {
|
|
7296
|
+
server.tool("add_comment", "Add a comment or note to a task. Comments are append-only.", {
|
|
7297
|
+
task_id: exports_external.string(),
|
|
7298
|
+
content: exports_external.string(),
|
|
7299
|
+
agent_id: exports_external.string().optional(),
|
|
7300
|
+
session_id: exports_external.string().optional()
|
|
7301
|
+
}, async ({ task_id, ...rest }) => {
|
|
7302
|
+
try {
|
|
7303
|
+
const resolvedId = resolveId(task_id);
|
|
7304
|
+
const comment = addComment({ task_id: resolvedId, ...rest });
|
|
7305
|
+
return { content: [{ type: "text", text: `Comment added (${comment.id.slice(0, 8)}) at ${comment.created_at}` }] };
|
|
7306
|
+
} catch (e) {
|
|
7307
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7308
|
+
}
|
|
7309
|
+
});
|
|
7310
|
+
}
|
|
7311
|
+
if (shouldRegisterTool("list_projects")) {
|
|
7312
|
+
server.tool("list_projects", "List all registered projects", {}, async () => {
|
|
7313
|
+
try {
|
|
7314
|
+
const projects = listProjects();
|
|
7315
|
+
if (projects.length === 0) {
|
|
7316
|
+
return { content: [{ type: "text", text: "No projects registered." }] };
|
|
7317
|
+
}
|
|
7318
|
+
const text = projects.map((p) => {
|
|
7319
|
+
const taskList = p.task_list_id ? ` [${p.task_list_id}]` : "";
|
|
7320
|
+
return `${p.id.slice(0, 8)} | ${p.name} | ${p.path}${taskList}${p.description ? ` - ${p.description}` : ""}`;
|
|
7321
|
+
}).join(`
|
|
7488
7322
|
`);
|
|
7489
|
-
|
|
7323
|
+
return { content: [{ type: "text", text: `${projects.length} project(s):
|
|
7490
7324
|
${text}` }] };
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
}
|
|
7494
|
-
});
|
|
7495
|
-
server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
|
|
7496
|
-
id: exports_external.string().optional(),
|
|
7497
|
-
name: exports_external.string().optional()
|
|
7498
|
-
}, async ({ id, name }) => {
|
|
7499
|
-
try {
|
|
7500
|
-
if (!id && !name) {
|
|
7501
|
-
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
7502
|
-
}
|
|
7503
|
-
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
7504
|
-
if (!agent) {
|
|
7505
|
-
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
7506
|
-
}
|
|
7507
|
-
const parts = [
|
|
7508
|
-
`ID: ${agent.id}`,
|
|
7509
|
-
`Name: ${agent.name}`
|
|
7510
|
-
];
|
|
7511
|
-
if (agent.description)
|
|
7512
|
-
parts.push(`Description: ${agent.description}`);
|
|
7513
|
-
if (Object.keys(agent.metadata).length > 0)
|
|
7514
|
-
parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
|
|
7515
|
-
parts.push(`Created: ${agent.created_at}`);
|
|
7516
|
-
parts.push(`Last seen: ${agent.last_seen_at}`);
|
|
7517
|
-
return { content: [{ type: "text", text: parts.join(`
|
|
7518
|
-
`) }] };
|
|
7519
|
-
} catch (e) {
|
|
7520
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7521
|
-
}
|
|
7522
|
-
});
|
|
7523
|
-
server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
|
|
7524
|
-
id: exports_external.string().optional(),
|
|
7525
|
-
name: exports_external.string().optional(),
|
|
7526
|
-
new_name: exports_external.string()
|
|
7527
|
-
}, async ({ id, name, new_name }) => {
|
|
7528
|
-
try {
|
|
7529
|
-
if (!id && !name) {
|
|
7530
|
-
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
7325
|
+
} catch (e) {
|
|
7326
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7531
7327
|
}
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7328
|
+
});
|
|
7329
|
+
}
|
|
7330
|
+
if (shouldRegisterTool("create_project")) {
|
|
7331
|
+
server.tool("create_project", "Register a new project with auto-generated task prefix.", {
|
|
7332
|
+
name: exports_external.string(),
|
|
7333
|
+
path: exports_external.string(),
|
|
7334
|
+
description: exports_external.string().optional(),
|
|
7335
|
+
task_list_id: exports_external.string().optional()
|
|
7336
|
+
}, async (params) => {
|
|
7337
|
+
try {
|
|
7338
|
+
const project = createProject(params);
|
|
7339
|
+
const taskList = project.task_list_id ? ` [${project.task_list_id}]` : "";
|
|
7340
|
+
return {
|
|
7341
|
+
content: [{
|
|
7342
|
+
type: "text",
|
|
7343
|
+
text: `Project created: ${project.id.slice(0, 8)} | ${project.name} | ${project.path}${taskList}`
|
|
7344
|
+
}]
|
|
7345
|
+
};
|
|
7346
|
+
} catch (e) {
|
|
7347
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7535
7348
|
}
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
}
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
return {
|
|
7349
|
+
});
|
|
7350
|
+
}
|
|
7351
|
+
if (shouldRegisterTool("create_plan")) {
|
|
7352
|
+
server.tool("create_plan", "Create a plan to group related tasks.", {
|
|
7353
|
+
name: exports_external.string(),
|
|
7354
|
+
project_id: exports_external.string().optional(),
|
|
7355
|
+
description: exports_external.string().optional(),
|
|
7356
|
+
status: exports_external.enum(["active", "completed", "archived"]).optional(),
|
|
7357
|
+
task_list_id: exports_external.string().optional(),
|
|
7358
|
+
agent_id: exports_external.string().optional()
|
|
7359
|
+
}, async (params) => {
|
|
7360
|
+
try {
|
|
7361
|
+
const resolved = { ...params };
|
|
7362
|
+
if (resolved.project_id)
|
|
7363
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
7364
|
+
if (resolved.task_list_id)
|
|
7365
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
7366
|
+
const plan = createPlan(resolved);
|
|
7367
|
+
return {
|
|
7368
|
+
content: [{
|
|
7369
|
+
type: "text",
|
|
7370
|
+
text: `Plan created: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
|
|
7371
|
+
}]
|
|
7372
|
+
};
|
|
7373
|
+
} catch (e) {
|
|
7374
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7555
7375
|
}
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7376
|
+
});
|
|
7377
|
+
}
|
|
7378
|
+
if (shouldRegisterTool("list_plans")) {
|
|
7379
|
+
server.tool("list_plans", "List all plans, optionally filtered by project.", {
|
|
7380
|
+
project_id: exports_external.string().optional()
|
|
7381
|
+
}, async ({ project_id }) => {
|
|
7382
|
+
try {
|
|
7383
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
7384
|
+
const plans = listPlans(resolvedProjectId);
|
|
7385
|
+
if (plans.length === 0) {
|
|
7386
|
+
return { content: [{ type: "text", text: "No plans found." }] };
|
|
7387
|
+
}
|
|
7388
|
+
const text = plans.map((p) => {
|
|
7389
|
+
const project = p.project_id ? ` (project: ${p.project_id.slice(0, 8)})` : "";
|
|
7390
|
+
return `[${p.status}] ${p.id.slice(0, 8)} | ${p.name}${project}`;
|
|
7391
|
+
}).join(`
|
|
7392
|
+
`);
|
|
7393
|
+
return { content: [{ type: "text", text: `${plans.length} plan(s):
|
|
7394
|
+
${text}` }] };
|
|
7395
|
+
} catch (e) {
|
|
7396
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7559
7397
|
}
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7398
|
+
});
|
|
7399
|
+
}
|
|
7400
|
+
if (shouldRegisterTool("get_plan")) {
|
|
7401
|
+
server.tool("get_plan", "Get plan details including status and timestamps.", {
|
|
7402
|
+
id: exports_external.string()
|
|
7403
|
+
}, async ({ id }) => {
|
|
7404
|
+
try {
|
|
7405
|
+
const resolvedId = resolveId(id, "plans");
|
|
7406
|
+
const plan = getPlan(resolvedId);
|
|
7407
|
+
if (!plan)
|
|
7408
|
+
return { content: [{ type: "text", text: `Plan not found: ${id}` }], isError: true };
|
|
7409
|
+
const parts = [
|
|
7410
|
+
`ID: ${plan.id}`,
|
|
7411
|
+
`Name: ${plan.name}`,
|
|
7412
|
+
`Status: ${plan.status}`
|
|
7413
|
+
];
|
|
7414
|
+
if (plan.description)
|
|
7415
|
+
parts.push(`Description: ${plan.description}`);
|
|
7416
|
+
if (plan.project_id)
|
|
7417
|
+
parts.push(`Project: ${plan.project_id}`);
|
|
7418
|
+
parts.push(`Created: ${plan.created_at}`);
|
|
7419
|
+
parts.push(`Updated: ${plan.updated_at}`);
|
|
7420
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
7421
|
+
`) }] };
|
|
7422
|
+
} catch (e) {
|
|
7423
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7424
|
+
}
|
|
7425
|
+
});
|
|
7426
|
+
}
|
|
7427
|
+
if (shouldRegisterTool("update_plan")) {
|
|
7428
|
+
server.tool("update_plan", "Update plan fields (name, description, status).", {
|
|
7429
|
+
id: exports_external.string(),
|
|
7430
|
+
name: exports_external.string().optional(),
|
|
7431
|
+
description: exports_external.string().optional(),
|
|
7432
|
+
status: exports_external.enum(["active", "completed", "archived"]).optional(),
|
|
7433
|
+
task_list_id: exports_external.string().optional(),
|
|
7434
|
+
agent_id: exports_external.string().optional()
|
|
7435
|
+
}, async ({ id, ...rest }) => {
|
|
7436
|
+
try {
|
|
7437
|
+
const resolvedId = resolveId(id, "plans");
|
|
7438
|
+
const resolved = { ...rest };
|
|
7439
|
+
if (resolved.task_list_id)
|
|
7440
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
7441
|
+
const plan = updatePlan(resolvedId, resolved);
|
|
7442
|
+
return {
|
|
7443
|
+
content: [{
|
|
7444
|
+
type: "text",
|
|
7445
|
+
text: `Plan updated: ${plan.id.slice(0, 8)} | ${plan.name} | ${plan.status}`
|
|
7446
|
+
}]
|
|
7447
|
+
};
|
|
7448
|
+
} catch (e) {
|
|
7449
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7450
|
+
}
|
|
7451
|
+
});
|
|
7452
|
+
}
|
|
7453
|
+
if (shouldRegisterTool("delete_plan")) {
|
|
7454
|
+
server.tool("delete_plan", "Delete a plan. Tasks in the plan are orphaned (not deleted).", {
|
|
7455
|
+
id: exports_external.string()
|
|
7456
|
+
}, async ({ id }) => {
|
|
7457
|
+
try {
|
|
7458
|
+
const resolvedId = resolveId(id, "plans");
|
|
7459
|
+
const deleted = deletePlan(resolvedId);
|
|
7460
|
+
return {
|
|
7461
|
+
content: [{
|
|
7462
|
+
type: "text",
|
|
7463
|
+
text: deleted ? `Plan ${id} deleted.` : `Plan ${id} not found.`
|
|
7464
|
+
}]
|
|
7465
|
+
};
|
|
7466
|
+
} catch (e) {
|
|
7467
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7468
|
+
}
|
|
7469
|
+
});
|
|
7470
|
+
}
|
|
7471
|
+
if (shouldRegisterTool("search_tasks")) {
|
|
7472
|
+
server.tool("search_tasks", "Full-text search across tasks with filters.", {
|
|
7473
|
+
query: exports_external.string(),
|
|
7474
|
+
project_id: exports_external.string().optional(),
|
|
7475
|
+
task_list_id: exports_external.string().optional(),
|
|
7476
|
+
status: exports_external.union([
|
|
7477
|
+
exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
|
|
7478
|
+
exports_external.array(exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]))
|
|
7479
|
+
]).optional(),
|
|
7480
|
+
priority: exports_external.union([
|
|
7481
|
+
exports_external.enum(["low", "medium", "high", "critical"]),
|
|
7482
|
+
exports_external.array(exports_external.enum(["low", "medium", "high", "critical"]))
|
|
7483
|
+
]).optional(),
|
|
7484
|
+
assigned_to: exports_external.string().optional(),
|
|
7485
|
+
agent_id: exports_external.string().optional(),
|
|
7486
|
+
created_after: exports_external.string().optional(),
|
|
7487
|
+
updated_after: exports_external.string().optional(),
|
|
7488
|
+
has_dependencies: exports_external.boolean().optional(),
|
|
7489
|
+
is_blocked: exports_external.boolean().optional()
|
|
7490
|
+
}, async ({ query, project_id, task_list_id, ...filters }) => {
|
|
7491
|
+
try {
|
|
7492
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
7493
|
+
const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
|
|
7494
|
+
const tasks = searchTasks({
|
|
7495
|
+
query,
|
|
7496
|
+
project_id: resolvedProjectId,
|
|
7497
|
+
task_list_id: resolvedTaskListId,
|
|
7498
|
+
...filters
|
|
7499
|
+
});
|
|
7500
|
+
if (tasks.length === 0) {
|
|
7501
|
+
return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
|
|
7502
|
+
}
|
|
7503
|
+
const text = tasks.map((t) => `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}`).join(`
|
|
7504
|
+
`);
|
|
7505
|
+
return { content: [{ type: "text", text: `${tasks.length} result(s) for "${query}":
|
|
7506
|
+
${text}` }] };
|
|
7507
|
+
} catch (e) {
|
|
7508
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7509
|
+
}
|
|
7510
|
+
});
|
|
7511
|
+
}
|
|
7512
|
+
if (shouldRegisterTool("sync")) {
|
|
7513
|
+
server.tool("sync", "Sync tasks between local DB and agent task list.", {
|
|
7514
|
+
task_list_id: exports_external.string().optional(),
|
|
7515
|
+
agent: exports_external.string().optional(),
|
|
7516
|
+
all_agents: exports_external.boolean().optional(),
|
|
7517
|
+
project_id: exports_external.string().optional(),
|
|
7518
|
+
direction: exports_external.enum(["push", "pull", "both"]).optional(),
|
|
7519
|
+
prefer: exports_external.enum(["local", "remote"]).optional()
|
|
7520
|
+
}, async ({ task_list_id, agent, all_agents, project_id, direction, prefer }) => {
|
|
7521
|
+
try {
|
|
7522
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
7523
|
+
const project = resolvedProjectId ? getProject(resolvedProjectId) : undefined;
|
|
7524
|
+
const dir = direction ?? "both";
|
|
7525
|
+
const options = { prefer: prefer ?? "remote" };
|
|
7526
|
+
let result;
|
|
7527
|
+
if (all_agents) {
|
|
7528
|
+
const agents = defaultSyncAgents();
|
|
7529
|
+
result = syncWithAgents(agents, (a) => resolveTaskListId(a, task_list_id || project?.task_list_id || undefined), resolvedProjectId, dir, options);
|
|
7530
|
+
} else {
|
|
7531
|
+
const resolvedAgent = agent || "claude";
|
|
7532
|
+
const taskListId = resolveTaskListId(resolvedAgent, task_list_id || project?.task_list_id || undefined);
|
|
7533
|
+
if (!taskListId) {
|
|
7534
|
+
return {
|
|
7535
|
+
content: [{
|
|
7536
|
+
type: "text",
|
|
7537
|
+
text: `Could not determine task list ID for ${resolvedAgent}. Provide task_list_id or set task_list_id on the project.`
|
|
7538
|
+
}],
|
|
7539
|
+
isError: true
|
|
7540
|
+
};
|
|
7541
|
+
}
|
|
7542
|
+
result = syncWithAgent(resolvedAgent, taskListId, resolvedProjectId, dir, options);
|
|
7543
|
+
}
|
|
7544
|
+
const parts = [];
|
|
7545
|
+
if (result.pulled > 0)
|
|
7546
|
+
parts.push(`Pulled ${result.pulled} task(s).`);
|
|
7547
|
+
if (result.pushed > 0)
|
|
7548
|
+
parts.push(`Pushed ${result.pushed} task(s).`);
|
|
7549
|
+
if (result.pulled === 0 && result.pushed === 0 && result.errors.length === 0) {
|
|
7550
|
+
parts.push("Nothing to sync.");
|
|
7551
|
+
}
|
|
7552
|
+
for (const err of result.errors) {
|
|
7553
|
+
parts.push(`Error: ${err}`);
|
|
7554
|
+
}
|
|
7555
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
7556
|
+
`) }] };
|
|
7557
|
+
} catch (e) {
|
|
7558
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7559
|
+
}
|
|
7560
|
+
});
|
|
7561
|
+
}
|
|
7562
|
+
if (shouldRegisterTool("register_agent")) {
|
|
7563
|
+
server.tool("register_agent", "Register an agent (idempotent by name). Updates last_seen_at.", {
|
|
7564
|
+
name: exports_external.string(),
|
|
7565
|
+
description: exports_external.string().optional()
|
|
7566
|
+
}, async ({ name, description }) => {
|
|
7567
|
+
try {
|
|
7568
|
+
const agent = registerAgent({ name, description });
|
|
7569
|
+
return {
|
|
7570
|
+
content: [{
|
|
7571
|
+
type: "text",
|
|
7572
|
+
text: `Agent registered:
|
|
7573
|
+
ID: ${agent.id}
|
|
7574
|
+
Name: ${agent.name}${agent.description ? `
|
|
7575
|
+
Description: ${agent.description}` : ""}
|
|
7576
|
+
Created: ${agent.created_at}
|
|
7577
|
+
Last seen: ${agent.last_seen_at}`
|
|
7578
|
+
}]
|
|
7579
|
+
};
|
|
7580
|
+
} catch (e) {
|
|
7581
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7582
|
+
}
|
|
7583
|
+
});
|
|
7584
|
+
}
|
|
7585
|
+
if (shouldRegisterTool("list_agents")) {
|
|
7586
|
+
server.tool("list_agents", "List all registered agents", {}, async () => {
|
|
7587
|
+
try {
|
|
7588
|
+
const agents = listAgents();
|
|
7589
|
+
if (agents.length === 0) {
|
|
7590
|
+
return { content: [{ type: "text", text: "No agents registered." }] };
|
|
7591
|
+
}
|
|
7592
|
+
const text = agents.map((a) => {
|
|
7593
|
+
return `${a.id} | ${a.name}${a.description ? ` - ${a.description}` : ""} (last seen: ${a.last_seen_at})`;
|
|
7594
|
+
}).join(`
|
|
7595
|
+
`);
|
|
7596
|
+
return { content: [{ type: "text", text: `${agents.length} agent(s):
|
|
7597
|
+
${text}` }] };
|
|
7598
|
+
} catch (e) {
|
|
7599
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7600
|
+
}
|
|
7601
|
+
});
|
|
7602
|
+
}
|
|
7603
|
+
if (shouldRegisterTool("get_agent")) {
|
|
7604
|
+
server.tool("get_agent", "Get agent details by ID or name. Provide one of id or name.", {
|
|
7605
|
+
id: exports_external.string().optional(),
|
|
7606
|
+
name: exports_external.string().optional()
|
|
7607
|
+
}, async ({ id, name }) => {
|
|
7608
|
+
try {
|
|
7609
|
+
if (!id && !name) {
|
|
7610
|
+
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
7611
|
+
}
|
|
7612
|
+
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
7613
|
+
if (!agent) {
|
|
7614
|
+
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
7615
|
+
}
|
|
7616
|
+
const parts = [
|
|
7617
|
+
`ID: ${agent.id}`,
|
|
7618
|
+
`Name: ${agent.name}`
|
|
7619
|
+
];
|
|
7620
|
+
if (agent.description)
|
|
7621
|
+
parts.push(`Description: ${agent.description}`);
|
|
7622
|
+
if (Object.keys(agent.metadata).length > 0)
|
|
7623
|
+
parts.push(`Metadata: ${JSON.stringify(agent.metadata)}`);
|
|
7624
|
+
parts.push(`Created: ${agent.created_at}`);
|
|
7625
|
+
parts.push(`Last seen: ${agent.last_seen_at}`);
|
|
7626
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
7627
|
+
`) }] };
|
|
7628
|
+
} catch (e) {
|
|
7629
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7630
|
+
}
|
|
7631
|
+
});
|
|
7632
|
+
}
|
|
7633
|
+
if (shouldRegisterTool("rename_agent")) {
|
|
7634
|
+
server.tool("rename_agent", "Rename an agent. Resolve by id or current name.", {
|
|
7635
|
+
id: exports_external.string().optional(),
|
|
7636
|
+
name: exports_external.string().optional(),
|
|
7637
|
+
new_name: exports_external.string()
|
|
7638
|
+
}, async ({ id, name, new_name }) => {
|
|
7639
|
+
try {
|
|
7640
|
+
if (!id && !name) {
|
|
7641
|
+
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
7642
|
+
}
|
|
7643
|
+
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
7644
|
+
if (!agent) {
|
|
7645
|
+
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
7646
|
+
}
|
|
7647
|
+
const updated = updateAgent(agent.id, { name: new_name });
|
|
7648
|
+
return {
|
|
7649
|
+
content: [{
|
|
7650
|
+
type: "text",
|
|
7651
|
+
text: `Agent renamed: ${agent.name} -> ${updated.name}
|
|
7652
|
+
ID: ${updated.id}`
|
|
7653
|
+
}]
|
|
7654
|
+
};
|
|
7655
|
+
} catch (e) {
|
|
7656
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7657
|
+
}
|
|
7658
|
+
});
|
|
7659
|
+
}
|
|
7660
|
+
if (shouldRegisterTool("delete_agent")) {
|
|
7661
|
+
server.tool("delete_agent", "Delete an agent permanently. Resolve by id or name.", {
|
|
7662
|
+
id: exports_external.string().optional(),
|
|
7663
|
+
name: exports_external.string().optional()
|
|
7664
|
+
}, async ({ id, name }) => {
|
|
7665
|
+
try {
|
|
7666
|
+
if (!id && !name) {
|
|
7667
|
+
return { content: [{ type: "text", text: "Provide either id or name." }], isError: true };
|
|
7668
|
+
}
|
|
7669
|
+
const agent = id ? getAgent(id) : getAgentByName(name);
|
|
7670
|
+
if (!agent) {
|
|
7671
|
+
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
7672
|
+
}
|
|
7673
|
+
const deleted = deleteAgent(agent.id);
|
|
7674
|
+
return {
|
|
7675
|
+
content: [{
|
|
7676
|
+
type: "text",
|
|
7677
|
+
text: deleted ? `Agent deleted: ${agent.name} (${agent.id})` : `Failed to delete agent: ${agent.name}`
|
|
7678
|
+
}],
|
|
7679
|
+
isError: !deleted
|
|
7680
|
+
};
|
|
7681
|
+
} catch (e) {
|
|
7682
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7683
|
+
}
|
|
7684
|
+
});
|
|
7685
|
+
}
|
|
7686
|
+
if (shouldRegisterTool("create_task_list")) {
|
|
7687
|
+
server.tool("create_task_list", "Create a task list container for organizing tasks.", {
|
|
7688
|
+
name: exports_external.string(),
|
|
7689
|
+
slug: exports_external.string().optional(),
|
|
7690
|
+
project_id: exports_external.string().optional(),
|
|
7691
|
+
description: exports_external.string().optional()
|
|
7692
|
+
}, async (params) => {
|
|
7693
|
+
try {
|
|
7694
|
+
const resolved = { ...params };
|
|
7695
|
+
if (resolved.project_id)
|
|
7696
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
7697
|
+
const list = createTaskList(resolved);
|
|
7698
|
+
return {
|
|
7699
|
+
content: [{
|
|
7700
|
+
type: "text",
|
|
7701
|
+
text: `Task list created:
|
|
7702
|
+
ID: ${list.id}
|
|
7703
|
+
Name: ${list.name}
|
|
7589
7704
|
Slug: ${list.slug}${list.project_id ? `
|
|
7590
7705
|
Project: ${list.project_id}` : ""}${list.description ? `
|
|
7591
7706
|
Description: ${list.description}` : ""}`
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
});
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7707
|
+
}]
|
|
7708
|
+
};
|
|
7709
|
+
} catch (e) {
|
|
7710
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7711
|
+
}
|
|
7712
|
+
});
|
|
7713
|
+
}
|
|
7714
|
+
if (shouldRegisterTool("list_task_lists")) {
|
|
7715
|
+
server.tool("list_task_lists", "List all task lists, optionally filtered by project.", {
|
|
7716
|
+
project_id: exports_external.string().optional()
|
|
7717
|
+
}, async ({ project_id }) => {
|
|
7718
|
+
try {
|
|
7719
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
7720
|
+
const lists = listTaskLists(resolvedProjectId);
|
|
7721
|
+
if (lists.length === 0) {
|
|
7722
|
+
return { content: [{ type: "text", text: "No task lists found." }] };
|
|
7723
|
+
}
|
|
7724
|
+
const text = lists.map((l) => {
|
|
7725
|
+
const project = l.project_id ? ` (project: ${l.project_id.slice(0, 8)})` : "";
|
|
7726
|
+
return `${l.id.slice(0, 8)} | ${l.name} [${l.slug}]${project}${l.description ? ` - ${l.description}` : ""}`;
|
|
7727
|
+
}).join(`
|
|
7611
7728
|
`);
|
|
7612
|
-
|
|
7729
|
+
return { content: [{ type: "text", text: `${lists.length} task list(s):
|
|
7613
7730
|
${text}` }] };
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
});
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7731
|
+
} catch (e) {
|
|
7732
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7733
|
+
}
|
|
7734
|
+
});
|
|
7735
|
+
}
|
|
7736
|
+
if (shouldRegisterTool("get_task_list")) {
|
|
7737
|
+
server.tool("get_task_list", "Get task list details including slug and metadata.", {
|
|
7738
|
+
id: exports_external.string()
|
|
7739
|
+
}, async ({ id }) => {
|
|
7740
|
+
try {
|
|
7741
|
+
const resolvedId = resolveId(id, "task_lists");
|
|
7742
|
+
const list = getTaskList(resolvedId);
|
|
7743
|
+
if (!list) {
|
|
7744
|
+
return { content: [{ type: "text", text: `Task list not found: ${id}` }], isError: true };
|
|
7745
|
+
}
|
|
7746
|
+
const parts = [
|
|
7747
|
+
`ID: ${list.id}`,
|
|
7748
|
+
`Name: ${list.name}`,
|
|
7749
|
+
`Slug: ${list.slug}`
|
|
7750
|
+
];
|
|
7751
|
+
if (list.project_id)
|
|
7752
|
+
parts.push(`Project: ${list.project_id}`);
|
|
7753
|
+
if (list.description)
|
|
7754
|
+
parts.push(`Description: ${list.description}`);
|
|
7755
|
+
if (Object.keys(list.metadata).length > 0)
|
|
7756
|
+
parts.push(`Metadata: ${JSON.stringify(list.metadata)}`);
|
|
7757
|
+
parts.push(`Created: ${list.created_at}`);
|
|
7758
|
+
parts.push(`Updated: ${list.updated_at}`);
|
|
7759
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
7641
7760
|
`) }] };
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
});
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
name
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7761
|
+
} catch (e) {
|
|
7762
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7763
|
+
}
|
|
7764
|
+
});
|
|
7765
|
+
}
|
|
7766
|
+
if (shouldRegisterTool("update_task_list")) {
|
|
7767
|
+
server.tool("update_task_list", "Update a task list's name or description.", {
|
|
7768
|
+
id: exports_external.string(),
|
|
7769
|
+
name: exports_external.string().optional(),
|
|
7770
|
+
description: exports_external.string().optional()
|
|
7771
|
+
}, async ({ id, ...rest }) => {
|
|
7772
|
+
try {
|
|
7773
|
+
const resolvedId = resolveId(id, "task_lists");
|
|
7774
|
+
const list = updateTaskList(resolvedId, rest);
|
|
7775
|
+
return {
|
|
7776
|
+
content: [{
|
|
7777
|
+
type: "text",
|
|
7778
|
+
text: `Task list updated:
|
|
7658
7779
|
ID: ${list.id}
|
|
7659
7780
|
Name: ${list.name}
|
|
7660
7781
|
Slug: ${list.slug}`
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
});
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
}
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
}
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7782
|
+
}]
|
|
7783
|
+
};
|
|
7784
|
+
} catch (e) {
|
|
7785
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7786
|
+
}
|
|
7787
|
+
});
|
|
7788
|
+
}
|
|
7789
|
+
if (shouldRegisterTool("delete_task_list")) {
|
|
7790
|
+
server.tool("delete_task_list", "Delete a task list. Tasks are orphaned, not deleted.", {
|
|
7791
|
+
id: exports_external.string()
|
|
7792
|
+
}, async ({ id }) => {
|
|
7793
|
+
try {
|
|
7794
|
+
const resolvedId = resolveId(id, "task_lists");
|
|
7795
|
+
const deleted = deleteTaskList(resolvedId);
|
|
7796
|
+
return {
|
|
7797
|
+
content: [{
|
|
7798
|
+
type: "text",
|
|
7799
|
+
text: deleted ? `Task list ${id} deleted.` : `Task list ${id} not found.`
|
|
7800
|
+
}]
|
|
7801
|
+
};
|
|
7802
|
+
} catch (e) {
|
|
7803
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7804
|
+
}
|
|
7805
|
+
});
|
|
7806
|
+
}
|
|
7807
|
+
if (shouldRegisterTool("get_task_history")) {
|
|
7808
|
+
server.tool("get_task_history", "Get audit log \u2014 field changes with timestamps and actors.", {
|
|
7809
|
+
task_id: exports_external.string()
|
|
7810
|
+
}, async ({ task_id }) => {
|
|
7811
|
+
try {
|
|
7812
|
+
const resolvedId = resolveId(task_id);
|
|
7813
|
+
const { getTaskHistory: getTaskHistory2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
|
|
7814
|
+
const history = getTaskHistory2(resolvedId);
|
|
7815
|
+
if (history.length === 0)
|
|
7816
|
+
return { content: [{ type: "text", text: "No history for this task." }] };
|
|
7817
|
+
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(`
|
|
7693
7818
|
`);
|
|
7694
|
-
|
|
7819
|
+
return { content: [{ type: "text", text: `${history.length} change(s):
|
|
7695
7820
|
${text}` }] };
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
});
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7821
|
+
} catch (e) {
|
|
7822
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7823
|
+
}
|
|
7824
|
+
});
|
|
7825
|
+
}
|
|
7826
|
+
if (shouldRegisterTool("get_recent_activity")) {
|
|
7827
|
+
server.tool("get_recent_activity", "Get recent task changes \u2014 global activity feed.", {
|
|
7828
|
+
limit: exports_external.number().optional()
|
|
7829
|
+
}, async ({ limit }) => {
|
|
7830
|
+
try {
|
|
7831
|
+
const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
|
|
7832
|
+
const activity = getRecentActivity2(limit || 50);
|
|
7833
|
+
if (activity.length === 0)
|
|
7834
|
+
return { content: [{ type: "text", text: "No recent activity." }] };
|
|
7835
|
+
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(`
|
|
7709
7836
|
`);
|
|
7710
|
-
|
|
7837
|
+
return { content: [{ type: "text", text: `${activity.length} recent change(s):
|
|
7711
7838
|
${text}` }] };
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
});
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
}
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7839
|
+
} catch (e) {
|
|
7840
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7841
|
+
}
|
|
7842
|
+
});
|
|
7843
|
+
}
|
|
7844
|
+
if (shouldRegisterTool("create_webhook")) {
|
|
7845
|
+
server.tool("create_webhook", "Register a webhook for task change events.", {
|
|
7846
|
+
url: exports_external.string(),
|
|
7847
|
+
events: exports_external.array(exports_external.string()).optional(),
|
|
7848
|
+
secret: exports_external.string().optional()
|
|
7849
|
+
}, async (params) => {
|
|
7850
|
+
try {
|
|
7851
|
+
const { createWebhook: createWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
|
|
7852
|
+
const wh = createWebhook2(params);
|
|
7853
|
+
return { content: [{ type: "text", text: `Webhook created: ${wh.id.slice(0, 8)} | ${wh.url} | events: ${wh.events.length === 0 ? "all" : wh.events.join(",")}` }] };
|
|
7854
|
+
} catch (e) {
|
|
7855
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7856
|
+
}
|
|
7857
|
+
});
|
|
7858
|
+
}
|
|
7859
|
+
if (shouldRegisterTool("list_webhooks")) {
|
|
7860
|
+
server.tool("list_webhooks", "List all registered webhooks", {}, async () => {
|
|
7861
|
+
try {
|
|
7862
|
+
const { listWebhooks: listWebhooks2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
|
|
7863
|
+
const webhooks = listWebhooks2();
|
|
7864
|
+
if (webhooks.length === 0)
|
|
7865
|
+
return { content: [{ type: "text", text: "No webhooks registered." }] };
|
|
7866
|
+
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(`
|
|
7736
7867
|
`);
|
|
7737
|
-
|
|
7868
|
+
return { content: [{ type: "text", text: `${webhooks.length} webhook(s):
|
|
7738
7869
|
${text}` }] };
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
});
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
}
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
}
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7870
|
+
} catch (e) {
|
|
7871
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7872
|
+
}
|
|
7873
|
+
});
|
|
7874
|
+
}
|
|
7875
|
+
if (shouldRegisterTool("delete_webhook")) {
|
|
7876
|
+
server.tool("delete_webhook", "Delete a webhook by ID.", {
|
|
7877
|
+
id: exports_external.string()
|
|
7878
|
+
}, async ({ id }) => {
|
|
7879
|
+
try {
|
|
7880
|
+
const { deleteWebhook: deleteWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
|
|
7881
|
+
const deleted = deleteWebhook2(id);
|
|
7882
|
+
return { content: [{ type: "text", text: deleted ? "Webhook deleted." : "Webhook not found." }] };
|
|
7883
|
+
} catch (e) {
|
|
7884
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7885
|
+
}
|
|
7886
|
+
});
|
|
7887
|
+
}
|
|
7888
|
+
if (shouldRegisterTool("create_template")) {
|
|
7889
|
+
server.tool("create_template", "Create a reusable task template.", {
|
|
7890
|
+
name: exports_external.string(),
|
|
7891
|
+
title_pattern: exports_external.string(),
|
|
7892
|
+
description: exports_external.string().optional(),
|
|
7893
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
7894
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
7895
|
+
project_id: exports_external.string().optional(),
|
|
7896
|
+
plan_id: exports_external.string().optional()
|
|
7897
|
+
}, async (params) => {
|
|
7898
|
+
try {
|
|
7899
|
+
const { createTemplate: createTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
7900
|
+
const t = createTemplate2(params);
|
|
7901
|
+
return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"` }] };
|
|
7902
|
+
} catch (e) {
|
|
7903
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7904
|
+
}
|
|
7905
|
+
});
|
|
7906
|
+
}
|
|
7907
|
+
if (shouldRegisterTool("list_templates")) {
|
|
7908
|
+
server.tool("list_templates", "List all task templates", {}, async () => {
|
|
7909
|
+
try {
|
|
7910
|
+
const { listTemplates: listTemplates2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
7911
|
+
const templates = listTemplates2();
|
|
7912
|
+
if (templates.length === 0)
|
|
7913
|
+
return { content: [{ type: "text", text: "No templates." }] };
|
|
7914
|
+
const text = templates.map((t) => `${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}" | ${t.priority}`).join(`
|
|
7778
7915
|
`);
|
|
7779
|
-
|
|
7916
|
+
return { content: [{ type: "text", text: `${templates.length} template(s):
|
|
7780
7917
|
${text}` }] };
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
});
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7918
|
+
} catch (e) {
|
|
7919
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7920
|
+
}
|
|
7921
|
+
});
|
|
7922
|
+
}
|
|
7923
|
+
if (shouldRegisterTool("create_task_from_template")) {
|
|
7924
|
+
server.tool("create_task_from_template", "Create a task from a template with optional overrides.", {
|
|
7925
|
+
template_id: exports_external.string(),
|
|
7926
|
+
title: exports_external.string().optional(),
|
|
7927
|
+
description: exports_external.string().optional(),
|
|
7928
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
7929
|
+
assigned_to: exports_external.string().optional(),
|
|
7930
|
+
project_id: exports_external.string().optional()
|
|
7931
|
+
}, async (params) => {
|
|
7932
|
+
try {
|
|
7933
|
+
const { taskFromTemplate: taskFromTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
7934
|
+
const input = taskFromTemplate2(params.template_id, {
|
|
7935
|
+
title: params.title,
|
|
7936
|
+
description: params.description,
|
|
7937
|
+
priority: params.priority,
|
|
7938
|
+
assigned_to: params.assigned_to,
|
|
7939
|
+
project_id: params.project_id
|
|
7940
|
+
});
|
|
7941
|
+
const task = createTask(input);
|
|
7942
|
+
return { content: [{ type: "text", text: `Task created from template:
|
|
7804
7943
|
${task.id.slice(0, 8)} | ${task.priority} | ${task.title}` }] };
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
});
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
}
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
});
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7944
|
+
} catch (e) {
|
|
7945
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7946
|
+
}
|
|
7947
|
+
});
|
|
7948
|
+
}
|
|
7949
|
+
if (shouldRegisterTool("delete_template")) {
|
|
7950
|
+
server.tool("delete_template", "Delete a task template by ID.", { id: exports_external.string() }, async ({ id }) => {
|
|
7951
|
+
try {
|
|
7952
|
+
const { deleteTemplate: deleteTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
7953
|
+
const deleted = deleteTemplate2(id);
|
|
7954
|
+
return { content: [{ type: "text", text: deleted ? "Template deleted." : "Template not found." }] };
|
|
7955
|
+
} catch (e) {
|
|
7956
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7957
|
+
}
|
|
7958
|
+
});
|
|
7959
|
+
}
|
|
7960
|
+
if (shouldRegisterTool("approve_task")) {
|
|
7961
|
+
server.tool("approve_task", "Approve a task with requires_approval=true.", {
|
|
7962
|
+
id: exports_external.string(),
|
|
7963
|
+
agent_id: exports_external.string().optional()
|
|
7964
|
+
}, async ({ id, agent_id }) => {
|
|
7965
|
+
try {
|
|
7966
|
+
const resolvedId = resolveId(id);
|
|
7967
|
+
const task = getTaskWithRelations(resolvedId);
|
|
7968
|
+
if (!task)
|
|
7969
|
+
return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
|
|
7970
|
+
if (!task.requires_approval)
|
|
7971
|
+
return { content: [{ type: "text", text: `Task ${id} does not require approval.` }] };
|
|
7972
|
+
if (task.approved_by)
|
|
7973
|
+
return { content: [{ type: "text", text: `Task already approved by ${task.approved_by}.` }] };
|
|
7974
|
+
const updated = updateTask(resolvedId, { approved_by: agent_id || "system", version: task.version });
|
|
7975
|
+
return { content: [{ type: "text", text: `Task approved by ${agent_id || "system"}: ${updated.id.slice(0, 8)} | ${updated.title}` }] };
|
|
7976
|
+
} catch (e) {
|
|
7977
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7978
|
+
}
|
|
7979
|
+
});
|
|
7980
|
+
}
|
|
7981
|
+
if (shouldRegisterTool("fail_task")) {
|
|
7982
|
+
server.tool("fail_task", "Mark a task as failed with structured reason and optional auto-retry.", {
|
|
7983
|
+
id: exports_external.string(),
|
|
7984
|
+
agent_id: exports_external.string().optional(),
|
|
7985
|
+
reason: exports_external.string().optional(),
|
|
7986
|
+
error_code: exports_external.string().optional(),
|
|
7987
|
+
retry: exports_external.boolean().optional(),
|
|
7988
|
+
retry_after: exports_external.string().optional()
|
|
7989
|
+
}, async ({ id, agent_id, reason, error_code, retry, retry_after }) => {
|
|
7990
|
+
try {
|
|
7991
|
+
const resolvedId = resolveId(id);
|
|
7992
|
+
const result = failTask(resolvedId, agent_id, reason, { retry, retry_after, error_code });
|
|
7993
|
+
let text = `failed: ${formatTask(result.task)}`;
|
|
7994
|
+
if (reason)
|
|
7995
|
+
text += `
|
|
7851
7996
|
Reason: ${reason}`;
|
|
7852
|
-
|
|
7853
|
-
|
|
7997
|
+
if (result.retryTask) {
|
|
7998
|
+
text += `
|
|
7854
7999
|
Retry task created: ${formatTask(result.retryTask)}`;
|
|
8000
|
+
}
|
|
8001
|
+
return { content: [{ type: "text", text }] };
|
|
8002
|
+
} catch (e) {
|
|
8003
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
7855
8004
|
}
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
if (pending.length > 0) {
|
|
7877
|
-
lines.push(`
|
|
8005
|
+
});
|
|
8006
|
+
}
|
|
8007
|
+
if (shouldRegisterTool("get_my_tasks")) {
|
|
8008
|
+
server.tool("get_my_tasks", "Get tasks assigned to/created by an agent with stats.", {
|
|
8009
|
+
agent_name: exports_external.string()
|
|
8010
|
+
}, async ({ agent_name }) => {
|
|
8011
|
+
try {
|
|
8012
|
+
const agent = registerAgent({ name: agent_name });
|
|
8013
|
+
const tasks = listTasks({});
|
|
8014
|
+
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);
|
|
8015
|
+
const pending = myTasks.filter((t) => t.status === "pending");
|
|
8016
|
+
const inProgress = myTasks.filter((t) => t.status === "in_progress");
|
|
8017
|
+
const completed = myTasks.filter((t) => t.status === "completed");
|
|
8018
|
+
const rate = myTasks.length > 0 ? Math.round(completed.length / myTasks.length * 100) : 0;
|
|
8019
|
+
const lines = [
|
|
8020
|
+
`Agent: ${agent.name} (${agent.id})`,
|
|
8021
|
+
`Tasks: ${myTasks.length} total, ${pending.length} pending, ${inProgress.length} active, ${completed.length} done (${rate}%)`
|
|
8022
|
+
];
|
|
8023
|
+
if (pending.length > 0) {
|
|
8024
|
+
lines.push(`
|
|
7878
8025
|
Pending:`);
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
8026
|
+
for (const t of pending.slice(0, 10))
|
|
8027
|
+
lines.push(` [${t.priority}] ${t.id.slice(0, 8)} | ${t.title}`);
|
|
8028
|
+
}
|
|
8029
|
+
if (inProgress.length > 0) {
|
|
8030
|
+
lines.push(`
|
|
7884
8031
|
In Progress:`);
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
8032
|
+
for (const t of inProgress)
|
|
8033
|
+
lines.push(` ${t.id.slice(0, 8)} | ${t.title}`);
|
|
8034
|
+
}
|
|
8035
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
7889
8036
|
`) }] };
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
});
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
8037
|
+
} catch (e) {
|
|
8038
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8039
|
+
}
|
|
8040
|
+
});
|
|
8041
|
+
}
|
|
8042
|
+
if (shouldRegisterTool("get_org_chart")) {
|
|
8043
|
+
server.tool("get_org_chart", "Get agent org chart showing reporting hierarchy.", {}, async () => {
|
|
8044
|
+
try {
|
|
8045
|
+
let render = function(nodes, indent = 0) {
|
|
8046
|
+
return nodes.map((n) => {
|
|
8047
|
+
const prefix = " ".repeat(indent);
|
|
8048
|
+
const title = n.agent.title ? ` \u2014 ${n.agent.title}` : "";
|
|
8049
|
+
const level = n.agent.level ? ` (${n.agent.level})` : "";
|
|
8050
|
+
const line = `${prefix}${n.agent.name}${title}${level}`;
|
|
8051
|
+
const children = n.reports.length > 0 ? `
|
|
7903
8052
|
` + render(n.reports, indent + 1) : "";
|
|
7904
|
-
|
|
7905
|
-
|
|
8053
|
+
return line + children;
|
|
8054
|
+
}).join(`
|
|
7906
8055
|
`);
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
});
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
if (
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
}
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
|
|
8056
|
+
};
|
|
8057
|
+
const { getOrgChart: getOrgChart2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
8058
|
+
const tree = getOrgChart2();
|
|
8059
|
+
const text = tree.length > 0 ? render(tree) : "No agents registered.";
|
|
8060
|
+
return { content: [{ type: "text", text }] };
|
|
8061
|
+
} catch (e) {
|
|
8062
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8063
|
+
}
|
|
8064
|
+
});
|
|
8065
|
+
}
|
|
8066
|
+
if (shouldRegisterTool("set_reports_to")) {
|
|
8067
|
+
server.tool("set_reports_to", "Set agent reporting relationship in org chart.", {
|
|
8068
|
+
agent_name: exports_external.string(),
|
|
8069
|
+
manager_name: exports_external.string().optional()
|
|
8070
|
+
}, async ({ agent_name, manager_name }) => {
|
|
8071
|
+
try {
|
|
8072
|
+
const agent = getAgentByName(agent_name);
|
|
8073
|
+
if (!agent)
|
|
8074
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_name}` }], isError: true };
|
|
8075
|
+
let managerId = null;
|
|
8076
|
+
if (manager_name) {
|
|
8077
|
+
const manager = getAgentByName(manager_name);
|
|
8078
|
+
if (!manager)
|
|
8079
|
+
return { content: [{ type: "text", text: `Manager not found: ${manager_name}` }], isError: true };
|
|
8080
|
+
managerId = manager.id;
|
|
8081
|
+
}
|
|
8082
|
+
const { updateAgent: updateAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
8083
|
+
updateAgent2(agent.id, { reports_to: managerId });
|
|
8084
|
+
const result = managerId ? `${agent_name} now reports to ${manager_name}` : `${agent_name} reports to no one (top-level)`;
|
|
8085
|
+
return { content: [{ type: "text", text: result }] };
|
|
8086
|
+
} catch (e) {
|
|
8087
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8088
|
+
}
|
|
8089
|
+
});
|
|
8090
|
+
}
|
|
8091
|
+
if (shouldRegisterTool("bulk_update_tasks")) {
|
|
8092
|
+
server.tool("bulk_update_tasks", "Update multiple tasks at once with the same changes.", {
|
|
8093
|
+
task_ids: exports_external.array(exports_external.string()),
|
|
8094
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
8095
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
8096
|
+
assigned_to: exports_external.string().optional(),
|
|
8097
|
+
tags: exports_external.array(exports_external.string()).optional()
|
|
8098
|
+
}, async ({ task_ids, ...updates }) => {
|
|
8099
|
+
try {
|
|
8100
|
+
const resolvedIds = task_ids.map((id) => resolveId(id));
|
|
8101
|
+
const result = bulkUpdateTasks(resolvedIds, updates);
|
|
8102
|
+
const parts = [`Updated ${result.updated} task(s).`];
|
|
8103
|
+
if (result.failed.length > 0) {
|
|
8104
|
+
parts.push(`Failed ${result.failed.length}:`);
|
|
8105
|
+
for (const f of result.failed)
|
|
8106
|
+
parts.push(` ${f.id.slice(0, 8)}: ${f.error}`);
|
|
8107
|
+
}
|
|
8108
|
+
return { content: [{ type: "text", text: parts.join(`
|
|
7956
8109
|
`) }] };
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
});
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
7978
|
-
resolved
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
}
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
filters
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
}
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
}
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8110
|
+
} catch (e) {
|
|
8111
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8112
|
+
}
|
|
8113
|
+
});
|
|
8114
|
+
}
|
|
8115
|
+
if (shouldRegisterTool("clone_task")) {
|
|
8116
|
+
server.tool("clone_task", "Duplicate a task with optional field overrides.", {
|
|
8117
|
+
task_id: exports_external.string(),
|
|
8118
|
+
title: exports_external.string().optional(),
|
|
8119
|
+
description: exports_external.string().optional(),
|
|
8120
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
8121
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
8122
|
+
project_id: exports_external.string().optional(),
|
|
8123
|
+
plan_id: exports_external.string().optional(),
|
|
8124
|
+
task_list_id: exports_external.string().optional(),
|
|
8125
|
+
assigned_to: exports_external.string().optional(),
|
|
8126
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
8127
|
+
estimated_minutes: exports_external.number().optional()
|
|
8128
|
+
}, async ({ task_id, ...overrides }) => {
|
|
8129
|
+
try {
|
|
8130
|
+
const resolvedId = resolveId(task_id);
|
|
8131
|
+
const resolved = { ...overrides };
|
|
8132
|
+
if (resolved.project_id)
|
|
8133
|
+
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
8134
|
+
if (resolved.plan_id)
|
|
8135
|
+
resolved.plan_id = resolveId(resolved.plan_id, "plans");
|
|
8136
|
+
if (resolved.task_list_id)
|
|
8137
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
8138
|
+
const task = cloneTask(resolvedId, resolved);
|
|
8139
|
+
return { content: [{ type: "text", text: `cloned: ${formatTask(task)}` }] };
|
|
8140
|
+
} catch (e) {
|
|
8141
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8142
|
+
}
|
|
8143
|
+
});
|
|
8144
|
+
}
|
|
8145
|
+
if (shouldRegisterTool("get_task_stats")) {
|
|
8146
|
+
server.tool("get_task_stats", "Get task analytics: counts by status, priority, agent.", {
|
|
8147
|
+
project_id: exports_external.string().optional(),
|
|
8148
|
+
task_list_id: exports_external.string().optional(),
|
|
8149
|
+
agent_id: exports_external.string().optional()
|
|
8150
|
+
}, async ({ project_id, task_list_id, agent_id }) => {
|
|
8151
|
+
try {
|
|
8152
|
+
const filters = {};
|
|
8153
|
+
if (project_id)
|
|
8154
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
8155
|
+
if (task_list_id)
|
|
8156
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
8157
|
+
if (agent_id)
|
|
8158
|
+
filters.agent_id = agent_id;
|
|
8159
|
+
const stats = getTaskStats(Object.keys(filters).length > 0 ? filters : undefined);
|
|
8160
|
+
return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
|
|
8161
|
+
} catch (e) {
|
|
8162
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8163
|
+
}
|
|
8164
|
+
});
|
|
8165
|
+
}
|
|
8166
|
+
if (shouldRegisterTool("get_task_graph")) {
|
|
8167
|
+
server.tool("get_task_graph", "Get full dependency tree for a task.", {
|
|
8168
|
+
id: exports_external.string(),
|
|
8169
|
+
direction: exports_external.enum(["up", "down", "both"]).optional()
|
|
8170
|
+
}, async ({ id, direction }) => {
|
|
8171
|
+
try {
|
|
8172
|
+
let formatNode = function(node, indent) {
|
|
8173
|
+
const prefix = " ".repeat(indent);
|
|
8174
|
+
const idLabel = node.task.short_id || node.task.id.slice(0, 8);
|
|
8175
|
+
const blocked = node.task.is_blocked ? " (blocked: yes)" : "";
|
|
8176
|
+
let out = `${prefix}[${node.task.status}] ${idLabel} | ${node.task.title}${blocked}
|
|
8018
8177
|
`;
|
|
8019
|
-
|
|
8020
|
-
|
|
8178
|
+
if (node.depends_on.length > 0) {
|
|
8179
|
+
out += `${prefix} Depends on:
|
|
8021
8180
|
`;
|
|
8022
|
-
|
|
8023
|
-
|
|
8181
|
+
for (const dep of node.depends_on) {
|
|
8182
|
+
out += formatNode(dep, indent + 2);
|
|
8183
|
+
}
|
|
8024
8184
|
}
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
out += `${prefix} Blocks:
|
|
8185
|
+
if (node.blocks.length > 0) {
|
|
8186
|
+
out += `${prefix} Blocks:
|
|
8028
8187
|
`;
|
|
8029
|
-
|
|
8030
|
-
|
|
8188
|
+
for (const dep of node.blocks) {
|
|
8189
|
+
out += formatNode(dep, indent + 2);
|
|
8190
|
+
}
|
|
8031
8191
|
}
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
text += `
|
|
8192
|
+
return out;
|
|
8193
|
+
};
|
|
8194
|
+
const taskId = resolveId(id, "tasks");
|
|
8195
|
+
const graph = getTaskGraph(taskId, direction || "both");
|
|
8196
|
+
let text = `Task: ${formatNode(graph, 0)}`;
|
|
8197
|
+
if (graph.depends_on.length > 0) {
|
|
8198
|
+
text += `
|
|
8040
8199
|
Depends on:
|
|
8041
8200
|
`;
|
|
8042
|
-
|
|
8043
|
-
|
|
8201
|
+
for (const dep of graph.depends_on) {
|
|
8202
|
+
text += formatNode(dep, 1);
|
|
8203
|
+
}
|
|
8044
8204
|
}
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
text += `
|
|
8205
|
+
if (graph.blocks.length > 0) {
|
|
8206
|
+
text += `
|
|
8048
8207
|
Blocks:
|
|
8049
8208
|
`;
|
|
8050
|
-
|
|
8051
|
-
|
|
8209
|
+
for (const dep of graph.blocks) {
|
|
8210
|
+
text += formatNode(dep, 1);
|
|
8211
|
+
}
|
|
8052
8212
|
}
|
|
8213
|
+
return { content: [{ type: "text", text }] };
|
|
8214
|
+
} catch (e) {
|
|
8215
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8053
8216
|
}
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8217
|
+
});
|
|
8218
|
+
}
|
|
8219
|
+
if (shouldRegisterTool("bulk_create_tasks")) {
|
|
8220
|
+
server.tool("bulk_create_tasks", "Create multiple tasks atomically with dependency support.", {
|
|
8221
|
+
tasks: exports_external.array(exports_external.object({
|
|
8222
|
+
temp_id: exports_external.string().optional(),
|
|
8223
|
+
title: exports_external.string(),
|
|
8224
|
+
description: exports_external.string().optional(),
|
|
8225
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
8226
|
+
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
8227
|
+
project_id: exports_external.string().optional(),
|
|
8228
|
+
plan_id: exports_external.string().optional(),
|
|
8229
|
+
task_list_id: exports_external.string().optional(),
|
|
8230
|
+
agent_id: exports_external.string().optional(),
|
|
8231
|
+
assigned_to: exports_external.string().optional(),
|
|
8232
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
8233
|
+
estimated_minutes: exports_external.number().optional(),
|
|
8234
|
+
depends_on_temp_ids: exports_external.array(exports_external.string()).optional()
|
|
8235
|
+
})),
|
|
8066
8236
|
project_id: exports_external.string().optional(),
|
|
8067
8237
|
plan_id: exports_external.string().optional(),
|
|
8068
|
-
task_list_id: exports_external.string().optional()
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
task_list_id: t.task_list_id || resolvedTaskListId
|
|
8088
|
-
}));
|
|
8089
|
-
const result = bulkCreateTasks(enrichedTasks);
|
|
8090
|
-
const lines = result.created.map((t) => {
|
|
8091
|
-
const tid = t.temp_id ? `[${t.temp_id}] ` : "";
|
|
8092
|
-
const sid = t.short_id || t.id.slice(0, 8);
|
|
8093
|
-
return ` ${tid}${sid} | ${t.title}`;
|
|
8094
|
-
});
|
|
8095
|
-
return { content: [{ type: "text", text: `Created ${result.created.length} task(s):
|
|
8238
|
+
task_list_id: exports_external.string().optional()
|
|
8239
|
+
}, async ({ tasks, project_id, plan_id, task_list_id }) => {
|
|
8240
|
+
try {
|
|
8241
|
+
const resolvedProjectId = project_id ? resolveId(project_id, "projects") : undefined;
|
|
8242
|
+
const resolvedPlanId = plan_id ? resolveId(plan_id, "plans") : undefined;
|
|
8243
|
+
const resolvedTaskListId = task_list_id ? resolveId(task_list_id, "task_lists") : undefined;
|
|
8244
|
+
const enrichedTasks = tasks.map((t) => ({
|
|
8245
|
+
...t,
|
|
8246
|
+
project_id: t.project_id || resolvedProjectId,
|
|
8247
|
+
plan_id: t.plan_id || resolvedPlanId,
|
|
8248
|
+
task_list_id: t.task_list_id || resolvedTaskListId
|
|
8249
|
+
}));
|
|
8250
|
+
const result = bulkCreateTasks(enrichedTasks);
|
|
8251
|
+
const lines = result.created.map((t) => {
|
|
8252
|
+
const tid = t.temp_id ? `[${t.temp_id}] ` : "";
|
|
8253
|
+
const sid = t.short_id || t.id.slice(0, 8);
|
|
8254
|
+
return ` ${tid}${sid} | ${t.title}`;
|
|
8255
|
+
});
|
|
8256
|
+
return { content: [{ type: "text", text: `Created ${result.created.length} task(s):
|
|
8096
8257
|
${lines.join(`
|
|
8097
8258
|
`)}` }] };
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
});
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
resolvedTarget
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
}
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
filters
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8259
|
+
} catch (e) {
|
|
8260
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8261
|
+
}
|
|
8262
|
+
});
|
|
8263
|
+
}
|
|
8264
|
+
if (shouldRegisterTool("move_task")) {
|
|
8265
|
+
server.tool("move_task", "Move a task to a different list, project, or plan.", {
|
|
8266
|
+
task_id: exports_external.string(),
|
|
8267
|
+
task_list_id: exports_external.string().nullable().optional(),
|
|
8268
|
+
project_id: exports_external.string().nullable().optional(),
|
|
8269
|
+
plan_id: exports_external.string().nullable().optional()
|
|
8270
|
+
}, async ({ task_id, ...target }) => {
|
|
8271
|
+
try {
|
|
8272
|
+
const resolvedId = resolveId(task_id);
|
|
8273
|
+
const resolvedTarget = {};
|
|
8274
|
+
if (target.task_list_id !== undefined)
|
|
8275
|
+
resolvedTarget.task_list_id = target.task_list_id ? resolveId(target.task_list_id, "task_lists") : null;
|
|
8276
|
+
if (target.project_id !== undefined)
|
|
8277
|
+
resolvedTarget.project_id = target.project_id ? resolveId(target.project_id, "projects") : null;
|
|
8278
|
+
if (target.plan_id !== undefined)
|
|
8279
|
+
resolvedTarget.plan_id = target.plan_id ? resolveId(target.plan_id, "plans") : null;
|
|
8280
|
+
const task = moveTask(resolvedId, resolvedTarget);
|
|
8281
|
+
return { content: [{ type: "text", text: `moved: ${formatTask(task)}` }] };
|
|
8282
|
+
} catch (e) {
|
|
8283
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8284
|
+
}
|
|
8285
|
+
});
|
|
8286
|
+
}
|
|
8287
|
+
if (shouldRegisterTool("get_next_task")) {
|
|
8288
|
+
server.tool("get_next_task", "Get the best pending task to work on next.", {
|
|
8289
|
+
agent_id: exports_external.string().optional(),
|
|
8290
|
+
project_id: exports_external.string().optional(),
|
|
8291
|
+
task_list_id: exports_external.string().optional(),
|
|
8292
|
+
plan_id: exports_external.string().optional(),
|
|
8293
|
+
tags: exports_external.array(exports_external.string()).optional()
|
|
8294
|
+
}, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
|
|
8295
|
+
try {
|
|
8296
|
+
const filters = {};
|
|
8297
|
+
if (project_id)
|
|
8298
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
8299
|
+
if (task_list_id)
|
|
8300
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
8301
|
+
if (plan_id)
|
|
8302
|
+
filters.plan_id = resolveId(plan_id, "plans");
|
|
8303
|
+
if (tags)
|
|
8304
|
+
filters.tags = tags;
|
|
8305
|
+
const task = getNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
|
|
8306
|
+
if (!task) {
|
|
8307
|
+
return { content: [{ type: "text", text: "No tasks available \u2014 all pending tasks are blocked, locked, or none exist." }] };
|
|
8308
|
+
}
|
|
8309
|
+
return { content: [{ type: "text", text: `next: ${formatTask(task)}
|
|
8145
8310
|
${formatTaskDetail(task)}` }] };
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
});
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8153
|
-
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
filters
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
const
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8311
|
+
} catch (e) {
|
|
8312
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8313
|
+
}
|
|
8314
|
+
});
|
|
8315
|
+
}
|
|
8316
|
+
if (shouldRegisterTool("get_active_work")) {
|
|
8317
|
+
server.tool("get_active_work", "See all in-progress tasks and who is working on them.", {
|
|
8318
|
+
project_id: exports_external.string().optional(),
|
|
8319
|
+
task_list_id: exports_external.string().optional()
|
|
8320
|
+
}, async ({ project_id, task_list_id }) => {
|
|
8321
|
+
try {
|
|
8322
|
+
const filters = {};
|
|
8323
|
+
if (project_id)
|
|
8324
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
8325
|
+
if (task_list_id)
|
|
8326
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
8327
|
+
const work = getActiveWork(Object.keys(filters).length > 0 ? filters : undefined);
|
|
8328
|
+
if (work.length === 0) {
|
|
8329
|
+
return { content: [{ type: "text", text: "No active work \u2014 no tasks are currently in progress." }] };
|
|
8330
|
+
}
|
|
8331
|
+
const text = work.map((w) => {
|
|
8332
|
+
const id = w.short_id || w.id.slice(0, 8);
|
|
8333
|
+
const agent = w.assigned_to || w.locked_by || "unassigned";
|
|
8334
|
+
const since = w.updated_at;
|
|
8335
|
+
return `${agent.padEnd(12)} | ${w.priority.padEnd(8)} | ${id} | ${w.title} (since ${since})`;
|
|
8336
|
+
}).join(`
|
|
8170
8337
|
`);
|
|
8171
|
-
|
|
8338
|
+
return { content: [{ type: "text", text: `${work.length} active task(s):
|
|
8172
8339
|
${text}` }] };
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
});
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
filters
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
|
|
8340
|
+
} catch (e) {
|
|
8341
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8342
|
+
}
|
|
8343
|
+
});
|
|
8344
|
+
}
|
|
8345
|
+
if (shouldRegisterTool("get_tasks_changed_since")) {
|
|
8346
|
+
server.tool("get_tasks_changed_since", "Get tasks modified after a timestamp for incremental sync.", {
|
|
8347
|
+
since: exports_external.string(),
|
|
8348
|
+
project_id: exports_external.string().optional(),
|
|
8349
|
+
task_list_id: exports_external.string().optional()
|
|
8350
|
+
}, async ({ since, project_id, task_list_id }) => {
|
|
8351
|
+
try {
|
|
8352
|
+
const filters = {};
|
|
8353
|
+
if (project_id)
|
|
8354
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
8355
|
+
if (task_list_id)
|
|
8356
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
8357
|
+
const tasks = getTasksChangedSince(since, Object.keys(filters).length > 0 ? filters : undefined);
|
|
8358
|
+
if (tasks.length === 0) {
|
|
8359
|
+
return { content: [{ type: "text", text: `No tasks changed since ${since}.` }] };
|
|
8360
|
+
}
|
|
8361
|
+
const text = tasks.map((t) => {
|
|
8362
|
+
const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
|
|
8363
|
+
return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned} (updated: ${t.updated_at})`;
|
|
8364
|
+
}).join(`
|
|
8196
8365
|
`);
|
|
8197
|
-
|
|
8366
|
+
return { content: [{ type: "text", text: `${tasks.length} task(s) changed since ${since}:
|
|
8198
8367
|
${text}` }] };
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
});
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
filters
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8368
|
+
} catch (e) {
|
|
8369
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8370
|
+
}
|
|
8371
|
+
});
|
|
8372
|
+
}
|
|
8373
|
+
if (shouldRegisterTool("claim_next_task")) {
|
|
8374
|
+
server.tool("claim_next_task", "Atomically claim, lock, and start the best pending task.", {
|
|
8375
|
+
agent_id: exports_external.string(),
|
|
8376
|
+
project_id: exports_external.string().optional(),
|
|
8377
|
+
task_list_id: exports_external.string().optional(),
|
|
8378
|
+
plan_id: exports_external.string().optional(),
|
|
8379
|
+
tags: exports_external.array(exports_external.string()).optional()
|
|
8380
|
+
}, async ({ agent_id, project_id, task_list_id, plan_id, tags }) => {
|
|
8381
|
+
try {
|
|
8382
|
+
const filters = {};
|
|
8383
|
+
if (project_id)
|
|
8384
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
8385
|
+
if (task_list_id)
|
|
8386
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
8387
|
+
if (plan_id)
|
|
8388
|
+
filters.plan_id = resolveId(plan_id, "plans");
|
|
8389
|
+
if (tags)
|
|
8390
|
+
filters.tags = tags;
|
|
8391
|
+
const task = claimNextTask(agent_id, Object.keys(filters).length > 0 ? filters : undefined);
|
|
8392
|
+
if (!task) {
|
|
8393
|
+
return { content: [{ type: "text", text: "No tasks available to claim." }] };
|
|
8394
|
+
}
|
|
8395
|
+
return { content: [{ type: "text", text: `claimed: ${formatTask(task)}
|
|
8225
8396
|
${formatTaskDetail(task)}` }] };
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
});
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
filters
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
const
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8397
|
+
} catch (e) {
|
|
8398
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8399
|
+
}
|
|
8400
|
+
});
|
|
8401
|
+
}
|
|
8402
|
+
if (shouldRegisterTool("get_stale_tasks")) {
|
|
8403
|
+
server.tool("get_stale_tasks", "Find stale in_progress tasks with no recent activity.", {
|
|
8404
|
+
stale_minutes: exports_external.number().optional(),
|
|
8405
|
+
project_id: exports_external.string().optional(),
|
|
8406
|
+
task_list_id: exports_external.string().optional()
|
|
8407
|
+
}, async ({ stale_minutes, project_id, task_list_id }) => {
|
|
8408
|
+
try {
|
|
8409
|
+
const filters = {};
|
|
8410
|
+
if (project_id)
|
|
8411
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
8412
|
+
if (task_list_id)
|
|
8413
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
8414
|
+
const tasks = getStaleTasks(stale_minutes || 30, Object.keys(filters).length > 0 ? filters : undefined);
|
|
8415
|
+
if (tasks.length === 0) {
|
|
8416
|
+
return { content: [{ type: "text", text: "No stale tasks found." }] };
|
|
8417
|
+
}
|
|
8418
|
+
const text = tasks.map((t) => {
|
|
8419
|
+
const id = t.short_id || t.id.slice(0, 8);
|
|
8420
|
+
const agent = t.locked_by || t.assigned_to || "unknown";
|
|
8421
|
+
const staleFor = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
|
|
8422
|
+
return `${id} | ${agent} | ${t.title} (stale ${staleFor}min)`;
|
|
8423
|
+
}).join(`
|
|
8251
8424
|
`);
|
|
8252
|
-
|
|
8425
|
+
return { content: [{ type: "text", text: `${tasks.length} stale task(s):
|
|
8253
8426
|
${text}` }] };
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
});
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
filters
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8427
|
+
} catch (e) {
|
|
8428
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8429
|
+
}
|
|
8430
|
+
});
|
|
8431
|
+
}
|
|
8432
|
+
if (shouldRegisterTool("get_status")) {
|
|
8433
|
+
server.tool("get_status", "Get a full project health snapshot \u2014 counts, active work, next task, stale/overdue summary.", {
|
|
8434
|
+
agent_id: exports_external.string().optional(),
|
|
8435
|
+
project_id: exports_external.string().optional(),
|
|
8436
|
+
task_list_id: exports_external.string().optional()
|
|
8437
|
+
}, async ({ agent_id, project_id, task_list_id }) => {
|
|
8438
|
+
try {
|
|
8439
|
+
const filters = {};
|
|
8440
|
+
if (project_id)
|
|
8441
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
8442
|
+
if (task_list_id)
|
|
8443
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
8444
|
+
const status = getStatus(Object.keys(filters).length > 0 ? filters : undefined, agent_id);
|
|
8445
|
+
const lines = [
|
|
8446
|
+
`Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done | ${status.total} total`
|
|
8447
|
+
];
|
|
8448
|
+
if (status.stale_count > 0)
|
|
8449
|
+
lines.push(`\u26A0\uFE0F ${status.stale_count} stale (stuck in_progress)`);
|
|
8450
|
+
if (status.overdue_recurring > 0)
|
|
8451
|
+
lines.push(`\uD83D\uDD01 ${status.overdue_recurring} overdue recurring`);
|
|
8452
|
+
if (status.active_work.length > 0) {
|
|
8453
|
+
lines.push(`
|
|
8279
8454
|
Active (${status.active_work.length}):`);
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8455
|
+
for (const w of status.active_work.slice(0, 5)) {
|
|
8456
|
+
const id = w.short_id || w.id.slice(0, 8);
|
|
8457
|
+
lines.push(` ${id} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
|
|
8458
|
+
}
|
|
8283
8459
|
}
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
lines.push(`
|
|
8460
|
+
if (status.next_task) {
|
|
8461
|
+
lines.push(`
|
|
8287
8462
|
Next up:`);
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8463
|
+
lines.push(` ${formatTask(status.next_task)}`);
|
|
8464
|
+
} else {
|
|
8465
|
+
lines.push(`
|
|
8291
8466
|
No pending tasks available.`);
|
|
8467
|
+
}
|
|
8468
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
8469
|
+
`) }] };
|
|
8470
|
+
} catch (e) {
|
|
8471
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8292
8472
|
}
|
|
8293
|
-
|
|
8473
|
+
});
|
|
8474
|
+
}
|
|
8475
|
+
if (shouldRegisterTool("decompose_task")) {
|
|
8476
|
+
server.tool("decompose_task", "Break a task into subtasks in one call. Optionally chain them sequentially with depends_on_prev.", {
|
|
8477
|
+
parent_id: exports_external.string(),
|
|
8478
|
+
subtasks: exports_external.array(exports_external.object({
|
|
8479
|
+
title: exports_external.string(),
|
|
8480
|
+
description: exports_external.string().optional(),
|
|
8481
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
8482
|
+
assigned_to: exports_external.string().optional(),
|
|
8483
|
+
estimated_minutes: exports_external.number().optional(),
|
|
8484
|
+
tags: exports_external.array(exports_external.string()).optional()
|
|
8485
|
+
})),
|
|
8486
|
+
depends_on_prev: exports_external.boolean().optional()
|
|
8487
|
+
}, async ({ parent_id, subtasks, depends_on_prev }) => {
|
|
8488
|
+
try {
|
|
8489
|
+
const resolvedId = resolveId(parent_id);
|
|
8490
|
+
const result = decomposeTasks(resolvedId, subtasks, { depends_on_prev }, undefined);
|
|
8491
|
+
const lines = [
|
|
8492
|
+
`Decomposed: ${formatTask(result.parent)}`,
|
|
8493
|
+
`Created ${result.subtasks.length} subtask(s)${depends_on_prev ? " (chained)" : ""}:`,
|
|
8494
|
+
...result.subtasks.map((t, i) => ` ${i + 1}. ${formatTask(t)}`)
|
|
8495
|
+
];
|
|
8496
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
8294
8497
|
`) }] };
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
});
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8498
|
+
} catch (e) {
|
|
8499
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8500
|
+
}
|
|
8501
|
+
});
|
|
8502
|
+
}
|
|
8503
|
+
if (shouldRegisterTool("search_tools")) {
|
|
8504
|
+
server.tool("search_tools", "List all tool names, optionally filtered by substring.", { query: exports_external.string().optional() }, async ({ query }) => {
|
|
8505
|
+
const all = [
|
|
8506
|
+
"create_task",
|
|
8507
|
+
"list_tasks",
|
|
8508
|
+
"get_task",
|
|
8509
|
+
"update_task",
|
|
8510
|
+
"delete_task",
|
|
8511
|
+
"start_task",
|
|
8512
|
+
"complete_task",
|
|
8513
|
+
"fail_task",
|
|
8514
|
+
"lock_task",
|
|
8515
|
+
"unlock_task",
|
|
8516
|
+
"approve_task",
|
|
8517
|
+
"add_dependency",
|
|
8518
|
+
"remove_dependency",
|
|
8519
|
+
"add_comment",
|
|
8520
|
+
"create_project",
|
|
8521
|
+
"list_projects",
|
|
8522
|
+
"create_plan",
|
|
8523
|
+
"list_plans",
|
|
8524
|
+
"get_plan",
|
|
8525
|
+
"update_plan",
|
|
8526
|
+
"delete_plan",
|
|
8527
|
+
"register_agent",
|
|
8528
|
+
"list_agents",
|
|
8529
|
+
"get_agent",
|
|
8530
|
+
"rename_agent",
|
|
8531
|
+
"delete_agent",
|
|
8532
|
+
"get_my_tasks",
|
|
8533
|
+
"get_org_chart",
|
|
8534
|
+
"set_reports_to",
|
|
8535
|
+
"create_task_list",
|
|
8536
|
+
"list_task_lists",
|
|
8537
|
+
"get_task_list",
|
|
8538
|
+
"update_task_list",
|
|
8539
|
+
"delete_task_list",
|
|
8540
|
+
"search_tasks",
|
|
8541
|
+
"sync",
|
|
8542
|
+
"clone_task",
|
|
8543
|
+
"move_task",
|
|
8544
|
+
"get_next_task",
|
|
8545
|
+
"claim_next_task",
|
|
8546
|
+
"get_task_history",
|
|
8547
|
+
"get_recent_activity",
|
|
8548
|
+
"create_webhook",
|
|
8549
|
+
"list_webhooks",
|
|
8550
|
+
"delete_webhook",
|
|
8551
|
+
"create_template",
|
|
8552
|
+
"list_templates",
|
|
8553
|
+
"create_task_from_template",
|
|
8554
|
+
"delete_template",
|
|
8555
|
+
"bulk_update_tasks",
|
|
8556
|
+
"bulk_create_tasks",
|
|
8557
|
+
"get_task_stats",
|
|
8558
|
+
"get_task_graph",
|
|
8559
|
+
"get_active_work",
|
|
8560
|
+
"get_tasks_changed_since",
|
|
8561
|
+
"get_stale_tasks",
|
|
8562
|
+
"get_status",
|
|
8563
|
+
"decompose_task",
|
|
8564
|
+
"search_tools",
|
|
8565
|
+
"describe_tools"
|
|
8566
|
+
].filter((name) => shouldRegisterTool(name));
|
|
8567
|
+
const q = query?.toLowerCase();
|
|
8568
|
+
const matches = q ? all.filter((n) => n.includes(q)) : all;
|
|
8569
|
+
return { content: [{ type: "text", text: matches.join(", ") }] };
|
|
8570
|
+
});
|
|
8571
|
+
}
|
|
8572
|
+
if (shouldRegisterTool("describe_tools")) {
|
|
8573
|
+
server.tool("describe_tools", "Get detailed parameter info for specific tools by name.", { names: exports_external.array(exports_external.string()) }, async ({ names }) => {
|
|
8574
|
+
const descriptions = {
|
|
8575
|
+
create_task: `Create a new task.
|
|
8368
8576
|
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)
|
|
8369
8577
|
Example: {title: 'Daily standup', recurrence_rule: 'every weekday', priority: 'medium'}`,
|
|
8370
|
-
|
|
8578
|
+
list_tasks: `List tasks with optional filters. Supports pagination.
|
|
8371
8579
|
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)
|
|
8372
8580
|
Example: {status: ['pending', 'in_progress'], has_recurrence: true, limit: 20}`,
|
|
8373
|
-
|
|
8581
|
+
get_task: `Get full task details with subtasks, deps, and comments.
|
|
8374
8582
|
Params: id(string, req \u2014 task ID, short_id like 'APP-00001', or partial ID)
|
|
8375
8583
|
Example: {id: 'a1b2c3d4'}`,
|
|
8376
|
-
|
|
8584
|
+
update_task: `Update task fields. Requires version for optimistic locking (get it from get_task first).
|
|
8377
8585
|
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)
|
|
8378
8586
|
Example: {id: 'a1b2c3d4', version: 3, status: 'completed'}`,
|
|
8379
|
-
|
|
8587
|
+
delete_task: `Delete a task permanently. Subtasks cascade-delete. Dependencies removed.
|
|
8380
8588
|
Params: id(string, req)
|
|
8381
8589
|
Example: {id: 'a1b2c3d4'}`,
|
|
8382
|
-
|
|
8590
|
+
start_task: `Claim, lock, and set task status to in_progress in one call.
|
|
8383
8591
|
Params: id(string, req), agent_id(string, req \u2014 your 8-char agent ID)
|
|
8384
8592
|
Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
|
|
8385
|
-
|
|
8593
|
+
complete_task: `Mark task completed, release lock, set completed_at timestamp. For recurring tasks, auto-spawns next instance unless skip_recurrence is true.
|
|
8386
8594
|
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)
|
|
8387
8595
|
Example: {id: 'a1b2c3d4', skip_recurrence: false}`,
|
|
8388
|
-
|
|
8596
|
+
lock_task: `Acquire exclusive lock on a task. Locks auto-expire after 30 min. Re-locking by same agent is idempotent.
|
|
8389
8597
|
Params: id(string, req), agent_id(string, req)
|
|
8390
8598
|
Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
|
|
8391
|
-
|
|
8599
|
+
unlock_task: `Release exclusive lock on a task.
|
|
8392
8600
|
Params: id(string, req), agent_id(string, optional \u2014 omit to force-unlock)
|
|
8393
8601
|
Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
|
|
8394
|
-
|
|
8602
|
+
approve_task: `Approve a task with requires_approval=true. Must be approved before completion.
|
|
8395
8603
|
Params: id(string, req), agent_id(string, optional \u2014 defaults to 'system')
|
|
8396
8604
|
Example: {id: 'a1b2c3d4', agent_id: 'e5f6g7h8'}`,
|
|
8397
|
-
|
|
8605
|
+
fail_task: `Mark a task as failed with structured reason and optional auto-retry. Stores failure info in metadata._failure, releases lock.
|
|
8398
8606
|
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)
|
|
8399
8607
|
Example: {id: 'a1b2c3d4', reason: 'Build timeout', error_code: 'TIMEOUT', retry: true}`,
|
|
8400
|
-
|
|
8608
|
+
add_dependency: `Add a dependency: task_id depends on depends_on. Prevents cycles via BFS.
|
|
8401
8609
|
Params: task_id(string, req), depends_on(string, req)
|
|
8402
8610
|
Example: {task_id: 'abc12345', depends_on: 'def67890'}`,
|
|
8403
|
-
|
|
8611
|
+
remove_dependency: `Remove a dependency link between two tasks.
|
|
8404
8612
|
Params: task_id(string, req), depends_on(string, req)
|
|
8405
8613
|
Example: {task_id: 'abc12345', depends_on: 'def67890'}`,
|
|
8406
|
-
|
|
8614
|
+
add_comment: `Add a comment/note to a task. Comments are append-only.
|
|
8407
8615
|
Params: task_id(string, req), content(string, req), agent_id(string), session_id(string)
|
|
8408
8616
|
Example: {task_id: 'a1b2c3d4', content: 'Blocked by API rate limit'}`,
|
|
8409
|
-
|
|
8617
|
+
create_project: `Register a new project. Auto-generates task prefix for short IDs (e.g. APP-00001).
|
|
8410
8618
|
Params: name(string, req), path(string, req \u2014 unique absolute path), description(string), task_list_id(string)
|
|
8411
8619
|
Example: {name: 'my-app', path: '/Users/dev/my-app'}`,
|
|
8412
|
-
|
|
8413
|
-
|
|
8620
|
+
list_projects: "List all registered projects. No params.",
|
|
8621
|
+
create_plan: `Create a plan to group related tasks.
|
|
8414
8622
|
Params: name(string, req), project_id(string), description(string), status(active|completed|archived, default:active), task_list_id(string), agent_id(string)
|
|
8415
8623
|
Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
|
|
8416
|
-
|
|
8624
|
+
list_plans: `List all plans, optionally filtered by project.
|
|
8417
8625
|
Params: project_id(string)
|
|
8418
8626
|
Example: {project_id: 'a1b2c3d4'}`,
|
|
8419
|
-
|
|
8627
|
+
get_plan: `Get plan details (name, status, description, timestamps).
|
|
8420
8628
|
Params: id(string, req)
|
|
8421
8629
|
Example: {id: 'a1b2c3d4'}`,
|
|
8422
|
-
|
|
8630
|
+
update_plan: `Update plan fields.
|
|
8423
8631
|
Params: id(string, req), name(string), description(string), status(active|completed|archived), task_list_id(string), agent_id(string)
|
|
8424
8632
|
Example: {id: 'a1b2c3d4', status: 'completed'}`,
|
|
8425
|
-
|
|
8633
|
+
delete_plan: `Delete a plan. Tasks in the plan are orphaned, not deleted.
|
|
8426
8634
|
Params: id(string, req)
|
|
8427
8635
|
Example: {id: 'a1b2c3d4'}`,
|
|
8428
|
-
|
|
8636
|
+
register_agent: `Register an agent (idempotent by name). Returns existing agent if name matches.
|
|
8429
8637
|
Params: name(string, req \u2014 e.g. 'maximus'), description(string)
|
|
8430
8638
|
Example: {name: 'maximus', description: 'Backend developer'}`,
|
|
8431
|
-
|
|
8432
|
-
|
|
8639
|
+
list_agents: "List all registered agents with IDs, names, and last seen timestamps. No params.",
|
|
8640
|
+
get_agent: `Get agent details by ID or name. Provide one of id or name.
|
|
8433
8641
|
Params: id(string), name(string)
|
|
8434
8642
|
Example: {name: 'maximus'}`,
|
|
8435
|
-
|
|
8643
|
+
rename_agent: `Rename an agent. Resolve by id or current name.
|
|
8436
8644
|
Params: id(string), name(string \u2014 current name), new_name(string, req)
|
|
8437
8645
|
Example: {name: 'old-name', new_name: 'new-name'}`,
|
|
8438
|
-
|
|
8646
|
+
delete_agent: `Delete an agent permanently. Resolve by id or name.
|
|
8439
8647
|
Params: id(string), name(string)
|
|
8440
8648
|
Example: {name: 'maximus'}`,
|
|
8441
|
-
|
|
8649
|
+
get_my_tasks: `Get all tasks assigned to/created by an agent, with stats (pending/active/done/rate).
|
|
8442
8650
|
Params: agent_name(string, req)
|
|
8443
8651
|
Example: {agent_name: 'maximus'}`,
|
|
8444
|
-
|
|
8445
|
-
|
|
8652
|
+
get_org_chart: "Get agent org chart showing reporting hierarchy. No params.",
|
|
8653
|
+
set_reports_to: `Set who an agent reports to in the org chart. Omit manager_name for top-level.
|
|
8446
8654
|
Params: agent_name(string, req), manager_name(string, optional)
|
|
8447
8655
|
Example: {agent_name: 'brutus', manager_name: 'maximus'}`,
|
|
8448
|
-
|
|
8656
|
+
create_task_list: `Create a task list \u2014 a container/folder for organizing tasks.
|
|
8449
8657
|
Params: name(string, req), slug(string \u2014 auto-generated if omitted), project_id(string), description(string)
|
|
8450
8658
|
Example: {name: 'Sprint 1', project_id: 'a1b2c3d4'}`,
|
|
8451
|
-
|
|
8659
|
+
list_task_lists: `List all task lists, optionally filtered by project.
|
|
8452
8660
|
Params: project_id(string)
|
|
8453
8661
|
Example: {project_id: 'a1b2c3d4'}`,
|
|
8454
|
-
|
|
8662
|
+
get_task_list: `Get task list details (name, slug, project, metadata).
|
|
8455
8663
|
Params: id(string, req)
|
|
8456
8664
|
Example: {id: 'a1b2c3d4'}`,
|
|
8457
|
-
|
|
8665
|
+
update_task_list: `Update a task list's name or description.
|
|
8458
8666
|
Params: id(string, req), name(string), description(string)
|
|
8459
8667
|
Example: {id: 'a1b2c3d4', name: 'Sprint 2'}`,
|
|
8460
|
-
|
|
8668
|
+
delete_task_list: `Delete a task list. Tasks are orphaned (not deleted).
|
|
8461
8669
|
Params: id(string, req)
|
|
8462
8670
|
Example: {id: 'a1b2c3d4'}`,
|
|
8463
|
-
|
|
8671
|
+
search_tasks: `Full-text search across task titles, descriptions, and tags. Supports filters.
|
|
8464
8672
|
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)
|
|
8465
8673
|
Example: {query: 'auth bug', status: 'pending'}`,
|
|
8466
|
-
|
|
8674
|
+
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.
|
|
8467
8675
|
Params: agent_id(string \u2014 prefers your tasks), project_id(string), task_list_id(string), plan_id(string), tags(string[])
|
|
8468
8676
|
Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
|
|
8469
|
-
|
|
8677
|
+
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.
|
|
8470
8678
|
Params: agent_id(string, req \u2014 used for lock and assignment), project_id(string), task_list_id(string), plan_id(string), tags(string[])
|
|
8471
8679
|
Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
|
|
8472
|
-
|
|
8680
|
+
sync: `Sync tasks between local DB and agent task list (e.g. Claude Code).
|
|
8473
8681
|
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)
|
|
8474
8682
|
Example: {agent: 'claude', direction: 'push'}`,
|
|
8475
|
-
|
|
8683
|
+
clone_task: `Duplicate a task with optional field overrides. Creates new independent copy.
|
|
8476
8684
|
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)
|
|
8477
8685
|
Example: {task_id: 'a1b2c3d4', title: 'Cloned task', assigned_to: 'brutus'}`,
|
|
8478
|
-
|
|
8686
|
+
move_task: `Move a task to a different list, project, or plan.
|
|
8479
8687
|
Params: task_id(string, req), task_list_id(string|null), project_id(string|null), plan_id(string|null)
|
|
8480
8688
|
Example: {task_id: 'a1b2c3d4', task_list_id: 'e5f6g7h8'}`,
|
|
8481
|
-
|
|
8689
|
+
bulk_update_tasks: `Update multiple tasks at once with the same changes.
|
|
8482
8690
|
Params: task_ids(string[], req), status(pending|in_progress|completed|failed|cancelled), priority(low|medium|high|critical), assigned_to(string), tags(string[])
|
|
8483
8691
|
Example: {task_ids: ['abc12345', 'def67890'], status: 'completed'}`,
|
|
8484
|
-
|
|
8692
|
+
bulk_create_tasks: `Create multiple tasks atomically. Supports inter-task dependencies via temp_id references.
|
|
8485
8693
|
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)
|
|
8486
8694
|
Example: {tasks: [{temp_id: 'a', title: 'First'}, {temp_id: 'b', title: 'Second', depends_on_temp_ids: ['a']}]}`,
|
|
8487
|
-
|
|
8695
|
+
get_task_stats: `Get task analytics: counts by status, priority, agent, and completion rate. All via SQL.
|
|
8488
8696
|
Params: project_id(string), task_list_id(string), agent_id(string)
|
|
8489
8697
|
Example: {project_id: 'a1b2c3d4'}`,
|
|
8490
|
-
|
|
8698
|
+
get_task_graph: `Get full dependency tree for a task \u2014 upstream blockers and downstream dependents.
|
|
8491
8699
|
Params: id(string, req), direction(up|down|both, default:both)
|
|
8492
8700
|
Example: {id: 'a1b2c3d4', direction: 'up'}`,
|
|
8493
|
-
|
|
8701
|
+
get_task_history: `Get audit log for a task \u2014 all field changes with timestamps and actors.
|
|
8494
8702
|
Params: task_id(string, req)
|
|
8495
8703
|
Example: {task_id: 'a1b2c3d4'}`,
|
|
8496
|
-
|
|
8704
|
+
get_recent_activity: `Get recent task changes across all tasks \u2014 global activity feed.
|
|
8497
8705
|
Params: limit(number, default:50)
|
|
8498
8706
|
Example: {limit: 20}`,
|
|
8499
|
-
|
|
8707
|
+
create_webhook: `Register a webhook for task change events.
|
|
8500
8708
|
Params: url(string, req), events(string[] \u2014 empty=all), secret(string \u2014 HMAC signing)
|
|
8501
8709
|
Example: {url: 'https://example.com/hook', events: ['task.created', 'task.completed']}`,
|
|
8502
|
-
|
|
8503
|
-
|
|
8710
|
+
list_webhooks: "List all registered webhooks. No params.",
|
|
8711
|
+
delete_webhook: `Delete a webhook by ID.
|
|
8504
8712
|
Params: id(string, req)
|
|
8505
8713
|
Example: {id: 'a1b2c3d4'}`,
|
|
8506
|
-
|
|
8714
|
+
create_template: `Create a reusable task template.
|
|
8507
8715
|
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)
|
|
8508
8716
|
Example: {name: 'Bug Report', title_pattern: 'Bug: {description}', priority: 'high', tags: ['bug']}`,
|
|
8509
|
-
|
|
8510
|
-
|
|
8717
|
+
list_templates: "List all task templates. No params.",
|
|
8718
|
+
create_task_from_template: `Create a task from a template with optional overrides.
|
|
8511
8719
|
Params: template_id(string, req), title(string), description(string), priority(low|medium|high|critical), assigned_to(string), project_id(string)
|
|
8512
8720
|
Example: {template_id: 'a1b2c3d4', assigned_to: 'maximus'}`,
|
|
8513
|
-
|
|
8721
|
+
delete_template: `Delete a task template.
|
|
8514
8722
|
Params: id(string, req)
|
|
8515
8723
|
Example: {id: 'a1b2c3d4'}`,
|
|
8516
|
-
|
|
8724
|
+
get_active_work: `See all in-progress tasks and who is working on them.
|
|
8517
8725
|
Params: project_id(string, optional), task_list_id(string, optional)
|
|
8518
8726
|
Example: {project_id: 'a1b2c3d4'}`,
|
|
8519
|
-
|
|
8727
|
+
get_tasks_changed_since: `Get tasks modified after a timestamp \u2014 incremental delta sync.
|
|
8520
8728
|
Params: since(string, req \u2014 ISO date), project_id(string, optional), task_list_id(string, optional)
|
|
8521
8729
|
Example: {since: '2026-03-14T10:00:00Z'}`,
|
|
8522
|
-
|
|
8730
|
+
get_stale_tasks: `Find stale in_progress tasks with no recent activity.
|
|
8523
8731
|
Params: stale_minutes(number, default:30), project_id(string, optional), task_list_id(string, optional)
|
|
8524
8732
|
Example: {stale_minutes: 60, project_id: 'a1b2c3d4'}`,
|
|
8525
|
-
|
|
8733
|
+
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.
|
|
8526
8734
|
Params: agent_id(string, optional \u2014 prefers tasks assigned to this agent for next_task), project_id(string, optional), task_list_id(string, optional)
|
|
8527
8735
|
Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
|
|
8528
|
-
|
|
8736
|
+
decompose_task: `Break a task into subtasks in one call. Subtasks inherit project/plan/list from parent.
|
|
8737
|
+
Params: parent_id(string, req), subtasks(array, req \u2014 [{title, description, priority, assigned_to, estimated_minutes, tags}]), depends_on_prev(boolean \u2014 chain subtasks sequentially)
|
|
8738
|
+
Example: {parent_id: 'a1b2c3d4', subtasks: [{title: 'Research'}, {title: 'Implement'}, {title: 'Test'}], depends_on_prev: true}`,
|
|
8739
|
+
search_tools: `List all tool names or filter by substring.
|
|
8529
8740
|
Params: query(string, optional)
|
|
8530
8741
|
Example: {query: 'task'}`,
|
|
8531
|
-
|
|
8742
|
+
describe_tools: `Get detailed descriptions and parameter info for tools by name.
|
|
8532
8743
|
Params: names(string[], req)
|
|
8533
8744
|
Example: {names: ['create_task', 'update_task']}`
|
|
8534
|
-
|
|
8535
|
-
|
|
8745
|
+
};
|
|
8746
|
+
const allToolNames = Object.keys(descriptions);
|
|
8747
|
+
const registeredCount = allToolNames.filter((n) => shouldRegisterTool(n)).length;
|
|
8748
|
+
const profileLine = `Profile: ${TODOS_PROFILE} (${registeredCount} tools active)
|
|
8749
|
+
|
|
8750
|
+
`;
|
|
8751
|
+
const result = names.map((n) => `${n}: ${descriptions[n] || "Unknown tool. Use search_tools to list available tools."}`).join(`
|
|
8536
8752
|
|
|
8537
8753
|
`);
|
|
8538
|
-
|
|
8539
|
-
});
|
|
8754
|
+
return { content: [{ type: "text", text: profileLine + result }] };
|
|
8755
|
+
});
|
|
8756
|
+
}
|
|
8540
8757
|
server.resource("tasks", "todos://tasks", { description: "All active tasks", mimeType: "application/json" }, async () => {
|
|
8541
8758
|
const tasks = listTasks({ status: ["pending", "in_progress"] });
|
|
8542
8759
|
return { contents: [{ uri: "todos://tasks", text: JSON.stringify(tasks, null, 2), mimeType: "application/json" }] };
|