agent-planner-mcp 0.5.0 → 0.6.0
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/AGENT_GUIDE.md +257 -0
- package/README.md +128 -286
- package/SKILL.md +434 -0
- package/package.json +17 -6
- package/src/api-client.js +298 -66
- package/src/index.js +3 -2
- package/src/server-http.js +73 -14
- package/src/session-manager.js +22 -0
- package/src/setup.js +1 -1
- package/src/tools.js +636 -440
- package/claude-code/AUTONOMOUS_EXECUTION_GUIDE.md +0 -335
- package/claude-code/commands/README.md +0 -112
- package/claude-code/commands/create-plan.md +0 -174
- package/claude-code/commands/execute-plan.md +0 -202
- package/claude-code/commands/plan-status.md +0 -145
- package/claude-code/settings.template.json +0 -12
package/src/tools.js
CHANGED
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
const { ListToolsRequestSchema, CallToolRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
|
|
13
|
-
const
|
|
13
|
+
const defaultApiClient = require('./api-client');
|
|
14
|
+
|
|
15
|
+
const APP_URL = (process.env.APP_URL || 'https://agentplanner.io').replace(/\/$/, '');
|
|
16
|
+
function buildPlanUrl(planId) { return `${APP_URL}/app/plans/${planId}`; }
|
|
17
|
+
function buildTaskUrl(planId, nodeId) { return `${APP_URL}/app/plans/${planId}?node=${nodeId}`; }
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* Format JSON data as text for Claude Desktop
|
|
@@ -43,8 +47,10 @@ function formatResponse(data) {
|
|
|
43
47
|
/**
|
|
44
48
|
* Setup tools for the MCP server
|
|
45
49
|
* @param {Server} server - MCP server instance
|
|
50
|
+
* @param {Object} [apiClientOverride] - Per-session API client (HTTP mode). Falls back to default (stdio mode).
|
|
46
51
|
*/
|
|
47
|
-
function setupTools(server) {
|
|
52
|
+
function setupTools(server, apiClientOverride) {
|
|
53
|
+
const apiClient = apiClientOverride || defaultApiClient;
|
|
48
54
|
// Suppress console logs when not in debug mode
|
|
49
55
|
if (process.env.NODE_ENV !== 'development') {
|
|
50
56
|
// Silent mode for production
|
|
@@ -62,7 +68,7 @@ function setupTools(server) {
|
|
|
62
68
|
// ========================================
|
|
63
69
|
{
|
|
64
70
|
name: "quick_plan",
|
|
65
|
-
description: "Create a plan quickly from a title and list of tasks. Perfect for getting started fast - just provide a title and task names. Returns plan URL and task IDs for immediate use.",
|
|
71
|
+
description: "Create a plan quickly from a title and list of tasks. Perfect for getting started fast - just provide a title and task names. Returns plan URL and task IDs for immediate use. Tip: provide a goal_id to automatically link this plan to a goal.",
|
|
66
72
|
inputSchema: {
|
|
67
73
|
type: "object",
|
|
68
74
|
properties: {
|
|
@@ -73,7 +79,7 @@ function setupTools(server) {
|
|
|
73
79
|
items: { type: "string" },
|
|
74
80
|
description: "List of task titles (simple strings). A phase will be created automatically."
|
|
75
81
|
},
|
|
76
|
-
goal_id: { type: "string", description: "Optionally link to a goal" },
|
|
82
|
+
goal_id: { type: "string", description: "Optionally link this plan to a goal. Recommended: always link plans to goals for tracking." },
|
|
77
83
|
organization_id: { type: "string", description: "Organization ID (uses default if not provided)" }
|
|
78
84
|
},
|
|
79
85
|
required: ["title", "tasks"]
|
|
@@ -104,7 +110,7 @@ function setupTools(server) {
|
|
|
104
110
|
plan_id: { type: "string", description: "Plan ID (required for API)" },
|
|
105
111
|
status: {
|
|
106
112
|
type: "string",
|
|
107
|
-
enum: ["not_started", "in_progress", "completed", "blocked"],
|
|
113
|
+
enum: ["not_started", "in_progress", "completed", "blocked", "plan_ready"],
|
|
108
114
|
description: "New status"
|
|
109
115
|
},
|
|
110
116
|
note: { type: "string", description: "Optional note explaining the status change (especially useful for 'blocked')" }
|
|
@@ -132,61 +138,64 @@ function setupTools(server) {
|
|
|
132
138
|
}
|
|
133
139
|
},
|
|
134
140
|
|
|
141
|
+
{
|
|
142
|
+
name: "check_goals_health",
|
|
143
|
+
description: "Check the health of all your goals. Returns per-goal health status (on_track/at_risk/stale), bottleneck summaries, knowledge gaps, and pending decisions. Call this FIRST in the autonomous loop to identify which goals need attention.",
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: "object",
|
|
146
|
+
properties: {
|
|
147
|
+
status_filter: { type: "string", description: "Filter by health status (e.g. 'on_track', 'at_risk', 'stale')" }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
|
|
135
152
|
// ========================================
|
|
136
|
-
//
|
|
137
|
-
// Use before starting work on a plan/goal
|
|
153
|
+
// TASK CLAIMING - Prevent agent collisions
|
|
138
154
|
// ========================================
|
|
139
155
|
{
|
|
140
|
-
name: "
|
|
141
|
-
description: "
|
|
156
|
+
name: "claim_task",
|
|
157
|
+
description: "Claim exclusive ownership of a task before starting work. Prevents other agents from working on the same task. Claims expire after ttl_minutes (default 30). Always claim before starting work on a task.",
|
|
142
158
|
inputSchema: {
|
|
143
159
|
type: "object",
|
|
144
160
|
properties: {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
161
|
+
task_id: { type: "string", description: "Task ID to claim" },
|
|
162
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
163
|
+
ttl_minutes: { type: "integer", description: "Claim duration in minutes (default 30)", default: 30 }
|
|
164
|
+
},
|
|
165
|
+
required: ["task_id", "plan_id"]
|
|
149
166
|
}
|
|
150
167
|
},
|
|
151
168
|
{
|
|
152
|
-
name: "
|
|
153
|
-
description: "
|
|
169
|
+
name: "release_task",
|
|
170
|
+
description: "Release a previously claimed task. Called automatically when you complete a task, but use this if you need to abandon work early.",
|
|
154
171
|
inputSchema: {
|
|
155
172
|
type: "object",
|
|
156
173
|
properties: {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
default: ["blocked", "in_progress"],
|
|
162
|
-
description: "Task statuses to include"
|
|
163
|
-
}
|
|
164
|
-
}
|
|
174
|
+
task_id: { type: "string", description: "Task ID to release" },
|
|
175
|
+
plan_id: { type: "string", description: "Plan ID" }
|
|
176
|
+
},
|
|
177
|
+
required: ["task_id", "plan_id"]
|
|
165
178
|
}
|
|
166
179
|
},
|
|
167
180
|
|
|
168
181
|
// ========================================
|
|
169
|
-
//
|
|
182
|
+
// CONTEXT LOADING - Get everything you need
|
|
183
|
+
// Use before starting work on a plan/goal
|
|
170
184
|
// ========================================
|
|
171
185
|
{
|
|
172
|
-
name: "
|
|
173
|
-
description: "
|
|
186
|
+
name: "get_my_tasks",
|
|
187
|
+
description: "Get tasks that need attention - blocked tasks, in-progress tasks, and next tasks to start. Perfect for status check-ins.",
|
|
174
188
|
inputSchema: {
|
|
175
189
|
type: "object",
|
|
176
190
|
properties: {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
type: "string",
|
|
184
|
-
enum: ["learning", "decision", "context", "constraint"],
|
|
185
|
-
default: "learning",
|
|
186
|
-
description: "Type of knowledge"
|
|
191
|
+
plan_id: { type: "string", description: "Specific plan to check (optional - checks all if not provided)" },
|
|
192
|
+
status: {
|
|
193
|
+
type: "array",
|
|
194
|
+
items: { type: "string" },
|
|
195
|
+
default: ["blocked", "in_progress"],
|
|
196
|
+
description: "Task statuses to include"
|
|
187
197
|
}
|
|
188
|
-
}
|
|
189
|
-
required: ["title", "content"]
|
|
198
|
+
}
|
|
190
199
|
}
|
|
191
200
|
},
|
|
192
201
|
|
|
@@ -380,14 +389,20 @@ function setupTools(server) {
|
|
|
380
389
|
status: {
|
|
381
390
|
type: "string",
|
|
382
391
|
description: "Node status",
|
|
383
|
-
enum: ["not_started", "in_progress", "completed", "blocked"],
|
|
392
|
+
enum: ["not_started", "in_progress", "completed", "blocked", "plan_ready"],
|
|
384
393
|
default: "not_started"
|
|
385
394
|
},
|
|
386
395
|
context: { type: "string", description: "Additional context for the node" },
|
|
387
396
|
agent_instructions: { type: "string", description: "Instructions for AI agents working on this node" },
|
|
388
397
|
acceptance_criteria: { type: "string", description: "Criteria for node completion" },
|
|
389
398
|
due_date: { type: "string", description: "Due date (ISO format)" },
|
|
390
|
-
metadata: { type: "object", description: "Additional metadata" }
|
|
399
|
+
metadata: { type: "object", description: "Additional metadata" },
|
|
400
|
+
task_mode: {
|
|
401
|
+
type: "string",
|
|
402
|
+
description: "RPI workflow mode for the node",
|
|
403
|
+
enum: ["research", "plan", "implement", "free"],
|
|
404
|
+
default: "free"
|
|
405
|
+
}
|
|
391
406
|
},
|
|
392
407
|
required: ["plan_id", "node_type", "title"]
|
|
393
408
|
}
|
|
@@ -405,13 +420,18 @@ function setupTools(server) {
|
|
|
405
420
|
status: {
|
|
406
421
|
type: "string",
|
|
407
422
|
description: "New node status",
|
|
408
|
-
enum: ["not_started", "in_progress", "completed", "blocked"]
|
|
423
|
+
enum: ["not_started", "in_progress", "completed", "blocked", "plan_ready"]
|
|
409
424
|
},
|
|
410
425
|
context: { type: "string", description: "New context" },
|
|
411
426
|
agent_instructions: { type: "string", description: "New agent instructions" },
|
|
412
427
|
acceptance_criteria: { type: "string", description: "New acceptance criteria" },
|
|
413
428
|
due_date: { type: "string", description: "New due date (ISO format)" },
|
|
414
|
-
metadata: { type: "object", description: "New metadata" }
|
|
429
|
+
metadata: { type: "object", description: "New metadata" },
|
|
430
|
+
task_mode: {
|
|
431
|
+
type: "string",
|
|
432
|
+
description: "RPI workflow mode for the node",
|
|
433
|
+
enum: ["research", "plan", "implement", "free"]
|
|
434
|
+
}
|
|
415
435
|
},
|
|
416
436
|
required: ["plan_id", "node_id"]
|
|
417
437
|
}
|
|
@@ -443,8 +463,8 @@ function setupTools(server) {
|
|
|
443
463
|
}
|
|
444
464
|
},
|
|
445
465
|
{
|
|
446
|
-
name: "
|
|
447
|
-
description: "Get
|
|
466
|
+
name: "get_node_ancestry",
|
|
467
|
+
description: "Get the path from root to a specific node",
|
|
448
468
|
inputSchema: {
|
|
449
469
|
type: "object",
|
|
450
470
|
properties: {
|
|
@@ -454,19 +474,169 @@ function setupTools(server) {
|
|
|
454
474
|
required: ["plan_id", "node_id"]
|
|
455
475
|
}
|
|
456
476
|
},
|
|
477
|
+
|
|
478
|
+
// ===== DEPENDENCY TOOLS =====
|
|
479
|
+
{
|
|
480
|
+
name: "create_dependency",
|
|
481
|
+
description: "Create a dependency edge between two nodes in a plan. Source 'blocks' target by default.",
|
|
482
|
+
inputSchema: {
|
|
483
|
+
type: "object",
|
|
484
|
+
properties: {
|
|
485
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
486
|
+
source_node_id: { type: "string", description: "Source node ID (the blocker)" },
|
|
487
|
+
target_node_id: { type: "string", description: "Target node ID (the blocked)" },
|
|
488
|
+
dependency_type: {
|
|
489
|
+
type: "string",
|
|
490
|
+
description: "Type of dependency",
|
|
491
|
+
enum: ["blocks", "requires", "relates_to"],
|
|
492
|
+
default: "blocks"
|
|
493
|
+
},
|
|
494
|
+
weight: { type: "integer", description: "Edge weight (default 1)", default: 1 },
|
|
495
|
+
metadata: { type: "object", description: "Additional metadata" }
|
|
496
|
+
},
|
|
497
|
+
required: ["plan_id", "source_node_id", "target_node_id"]
|
|
498
|
+
}
|
|
499
|
+
},
|
|
457
500
|
{
|
|
458
|
-
name: "
|
|
459
|
-
description: "
|
|
501
|
+
name: "delete_dependency",
|
|
502
|
+
description: "Delete a dependency edge",
|
|
460
503
|
inputSchema: {
|
|
461
504
|
type: "object",
|
|
462
505
|
properties: {
|
|
463
506
|
plan_id: { type: "string", description: "Plan ID" },
|
|
464
|
-
|
|
507
|
+
dependency_id: { type: "string", description: "Dependency edge ID" }
|
|
508
|
+
},
|
|
509
|
+
required: ["plan_id", "dependency_id"]
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
name: "list_dependencies",
|
|
514
|
+
description: "List all dependency edges in a plan",
|
|
515
|
+
inputSchema: {
|
|
516
|
+
type: "object",
|
|
517
|
+
properties: {
|
|
518
|
+
plan_id: { type: "string", description: "Plan ID" }
|
|
519
|
+
},
|
|
520
|
+
required: ["plan_id"]
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
name: "get_node_dependencies",
|
|
525
|
+
description: "Get upstream and downstream dependencies for a node",
|
|
526
|
+
inputSchema: {
|
|
527
|
+
type: "object",
|
|
528
|
+
properties: {
|
|
529
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
530
|
+
node_id: { type: "string", description: "Node ID" },
|
|
531
|
+
direction: {
|
|
532
|
+
type: "string",
|
|
533
|
+
description: "Direction to query",
|
|
534
|
+
enum: ["upstream", "downstream", "both"],
|
|
535
|
+
default: "both"
|
|
536
|
+
}
|
|
465
537
|
},
|
|
466
538
|
required: ["plan_id", "node_id"]
|
|
467
539
|
}
|
|
468
540
|
},
|
|
469
|
-
|
|
541
|
+
|
|
542
|
+
// ===== RPI WORKFLOW =====
|
|
543
|
+
{
|
|
544
|
+
name: "create_rpi_chain",
|
|
545
|
+
description: "Create a Research→Plan→Implement task chain with automatic dependency edges. The three tasks are linked: Research blocks Plan, Plan blocks Implement.",
|
|
546
|
+
inputSchema: {
|
|
547
|
+
type: "object",
|
|
548
|
+
properties: {
|
|
549
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
550
|
+
title: { type: "string", description: "Base title for the chain (e.g. 'Auth refactor')" },
|
|
551
|
+
description: { type: "string", description: "Description for the research task" },
|
|
552
|
+
parent_id: { type: "string", description: "Parent node ID (optional, defaults to root)" }
|
|
553
|
+
},
|
|
554
|
+
required: ["plan_id", "title"]
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
|
|
558
|
+
// ===== ANALYSIS TOOLS =====
|
|
559
|
+
{
|
|
560
|
+
name: "analyze_impact",
|
|
561
|
+
description: "Analyze what happens if a node is delayed, blocked, or removed. Shows directly and transitively affected nodes.",
|
|
562
|
+
inputSchema: {
|
|
563
|
+
type: "object",
|
|
564
|
+
properties: {
|
|
565
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
566
|
+
node_id: { type: "string", description: "Node ID to analyze" },
|
|
567
|
+
scenario: {
|
|
568
|
+
type: "string",
|
|
569
|
+
description: "Impact scenario",
|
|
570
|
+
enum: ["delay", "block", "remove"],
|
|
571
|
+
default: "block"
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
required: ["plan_id", "node_id"]
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
name: "get_critical_path",
|
|
579
|
+
description: "Find the critical path (longest dependency chain) through incomplete tasks in a plan",
|
|
580
|
+
inputSchema: {
|
|
581
|
+
type: "object",
|
|
582
|
+
properties: {
|
|
583
|
+
plan_id: { type: "string", description: "Plan ID" }
|
|
584
|
+
},
|
|
585
|
+
required: ["plan_id"]
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
|
|
589
|
+
// ===== PROGRESSIVE CONTEXT TOOLS =====
|
|
590
|
+
{
|
|
591
|
+
name: "get_task_context",
|
|
592
|
+
description: "Get progressive context for a task at adjustable depth. This is the PRIMARY way to load context before starting work on a task.\n\nDepth levels:\n- 1: Task focus — node details + recent logs\n- 2: Local neighborhood — adds parent, siblings, direct dependencies\n- 3: Knowledge — adds plan-scoped knowledge entries\n- 4: Extended — adds plan overview, ancestry, goals, transitive dependencies\n\nFor RPI implement tasks, automatically includes research/plan outputs from the chain.",
|
|
593
|
+
inputSchema: {
|
|
594
|
+
type: "object",
|
|
595
|
+
properties: {
|
|
596
|
+
node_id: { type: "string", description: "Task/node ID to get context for" },
|
|
597
|
+
depth: {
|
|
598
|
+
type: "integer",
|
|
599
|
+
description: "Context depth 1-4 (default 2). Start with 2, go deeper if needed.",
|
|
600
|
+
minimum: 1,
|
|
601
|
+
maximum: 4,
|
|
602
|
+
default: 2
|
|
603
|
+
},
|
|
604
|
+
token_budget: {
|
|
605
|
+
type: "integer",
|
|
606
|
+
description: "Max estimated tokens (0 = unlimited). Use to stay within context window limits.",
|
|
607
|
+
default: 0
|
|
608
|
+
},
|
|
609
|
+
log_limit: {
|
|
610
|
+
type: "integer",
|
|
611
|
+
description: "Max recent logs to include per node",
|
|
612
|
+
default: 10
|
|
613
|
+
},
|
|
614
|
+
include_research: {
|
|
615
|
+
type: "boolean",
|
|
616
|
+
description: "Include research outputs from RPI chain siblings (for implement tasks)",
|
|
617
|
+
default: true
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
required: ["node_id"]
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
name: "suggest_next_tasks",
|
|
625
|
+
description: "Suggest the next actionable tasks for a plan based on dependency analysis. Returns tasks where all upstream blockers are completed, prioritized by: RPI research tasks first, then by how many downstream tasks each unblocks.",
|
|
626
|
+
inputSchema: {
|
|
627
|
+
type: "object",
|
|
628
|
+
properties: {
|
|
629
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
630
|
+
limit: {
|
|
631
|
+
type: "integer",
|
|
632
|
+
description: "Maximum suggestions to return",
|
|
633
|
+
default: 5
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
required: ["plan_id"]
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
|
|
470
640
|
// ===== LOGGING TOOLS (Replaces Comments) =====
|
|
471
641
|
{
|
|
472
642
|
name: "add_log",
|
|
@@ -532,7 +702,7 @@ function setupTools(server) {
|
|
|
532
702
|
node_id: { type: "string", description: "Node ID" },
|
|
533
703
|
status: {
|
|
534
704
|
type: "string",
|
|
535
|
-
enum: ["not_started", "in_progress", "completed", "blocked"]
|
|
705
|
+
enum: ["not_started", "in_progress", "completed", "blocked", "plan_ready"]
|
|
536
706
|
},
|
|
537
707
|
title: { type: "string" },
|
|
538
708
|
description: { type: "string" }
|
|
@@ -548,7 +718,7 @@ function setupTools(server) {
|
|
|
548
718
|
// ===== PLAN STRUCTURE & SUMMARY =====
|
|
549
719
|
{
|
|
550
720
|
name: "get_plan_structure",
|
|
551
|
-
description: "Get the hierarchical structure of a plan with minimal fields (id, parent_id, node_type, title, status, order_index). Use
|
|
721
|
+
description: "Get the hierarchical structure of a plan with minimal fields (id, parent_id, node_type, title, status, order_index). Use get_task_context for detailed information about specific nodes.",
|
|
552
722
|
inputSchema: {
|
|
553
723
|
type: "object",
|
|
554
724
|
properties: {
|
|
@@ -575,33 +745,9 @@ function setupTools(server) {
|
|
|
575
745
|
},
|
|
576
746
|
|
|
577
747
|
// ===== AGENT CONTEXT TOOLS (Leaf-up context loading) =====
|
|
578
|
-
{
|
|
579
|
-
name: "get_agent_context",
|
|
580
|
-
description: "Get focused context for a specific task/node. Uses leaf-up traversal - returns only the relevant path from the node to root, not the entire plan tree. Best for agents starting work on a specific task.",
|
|
581
|
-
inputSchema: {
|
|
582
|
-
type: "object",
|
|
583
|
-
properties: {
|
|
584
|
-
node_id: {
|
|
585
|
-
type: "string",
|
|
586
|
-
description: "Task or phase node ID to get context for"
|
|
587
|
-
},
|
|
588
|
-
include_knowledge: {
|
|
589
|
-
type: "boolean",
|
|
590
|
-
description: "Include knowledge entries from relevant scopes (plan, goals, org)",
|
|
591
|
-
default: true
|
|
592
|
-
},
|
|
593
|
-
include_siblings: {
|
|
594
|
-
type: "boolean",
|
|
595
|
-
description: "Include sibling tasks in the same phase",
|
|
596
|
-
default: false
|
|
597
|
-
}
|
|
598
|
-
},
|
|
599
|
-
required: ["node_id"]
|
|
600
|
-
}
|
|
601
|
-
},
|
|
602
748
|
{
|
|
603
749
|
name: "get_plan_context",
|
|
604
|
-
description: "Get plan-level context overview. Returns plan details, phase summaries (not full tree), linked goals, and organization. Use
|
|
750
|
+
description: "Get plan-level context overview. Returns plan details, phase summaries (not full tree), linked goals, and organization. Use get_task_context for task-focused work.",
|
|
605
751
|
inputSchema: {
|
|
606
752
|
type: "object",
|
|
607
753
|
properties: {
|
|
@@ -766,102 +912,174 @@ function setupTools(server) {
|
|
|
766
912
|
}
|
|
767
913
|
},
|
|
768
914
|
|
|
769
|
-
// =====
|
|
915
|
+
// ===== CROSS-PLAN & EXTERNAL DEPENDENCY TOOLS =====
|
|
770
916
|
{
|
|
771
|
-
name: "
|
|
772
|
-
description: "
|
|
917
|
+
name: "create_cross_plan_dependency",
|
|
918
|
+
description: "Create a dependency edge between nodes in different plans. Use when a task in one plan blocks or requires a task in another plan.",
|
|
773
919
|
inputSchema: {
|
|
774
920
|
type: "object",
|
|
775
921
|
properties: {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
},
|
|
781
|
-
scope_id: { type: "string", description: "ID of the org, goal, or plan" },
|
|
782
|
-
entry_type: {
|
|
783
|
-
type: "string",
|
|
784
|
-
description: "Type of knowledge entry",
|
|
785
|
-
enum: ["decision", "context", "constraint", "learning", "reference", "note"]
|
|
786
|
-
},
|
|
787
|
-
title: { type: "string", description: "Entry title" },
|
|
788
|
-
content: { type: "string", description: "Entry content" },
|
|
789
|
-
source_url: { type: "string", description: "Source URL (optional)" },
|
|
790
|
-
tags: { type: "array", description: "Tags for categorization", items: { type: "string" } }
|
|
922
|
+
source_node_id: { type: "string", description: "Source node ID (the blocker/prerequisite)" },
|
|
923
|
+
target_node_id: { type: "string", description: "Target node ID (the blocked/dependent task)" },
|
|
924
|
+
dependency_type: { type: "string", enum: ["blocks", "requires", "relates_to"], default: "blocks", description: "Edge type (default: blocks)" },
|
|
925
|
+
weight: { type: "number", description: "Edge weight (default 1)" }
|
|
791
926
|
},
|
|
792
|
-
required: ["
|
|
927
|
+
required: ["source_node_id", "target_node_id"]
|
|
793
928
|
}
|
|
794
929
|
},
|
|
795
930
|
{
|
|
796
|
-
name: "
|
|
797
|
-
description: "List
|
|
931
|
+
name: "list_cross_plan_dependencies",
|
|
932
|
+
description: "List all dependency edges that cross plan boundaries between specified plans.",
|
|
798
933
|
inputSchema: {
|
|
799
934
|
type: "object",
|
|
800
935
|
properties: {
|
|
801
|
-
|
|
802
|
-
type: "
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
}
|
|
806
|
-
scope_id: { type: "string", description: "ID of the org, goal, or plan" },
|
|
807
|
-
entry_type: {
|
|
808
|
-
type: "string",
|
|
809
|
-
description: "Filter by entry type",
|
|
810
|
-
enum: ["decision", "context", "constraint", "learning", "reference", "note"]
|
|
811
|
-
},
|
|
812
|
-
tags: { type: "string", description: "Filter by tags (comma-separated)" },
|
|
813
|
-
limit: { type: "integer", description: "Max entries to return", default: 50 }
|
|
936
|
+
plan_ids: {
|
|
937
|
+
type: "array",
|
|
938
|
+
items: { type: "string" },
|
|
939
|
+
description: "Plan IDs to check for cross-plan edges (at least 2)"
|
|
940
|
+
}
|
|
814
941
|
},
|
|
815
|
-
required: ["
|
|
942
|
+
required: ["plan_ids"]
|
|
816
943
|
}
|
|
817
944
|
},
|
|
818
945
|
{
|
|
819
|
-
name: "
|
|
820
|
-
description: "
|
|
946
|
+
name: "create_external_dependency",
|
|
947
|
+
description: "Create an external dependency node representing a blocker outside the system (vendor API, legal approval, etc.). Optionally blocks a target task.",
|
|
821
948
|
inputSchema: {
|
|
822
949
|
type: "object",
|
|
823
950
|
properties: {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
951
|
+
plan_id: { type: "string", description: "Plan to add the external dependency to" },
|
|
952
|
+
title: { type: "string", description: "External dependency title (e.g., 'Waiting for vendor API access')" },
|
|
953
|
+
description: { type: "string", description: "Details about the external dependency" },
|
|
954
|
+
url: { type: "string", description: "URL reference (ticket, docs, etc.)" },
|
|
955
|
+
blocks_node_id: { type: "string", description: "Node ID that this external dep blocks" }
|
|
956
|
+
},
|
|
957
|
+
required: ["plan_id", "title"]
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
|
|
961
|
+
// ===== GOAL-DEPENDENCY TOOLS =====
|
|
962
|
+
{
|
|
963
|
+
name: "goal_path",
|
|
964
|
+
description: "Get the full dependency path to a goal — all tasks that contribute to achieving it (direct achievers + their upstream blockers). Shows completion stats and which tasks are blocking progress.",
|
|
965
|
+
inputSchema: {
|
|
966
|
+
type: "object",
|
|
967
|
+
properties: {
|
|
968
|
+
goal_id: { type: "string", description: "Goal ID" },
|
|
969
|
+
max_depth: { type: "number", description: "Max traversal depth (default 20)" }
|
|
970
|
+
},
|
|
971
|
+
required: ["goal_id"]
|
|
972
|
+
}
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
name: "goal_progress",
|
|
976
|
+
description: "Get goal progress calculated from its dependency graph. Returns overall completion percentage and direct achiever progress.",
|
|
977
|
+
inputSchema: {
|
|
978
|
+
type: "object",
|
|
979
|
+
properties: {
|
|
980
|
+
goal_id: { type: "string", description: "Goal ID" }
|
|
981
|
+
},
|
|
982
|
+
required: ["goal_id"]
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
name: "add_achiever",
|
|
987
|
+
description: "Link a task to a goal via an 'achieves' dependency edge. This declares that completing this task contributes to achieving the goal.",
|
|
988
|
+
inputSchema: {
|
|
989
|
+
type: "object",
|
|
990
|
+
properties: {
|
|
991
|
+
goal_id: { type: "string", description: "Goal ID" },
|
|
992
|
+
node_id: { type: "string", description: "Task/node ID that achieves this goal" },
|
|
993
|
+
weight: { type: "number", description: "Edge weight for critical path (default 1)" }
|
|
994
|
+
},
|
|
995
|
+
required: ["goal_id", "node_id"]
|
|
996
|
+
}
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
name: "remove_achiever",
|
|
1000
|
+
description: "Remove an achieves edge between a task and a goal",
|
|
1001
|
+
inputSchema: {
|
|
1002
|
+
type: "object",
|
|
1003
|
+
properties: {
|
|
1004
|
+
goal_id: { type: "string", description: "Goal ID" },
|
|
1005
|
+
dependency_id: { type: "string", description: "Dependency edge ID to remove" }
|
|
1006
|
+
},
|
|
1007
|
+
required: ["goal_id", "dependency_id"]
|
|
1008
|
+
}
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
name: "goal_knowledge_gaps",
|
|
1012
|
+
description: "Detect knowledge gaps for a goal — checks which tasks on the goal's dependency path lack relevant knowledge in the temporal knowledge graph. Useful for identifying where research is needed before implementation.",
|
|
1013
|
+
inputSchema: {
|
|
1014
|
+
type: "object",
|
|
1015
|
+
properties: {
|
|
1016
|
+
goal_id: { type: "string", description: "Goal ID" }
|
|
1017
|
+
},
|
|
1018
|
+
required: ["goal_id"]
|
|
1019
|
+
}
|
|
1020
|
+
},
|
|
1021
|
+
|
|
1022
|
+
// ===== GRAPHITI KNOWLEDGE GRAPH TOOLS =====
|
|
1023
|
+
{
|
|
1024
|
+
name: "add_learning",
|
|
1025
|
+
description: "Record a knowledge episode to the temporal knowledge graph. Use this after research, when making decisions, or discovering important context. Graphiti automatically extracts entities and relationships. The knowledge persists across plans and sessions.",
|
|
1026
|
+
inputSchema: {
|
|
1027
|
+
type: "object",
|
|
1028
|
+
properties: {
|
|
1029
|
+
content: { type: "string", description: "The knowledge content — be detailed. Include context, reasoning, and conclusions." },
|
|
1030
|
+
title: { type: "string", description: "Short title/name for the episode" },
|
|
1031
|
+
entry_type: { type: "string", enum: ["decision", "learning", "context", "constraint"], description: "Type of knowledge" },
|
|
1032
|
+
plan_id: { type: "string", description: "Plan ID this knowledge relates to (optional)" },
|
|
1033
|
+
node_id: { type: "string", description: "Node/task ID this knowledge relates to (optional)" }
|
|
1034
|
+
},
|
|
1035
|
+
required: ["content"]
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
name: "recall_knowledge",
|
|
1040
|
+
description: "Search the temporal knowledge graph for relevant facts, decisions, and learnings. Searches across ALL plans in the organization. Use before starting work or making decisions.",
|
|
1041
|
+
inputSchema: {
|
|
1042
|
+
type: "object",
|
|
1043
|
+
properties: {
|
|
1044
|
+
query: { type: "string", description: "What to search for — be specific" },
|
|
1045
|
+
max_results: { type: "number", description: "Maximum results (default 10)", default: 10 }
|
|
837
1046
|
},
|
|
838
1047
|
required: ["query"]
|
|
839
1048
|
}
|
|
840
1049
|
},
|
|
841
1050
|
{
|
|
842
|
-
name: "
|
|
843
|
-
description: "
|
|
1051
|
+
name: "find_entities",
|
|
1052
|
+
description: "Search for entities (technologies, people, patterns, constraints) in the knowledge graph. Returns entity nodes with their relationships.",
|
|
844
1053
|
inputSchema: {
|
|
845
1054
|
type: "object",
|
|
846
1055
|
properties: {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
content: { type: "string", description: "New content" },
|
|
850
|
-
entry_type: { type: "string", description: "New type" },
|
|
851
|
-
tags: { type: "array", description: "New tags", items: { type: "string" } }
|
|
1056
|
+
query: { type: "string", description: "Entity search query" },
|
|
1057
|
+
max_results: { type: "number", description: "Maximum results (default 10)", default: 10 }
|
|
852
1058
|
},
|
|
853
|
-
required: ["
|
|
1059
|
+
required: ["query"]
|
|
854
1060
|
}
|
|
855
1061
|
},
|
|
1062
|
+
|
|
856
1063
|
{
|
|
857
|
-
name: "
|
|
858
|
-
description: "
|
|
1064
|
+
name: "check_contradictions",
|
|
1065
|
+
description: "Check if knowledge about a topic has changed over time. Returns current facts and any superseded (outdated) facts. Useful before making decisions based on past knowledge — ensures you're working with the latest information.",
|
|
859
1066
|
inputSchema: {
|
|
860
1067
|
type: "object",
|
|
861
1068
|
properties: {
|
|
862
|
-
|
|
1069
|
+
query: { type: "string", description: "Topic to check for contradictions" },
|
|
1070
|
+
max_results: { type: "number", description: "Maximum results (default 10)", default: 10 }
|
|
863
1071
|
},
|
|
864
|
-
required: ["
|
|
1072
|
+
required: ["query"]
|
|
1073
|
+
}
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
name: "get_recent_episodes",
|
|
1077
|
+
description: "Get recent knowledge episodes from the temporal graph. Returns the latest episodes (learnings, decisions, context) across all plans. Useful to understand what has been learned recently or to review your own work session history.",
|
|
1078
|
+
inputSchema: {
|
|
1079
|
+
type: "object",
|
|
1080
|
+
properties: {
|
|
1081
|
+
max_episodes: { type: "number", description: "Maximum episodes to return (default 20)", default: 20 }
|
|
1082
|
+
}
|
|
865
1083
|
}
|
|
866
1084
|
},
|
|
867
1085
|
|
|
@@ -881,18 +1099,6 @@ function setupTools(server) {
|
|
|
881
1099
|
}
|
|
882
1100
|
}
|
|
883
1101
|
},
|
|
884
|
-
{
|
|
885
|
-
name: "understand_context",
|
|
886
|
-
description: "Get comprehensive context about a plan or goal - its purpose, current state, recent activity, blocked tasks, and relevant knowledge. Use this BEFORE starting work to understand the full situation.",
|
|
887
|
-
inputSchema: {
|
|
888
|
-
type: "object",
|
|
889
|
-
properties: {
|
|
890
|
-
plan_id: { type: "string", description: "Plan ID to understand" },
|
|
891
|
-
goal_id: { type: "string", description: "Goal ID to understand" },
|
|
892
|
-
include_knowledge: { type: "boolean", description: "Include relevant knowledge entries", default: true }
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
1102
|
]
|
|
897
1103
|
};
|
|
898
1104
|
});
|
|
@@ -967,14 +1173,14 @@ function setupTools(server) {
|
|
|
967
1173
|
success: true,
|
|
968
1174
|
message: `Plan "${title}" created with ${tasks.length} tasks`,
|
|
969
1175
|
plan_id: plan.id,
|
|
970
|
-
plan_url:
|
|
1176
|
+
plan_url: buildPlanUrl(plan.id),
|
|
971
1177
|
phase_id: phase.id,
|
|
972
1178
|
task_ids: createdTasks.map(t => t.id),
|
|
973
1179
|
tasks: createdTasks,
|
|
974
1180
|
next_steps: [
|
|
975
1181
|
"Use quick_status to update task progress",
|
|
976
1182
|
"Use quick_log to document your work",
|
|
977
|
-
"Use
|
|
1183
|
+
"Use get_plan_context to load full plan details"
|
|
978
1184
|
]
|
|
979
1185
|
});
|
|
980
1186
|
}
|
|
@@ -1029,7 +1235,7 @@ function setupTools(server) {
|
|
|
1029
1235
|
task_id: task.id,
|
|
1030
1236
|
plan_id: plan_id,
|
|
1031
1237
|
phase_id: targetPhaseId,
|
|
1032
|
-
task_url:
|
|
1238
|
+
task_url: buildTaskUrl(plan_id, task.id),
|
|
1033
1239
|
next_steps: [
|
|
1034
1240
|
"Use quick_status to mark as in_progress when you start",
|
|
1035
1241
|
"Use quick_log to document progress"
|
|
@@ -1110,96 +1316,7 @@ function setupTools(server) {
|
|
|
1110
1316
|
// ========================================
|
|
1111
1317
|
// CONTEXT LOADING IMPLEMENTATIONS
|
|
1112
1318
|
// ========================================
|
|
1113
|
-
|
|
1114
|
-
if (name === "get_context") {
|
|
1115
|
-
// Redirect to understand_context implementation (same functionality)
|
|
1116
|
-
const { plan_id, goal_id, include_knowledge = true } = args;
|
|
1117
|
-
|
|
1118
|
-
if (!plan_id && !goal_id) {
|
|
1119
|
-
return formatResponse({
|
|
1120
|
-
error: "Provide either plan_id or goal_id to get context",
|
|
1121
|
-
suggestion: "Use list_plans or list_goals to find IDs"
|
|
1122
|
-
});
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
const context = {
|
|
1126
|
-
retrieved_at: new Date().toISOString()
|
|
1127
|
-
};
|
|
1128
|
-
|
|
1129
|
-
// Get goal context
|
|
1130
|
-
if (goal_id) {
|
|
1131
|
-
try {
|
|
1132
|
-
context.goal = await apiClient.goals.get(goal_id);
|
|
1133
|
-
if (include_knowledge) {
|
|
1134
|
-
try {
|
|
1135
|
-
const knowledge = await apiClient.knowledge.getEntries({
|
|
1136
|
-
scope: 'goal',
|
|
1137
|
-
scope_id: goal_id,
|
|
1138
|
-
limit: 10
|
|
1139
|
-
});
|
|
1140
|
-
context.goal_knowledge = knowledge.entries || [];
|
|
1141
|
-
} catch (e) {}
|
|
1142
|
-
}
|
|
1143
|
-
} catch (e) {
|
|
1144
|
-
context.goal_error = e.message;
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// Get plan context
|
|
1149
|
-
if (plan_id) {
|
|
1150
|
-
try {
|
|
1151
|
-
context.plan = await apiClient.plans.getPlan(plan_id);
|
|
1152
|
-
context.plan_url = `https://www.agentplanner.io/app/plans/${plan_id}`;
|
|
1153
|
-
|
|
1154
|
-
const nodes = await apiClient.nodes.getNodes(plan_id);
|
|
1155
|
-
context.statistics = calculatePlanStatistics(nodes);
|
|
1156
|
-
context.progress_percentage = context.statistics.total > 0
|
|
1157
|
-
? ((context.statistics.status_counts.completed / context.statistics.total) * 100).toFixed(1) + '%'
|
|
1158
|
-
: '0%';
|
|
1159
|
-
|
|
1160
|
-
const flatNodes = flattenNodes(nodes);
|
|
1161
|
-
context.needs_attention = {
|
|
1162
|
-
blocked: flatNodes.filter(n => n.status === 'blocked').map(n => ({
|
|
1163
|
-
id: n.id, title: n.title, type: n.node_type
|
|
1164
|
-
})),
|
|
1165
|
-
in_progress: flatNodes.filter(n => n.status === 'in_progress').map(n => ({
|
|
1166
|
-
id: n.id, title: n.title, type: n.node_type
|
|
1167
|
-
})),
|
|
1168
|
-
ready_to_start: flatNodes
|
|
1169
|
-
.filter(n => n.node_type === 'task' && n.status === 'not_started')
|
|
1170
|
-
.slice(0, 5)
|
|
1171
|
-
.map(n => ({ id: n.id, title: n.title }))
|
|
1172
|
-
};
|
|
1173
|
-
|
|
1174
|
-
try {
|
|
1175
|
-
const activity = await apiClient.activity.getPlanActivity(plan_id);
|
|
1176
|
-
context.recent_activity = (activity || []).slice(0, 5);
|
|
1177
|
-
} catch (e) {}
|
|
1178
|
-
|
|
1179
|
-
if (include_knowledge) {
|
|
1180
|
-
try {
|
|
1181
|
-
const knowledge = await apiClient.knowledge.getEntries({
|
|
1182
|
-
scope: 'plan',
|
|
1183
|
-
scope_id: plan_id,
|
|
1184
|
-
limit: 10
|
|
1185
|
-
});
|
|
1186
|
-
context.plan_knowledge = knowledge.entries || [];
|
|
1187
|
-
} catch (e) {}
|
|
1188
|
-
}
|
|
1189
|
-
} catch (e) {
|
|
1190
|
-
context.plan_error = e.message;
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
context.recommendation = context.needs_attention?.blocked?.length > 0
|
|
1195
|
-
? "⚠️ There are blocked tasks that need attention first"
|
|
1196
|
-
: context.needs_attention?.in_progress?.length > 0
|
|
1197
|
-
? "Continue working on the in-progress tasks"
|
|
1198
|
-
: "Pick a task from ready_to_start to begin";
|
|
1199
|
-
|
|
1200
|
-
return formatResponse(context);
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1319
|
+
|
|
1203
1320
|
if (name === "get_my_tasks") {
|
|
1204
1321
|
const { plan_id, status = ["blocked", "in_progress"] } = args;
|
|
1205
1322
|
|
|
@@ -1260,34 +1377,8 @@ function setupTools(server) {
|
|
|
1260
1377
|
return formatResponse(tasks);
|
|
1261
1378
|
}
|
|
1262
1379
|
|
|
1263
|
-
//
|
|
1264
|
-
|
|
1265
|
-
// ========================================
|
|
1266
|
-
|
|
1267
|
-
if (name === "add_learning") {
|
|
1268
|
-
const { title, content, scope = 'organization', scope_id, tags, entry_type = 'learning' } = args;
|
|
1269
|
-
|
|
1270
|
-
const entryData = {
|
|
1271
|
-
entry_type,
|
|
1272
|
-
title,
|
|
1273
|
-
content
|
|
1274
|
-
};
|
|
1275
|
-
if (scope) entryData.scope = scope;
|
|
1276
|
-
if (scope_id) entryData.scope_id = scope_id;
|
|
1277
|
-
if (tags) entryData.tags = tags;
|
|
1278
|
-
|
|
1279
|
-
const entry = await apiClient.knowledge.createEntry(entryData);
|
|
1280
|
-
|
|
1281
|
-
return formatResponse({
|
|
1282
|
-
success: true,
|
|
1283
|
-
message: "Knowledge captured for future reference",
|
|
1284
|
-
entry_id: entry.id,
|
|
1285
|
-
entry_type,
|
|
1286
|
-
title,
|
|
1287
|
-
tip: "This will be searchable via search_knowledge. Good practice!"
|
|
1288
|
-
});
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1380
|
+
// add_learning handled in GRAPHITI KNOWLEDGE GRAPH HANDLERS section below
|
|
1381
|
+
|
|
1291
1382
|
// ========================================
|
|
1292
1383
|
// MARKDOWN EXPORT
|
|
1293
1384
|
// ========================================
|
|
@@ -1475,11 +1566,11 @@ function setupTools(server) {
|
|
|
1475
1566
|
success: true,
|
|
1476
1567
|
message: `Plan "${planTitle}" created from markdown with ${phases.length} phases and ${createdTasks.length} tasks`,
|
|
1477
1568
|
plan_id: plan.id,
|
|
1478
|
-
plan_url:
|
|
1569
|
+
plan_url: buildPlanUrl(plan.id),
|
|
1479
1570
|
phases: createdPhases,
|
|
1480
1571
|
tasks: createdTasks,
|
|
1481
1572
|
next_steps: [
|
|
1482
|
-
"Use
|
|
1573
|
+
"Use get_plan_context to review the imported plan",
|
|
1483
1574
|
"Use quick_status to update task progress"
|
|
1484
1575
|
]
|
|
1485
1576
|
});
|
|
@@ -1600,7 +1691,7 @@ function setupTools(server) {
|
|
|
1600
1691
|
const result = await apiClient.plans.updateVisibility(plan_id, visibilityData);
|
|
1601
1692
|
|
|
1602
1693
|
const shareUrl = visibility === "public"
|
|
1603
|
-
?
|
|
1694
|
+
? buildPlanUrl(plan_id)
|
|
1604
1695
|
: null;
|
|
1605
1696
|
|
|
1606
1697
|
return formatResponse({
|
|
@@ -1641,55 +1732,130 @@ function setupTools(server) {
|
|
|
1641
1732
|
|
|
1642
1733
|
if (name === "move_node") {
|
|
1643
1734
|
const { plan_id, node_id, parent_id, order_index } = args;
|
|
1644
|
-
|
|
1735
|
+
|
|
1645
1736
|
try {
|
|
1737
|
+
// Build request body with only provided fields - don't send nulls
|
|
1738
|
+
const body = {};
|
|
1739
|
+
if (parent_id) body.parent_id = parent_id;
|
|
1740
|
+
if (order_index !== undefined) body.order_index = order_index;
|
|
1741
|
+
|
|
1646
1742
|
// Call the move endpoint - using POST as per API definition
|
|
1647
1743
|
const response = await apiClient.axiosInstance.post(
|
|
1648
1744
|
`/plans/${plan_id}/nodes/${node_id}/move`,
|
|
1649
|
-
|
|
1650
|
-
parent_id: parent_id || null,
|
|
1651
|
-
order_index: order_index !== undefined ? order_index : null
|
|
1652
|
-
}
|
|
1745
|
+
body
|
|
1653
1746
|
);
|
|
1654
|
-
|
|
1747
|
+
|
|
1655
1748
|
return formatResponse(response.data);
|
|
1656
1749
|
} catch (error) {
|
|
1657
1750
|
// If endpoint still doesn't work, try updating the node directly
|
|
1658
1751
|
if (error.response && error.response.status === 404) {
|
|
1659
1752
|
console.error('Move endpoint not found, trying direct update');
|
|
1660
1753
|
// Fallback to updating the node's parent_id via regular update
|
|
1661
|
-
const
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1754
|
+
const updateData = {};
|
|
1755
|
+
if (parent_id) updateData.parent_id = parent_id;
|
|
1756
|
+
if (order_index !== undefined) updateData.order_index = order_index;
|
|
1757
|
+
const updateResponse = await apiClient.nodes.updateNode(plan_id, node_id, updateData);
|
|
1665
1758
|
return formatResponse(updateResponse);
|
|
1666
1759
|
}
|
|
1667
1760
|
throw error;
|
|
1668
1761
|
}
|
|
1669
1762
|
}
|
|
1670
1763
|
|
|
1671
|
-
if (name === "
|
|
1764
|
+
if (name === "get_node_ancestry") {
|
|
1672
1765
|
const { plan_id, node_id } = args;
|
|
1673
1766
|
|
|
1674
|
-
// Get node
|
|
1767
|
+
// Get node ancestry
|
|
1675
1768
|
const response = await apiClient.axiosInstance.get(
|
|
1676
|
-
`/plans/${plan_id}/nodes/${node_id}/
|
|
1769
|
+
`/plans/${plan_id}/nodes/${node_id}/ancestry`
|
|
1677
1770
|
);
|
|
1678
1771
|
|
|
1679
1772
|
return formatResponse(response.data);
|
|
1680
1773
|
}
|
|
1681
1774
|
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1775
|
+
// ===== DEPENDENCIES =====
|
|
1776
|
+
if (name === "create_dependency") {
|
|
1777
|
+
const { plan_id, source_node_id, target_node_id, dependency_type, weight, metadata } = args;
|
|
1778
|
+
const response = await apiClient.axiosInstance.post(
|
|
1779
|
+
`/plans/${plan_id}/dependencies`,
|
|
1780
|
+
{ source_node_id, target_node_id, dependency_type, weight, metadata }
|
|
1781
|
+
);
|
|
1782
|
+
return formatResponse(response.data);
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
if (name === "delete_dependency") {
|
|
1786
|
+
const { plan_id, dependency_id } = args;
|
|
1787
|
+
const response = await apiClient.axiosInstance.delete(
|
|
1788
|
+
`/plans/${plan_id}/dependencies/${dependency_id}`
|
|
1789
|
+
);
|
|
1790
|
+
return formatResponse(response.data);
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
if (name === "list_dependencies") {
|
|
1794
|
+
const { plan_id } = args;
|
|
1686
1795
|
const response = await apiClient.axiosInstance.get(
|
|
1687
|
-
`/plans/${plan_id}/
|
|
1796
|
+
`/plans/${plan_id}/dependencies`
|
|
1688
1797
|
);
|
|
1689
|
-
|
|
1690
1798
|
return formatResponse(response.data);
|
|
1691
1799
|
}
|
|
1692
|
-
|
|
1800
|
+
|
|
1801
|
+
if (name === "get_node_dependencies") {
|
|
1802
|
+
const { plan_id, node_id, direction = 'both' } = args;
|
|
1803
|
+
const response = await apiClient.axiosInstance.get(
|
|
1804
|
+
`/plans/${plan_id}/nodes/${node_id}/dependencies`,
|
|
1805
|
+
{ params: { direction } }
|
|
1806
|
+
);
|
|
1807
|
+
return formatResponse(response.data);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// ===== RPI WORKFLOW =====
|
|
1811
|
+
if (name === "create_rpi_chain") {
|
|
1812
|
+
const { plan_id, title, description, parent_id } = args;
|
|
1813
|
+
const response = await apiClient.axiosInstance.post(
|
|
1814
|
+
`/plans/${plan_id}/nodes/rpi-chain`,
|
|
1815
|
+
{ title, description, parent_id }
|
|
1816
|
+
);
|
|
1817
|
+
return formatResponse(response.data);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// ===== ANALYSIS =====
|
|
1821
|
+
if (name === "analyze_impact") {
|
|
1822
|
+
const { plan_id, node_id, scenario = 'block' } = args;
|
|
1823
|
+
const response = await apiClient.axiosInstance.get(
|
|
1824
|
+
`/plans/${plan_id}/nodes/${node_id}/impact`,
|
|
1825
|
+
{ params: { scenario } }
|
|
1826
|
+
);
|
|
1827
|
+
return formatResponse(response.data);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
if (name === "get_critical_path") {
|
|
1831
|
+
const { plan_id } = args;
|
|
1832
|
+
const response = await apiClient.axiosInstance.get(
|
|
1833
|
+
`/plans/${plan_id}/critical-path`
|
|
1834
|
+
);
|
|
1835
|
+
return formatResponse(response.data);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
// ===== PROGRESSIVE CONTEXT =====
|
|
1839
|
+
if (name === "get_task_context") {
|
|
1840
|
+
const { node_id, depth = 2, token_budget = 0, log_limit = 10, include_research = true } = args;
|
|
1841
|
+
const params = new URLSearchParams({
|
|
1842
|
+
node_id,
|
|
1843
|
+
depth: String(depth),
|
|
1844
|
+
token_budget: String(token_budget),
|
|
1845
|
+
log_limit: String(log_limit),
|
|
1846
|
+
include_research: String(include_research),
|
|
1847
|
+
});
|
|
1848
|
+
const response = await apiClient.axiosInstance.get(`/context/progressive?${params.toString()}`);
|
|
1849
|
+
return formatResponse(response.data);
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
if (name === "suggest_next_tasks") {
|
|
1853
|
+
const { plan_id, limit = 5 } = args;
|
|
1854
|
+
const params = new URLSearchParams({ plan_id, limit: String(limit) });
|
|
1855
|
+
const response = await apiClient.axiosInstance.get(`/context/suggest?${params.toString()}`);
|
|
1856
|
+
return formatResponse(response.data);
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1693
1859
|
// ===== LOGGING =====
|
|
1694
1860
|
if (name === "add_log") {
|
|
1695
1861
|
const { plan_id, node_id, content, log_type = "comment", tags } = args;
|
|
@@ -1804,17 +1970,6 @@ function setupTools(server) {
|
|
|
1804
1970
|
}
|
|
1805
1971
|
|
|
1806
1972
|
// ===== AGENT CONTEXT TOOLS =====
|
|
1807
|
-
if (name === "get_agent_context") {
|
|
1808
|
-
const { node_id, include_knowledge = true, include_siblings = false } = args;
|
|
1809
|
-
|
|
1810
|
-
const result = await apiClient.context.getNodeContext(node_id, {
|
|
1811
|
-
include_knowledge,
|
|
1812
|
-
include_siblings
|
|
1813
|
-
});
|
|
1814
|
-
|
|
1815
|
-
return formatResponse(result);
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
1973
|
if (name === "get_plan_context") {
|
|
1819
1974
|
const { plan_id, include_knowledge = true } = args;
|
|
1820
1975
|
|
|
@@ -1901,60 +2056,154 @@ function setupTools(server) {
|
|
|
1901
2056
|
});
|
|
1902
2057
|
}
|
|
1903
2058
|
|
|
1904
|
-
// =====
|
|
1905
|
-
if (name === "
|
|
1906
|
-
const {
|
|
1907
|
-
const result = await apiClient.
|
|
1908
|
-
|
|
1909
|
-
scope_id,
|
|
1910
|
-
entry_type,
|
|
1911
|
-
title,
|
|
1912
|
-
content,
|
|
1913
|
-
source_url,
|
|
1914
|
-
tags
|
|
2059
|
+
// ===== CROSS-PLAN & EXTERNAL DEPENDENCY HANDLERS =====
|
|
2060
|
+
if (name === "create_cross_plan_dependency") {
|
|
2061
|
+
const { source_node_id, target_node_id, dependency_type, weight } = args;
|
|
2062
|
+
const result = await apiClient.dependencies.createCrossPlan({
|
|
2063
|
+
source_node_id, target_node_id, dependency_type, weight
|
|
1915
2064
|
});
|
|
1916
2065
|
return formatResponse(result);
|
|
1917
2066
|
}
|
|
1918
|
-
|
|
1919
|
-
if (name === "list_knowledge_entries") {
|
|
1920
|
-
const { scope, scope_id, entry_type, tags, limit = 50 } = args;
|
|
1921
2067
|
|
|
1922
|
-
|
|
2068
|
+
if (name === "list_cross_plan_dependencies") {
|
|
2069
|
+
const { plan_ids } = args;
|
|
2070
|
+
const result = await apiClient.dependencies.listCrossPlan(plan_ids);
|
|
1923
2071
|
return formatResponse(result);
|
|
1924
2072
|
}
|
|
1925
|
-
|
|
1926
|
-
if (name === "
|
|
1927
|
-
const {
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
if (scope && scope_id) {
|
|
1932
|
-
searchData.scope = scope;
|
|
1933
|
-
searchData.scope_id = scope_id;
|
|
1934
|
-
}
|
|
1935
|
-
if (entry_types) {
|
|
1936
|
-
searchData.entry_types = entry_types;
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
const result = await apiClient.knowledge.search(searchData);
|
|
2073
|
+
|
|
2074
|
+
if (name === "create_external_dependency") {
|
|
2075
|
+
const { plan_id, title, description, url, blocks_node_id } = args;
|
|
2076
|
+
const result = await apiClient.dependencies.createExternal({
|
|
2077
|
+
plan_id, title, description, url, blocks_node_id
|
|
2078
|
+
});
|
|
1940
2079
|
return formatResponse(result);
|
|
1941
2080
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
const
|
|
2081
|
+
|
|
2082
|
+
// ===== GOAL-DEPENDENCY HANDLERS =====
|
|
2083
|
+
if (name === "goal_path") {
|
|
2084
|
+
const { goal_id, max_depth } = args;
|
|
2085
|
+
const result = await apiClient.goals.getPath(goal_id, max_depth);
|
|
1946
2086
|
return formatResponse(result);
|
|
1947
2087
|
}
|
|
1948
|
-
|
|
1949
|
-
if (name === "
|
|
1950
|
-
const {
|
|
1951
|
-
await apiClient.
|
|
2088
|
+
|
|
2089
|
+
if (name === "goal_progress") {
|
|
2090
|
+
const { goal_id } = args;
|
|
2091
|
+
const result = await apiClient.goals.getProgress(goal_id);
|
|
2092
|
+
return formatResponse(result);
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
if (name === "add_achiever") {
|
|
2096
|
+
const { goal_id, node_id, weight } = args;
|
|
2097
|
+
const result = await apiClient.goals.addAchiever(goal_id, node_id, weight);
|
|
1952
2098
|
return formatResponse({
|
|
1953
|
-
|
|
1954
|
-
message: `
|
|
2099
|
+
...result,
|
|
2100
|
+
message: `Task ${node_id} now achieves goal ${goal_id}`,
|
|
1955
2101
|
});
|
|
1956
2102
|
}
|
|
1957
2103
|
|
|
2104
|
+
if (name === "remove_achiever") {
|
|
2105
|
+
const { goal_id, dependency_id } = args;
|
|
2106
|
+
const result = await apiClient.goals.removeAchiever(goal_id, dependency_id);
|
|
2107
|
+
return formatResponse(result);
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
if (name === "goal_knowledge_gaps") {
|
|
2111
|
+
const { goal_id } = args;
|
|
2112
|
+
const result = await apiClient.goals.getKnowledgeGaps(goal_id);
|
|
2113
|
+
return formatResponse(result);
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
// ===== GRAPHITI KNOWLEDGE GRAPH HANDLERS =====
|
|
2117
|
+
if (name === "add_learning") {
|
|
2118
|
+
const { content, title, entry_type, plan_id, node_id } = args;
|
|
2119
|
+
|
|
2120
|
+
// Add to Graphiti temporal knowledge graph
|
|
2121
|
+
const result = await apiClient.graphiti.addEpisode({
|
|
2122
|
+
content,
|
|
2123
|
+
name: title,
|
|
2124
|
+
plan_id,
|
|
2125
|
+
node_id,
|
|
2126
|
+
metadata: { entry_type: entry_type || 'learning' },
|
|
2127
|
+
});
|
|
2128
|
+
return formatResponse({
|
|
2129
|
+
...result,
|
|
2130
|
+
message: 'Knowledge recorded in temporal graph',
|
|
2131
|
+
tip: 'This is now searchable via recall_knowledge across all plans'
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
if (name === "recall_knowledge") {
|
|
2136
|
+
const { query, max_results = 10 } = args;
|
|
2137
|
+
|
|
2138
|
+
// Try Graphiti first (temporal, cross-plan)
|
|
2139
|
+
try {
|
|
2140
|
+
const graphResult = await apiClient.graphiti.graphSearch({ query, max_results });
|
|
2141
|
+
if (graphResult?.results) {
|
|
2142
|
+
return formatResponse({
|
|
2143
|
+
...graphResult,
|
|
2144
|
+
source: 'graphiti_temporal_graph'
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
} catch (err) {
|
|
2148
|
+
return formatResponse({
|
|
2149
|
+
results: [],
|
|
2150
|
+
source: 'graphiti_temporal_graph',
|
|
2151
|
+
error: 'Knowledge graph not available: ' + err.message,
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
if (name === "find_entities") {
|
|
2157
|
+
const { query, max_results = 10 } = args;
|
|
2158
|
+
|
|
2159
|
+
try {
|
|
2160
|
+
const result = await apiClient.graphiti.searchEntities({ query, max_results });
|
|
2161
|
+
return formatResponse(result);
|
|
2162
|
+
} catch (err) {
|
|
2163
|
+
return formatResponse({
|
|
2164
|
+
error: 'Entity search requires the temporal knowledge graph (Graphiti)',
|
|
2165
|
+
detail: err.message
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
if (name === "check_contradictions") {
|
|
2171
|
+
const { query, max_results = 10 } = args;
|
|
2172
|
+
|
|
2173
|
+
try {
|
|
2174
|
+
const result = await apiClient.graphiti.detectContradictions({ query, max_results });
|
|
2175
|
+
if (result.contradictions_found) {
|
|
2176
|
+
return formatResponse({
|
|
2177
|
+
...result,
|
|
2178
|
+
warning: 'Some knowledge has been superseded. Review the "superseded" facts before proceeding.',
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
return formatResponse({
|
|
2182
|
+
...result,
|
|
2183
|
+
message: 'No contradictions found — all facts are current.',
|
|
2184
|
+
});
|
|
2185
|
+
} catch (err) {
|
|
2186
|
+
return formatResponse({
|
|
2187
|
+
error: 'Contradiction detection requires the temporal knowledge graph (Graphiti)',
|
|
2188
|
+
detail: err.message,
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
if (name === "get_recent_episodes") {
|
|
2194
|
+
const { max_episodes = 20 } = args || {};
|
|
2195
|
+
|
|
2196
|
+
try {
|
|
2197
|
+
const result = await apiClient.graphiti.getEpisodes({ max_episodes });
|
|
2198
|
+
return formatResponse(result);
|
|
2199
|
+
} catch (err) {
|
|
2200
|
+
return formatResponse({
|
|
2201
|
+
error: 'Episodic memory requires the temporal knowledge graph (Graphiti)',
|
|
2202
|
+
detail: err.message
|
|
2203
|
+
});
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
|
|
1958
2207
|
// ===== HELPER TOOLS =====
|
|
1959
2208
|
if (name === "get_started") {
|
|
1960
2209
|
const { topic = "overview" } = args || {};
|
|
@@ -1973,10 +2222,10 @@ function setupTools(server) {
|
|
|
1973
2222
|
recommended_workflow: [
|
|
1974
2223
|
"1. Check list_goals to understand current objectives",
|
|
1975
2224
|
"2. Use list_plans to see existing plans",
|
|
1976
|
-
"3. Before working on a plan, use
|
|
2225
|
+
"3. Before working on a plan, use get_plan_context to get the full picture",
|
|
1977
2226
|
"4. Update task statuses as you work (update_node with status)",
|
|
1978
|
-
"5. Store important decisions and learnings using
|
|
1979
|
-
"6. Check
|
|
2227
|
+
"5. Store important decisions and learnings using add_learning",
|
|
2228
|
+
"6. Check recall_knowledge before making decisions to see past context"
|
|
1980
2229
|
],
|
|
1981
2230
|
quick_tips: [
|
|
1982
2231
|
"Always capture WHY decisions were made, not just WHAT",
|
|
@@ -2008,7 +2257,7 @@ function setupTools(server) {
|
|
|
2008
2257
|
workflow: [
|
|
2009
2258
|
"1. Use get_plan_structure to see the full plan",
|
|
2010
2259
|
"2. Find tasks with status 'not_started' or 'in_progress'",
|
|
2011
|
-
"3. Before starting a task, check
|
|
2260
|
+
"3. Before starting a task, check recall_knowledge for relevant context",
|
|
2012
2261
|
"4. Update task status to 'in_progress' when you begin",
|
|
2013
2262
|
"5. Add logs to document what you're doing",
|
|
2014
2263
|
"6. Mark 'completed' when done, or 'blocked' if stuck"
|
|
@@ -2025,7 +2274,7 @@ function setupTools(server) {
|
|
|
2025
2274
|
"When blocked, clearly document what's blocking you",
|
|
2026
2275
|
"Store learnings as you go - don't wait until the end"
|
|
2027
2276
|
],
|
|
2028
|
-
tools_to_use: ["get_plan_structure", "update_node", "add_log", "
|
|
2277
|
+
tools_to_use: ["get_plan_structure", "update_node", "add_log", "recall_knowledge"]
|
|
2029
2278
|
},
|
|
2030
2279
|
knowledge: {
|
|
2031
2280
|
title: "Knowledge Management",
|
|
@@ -2051,7 +2300,7 @@ function setupTools(server) {
|
|
|
2051
2300
|
"When you discover a constraint or rule",
|
|
2052
2301
|
"When you find a useful resource or reference"
|
|
2053
2302
|
],
|
|
2054
|
-
tools_to_use: ["
|
|
2303
|
+
tools_to_use: ["add_learning", "recall_knowledge", "find_entities", "check_contradictions"]
|
|
2055
2304
|
},
|
|
2056
2305
|
collaboration: {
|
|
2057
2306
|
title: "Collaboration",
|
|
@@ -2074,98 +2323,45 @@ function setupTools(server) {
|
|
|
2074
2323
|
return formatResponse(guides[topic] || guides.overview);
|
|
2075
2324
|
}
|
|
2076
2325
|
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
const context = {
|
|
2087
|
-
retrieved_at: new Date().toISOString()
|
|
2088
|
-
};
|
|
2089
|
-
|
|
2090
|
-
// Get goal context
|
|
2091
|
-
if (goal_id) {
|
|
2092
|
-
try {
|
|
2093
|
-
context.goal = await apiClient.goals.get(goal_id);
|
|
2094
|
-
|
|
2095
|
-
// Get related knowledge if available
|
|
2096
|
-
if (include_knowledge) {
|
|
2097
|
-
try {
|
|
2098
|
-
const knowledge = await apiClient.knowledge.getEntries({
|
|
2099
|
-
scope: 'goal',
|
|
2100
|
-
scope_id: goal_id,
|
|
2101
|
-
limit: 10
|
|
2102
|
-
});
|
|
2103
|
-
context.goal_knowledge = knowledge.entries || [];
|
|
2104
|
-
} catch (e) {
|
|
2105
|
-
// Knowledge fetch failed, continue without it
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
} catch (e) {
|
|
2109
|
-
context.goal_error = e.message;
|
|
2110
|
-
}
|
|
2111
|
-
}
|
|
2112
|
-
|
|
2113
|
-
// Get plan context
|
|
2114
|
-
if (plan_id) {
|
|
2115
|
-
try {
|
|
2116
|
-
context.plan = await apiClient.plans.getPlan(plan_id);
|
|
2117
|
-
|
|
2118
|
-
const nodes = await apiClient.nodes.getNodes(plan_id);
|
|
2119
|
-
context.statistics = calculatePlanStatistics(nodes);
|
|
2120
|
-
context.progress_percentage = context.statistics.total > 0
|
|
2121
|
-
? ((context.statistics.status_counts.completed / context.statistics.total) * 100).toFixed(1) + '%'
|
|
2122
|
-
: '0%';
|
|
2123
|
-
|
|
2124
|
-
// Get blocked and in-progress tasks for attention
|
|
2125
|
-
const flatNodes = flattenNodes(nodes);
|
|
2126
|
-
context.needs_attention = {
|
|
2127
|
-
blocked: flatNodes.filter(n => n.status === 'blocked').map(n => ({
|
|
2128
|
-
id: n.id,
|
|
2129
|
-
title: n.title,
|
|
2130
|
-
type: n.node_type
|
|
2131
|
-
})),
|
|
2132
|
-
in_progress: flatNodes.filter(n => n.status === 'in_progress').map(n => ({
|
|
2133
|
-
id: n.id,
|
|
2134
|
-
title: n.title,
|
|
2135
|
-
type: n.node_type
|
|
2136
|
-
}))
|
|
2137
|
-
};
|
|
2138
|
-
|
|
2139
|
-
// Get recent activity
|
|
2140
|
-
try {
|
|
2141
|
-
const activity = await apiClient.activity.getPlanActivity(plan_id);
|
|
2142
|
-
context.recent_activity = (activity || []).slice(0, 5);
|
|
2143
|
-
} catch (e) {
|
|
2144
|
-
// Activity fetch failed, continue without it
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
// Get related knowledge if available
|
|
2148
|
-
if (include_knowledge) {
|
|
2149
|
-
try {
|
|
2150
|
-
const knowledge = await apiClient.knowledge.getEntries({
|
|
2151
|
-
scope: 'plan',
|
|
2152
|
-
scope_id: plan_id,
|
|
2153
|
-
limit: 10
|
|
2154
|
-
});
|
|
2155
|
-
context.plan_knowledge = knowledge.entries || [];
|
|
2156
|
-
} catch (e) {
|
|
2157
|
-
// Knowledge fetch failed, continue without it
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
} catch (e) {
|
|
2161
|
-
context.plan_error = e.message;
|
|
2162
|
-
}
|
|
2326
|
+
// ===== GOALS HEALTH DASHBOARD =====
|
|
2327
|
+
if (name === "check_goals_health") {
|
|
2328
|
+
const { status_filter } = args || {};
|
|
2329
|
+
const result = await apiClient.goals.getDashboard();
|
|
2330
|
+
|
|
2331
|
+
let goals = result.goals || result;
|
|
2332
|
+
if (status_filter && Array.isArray(goals)) {
|
|
2333
|
+
goals = goals.filter(g => g.health_status === status_filter || g.status === status_filter);
|
|
2163
2334
|
}
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2335
|
+
|
|
2336
|
+
return formatResponse({
|
|
2337
|
+
...result,
|
|
2338
|
+
goals,
|
|
2339
|
+
tip: "Prioritize: stale goals first, then at_risk, then on_track."
|
|
2340
|
+
});
|
|
2167
2341
|
}
|
|
2168
|
-
|
|
2342
|
+
|
|
2343
|
+
// ===== TASK CLAIMING =====
|
|
2344
|
+
if (name === "claim_task") {
|
|
2345
|
+
const { task_id, plan_id, ttl_minutes = 30 } = args;
|
|
2346
|
+
const result = await apiClient.nodes.claimTask(plan_id, task_id, 'mcp-agent', ttl_minutes);
|
|
2347
|
+
return formatResponse({
|
|
2348
|
+
success: true,
|
|
2349
|
+
message: `Task ${task_id} claimed for ${ttl_minutes} minutes`,
|
|
2350
|
+
...result,
|
|
2351
|
+
tip: "Remember to release the task when done, or it will auto-expire."
|
|
2352
|
+
});
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
if (name === "release_task") {
|
|
2356
|
+
const { task_id, plan_id } = args;
|
|
2357
|
+
const result = await apiClient.nodes.releaseTask(plan_id, task_id, 'mcp-agent');
|
|
2358
|
+
return formatResponse({
|
|
2359
|
+
success: true,
|
|
2360
|
+
message: `Task ${task_id} released`,
|
|
2361
|
+
...result
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2169
2365
|
// Tool not found
|
|
2170
2366
|
throw new Error(`Unknown tool: ${name}`);
|
|
2171
2367
|
} catch (error) {
|