@aaronsb/kg-cli 0.6.6 → 0.7.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.
Files changed (96) hide show
  1. package/dist/api/client.d.ts +127 -1
  2. package/dist/api/client.d.ts.map +1 -1
  3. package/dist/api/client.js +147 -0
  4. package/dist/api/client.js.map +1 -1
  5. package/dist/cli/batch.d.ts +8 -0
  6. package/dist/cli/batch.d.ts.map +1 -0
  7. package/dist/cli/batch.js +225 -0
  8. package/dist/cli/batch.js.map +1 -0
  9. package/dist/cli/commands.d.ts.map +1 -1
  10. package/dist/cli/commands.js +8 -0
  11. package/dist/cli/commands.js.map +1 -1
  12. package/dist/cli/concept.d.ts +9 -0
  13. package/dist/cli/concept.d.ts.map +1 -0
  14. package/dist/cli/concept.js +336 -0
  15. package/dist/cli/concept.js.map +1 -0
  16. package/dist/cli/edge.d.ts +9 -0
  17. package/dist/cli/edge.d.ts.map +1 -0
  18. package/dist/cli/edge.js +364 -0
  19. package/dist/cli/edge.js.map +1 -0
  20. package/dist/cli/jobs.js +111 -3
  21. package/dist/cli/jobs.js.map +1 -1
  22. package/dist/cli/storage.d.ts +10 -0
  23. package/dist/cli/storage.d.ts.map +1 -0
  24. package/dist/cli/storage.js +260 -0
  25. package/dist/cli/storage.js.map +1 -0
  26. package/dist/cli/vocabulary/index.d.ts.map +1 -1
  27. package/dist/cli/vocabulary/index.js +1 -0
  28. package/dist/cli/vocabulary/index.js.map +1 -1
  29. package/dist/cli/vocabulary/similarity.d.ts +1 -0
  30. package/dist/cli/vocabulary/similarity.d.ts.map +1 -1
  31. package/dist/cli/vocabulary/similarity.js +60 -9
  32. package/dist/cli/vocabulary/similarity.js.map +1 -1
  33. package/dist/lib/interactive.d.ts +84 -0
  34. package/dist/lib/interactive.d.ts.map +1 -0
  35. package/dist/lib/interactive.js +296 -0
  36. package/dist/lib/interactive.js.map +1 -0
  37. package/dist/lib/validation.d.ts +106 -0
  38. package/dist/lib/validation.d.ts.map +1 -0
  39. package/dist/lib/validation.js +354 -0
  40. package/dist/lib/validation.js.map +1 -0
  41. package/dist/mcp/formatters/concept.d.ts +24 -0
  42. package/dist/mcp/formatters/concept.d.ts.map +1 -0
  43. package/dist/mcp/formatters/concept.js +243 -0
  44. package/dist/mcp/formatters/concept.js.map +1 -0
  45. package/dist/mcp/formatters/document.d.ts +25 -0
  46. package/dist/mcp/formatters/document.d.ts.map +1 -0
  47. package/dist/mcp/formatters/document.js +178 -0
  48. package/dist/mcp/formatters/document.js.map +1 -0
  49. package/dist/mcp/formatters/epistemic.d.ts +16 -0
  50. package/dist/mcp/formatters/epistemic.d.ts.map +1 -0
  51. package/dist/mcp/formatters/epistemic.js +269 -0
  52. package/dist/mcp/formatters/epistemic.js.map +1 -0
  53. package/dist/mcp/formatters/graph.d.ts +63 -0
  54. package/dist/mcp/formatters/graph.d.ts.map +1 -0
  55. package/dist/mcp/formatters/graph.js +220 -0
  56. package/dist/mcp/formatters/graph.js.map +1 -0
  57. package/dist/mcp/formatters/index.d.ts +18 -0
  58. package/dist/mcp/formatters/index.d.ts.map +1 -0
  59. package/dist/mcp/formatters/index.js +63 -0
  60. package/dist/mcp/formatters/index.js.map +1 -0
  61. package/dist/mcp/formatters/ingest.d.ts +16 -0
  62. package/dist/mcp/formatters/ingest.d.ts.map +1 -0
  63. package/dist/mcp/formatters/ingest.js +169 -0
  64. package/dist/mcp/formatters/ingest.js.map +1 -0
  65. package/dist/mcp/formatters/job.d.ts +13 -0
  66. package/dist/mcp/formatters/job.d.ts.map +1 -0
  67. package/dist/mcp/formatters/job.js +204 -0
  68. package/dist/mcp/formatters/job.js.map +1 -0
  69. package/dist/mcp/formatters/source.d.ts +19 -0
  70. package/dist/mcp/formatters/source.d.ts.map +1 -0
  71. package/dist/mcp/formatters/source.js +182 -0
  72. package/dist/mcp/formatters/source.js.map +1 -0
  73. package/dist/mcp/formatters/system.d.ts +10 -0
  74. package/dist/mcp/formatters/system.d.ts.map +1 -0
  75. package/dist/mcp/formatters/system.js +152 -0
  76. package/dist/mcp/formatters/system.js.map +1 -0
  77. package/dist/mcp/formatters/utils.d.ts +17 -0
  78. package/dist/mcp/formatters/utils.d.ts.map +1 -0
  79. package/dist/mcp/formatters/utils.js +55 -0
  80. package/dist/mcp/formatters/utils.js.map +1 -0
  81. package/dist/mcp/graph-operations.d.ts +242 -0
  82. package/dist/mcp/graph-operations.d.ts.map +1 -0
  83. package/dist/mcp/graph-operations.js +532 -0
  84. package/dist/mcp/graph-operations.js.map +1 -0
  85. package/dist/mcp-server.js +460 -32
  86. package/dist/mcp-server.js.map +1 -1
  87. package/dist/types/index.d.ts +153 -0
  88. package/dist/types/index.d.ts.map +1 -1
  89. package/dist/version.d.ts +3 -3
  90. package/dist/version.js +3 -3
  91. package/dist/version.js.map +1 -1
  92. package/package.json +1 -1
  93. package/dist/mcp/formatters.d.ts +0 -100
  94. package/dist/mcp/formatters.d.ts.map +0 -1
  95. package/dist/mcp/formatters.js +0 -1411
  96. package/dist/mcp/formatters.js.map +0 -1
@@ -49,7 +49,8 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
49
49
  const client_js_1 = require("./api/client.js");
50
50
  const auth_client_js_1 = require("./lib/auth/auth-client.js");
51
51
  const mcp_allowlist_js_1 = require("./lib/mcp-allowlist.js");
52
- const formatters_js_1 = require("./mcp/formatters.js");
52
+ const index_js_2 = require("./mcp/formatters/index.js");
53
+ const graph_operations_js_1 = require("./mcp/graph-operations.js");
53
54
  const fs = __importStar(require("fs"));
54
55
  const path = __importStar(require("path"));
55
56
  const package_json_1 = __importDefault(require("../package.json"));
@@ -407,28 +408,56 @@ PERFORMANCE CRITICAL: For "connect" action, use threshold >= 0.75 to avoid datab
407
408
  },
408
409
  {
409
410
  name: 'job',
410
- description: 'Manage ingestion jobs: get status, list jobs, approve, or cancel. Use action parameter to specify operation.',
411
+ description: 'Manage ingestion jobs: get status, list jobs, approve, cancel, delete, or cleanup. Use action parameter to specify operation.',
411
412
  inputSchema: {
412
413
  type: 'object',
413
414
  properties: {
414
415
  action: {
415
416
  type: 'string',
416
- enum: ['status', 'list', 'approve', 'cancel'],
417
- description: 'Operation: "status" (get job status), "list" (list jobs), "approve" (approve job), "cancel" (cancel job)',
417
+ enum: ['status', 'list', 'approve', 'cancel', 'delete', 'cleanup'],
418
+ description: 'Operation: "status" (get job status), "list" (list jobs), "approve" (approve job), "cancel" (cancel job), "delete" (permanently delete single job), "cleanup" (delete jobs matching filters)',
418
419
  },
419
420
  job_id: {
420
421
  type: 'string',
421
- description: 'Job ID (required for status, approve, cancel)',
422
+ description: 'Job ID (required for status, approve, cancel, delete)',
422
423
  },
423
424
  status: {
424
425
  type: 'string',
425
- description: 'Filter by status for list (pending, awaiting_approval, running, completed, failed)',
426
+ description: 'Filter by status for list/cleanup (pending, awaiting_approval, running, completed, failed)',
426
427
  },
427
428
  limit: {
428
429
  type: 'number',
429
430
  description: 'Max jobs to return for list (default: 50)',
430
431
  default: 50,
431
432
  },
433
+ force: {
434
+ type: 'boolean',
435
+ description: 'Force delete even if job is processing (for delete action)',
436
+ default: false,
437
+ },
438
+ system_only: {
439
+ type: 'boolean',
440
+ description: 'Only delete system/scheduled jobs (for cleanup action)',
441
+ default: false,
442
+ },
443
+ older_than: {
444
+ type: 'string',
445
+ description: 'Delete jobs older than duration: 1h, 24h, 7d, 30d (for cleanup action)',
446
+ },
447
+ job_type: {
448
+ type: 'string',
449
+ description: 'Filter by job type for cleanup (ingestion, epistemic_remeasurement, projection, etc)',
450
+ },
451
+ dry_run: {
452
+ type: 'boolean',
453
+ description: 'Preview what would be deleted without deleting (for cleanup, default: true)',
454
+ default: true,
455
+ },
456
+ confirm: {
457
+ type: 'boolean',
458
+ description: 'Confirm deletion - set to true to actually delete (for cleanup action)',
459
+ default: false,
460
+ },
432
461
  },
433
462
  required: ['action'],
434
463
  },
@@ -749,6 +778,193 @@ Use search tool with type="documents" to find documents semantically.`,
749
778
  required: ['action'],
750
779
  },
751
780
  },
781
+ // ADR-089 Phase 3a: Graph CRUD Tool
782
+ {
783
+ name: 'graph',
784
+ description: `Create, edit, delete, and list concepts and edges in the knowledge graph (ADR-089).
785
+
786
+ This tool provides deterministic graph editing without going through the LLM ingest pipeline.
787
+ Use for manual curation, agent-driven knowledge building, and precise graph manipulation.
788
+
789
+ **Actions:**
790
+ - "create": Create a new concept or edge
791
+ - "edit": Update an existing concept or edge
792
+ - "delete": Delete a concept or edge
793
+ - "list": List concepts or edges with filters
794
+
795
+ **Entity Types:**
796
+ - "concept": Knowledge graph concepts (nodes)
797
+ - "edge": Relationships between concepts
798
+
799
+ **Matching Modes (for create):**
800
+ - "auto": Link to existing if match found, create if not (default)
801
+ - "force_create": Always create new, even if similar exists
802
+ - "match_only": Only link to existing, error if no match
803
+
804
+ **Semantic Resolution:**
805
+ - Use \`from_label\`/\`to_label\` to reference concepts by name instead of ID
806
+ - Resolution uses vector similarity (85% threshold) to find matching concepts
807
+
808
+ **Examples:**
809
+ - Create concept: \`{action: "create", entity: "concept", label: "CAP Theorem", ontology: "distributed-systems"}\`
810
+ - Create edge: \`{action: "create", entity: "edge", from_label: "CAP Theorem", to_label: "Partition Tolerance", relationship_type: "REQUIRES"}\`
811
+ - List concepts: \`{action: "list", entity: "concept", ontology: "distributed-systems"}\`
812
+ - Delete concept: \`{action: "delete", entity: "concept", concept_id: "c_abc123"}\`
813
+
814
+ **Queue Mode** (batch multiple operations in one call):
815
+ \`\`\`json
816
+ {
817
+ "action": "queue",
818
+ "operations": [
819
+ {"op": "create", "entity": "concept", "label": "A", "ontology": "test"},
820
+ {"op": "create", "entity": "concept", "label": "B", "ontology": "test"},
821
+ {"op": "create", "entity": "edge", "from_label": "A", "to_label": "B", "relationship_type": "IMPLIES"}
822
+ ]
823
+ }
824
+ \`\`\`
825
+ Queue executes sequentially, stops on first error (unless continue_on_error=true). Max 20 operations.`,
826
+ inputSchema: {
827
+ type: 'object',
828
+ properties: {
829
+ action: {
830
+ type: 'string',
831
+ enum: ['create', 'edit', 'delete', 'list', 'queue'],
832
+ description: 'Operation to perform. Use "queue" to batch multiple operations.',
833
+ },
834
+ entity: {
835
+ type: 'string',
836
+ enum: ['concept', 'edge'],
837
+ description: 'Entity type (required for create/edit/delete/list, not for queue)',
838
+ },
839
+ // Queue fields
840
+ operations: {
841
+ type: 'array',
842
+ items: {
843
+ type: 'object',
844
+ properties: {
845
+ op: { type: 'string', enum: ['create', 'edit', 'delete', 'list'], description: 'Operation' },
846
+ entity: { type: 'string', enum: ['concept', 'edge'] },
847
+ label: { type: 'string' },
848
+ ontology: { type: 'string' },
849
+ description: { type: 'string' },
850
+ search_terms: { type: 'array', items: { type: 'string' } },
851
+ matching_mode: { type: 'string', enum: ['auto', 'force_create', 'match_only'] },
852
+ concept_id: { type: 'string' },
853
+ from_concept_id: { type: 'string' },
854
+ to_concept_id: { type: 'string' },
855
+ from_label: { type: 'string' },
856
+ to_label: { type: 'string' },
857
+ relationship_type: { type: 'string' },
858
+ category: { type: 'string' },
859
+ confidence: { type: 'number' },
860
+ limit: { type: 'number' },
861
+ offset: { type: 'number' },
862
+ cascade: { type: 'boolean' },
863
+ },
864
+ required: ['op', 'entity'],
865
+ },
866
+ description: 'Array of operations for queue action (max 20). Each has op, entity, and action-specific fields.',
867
+ },
868
+ continue_on_error: {
869
+ type: 'boolean',
870
+ description: 'For queue: continue executing after errors (default: false, stop on first error)',
871
+ default: false,
872
+ },
873
+ // Concept fields
874
+ label: {
875
+ type: 'string',
876
+ description: 'Concept label (required for create concept)',
877
+ },
878
+ ontology: {
879
+ type: 'string',
880
+ description: 'Ontology/namespace (required for create concept, optional filter for list)',
881
+ },
882
+ description: {
883
+ type: 'string',
884
+ description: 'Concept description (optional)',
885
+ },
886
+ search_terms: {
887
+ type: 'array',
888
+ items: { type: 'string' },
889
+ description: 'Alternative search terms for the concept',
890
+ },
891
+ matching_mode: {
892
+ type: 'string',
893
+ enum: ['auto', 'force_create', 'match_only'],
894
+ description: 'How to handle similar existing concepts (default: auto)',
895
+ default: 'auto',
896
+ },
897
+ // Edge fields
898
+ from_concept_id: {
899
+ type: 'string',
900
+ description: 'Source concept ID (for edge create/delete)',
901
+ },
902
+ to_concept_id: {
903
+ type: 'string',
904
+ description: 'Target concept ID (for edge create/delete)',
905
+ },
906
+ from_label: {
907
+ type: 'string',
908
+ description: 'Source concept by label (semantic resolution)',
909
+ },
910
+ to_label: {
911
+ type: 'string',
912
+ description: 'Target concept by label (semantic resolution)',
913
+ },
914
+ relationship_type: {
915
+ type: 'string',
916
+ description: 'Edge relationship type (e.g., IMPLIES, SUPPORTS, CONTRADICTS)',
917
+ },
918
+ category: {
919
+ type: 'string',
920
+ enum: ['logical_truth', 'empirical_claim', 'value_judgment', 'conceptual', 'temporal_sequence', 'causal', 'other'],
921
+ description: 'Semantic category of the relationship (default: logical_truth)',
922
+ default: 'logical_truth',
923
+ },
924
+ confidence: {
925
+ type: 'number',
926
+ description: 'Edge confidence 0.0-1.0 (default: 1.0)',
927
+ default: 1.0,
928
+ },
929
+ // Identifiers for edit/delete
930
+ concept_id: {
931
+ type: 'string',
932
+ description: 'Concept ID (for edit/delete concept)',
933
+ },
934
+ // List filters
935
+ label_contains: {
936
+ type: 'string',
937
+ description: 'Filter concepts by label substring (for list)',
938
+ },
939
+ creation_method: {
940
+ type: 'string',
941
+ description: 'Filter by creation method (for list)',
942
+ },
943
+ source: {
944
+ type: 'string',
945
+ description: 'Filter edges by source (for list)',
946
+ },
947
+ // Pagination
948
+ limit: {
949
+ type: 'number',
950
+ description: 'Max results to return (default: 20)',
951
+ default: 20,
952
+ },
953
+ offset: {
954
+ type: 'number',
955
+ description: 'Number to skip for pagination (default: 0)',
956
+ default: 0,
957
+ },
958
+ // Delete option
959
+ cascade: {
960
+ type: 'boolean',
961
+ description: 'For concept delete: also delete orphaned synthetic sources (default: false)',
962
+ default: false,
963
+ },
964
+ },
965
+ required: ['action'],
966
+ },
967
+ },
752
968
  ],
753
969
  };
754
970
  });
@@ -806,7 +1022,7 @@ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) =
806
1022
  contents: [{
807
1023
  uri,
808
1024
  mimeType: 'text/plain',
809
- text: (0, formatters_js_1.formatDatabaseStats)(result)
1025
+ text: (0, index_js_2.formatDatabaseStats)(result)
810
1026
  }]
811
1027
  };
812
1028
  }
@@ -816,7 +1032,7 @@ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) =
816
1032
  contents: [{
817
1033
  uri,
818
1034
  mimeType: 'text/plain',
819
- text: (0, formatters_js_1.formatDatabaseInfo)(result)
1035
+ text: (0, index_js_2.formatDatabaseInfo)(result)
820
1036
  }]
821
1037
  };
822
1038
  }
@@ -826,7 +1042,7 @@ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) =
826
1042
  contents: [{
827
1043
  uri,
828
1044
  mimeType: 'text/plain',
829
- text: (0, formatters_js_1.formatDatabaseHealth)(result)
1045
+ text: (0, index_js_2.formatDatabaseHealth)(result)
830
1046
  }]
831
1047
  };
832
1048
  }
@@ -836,7 +1052,7 @@ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) =
836
1052
  contents: [{
837
1053
  uri,
838
1054
  mimeType: 'text/plain',
839
- text: (0, formatters_js_1.formatSystemStatus)(result)
1055
+ text: (0, index_js_2.formatSystemStatus)(result)
840
1056
  }]
841
1057
  };
842
1058
  }
@@ -846,7 +1062,7 @@ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) =
846
1062
  contents: [{
847
1063
  uri,
848
1064
  mimeType: 'text/plain',
849
- text: (0, formatters_js_1.formatApiHealth)(result)
1065
+ text: (0, index_js_2.formatApiHealth)(result)
850
1066
  }]
851
1067
  };
852
1068
  }
@@ -858,7 +1074,7 @@ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) =
858
1074
  contents: [{
859
1075
  uri,
860
1076
  mimeType: 'text/plain',
861
- text: (0, formatters_js_1.formatMcpAllowedPaths)({
1077
+ text: (0, index_js_2.formatMcpAllowedPaths)({
862
1078
  configured: false,
863
1079
  message: 'Allowlist not initialized. Run: kg mcp-config init-allowlist',
864
1080
  hint: 'Initialize with default safe patterns for .md, .txt, .pdf, .png, .jpg files'
@@ -870,7 +1086,7 @@ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) =
870
1086
  contents: [{
871
1087
  uri,
872
1088
  mimeType: 'text/plain',
873
- text: (0, formatters_js_1.formatMcpAllowedPaths)({
1089
+ text: (0, index_js_2.formatMcpAllowedPaths)({
874
1090
  configured: true,
875
1091
  version: config.version,
876
1092
  allowed_directories: config.allowed_directories,
@@ -948,7 +1164,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
948
1164
  include_concepts: true,
949
1165
  include_full_text: true,
950
1166
  });
951
- const formattedText = (0, formatters_js_1.formatSourceSearchResults)(result);
1167
+ const formattedText = (0, index_js_2.formatSourceSearchResults)(result);
952
1168
  return {
953
1169
  content: [{ type: 'text', text: formattedText }],
954
1170
  };
@@ -963,7 +1179,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
963
1179
  });
964
1180
  // Add query to result for formatting
965
1181
  const resultWithQuery = { ...result, query };
966
- const formattedText = (0, formatters_js_1.formatDocumentSearchResults)(resultWithQuery);
1182
+ const formattedText = (0, index_js_2.formatDocumentSearchResults)(resultWithQuery);
967
1183
  return {
968
1184
  content: [{ type: 'text', text: formattedText }],
969
1185
  };
@@ -980,7 +1196,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
980
1196
  include_diversity: true,
981
1197
  diversity_max_hops: 2,
982
1198
  });
983
- const formattedText = (0, formatters_js_1.formatSearchResults)(result);
1199
+ const formattedText = (0, index_js_2.formatSearchResults)(result);
984
1200
  return {
985
1201
  content: [{ type: 'text', text: formattedText }],
986
1202
  };
@@ -995,7 +1211,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
995
1211
  const diversityMaxHops = toolArgs.diversity_max_hops || 2;
996
1212
  const truncateEvidence = toolArgs.truncate_evidence !== false; // Default true
997
1213
  const result = await client.getConceptDetails(toolArgs.concept_id, includeGrounding, includeDiversity, diversityMaxHops);
998
- const formattedText = (0, formatters_js_1.formatConceptDetails)(result, truncateEvidence);
1214
+ const formattedText = (0, index_js_2.formatConceptDetails)(result, truncateEvidence);
999
1215
  return {
1000
1216
  content: [{ type: 'text', text: formattedText }],
1001
1217
  };
@@ -1009,7 +1225,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1009
1225
  include_epistemic_status: toolArgs.include_epistemic_status,
1010
1226
  exclude_epistemic_status: toolArgs.exclude_epistemic_status,
1011
1227
  });
1012
- const formattedText = (0, formatters_js_1.formatRelatedConcepts)(result);
1228
+ const formattedText = (0, index_js_2.formatRelatedConcepts)(result);
1013
1229
  return {
1014
1230
  content: [{ type: 'text', text: formattedText }],
1015
1231
  };
@@ -1045,7 +1261,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1045
1261
  include_epistemic_status: toolArgs.include_epistemic_status,
1046
1262
  exclude_epistemic_status: toolArgs.exclude_epistemic_status,
1047
1263
  });
1048
- const formattedText = (0, formatters_js_1.formatConnectionPaths)(result);
1264
+ const formattedText = (0, index_js_2.formatConnectionPaths)(result);
1049
1265
  return {
1050
1266
  content: [{ type: 'text', text: formattedText }],
1051
1267
  };
@@ -1091,14 +1307,14 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1091
1307
  switch (action) {
1092
1308
  case 'status': {
1093
1309
  const result = await client.getJobStatus(toolArgs.job_id);
1094
- const formattedText = (0, formatters_js_1.formatJobStatus)(result);
1310
+ const formattedText = (0, index_js_2.formatJobStatus)(result);
1095
1311
  return {
1096
1312
  content: [{ type: 'text', text: formattedText }],
1097
1313
  };
1098
1314
  }
1099
1315
  case 'list': {
1100
1316
  const result = await client.listJobs(toolArgs.status, undefined, toolArgs.limit || 50);
1101
- const formattedText = (0, formatters_js_1.formatJobList)(result);
1317
+ const formattedText = (0, index_js_2.formatJobList)(result);
1102
1318
  return {
1103
1319
  content: [{ type: 'text', text: formattedText }],
1104
1320
  };
@@ -1115,6 +1331,49 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1115
1331
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1116
1332
  };
1117
1333
  }
1334
+ case 'delete': {
1335
+ const result = await client.deleteJob(toolArgs.job_id, {
1336
+ purge: true,
1337
+ force: toolArgs.force || false
1338
+ });
1339
+ return {
1340
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1341
+ };
1342
+ }
1343
+ case 'cleanup': {
1344
+ const result = await client.deleteJobs({
1345
+ dryRun: toolArgs.dry_run !== false && !toolArgs.confirm,
1346
+ confirm: toolArgs.confirm || false,
1347
+ status: toolArgs.status,
1348
+ system: toolArgs.system_only || false,
1349
+ olderThan: toolArgs.older_than,
1350
+ jobType: toolArgs.job_type
1351
+ });
1352
+ let output = '';
1353
+ if (result.dry_run) {
1354
+ output = `## Cleanup Preview (dry run)\n\n`;
1355
+ output += `Jobs matching filters: ${result.jobs_to_delete}\n\n`;
1356
+ if (result.jobs && result.jobs.length > 0) {
1357
+ output += `| Job ID | Type | Status | Created |\n`;
1358
+ output += `|--------|------|--------|--------|\n`;
1359
+ for (const job of result.jobs.slice(0, 20)) {
1360
+ output += `| ${job.job_id.substring(0, 16)} | ${job.job_type} | ${job.status} | ${job.created_at} |\n`;
1361
+ }
1362
+ if (result.jobs.length > 20) {
1363
+ output += `\n... and ${result.jobs.length - 20} more\n`;
1364
+ }
1365
+ }
1366
+ output += `\nTo delete, use confirm: true`;
1367
+ }
1368
+ else {
1369
+ output = `## Cleanup Complete\n\n`;
1370
+ output += `Jobs deleted: ${result.jobs_deleted}\n`;
1371
+ output += `\n${result.message}`;
1372
+ }
1373
+ return {
1374
+ content: [{ type: 'text', text: output }],
1375
+ };
1376
+ }
1118
1377
  default:
1119
1378
  throw new Error(`Unknown job action: ${action}`);
1120
1379
  }
@@ -1201,7 +1460,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1201
1460
  content: [
1202
1461
  {
1203
1462
  type: 'text',
1204
- text: (0, formatters_js_1.formatInspectFileResult)(result),
1463
+ text: (0, index_js_2.formatInspectFileResult)(result),
1205
1464
  },
1206
1465
  ],
1207
1466
  };
@@ -1297,7 +1556,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1297
1556
  content: [
1298
1557
  {
1299
1558
  type: 'text',
1300
- text: (0, formatters_js_1.formatIngestFileResult)(result),
1559
+ text: (0, index_js_2.formatIngestFileResult)(result),
1301
1560
  },
1302
1561
  ],
1303
1562
  };
@@ -1380,7 +1639,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1380
1639
  content: [
1381
1640
  {
1382
1641
  type: 'text',
1383
- text: (0, formatters_js_1.formatIngestDirectoryResult)(result),
1642
+ text: (0, index_js_2.formatIngestDirectoryResult)(result),
1384
1643
  },
1385
1644
  ],
1386
1645
  };
@@ -1449,7 +1708,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1449
1708
  case 'list': {
1450
1709
  const statusFilter = toolArgs.status_filter;
1451
1710
  const result = await client.listEpistemicStatus(statusFilter);
1452
- const output = (0, formatters_js_1.formatEpistemicStatusList)(result);
1711
+ const output = (0, index_js_2.formatEpistemicStatusList)(result);
1453
1712
  return {
1454
1713
  content: [{ type: 'text', text: output }],
1455
1714
  };
@@ -1460,7 +1719,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1460
1719
  throw new Error('relationship_type is required for show action');
1461
1720
  }
1462
1721
  const result = await client.getEpistemicStatus(relationshipType);
1463
- const output = (0, formatters_js_1.formatEpistemicStatusDetails)(result);
1722
+ const output = (0, index_js_2.formatEpistemicStatusDetails)(result);
1464
1723
  return {
1465
1724
  content: [{ type: 'text', text: output }],
1466
1725
  };
@@ -1474,7 +1733,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1474
1733
  store: store,
1475
1734
  verbose: verbose,
1476
1735
  });
1477
- const output = (0, formatters_js_1.formatEpistemicStatusMeasurement)(result);
1736
+ const output = (0, index_js_2.formatEpistemicStatusMeasurement)(result);
1478
1737
  return {
1479
1738
  content: [{ type: 'text', text: output }],
1480
1739
  };
@@ -1492,7 +1751,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1492
1751
  max_candidates: toolArgs.max_candidates || 20,
1493
1752
  max_hops: toolArgs.max_hops || 2,
1494
1753
  });
1495
- const formattedText = (0, formatters_js_1.formatPolarityAxisResults)(result);
1754
+ const formattedText = (0, index_js_2.formatPolarityAxisResults)(result);
1496
1755
  return {
1497
1756
  content: [{ type: 'text', text: formattedText }],
1498
1757
  };
@@ -1596,7 +1855,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1596
1855
  limit: toolArgs.limit || 50,
1597
1856
  offset: toolArgs.offset || 0,
1598
1857
  });
1599
- const formattedText = (0, formatters_js_1.formatDocumentList)(result);
1858
+ const formattedText = (0, index_js_2.formatDocumentList)(result);
1600
1859
  return {
1601
1860
  content: [{ type: 'text', text: formattedText }],
1602
1861
  };
@@ -1607,7 +1866,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1607
1866
  throw new Error('document_id is required for show action');
1608
1867
  }
1609
1868
  const result = await client.getDocumentContent(documentId);
1610
- const formattedText = (0, formatters_js_1.formatDocumentContent)(result);
1869
+ const formattedText = (0, index_js_2.formatDocumentContent)(result);
1611
1870
  return {
1612
1871
  content: [{ type: 'text', text: formattedText }],
1613
1872
  };
@@ -1636,13 +1895,13 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1636
1895
  });
1637
1896
  }
1638
1897
  }
1639
- const formattedText = (0, formatters_js_1.formatDocumentConceptsDetailed)(result, conceptDetails);
1898
+ const formattedText = (0, index_js_2.formatDocumentConceptsDetailed)(result, conceptDetails);
1640
1899
  return {
1641
1900
  content: [{ type: 'text', text: formattedText }],
1642
1901
  };
1643
1902
  }
1644
1903
  else {
1645
- const formattedText = (0, formatters_js_1.formatDocumentConcepts)(result);
1904
+ const formattedText = (0, index_js_2.formatDocumentConcepts)(result);
1646
1905
  return {
1647
1906
  content: [{ type: 'text', text: formattedText }],
1648
1907
  };
@@ -1652,6 +1911,175 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1652
1911
  throw new Error(`Unknown document action: ${action}`);
1653
1912
  }
1654
1913
  }
1914
+ // ADR-089 Phase 3a: Graph CRUD Tool Handler (Refactored)
1915
+ case 'graph': {
1916
+ const action = toolArgs.action;
1917
+ const entity = toolArgs.entity;
1918
+ // Entity required for all actions except queue
1919
+ if (action !== 'queue' && !entity) {
1920
+ throw new Error('entity is required (concept or edge)');
1921
+ }
1922
+ // Create executor instance for this request
1923
+ const executor = new graph_operations_js_1.GraphOperationExecutor(client);
1924
+ switch (action) {
1925
+ case 'create': {
1926
+ if (entity === 'concept') {
1927
+ const result = await executor.createConcept({
1928
+ label: toolArgs.label,
1929
+ ontology: toolArgs.ontology,
1930
+ description: toolArgs.description,
1931
+ search_terms: toolArgs.search_terms,
1932
+ matching_mode: toolArgs.matching_mode,
1933
+ });
1934
+ if (!result.success)
1935
+ throw new Error(result.error);
1936
+ return {
1937
+ content: [{ type: 'text', text: (0, index_js_2.formatGraphConceptResult)(result.data, 'create') }],
1938
+ };
1939
+ }
1940
+ else if (entity === 'edge') {
1941
+ const result = await executor.createEdge({
1942
+ from_concept_id: toolArgs.from_concept_id,
1943
+ from_label: toolArgs.from_label,
1944
+ to_concept_id: toolArgs.to_concept_id,
1945
+ to_label: toolArgs.to_label,
1946
+ relationship_type: toolArgs.relationship_type,
1947
+ category: toolArgs.category,
1948
+ confidence: toolArgs.confidence,
1949
+ });
1950
+ if (!result.success)
1951
+ throw new Error(result.error);
1952
+ return {
1953
+ content: [{ type: 'text', text: (0, index_js_2.formatGraphEdgeResult)(result.data, 'create') }],
1954
+ };
1955
+ }
1956
+ else {
1957
+ throw new Error(`Unknown entity type: ${entity}`);
1958
+ }
1959
+ }
1960
+ case 'list': {
1961
+ if (entity === 'concept') {
1962
+ const result = await executor.listConcepts({
1963
+ ontology: toolArgs.ontology,
1964
+ label_contains: toolArgs.label_contains,
1965
+ creation_method: toolArgs.creation_method,
1966
+ offset: toolArgs.offset,
1967
+ limit: toolArgs.limit,
1968
+ });
1969
+ if (!result.success)
1970
+ throw new Error(result.error);
1971
+ return {
1972
+ content: [{ type: 'text', text: (0, index_js_2.formatGraphConceptList)(result.data) }],
1973
+ };
1974
+ }
1975
+ else if (entity === 'edge') {
1976
+ const result = await executor.listEdges({
1977
+ from_concept_id: toolArgs.from_concept_id,
1978
+ from_label: toolArgs.from_label,
1979
+ to_concept_id: toolArgs.to_concept_id,
1980
+ to_label: toolArgs.to_label,
1981
+ relationship_type: toolArgs.relationship_type,
1982
+ category: toolArgs.category,
1983
+ source: toolArgs.source,
1984
+ offset: toolArgs.offset,
1985
+ limit: toolArgs.limit,
1986
+ });
1987
+ if (!result.success)
1988
+ throw new Error(result.error);
1989
+ return {
1990
+ content: [{ type: 'text', text: (0, index_js_2.formatGraphEdgeList)(result.data) }],
1991
+ };
1992
+ }
1993
+ else {
1994
+ throw new Error(`Unknown entity type: ${entity}`);
1995
+ }
1996
+ }
1997
+ case 'edit': {
1998
+ if (entity === 'concept') {
1999
+ const result = await executor.editConcept({
2000
+ concept_id: toolArgs.concept_id,
2001
+ label: toolArgs.label,
2002
+ description: toolArgs.description,
2003
+ search_terms: toolArgs.search_terms,
2004
+ });
2005
+ if (!result.success)
2006
+ throw new Error(result.error);
2007
+ return {
2008
+ content: [{ type: 'text', text: (0, index_js_2.formatGraphConceptResult)(result.data, 'edit') }],
2009
+ };
2010
+ }
2011
+ else if (entity === 'edge') {
2012
+ const result = await executor.editEdge({
2013
+ from_concept_id: toolArgs.from_concept_id,
2014
+ from_label: toolArgs.from_label,
2015
+ to_concept_id: toolArgs.to_concept_id,
2016
+ to_label: toolArgs.to_label,
2017
+ relationship_type: toolArgs.relationship_type,
2018
+ confidence: toolArgs.confidence,
2019
+ category: toolArgs.category,
2020
+ });
2021
+ if (!result.success)
2022
+ throw new Error(result.error);
2023
+ return {
2024
+ content: [{ type: 'text', text: (0, index_js_2.formatGraphEdgeResult)(result.data, 'edit') }],
2025
+ };
2026
+ }
2027
+ else {
2028
+ throw new Error(`Unknown entity type: ${entity}`);
2029
+ }
2030
+ }
2031
+ case 'delete': {
2032
+ if (entity === 'concept') {
2033
+ const result = await executor.deleteConcept({
2034
+ concept_id: toolArgs.concept_id,
2035
+ cascade: toolArgs.cascade,
2036
+ });
2037
+ if (!result.success)
2038
+ throw new Error(result.error);
2039
+ return {
2040
+ content: [{ type: 'text', text: (0, index_js_2.formatGraphConceptResult)(result.data, 'delete') }],
2041
+ };
2042
+ }
2043
+ else if (entity === 'edge') {
2044
+ const result = await executor.deleteEdge({
2045
+ from_concept_id: toolArgs.from_concept_id,
2046
+ from_label: toolArgs.from_label,
2047
+ to_concept_id: toolArgs.to_concept_id,
2048
+ to_label: toolArgs.to_label,
2049
+ relationship_type: toolArgs.relationship_type,
2050
+ });
2051
+ if (!result.success)
2052
+ throw new Error(result.error);
2053
+ return {
2054
+ content: [{ type: 'text', text: (0, index_js_2.formatGraphEdgeResult)(result.data, 'delete') }],
2055
+ };
2056
+ }
2057
+ else {
2058
+ throw new Error(`Unknown entity type: ${entity}`);
2059
+ }
2060
+ }
2061
+ case 'queue': {
2062
+ const operations = toolArgs.operations;
2063
+ const continueOnError = toolArgs.continue_on_error === true;
2064
+ if (!operations || !Array.isArray(operations)) {
2065
+ throw new Error('operations array is required for queue action');
2066
+ }
2067
+ if (operations.length === 0) {
2068
+ throw new Error('operations array cannot be empty');
2069
+ }
2070
+ if (operations.length > 20) {
2071
+ throw new Error(`Queue too large: ${operations.length} operations (max 20)`);
2072
+ }
2073
+ const queueResult = await executor.executeQueue(operations, continueOnError);
2074
+ const formattedOutput = (0, index_js_2.formatGraphQueueResult)(queueResult, operations.length);
2075
+ return {
2076
+ content: [{ type: 'text', text: formattedOutput }],
2077
+ };
2078
+ }
2079
+ default:
2080
+ throw new Error(`Unknown graph action: ${action}. Use: create, edit, delete, list, or queue`);
2081
+ }
2082
+ }
1655
2083
  default:
1656
2084
  throw new Error(`Unknown tool: ${name}`);
1657
2085
  }