@intangle/mcp-server 2.5.4 → 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
@@ -312,7 +312,7 @@ export const TOOLS = [
312
312
  {
313
313
  name: "message",
314
314
  title: "Message Assistant",
315
- description: "Send a user message to the Intangle assistant and wait for one assistant turn to complete. Returns assistant text, stop reason, session_id, and conversation_id for continuity.",
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: {
@@ -330,11 +330,11 @@ export const TOOLS = [
330
330
  },
331
331
  conversation_id: {
332
332
  type: "string",
333
- description: "Optional existing conversation/chat summary ID to resume continuity."
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
334
  },
335
335
  session_id: {
336
336
  type: "string",
337
- description: "Optional runtime session ID to reuse across multiple message calls."
337
+ description: "Optional runtime session ID to reuse across calls. Reuse the returned session_id for best continuity and lower warm-up overhead."
338
338
  },
339
339
  project_id: {
340
340
  type: "string",
@@ -355,7 +355,7 @@ export const TOOLS = [
355
355
  },
356
356
  required: ["space_id", "content"]
357
357
  }
358
- },
358
+ }
359
359
  // DISABLED: memory_action tool is broken and causing errors.
360
360
  // Pending OHM protocol fix. Use update_space, search, or fetch_items instead.
361
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intangle/mcp-server",
3
- "version": "2.5.4",
3
+ "version": "2.5.5",
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",
@@ -346,7 +346,7 @@ export const TOOLS = [
346
346
  name: "message",
347
347
  title: "Message Assistant",
348
348
  description:
349
- "Send a user message to the Intangle assistant and wait for one assistant turn to complete. Returns assistant text, stop reason, session_id, and conversation_id for continuity.",
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: {
@@ -366,12 +366,12 @@ export const TOOLS = [
366
366
  conversation_id: {
367
367
  type: "string",
368
368
  description:
369
- "Optional existing conversation/chat summary ID to resume continuity."
369
+ "Optional existing conversation/chat summary ID to resume a specific conversation. If omitted, Intangle uses/creates the active conversation for this space."
370
370
  },
371
371
  session_id: {
372
372
  type: "string",
373
373
  description:
374
- "Optional runtime session ID to reuse across multiple message calls."
374
+ "Optional runtime session ID to reuse across calls. Reuse the returned session_id for best continuity and lower warm-up overhead."
375
375
  },
376
376
  project_id: {
377
377
  type: "string",
@@ -394,7 +394,7 @@ export const TOOLS = [
394
394
  },
395
395
  required: ["space_id", "content"]
396
396
  }
397
- },
397
+ }
398
398
  // DISABLED: memory_action tool is broken and causing errors.
399
399
  // Pending OHM protocol fix. Use update_space, search, or fetch_items instead.
400
400
  // {