@intangle/mcp-server 2.5.3 → 2.5.5

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/index.js CHANGED
@@ -7,7 +7,7 @@ import { dirname, join } from "path";
7
7
  import { fileURLToPath } from "url";
8
8
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
9
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
- import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
10
+ import { CallToolRequestSchema, ErrorCode, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
11
11
  import { config } from "dotenv";
12
12
  import fetch from "node-fetch";
13
13
  import { TOOLS } from "./tool-definitions.js";
@@ -101,6 +101,13 @@ try {
101
101
  }
102
102
  return url.toString();
103
103
  }
104
+ function buildRemoteApiUrl() {
105
+ const url = new URL("/api/mcp-remote", API_BASE_URL);
106
+ if (VERCEL_BYPASS_TOKEN) {
107
+ url.searchParams.set("x-vercel-protection-bypass", VERCEL_BYPASS_TOKEN);
108
+ }
109
+ return url.toString();
110
+ }
104
111
  async function makeApiCall(endpoint, data, timeoutMs) {
105
112
  // Ensure we have client info before making requests
106
113
  ensureClientInfo();
@@ -160,6 +167,57 @@ try {
160
167
  clearTimeout(timeoutId);
161
168
  }
162
169
  }
170
+ async function makeRemoteRpcCall(method, params) {
171
+ ensureClientInfo();
172
+ const headers = {
173
+ "Content-Type": "application/json",
174
+ Authorization: `Bearer ${MCP_API_KEY}`,
175
+ "User-Agent": mcpClientName
176
+ ? `${mcpClientName}/${mcpClientVersion || "unknown"} (mcp-stdio)`
177
+ : "MCP-Client-Stdio/1.1.2 (mcp)",
178
+ };
179
+ if (mcpClientName) {
180
+ headers["X-MCP-Client-Name"] = mcpClientName;
181
+ }
182
+ if (mcpClientVersion) {
183
+ headers["X-MCP-Client-Version"] = mcpClientVersion;
184
+ }
185
+ if (VERCEL_BYPASS_TOKEN) {
186
+ headers["x-vercel-protection-bypass"] = VERCEL_BYPASS_TOKEN;
187
+ }
188
+ const requestId = Date.now();
189
+ const rpcBody = {
190
+ jsonrpc: "2.0",
191
+ id: requestId,
192
+ method,
193
+ params,
194
+ };
195
+ const response = await fetch(buildRemoteApiUrl(), {
196
+ method: "POST",
197
+ headers,
198
+ body: JSON.stringify(rpcBody),
199
+ });
200
+ const responseText = await response.text();
201
+ let responseJson = {};
202
+ try {
203
+ responseJson = responseText ? JSON.parse(responseText) : {};
204
+ }
205
+ catch {
206
+ throw new Error(`Failed to parse MCP remote ${method} response`);
207
+ }
208
+ if (!response.ok) {
209
+ const message = responseJson?.error?.message ||
210
+ responseJson?.message ||
211
+ `${response.status} ${response.statusText}`;
212
+ throw new Error(`MCP remote ${method} failed: ${message}`);
213
+ }
214
+ if (responseJson?.error) {
215
+ const code = responseJson.error.code;
216
+ const message = responseJson.error.message || "Unknown MCP remote error";
217
+ throw new Error(`MCP remote ${method} error (${code}): ${message}`);
218
+ }
219
+ return responseJson?.result ?? {};
220
+ }
163
221
  const server = new Server({
164
222
  name: "intangle-context",
165
223
  version: "1.0.0",
@@ -171,12 +229,33 @@ try {
171
229
  ],
172
230
  }, {
173
231
  capabilities: {
232
+ resources: {},
174
233
  tools: {},
175
234
  },
176
235
  });
177
236
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
178
237
  tools: TOOLS,
179
238
  }));
239
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
240
+ const result = await makeRemoteRpcCall("resources/list", {});
241
+ return {
242
+ resources: Array.isArray(result?.resources) ? result.resources : [],
243
+ nextCursor: typeof result?.nextCursor === "string" ? result.nextCursor : undefined,
244
+ };
245
+ });
246
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
247
+ const uri = request?.params?.uri;
248
+ if (!uri || typeof uri !== "string") {
249
+ throw new McpError(ErrorCode.InvalidParams, "uri is required for resources/read");
250
+ }
251
+ const result = await makeRemoteRpcCall("resources/read", { uri });
252
+ return {
253
+ contents: Array.isArray(result?.contents) ? result.contents : [],
254
+ };
255
+ });
256
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
257
+ resourceTemplates: [],
258
+ }));
180
259
  async function handleSearchContext(args) {
181
260
  const { space_id, query, topics } = args;
182
261
  // Require space_id
@@ -251,18 +330,15 @@ try {
251
330
  });
252
331
  }
253
332
  async function handleMessage(args) {
254
- if (!args.type || !args.content) {
255
- throw new Error("type and content are required");
333
+ if (!args.space_id || !args.content) {
334
+ throw new Error("space_id and content are required");
256
335
  }
257
- // Inject session_id from environment variable
258
- const sessionId = process.env.INTANGLE_SESSION_ID;
259
- if (!sessionId) {
260
- throw new Error("INTANGLE_SESSION_ID environment variable is required for the message tool");
261
- }
262
- return makeApiCall("message", {
263
- ...args,
264
- session_id: sessionId,
265
- });
336
+ const timeoutMs = typeof args.timeout_ms === "number" && Number.isFinite(args.timeout_ms)
337
+ ? Math.max(10_000, Math.min(300_000, Math.trunc(args.timeout_ms)))
338
+ : undefined;
339
+ // Give backend a little extra headroom beyond requested assistant timeout.
340
+ const requestTimeout = timeoutMs ? timeoutMs + 10_000 : undefined;
341
+ return makeApiCall("message", args, requestTimeout);
266
342
  }
267
343
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
268
344
  const { name, arguments: args } = request.params;
@@ -311,42 +311,51 @@ export const TOOLS = [
311
311
  },
312
312
  {
313
313
  name: "message",
314
- title: "Send Message",
315
- description: "Send a message to the Intangle assistant for user interaction. Use type 'approval' to request approval (blocks until user responds), 'question' to ask the user a question (blocks until answered), 'notification' for informational updates (returns immediately), 'completion' to signal task completion (returns immediately), or 'error' to report errors (returns immediately).",
314
+ title: "Message Assistant",
315
+ description: "Primary orchestration tool. Send a request to the Intangle assistant and wait for one assistant turn to complete. Returns assistant text plus continuity IDs (`session_id`, `conversation_id`) so callers can continue the same thread reliably across turns.",
316
316
  inputSchema: {
317
317
  type: "object",
318
318
  properties: {
319
- type: {
319
+ space_id: {
320
320
  type: "string",
321
- enum: ["approval", "question", "notification", "completion", "error"],
322
- description: "Message type. 'approval' and 'question' block until the user responds. 'notification', 'completion', and 'error' return immediately."
321
+ description: "Space to run the assistant turn in (use view_spaces first)."
323
322
  },
324
323
  content: {
325
324
  type: "string",
326
- description: "The message content to display to the user"
325
+ description: "User message content to send to the assistant."
327
326
  },
328
- metadata: {
329
- type: "object",
330
- description: "Optional context about the message",
331
- properties: {
332
- file: {
333
- type: "string",
334
- description: "Related file path"
335
- },
336
- action: {
337
- type: "string",
338
- description: "Action being performed"
339
- },
340
- details: {
341
- type: "string",
342
- description: "Additional details"
343
- }
344
- }
327
+ provider: {
328
+ type: "string",
329
+ description: "Optional provider alias (defaults to 'tan')."
330
+ },
331
+ conversation_id: {
332
+ type: "string",
333
+ description: "Optional existing conversation/chat summary ID to resume a specific conversation. If omitted, Intangle uses/creates the active conversation for this space."
334
+ },
335
+ session_id: {
336
+ type: "string",
337
+ description: "Optional runtime session ID to reuse across calls. Reuse the returned session_id for best continuity and lower warm-up overhead."
338
+ },
339
+ project_id: {
340
+ type: "string",
341
+ description: "Optional project scope for the assistant turn."
342
+ },
343
+ organization_id: {
344
+ type: "string",
345
+ description: "Optional Clerk org ID when the space belongs to an organization graph."
346
+ },
347
+ timeout_ms: {
348
+ type: "integer",
349
+ description: "Optional timeout in milliseconds (10,000 to 300,000; default 120,000)."
350
+ },
351
+ timezone: {
352
+ type: "string",
353
+ description: "Optional IANA timezone for assistant context."
345
354
  }
346
355
  },
347
- required: ["type", "content"]
356
+ required: ["space_id", "content"]
348
357
  }
349
- },
358
+ }
350
359
  // DISABLED: memory_action tool is broken and causing errors.
351
360
  // Pending OHM protocol fix. Use update_space, search, or fetch_items instead.
352
361
  // {
package/index.ts CHANGED
@@ -12,8 +12,11 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
12
12
  import {
13
13
  CallToolRequestSchema,
14
14
  ErrorCode,
15
+ ListResourceTemplatesRequestSchema,
16
+ ListResourcesRequestSchema,
15
17
  ListToolsRequestSchema,
16
18
  McpError,
19
+ ReadResourceRequestSchema,
17
20
  } from "@modelcontextprotocol/sdk/types.js"
18
21
  import { config } from "dotenv"
19
22
  import fetch from "node-fetch"
@@ -134,6 +137,16 @@ try {
134
137
  return url.toString()
135
138
  }
136
139
 
140
+ function buildRemoteApiUrl() {
141
+ const url = new URL("/api/mcp-remote", API_BASE_URL)
142
+
143
+ if (VERCEL_BYPASS_TOKEN) {
144
+ url.searchParams.set("x-vercel-protection-bypass", VERCEL_BYPASS_TOKEN)
145
+ }
146
+
147
+ return url.toString()
148
+ }
149
+
137
150
  async function makeApiCall(endpoint: string, data: any, timeoutMs?: number) {
138
151
  // Ensure we have client info before making requests
139
152
  ensureClientInfo()
@@ -187,7 +200,9 @@ try {
187
200
  )
188
201
  }
189
202
 
190
- throw new Error(`API call failed: ${response.status} ${response.statusText}`)
203
+ throw new Error(
204
+ `API call failed: ${response.status} ${response.statusText}`
205
+ )
191
206
  }
192
207
 
193
208
  if (responseBody.trim() === "") {
@@ -207,6 +222,70 @@ try {
207
222
  }
208
223
  }
209
224
 
225
+ async function makeRemoteRpcCall(
226
+ method: string,
227
+ params: Record<string, unknown>
228
+ ) {
229
+ ensureClientInfo()
230
+
231
+ const headers: Record<string, string> = {
232
+ "Content-Type": "application/json",
233
+ Authorization: `Bearer ${MCP_API_KEY}`,
234
+ "User-Agent": mcpClientName
235
+ ? `${mcpClientName}/${mcpClientVersion || "unknown"} (mcp-stdio)`
236
+ : "MCP-Client-Stdio/1.1.2 (mcp)",
237
+ }
238
+
239
+ if (mcpClientName) {
240
+ headers["X-MCP-Client-Name"] = mcpClientName
241
+ }
242
+ if (mcpClientVersion) {
243
+ headers["X-MCP-Client-Version"] = mcpClientVersion
244
+ }
245
+ if (VERCEL_BYPASS_TOKEN) {
246
+ headers["x-vercel-protection-bypass"] = VERCEL_BYPASS_TOKEN
247
+ }
248
+
249
+ const requestId = Date.now()
250
+ const rpcBody = {
251
+ jsonrpc: "2.0",
252
+ id: requestId,
253
+ method,
254
+ params,
255
+ }
256
+
257
+ const response = await fetch(buildRemoteApiUrl(), {
258
+ method: "POST",
259
+ headers,
260
+ body: JSON.stringify(rpcBody),
261
+ })
262
+
263
+ const responseText = await response.text()
264
+ let responseJson: any = {}
265
+
266
+ try {
267
+ responseJson = responseText ? JSON.parse(responseText) : {}
268
+ } catch {
269
+ throw new Error(`Failed to parse MCP remote ${method} response`)
270
+ }
271
+
272
+ if (!response.ok) {
273
+ const message =
274
+ responseJson?.error?.message ||
275
+ responseJson?.message ||
276
+ `${response.status} ${response.statusText}`
277
+ throw new Error(`MCP remote ${method} failed: ${message}`)
278
+ }
279
+
280
+ if (responseJson?.error) {
281
+ const code = responseJson.error.code
282
+ const message = responseJson.error.message || "Unknown MCP remote error"
283
+ throw new Error(`MCP remote ${method} error (${code}): ${message}`)
284
+ }
285
+
286
+ return responseJson?.result ?? {}
287
+ }
288
+
210
289
  const server = new Server(
211
290
  {
212
291
  name: "intangle-context",
@@ -220,6 +299,7 @@ try {
220
299
  },
221
300
  {
222
301
  capabilities: {
302
+ resources: {},
223
303
  tools: {},
224
304
  },
225
305
  }
@@ -229,6 +309,34 @@ try {
229
309
  tools: TOOLS,
230
310
  }))
231
311
 
312
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
313
+ const result = await makeRemoteRpcCall("resources/list", {})
314
+ return {
315
+ resources: Array.isArray(result?.resources) ? result.resources : [],
316
+ nextCursor:
317
+ typeof result?.nextCursor === "string" ? result.nextCursor : undefined,
318
+ }
319
+ })
320
+
321
+ server.setRequestHandler(ReadResourceRequestSchema, async (request: any) => {
322
+ const uri = request?.params?.uri
323
+ if (!uri || typeof uri !== "string") {
324
+ throw new McpError(
325
+ ErrorCode.InvalidParams,
326
+ "uri is required for resources/read"
327
+ )
328
+ }
329
+
330
+ const result = await makeRemoteRpcCall("resources/read", { uri })
331
+ return {
332
+ contents: Array.isArray(result?.contents) ? result.contents : [],
333
+ }
334
+ })
335
+
336
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
337
+ resourceTemplates: [],
338
+ }))
339
+
232
340
  async function handleSearchContext(args: any) {
233
341
  const { space_id, query, topics } = args as {
234
342
  space_id: string
@@ -278,11 +386,15 @@ try {
278
386
  }
279
387
 
280
388
  if (!project_id && !slug) {
281
- throw new Error("Either project_id or slug (with space_id) is required. Use view_projects to get valid IDs.")
389
+ throw new Error(
390
+ "Either project_id or slug (with space_id) is required. Use view_projects to get valid IDs."
391
+ )
282
392
  }
283
393
 
284
394
  if (slug && !space_id) {
285
- throw new Error("space_id is required when using slug. Use view_spaces to see available options.")
395
+ throw new Error(
396
+ "space_id is required when using slug. Use view_spaces to see available options."
397
+ )
286
398
  }
287
399
 
288
400
  return makeApiCall("view-project", { project_id, space_id, slug })
@@ -310,9 +422,7 @@ try {
310
422
  }
311
423
 
312
424
  if (!args.add && !args.update && !args.delete) {
313
- throw new Error(
314
- "At least one operation must be provided"
315
- )
425
+ throw new Error("At least one operation must be provided")
316
426
  }
317
427
 
318
428
  // Ensure all add items have a type field to prevent classification timeout
@@ -320,7 +430,9 @@ try {
320
430
  if (add?.items && Array.isArray(add.items)) {
321
431
  for (const item of add.items) {
322
432
  if (!item.type) {
323
- log(`WARNING: Item "${item.title}" missing type field, defaulting to "context" to prevent classification timeout`)
433
+ log(
434
+ `WARNING: Item "${item.title}" missing type field, defaulting to "context" to prevent classification timeout`
435
+ )
324
436
  item.type = "context"
325
437
  }
326
438
  }
@@ -337,22 +449,19 @@ try {
337
449
  }
338
450
 
339
451
  async function handleMessage(args: any) {
340
- if (!args.type || !args.content) {
341
- throw new Error("type and content are required")
452
+ if (!args.space_id || !args.content) {
453
+ throw new Error("space_id and content are required")
342
454
  }
343
455
 
344
- // Inject session_id from environment variable
345
- const sessionId = process.env.INTANGLE_SESSION_ID
346
- if (!sessionId) {
347
- throw new Error(
348
- "INTANGLE_SESSION_ID environment variable is required for the message tool"
349
- )
350
- }
456
+ const timeoutMs =
457
+ typeof args.timeout_ms === "number" && Number.isFinite(args.timeout_ms)
458
+ ? Math.max(10_000, Math.min(300_000, Math.trunc(args.timeout_ms)))
459
+ : undefined
351
460
 
352
- return makeApiCall("message", {
353
- ...args,
354
- session_id: sessionId,
355
- })
461
+ // Give backend a little extra headroom beyond requested assistant timeout.
462
+ const requestTimeout = timeoutMs ? timeoutMs + 10_000 : undefined
463
+
464
+ return makeApiCall("message", args, requestTimeout)
356
465
  }
357
466
 
358
467
  server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
package/package.json CHANGED
@@ -1,53 +1,53 @@
1
1
  {
2
- "name": "@intangle/mcp-server",
3
- "version": "2.5.3",
4
- "description": "Model Context Protocol server for Intangle - AI context that persists across conversations",
5
- "main": "dist/index.js",
6
- "type": "module",
7
- "scripts": {
8
- "start": "node dist/index.js",
9
- "dev": "tsx watch index.ts",
10
- "prebuild": "cp ../web/src/app/api/mcp-remote/tool-definitions.ts ./tool-definitions.ts",
11
- "build": "tsc",
12
- "lint": "biome check .",
13
- "lint:fix": "biome check --fix .",
14
- "prepublishOnly": "npm run build"
15
- },
16
- "bin": {
17
- "intangle-mcp": "dist/index.js"
18
- },
19
- "keywords": [
20
- "mcp",
21
- "model-context-protocol",
22
- "claude",
23
- "claude-code",
24
- "ai",
25
- "context",
26
- "knowledge-management",
27
- "intangle",
28
- "claude-desktop",
29
- "anthropic"
30
- ],
31
- "author": "Intangle",
32
- "license": "MIT",
33
- "repository": {
34
- "type": "git",
35
- "url": "git+https://github.com/intangle/mcp-server.git"
36
- },
37
- "homepage": "https://intangle.app",
38
- "engines": {
39
- "node": ">=18.0.0"
40
- },
41
- "dependencies": {
42
- "@modelcontextprotocol/sdk": "^1.0.0",
43
- "dotenv": "^17.2.0",
44
- "node-fetch": "^3.3.2"
45
- },
46
- "devDependencies": {
47
- "@biomejs/biome": "^1.9.4",
48
- "@types/node": "^22.0.0",
49
- "@types/node-fetch": "^2.6.12",
50
- "tsx": "^4.0.0",
51
- "typescript": "^5.0.0"
52
- }
2
+ "name": "@intangle/mcp-server",
3
+ "version": "2.5.5",
4
+ "description": "Model Context Protocol server for Intangle - AI context that persists across conversations",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "start": "node dist/index.js",
9
+ "dev": "tsx watch index.ts",
10
+ "prebuild": "cp ../web/src/app/api/mcp-remote/tool-definitions.ts ./tool-definitions.ts",
11
+ "build": "tsc",
12
+ "lint": "biome check .",
13
+ "lint:fix": "biome check --fix .",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "bin": {
17
+ "intangle-mcp": "dist/index.js"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "model-context-protocol",
22
+ "claude",
23
+ "claude-code",
24
+ "ai",
25
+ "context",
26
+ "knowledge-management",
27
+ "intangle",
28
+ "claude-desktop",
29
+ "anthropic"
30
+ ],
31
+ "author": "Intangle",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/intangle/mcp-server.git"
36
+ },
37
+ "homepage": "https://intangle.app",
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
41
+ "dependencies": {
42
+ "@modelcontextprotocol/sdk": "^1.0.0",
43
+ "dotenv": "^17.2.0",
44
+ "node-fetch": "^3.3.2"
45
+ },
46
+ "devDependencies": {
47
+ "@biomejs/biome": "^1.9.4",
48
+ "@types/node": "^22.0.0",
49
+ "@types/node-fetch": "^2.6.12",
50
+ "tsx": "^4.0.0",
51
+ "typescript": "^5.0.0"
52
+ }
53
53
  }
@@ -344,44 +344,57 @@ export const TOOLS = [
344
344
  },
345
345
  {
346
346
  name: "message",
347
- title: "Send Message",
347
+ title: "Message Assistant",
348
348
  description:
349
- "Send a message to the Intangle assistant for user interaction. Use type 'approval' to request approval (blocks until user responds), 'question' to ask the user a question (blocks until answered), 'notification' for informational updates (returns immediately), 'completion' to signal task completion (returns immediately), or 'error' to report errors (returns immediately).",
349
+ "Primary orchestration tool. Send a request to the Intangle assistant and wait for one assistant turn to complete. Returns assistant text plus continuity IDs (`session_id`, `conversation_id`) so callers can continue the same thread reliably across turns.",
350
350
  inputSchema: {
351
351
  type: "object",
352
352
  properties: {
353
- type: {
353
+ space_id: {
354
354
  type: "string",
355
- enum: ["approval", "question", "notification", "completion", "error"],
356
355
  description:
357
- "Message type. 'approval' and 'question' block until the user responds. 'notification', 'completion', and 'error' return immediately."
356
+ "Space to run the assistant turn in (use view_spaces first)."
358
357
  },
359
358
  content: {
360
359
  type: "string",
361
- description: "The message content to display to the user"
360
+ description: "User message content to send to the assistant."
362
361
  },
363
- metadata: {
364
- type: "object",
365
- description: "Optional context about the message",
366
- properties: {
367
- file: {
368
- type: "string",
369
- description: "Related file path"
370
- },
371
- action: {
372
- type: "string",
373
- description: "Action being performed"
374
- },
375
- details: {
376
- type: "string",
377
- description: "Additional details"
378
- }
379
- }
362
+ provider: {
363
+ type: "string",
364
+ description: "Optional provider alias (defaults to 'tan')."
365
+ },
366
+ conversation_id: {
367
+ type: "string",
368
+ description:
369
+ "Optional existing conversation/chat summary ID to resume a specific conversation. If omitted, Intangle uses/creates the active conversation for this space."
370
+ },
371
+ session_id: {
372
+ type: "string",
373
+ description:
374
+ "Optional runtime session ID to reuse across calls. Reuse the returned session_id for best continuity and lower warm-up overhead."
375
+ },
376
+ project_id: {
377
+ type: "string",
378
+ description: "Optional project scope for the assistant turn."
379
+ },
380
+ organization_id: {
381
+ type: "string",
382
+ description:
383
+ "Optional Clerk org ID when the space belongs to an organization graph."
384
+ },
385
+ timeout_ms: {
386
+ type: "integer",
387
+ description:
388
+ "Optional timeout in milliseconds (10,000 to 300,000; default 120,000)."
389
+ },
390
+ timezone: {
391
+ type: "string",
392
+ description: "Optional IANA timezone for assistant context."
380
393
  }
381
394
  },
382
- required: ["type", "content"]
395
+ required: ["space_id", "content"]
383
396
  }
384
- },
397
+ }
385
398
  // DISABLED: memory_action tool is broken and causing errors.
386
399
  // Pending OHM protocol fix. Use update_space, search, or fetch_items instead.
387
400
  // {