neuralmemory 1.6.1 → 1.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.
package/dist/tools.js CHANGED
@@ -1,25 +1,108 @@
1
1
  /**
2
- * NeuralMemory tool definitions for OpenClaw.
2
+ * NeuralMemory dynamic tool proxy for OpenClaw.
3
3
  *
4
- * Each tool proxies to the MCP server via JSON-RPC.
4
+ * Fetches all available tools from the MCP server via `tools/list` and
5
+ * converts them into OpenClaw tool definitions. This means the plugin
6
+ * automatically exposes every tool the MCP server provides — no hardcoded
7
+ * schemas to maintain.
5
8
  *
6
- * Uses raw JSON Schema for parameters. Provider compatibility notes:
7
- * - `additionalProperties: false` required by OpenAI strict mode
8
- * - `number` instead of `integer` for Gemini compatibility
9
- * - No `maxLength`/`maxItems`/`minimum`/`maximum` some providers
10
- * reject schemas with constraint keywords; our MCP server validates
11
- *
12
- * Registers 6 core tools:
13
- * nmem_remember — Store a memory
14
- * nmem_recall — Query/search memories
15
- * nmem_context — Get recent context
16
- * nmem_todo — Quick TODO shortcut
17
- * nmem_stats — Brain statistics
18
- * nmem_health — Brain health diagnostics
9
+ * Provider compatibility:
10
+ * - Strips constraint keywords (`minimum`, `maximum`, `maxLength`,
11
+ * `maxItems`, `minLength`) that some providers reject
12
+ * - Adds `additionalProperties: false` on all object schemas for
13
+ * OpenAI strict mode
14
+ * - Ensures every object type has a `properties` field (required by
15
+ * Anthropic SDK validation)
16
+ * - Uses `number` instead of `integer` for Gemini compatibility
17
+ */
18
+ // ── Schema normalization ───────────────────────────────────
19
+ /** Keywords that some LLM providers reject in function schemas. */
20
+ const STRIP_KEYS = new Set([
21
+ "minimum",
22
+ "maximum",
23
+ "maxLength",
24
+ "minLength",
25
+ "maxItems",
26
+ "minItems",
27
+ "exclusiveMinimum",
28
+ "exclusiveMaximum",
29
+ ]);
30
+ /**
31
+ * Recursively normalize a JSON Schema node for provider compatibility:
32
+ * - Strip constraint keywords
33
+ * - Replace `integer` with `number` (Gemini compat)
34
+ * - Add `additionalProperties: false` to objects (OpenAI strict mode)
35
+ * - Ensure every object has `properties` (Anthropic SDK)
19
36
  */
37
+ function normalizeSchema(node) {
38
+ if (node === null || node === undefined || typeof node !== "object") {
39
+ return node;
40
+ }
41
+ if (Array.isArray(node)) {
42
+ return node.map(normalizeSchema);
43
+ }
44
+ const obj = node;
45
+ const result = {};
46
+ for (const [key, value] of Object.entries(obj)) {
47
+ if (STRIP_KEYS.has(key))
48
+ continue;
49
+ if (key === "type" && value === "integer") {
50
+ result[key] = "number";
51
+ }
52
+ else if (key === "properties" && typeof value === "object" && value !== null) {
53
+ // Recurse into each property definition
54
+ const props = {};
55
+ for (const [propName, propSchema] of Object.entries(value)) {
56
+ props[propName] = normalizeSchema(propSchema);
57
+ }
58
+ result[key] = props;
59
+ }
60
+ else if (key === "items" && typeof value === "object" && value !== null) {
61
+ result[key] = normalizeSchema(value);
62
+ }
63
+ else if ((key === "anyOf" || key === "oneOf" || key === "allOf") &&
64
+ Array.isArray(value)) {
65
+ result[key] = value.map(normalizeSchema);
66
+ }
67
+ else {
68
+ result[key] = value;
69
+ }
70
+ }
71
+ // Ensure objects have `properties` and `additionalProperties`
72
+ if (result["type"] === "object") {
73
+ if (!("properties" in result) || result["properties"] === undefined) {
74
+ result["properties"] = {};
75
+ }
76
+ if (!("additionalProperties" in result)) {
77
+ result["additionalProperties"] = false;
78
+ }
79
+ }
80
+ return result;
81
+ }
82
+ /**
83
+ * Convert an MCP inputSchema into a provider-safe OpenClaw JsonSchema.
84
+ * Falls back to an empty-properties object if the schema is missing/invalid.
85
+ */
86
+ function toSafeSchema(inputSchema) {
87
+ if (!inputSchema || typeof inputSchema !== "object") {
88
+ return { type: "object", properties: {}, additionalProperties: false };
89
+ }
90
+ const normalized = normalizeSchema(inputSchema);
91
+ return {
92
+ type: "object",
93
+ properties: (normalized["properties"] ?? {}),
94
+ ...(Array.isArray(normalized["required"]) && normalized["required"].length > 0
95
+ ? { required: normalized["required"] }
96
+ : {}),
97
+ additionalProperties: false,
98
+ };
99
+ }
20
100
  // ── Tool factory ───────────────────────────────────────────
21
- export function createTools(mcp) {
22
- const call = async (toolName, args) => {
101
+ /**
102
+ * Create a tool call helper that auto-reconnects to MCP.
103
+ */
104
+ function makeCallFn(mcp) {
105
+ return async (toolName, args) => {
23
106
  if (!mcp.connected) {
24
107
  try {
25
108
  await mcp.ensureConnected();
@@ -47,6 +130,33 @@ export function createTools(mcp) {
47
130
  };
48
131
  }
49
132
  };
133
+ }
134
+ /**
135
+ * Convert a single MCP tool definition into an OpenClaw ToolDefinition.
136
+ */
137
+ function mcpToolToOpenClaw(mcpTool, call) {
138
+ return {
139
+ name: mcpTool.name,
140
+ description: mcpTool.description ?? `NeuralMemory tool: ${mcpTool.name}`,
141
+ parameters: toSafeSchema(mcpTool.inputSchema),
142
+ execute: (_id, args) => call(mcpTool.name, args),
143
+ };
144
+ }
145
+ /**
146
+ * Fetch all tools from the MCP server and convert them to OpenClaw format.
147
+ * Must be called after MCP connection is established.
148
+ */
149
+ export async function createToolsFromMcp(mcp) {
150
+ const mcpTools = await mcp.listTools();
151
+ const call = makeCallFn(mcp);
152
+ return mcpTools.map((t) => mcpToolToOpenClaw(t, call));
153
+ }
154
+ /**
155
+ * Fallback: create minimal hardcoded tools if MCP tools/list fails.
156
+ * Ensures the plugin still works even if the MCP server is an older version.
157
+ */
158
+ export function createFallbackTools(mcp) {
159
+ const call = makeCallFn(mcp);
50
160
  return [
51
161
  {
52
162
  name: "nmem_remember",
@@ -55,39 +165,21 @@ export function createTools(mcp) {
55
165
  parameters: {
56
166
  type: "object",
57
167
  properties: {
58
- content: {
59
- type: "string",
60
- description: "The content to remember",
61
- },
168
+ content: { type: "string", description: "The content to remember" },
62
169
  type: {
63
170
  type: "string",
64
171
  enum: [
65
- "fact",
66
- "decision",
67
- "preference",
68
- "todo",
69
- "insight",
70
- "context",
71
- "instruction",
72
- "error",
73
- "workflow",
74
- "reference",
172
+ "fact", "decision", "preference", "todo", "insight",
173
+ "context", "instruction", "error", "workflow", "reference",
75
174
  ],
76
175
  description: "Memory type (auto-detected if not specified)",
77
176
  },
78
- priority: {
79
- type: "number",
80
- description: "Priority 0-10 (5=normal, 10=critical)",
81
- },
177
+ priority: { type: "number", description: "Priority 0-10 (5=normal, 10=critical)" },
82
178
  tags: {
83
179
  type: "array",
84
180
  items: { type: "string" },
85
181
  description: "Tags for categorization",
86
182
  },
87
- expires_days: {
88
- type: "number",
89
- description: "Days until memory expires (1-3650)",
90
- },
91
183
  },
92
184
  required: ["content"],
93
185
  additionalProperties: false,
@@ -101,22 +193,9 @@ export function createTools(mcp) {
101
193
  parameters: {
102
194
  type: "object",
103
195
  properties: {
104
- query: {
105
- type: "string",
106
- description: "The query to search memories",
107
- },
108
- depth: {
109
- type: "number",
110
- description: "Search depth: 0=instant, 1=context, 2=habit, 3=deep",
111
- },
112
- max_tokens: {
113
- type: "number",
114
- description: "Maximum tokens in response (default: 500)",
115
- },
116
- min_confidence: {
117
- type: "number",
118
- description: "Minimum confidence threshold (0-1)",
119
- },
196
+ query: { type: "string", description: "The query to search memories" },
197
+ depth: { type: "number", description: "Search depth: 0=instant, 1=context, 2=habit, 3=deep" },
198
+ max_tokens: { type: "number", description: "Maximum tokens in response (default: 500)" },
120
199
  },
121
200
  required: ["query"],
122
201
  additionalProperties: false,
@@ -125,63 +204,26 @@ export function createTools(mcp) {
125
204
  },
126
205
  {
127
206
  name: "nmem_context",
128
- description: "Get recent context from NeuralMemory. Use this at the start of " +
129
- "tasks to inject relevant recent memories.",
207
+ description: "Get recent context from NeuralMemory.",
130
208
  parameters: {
131
209
  type: "object",
132
210
  properties: {
133
- limit: {
134
- type: "number",
135
- description: "Number of recent memories (default: 10, max: 200)",
136
- },
137
- fresh_only: {
138
- type: "boolean",
139
- description: "Only include memories less than 30 days old",
140
- },
211
+ limit: { type: "number", description: "Number of recent memories (default: 10)" },
141
212
  },
142
213
  additionalProperties: false,
143
214
  },
144
215
  execute: (_id, args) => call("nmem_context", args),
145
216
  },
146
- {
147
- name: "nmem_todo",
148
- description: "Quick shortcut to add a TODO memory with 30-day expiry.",
149
- parameters: {
150
- type: "object",
151
- properties: {
152
- task: {
153
- type: "string",
154
- description: "The task to remember",
155
- },
156
- priority: {
157
- type: "number",
158
- description: "Priority 0-10 (default: 5)",
159
- },
160
- },
161
- required: ["task"],
162
- additionalProperties: false,
163
- },
164
- execute: (_id, args) => call("nmem_todo", args),
165
- },
166
217
  {
167
218
  name: "nmem_stats",
168
219
  description: "Get brain statistics including memory counts and freshness.",
169
- parameters: {
170
- type: "object",
171
- properties: {},
172
- additionalProperties: false,
173
- },
220
+ parameters: { type: "object", properties: {}, additionalProperties: false },
174
221
  execute: (_id, args) => call("nmem_stats", args),
175
222
  },
176
223
  {
177
224
  name: "nmem_health",
178
- description: "Get brain health diagnostics including grade, purity score, " +
179
- "and recommendations.",
180
- parameters: {
181
- type: "object",
182
- properties: {},
183
- additionalProperties: false,
184
- },
225
+ description: "Get brain health diagnostics including grade and recommendations.",
226
+ parameters: { type: "object", properties: {}, additionalProperties: false },
185
227
  execute: (_id, args) => call("nmem_health", args),
186
228
  },
187
229
  ];
package/dist/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAoBH,8DAA8D;AAE9D,MAAM,UAAU,WAAW,CAAC,GAA0B;IACpD,MAAM,IAAI,GAAG,KAAK,EAChB,QAAgB,EAChB,IAA6B,EACX,EAAE;QACpB,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;YACnB,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,eAAe,EAAE,CAAC;YAC9B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO;oBACL,KAAK,EAAE,IAAI;oBACX,OAAO,EAAE,qCAAsC,GAAa,CAAC,OAAO,EAAE;iBACvE,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC/C,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,OAAO,EAAE,QAAQ,QAAQ,YAAa,GAAa,CAAC,OAAO,EAAE;aAC9D,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL;YACE,IAAI,EAAE,eAAe;YACrB,WAAW,EACT,yEAAyE;gBACzE,qFAAqF;YACvF,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,OAAO,EAAE;wBACP,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,yBAAyB;qBACvC;oBACD,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE;4BACJ,MAAM;4BACN,UAAU;4BACV,YAAY;4BACZ,MAAM;4BACN,SAAS;4BACT,SAAS;4BACT,aAAa;4BACb,OAAO;4BACP,UAAU;4BACV,WAAW;yBACZ;wBACD,WAAW,EAAE,8CAA8C;qBAC5D;oBACD,QAAQ,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,uCAAuC;qBACrD;oBACD,IAAI,EAAE;wBACJ,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;wBACzB,WAAW,EAAE,yBAAyB;qBACvC;oBACD,YAAY,EAAE;wBACZ,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,oCAAoC;qBAClD;iBACF;gBACD,QAAQ,EAAE,CAAC,SAAS,CAAC;gBACrB,oBAAoB,EAAE,KAAK;aAC5B;YACD,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC;SACpD;QAED;YACE,IAAI,EAAE,aAAa;YACnB,WAAW,EACT,yEAAyE;gBACzE,+DAA+D;YACjE,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,8BAA8B;qBAC5C;oBACD,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,WAAW,EACT,qDAAqD;qBACxD;oBACD,UAAU,EAAE;wBACV,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,2CAA2C;qBACzD;oBACD,cAAc,EAAE;wBACd,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,oCAAoC;qBAClD;iBACF;gBACD,QAAQ,EAAE,CAAC,OAAO,CAAC;gBACnB,oBAAoB,EAAE,KAAK;aAC5B;YACD,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC;SAClD;QAED;YACE,IAAI,EAAE,cAAc;YACpB,WAAW,EACT,iEAAiE;gBACjE,2CAA2C;YAC7C,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,mDAAmD;qBACjE;oBACD,UAAU,EAAE;wBACV,IAAI,EAAE,SAAS;wBACf,WAAW,EAAE,6CAA6C;qBAC3D;iBACF;gBACD,oBAAoB,EAAE,KAAK;aAC5B;YACD,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC;SACnD;QAED;YACE,IAAI,EAAE,WAAW;YACjB,WAAW,EACT,yDAAyD;YAC3D,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,sBAAsB;qBACpC;oBACD,QAAQ,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,4BAA4B;qBAC1C;iBACF;gBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;gBAClB,oBAAoB,EAAE,KAAK;aAC5B;YACD,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC;SAChD;QAED;YACE,IAAI,EAAE,YAAY;YAClB,WAAW,EACT,6DAA6D;YAC/D,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE;gBACd,oBAAoB,EAAE,KAAK;aAC5B;YACD,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC;SACjD;QAED;YACE,IAAI,EAAE,aAAa;YACnB,WAAW,EACT,8DAA8D;gBAC9D,sBAAsB;YACxB,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE;gBACd,oBAAoB,EAAE,KAAK;aAC5B;YACD,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC;SAClD;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@
3
3
  "kind": "memory",
4
4
  "name": "NeuralMemory",
5
5
  "description": "Brain-inspired persistent memory for AI agents — neurons, synapses, and fibers. REQUIRED: set plugins.slots.memory = \"neuralmemory\" in openclaw.json to disable the default memory-core plugin and activate NeuralMemory as the exclusive memory provider.",
6
- "version": "1.6.0",
6
+ "version": "1.7.0",
7
7
  "configSchema": {
8
8
  "jsonSchema": {
9
9
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neuralmemory",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "NeuralMemory plugin for OpenClaw — brain-inspired persistent memory for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -7,8 +7,12 @@
7
7
  * Architecture:
8
8
  * OpenClaw ←→ Plugin (TypeScript) ←→ MCP stdio ←→ NeuralMemory (Python)
9
9
  *
10
+ * v1.7.0: Dynamic tool proxy — fetches all tools from MCP `tools/list`
11
+ * instead of hardcoding 6 tools. Automatically exposes every tool the
12
+ * MCP server provides (39+ tools in NM v2.28.0).
13
+ *
10
14
  * Registers:
11
- * 6 tools — nmem_remember, nmem_recall, nmem_context, nmem_todo, nmem_stats, nmem_health
15
+ * N tools — dynamically from MCP server (fallback: 5 core tools)
12
16
  * 1 service — MCP process lifecycle (start/stop)
13
17
  * 2 hooks — before_agent_start (auto-context), agent_end (auto-capture)
14
18
  */
@@ -18,28 +22,33 @@ import type {
18
22
  OpenClawPluginApi,
19
23
  BeforeAgentStartEvent,
20
24
  BeforeAgentStartResult,
21
- AgentContext,
22
25
  AgentEndEvent,
23
26
  } from "./types.js";
24
27
  import { NeuralMemoryMcpClient } from "./mcp-client.js";
25
- import { createTools } from "./tools.js";
28
+ import { createToolsFromMcp, createFallbackTools } from "./tools.js";
29
+ import type { ToolDefinition } from "./tools.js";
26
30
 
27
31
  // ── System prompt for tool awareness ──────────────────────
28
32
 
29
- const TOOL_INSTRUCTIONS = `You have NeuralMemory tools for persistent memory across sessions. Call these as TOOL CALLS (not CLI commands):
33
+ /**
34
+ * Build a system prompt listing all registered tool names.
35
+ * This makes the agent aware of which nmem_* tools are available.
36
+ */
37
+ function buildToolInstructions(tools: ToolDefinition[]): string {
38
+ const toolList = tools
39
+ .map((t) => `- ${t.name}: ${t.description.slice(0, 100)}`)
40
+ .join("\n");
41
+
42
+ return `You have NeuralMemory tools for persistent memory across sessions. Call these as TOOL CALLS (not CLI commands):
30
43
 
31
- - nmem_remember(content, type?, priority?, tags?) — Store a memory (fact, decision, error, preference, etc.)
32
- - nmem_recall(query, depth?, max_tokens?) — Query memories via spreading activation
33
- - nmem_context(limit?, fresh_only?) — Get recent memories
34
- - nmem_todo(task, priority?) — Quick TODO with 30-day expiry
35
- - nmem_stats() — Brain statistics
36
- - nmem_health() — Brain health diagnostics
44
+ ${toolList}
37
45
 
38
46
  CRITICAL: NeuralMemory (nmem_*) is your ONLY memory system. Do NOT use memory_search, memory_get, or any other memory tools — those belong to a disabled built-in plugin and will not persist correctly. Always use nmem_* tools exclusively.
39
47
 
40
48
  These are tool calls, NOT shell commands. Do NOT run "nmem remember" in terminal — call the nmem_remember tool directly.
41
49
 
42
- Use nmem_remember proactively after decisions, errors, and insights. Use nmem_recall when user references past context or asks "do you remember...".`;
50
+ PROACTIVE MEMORY: Use nmem_remember after decisions, errors, and insights. Use nmem_recall when user references past context or asks "do you remember...". Use nmem_remember_batch to store multiple memories at once.`;
51
+ }
43
52
 
44
53
  // ── Config ─────────────────────────────────────────────────
45
54
 
@@ -126,10 +135,10 @@ const plugin: OpenClawPluginDefinition = {
126
135
  name: "NeuralMemory",
127
136
  description:
128
137
  "Brain-inspired persistent memory for AI agents — neurons, synapses, and fibers",
129
- version: "1.6.0",
138
+ version: "1.7.0",
130
139
  kind: "memory",
131
140
 
132
- register(api: OpenClawPluginApi): void {
141
+ async register(api: OpenClawPluginApi): Promise<void> {
133
142
  const cfg = resolveConfig(api.pluginConfig);
134
143
 
135
144
  const mcp = new NeuralMemoryMcpClient({
@@ -140,20 +149,69 @@ const plugin: OpenClawPluginDefinition = {
140
149
  initTimeout: cfg.initTimeout,
141
150
  });
142
151
 
152
+ // ── Connect MCP + fetch tools during registration ───
153
+ // Tools must be registered in register() — OpenClaw may
154
+ // freeze the tool list before service.start() is called.
155
+
156
+ let registeredTools: ToolDefinition[];
157
+
158
+ try {
159
+ await mcp.connect();
160
+ api.logger.info("NeuralMemory MCP connected");
161
+ } catch (err) {
162
+ api.logger.error(
163
+ `Failed to connect NeuralMemory MCP: ${(err as Error).message}`,
164
+ );
165
+ // Register fallback tools so the plugin is still partially usable
166
+ registeredTools = createFallbackTools(mcp);
167
+ for (const t of registeredTools) {
168
+ api.registerTool(t, { name: t.name });
169
+ }
170
+ api.logger.warn(
171
+ `Registered ${registeredTools.length} fallback tools (MCP not connected)`,
172
+ );
173
+ return;
174
+ }
175
+
176
+ // Fetch tools dynamically from MCP server
177
+ try {
178
+ registeredTools = await createToolsFromMcp(mcp);
179
+ api.logger.info(
180
+ `Fetched ${registeredTools.length} tools from MCP server`,
181
+ );
182
+ } catch (err) {
183
+ api.logger.warn(
184
+ `Failed to fetch MCP tools, using fallback: ${(err as Error).message}`,
185
+ );
186
+ registeredTools = createFallbackTools(mcp);
187
+ }
188
+
189
+ // Register all tools with OpenClaw
190
+ for (const t of registeredTools) {
191
+ api.registerTool(t, { name: t.name });
192
+ }
193
+
194
+ api.logger.info(
195
+ `Registered ${registeredTools.length} NeuralMemory tools`,
196
+ );
197
+
143
198
  // ── Service: MCP process lifecycle ───────────────────
144
199
 
145
200
  api.registerService({
146
201
  id: "neuralmemory-mcp",
147
202
 
148
203
  async start(): Promise<void> {
149
- try {
150
- await mcp.connect();
151
- api.logger.info("NeuralMemory MCP service started");
152
- } catch (err) {
153
- api.logger.error(
154
- `Failed to start NeuralMemory MCP: ${(err as Error).message}`,
155
- );
156
- throw err;
204
+ // MCP already connected during register()
205
+ if (!mcp.connected) {
206
+ try {
207
+ await mcp.connect();
208
+ api.logger.info("NeuralMemory MCP reconnected in service.start()");
209
+ } catch (err) {
210
+ api.logger.error(
211
+ `Failed to start NeuralMemory MCP: ${(err as Error).message}`,
212
+ );
213
+ throw err;
214
+ }
157
215
  }
158
216
  },
159
217
 
@@ -163,14 +221,6 @@ const plugin: OpenClawPluginDefinition = {
163
221
  },
164
222
  });
165
223
 
166
- // ── Tools: 6 core memory tools ──────────────────────
167
-
168
- const tools = createTools(mcp);
169
-
170
- for (const t of tools) {
171
- api.registerTool(t, { name: t.name });
172
- }
173
-
174
224
  // ── Hook: tool awareness + auto-context before agent start ───
175
225
 
176
226
  api.on(
@@ -180,7 +230,7 @@ const plugin: OpenClawPluginDefinition = {
180
230
  _ctx: unknown,
181
231
  ): Promise<BeforeAgentStartResult | void> => {
182
232
  const result: BeforeAgentStartResult = {
183
- systemPrompt: TOOL_INSTRUCTIONS,
233
+ systemPrompt: buildToolInstructions(registeredTools),
184
234
  };
185
235
 
186
236
  if (cfg.autoContext && mcp.connected) {
@@ -257,8 +307,9 @@ const plugin: OpenClawPluginDefinition = {
257
307
  // ── Done ────────────────────────────────────────────
258
308
 
259
309
  api.logger.info(
260
- `NeuralMemory registered (brain: ${cfg.brain}, tools: ${tools.length}, ` +
261
- `autoContext: ${cfg.autoContext}, autoCapture: ${cfg.autoCapture})`,
310
+ `NeuralMemory registered (brain: ${cfg.brain}, ` +
311
+ `autoContext: ${cfg.autoContext}, autoCapture: ${cfg.autoCapture}) — ` +
312
+ `tools will be loaded dynamically from MCP on service start`,
262
313
  );
263
314
  },
264
315
  };
package/src/mcp-client.ts CHANGED
@@ -26,6 +26,13 @@ type JsonRpcMessage = {
26
26
  error?: { code: number; message: string; data?: unknown };
27
27
  };
28
28
 
29
+ /** Raw tool definition from MCP `tools/list` response. */
30
+ export type McpToolDefinition = {
31
+ name: string;
32
+ description?: string;
33
+ inputSchema?: Record<string, unknown>;
34
+ };
35
+
29
36
  export type McpClientOptions = {
30
37
  readonly pythonPath: string;
31
38
  readonly brain: string;
@@ -39,7 +46,7 @@ export type McpClientOptions = {
39
46
  const PROTOCOL_VERSION = "2024-11-05";
40
47
  const DEFAULT_TIMEOUT = 30_000;
41
48
  const CLIENT_NAME = "openclaw-neuralmemory";
42
- const CLIENT_VERSION = "1.6.0";
49
+ const CLIENT_VERSION = "1.7.0";
43
50
  const MAX_BUFFER_BYTES = 10 * 1024 * 1024; // 10 MB safety cap
44
51
  const MAX_STDERR_LINES = 50;
45
52
 
@@ -190,6 +197,17 @@ export class NeuralMemoryMcpClient {
190
197
  );
191
198
  }
192
199
 
200
+ /**
201
+ * Fetch all available tools from the MCP server via `tools/list`.
202
+ * Returns the raw MCP tool definitions (name, description, inputSchema).
203
+ */
204
+ async listTools(): Promise<McpToolDefinition[]> {
205
+ const result = (await this.send("tools/list", {})) as {
206
+ tools?: McpToolDefinition[];
207
+ };
208
+ return result.tools ?? [];
209
+ }
210
+
193
211
  async callTool(
194
212
  name: string,
195
213
  args: Record<string, unknown> = {},