@starascendin/lifeos-mcp 0.7.67 → 0.7.68

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/README.md CHANGED
@@ -24,17 +24,20 @@ npx @starascendin/lifeos-mcp --url <your-convex-url> --user-id <your-user-id> --
24
24
  | User ID | `--user-id`, `-i` | `LIFEOS_USER_ID` | Your LifeOS user ID |
25
25
  | API Key | `--api-key`, `-k` | `LIFEOS_API_KEY` | Your API key for authentication |
26
26
 
27
- ### Optional SurrealDB Sidecar Parameters
27
+ ### Optional FalkorDB Sidecar Parameters
28
28
 
29
- These are required only for the Surreal graph tools.
29
+ These are required only for the Falkor graph tools.
30
30
 
31
31
  | Env Variable | Description |
32
32
  |--------------|-------------|
33
- | `SURREAL_ENDPOINT` | SurrealDB HTTP endpoint, e.g. `https://surrealdbprod.apps.rjlabs.dev` |
34
- | `SURREAL_USER` | SurrealDB username |
35
- | `SURREAL_PASS` | SurrealDB password |
36
- | `SURREAL_NS` | Namespace, defaults to `lifeos` |
37
- | `SURREAL_DB` | Database, defaults to `graph` |
33
+ | `FALKOR_BROWSER_ENDPOINT` | FalkorDB Browser HTTP endpoint, defaults to `https://falkordb.apps.rjlabs.dev` |
34
+ | `FALKOR_GRAPH` | Falkor graph name, defaults to `lifeos_ppv` |
35
+ | `FALKOR_PASS` | Falkor password, used to mint short-lived browser tokens |
36
+ | `FALKOR_TOKEN` / `FALKOR_PAT` | Optional pre-provisioned Falkor browser token |
37
+ | `FALKOR_USER` | Falkor username, defaults to `default` |
38
+ | `FALKOR_HOST` | Browser backend Redis host, defaults to `localhost` |
39
+ | `FALKOR_PORT` | Browser backend Redis port, defaults to `6379` |
40
+ | `FALKOR_TLS` | Browser backend Redis TLS flag, defaults to `false` |
38
41
 
39
42
  ### Getting Your Credentials
40
43
 
@@ -190,16 +193,16 @@ Add to your `.mcp.json` (project root or `~/.claude/mcp.json`):
190
193
  - **refresh_unified_graph_cache** - Recompute inferred graph links and refresh the cached projection
191
194
  - **upsert_unified_graph_link / delete_unified_graph_link** - Create or remove manual links between graph nodes
192
195
 
193
- ### SurrealDB Sidecar Graph
194
- - **surreal_graph_schema** - Inspect the agent graph contract, configured namespace/db, allowed table prefixes, relation tables, and examples
195
- - **surreal_graph_query** - Run guarded read-only SurrealQL (`SELECT`, `INFO`, `EXPLAIN` only)
196
- - **surreal_graph_link** - Create/update an agent-owned relationship in `lifeos_agent_links` without mutating Convex-synced entity tables
197
- - **surreal_graph_unlink** - Remove a relationship previously created by `surreal_graph_link`
196
+ ### FalkorDB Sidecar Graph
197
+ - **falkor_graph_schema** - Inspect the Falkor graph contract, configured graph, allowed labels, relationship types, and examples
198
+ - **falkor_graph_query** - Run guarded read-only Cypher against the Falkor PPV sidecar
199
+ - **falkor_graph_link** - Create/update an agent-owned `AGENT_LINK` relationship without mutating Convex-synced nodes or inferred PPV relationships
200
+ - **falkor_graph_unlink** - Remove a relationship previously created by `falkor_graph_link`
198
201
 
199
- Surreal graph rules:
202
+ Falkor graph rules:
200
203
  - Convex remains canonical for entity data.
201
- - SurrealDB is the relationship/query sidecar.
202
- - Agents may query graph data and write only agent-owned links through `lifeos_agent_links`.
204
+ - FalkorDB is a graph query/read sidecar for the PPV experiment.
205
+ - Agents may query graph data and write only agent-owned `AGENT_LINK` relationships.
203
206
  - Every agent-created link must include a reason and confidence.
204
207
  - Raw read queries should include `LIMIT`.
205
208
 
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.7.67";
2
- export declare const BUILD_TIME = "2026-05-17T16:11:21.616Z";
1
+ export declare const VERSION = "0.7.68";
2
+ export declare const BUILD_TIME = "2026-05-17T19:06:09.988Z";
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Regenerated on every build.
2
- export const VERSION = "0.7.67";
3
- export const BUILD_TIME = "2026-05-17T16:11:21.616Z";
2
+ export const VERSION = "0.7.68";
3
+ export const BUILD_TIME = "2026-05-17T19:06:09.988Z";
package/dist/index.js CHANGED
@@ -60,7 +60,7 @@ program
60
60
  });
61
61
  program
62
62
  .command("ppv <action>")
63
- .description("Manage PPV as JSON. Actions: workspace, graph, vision-graph, seed, vision, activate-vision, set-active-vision, identity, pillar, action, reflection, adjustment.")
63
+ .description("Manage PPV as JSON. Actions: workspace, graph, vision-graph, seed, vision, activate-vision, set-active-vision, identity, pillar.")
64
64
  .option("--file <file>", "JSON payload file for mutating actions. workspace, graph, vision-graph, and seed do not require a file.")
65
65
  .action((action, commandOptions) => {
66
66
  directCliCommand = {
@@ -4542,7 +4542,7 @@ const TOOLS = [
4542
4542
  // PPV v1 tools
4543
4543
  {
4544
4544
  name: "get_ppv_workspace",
4545
- description: "Get the PPV workspace for the selected vision, plus all owned visions, active-vision context, identity, pillars, linked projects, weekly actions, reflections, and adjustments.",
4545
+ description: "Get the PPV workspace for the selected vision, plus all owned visions, active-vision context, identity, pillars, and linked projects.",
4546
4546
  inputSchema: {
4547
4547
  type: "object",
4548
4548
  properties: {
@@ -4553,7 +4553,7 @@ const TOOLS = [
4553
4553
  },
4554
4554
  {
4555
4555
  name: "get_active_vision_graph",
4556
- description: "Best default graph read for PPV questions. Get the unified active-vision graph linking PPV vision, identity, pillars, weekly actions, reflections, adjustments, related projects/issues, and recent voice memos.",
4556
+ description: "Best default graph read for PPV questions. Get the unified active-vision graph linking PPV vision, identity, pillars, related projects/issues, and recent voice memos.",
4557
4557
  inputSchema: {
4558
4558
  type: "object",
4559
4559
  properties: {
@@ -4730,52 +4730,52 @@ const TOOLS = [
4730
4730
  },
4731
4731
  },
4732
4732
  {
4733
- name: "surreal_graph_schema",
4734
- description: "Inspect the SurrealDB sidecar graph contract for agents: connection state, allowed node-table prefixes, safe query rules, relation tables, and example SurrealQL. Use before raw SurrealQL graph work.",
4733
+ name: "falkor_graph_schema",
4734
+ description: "Inspect the FalkorDB sidecar graph contract for agents: configured graph, mirrored PPV labels, safe Cypher rules, relationship types, and examples. Use before raw Falkor Cypher graph work.",
4735
4735
  inputSchema: {
4736
4736
  type: "object",
4737
4737
  properties: {},
4738
4738
  },
4739
4739
  },
4740
4740
  {
4741
- name: "surreal_graph_query",
4742
- description: "Run guarded read-only SurrealQL against the LifeOS SurrealDB graph sidecar. Allowed statements are SELECT, INFO, and EXPLAIN only. Use LIMIT for SELECT queries. Do not use this for Convex-canonical writes.",
4741
+ name: "falkor_graph_query",
4742
+ description: "Run guarded read-only Cypher against the LifeOS FalkorDB graph sidecar. Allowed statements are MATCH, WITH, RETURN, EXPLAIN, and PROFILE query shapes only. MATCH queries should include LIMIT unless doing count aggregation. Do not use this for Convex-canonical writes.",
4743
4743
  inputSchema: {
4744
4744
  type: "object",
4745
4745
  properties: {
4746
4746
  query: {
4747
4747
  type: "string",
4748
- description: "Read-only SurrealQL. SELECT statements must include LIMIT unless they are aggregate count queries.",
4748
+ description: "Read-only Cypher. MATCH queries should include LIMIT unless they are aggregate count queries.",
4749
4749
  },
4750
4750
  maxRows: {
4751
4751
  type: "number",
4752
- description: "Maximum rows returned per statement result after execution. Default 50, max 200.",
4752
+ description: "Maximum rows returned after execution. Default 50, max 200.",
4753
4753
  },
4754
4754
  },
4755
4755
  required: ["query"],
4756
4756
  },
4757
4757
  },
4758
4758
  {
4759
- name: "surreal_graph_link",
4760
- description: "Create or update an agent-owned graph relationship in SurrealDB between two LifeOS records. This writes only to the lifeos_agent_links relation table and never mutates Convex-synced entity records.",
4759
+ name: "falkor_graph_link",
4760
+ description: "Create or update an agent-owned AGENT_LINK relationship in FalkorDB between two existing LifeOS graph records. This never mutates Convex-synced nodes or inferred PPV relationships.",
4761
4761
  inputSchema: {
4762
4762
  type: "object",
4763
4763
  properties: {
4764
- fromTable: {
4764
+ fromLabel: {
4765
4765
  type: "string",
4766
- description: "Source Surreal table, e.g. ppv1_pillars or lifeos_pmProjects.",
4766
+ description: "Source Falkor node label, e.g. PpvPillar.",
4767
4767
  },
4768
4768
  fromId: {
4769
4769
  type: "string",
4770
- description: "Source record id without the table prefix.",
4770
+ description: "Source Convex record id stored in node.convexId.",
4771
4771
  },
4772
- toTable: {
4772
+ toLabel: {
4773
4773
  type: "string",
4774
- description: "Target Surreal table, e.g. lifeos_pmProjects or lifeos_frmPeople.",
4774
+ description: "Target Falkor node label, e.g. PpvProject.",
4775
4775
  },
4776
4776
  toId: {
4777
4777
  type: "string",
4778
- description: "Target record id without the table prefix.",
4778
+ description: "Target Convex record id stored in node.convexId.",
4779
4779
  },
4780
4780
  kind: {
4781
4781
  type: "string",
@@ -4799,31 +4799,31 @@ const TOOLS = [
4799
4799
  additionalProperties: true,
4800
4800
  },
4801
4801
  },
4802
- required: ["fromTable", "fromId", "toTable", "toId", "kind", "reason"],
4802
+ required: ["fromLabel", "fromId", "toLabel", "toId", "kind", "reason"],
4803
4803
  },
4804
4804
  },
4805
4805
  {
4806
- name: "surreal_graph_unlink",
4807
- description: "Remove an agent-owned SurrealDB graph relationship previously created by surreal_graph_link. This only deletes from lifeos_agent_links.",
4806
+ name: "falkor_graph_unlink",
4807
+ description: "Remove an agent-owned FalkorDB AGENT_LINK relationship previously created by falkor_graph_link.",
4808
4808
  inputSchema: {
4809
4809
  type: "object",
4810
4810
  properties: {
4811
- fromTable: { type: "string", description: "Source Surreal table." },
4811
+ fromLabel: { type: "string", description: "Source Falkor node label." },
4812
4812
  fromId: {
4813
4813
  type: "string",
4814
- description: "Source record id without table prefix.",
4814
+ description: "Source Convex record id stored in node.convexId.",
4815
4815
  },
4816
- toTable: { type: "string", description: "Target Surreal table." },
4816
+ toLabel: { type: "string", description: "Target Falkor node label." },
4817
4817
  toId: {
4818
4818
  type: "string",
4819
- description: "Target record id without table prefix.",
4819
+ description: "Target Convex record id stored in node.convexId.",
4820
4820
  },
4821
4821
  kind: {
4822
4822
  type: "string",
4823
4823
  description: "Relationship kind used when linking.",
4824
4824
  },
4825
4825
  },
4826
- required: ["fromTable", "fromId", "toTable", "toId", "kind"],
4826
+ required: ["fromLabel", "fromId", "toLabel", "toId", "kind"],
4827
4827
  },
4828
4828
  },
4829
4829
  {
@@ -4914,7 +4914,7 @@ const TOOLS = [
4914
4914
  },
4915
4915
  {
4916
4916
  name: "delete_ppv_vision",
4917
- description: "Delete a PPV vision and cascade-delete its identity, pillars, weekly actions, reflections, and adjustments.",
4917
+ description: "Delete a PPV vision and cascade-delete its identity and pillars.",
4918
4918
  inputSchema: {
4919
4919
  type: "object",
4920
4920
  properties: {
@@ -5017,143 +5017,6 @@ const TOOLS = [
5017
5017
  required: ["pillarId"],
5018
5018
  },
5019
5019
  },
5020
- {
5021
- name: "create_ppv_weekly_action",
5022
- description: "Create an identity-aligned PPV weekly action.",
5023
- inputSchema: {
5024
- type: "object",
5025
- properties: {
5026
- userId: { type: "string", description: "Override user ID" },
5027
- visionId: { type: "string", description: "PPV vision ID" },
5028
- pillarId: { type: "string", description: "PPV pillar ID" },
5029
- projectId: {
5030
- type: "string",
5031
- description: "Existing LifeOS project ID",
5032
- },
5033
- category: { type: "string", description: "Action category" },
5034
- action: { type: "string", description: "Small action to take" },
5035
- status: {
5036
- type: "string",
5037
- enum: ["pending", "in_progress", "completed", "skipped"],
5038
- description: "Action status",
5039
- },
5040
- scheduledDate: { type: "string", description: "ISO scheduled date" },
5041
- week: { type: "string", description: "ISO week label" },
5042
- },
5043
- required: ["visionId", "category", "action"],
5044
- },
5045
- },
5046
- {
5047
- name: "update_ppv_weekly_action",
5048
- description: "Update a PPV weekly action.",
5049
- inputSchema: {
5050
- type: "object",
5051
- properties: {
5052
- userId: { type: "string", description: "Override user ID" },
5053
- actionId: { type: "string", description: "PPV weekly action ID" },
5054
- pillarId: { type: "string", description: "PPV pillar ID" },
5055
- projectId: {
5056
- type: "string",
5057
- description: "Existing LifeOS project ID",
5058
- },
5059
- category: { type: "string", description: "Updated category" },
5060
- action: { type: "string", description: "Updated action" },
5061
- status: {
5062
- type: "string",
5063
- enum: ["pending", "in_progress", "completed", "skipped"],
5064
- description: "Updated status",
5065
- },
5066
- scheduledDate: { type: "string", description: "ISO scheduled date" },
5067
- week: { type: "string", description: "ISO week label" },
5068
- },
5069
- required: ["actionId"],
5070
- },
5071
- },
5072
- {
5073
- name: "delete_ppv_weekly_action",
5074
- description: "Delete a PPV weekly action.",
5075
- inputSchema: {
5076
- type: "object",
5077
- properties: {
5078
- userId: { type: "string", description: "Override user ID" },
5079
- actionId: { type: "string", description: "PPV weekly action ID" },
5080
- },
5081
- required: ["actionId"],
5082
- },
5083
- },
5084
- {
5085
- name: "create_ppv_reflection",
5086
- description: "Create a PPV weekly reflection.",
5087
- inputSchema: {
5088
- type: "object",
5089
- properties: {
5090
- userId: { type: "string", description: "Override user ID" },
5091
- visionId: { type: "string", description: "PPV vision ID" },
5092
- week: { type: "string", description: "ISO week label" },
5093
- questions: {
5094
- type: "array",
5095
- items: {
5096
- type: "object",
5097
- properties: {
5098
- question: { type: "string" },
5099
- answer: { type: "string" },
5100
- },
5101
- required: ["question", "answer"],
5102
- },
5103
- description: "Reflection questions and answers",
5104
- },
5105
- alignmentScore: { type: "number", description: "Optional score" },
5106
- },
5107
- required: ["visionId", "week", "questions"],
5108
- },
5109
- },
5110
- {
5111
- name: "delete_ppv_reflection",
5112
- description: "Delete a PPV reflection.",
5113
- inputSchema: {
5114
- type: "object",
5115
- properties: {
5116
- userId: { type: "string", description: "Override user ID" },
5117
- reflectionId: { type: "string", description: "PPV reflection ID" },
5118
- },
5119
- required: ["reflectionId"],
5120
- },
5121
- },
5122
- {
5123
- name: "create_ppv_adjustment",
5124
- description: "Create a PPV adjustment from reflection or lived evidence.",
5125
- inputSchema: {
5126
- type: "object",
5127
- properties: {
5128
- userId: { type: "string", description: "Override user ID" },
5129
- visionId: { type: "string", description: "PPV vision ID" },
5130
- sourceReflectionId: {
5131
- type: "string",
5132
- description: "Source PPV reflection ID",
5133
- },
5134
- observation: { type: "string", description: "What was observed" },
5135
- decision: { type: "string", description: "What will change" },
5136
- affectedComponents: {
5137
- type: "array",
5138
- items: { type: "string" },
5139
- description: "Components affected by the adjustment",
5140
- },
5141
- },
5142
- required: ["visionId", "observation", "decision", "affectedComponents"],
5143
- },
5144
- },
5145
- {
5146
- name: "delete_ppv_adjustment",
5147
- description: "Delete a PPV adjustment.",
5148
- inputSchema: {
5149
- type: "object",
5150
- properties: {
5151
- userId: { type: "string", description: "Override user ID" },
5152
- adjustmentId: { type: "string", description: "PPV adjustment ID" },
5153
- },
5154
- required: ["adjustmentId"],
5155
- },
5156
- },
5157
5020
  // MCP Server Info
5158
5021
  {
5159
5022
  name: "get_version",
@@ -5233,22 +5096,22 @@ const PROMPTS = [
5233
5096
  },
5234
5097
  {
5235
5098
  name: "ppv",
5236
- description: "Manage the PPV life design system: vision, identity, pillars, projects, weekly actions, reflections, and adjustments.",
5099
+ description: "Manage the PPV life design system: vision, identity, pillars, and projects.",
5237
5100
  arguments: [
5238
5101
  {
5239
5102
  name: "intent",
5240
- description: "What to do with PPV, e.g. create vision, add weekly action, reflect, adjust, link project.",
5103
+ description: "What to do with PPV, e.g. create vision, update identity, add pillar, link project.",
5241
5104
  required: false,
5242
5105
  },
5243
5106
  ],
5244
5107
  },
5245
5108
  {
5246
- name: "surreal-graph",
5247
- description: "Use the SurrealDB sidecar graph: query LifeOS graph relationships with SurrealQL and create controlled agent-owned graph links.",
5109
+ name: "falkor-graph",
5110
+ description: "Use the FalkorDB sidecar graph: query PPV graph relationships with Cypher and create controlled agent-owned graph links.",
5248
5111
  arguments: [
5249
5112
  {
5250
5113
  name: "intent",
5251
- description: "What to do with the Surreal graph, e.g. inspect PPV-project links, connect a chat to a project, or find related people.",
5114
+ description: "What to do with the Falkor graph, e.g. inspect PPV-project links or add an agent-owned PPV relationship.",
5252
5115
  required: false,
5253
5116
  },
5254
5117
  ],
@@ -5614,7 +5477,7 @@ Do not ask for confirmation; the user intends this planning workflow to mutate L
5614
5477
  ${intentClause}
5615
5478
 
5616
5479
  Use the LifeOS MCP PPV tools:
5617
- 1. Call get_ppv_workspace first to inspect all owned visions, the selected/current vision, identity, pillars, linked projects, weekly actions, reflections, adjustments, and available LifeOS projects.
5480
+ 1. Call get_ppv_workspace first to inspect all owned visions, the selected/current vision, identity, pillars, linked projects, and available LifeOS projects.
5618
5481
  2. If there is no vision and the user wants the example, call seed_ppv_beijing_workspace.
5619
5482
  3. For vision changes, call upsert_ppv_vision.
5620
5483
  4. If the user wants to change a vision lifecycle without editing content, call set_ppv_vision_status.
@@ -5623,8 +5486,6 @@ Use the LifeOS MCP PPV tools:
5623
5486
  7. If the user wants to permanently remove a vision and its PPV subcomponents, call delete_ppv_vision.
5624
5487
  8. For identity work, call upsert_ppv_identity.
5625
5488
  9. For pillars, call create_ppv_pillar or update_ppv_pillar. Link only existing LifeOS project IDs from get_ppv_workspace.projects.
5626
- 10. For execution, call create_ppv_weekly_action or update_ppv_weekly_action.
5627
- 11. For learning loops, call create_ppv_reflection and create_ppv_adjustment.
5628
5489
 
5629
5490
  Keep the system simple:
5630
5491
  - Vision is directional and experiential, not a task list.
@@ -5632,38 +5493,37 @@ Keep the system simple:
5632
5493
  - Pillars are ongoing systems.
5633
5494
  - Projects are existing LifeOS projects.
5634
5495
  - Weekly actions should be small, concrete, and identity-aligned.
5635
- - Reflections and adjustments should update behavior based on evidence.
5636
5496
 
5637
5497
  After mutating, report exactly what changed and any IDs needed for future updates.`,
5638
5498
  },
5639
5499
  },
5640
5500
  ];
5641
5501
  },
5642
- "surreal-graph": (args) => {
5502
+ "falkor-graph": (args) => {
5643
5503
  const intentClause = args.intent
5644
5504
  ? `User intent: ${args.intent}`
5645
- : "Start by inspecting the Surreal graph schema and current agent links.";
5505
+ : "Start by inspecting the Falkor graph schema and current agent links.";
5646
5506
  return [
5647
5507
  {
5648
5508
  role: "user",
5649
5509
  content: {
5650
5510
  type: "text",
5651
- text: `Use the LifeOS SurrealDB sidecar graph.
5511
+ text: `Use the LifeOS FalkorDB sidecar graph.
5652
5512
 
5653
5513
  ${intentClause}
5654
5514
 
5655
5515
  Use these MCP tools:
5656
- 1. Call surreal_graph_schema first to inspect the allowed graph contract and examples.
5657
- 2. Use surreal_graph_query for read-only SurrealQL. Only SELECT, INFO, and EXPLAIN are allowed; SELECT queries must include LIMIT unless doing count aggregation.
5658
- 3. Use surreal_graph_link when you need to create a relationship between existing LifeOS records. This writes only to lifeos_agent_links.
5659
- 4. Use surreal_graph_unlink only to remove relationships previously created by surreal_graph_link.
5516
+ 1. Call falkor_graph_schema first to inspect the allowed graph contract and examples.
5517
+ 2. Use falkor_graph_query for read-only Cypher. MATCH/WITH queries must include LIMIT unless doing count aggregation.
5518
+ 3. Use falkor_graph_link when you need to create a relationship between existing Falkor nodes. This writes only AGENT_LINK relationships.
5519
+ 4. Use falkor_graph_unlink only to remove relationships previously created by falkor_graph_link.
5660
5520
 
5661
5521
  Rules:
5662
- - Convex remains canonical for entity data. Do not use SurrealDB to edit projects, PPV records, CRM people, chats, or notes.
5663
- - SurrealDB is the graph memory / relationship layer.
5522
+ - Convex remains canonical for entity data. Do not use FalkorDB to edit projects or PPV records.
5523
+ - FalkorDB is the graph sidecar.
5664
5524
  - Always include a concrete reason and confidence when linking.
5665
- - Prefer precise ids from prior reads; do not invent ids.
5666
- - Keep queries scoped with LIMIT and return concise findings plus the SurrealQL you used when useful.`,
5525
+ - Prefer precise node labels and convexId values from prior reads; do not invent ids.
5526
+ - Keep queries scoped with LIMIT and return concise findings plus the Cypher you used when useful.`,
5667
5527
  },
5668
5528
  },
5669
5529
  ];
@@ -6550,11 +6410,14 @@ Keep it concise and direct. This is an accountability report, not a feel-good su
6550
6410
  const CONVEX_URL = options.url || process.env.CONVEX_URL;
6551
6411
  const API_KEY = options.apiKey || process.env.LIFEOS_API_KEY;
6552
6412
  const USER_ID = options.userId || process.env.LIFEOS_USER_ID;
6553
- const SURREAL_ENDPOINT = process.env.SURREAL_ENDPOINT;
6554
- const SURREAL_USER = process.env.SURREAL_USER;
6555
- const SURREAL_PASS = process.env.SURREAL_PASS;
6556
- const SURREAL_NS = process.env.SURREAL_NS || "lifeos";
6557
- const SURREAL_DB = process.env.SURREAL_DB || "graph";
6413
+ const FALKOR_BROWSER_ENDPOINT = process.env.FALKOR_BROWSER_ENDPOINT || "https://falkordb.apps.rjlabs.dev";
6414
+ const FALKOR_GRAPH = process.env.FALKOR_GRAPH || "lifeos_ppv";
6415
+ const FALKOR_USER = process.env.FALKOR_USER || "default";
6416
+ const FALKOR_PASS = process.env.FALKOR_PASS;
6417
+ const FALKOR_TOKEN = process.env.FALKOR_TOKEN || process.env.FALKOR_PAT;
6418
+ const FALKOR_HOST = process.env.FALKOR_HOST || "localhost";
6419
+ const FALKOR_PORT = process.env.FALKOR_PORT || "6379";
6420
+ const FALKOR_TLS = process.env.FALKOR_TLS || "false";
6558
6421
  // Validate required configuration
6559
6422
  const missingConfig = [];
6560
6423
  if (!CONVEX_URL)
@@ -6666,13 +6529,6 @@ async function runDirectCliCommand(command) {
6666
6529
  pillar: "create_ppv_pillar",
6667
6530
  "update-pillar": "update_ppv_pillar",
6668
6531
  "delete-pillar": "delete_ppv_pillar",
6669
- action: "create_ppv_weekly_action",
6670
- "update-action": "update_ppv_weekly_action",
6671
- "delete-action": "delete_ppv_weekly_action",
6672
- reflection: "create_ppv_reflection",
6673
- "delete-reflection": "delete_ppv_reflection",
6674
- adjustment: "create_ppv_adjustment",
6675
- "delete-adjustment": "delete_ppv_adjustment",
6676
6532
  };
6677
6533
  const tool = actionToTool[command.action];
6678
6534
  if (!tool) {
@@ -6708,55 +6564,6 @@ async function callConvexJsonEndpoint(path, body) {
6708
6564
  }
6709
6565
  return await response.json();
6710
6566
  }
6711
- const SURREAL_AGENT_LINK_TABLE = "lifeos_agent_links";
6712
- const SURREAL_TABLE_PREFIXES = ["lifeos_", "life_", "ppv1_"];
6713
- const SURREAL_RELATION_TABLES = [
6714
- "ppv_has_identity",
6715
- "ppv_has_pillar",
6716
- "ppv_has_action",
6717
- "ppv_has_reflection",
6718
- "ppv_has_adjustment",
6719
- "ppv_pillar_drives_action",
6720
- "ppv_pillar_supports_project",
6721
- "ppv_project_executes_action",
6722
- "ppv_reflection_inspires_adjustment",
6723
- SURREAL_AGENT_LINK_TABLE,
6724
- ];
6725
- const SURREAL_MUTATING_KEYWORDS = [
6726
- "ALTER",
6727
- "CREATE",
6728
- "DEFINE",
6729
- "DELETE",
6730
- "INSERT",
6731
- "KILL",
6732
- "LIVE",
6733
- "REBUILD",
6734
- "RELATE",
6735
- "REMOVE",
6736
- "SLEEP",
6737
- "THROW",
6738
- "UPDATE",
6739
- "UPSERT",
6740
- ];
6741
- function requireSurrealConfig() {
6742
- const missing = [];
6743
- if (!SURREAL_ENDPOINT)
6744
- missing.push("SURREAL_ENDPOINT");
6745
- if (!SURREAL_USER)
6746
- missing.push("SURREAL_USER");
6747
- if (!SURREAL_PASS)
6748
- missing.push("SURREAL_PASS");
6749
- if (missing.length > 0) {
6750
- throw new Error(`Missing SurrealDB configuration: ${missing.join(", ")}. Set SURREAL_ENDPOINT, SURREAL_USER, SURREAL_PASS, SURREAL_NS, and SURREAL_DB in the agent runtime.`);
6751
- }
6752
- return {
6753
- endpoint: SURREAL_ENDPOINT,
6754
- user: SURREAL_USER,
6755
- pass: SURREAL_PASS,
6756
- ns: SURREAL_NS,
6757
- db: SURREAL_DB,
6758
- };
6759
- }
6760
6567
  function stripSqlCommentsAndStrings(sql) {
6761
6568
  let output = "";
6762
6569
  let index = 0;
@@ -6809,74 +6616,11 @@ function stripSqlCommentsAndStrings(sql) {
6809
6616
  }
6810
6617
  return output;
6811
6618
  }
6812
- function splitSqlStatements(sql) {
6813
- return stripSqlCommentsAndStrings(sql)
6814
- .split(";")
6815
- .map((statement) => statement.trim())
6816
- .filter(Boolean);
6817
- }
6818
- function assertReadOnlySurrealSql(sql) {
6819
- const statements = splitSqlStatements(sql);
6820
- if (statements.length === 0) {
6821
- throw new Error("SurrealQL query is empty.");
6822
- }
6823
- if (statements.length > 5) {
6824
- throw new Error("SurrealQL query is too broad. Use 5 statements or fewer.");
6825
- }
6826
- for (const statement of statements) {
6827
- const upper = statement.toUpperCase();
6828
- const startsReadOnly = upper.startsWith("SELECT ") ||
6829
- upper.startsWith("INFO ") ||
6830
- upper.startsWith("EXPLAIN ");
6831
- if (!startsReadOnly) {
6832
- throw new Error("surreal_graph_query only allows SELECT, INFO, and EXPLAIN statements. Use surreal_graph_link for controlled relationship writes.");
6833
- }
6834
- for (const keyword of SURREAL_MUTATING_KEYWORDS) {
6835
- if (new RegExp(`\\b${keyword}\\b`, "i").test(statement)) {
6836
- throw new Error(`surreal_graph_query blocked mutating keyword ${keyword}. Use guarded graph write tools instead.`);
6837
- }
6838
- }
6839
- const isAggregateCount = /\bCOUNT\s*\(/i.test(statement);
6840
- if (upper.startsWith("SELECT ") &&
6841
- !/\bLIMIT\b/i.test(statement) &&
6842
- !isAggregateCount) {
6843
- throw new Error("SELECT queries must include LIMIT unless they are aggregate count queries.");
6844
- }
6845
- }
6846
- }
6847
6619
  function clampMaxRows(value) {
6848
6620
  if (typeof value !== "number" || !Number.isFinite(value))
6849
6621
  return 50;
6850
6622
  return Math.max(1, Math.min(200, Math.floor(value)));
6851
6623
  }
6852
- function capSurrealResults(results, maxRows) {
6853
- return results.map((entry) => {
6854
- if (!Array.isArray(entry.result) || entry.result.length <= maxRows) {
6855
- return entry;
6856
- }
6857
- return {
6858
- ...entry,
6859
- result: entry.result.slice(0, maxRows),
6860
- detail: `${entry.result.length - maxRows} additional rows omitted by lifeos-mcp maxRows=${maxRows}`,
6861
- };
6862
- });
6863
- }
6864
- function assertSurrealIdentifier(value, label) {
6865
- if (typeof value !== "string" || !/^[A-Za-z][A-Za-z0-9_]*$/.test(value)) {
6866
- throw new Error(`${label} must be a simple Surreal identifier.`);
6867
- }
6868
- return value;
6869
- }
6870
- function assertLifeOsNodeTable(value, label) {
6871
- const table = assertSurrealIdentifier(value, label);
6872
- if (!SURREAL_TABLE_PREFIXES.some((prefix) => table.startsWith(prefix))) {
6873
- throw new Error(`${label} must start with one of: ${SURREAL_TABLE_PREFIXES.join(", ")}`);
6874
- }
6875
- if (SURREAL_RELATION_TABLES.includes(table)) {
6876
- throw new Error(`${label} must be an entity table, not a relation table.`);
6877
- }
6878
- return table;
6879
- }
6880
6624
  function assertRecordId(value, label) {
6881
6625
  if (typeof value !== "string" || value.trim().length === 0) {
6882
6626
  throw new Error(`${label} is required.`);
@@ -6892,79 +6636,237 @@ function assertRelationKind(value) {
6892
6636
  }
6893
6637
  return value;
6894
6638
  }
6895
- function escapeSurrealRecordId(id) {
6896
- return id.replace(/`/g, "\\`");
6639
+ const FALKOR_AGENT_RELATIONSHIP = "AGENT_LINK";
6640
+ const FALKOR_NODE_LABELS = [
6641
+ "PpvVision",
6642
+ "PpvIdentity",
6643
+ "PpvPillar",
6644
+ "PpvProject",
6645
+ ];
6646
+ const FALKOR_INFERRED_RELATIONSHIPS = [
6647
+ "HAS_IDENTITY",
6648
+ "HAS_PILLAR",
6649
+ "PILLAR_SUPPORTS_PROJECT",
6650
+ ];
6651
+ const FALKOR_MUTATING_KEYWORDS = [
6652
+ "CALL",
6653
+ "CREATE",
6654
+ "DELETE",
6655
+ "DROP",
6656
+ "FOREACH",
6657
+ "LOAD",
6658
+ "MERGE",
6659
+ "REMOVE",
6660
+ "SET",
6661
+ ];
6662
+ function requireFalkorConfig() {
6663
+ if (!FALKOR_TOKEN && !FALKOR_PASS) {
6664
+ throw new Error("Missing FalkorDB configuration: set FALKOR_TOKEN/FALKOR_PAT or FALKOR_PASS in the agent runtime.");
6665
+ }
6666
+ return {
6667
+ endpoint: FALKOR_BROWSER_ENDPOINT,
6668
+ graph: FALKOR_GRAPH,
6669
+ username: FALKOR_USER,
6670
+ password: FALKOR_PASS,
6671
+ token: FALKOR_TOKEN,
6672
+ host: FALKOR_HOST,
6673
+ port: FALKOR_PORT,
6674
+ tls: FALKOR_TLS,
6675
+ };
6897
6676
  }
6898
- function surrealThing(table, id) {
6899
- return `${table}:\`${escapeSurrealRecordId(id)}\``;
6677
+ function sleep(ms) {
6678
+ return new Promise((resolve) => setTimeout(resolve, ms));
6900
6679
  }
6901
- function agentLinkId(parts) {
6902
- return [parts.kind, parts.fromTable, parts.fromId, parts.toTable, parts.toId]
6903
- .join("__")
6904
- .replace(/[^A-Za-z0-9_:-]/g, "_")
6905
- .slice(0, 512);
6680
+ function cypherString(value) {
6681
+ return `'${String(value).replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
6682
+ }
6683
+ function cypherValue(value) {
6684
+ if (value === null || value === undefined)
6685
+ return "NULL";
6686
+ if (typeof value === "number")
6687
+ return Number.isFinite(value) ? String(value) : "NULL";
6688
+ if (typeof value === "boolean")
6689
+ return value ? "true" : "false";
6690
+ if (typeof value === "string")
6691
+ return cypherString(value);
6692
+ if (Array.isArray(value))
6693
+ return `[${value.map(cypherValue).join(", ")}]`;
6694
+ return cypherString(JSON.stringify(value));
6906
6695
  }
6907
- async function runSurrealSql(sql) {
6908
- const cfg = requireSurrealConfig();
6909
- const credentials = Buffer.from(`${cfg.user}:${cfg.pass}`).toString("base64");
6910
- const response = await fetch(`${cfg.endpoint.replace(/\/$/, "")}/sql`, {
6696
+ function cypherProps(props) {
6697
+ return `{${Object.entries(props)
6698
+ .filter(([, value]) => value !== undefined)
6699
+ .map(([key, value]) => `${key}: ${cypherValue(value)}`)
6700
+ .join(", ")}}`;
6701
+ }
6702
+ async function requestFalkorToken(config, attempt) {
6703
+ if (!config.password) {
6704
+ throw new Error("FALKOR_PASS is required when FALKOR_TOKEN is not set.");
6705
+ }
6706
+ const tokenName = [
6707
+ "lifeos-mcp",
6708
+ attempt === 0 ? "graph" : "retry",
6709
+ Date.now(),
6710
+ Math.random().toString(36).slice(2, 10),
6711
+ ].join("-");
6712
+ const response = await fetch(`${config.endpoint.replace(/\/$/, "")}/api/auth/tokens/credentials`, {
6911
6713
  method: "POST",
6714
+ headers: { "Content-Type": "application/json" },
6715
+ body: JSON.stringify({
6716
+ username: config.username,
6717
+ password: config.password,
6718
+ host: config.host,
6719
+ port: config.port,
6720
+ tls: config.tls,
6721
+ name: tokenName,
6722
+ ttlSeconds: 3600,
6723
+ }),
6724
+ });
6725
+ const text = await response.text();
6726
+ if (!response.ok) {
6727
+ throw new Error(`FalkorDB auth failed (${response.status}): ${text}`);
6728
+ }
6729
+ const parsed = JSON.parse(text);
6730
+ if (!parsed.token) {
6731
+ throw new Error(`FalkorDB auth returned no token: ${text}`);
6732
+ }
6733
+ return parsed.token;
6734
+ }
6735
+ async function getFalkorToken(config) {
6736
+ if (config.token)
6737
+ return config.token;
6738
+ let lastError;
6739
+ for (let attempt = 0; attempt < 3; attempt += 1) {
6740
+ try {
6741
+ return await requestFalkorToken(config, attempt);
6742
+ }
6743
+ catch (error) {
6744
+ lastError = error;
6745
+ if (attempt < 2)
6746
+ await sleep(250 * (attempt + 1));
6747
+ }
6748
+ }
6749
+ throw lastError;
6750
+ }
6751
+ function parseFalkorEventStream(text) {
6752
+ const data = text
6753
+ .split(/\r?\n/)
6754
+ .filter((line) => line.startsWith("data:"))
6755
+ .map((line) => line.slice(5).trimStart())
6756
+ .join("\n")
6757
+ .trim();
6758
+ if (!data)
6759
+ return { data: [], metadata: [] };
6760
+ return JSON.parse(data);
6761
+ }
6762
+ async function runFalkorQuery(query) {
6763
+ const config = requireFalkorConfig();
6764
+ const token = await getFalkorToken(config);
6765
+ const url = `${config.endpoint.replace(/\/$/, "")}/api/graph/${encodeURIComponent(config.graph)}?query=${encodeURIComponent(query)}&timeout=30000`;
6766
+ const response = await fetch(url, {
6767
+ method: "GET",
6912
6768
  headers: {
6913
- Authorization: `Basic ${credentials}`,
6914
- "Content-Type": "text/plain",
6915
- Accept: "application/json",
6916
- "Surreal-NS": cfg.ns,
6917
- "Surreal-DB": cfg.db,
6769
+ Authorization: `Bearer ${token}`,
6770
+ Accept: "text/event-stream",
6918
6771
  },
6919
- body: sql,
6920
6772
  });
6921
6773
  const text = await response.text();
6922
6774
  if (!response.ok) {
6923
- throw new Error(`SurrealDB SQL failed (${response.status}): ${text}`);
6775
+ throw new Error(`FalkorDB query failed (${response.status}): ${text}`);
6924
6776
  }
6925
- const parsed = JSON.parse(text);
6926
- const failed = parsed.find((entry) => entry.status !== "OK");
6927
- if (failed) {
6928
- throw new Error(`SurrealDB SQL failed: ${JSON.stringify(failed, null, 2)}`);
6777
+ return parseFalkorEventStream(text);
6778
+ }
6779
+ function assertReadOnlyFalkorCypher(query) {
6780
+ const stripped = stripSqlCommentsAndStrings(query).trim();
6781
+ if (!stripped)
6782
+ throw new Error("FalkorDB Cypher query is empty.");
6783
+ if (stripped.includes(";")) {
6784
+ throw new Error("falkor_graph_query allows one Cypher statement at a time.");
6785
+ }
6786
+ const upper = stripped.toUpperCase();
6787
+ const startsReadOnly = upper.startsWith("MATCH ") ||
6788
+ upper.startsWith("WITH ") ||
6789
+ upper.startsWith("RETURN ") ||
6790
+ upper.startsWith("EXPLAIN ") ||
6791
+ upper.startsWith("PROFILE ");
6792
+ if (!startsReadOnly) {
6793
+ throw new Error("falkor_graph_query only allows read-only Cypher. Start with MATCH, WITH, RETURN, EXPLAIN, or PROFILE.");
6794
+ }
6795
+ for (const keyword of FALKOR_MUTATING_KEYWORDS) {
6796
+ if (new RegExp(`\\b${keyword}\\b`, "i").test(stripped)) {
6797
+ throw new Error(`falkor_graph_query blocked mutating keyword ${keyword}. Use falkor_graph_link for guarded relationship writes.`);
6798
+ }
6799
+ }
6800
+ const isAggregateCount = /\bCOUNT\s*\(/i.test(stripped);
6801
+ if ((upper.startsWith("MATCH ") || upper.startsWith("WITH ")) &&
6802
+ !/\bLIMIT\b/i.test(stripped) &&
6803
+ !isAggregateCount) {
6804
+ throw new Error("MATCH/WITH queries must include LIMIT unless they are aggregate count queries.");
6929
6805
  }
6930
- return parsed;
6931
6806
  }
6932
- function surrealGraphSchema() {
6807
+ function capFalkorResult(result, maxRows) {
6808
+ const rows = result.data ?? [];
6809
+ if (rows.length <= maxRows)
6810
+ return result;
6933
6811
  return {
6934
- configured: Boolean(SURREAL_ENDPOINT && SURREAL_USER && SURREAL_PASS),
6935
- endpointConfigured: Boolean(SURREAL_ENDPOINT),
6936
- namespace: SURREAL_NS,
6937
- database: SURREAL_DB,
6938
- nodeTablePrefixes: SURREAL_TABLE_PREFIXES,
6939
- relationTables: SURREAL_RELATION_TABLES,
6940
- agentWritableRelationTable: SURREAL_AGENT_LINK_TABLE,
6812
+ ...result,
6813
+ data: rows.slice(0, maxRows),
6814
+ detail: `${rows.length - maxRows} additional rows omitted by lifeos-mcp maxRows=${maxRows}`,
6815
+ };
6816
+ }
6817
+ function assertFalkorIdentifier(value, label) {
6818
+ if (typeof value !== "string" || !/^[A-Za-z][A-Za-z0-9_]*$/.test(value)) {
6819
+ throw new Error(`${label} must be a simple Falkor identifier.`);
6820
+ }
6821
+ return value;
6822
+ }
6823
+ function assertFalkorLabel(value, label) {
6824
+ const nodeLabel = assertFalkorIdentifier(value, label);
6825
+ if (!FALKOR_NODE_LABELS.includes(nodeLabel)) {
6826
+ throw new Error(`${label} must be one of: ${FALKOR_NODE_LABELS.join(", ")}`);
6827
+ }
6828
+ return nodeLabel;
6829
+ }
6830
+ function falkorLinkId(parts) {
6831
+ return [parts.kind, parts.fromLabel, parts.fromId, parts.toLabel, parts.toId]
6832
+ .join("__")
6833
+ .replace(/[^A-Za-z0-9_:-]/g, "_")
6834
+ .slice(0, 512);
6835
+ }
6836
+ function falkorGraphSchema() {
6837
+ return {
6838
+ configured: Boolean(FALKOR_TOKEN || FALKOR_PASS),
6839
+ endpointConfigured: Boolean(FALKOR_BROWSER_ENDPOINT),
6840
+ graph: FALKOR_GRAPH,
6841
+ nodeLabels: FALKOR_NODE_LABELS,
6842
+ inferredRelationships: FALKOR_INFERRED_RELATIONSHIPS,
6843
+ agentWritableRelationshipType: FALKOR_AGENT_RELATIONSHIP,
6941
6844
  rules: [
6942
- "Use surreal_graph_query for read-only SELECT, INFO, and EXPLAIN statements.",
6943
- "SELECT queries must include LIMIT unless they are aggregate count queries.",
6944
- "Use surreal_graph_link for agent-created relationships; it writes only to lifeos_agent_links.",
6945
- "Do not mutate Convex-synced entity tables from SurrealDB. Convex remains canonical.",
6845
+ "Use falkor_graph_query for read-only Cypher only.",
6846
+ "MATCH/WITH queries must include LIMIT unless they are aggregate count queries.",
6847
+ "Use falkor_graph_link for agent-created relationships; it writes only AGENT_LINK edges.",
6848
+ "Do not mutate Convex-synced nodes or inferred PPV relationships from FalkorDB. Convex remains canonical.",
6946
6849
  "Every agent link must include a reason and confidence.",
6947
6850
  ],
6948
6851
  examples: {
6949
- activePpvProjects: 'SELECT id, title, ->ppv_has_pillar->ppv1_pillars.{id, name, ->ppv_pillar_supports_project->lifeos_pmProjects.{id, name, status}} AS pillars FROM ppv1_visions WHERE status = "in_progress" LIMIT 1;',
6950
- projectInboundLinks: "SELECT <-ppv_pillar_supports_project<-ppv1_pillars.{id, name, purpose} AS supportedByPillars FROM lifeos_pmProjects:`PROJECT_ID` LIMIT 1;",
6951
- agentLinks: "SELECT id, in, out, kind, reason, confidence, createdBy, createdAt FROM lifeos_agent_links LIMIT 25;",
6852
+ ppvOverview: "MATCH (v:PpvVision)-[:HAS_PILLAR]->(p:PpvPillar)-[:PILLAR_SUPPORTS_PROJECT]->(project:PpvProject) RETURN v.convexId, v.title, p.convexId, p.title, project.convexId, project.title LIMIT 25",
6853
+ projectInboundLinks: "MATCH (pillar:PpvPillar)-[:PILLAR_SUPPORTS_PROJECT]->(project:PpvProject {convexId: 'PROJECT_ID'}) RETURN pillar.convexId, pillar.title, project.title LIMIT 10",
6854
+ agentLinks: "MATCH (a)-[r:AGENT_LINK]->(b) RETURN a.convexId, type(r), r.kind, r.reason, r.confidence, b.convexId LIMIT 25",
6952
6855
  },
6953
6856
  };
6954
6857
  }
6955
- async function surrealGraphQuery(args) {
6858
+ async function falkorGraphQuery(args) {
6956
6859
  const query = args.query;
6957
6860
  if (typeof query !== "string") {
6958
6861
  throw new Error("query is required.");
6959
6862
  }
6960
- assertReadOnlySurrealSql(query);
6961
- const results = await runSurrealSql(query);
6962
- return capSurrealResults(results, clampMaxRows(args.maxRows));
6863
+ assertReadOnlyFalkorCypher(query);
6864
+ return capFalkorResult(await runFalkorQuery(query), clampMaxRows(args.maxRows));
6963
6865
  }
6964
- async function surrealGraphLink(args) {
6965
- const fromTable = assertLifeOsNodeTable(args.fromTable, "fromTable");
6866
+ async function falkorGraphLink(args) {
6867
+ const fromLabel = assertFalkorLabel(args.fromLabel, "fromLabel");
6966
6868
  const fromId = assertRecordId(args.fromId, "fromId");
6967
- const toTable = assertLifeOsNodeTable(args.toTable, "toTable");
6869
+ const toLabel = assertFalkorLabel(args.toLabel, "toLabel");
6968
6870
  const toId = assertRecordId(args.toId, "toId");
6969
6871
  const kind = assertRelationKind(args.kind);
6970
6872
  const reason = assertRecordId(args.reason, "reason");
@@ -6980,8 +6882,9 @@ async function surrealGraphLink(args) {
6980
6882
  ? args.metadata
6981
6883
  : {};
6982
6884
  const now = new Date().toISOString();
6983
- const linkId = agentLinkId({ fromTable, fromId, toTable, toId, kind });
6984
- const content = {
6885
+ const linkId = falkorLinkId({ fromLabel, fromId, toLabel, toId, kind });
6886
+ const props = cypherProps({
6887
+ linkId,
6985
6888
  kind,
6986
6889
  reason,
6987
6890
  confidence,
@@ -6989,41 +6892,35 @@ async function surrealGraphLink(args) {
6989
6892
  sourceSystem: "agent",
6990
6893
  sourceUserId: USER_ID,
6991
6894
  userId: USER_ID,
6992
- sourceTable: fromTable,
6993
- targetTable: toTable,
6994
- metadata,
6895
+ metadataJson: JSON.stringify(metadata),
6995
6896
  createdAt: now,
6996
6897
  updatedAt: now,
6997
- };
6998
- const sql = [
6999
- `DEFINE TABLE IF NOT EXISTS ${SURREAL_AGENT_LINK_TABLE} TYPE RELATION SCHEMALESS;`,
7000
- `DEFINE INDEX IF NOT EXISTS ${SURREAL_AGENT_LINK_TABLE}_kind ON TABLE ${SURREAL_AGENT_LINK_TABLE} COLUMNS kind;`,
7001
- `DEFINE INDEX IF NOT EXISTS ${SURREAL_AGENT_LINK_TABLE}_createdBy ON TABLE ${SURREAL_AGENT_LINK_TABLE} COLUMNS createdBy;`,
7002
- `DELETE ${SURREAL_AGENT_LINK_TABLE}:\`${escapeSurrealRecordId(linkId)}\`;`,
7003
- `RELATE ${surrealThing(fromTable, fromId)}->${SURREAL_AGENT_LINK_TABLE}:\`${escapeSurrealRecordId(linkId)}\`->${surrealThing(toTable, toId)} CONTENT ${JSON.stringify(content)};`,
7004
- ].join("\n");
7005
- const results = await runSurrealSql(sql);
6898
+ });
6899
+ await runFalkorQuery(`MATCH (a:${fromLabel} {convexId: ${cypherString(fromId)}})-[r:${FALKOR_AGENT_RELATIONSHIP} {linkId: ${cypherString(linkId)}}]->(b:${toLabel} {convexId: ${cypherString(toId)}}) DELETE r`);
6900
+ const result = await runFalkorQuery(`MATCH (a:${fromLabel} {convexId: ${cypherString(fromId)}}), (b:${toLabel} {convexId: ${cypherString(toId)}}) CREATE (a)-[r:${FALKOR_AGENT_RELATIONSHIP} ${props}]->(b) RETURN r.linkId AS linkId, r.kind AS kind, r.reason AS reason, r.confidence AS confidence`);
7006
6901
  return {
7007
6902
  linked: true,
7008
- relationId: `${SURREAL_AGENT_LINK_TABLE}:${linkId}`,
7009
- from: `${fromTable}:${fromId}`,
7010
- to: `${toTable}:${toId}`,
6903
+ relationType: FALKOR_AGENT_RELATIONSHIP,
6904
+ relationId: linkId,
6905
+ from: `${fromLabel}:${fromId}`,
6906
+ to: `${toLabel}:${toId}`,
7011
6907
  kind,
7012
- results,
6908
+ result,
7013
6909
  };
7014
6910
  }
7015
- async function surrealGraphUnlink(args) {
7016
- const fromTable = assertLifeOsNodeTable(args.fromTable, "fromTable");
6911
+ async function falkorGraphUnlink(args) {
6912
+ const fromLabel = assertFalkorLabel(args.fromLabel, "fromLabel");
7017
6913
  const fromId = assertRecordId(args.fromId, "fromId");
7018
- const toTable = assertLifeOsNodeTable(args.toTable, "toTable");
6914
+ const toLabel = assertFalkorLabel(args.toLabel, "toLabel");
7019
6915
  const toId = assertRecordId(args.toId, "toId");
7020
6916
  const kind = assertRelationKind(args.kind);
7021
- const linkId = agentLinkId({ fromTable, fromId, toTable, toId, kind });
7022
- const results = await runSurrealSql(`DELETE ${SURREAL_AGENT_LINK_TABLE}:\`${escapeSurrealRecordId(linkId)}\`;`);
6917
+ const linkId = falkorLinkId({ fromLabel, fromId, toLabel, toId, kind });
6918
+ const result = await runFalkorQuery(`MATCH (a:${fromLabel} {convexId: ${cypherString(fromId)}})-[r:${FALKOR_AGENT_RELATIONSHIP} {linkId: ${cypherString(linkId)}}]->(b:${toLabel} {convexId: ${cypherString(toId)}}) DELETE r`);
7023
6919
  return {
7024
6920
  unlinked: true,
7025
- relationId: `${SURREAL_AGENT_LINK_TABLE}:${linkId}`,
7026
- results,
6921
+ relationType: FALKOR_AGENT_RELATIONSHIP,
6922
+ relationId: linkId,
6923
+ result,
7027
6924
  };
7028
6925
  }
7029
6926
  const STATIC_RESOURCES = [
@@ -7408,18 +7305,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
7408
7305
  ],
7409
7306
  };
7410
7307
  }
7411
- if (name === "surreal_graph_schema") {
7308
+ if (name === "falkor_graph_schema") {
7412
7309
  return {
7413
7310
  content: [
7414
7311
  {
7415
7312
  type: "text",
7416
- text: JSON.stringify(surrealGraphSchema(), null, 2),
7313
+ text: JSON.stringify(falkorGraphSchema(), null, 2),
7417
7314
  },
7418
7315
  ],
7419
7316
  };
7420
7317
  }
7421
- if (name === "surreal_graph_query") {
7422
- const result = await surrealGraphQuery(args || {});
7318
+ if (name === "falkor_graph_query") {
7319
+ const result = await falkorGraphQuery(args || {});
7423
7320
  return {
7424
7321
  content: [
7425
7322
  {
@@ -7429,8 +7326,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
7429
7326
  ],
7430
7327
  };
7431
7328
  }
7432
- if (name === "surreal_graph_link") {
7433
- const result = await surrealGraphLink(args || {});
7329
+ if (name === "falkor_graph_link") {
7330
+ const result = await falkorGraphLink(args || {});
7434
7331
  return {
7435
7332
  content: [
7436
7333
  {
@@ -7440,8 +7337,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
7440
7337
  ],
7441
7338
  };
7442
7339
  }
7443
- if (name === "surreal_graph_unlink") {
7444
- const result = await surrealGraphUnlink(args || {});
7340
+ if (name === "falkor_graph_unlink") {
7341
+ const result = await falkorGraphUnlink(args || {});
7445
7342
  return {
7446
7343
  content: [
7447
7344
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starascendin/lifeos-mcp",
3
- "version": "0.7.67",
3
+ "version": "0.7.68",
4
4
  "description": "MCP server for LifeOS Project Management - manage projects, tasks, notes, and contacts via AI assistants",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",