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/README.md +144 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +66 -27
- package/dist/index.js.map +1 -1
- package/dist/mcp-client.d.ts +11 -0
- package/dist/mcp-client.js +9 -1
- package/dist/mcp-client.js.map +1 -1
- package/dist/tools.d.ts +23 -16
- package/dist/tools.js +139 -97
- package/dist/tools.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/index.ts +83 -32
- package/src/mcp-client.ts +19 -1
- package/src/tools.ts +160 -109
package/dist/tools.js
CHANGED
|
@@ -1,25 +1,108 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* NeuralMemory tool
|
|
2
|
+
* NeuralMemory dynamic tool proxy for OpenClaw.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
106
|
-
|
|
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.
|
|
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
|
|
179
|
-
|
|
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
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/openclaw.plugin.json
CHANGED
|
@@ -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
|
+
"version": "1.7.0",
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"jsonSchema": {
|
|
9
9
|
"type": "object",
|
package/package.json
CHANGED
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
|
-
*
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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:
|
|
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},
|
|
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.
|
|
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> = {},
|