@intangle/mcp-server 2.3.9 → 2.5.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/index.js CHANGED
@@ -90,7 +90,9 @@ try {
90
90
  }
91
91
  }
92
92
  }
93
- async function makeApiCall(endpoint, data) {
93
+ // Default timeout for API calls (90 seconds)
94
+ const API_TIMEOUT_MS = 90_000;
95
+ async function makeApiCall(endpoint, data, timeoutMs) {
94
96
  // Ensure we have client info before making requests
95
97
  ensureClientInfo();
96
98
  // Build headers with client info if available
@@ -108,15 +110,31 @@ try {
108
110
  if (mcpClientVersion) {
109
111
  headers["X-MCP-Client-Version"] = mcpClientVersion;
110
112
  }
111
- const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
112
- method: "POST",
113
- headers,
114
- body: JSON.stringify(data),
115
- });
116
- if (!response.ok) {
117
- throw new Error(`API call failed: ${response.status} ${response.statusText}`);
113
+ // Apply timeout to prevent hanging requests
114
+ const controller = new AbortController();
115
+ const effectiveTimeout = timeoutMs || API_TIMEOUT_MS;
116
+ const timeoutId = setTimeout(() => controller.abort(), effectiveTimeout);
117
+ try {
118
+ const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
119
+ method: "POST",
120
+ headers,
121
+ body: JSON.stringify(data),
122
+ signal: controller.signal,
123
+ });
124
+ if (!response.ok) {
125
+ throw new Error(`API call failed: ${response.status} ${response.statusText}`);
126
+ }
127
+ return response.json();
128
+ }
129
+ catch (error) {
130
+ if (error instanceof Error && error.name === "AbortError") {
131
+ throw new Error(`API call to ${endpoint} timed out after ${effectiveTimeout / 1000}s`);
132
+ }
133
+ throw error;
134
+ }
135
+ finally {
136
+ clearTimeout(timeoutId);
118
137
  }
119
- return response.json();
120
138
  }
121
139
  const server = new Server({
122
140
  name: "intangle-context",
@@ -185,17 +203,41 @@ try {
185
203
  if (!args.add && !args.update && !args.delete && !args.link && !args.unlink) {
186
204
  throw new Error("At least one operation must be provided");
187
205
  }
206
+ // Ensure all add items have a type field to prevent classification timeout
207
+ const add = args.add;
208
+ if (add?.items && Array.isArray(add.items)) {
209
+ for (const item of add.items) {
210
+ if (!item.type) {
211
+ log(`WARNING: Item "${item.title}" missing type field, defaulting to "context" to prevent classification timeout`);
212
+ item.type = "context";
213
+ }
214
+ }
215
+ }
188
216
  // Pass through to API with new structure
189
217
  return makeApiCall("update-memory", {
190
218
  space_id: args.space_id,
191
219
  project_id: args.project_id,
192
- add: args.add,
220
+ add,
193
221
  update: args.update,
194
222
  delete: args.delete,
195
223
  link: args.link,
196
224
  unlink: args.unlink,
197
225
  });
198
226
  }
227
+ async function handleMessage(args) {
228
+ if (!args.type || !args.content) {
229
+ throw new Error("type and content are required");
230
+ }
231
+ // Inject session_id from environment variable
232
+ const sessionId = process.env.INTANGLE_SESSION_ID;
233
+ if (!sessionId) {
234
+ throw new Error("INTANGLE_SESSION_ID environment variable is required for the message tool");
235
+ }
236
+ return makeApiCall("message", {
237
+ ...args,
238
+ session_id: sessionId,
239
+ });
240
+ }
199
241
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
200
242
  const { name, arguments: args } = request.params;
201
243
  try {
@@ -228,6 +270,9 @@ try {
228
270
  case "update_space":
229
271
  result = await handleUpdateSpace(args);
230
272
  break;
273
+ case "message":
274
+ result = await handleMessage(args);
275
+ break;
231
276
  default:
232
277
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
233
278
  }
@@ -302,6 +302,44 @@ export const TOOLS = [
302
302
  required: ["space_id"]
303
303
  }
304
304
  },
305
+ {
306
+ name: "message",
307
+ title: "Send Message",
308
+ 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).",
309
+ inputSchema: {
310
+ type: "object",
311
+ properties: {
312
+ type: {
313
+ type: "string",
314
+ enum: ["approval", "question", "notification", "completion", "error"],
315
+ description: "Message type. 'approval' and 'question' block until the user responds. 'notification', 'completion', and 'error' return immediately."
316
+ },
317
+ content: {
318
+ type: "string",
319
+ description: "The message content to display to the user"
320
+ },
321
+ metadata: {
322
+ type: "object",
323
+ description: "Optional context about the message",
324
+ properties: {
325
+ file: {
326
+ type: "string",
327
+ description: "Related file path"
328
+ },
329
+ action: {
330
+ type: "string",
331
+ description: "Action being performed"
332
+ },
333
+ details: {
334
+ type: "string",
335
+ description: "Additional details"
336
+ }
337
+ }
338
+ }
339
+ },
340
+ required: ["type", "content"]
341
+ }
342
+ },
305
343
  // DISABLED: memory_action tool is broken and causing errors.
306
344
  // Pending OHM protocol fix. Use update_space, search, or fetch_items instead.
307
345
  // {
package/index.ts CHANGED
@@ -118,7 +118,10 @@ try {
118
118
  }
119
119
  }
120
120
 
121
- async function makeApiCall(endpoint: string, data: any) {
121
+ // Default timeout for API calls (90 seconds)
122
+ const API_TIMEOUT_MS = 90_000
123
+
124
+ async function makeApiCall(endpoint: string, data: any, timeoutMs?: number) {
122
125
  // Ensure we have client info before making requests
123
126
  ensureClientInfo()
124
127
 
@@ -139,19 +142,36 @@ try {
139
142
  headers["X-MCP-Client-Version"] = mcpClientVersion
140
143
  }
141
144
 
142
- const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
143
- method: "POST",
144
- headers,
145
- body: JSON.stringify(data),
146
- })
145
+ // Apply timeout to prevent hanging requests
146
+ const controller = new AbortController()
147
+ const effectiveTimeout = timeoutMs || API_TIMEOUT_MS
148
+ const timeoutId = setTimeout(() => controller.abort(), effectiveTimeout)
147
149
 
148
- if (!response.ok) {
149
- throw new Error(
150
- `API call failed: ${response.status} ${response.statusText}`
151
- )
152
- }
150
+ try {
151
+ const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
152
+ method: "POST",
153
+ headers,
154
+ body: JSON.stringify(data),
155
+ signal: controller.signal as AbortSignal,
156
+ })
157
+
158
+ if (!response.ok) {
159
+ throw new Error(
160
+ `API call failed: ${response.status} ${response.statusText}`
161
+ )
162
+ }
153
163
 
154
- return response.json()
164
+ return response.json()
165
+ } catch (error: unknown) {
166
+ if (error instanceof Error && error.name === "AbortError") {
167
+ throw new Error(
168
+ `API call to ${endpoint} timed out after ${effectiveTimeout / 1000}s`
169
+ )
170
+ }
171
+ throw error
172
+ } finally {
173
+ clearTimeout(timeoutId)
174
+ }
155
175
  }
156
176
 
157
177
  const server = new Server(
@@ -258,11 +278,22 @@ try {
258
278
  )
259
279
  }
260
280
 
281
+ // Ensure all add items have a type field to prevent classification timeout
282
+ const add = args.add
283
+ if (add?.items && Array.isArray(add.items)) {
284
+ for (const item of add.items) {
285
+ if (!item.type) {
286
+ log(`WARNING: Item "${item.title}" missing type field, defaulting to "context" to prevent classification timeout`)
287
+ item.type = "context"
288
+ }
289
+ }
290
+ }
291
+
261
292
  // Pass through to API with new structure
262
293
  return makeApiCall("update-memory", {
263
294
  space_id: args.space_id,
264
295
  project_id: args.project_id,
265
- add: args.add,
296
+ add,
266
297
  update: args.update,
267
298
  delete: args.delete,
268
299
  link: args.link,
@@ -270,6 +301,25 @@ try {
270
301
  })
271
302
  }
272
303
 
304
+ async function handleMessage(args: any) {
305
+ if (!args.type || !args.content) {
306
+ throw new Error("type and content are required")
307
+ }
308
+
309
+ // Inject session_id from environment variable
310
+ const sessionId = process.env.INTANGLE_SESSION_ID
311
+ if (!sessionId) {
312
+ throw new Error(
313
+ "INTANGLE_SESSION_ID environment variable is required for the message tool"
314
+ )
315
+ }
316
+
317
+ return makeApiCall("message", {
318
+ ...args,
319
+ session_id: sessionId,
320
+ })
321
+ }
322
+
273
323
  server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
274
324
  const { name, arguments: args } = request.params
275
325
 
@@ -304,6 +354,9 @@ try {
304
354
  case "update_space":
305
355
  result = await handleUpdateSpace(args)
306
356
  break
357
+ case "message":
358
+ result = await handleMessage(args)
359
+ break
307
360
  default:
308
361
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`)
309
362
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intangle/mcp-server",
3
- "version": "2.3.9",
3
+ "version": "2.5.0",
4
4
  "description": "Model Context Protocol server for Intangle - AI context that persists across conversations",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -341,6 +341,46 @@ export const TOOLS = [
341
341
  required: ["space_id"]
342
342
  }
343
343
  },
344
+ {
345
+ name: "message",
346
+ title: "Send Message",
347
+ description:
348
+ "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
+ inputSchema: {
350
+ type: "object",
351
+ properties: {
352
+ type: {
353
+ type: "string",
354
+ enum: ["approval", "question", "notification", "completion", "error"],
355
+ description:
356
+ "Message type. 'approval' and 'question' block until the user responds. 'notification', 'completion', and 'error' return immediately."
357
+ },
358
+ content: {
359
+ type: "string",
360
+ description: "The message content to display to the user"
361
+ },
362
+ metadata: {
363
+ type: "object",
364
+ description: "Optional context about the message",
365
+ properties: {
366
+ file: {
367
+ type: "string",
368
+ description: "Related file path"
369
+ },
370
+ action: {
371
+ type: "string",
372
+ description: "Action being performed"
373
+ },
374
+ details: {
375
+ type: "string",
376
+ description: "Additional details"
377
+ }
378
+ }
379
+ }
380
+ },
381
+ required: ["type", "content"]
382
+ }
383
+ },
344
384
  // DISABLED: memory_action tool is broken and causing errors.
345
385
  // Pending OHM protocol fix. Use update_space, search, or fetch_items instead.
346
386
  // {