agent-planner-mcp 0.5.0 → 0.5.1
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 +438 -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 +653 -210
- 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,6 +138,46 @@ 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
|
+
|
|
152
|
+
// ========================================
|
|
153
|
+
// TASK CLAIMING - Prevent agent collisions
|
|
154
|
+
// ========================================
|
|
155
|
+
{
|
|
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.",
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: "object",
|
|
160
|
+
properties: {
|
|
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"]
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
{
|
|
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.",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
type: "object",
|
|
173
|
+
properties: {
|
|
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"]
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
|
|
135
181
|
// ========================================
|
|
136
182
|
// CONTEXT LOADING - Get everything you need
|
|
137
183
|
// Use before starting work on a plan/goal
|
|
@@ -150,7 +196,7 @@ function setupTools(server) {
|
|
|
150
196
|
},
|
|
151
197
|
{
|
|
152
198
|
name: "get_my_tasks",
|
|
153
|
-
description: "Get tasks that need attention - blocked tasks, in-progress tasks, and next tasks to start. Perfect for
|
|
199
|
+
description: "Get tasks that need attention - blocked tasks, in-progress tasks, and next tasks to start. Perfect for status check-ins.",
|
|
154
200
|
inputSchema: {
|
|
155
201
|
type: "object",
|
|
156
202
|
properties: {
|
|
@@ -165,31 +211,6 @@ function setupTools(server) {
|
|
|
165
211
|
}
|
|
166
212
|
},
|
|
167
213
|
|
|
168
|
-
// ========================================
|
|
169
|
-
// KNOWLEDGE - Build persistent memory
|
|
170
|
-
// ========================================
|
|
171
|
-
{
|
|
172
|
-
name: "add_learning",
|
|
173
|
-
description: "Capture something you learned for future reference. This persists beyond the current session - other agents and future-you will benefit.",
|
|
174
|
-
inputSchema: {
|
|
175
|
-
type: "object",
|
|
176
|
-
properties: {
|
|
177
|
-
title: { type: "string", description: "Brief title summarizing the learning" },
|
|
178
|
-
content: { type: "string", description: "What you learned - be specific and include context" },
|
|
179
|
-
scope: { type: "string", enum: ["organization", "goal", "plan"], description: "Where to store this (defaults to organization)" },
|
|
180
|
-
scope_id: { type: "string", description: "ID of the organization/goal/plan" },
|
|
181
|
-
tags: { type: "array", items: { type: "string" }, description: "Tags for easier retrieval" },
|
|
182
|
-
entry_type: {
|
|
183
|
-
type: "string",
|
|
184
|
-
enum: ["learning", "decision", "context", "constraint"],
|
|
185
|
-
default: "learning",
|
|
186
|
-
description: "Type of knowledge"
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
required: ["title", "content"]
|
|
190
|
-
}
|
|
191
|
-
},
|
|
192
|
-
|
|
193
214
|
// ========================================
|
|
194
215
|
// MARKDOWN EXPORT/IMPORT - Filesystem pattern
|
|
195
216
|
// ========================================
|
|
@@ -380,14 +401,20 @@ function setupTools(server) {
|
|
|
380
401
|
status: {
|
|
381
402
|
type: "string",
|
|
382
403
|
description: "Node status",
|
|
383
|
-
enum: ["not_started", "in_progress", "completed", "blocked"],
|
|
404
|
+
enum: ["not_started", "in_progress", "completed", "blocked", "plan_ready"],
|
|
384
405
|
default: "not_started"
|
|
385
406
|
},
|
|
386
407
|
context: { type: "string", description: "Additional context for the node" },
|
|
387
408
|
agent_instructions: { type: "string", description: "Instructions for AI agents working on this node" },
|
|
388
409
|
acceptance_criteria: { type: "string", description: "Criteria for node completion" },
|
|
389
410
|
due_date: { type: "string", description: "Due date (ISO format)" },
|
|
390
|
-
metadata: { type: "object", description: "Additional metadata" }
|
|
411
|
+
metadata: { type: "object", description: "Additional metadata" },
|
|
412
|
+
task_mode: {
|
|
413
|
+
type: "string",
|
|
414
|
+
description: "RPI workflow mode for the node",
|
|
415
|
+
enum: ["research", "plan", "implement", "free"],
|
|
416
|
+
default: "free"
|
|
417
|
+
}
|
|
391
418
|
},
|
|
392
419
|
required: ["plan_id", "node_type", "title"]
|
|
393
420
|
}
|
|
@@ -405,13 +432,18 @@ function setupTools(server) {
|
|
|
405
432
|
status: {
|
|
406
433
|
type: "string",
|
|
407
434
|
description: "New node status",
|
|
408
|
-
enum: ["not_started", "in_progress", "completed", "blocked"]
|
|
435
|
+
enum: ["not_started", "in_progress", "completed", "blocked", "plan_ready"]
|
|
409
436
|
},
|
|
410
437
|
context: { type: "string", description: "New context" },
|
|
411
438
|
agent_instructions: { type: "string", description: "New agent instructions" },
|
|
412
439
|
acceptance_criteria: { type: "string", description: "New acceptance criteria" },
|
|
413
440
|
due_date: { type: "string", description: "New due date (ISO format)" },
|
|
414
|
-
metadata: { type: "object", description: "New metadata" }
|
|
441
|
+
metadata: { type: "object", description: "New metadata" },
|
|
442
|
+
task_mode: {
|
|
443
|
+
type: "string",
|
|
444
|
+
description: "RPI workflow mode for the node",
|
|
445
|
+
enum: ["research", "plan", "implement", "free"]
|
|
446
|
+
}
|
|
415
447
|
},
|
|
416
448
|
required: ["plan_id", "node_id"]
|
|
417
449
|
}
|
|
@@ -467,6 +499,168 @@ function setupTools(server) {
|
|
|
467
499
|
}
|
|
468
500
|
},
|
|
469
501
|
|
|
502
|
+
// ===== DEPENDENCY TOOLS =====
|
|
503
|
+
{
|
|
504
|
+
name: "create_dependency",
|
|
505
|
+
description: "Create a dependency edge between two nodes in a plan. Source 'blocks' target by default.",
|
|
506
|
+
inputSchema: {
|
|
507
|
+
type: "object",
|
|
508
|
+
properties: {
|
|
509
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
510
|
+
source_node_id: { type: "string", description: "Source node ID (the blocker)" },
|
|
511
|
+
target_node_id: { type: "string", description: "Target node ID (the blocked)" },
|
|
512
|
+
dependency_type: {
|
|
513
|
+
type: "string",
|
|
514
|
+
description: "Type of dependency",
|
|
515
|
+
enum: ["blocks", "requires", "relates_to"],
|
|
516
|
+
default: "blocks"
|
|
517
|
+
},
|
|
518
|
+
weight: { type: "integer", description: "Edge weight (default 1)", default: 1 },
|
|
519
|
+
metadata: { type: "object", description: "Additional metadata" }
|
|
520
|
+
},
|
|
521
|
+
required: ["plan_id", "source_node_id", "target_node_id"]
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: "delete_dependency",
|
|
526
|
+
description: "Delete a dependency edge",
|
|
527
|
+
inputSchema: {
|
|
528
|
+
type: "object",
|
|
529
|
+
properties: {
|
|
530
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
531
|
+
dependency_id: { type: "string", description: "Dependency edge ID" }
|
|
532
|
+
},
|
|
533
|
+
required: ["plan_id", "dependency_id"]
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
name: "list_dependencies",
|
|
538
|
+
description: "List all dependency edges in a plan",
|
|
539
|
+
inputSchema: {
|
|
540
|
+
type: "object",
|
|
541
|
+
properties: {
|
|
542
|
+
plan_id: { type: "string", description: "Plan ID" }
|
|
543
|
+
},
|
|
544
|
+
required: ["plan_id"]
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: "get_node_dependencies",
|
|
549
|
+
description: "Get upstream and downstream dependencies for a node",
|
|
550
|
+
inputSchema: {
|
|
551
|
+
type: "object",
|
|
552
|
+
properties: {
|
|
553
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
554
|
+
node_id: { type: "string", description: "Node ID" },
|
|
555
|
+
direction: {
|
|
556
|
+
type: "string",
|
|
557
|
+
description: "Direction to query",
|
|
558
|
+
enum: ["upstream", "downstream", "both"],
|
|
559
|
+
default: "both"
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
required: ["plan_id", "node_id"]
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
|
|
566
|
+
// ===== RPI WORKFLOW =====
|
|
567
|
+
{
|
|
568
|
+
name: "create_rpi_chain",
|
|
569
|
+
description: "Create a Research→Plan→Implement task chain with automatic dependency edges. The three tasks are linked: Research blocks Plan, Plan blocks Implement.",
|
|
570
|
+
inputSchema: {
|
|
571
|
+
type: "object",
|
|
572
|
+
properties: {
|
|
573
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
574
|
+
title: { type: "string", description: "Base title for the chain (e.g. 'Auth refactor')" },
|
|
575
|
+
description: { type: "string", description: "Description for the research task" },
|
|
576
|
+
parent_id: { type: "string", description: "Parent node ID (optional, defaults to root)" }
|
|
577
|
+
},
|
|
578
|
+
required: ["plan_id", "title"]
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
|
|
582
|
+
// ===== ANALYSIS TOOLS =====
|
|
583
|
+
{
|
|
584
|
+
name: "analyze_impact",
|
|
585
|
+
description: "Analyze what happens if a node is delayed, blocked, or removed. Shows directly and transitively affected nodes.",
|
|
586
|
+
inputSchema: {
|
|
587
|
+
type: "object",
|
|
588
|
+
properties: {
|
|
589
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
590
|
+
node_id: { type: "string", description: "Node ID to analyze" },
|
|
591
|
+
scenario: {
|
|
592
|
+
type: "string",
|
|
593
|
+
description: "Impact scenario",
|
|
594
|
+
enum: ["delay", "block", "remove"],
|
|
595
|
+
default: "block"
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
required: ["plan_id", "node_id"]
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
name: "get_critical_path",
|
|
603
|
+
description: "Find the critical path (longest dependency chain) through incomplete tasks in a plan",
|
|
604
|
+
inputSchema: {
|
|
605
|
+
type: "object",
|
|
606
|
+
properties: {
|
|
607
|
+
plan_id: { type: "string", description: "Plan ID" }
|
|
608
|
+
},
|
|
609
|
+
required: ["plan_id"]
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
|
|
613
|
+
// ===== PROGRESSIVE CONTEXT TOOLS =====
|
|
614
|
+
{
|
|
615
|
+
name: "get_task_context",
|
|
616
|
+
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.",
|
|
617
|
+
inputSchema: {
|
|
618
|
+
type: "object",
|
|
619
|
+
properties: {
|
|
620
|
+
node_id: { type: "string", description: "Task/node ID to get context for" },
|
|
621
|
+
depth: {
|
|
622
|
+
type: "integer",
|
|
623
|
+
description: "Context depth 1-4 (default 2). Start with 2, go deeper if needed.",
|
|
624
|
+
minimum: 1,
|
|
625
|
+
maximum: 4,
|
|
626
|
+
default: 2
|
|
627
|
+
},
|
|
628
|
+
token_budget: {
|
|
629
|
+
type: "integer",
|
|
630
|
+
description: "Max estimated tokens (0 = unlimited). Use to stay within context window limits.",
|
|
631
|
+
default: 0
|
|
632
|
+
},
|
|
633
|
+
log_limit: {
|
|
634
|
+
type: "integer",
|
|
635
|
+
description: "Max recent logs to include per node",
|
|
636
|
+
default: 10
|
|
637
|
+
},
|
|
638
|
+
include_research: {
|
|
639
|
+
type: "boolean",
|
|
640
|
+
description: "Include research outputs from RPI chain siblings (for implement tasks)",
|
|
641
|
+
default: true
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
required: ["node_id"]
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
name: "suggest_next_tasks",
|
|
649
|
+
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.",
|
|
650
|
+
inputSchema: {
|
|
651
|
+
type: "object",
|
|
652
|
+
properties: {
|
|
653
|
+
plan_id: { type: "string", description: "Plan ID" },
|
|
654
|
+
limit: {
|
|
655
|
+
type: "integer",
|
|
656
|
+
description: "Maximum suggestions to return",
|
|
657
|
+
default: 5
|
|
658
|
+
}
|
|
659
|
+
},
|
|
660
|
+
required: ["plan_id"]
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
|
|
470
664
|
// ===== LOGGING TOOLS (Replaces Comments) =====
|
|
471
665
|
{
|
|
472
666
|
name: "add_log",
|
|
@@ -532,7 +726,7 @@ function setupTools(server) {
|
|
|
532
726
|
node_id: { type: "string", description: "Node ID" },
|
|
533
727
|
status: {
|
|
534
728
|
type: "string",
|
|
535
|
-
enum: ["not_started", "in_progress", "completed", "blocked"]
|
|
729
|
+
enum: ["not_started", "in_progress", "completed", "blocked", "plan_ready"]
|
|
536
730
|
},
|
|
537
731
|
title: { type: "string" },
|
|
538
732
|
description: { type: "string" }
|
|
@@ -766,102 +960,174 @@ function setupTools(server) {
|
|
|
766
960
|
}
|
|
767
961
|
},
|
|
768
962
|
|
|
769
|
-
// =====
|
|
963
|
+
// ===== CROSS-PLAN & EXTERNAL DEPENDENCY TOOLS =====
|
|
770
964
|
{
|
|
771
|
-
name: "
|
|
772
|
-
description: "
|
|
965
|
+
name: "create_cross_plan_dependency",
|
|
966
|
+
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
967
|
inputSchema: {
|
|
774
968
|
type: "object",
|
|
775
969
|
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" } }
|
|
970
|
+
source_node_id: { type: "string", description: "Source node ID (the blocker/prerequisite)" },
|
|
971
|
+
target_node_id: { type: "string", description: "Target node ID (the blocked/dependent task)" },
|
|
972
|
+
dependency_type: { type: "string", enum: ["blocks", "requires", "relates_to"], default: "blocks", description: "Edge type (default: blocks)" },
|
|
973
|
+
weight: { type: "number", description: "Edge weight (default 1)" }
|
|
791
974
|
},
|
|
792
|
-
required: ["
|
|
975
|
+
required: ["source_node_id", "target_node_id"]
|
|
793
976
|
}
|
|
794
977
|
},
|
|
795
978
|
{
|
|
796
|
-
name: "
|
|
797
|
-
description: "List
|
|
979
|
+
name: "list_cross_plan_dependencies",
|
|
980
|
+
description: "List all dependency edges that cross plan boundaries between specified plans.",
|
|
798
981
|
inputSchema: {
|
|
799
982
|
type: "object",
|
|
800
983
|
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 }
|
|
984
|
+
plan_ids: {
|
|
985
|
+
type: "array",
|
|
986
|
+
items: { type: "string" },
|
|
987
|
+
description: "Plan IDs to check for cross-plan edges (at least 2)"
|
|
988
|
+
}
|
|
814
989
|
},
|
|
815
|
-
required: ["
|
|
990
|
+
required: ["plan_ids"]
|
|
816
991
|
}
|
|
817
992
|
},
|
|
818
993
|
{
|
|
819
|
-
name: "
|
|
820
|
-
description: "
|
|
994
|
+
name: "create_external_dependency",
|
|
995
|
+
description: "Create an external dependency node representing a blocker outside the system (vendor API, legal approval, etc.). Optionally blocks a target task.",
|
|
821
996
|
inputSchema: {
|
|
822
997
|
type: "object",
|
|
823
998
|
properties: {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
999
|
+
plan_id: { type: "string", description: "Plan to add the external dependency to" },
|
|
1000
|
+
title: { type: "string", description: "External dependency title (e.g., 'Waiting for vendor API access')" },
|
|
1001
|
+
description: { type: "string", description: "Details about the external dependency" },
|
|
1002
|
+
url: { type: "string", description: "URL reference (ticket, docs, etc.)" },
|
|
1003
|
+
blocks_node_id: { type: "string", description: "Node ID that this external dep blocks" }
|
|
1004
|
+
},
|
|
1005
|
+
required: ["plan_id", "title"]
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
|
|
1009
|
+
// ===== GOAL-DEPENDENCY TOOLS =====
|
|
1010
|
+
{
|
|
1011
|
+
name: "goal_path",
|
|
1012
|
+
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.",
|
|
1013
|
+
inputSchema: {
|
|
1014
|
+
type: "object",
|
|
1015
|
+
properties: {
|
|
1016
|
+
goal_id: { type: "string", description: "Goal ID" },
|
|
1017
|
+
max_depth: { type: "number", description: "Max traversal depth (default 20)" }
|
|
1018
|
+
},
|
|
1019
|
+
required: ["goal_id"]
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
name: "goal_progress",
|
|
1024
|
+
description: "Get goal progress calculated from its dependency graph. Returns overall completion percentage and direct achiever progress.",
|
|
1025
|
+
inputSchema: {
|
|
1026
|
+
type: "object",
|
|
1027
|
+
properties: {
|
|
1028
|
+
goal_id: { type: "string", description: "Goal ID" }
|
|
1029
|
+
},
|
|
1030
|
+
required: ["goal_id"]
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
name: "add_achiever",
|
|
1035
|
+
description: "Link a task to a goal via an 'achieves' dependency edge. This declares that completing this task contributes to achieving the goal.",
|
|
1036
|
+
inputSchema: {
|
|
1037
|
+
type: "object",
|
|
1038
|
+
properties: {
|
|
1039
|
+
goal_id: { type: "string", description: "Goal ID" },
|
|
1040
|
+
node_id: { type: "string", description: "Task/node ID that achieves this goal" },
|
|
1041
|
+
weight: { type: "number", description: "Edge weight for critical path (default 1)" }
|
|
1042
|
+
},
|
|
1043
|
+
required: ["goal_id", "node_id"]
|
|
1044
|
+
}
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
name: "remove_achiever",
|
|
1048
|
+
description: "Remove an achieves edge between a task and a goal",
|
|
1049
|
+
inputSchema: {
|
|
1050
|
+
type: "object",
|
|
1051
|
+
properties: {
|
|
1052
|
+
goal_id: { type: "string", description: "Goal ID" },
|
|
1053
|
+
dependency_id: { type: "string", description: "Dependency edge ID to remove" }
|
|
1054
|
+
},
|
|
1055
|
+
required: ["goal_id", "dependency_id"]
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
name: "goal_knowledge_gaps",
|
|
1060
|
+
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.",
|
|
1061
|
+
inputSchema: {
|
|
1062
|
+
type: "object",
|
|
1063
|
+
properties: {
|
|
1064
|
+
goal_id: { type: "string", description: "Goal ID" }
|
|
1065
|
+
},
|
|
1066
|
+
required: ["goal_id"]
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
|
|
1070
|
+
// ===== GRAPHITI KNOWLEDGE GRAPH TOOLS =====
|
|
1071
|
+
{
|
|
1072
|
+
name: "add_learning",
|
|
1073
|
+
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.",
|
|
1074
|
+
inputSchema: {
|
|
1075
|
+
type: "object",
|
|
1076
|
+
properties: {
|
|
1077
|
+
content: { type: "string", description: "The knowledge content — be detailed. Include context, reasoning, and conclusions." },
|
|
1078
|
+
title: { type: "string", description: "Short title/name for the episode" },
|
|
1079
|
+
entry_type: { type: "string", enum: ["decision", "learning", "context", "constraint"], description: "Type of knowledge" },
|
|
1080
|
+
plan_id: { type: "string", description: "Plan ID this knowledge relates to (optional)" },
|
|
1081
|
+
node_id: { type: "string", description: "Node/task ID this knowledge relates to (optional)" }
|
|
1082
|
+
},
|
|
1083
|
+
required: ["content"]
|
|
1084
|
+
}
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
name: "recall_knowledge",
|
|
1088
|
+
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.",
|
|
1089
|
+
inputSchema: {
|
|
1090
|
+
type: "object",
|
|
1091
|
+
properties: {
|
|
1092
|
+
query: { type: "string", description: "What to search for — be specific" },
|
|
1093
|
+
max_results: { type: "number", description: "Maximum results (default 10)", default: 10 }
|
|
837
1094
|
},
|
|
838
1095
|
required: ["query"]
|
|
839
1096
|
}
|
|
840
1097
|
},
|
|
841
1098
|
{
|
|
842
|
-
name: "
|
|
843
|
-
description: "
|
|
1099
|
+
name: "find_entities",
|
|
1100
|
+
description: "Search for entities (technologies, people, patterns, constraints) in the knowledge graph. Returns entity nodes with their relationships.",
|
|
844
1101
|
inputSchema: {
|
|
845
1102
|
type: "object",
|
|
846
1103
|
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" } }
|
|
1104
|
+
query: { type: "string", description: "Entity search query" },
|
|
1105
|
+
max_results: { type: "number", description: "Maximum results (default 10)", default: 10 }
|
|
852
1106
|
},
|
|
853
|
-
required: ["
|
|
1107
|
+
required: ["query"]
|
|
854
1108
|
}
|
|
855
1109
|
},
|
|
1110
|
+
|
|
856
1111
|
{
|
|
857
|
-
name: "
|
|
858
|
-
description: "
|
|
1112
|
+
name: "check_contradictions",
|
|
1113
|
+
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
1114
|
inputSchema: {
|
|
860
1115
|
type: "object",
|
|
861
1116
|
properties: {
|
|
862
|
-
|
|
1117
|
+
query: { type: "string", description: "Topic to check for contradictions" },
|
|
1118
|
+
max_results: { type: "number", description: "Maximum results (default 10)", default: 10 }
|
|
863
1119
|
},
|
|
864
|
-
required: ["
|
|
1120
|
+
required: ["query"]
|
|
1121
|
+
}
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
name: "get_recent_episodes",
|
|
1125
|
+
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.",
|
|
1126
|
+
inputSchema: {
|
|
1127
|
+
type: "object",
|
|
1128
|
+
properties: {
|
|
1129
|
+
max_episodes: { type: "number", description: "Maximum episodes to return (default 20)", default: 20 }
|
|
1130
|
+
}
|
|
865
1131
|
}
|
|
866
1132
|
},
|
|
867
1133
|
|
|
@@ -967,7 +1233,7 @@ function setupTools(server) {
|
|
|
967
1233
|
success: true,
|
|
968
1234
|
message: `Plan "${title}" created with ${tasks.length} tasks`,
|
|
969
1235
|
plan_id: plan.id,
|
|
970
|
-
plan_url:
|
|
1236
|
+
plan_url: buildPlanUrl(plan.id),
|
|
971
1237
|
phase_id: phase.id,
|
|
972
1238
|
task_ids: createdTasks.map(t => t.id),
|
|
973
1239
|
tasks: createdTasks,
|
|
@@ -1029,7 +1295,7 @@ function setupTools(server) {
|
|
|
1029
1295
|
task_id: task.id,
|
|
1030
1296
|
plan_id: plan_id,
|
|
1031
1297
|
phase_id: targetPhaseId,
|
|
1032
|
-
task_url:
|
|
1298
|
+
task_url: buildTaskUrl(plan_id, task.id),
|
|
1033
1299
|
next_steps: [
|
|
1034
1300
|
"Use quick_status to mark as in_progress when you start",
|
|
1035
1301
|
"Use quick_log to document progress"
|
|
@@ -1132,12 +1398,8 @@ function setupTools(server) {
|
|
|
1132
1398
|
context.goal = await apiClient.goals.get(goal_id);
|
|
1133
1399
|
if (include_knowledge) {
|
|
1134
1400
|
try {
|
|
1135
|
-
const
|
|
1136
|
-
|
|
1137
|
-
scope_id: goal_id,
|
|
1138
|
-
limit: 10
|
|
1139
|
-
});
|
|
1140
|
-
context.goal_knowledge = knowledge.entries || [];
|
|
1401
|
+
const graphResult = await apiClient.graphiti.graphSearch({ query: context.goal?.title || '', max_results: 10 });
|
|
1402
|
+
context.goal_knowledge = graphResult?.results?.facts || [];
|
|
1141
1403
|
} catch (e) {}
|
|
1142
1404
|
}
|
|
1143
1405
|
} catch (e) {
|
|
@@ -1149,7 +1411,7 @@ function setupTools(server) {
|
|
|
1149
1411
|
if (plan_id) {
|
|
1150
1412
|
try {
|
|
1151
1413
|
context.plan = await apiClient.plans.getPlan(plan_id);
|
|
1152
|
-
context.plan_url =
|
|
1414
|
+
context.plan_url = buildPlanUrl(plan_id);
|
|
1153
1415
|
|
|
1154
1416
|
const nodes = await apiClient.nodes.getNodes(plan_id);
|
|
1155
1417
|
context.statistics = calculatePlanStatistics(nodes);
|
|
@@ -1178,12 +1440,8 @@ function setupTools(server) {
|
|
|
1178
1440
|
|
|
1179
1441
|
if (include_knowledge) {
|
|
1180
1442
|
try {
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
scope_id: plan_id,
|
|
1184
|
-
limit: 10
|
|
1185
|
-
});
|
|
1186
|
-
context.plan_knowledge = knowledge.entries || [];
|
|
1443
|
+
const graphResult = await apiClient.graphiti.graphSearch({ query: context.plan?.title || '', max_results: 10 });
|
|
1444
|
+
context.plan_knowledge = graphResult?.results?.facts || [];
|
|
1187
1445
|
} catch (e) {}
|
|
1188
1446
|
}
|
|
1189
1447
|
} catch (e) {
|
|
@@ -1260,34 +1518,8 @@ function setupTools(server) {
|
|
|
1260
1518
|
return formatResponse(tasks);
|
|
1261
1519
|
}
|
|
1262
1520
|
|
|
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
|
-
|
|
1521
|
+
// add_learning handled in GRAPHITI KNOWLEDGE GRAPH HANDLERS section below
|
|
1522
|
+
|
|
1291
1523
|
// ========================================
|
|
1292
1524
|
// MARKDOWN EXPORT
|
|
1293
1525
|
// ========================================
|
|
@@ -1475,7 +1707,7 @@ function setupTools(server) {
|
|
|
1475
1707
|
success: true,
|
|
1476
1708
|
message: `Plan "${planTitle}" created from markdown with ${phases.length} phases and ${createdTasks.length} tasks`,
|
|
1477
1709
|
plan_id: plan.id,
|
|
1478
|
-
plan_url:
|
|
1710
|
+
plan_url: buildPlanUrl(plan.id),
|
|
1479
1711
|
phases: createdPhases,
|
|
1480
1712
|
tasks: createdTasks,
|
|
1481
1713
|
next_steps: [
|
|
@@ -1600,7 +1832,7 @@ function setupTools(server) {
|
|
|
1600
1832
|
const result = await apiClient.plans.updateVisibility(plan_id, visibilityData);
|
|
1601
1833
|
|
|
1602
1834
|
const shareUrl = visibility === "public"
|
|
1603
|
-
?
|
|
1835
|
+
? buildPlanUrl(plan_id)
|
|
1604
1836
|
: null;
|
|
1605
1837
|
|
|
1606
1838
|
return formatResponse({
|
|
@@ -1641,27 +1873,29 @@ function setupTools(server) {
|
|
|
1641
1873
|
|
|
1642
1874
|
if (name === "move_node") {
|
|
1643
1875
|
const { plan_id, node_id, parent_id, order_index } = args;
|
|
1644
|
-
|
|
1876
|
+
|
|
1645
1877
|
try {
|
|
1878
|
+
// Build request body with only provided fields - don't send nulls
|
|
1879
|
+
const body = {};
|
|
1880
|
+
if (parent_id) body.parent_id = parent_id;
|
|
1881
|
+
if (order_index !== undefined) body.order_index = order_index;
|
|
1882
|
+
|
|
1646
1883
|
// Call the move endpoint - using POST as per API definition
|
|
1647
1884
|
const response = await apiClient.axiosInstance.post(
|
|
1648
1885
|
`/plans/${plan_id}/nodes/${node_id}/move`,
|
|
1649
|
-
|
|
1650
|
-
parent_id: parent_id || null,
|
|
1651
|
-
order_index: order_index !== undefined ? order_index : null
|
|
1652
|
-
}
|
|
1886
|
+
body
|
|
1653
1887
|
);
|
|
1654
|
-
|
|
1888
|
+
|
|
1655
1889
|
return formatResponse(response.data);
|
|
1656
1890
|
} catch (error) {
|
|
1657
1891
|
// If endpoint still doesn't work, try updating the node directly
|
|
1658
1892
|
if (error.response && error.response.status === 404) {
|
|
1659
1893
|
console.error('Move endpoint not found, trying direct update');
|
|
1660
1894
|
// Fallback to updating the node's parent_id via regular update
|
|
1661
|
-
const
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1895
|
+
const updateData = {};
|
|
1896
|
+
if (parent_id) updateData.parent_id = parent_id;
|
|
1897
|
+
if (order_index !== undefined) updateData.order_index = order_index;
|
|
1898
|
+
const updateResponse = await apiClient.nodes.updateNode(plan_id, node_id, updateData);
|
|
1665
1899
|
return formatResponse(updateResponse);
|
|
1666
1900
|
}
|
|
1667
1901
|
throw error;
|
|
@@ -1690,6 +1924,90 @@ function setupTools(server) {
|
|
|
1690
1924
|
return formatResponse(response.data);
|
|
1691
1925
|
}
|
|
1692
1926
|
|
|
1927
|
+
// ===== DEPENDENCIES =====
|
|
1928
|
+
if (name === "create_dependency") {
|
|
1929
|
+
const { plan_id, source_node_id, target_node_id, dependency_type, weight, metadata } = args;
|
|
1930
|
+
const response = await apiClient.axiosInstance.post(
|
|
1931
|
+
`/plans/${plan_id}/dependencies`,
|
|
1932
|
+
{ source_node_id, target_node_id, dependency_type, weight, metadata }
|
|
1933
|
+
);
|
|
1934
|
+
return formatResponse(response.data);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
if (name === "delete_dependency") {
|
|
1938
|
+
const { plan_id, dependency_id } = args;
|
|
1939
|
+
const response = await apiClient.axiosInstance.delete(
|
|
1940
|
+
`/plans/${plan_id}/dependencies/${dependency_id}`
|
|
1941
|
+
);
|
|
1942
|
+
return formatResponse(response.data);
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
if (name === "list_dependencies") {
|
|
1946
|
+
const { plan_id } = args;
|
|
1947
|
+
const response = await apiClient.axiosInstance.get(
|
|
1948
|
+
`/plans/${plan_id}/dependencies`
|
|
1949
|
+
);
|
|
1950
|
+
return formatResponse(response.data);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
if (name === "get_node_dependencies") {
|
|
1954
|
+
const { plan_id, node_id, direction = 'both' } = args;
|
|
1955
|
+
const response = await apiClient.axiosInstance.get(
|
|
1956
|
+
`/plans/${plan_id}/nodes/${node_id}/dependencies`,
|
|
1957
|
+
{ params: { direction } }
|
|
1958
|
+
);
|
|
1959
|
+
return formatResponse(response.data);
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
// ===== RPI WORKFLOW =====
|
|
1963
|
+
if (name === "create_rpi_chain") {
|
|
1964
|
+
const { plan_id, title, description, parent_id } = args;
|
|
1965
|
+
const response = await apiClient.axiosInstance.post(
|
|
1966
|
+
`/plans/${plan_id}/nodes/rpi-chain`,
|
|
1967
|
+
{ title, description, parent_id }
|
|
1968
|
+
);
|
|
1969
|
+
return formatResponse(response.data);
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
// ===== ANALYSIS =====
|
|
1973
|
+
if (name === "analyze_impact") {
|
|
1974
|
+
const { plan_id, node_id, scenario = 'block' } = args;
|
|
1975
|
+
const response = await apiClient.axiosInstance.get(
|
|
1976
|
+
`/plans/${plan_id}/nodes/${node_id}/impact`,
|
|
1977
|
+
{ params: { scenario } }
|
|
1978
|
+
);
|
|
1979
|
+
return formatResponse(response.data);
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
if (name === "get_critical_path") {
|
|
1983
|
+
const { plan_id } = args;
|
|
1984
|
+
const response = await apiClient.axiosInstance.get(
|
|
1985
|
+
`/plans/${plan_id}/critical-path`
|
|
1986
|
+
);
|
|
1987
|
+
return formatResponse(response.data);
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// ===== PROGRESSIVE CONTEXT =====
|
|
1991
|
+
if (name === "get_task_context") {
|
|
1992
|
+
const { node_id, depth = 2, token_budget = 0, log_limit = 10, include_research = true } = args;
|
|
1993
|
+
const params = new URLSearchParams({
|
|
1994
|
+
node_id,
|
|
1995
|
+
depth: String(depth),
|
|
1996
|
+
token_budget: String(token_budget),
|
|
1997
|
+
log_limit: String(log_limit),
|
|
1998
|
+
include_research: String(include_research),
|
|
1999
|
+
});
|
|
2000
|
+
const response = await apiClient.axiosInstance.get(`/context/progressive?${params.toString()}`);
|
|
2001
|
+
return formatResponse(response.data);
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
if (name === "suggest_next_tasks") {
|
|
2005
|
+
const { plan_id, limit = 5 } = args;
|
|
2006
|
+
const params = new URLSearchParams({ plan_id, limit: String(limit) });
|
|
2007
|
+
const response = await apiClient.axiosInstance.get(`/context/suggest?${params.toString()}`);
|
|
2008
|
+
return formatResponse(response.data);
|
|
2009
|
+
}
|
|
2010
|
+
|
|
1693
2011
|
// ===== LOGGING =====
|
|
1694
2012
|
if (name === "add_log") {
|
|
1695
2013
|
const { plan_id, node_id, content, log_type = "comment", tags } = args;
|
|
@@ -1901,60 +2219,154 @@ function setupTools(server) {
|
|
|
1901
2219
|
});
|
|
1902
2220
|
}
|
|
1903
2221
|
|
|
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
|
|
2222
|
+
// ===== CROSS-PLAN & EXTERNAL DEPENDENCY HANDLERS =====
|
|
2223
|
+
if (name === "create_cross_plan_dependency") {
|
|
2224
|
+
const { source_node_id, target_node_id, dependency_type, weight } = args;
|
|
2225
|
+
const result = await apiClient.dependencies.createCrossPlan({
|
|
2226
|
+
source_node_id, target_node_id, dependency_type, weight
|
|
1915
2227
|
});
|
|
1916
2228
|
return formatResponse(result);
|
|
1917
2229
|
}
|
|
1918
|
-
|
|
1919
|
-
if (name === "list_knowledge_entries") {
|
|
1920
|
-
const { scope, scope_id, entry_type, tags, limit = 50 } = args;
|
|
1921
2230
|
|
|
1922
|
-
|
|
2231
|
+
if (name === "list_cross_plan_dependencies") {
|
|
2232
|
+
const { plan_ids } = args;
|
|
2233
|
+
const result = await apiClient.dependencies.listCrossPlan(plan_ids);
|
|
1923
2234
|
return formatResponse(result);
|
|
1924
2235
|
}
|
|
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);
|
|
2236
|
+
|
|
2237
|
+
if (name === "create_external_dependency") {
|
|
2238
|
+
const { plan_id, title, description, url, blocks_node_id } = args;
|
|
2239
|
+
const result = await apiClient.dependencies.createExternal({
|
|
2240
|
+
plan_id, title, description, url, blocks_node_id
|
|
2241
|
+
});
|
|
1940
2242
|
return formatResponse(result);
|
|
1941
2243
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
const
|
|
2244
|
+
|
|
2245
|
+
// ===== GOAL-DEPENDENCY HANDLERS =====
|
|
2246
|
+
if (name === "goal_path") {
|
|
2247
|
+
const { goal_id, max_depth } = args;
|
|
2248
|
+
const result = await apiClient.goals.getPath(goal_id, max_depth);
|
|
1946
2249
|
return formatResponse(result);
|
|
1947
2250
|
}
|
|
1948
|
-
|
|
1949
|
-
if (name === "
|
|
1950
|
-
const {
|
|
1951
|
-
await apiClient.
|
|
2251
|
+
|
|
2252
|
+
if (name === "goal_progress") {
|
|
2253
|
+
const { goal_id } = args;
|
|
2254
|
+
const result = await apiClient.goals.getProgress(goal_id);
|
|
2255
|
+
return formatResponse(result);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
if (name === "add_achiever") {
|
|
2259
|
+
const { goal_id, node_id, weight } = args;
|
|
2260
|
+
const result = await apiClient.goals.addAchiever(goal_id, node_id, weight);
|
|
1952
2261
|
return formatResponse({
|
|
1953
|
-
|
|
1954
|
-
message: `
|
|
2262
|
+
...result,
|
|
2263
|
+
message: `Task ${node_id} now achieves goal ${goal_id}`,
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
if (name === "remove_achiever") {
|
|
2268
|
+
const { goal_id, dependency_id } = args;
|
|
2269
|
+
const result = await apiClient.goals.removeAchiever(goal_id, dependency_id);
|
|
2270
|
+
return formatResponse(result);
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
if (name === "goal_knowledge_gaps") {
|
|
2274
|
+
const { goal_id } = args;
|
|
2275
|
+
const result = await apiClient.goals.getKnowledgeGaps(goal_id);
|
|
2276
|
+
return formatResponse(result);
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// ===== GRAPHITI KNOWLEDGE GRAPH HANDLERS =====
|
|
2280
|
+
if (name === "add_learning") {
|
|
2281
|
+
const { content, title, entry_type, plan_id, node_id } = args;
|
|
2282
|
+
|
|
2283
|
+
// Add to Graphiti temporal knowledge graph
|
|
2284
|
+
const result = await apiClient.graphiti.addEpisode({
|
|
2285
|
+
content,
|
|
2286
|
+
name: title,
|
|
2287
|
+
plan_id,
|
|
2288
|
+
node_id,
|
|
2289
|
+
metadata: { entry_type: entry_type || 'learning' },
|
|
2290
|
+
});
|
|
2291
|
+
return formatResponse({
|
|
2292
|
+
...result,
|
|
2293
|
+
message: 'Knowledge recorded in temporal graph',
|
|
2294
|
+
tip: 'This is now searchable via recall_knowledge across all plans'
|
|
1955
2295
|
});
|
|
1956
2296
|
}
|
|
1957
2297
|
|
|
2298
|
+
if (name === "recall_knowledge") {
|
|
2299
|
+
const { query, max_results = 10 } = args;
|
|
2300
|
+
|
|
2301
|
+
// Try Graphiti first (temporal, cross-plan)
|
|
2302
|
+
try {
|
|
2303
|
+
const graphResult = await apiClient.graphiti.graphSearch({ query, max_results });
|
|
2304
|
+
if (graphResult?.results) {
|
|
2305
|
+
return formatResponse({
|
|
2306
|
+
...graphResult,
|
|
2307
|
+
source: 'graphiti_temporal_graph'
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
2310
|
+
} catch (err) {
|
|
2311
|
+
return formatResponse({
|
|
2312
|
+
results: [],
|
|
2313
|
+
source: 'graphiti_temporal_graph',
|
|
2314
|
+
error: 'Knowledge graph not available: ' + err.message,
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
if (name === "find_entities") {
|
|
2320
|
+
const { query, max_results = 10 } = args;
|
|
2321
|
+
|
|
2322
|
+
try {
|
|
2323
|
+
const result = await apiClient.graphiti.searchEntities({ query, max_results });
|
|
2324
|
+
return formatResponse(result);
|
|
2325
|
+
} catch (err) {
|
|
2326
|
+
return formatResponse({
|
|
2327
|
+
error: 'Entity search requires the temporal knowledge graph (Graphiti)',
|
|
2328
|
+
detail: err.message
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
if (name === "check_contradictions") {
|
|
2334
|
+
const { query, max_results = 10 } = args;
|
|
2335
|
+
|
|
2336
|
+
try {
|
|
2337
|
+
const result = await apiClient.graphiti.detectContradictions({ query, max_results });
|
|
2338
|
+
if (result.contradictions_found) {
|
|
2339
|
+
return formatResponse({
|
|
2340
|
+
...result,
|
|
2341
|
+
warning: 'Some knowledge has been superseded. Review the "superseded" facts before proceeding.',
|
|
2342
|
+
});
|
|
2343
|
+
}
|
|
2344
|
+
return formatResponse({
|
|
2345
|
+
...result,
|
|
2346
|
+
message: 'No contradictions found — all facts are current.',
|
|
2347
|
+
});
|
|
2348
|
+
} catch (err) {
|
|
2349
|
+
return formatResponse({
|
|
2350
|
+
error: 'Contradiction detection requires the temporal knowledge graph (Graphiti)',
|
|
2351
|
+
detail: err.message,
|
|
2352
|
+
});
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
if (name === "get_recent_episodes") {
|
|
2357
|
+
const { max_episodes = 20 } = args || {};
|
|
2358
|
+
|
|
2359
|
+
try {
|
|
2360
|
+
const result = await apiClient.graphiti.getEpisodes({ max_episodes });
|
|
2361
|
+
return formatResponse(result);
|
|
2362
|
+
} catch (err) {
|
|
2363
|
+
return formatResponse({
|
|
2364
|
+
error: 'Episodic memory requires the temporal knowledge graph (Graphiti)',
|
|
2365
|
+
detail: err.message
|
|
2366
|
+
});
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
|
|
1958
2370
|
// ===== HELPER TOOLS =====
|
|
1959
2371
|
if (name === "get_started") {
|
|
1960
2372
|
const { topic = "overview" } = args || {};
|
|
@@ -1975,8 +2387,8 @@ function setupTools(server) {
|
|
|
1975
2387
|
"2. Use list_plans to see existing plans",
|
|
1976
2388
|
"3. Before working on a plan, use understand_context to get the full picture",
|
|
1977
2389
|
"4. Update task statuses as you work (update_node with status)",
|
|
1978
|
-
"5. Store important decisions and learnings using
|
|
1979
|
-
"6. Check
|
|
2390
|
+
"5. Store important decisions and learnings using add_learning",
|
|
2391
|
+
"6. Check recall_knowledge before making decisions to see past context"
|
|
1980
2392
|
],
|
|
1981
2393
|
quick_tips: [
|
|
1982
2394
|
"Always capture WHY decisions were made, not just WHAT",
|
|
@@ -2008,7 +2420,7 @@ function setupTools(server) {
|
|
|
2008
2420
|
workflow: [
|
|
2009
2421
|
"1. Use get_plan_structure to see the full plan",
|
|
2010
2422
|
"2. Find tasks with status 'not_started' or 'in_progress'",
|
|
2011
|
-
"3. Before starting a task, check
|
|
2423
|
+
"3. Before starting a task, check recall_knowledge for relevant context",
|
|
2012
2424
|
"4. Update task status to 'in_progress' when you begin",
|
|
2013
2425
|
"5. Add logs to document what you're doing",
|
|
2014
2426
|
"6. Mark 'completed' when done, or 'blocked' if stuck"
|
|
@@ -2025,7 +2437,7 @@ function setupTools(server) {
|
|
|
2025
2437
|
"When blocked, clearly document what's blocking you",
|
|
2026
2438
|
"Store learnings as you go - don't wait until the end"
|
|
2027
2439
|
],
|
|
2028
|
-
tools_to_use: ["get_plan_structure", "update_node", "add_log", "
|
|
2440
|
+
tools_to_use: ["get_plan_structure", "update_node", "add_log", "recall_knowledge"]
|
|
2029
2441
|
},
|
|
2030
2442
|
knowledge: {
|
|
2031
2443
|
title: "Knowledge Management",
|
|
@@ -2051,7 +2463,7 @@ function setupTools(server) {
|
|
|
2051
2463
|
"When you discover a constraint or rule",
|
|
2052
2464
|
"When you find a useful resource or reference"
|
|
2053
2465
|
],
|
|
2054
|
-
tools_to_use: ["
|
|
2466
|
+
tools_to_use: ["add_learning", "recall_knowledge", "find_entities", "check_contradictions"]
|
|
2055
2467
|
},
|
|
2056
2468
|
collaboration: {
|
|
2057
2469
|
title: "Collaboration",
|
|
@@ -2095,12 +2507,8 @@ function setupTools(server) {
|
|
|
2095
2507
|
// Get related knowledge if available
|
|
2096
2508
|
if (include_knowledge) {
|
|
2097
2509
|
try {
|
|
2098
|
-
const
|
|
2099
|
-
|
|
2100
|
-
scope_id: goal_id,
|
|
2101
|
-
limit: 10
|
|
2102
|
-
});
|
|
2103
|
-
context.goal_knowledge = knowledge.entries || [];
|
|
2510
|
+
const graphResult = await apiClient.graphiti.graphSearch({ query: context.goal?.title || '', max_results: 10 });
|
|
2511
|
+
context.goal_knowledge = graphResult?.results?.facts || [];
|
|
2104
2512
|
} catch (e) {
|
|
2105
2513
|
// Knowledge fetch failed, continue without it
|
|
2106
2514
|
}
|
|
@@ -2147,12 +2555,8 @@ function setupTools(server) {
|
|
|
2147
2555
|
// Get related knowledge if available
|
|
2148
2556
|
if (include_knowledge) {
|
|
2149
2557
|
try {
|
|
2150
|
-
const
|
|
2151
|
-
|
|
2152
|
-
scope_id: plan_id,
|
|
2153
|
-
limit: 10
|
|
2154
|
-
});
|
|
2155
|
-
context.plan_knowledge = knowledge.entries || [];
|
|
2558
|
+
const graphResult = await apiClient.graphiti.graphSearch({ query: context.plan?.title || '', max_results: 10 });
|
|
2559
|
+
context.plan_knowledge = graphResult?.results?.facts || [];
|
|
2156
2560
|
} catch (e) {
|
|
2157
2561
|
// Knowledge fetch failed, continue without it
|
|
2158
2562
|
}
|
|
@@ -2166,6 +2570,45 @@ function setupTools(server) {
|
|
|
2166
2570
|
return formatResponse(context);
|
|
2167
2571
|
}
|
|
2168
2572
|
|
|
2573
|
+
// ===== GOALS HEALTH DASHBOARD =====
|
|
2574
|
+
if (name === "check_goals_health") {
|
|
2575
|
+
const { status_filter } = args || {};
|
|
2576
|
+
const result = await apiClient.goals.getDashboard();
|
|
2577
|
+
|
|
2578
|
+
let goals = result.goals || result;
|
|
2579
|
+
if (status_filter && Array.isArray(goals)) {
|
|
2580
|
+
goals = goals.filter(g => g.health_status === status_filter || g.status === status_filter);
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
return formatResponse({
|
|
2584
|
+
...result,
|
|
2585
|
+
goals,
|
|
2586
|
+
tip: "Prioritize: stale goals first, then at_risk, then on_track."
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
// ===== TASK CLAIMING =====
|
|
2591
|
+
if (name === "claim_task") {
|
|
2592
|
+
const { task_id, plan_id, ttl_minutes = 30 } = args;
|
|
2593
|
+
const result = await apiClient.nodes.claimTask(plan_id, task_id, 'mcp-agent', ttl_minutes);
|
|
2594
|
+
return formatResponse({
|
|
2595
|
+
success: true,
|
|
2596
|
+
message: `Task ${task_id} claimed for ${ttl_minutes} minutes`,
|
|
2597
|
+
...result,
|
|
2598
|
+
tip: "Remember to release the task when done, or it will auto-expire."
|
|
2599
|
+
});
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
if (name === "release_task") {
|
|
2603
|
+
const { task_id, plan_id } = args;
|
|
2604
|
+
const result = await apiClient.nodes.releaseTask(plan_id, task_id, 'mcp-agent');
|
|
2605
|
+
return formatResponse({
|
|
2606
|
+
success: true,
|
|
2607
|
+
message: `Task ${task_id} released`,
|
|
2608
|
+
...result
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2169
2612
|
// Tool not found
|
|
2170
2613
|
throw new Error(`Unknown tool: ${name}`);
|
|
2171
2614
|
} catch (error) {
|