@episoda/mcp 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -278,7 +278,7 @@ ${task.description_md || "_No description_"}`
278
278
  }
279
279
  },
280
280
  async (args) => {
281
- const result = await apiRequest("GET", `/api/modules/${args.module_uid}/tasks`);
281
+ const result = await apiRequest("GET", `/api/tasks?module_id=${args.module_uid}`);
282
282
  if (!result.success || !result.tasks) {
283
283
  return { content: [{ type: "text", text: `Error: ${result.error || "Failed to fetch tasks"}` }], isError: true };
284
284
  }
@@ -300,6 +300,27 @@ ${taskList}`
300
300
  };
301
301
  }
302
302
  );
303
+ server.registerTool(
304
+ "delete_task",
305
+ {
306
+ description: "Delete a task by UID",
307
+ inputSchema: {
308
+ task_uid: z.string().describe("The UID of the task (e.g., EP584-1)")
309
+ }
310
+ },
311
+ async (args) => {
312
+ const result = await apiRequest("DELETE", `/api/tasks/${args.task_uid}`);
313
+ if (!result.success) {
314
+ return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
315
+ }
316
+ return {
317
+ content: [{
318
+ type: "text",
319
+ text: `Task ${args.task_uid} deleted`
320
+ }]
321
+ };
322
+ }
323
+ );
303
324
  server.registerTool(
304
325
  "batch_update_task_state",
305
326
  {
@@ -629,6 +650,123 @@ ${mod.description_md || "_No description_"}`
629
650
  };
630
651
  }
631
652
  );
653
+ server.registerTool(
654
+ "delete_module",
655
+ {
656
+ description: "Delete a module by UID",
657
+ inputSchema: {
658
+ module_uid: z.string().describe("The UID of the module (e.g., EP584)")
659
+ }
660
+ },
661
+ async (args) => {
662
+ const result = await apiRequest("DELETE", `/api/modules/${args.module_uid}`);
663
+ if (!result.success) {
664
+ return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
665
+ }
666
+ return {
667
+ content: [{
668
+ type: "text",
669
+ text: `Module ${args.module_uid} deleted`
670
+ }]
671
+ };
672
+ }
673
+ );
674
+ server.registerTool(
675
+ "list_modules",
676
+ {
677
+ description: `List modules in the project with optional filtering.
678
+
679
+ USE THIS WHEN:
680
+ - Discovering modules without knowing UIDs
681
+ - Checking what's currently in progress
682
+ - Finding modules by state`,
683
+ inputSchema: {
684
+ state: z.enum(["backlog", "ready", "doing", "review", "done"]).optional().describe("Filter by module state"),
685
+ limit: z.number().optional().describe("Maximum results (default: 25)"),
686
+ include_task_counts: z.boolean().optional().describe("Include task counts per module")
687
+ }
688
+ },
689
+ async (args) => {
690
+ const params = new URLSearchParams();
691
+ if (args.state) params.set("state", args.state);
692
+ if (args.limit) params.set("limit", String(args.limit));
693
+ if (args.include_task_counts) params.set("includeTaskCounts", "true");
694
+ const query = params.toString();
695
+ const result = await apiRequest("GET", `/api/modules${query ? `?${query}` : ""}`);
696
+ if (!result.success || !result.modules) {
697
+ return { content: [{ type: "text", text: `Error: ${result.error || "Failed to fetch modules"}` }], isError: true };
698
+ }
699
+ if (result.modules.length === 0) {
700
+ return { content: [{ type: "text", text: `No modules found${args.state ? ` in state "${args.state}"` : ""}.` }] };
701
+ }
702
+ const moduleList = result.modules.map((m) => {
703
+ const taskInfo = m.task_counts ? ` [${m.task_counts.done}/${m.task_counts.total} tasks]` : "";
704
+ return `- **${m.uid}**: ${m.title} (${m.state})${taskInfo}`;
705
+ }).join("\n");
706
+ return {
707
+ content: [{
708
+ type: "text",
709
+ text: `# Modules${args.state ? ` (${args.state})` : ""}
710
+
711
+ ${moduleList}`
712
+ }]
713
+ };
714
+ }
715
+ );
716
+ server.registerTool(
717
+ "transition_module",
718
+ {
719
+ description: `Transition a module to a new state using the orchestrated transition API.
720
+
721
+ This triggers the full transition flow (e.g., doing\u2192review creates PR, review\u2192done triggers merge).
722
+ Use this instead of request_review or mark_done for full control.`,
723
+ inputSchema: {
724
+ module_uid: z.string().describe("The UID of the module (e.g., EP584)"),
725
+ target_state: z.enum(["ready", "doing", "review", "done"]).describe("Target state for the module")
726
+ }
727
+ },
728
+ async (args) => {
729
+ const result = await apiRequest(
730
+ "POST",
731
+ `/api/modules/${args.module_uid}/transition`,
732
+ { target_state: args.target_state }
733
+ );
734
+ if (!result.success) {
735
+ return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
736
+ }
737
+ return {
738
+ content: [{
739
+ type: "text",
740
+ text: `Module ${args.module_uid} transitioned to ${args.target_state}`
741
+ }]
742
+ };
743
+ }
744
+ );
745
+ server.registerTool(
746
+ "archive_module",
747
+ {
748
+ description: "Archive a module instead of deleting it",
749
+ inputSchema: {
750
+ module_uid: z.string().describe("The UID of the module (e.g., EP584)")
751
+ }
752
+ },
753
+ async (args) => {
754
+ const result = await apiRequest(
755
+ "POST",
756
+ `/api/modules/${args.module_uid}/archive`,
757
+ {}
758
+ );
759
+ if (!result.success) {
760
+ return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
761
+ }
762
+ return {
763
+ content: [{
764
+ type: "text",
765
+ text: `Module ${args.module_uid} archived`
766
+ }]
767
+ };
768
+ }
769
+ );
632
770
  server.registerTool(
633
771
  "get_knowledge",
634
772
  {
@@ -856,6 +994,62 @@ Both semantic and text search returned no results.` }] };
856
994
  Query: "${args.query}"
857
995
  Found: ${ranked.length} documents (${bothCount} matched both semantic + text)
858
996
 
997
+ ${list}`
998
+ }]
999
+ };
1000
+ }
1001
+ );
1002
+ server.registerTool(
1003
+ "search_conversations",
1004
+ {
1005
+ description: `Search past agent conversations in this project.
1006
+
1007
+ This searches agent conversation history (agent_messages) across sessions you can access.
1008
+
1009
+ USE THIS WHEN:
1010
+ - The user references a prior discussion
1011
+ - You need to recover context after compaction
1012
+ - You want to find when/where something was decided`,
1013
+ inputSchema: {
1014
+ query: z.string().describe("Semantic search query"),
1015
+ limit: z.number().optional().describe("Max results (default: 10)"),
1016
+ threshold: z.number().optional().describe("Min similarity 0-1 (default: 0.5)"),
1017
+ days_back: z.number().optional().describe("Limit to recent N days (optional)"),
1018
+ session_id: z.string().optional().describe("Limit to a specific session UUID (optional)")
1019
+ }
1020
+ },
1021
+ async (args) => {
1022
+ const result = await apiRequest(
1023
+ "POST",
1024
+ "/api/search/conversations",
1025
+ {
1026
+ query: args.query,
1027
+ limit: args.limit || 10,
1028
+ threshold: args.threshold || 0.5,
1029
+ ...args.days_back !== void 0 ? { days_back: args.days_back } : {},
1030
+ ...args.session_id ? { session_id: args.session_id } : {}
1031
+ }
1032
+ );
1033
+ if (!result.success || !result.results) {
1034
+ return { content: [{ type: "text", text: `Error: ${result.error || "Conversation search failed"}` }], isError: true };
1035
+ }
1036
+ if (result.results.length === 0) {
1037
+ return { content: [{ type: "text", text: `No conversation messages found matching: "${args.query}"` }] };
1038
+ }
1039
+ const list = result.results.map((r) => {
1040
+ const score = Math.round((r.similarity || 0) * 100);
1041
+ const moduleInfo = r.module_uid ? ` | ${r.module_uid}${r.module_title ? `: ${r.module_title}` : ""}` : "";
1042
+ const when = r.created_at ? ` | ${r.created_at}` : "";
1043
+ return `- [${r.role}, ${score}%] ${r.content_snippet}${moduleInfo}${when}`;
1044
+ }).join("\n");
1045
+ return {
1046
+ content: [{
1047
+ type: "text",
1048
+ text: `# Conversation Search Results
1049
+
1050
+ Query: "${args.query}"
1051
+ Found: ${result.results.length}
1052
+
859
1053
  ${list}`
860
1054
  }]
861
1055
  };
@@ -917,6 +1111,98 @@ ${list}`
917
1111
  };
918
1112
  }
919
1113
  );
1114
+ server.registerTool(
1115
+ "create_knowledge",
1116
+ {
1117
+ description: `Create a new knowledge document in the knowledge base.
1118
+
1119
+ USE THIS WHEN:
1120
+ - Documenting technical decisions or architecture
1121
+ - Capturing research findings or discoveries
1122
+ - Creating process directives or guidelines
1123
+
1124
+ All documentation MUST be created in the knowledge base, not as markdown files.`,
1125
+ inputSchema: {
1126
+ title: z.string().describe("Title of the knowledge document"),
1127
+ domain: z.enum(["tech", "business", "process", "people"]).describe("Knowledge domain"),
1128
+ doc_type: z.enum(["directive", "discovery", "documentation"]).describe("Document type"),
1129
+ significance: z.number().describe("Significance score from 0.0 to 1.0"),
1130
+ markdown: z.string().describe("The document content in markdown")
1131
+ }
1132
+ },
1133
+ async (args) => {
1134
+ const result = await apiRequest("POST", "/api/knowledge", {
1135
+ title: args.title,
1136
+ domain: args.domain,
1137
+ doc_type: args.doc_type,
1138
+ significance: args.significance,
1139
+ markdown: args.markdown
1140
+ });
1141
+ if (!result.success) {
1142
+ return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
1143
+ }
1144
+ const k = result.knowledge;
1145
+ return {
1146
+ content: [{
1147
+ type: "text",
1148
+ text: `Created knowledge document${k?.uid ? ` ${k.uid}` : ""}: ${k?.title} (id: ${k?.id})`
1149
+ }]
1150
+ };
1151
+ }
1152
+ );
1153
+ server.registerTool(
1154
+ "update_knowledge",
1155
+ {
1156
+ description: "Update an existing knowledge document",
1157
+ inputSchema: {
1158
+ knowledge_id: z.string().describe("The ID of the knowledge document"),
1159
+ title: z.string().optional().describe("New title"),
1160
+ markdown: z.string().optional().describe("New markdown content"),
1161
+ domain: z.enum(["tech", "business", "process", "people"]).optional().describe("New domain"),
1162
+ doc_type: z.enum(["directive", "discovery", "documentation"]).optional().describe("New document type"),
1163
+ significance: z.number().optional().describe("New significance score (0.0-1.0)")
1164
+ }
1165
+ },
1166
+ async (args) => {
1167
+ const updates = {};
1168
+ if (args.title) updates.title = args.title;
1169
+ if (args.markdown) updates.markdown = args.markdown;
1170
+ if (args.domain) updates.domain = args.domain;
1171
+ if (args.doc_type) updates.doc_type = args.doc_type;
1172
+ if (args.significance !== void 0) updates.significance = args.significance;
1173
+ const result = await apiRequest("PATCH", `/api/knowledge/${args.knowledge_id}`, updates);
1174
+ if (!result.success) {
1175
+ return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
1176
+ }
1177
+ return {
1178
+ content: [{
1179
+ type: "text",
1180
+ text: `Knowledge document ${args.knowledge_id} updated`
1181
+ }]
1182
+ };
1183
+ }
1184
+ );
1185
+ server.registerTool(
1186
+ "delete_knowledge",
1187
+ {
1188
+ description: "Delete a knowledge document by ID",
1189
+ inputSchema: {
1190
+ knowledge_id: z.string().describe("The ID of the knowledge document")
1191
+ }
1192
+ },
1193
+ async (args) => {
1194
+ const result = await apiRequest("DELETE", `/api/knowledge/${args.knowledge_id}`);
1195
+ if (!result.success) {
1196
+ return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
1197
+ }
1198
+ return {
1199
+ content: [{
1200
+ type: "text",
1201
+ text: `Knowledge document ${args.knowledge_id} deleted`
1202
+ }]
1203
+ };
1204
+ }
1205
+ );
920
1206
  server.registerTool(
921
1207
  "get_module_context",
922
1208
  {