@intangle/mcp-server 2.4.0 → 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.
Files changed (3) hide show
  1. package/dist/index.js +38 -10
  2. package/index.ts +44 -13
  3. package/package.json +1 -1
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,11 +203,21 @@ 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,
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intangle/mcp-server",
3
- "version": "2.4.0",
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",