@intangle/mcp-server 2.6.1 → 2.6.3

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 CHANGED
@@ -17,7 +17,7 @@ Intangle is an AI context system that gives Claude and other AI assistants persi
17
17
  Claude code by running the command below (recommend "-s" user for global across projects):
18
18
 
19
19
  ```bash
20
- claude mcp add intangle -s user --env MCP_API_KEY=<your-api-key-here> --env NEXT_APP_URL=https://intangle.app -- npx -y @intangle/mcp-server
20
+ claude mcp add intangle -s user --env MCP_API_KEY=<your-api-key-here> --env NEXT_APP_URL=https://app.intangle.app -- npx -y @intangle/mcp-server
21
21
  ```
22
22
 
23
23
  If your deployment is behind Vercel Deployment Protection (for example staging), add:
@@ -36,7 +36,7 @@ Standard MCP (Claude desktop, Cursor): by adding to your `claude_desktop_config.
36
36
  "args": ["@intangle/mcp-server"],
37
37
  "env": {
38
38
  "MCP_API_KEY": "your-api-key-here",
39
- "NEXT_APP_URL": "https://intangle.app",
39
+ "NEXT_APP_URL": "https://app.intangle.app",
40
40
  "VERCEL_BYPASS_TOKEN": "optional-vercel-bypass-token-for-protected-deployments"
41
41
  }
42
42
  }
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ try {
30
30
  config({ quiet: true });
31
31
  config({ path: ".env.local", quiet: true });
32
32
  // Base URL for API calls to Next.js app
33
- const API_BASE_URL = process.env.NEXT_APP_URL || "https://intangle.app";
33
+ const API_BASE_URL = process.env.NEXT_APP_URL || "https://app.intangle.app";
34
34
  const MCP_API_KEY = process.env.MCP_API_KEY;
35
35
  const VERCEL_BYPASS_TOKEN = process.env.VERCEL_BYPASS_TOKEN || process.env.VERCEL_PROTECTION_BYPASS;
36
36
  log(`CWD: ${process.cwd()}`);
@@ -155,6 +155,11 @@ try {
155
155
  if (responseBody.trim() === "") {
156
156
  return {};
157
157
  }
158
+ if (contentType.includes("text/html") ||
159
+ /^\s*<!DOCTYPE\s+html/i.test(responseBody) ||
160
+ /^\s*<html\b/i.test(responseBody)) {
161
+ throw new Error(`API call to ${buildApiUrl(endpoint)} returned HTML instead of JSON. Check NEXT_APP_URL and point it at the app host, e.g. https://app.intangle.app.`);
162
+ }
158
163
  return JSON.parse(responseBody);
159
164
  }
160
165
  catch (error) {
@@ -257,7 +262,7 @@ try {
257
262
  resourceTemplates: [],
258
263
  }));
259
264
  async function handleSearchContext(args) {
260
- const { space_id, query, topics } = args;
265
+ const { space_id, query, topics, group_id, project_id, folder_id } = args;
261
266
  // Require space_id
262
267
  if (!space_id) {
263
268
  throw new Error("space_id is required. Use list_spaces to see available options.");
@@ -265,12 +270,18 @@ try {
265
270
  return makeApiCall("search", {
266
271
  space_id,
267
272
  query,
273
+ group_id,
274
+ project_id,
275
+ folder_id,
268
276
  topics,
269
277
  });
270
278
  }
271
279
  async function handleFetch(args) {
272
- const { id, ids } = args;
273
- return makeApiCall("fetch", { id, ids });
280
+ const { id, ids, space_id } = args;
281
+ return makeApiCall("fetch", { id, ids, space_id });
282
+ }
283
+ async function handleUsageStatus() {
284
+ return makeApiCall("usage-status", {});
274
285
  }
275
286
  async function handleFetchConversationMessages(args) {
276
287
  const { space_id, conversation_id } = args;
@@ -308,14 +319,14 @@ try {
308
319
  if (!space_id) {
309
320
  throw new Error("space_id is required. Use view_spaces to see available options.");
310
321
  }
311
- return makeApiCall("view-groups", { space_id });
322
+ return makeApiCall("view-groups", args);
312
323
  }
313
324
  async function handleViewProjects(args) {
314
325
  const { space_id } = args;
315
326
  if (!space_id) {
316
327
  throw new Error("space_id is required. Use view_spaces to see available options.");
317
328
  }
318
- return makeApiCall("view-projects", { space_id });
329
+ return makeApiCall("view-projects", args);
319
330
  }
320
331
  async function handleViewProject(args) {
321
332
  const { project_id, space_id, slug } = args;
@@ -325,71 +336,67 @@ try {
325
336
  if (!space_id) {
326
337
  throw new Error("space_id is required. Use view_spaces to see available options.");
327
338
  }
328
- return makeApiCall("view-project", { project_id, space_id, slug });
339
+ return makeApiCall("view-project", args);
329
340
  }
330
341
  async function handleCreateGroup(args) {
331
342
  const { space_id, name } = args;
332
343
  if (!space_id || !name) {
333
344
  throw new Error("space_id and name are required");
334
345
  }
335
- return makeApiCall("create-group", { space_id, name });
346
+ return makeApiCall("create-group", args);
336
347
  }
337
348
  async function handleRenameGroup(args) {
338
349
  const { space_id, group_id, name } = args;
339
350
  if (!space_id || !group_id || !name) {
340
351
  throw new Error("space_id, group_id, and name are required");
341
352
  }
342
- return makeApiCall("rename-group", { space_id, group_id, name });
353
+ return makeApiCall("rename-group", args);
343
354
  }
344
355
  async function handleDeleteGroup(args) {
345
356
  const { space_id, group_id } = args;
346
357
  if (!space_id || !group_id) {
347
358
  throw new Error("space_id and group_id are required");
348
359
  }
349
- return makeApiCall("delete-group", { space_id, group_id });
360
+ return makeApiCall("delete-group", args);
350
361
  }
351
362
  async function handleCreateProject(args) {
352
363
  const { space_id, name, group_id } = args;
353
364
  if (!space_id || !name) {
354
365
  throw new Error("space_id and name are required");
355
366
  }
356
- return makeApiCall("create-project", { space_id, name, group_id });
367
+ return makeApiCall("create-project", args);
357
368
  }
358
369
  async function handleRenameProject(args) {
359
370
  const { space_id, project_id, name } = args;
360
371
  if (!space_id || !project_id || !name) {
361
372
  throw new Error("space_id, project_id, and name are required");
362
373
  }
363
- return makeApiCall("rename-project", { space_id, project_id, name });
374
+ return makeApiCall("rename-project", args);
364
375
  }
365
376
  async function handleDeleteProject(args) {
366
377
  const { space_id, project_id } = args;
367
378
  if (!space_id || !project_id) {
368
379
  throw new Error("space_id and project_id are required");
369
380
  }
370
- return makeApiCall("delete-project", { space_id, project_id });
381
+ return makeApiCall("delete-project", args);
371
382
  }
372
383
  async function handleAssignProjectToGroup(args) {
373
384
  const { space_id, project_id, group_id } = args;
374
385
  if (!space_id || !project_id) {
375
386
  throw new Error("space_id and project_id are required");
376
387
  }
377
- return makeApiCall("assign-project-to-group", {
378
- space_id,
379
- project_id,
380
- group_id,
381
- });
388
+ return makeApiCall("assign-project-to-group", args);
382
389
  }
383
390
  async function handleCreateSpace(args) {
384
391
  return makeApiCall("create-space", args);
385
392
  }
386
393
  async function handleViewSpace(args) {
387
394
  const { space_id } = args;
388
- return makeApiCall("view-space", { space_id });
395
+ return makeApiCall("view-space", args);
389
396
  }
390
397
  async function handleStart(args) {
391
398
  const { space_id } = args;
392
- return makeApiCall("start", { space_id });
399
+ return makeApiCall("start", args);
393
400
  }
394
401
  async function handleUpdateSpace(args) {
395
402
  if (!args.space_id) {
@@ -462,6 +469,9 @@ try {
462
469
  case "fetch_items":
463
470
  result = await handleFetch(args);
464
471
  break;
472
+ case "usage_status":
473
+ result = await handleUsageStatus();
474
+ break;
465
475
  case "fetch_conversation_messages":
466
476
  result = await handleFetchConversationMessages(args);
467
477
  break;
@@ -15,6 +15,18 @@ const TOOL_DEFINITIONS = [
15
15
  type: "string",
16
16
  description: "REQUIRED: Space to search in (use view_spaces to see available options)"
17
17
  },
18
+ group_id: {
19
+ type: "string",
20
+ description: "Optional: Restrict search to one group inside the chosen space."
21
+ },
22
+ project_id: {
23
+ type: "string",
24
+ description: "Optional: Restrict search to one project inside the chosen space."
25
+ },
26
+ folder_id: {
27
+ type: "string",
28
+ description: "Optional: Restrict search to one folder inside the chosen space/project."
29
+ },
18
30
  topics: {
19
31
  type: "array",
20
32
  items: { type: "string" },
@@ -31,6 +43,10 @@ const TOOL_DEFINITIONS = [
31
43
  inputSchema: {
32
44
  type: "object",
33
45
  properties: {
46
+ space_id: {
47
+ type: "string",
48
+ description: "Optional: Space containing the items. Provide this for shared/org spaces so fetch resolves against the correct graph."
49
+ },
34
50
  id: {
35
51
  type: "string",
36
52
  description: "Single ID to fetch (context or task ID like 'mem_123' or 'task_456' or 'skill_789')"
@@ -43,10 +59,22 @@ const TOOL_DEFINITIONS = [
43
59
  }
44
60
  }
45
61
  },
62
+ {
63
+ name: "usage_status",
64
+ title: "Usage Status",
65
+ description: "Return the live shared AI + voice usage status for the authenticated user, including 4-hour and weekly counters and reset times when available.",
66
+ inputSchema: {
67
+ type: "object",
68
+ properties: {}
69
+ },
70
+ annotations: {
71
+ readOnlyHint: true
72
+ }
73
+ },
46
74
  {
47
75
  name: "fetch_conversation_messages",
48
76
  title: "Fetch Conversation Messages",
49
- description: "Read persisted messages from a conversation/chat summary. Useful for polling shared threads after async `message` calls or when multiple participants are collaborating in the same conversation. Returns stable message IDs and timestamps so callers can dedupe and continue from `next_cursor`.",
77
+ description: "Read persisted messages from a conversation/chat summary. Use this to poll shared threads after async `message` calls and to fetch only new messages via `since_timestamp` instead of reloading the whole thread. External agents should pass stable participant fields so unread counts and subscriptions stay consistent.",
50
78
  inputSchema: {
51
79
  type: "object",
52
80
  properties: {
@@ -77,7 +105,7 @@ const TOOL_DEFINITIONS = [
77
105
  {
78
106
  name: "subscribe_conversation",
79
107
  title: "Subscribe Conversation",
80
- description: "Register a participant in a shared conversation so it can use inbox and read-cursor tools without first sending a message. By default the subscription starts caught up at the latest message.",
108
+ description: "Register a participant in a shared conversation so it can use inbox and read-cursor tools without first sending a message. By default the subscription starts caught up at the latest message. External agents should reuse a stable participant_id, set participant_type to `agent`, and send a clear participant_label so Tan can distinguish callers.",
81
109
  inputSchema: {
82
110
  type: "object",
83
111
  properties: {
@@ -95,15 +123,15 @@ const TOOL_DEFINITIONS = [
95
123
  },
96
124
  participant_id: {
97
125
  type: "string",
98
- description: "Optional stable participant ID. Use an explicit `user_*` or `agent_*` style ID when you want the same participant identity reused across calls."
126
+ description: "Optional stable participant ID. External agents should always reuse the same explicit `agent_*` style ID across message, subscribe_conversation, list_conversation_inbox, fetch_conversation_messages, and mark_conversation_read."
99
127
  },
100
128
  participant_type: {
101
129
  type: "string",
102
- description: "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
130
+ description: "Optional participant type. External MCP callers should set this to `agent`. Supported values: `user`, `agent`, `assistant`, `connector`."
103
131
  },
104
132
  participant_label: {
105
133
  type: "string",
106
- description: "Optional display label for the participant in collaborative chat UIs."
134
+ description: "Optional display label for the participant in collaborative chat UIs. External agents should send a human-readable label such as `Claude Research` or `Codex Builder`."
107
135
  },
108
136
  mark_read: {
109
137
  type: "boolean",
@@ -116,7 +144,7 @@ const TOOL_DEFINITIONS = [
116
144
  {
117
145
  name: "list_conversation_inbox",
118
146
  title: "List Conversation Inbox",
119
- description: "List the conversations a participant is subscribed to in one space, with unread counts and read cursors. Use this to poll multiple shared threads efficiently.",
147
+ description: "List the conversations a participant is subscribed to in one space, with unread counts and read cursors. Use this to poll multiple shared threads efficiently without reloading full transcripts. Reuse the same participant_id, participant_type=`agent`, and participant_label so unread state stays attached to the right caller.",
120
148
  inputSchema: {
121
149
  type: "object",
122
150
  properties: {
@@ -130,15 +158,15 @@ const TOOL_DEFINITIONS = [
130
158
  },
131
159
  participant_id: {
132
160
  type: "string",
133
- description: "Optional stable participant ID. Use the same value you used for message/subscribe calls to read the same inbox."
161
+ description: "Optional stable participant ID. Use the exact same value you used for message and subscribe_conversation so the inbox stays tied to one external agent identity."
134
162
  },
135
163
  participant_type: {
136
164
  type: "string",
137
- description: "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
165
+ description: "Optional participant type. External MCP callers should set this to `agent`. Supported values: `user`, `agent`, `assistant`, `connector`."
138
166
  },
139
167
  participant_label: {
140
168
  type: "string",
141
- description: "Optional display label used when the participant identity is being created for the first time."
169
+ description: "Optional display label used when the participant identity is being created for the first time. Reuse the same label for the same external agent."
142
170
  },
143
171
  unread_only: {
144
172
  type: "boolean",
@@ -159,7 +187,7 @@ const TOOL_DEFINITIONS = [
159
187
  {
160
188
  name: "mark_conversation_read",
161
189
  title: "Mark Conversation Read",
162
- description: "Advance a participant's read cursor for a shared conversation. Use this after processing messages returned from fetch_conversation_messages.",
190
+ description: "Advance a participant's read cursor for a shared conversation. Use this after processing messages returned from fetch_conversation_messages so unread counts stay accurate for external agents and separate callers do not share one cursor.",
163
191
  inputSchema: {
164
192
  type: "object",
165
193
  properties: {
@@ -177,15 +205,15 @@ const TOOL_DEFINITIONS = [
177
205
  },
178
206
  participant_id: {
179
207
  type: "string",
180
- description: "Optional stable participant ID. Use the same value you used for message/subscribe calls."
208
+ description: "Optional stable participant ID. Use the same value you used for message, subscribe_conversation, and list_conversation_inbox."
181
209
  },
182
210
  participant_type: {
183
211
  type: "string",
184
- description: "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
212
+ description: "Optional participant type. External MCP callers should set this to `agent`. Supported values: `user`, `agent`, `assistant`, `connector`."
185
213
  },
186
214
  participant_label: {
187
215
  type: "string",
188
- description: "Optional display label used when the participant identity is being created for the first time."
216
+ description: "Optional display label used when the participant identity is being created for the first time. Reuse the same label for the same external agent."
189
217
  },
190
218
  read_at: {
191
219
  type: "integer",
@@ -400,7 +428,7 @@ const TOOL_DEFINITIONS = [
400
428
  {
401
429
  name: "start",
402
430
  title: "Start Space Session",
403
- description: "Load a space fast for a new external caller. Returns assistant-curated current items, useful insights, skills, recent conversations, and assistant preferences so the caller can get oriented quickly and fetch deeper details only when needed.",
431
+ description: "Load a space fast for a new external caller. Returns assistant-curated current items, useful insights, skills, recent conversations, and assistant preferences so the caller can get oriented quickly and fetch deeper details only when needed. Use this before asking Tan to do work in that space.",
404
432
  inputSchema: {
405
433
  type: "object",
406
434
  properties: {
@@ -415,7 +443,7 @@ const TOOL_DEFINITIONS = [
415
443
  {
416
444
  name: "view_spaces",
417
445
  title: "View Spaces",
418
- description: "View all available spaces with their descriptions and metrics.",
446
+ description: "View the spaces this connector can access so you can choose the right space for memory, communicating and collaborating with Tan agent. If your connector is scoped to one space, you will only see that space. Use the exact space_id from this list for all space-scoped tools.",
419
447
  inputSchema: {
420
448
  type: "object",
421
449
  properties: {}
@@ -452,7 +480,7 @@ const TOOL_DEFINITIONS = [
452
480
  {
453
481
  name: "view_space",
454
482
  title: "View Space",
455
- description: "View detailed information about a space including configuration and overview.",
483
+ description: "View detailed information about a space including configuration and overview. Use this when you need the exact active scope before saving memory or communicating and collaborating with Tan agent.",
456
484
  inputSchema: {
457
485
  type: "object",
458
486
  properties: {
@@ -467,7 +495,7 @@ const TOOL_DEFINITIONS = [
467
495
  {
468
496
  name: "update_memory",
469
497
  title: "Update Memory",
470
- description: "Add, update, or delete items in a space. Supports any combination of operations in a single call.\n\nIMPORTANT: Include at least one operation: add, update, or delete.\n\nTIPS FOR RICH MEMORY: The more context you provide, the smarter the system becomes:\n- Include WHY something matters, not just WHAT it is\n- Note user preferences, patterns, and approaches you observe\n- Describe repeatable workflows as skills (step-by-step procedures)\n- Link related items using parent_id or linkedItemIds\n- Use descriptive titles that capture the essence\n- Add context about decisions, reasoning, and outcomes\n\nThe system learns from patterns - sharing how the user works helps it anticipate their needs.",
498
+ description: "Add, update, or delete items in a space. Supports any combination of operations in a single call.\n\nIMPORTANT: Include at least one operation: add, update, or delete.\n\nTIPS FOR RICH MEMORY: The more context you provide, the smarter the system becomes:\n- Include WHY something matters, not just WHAT it is\n- Note user preferences, patterns, and approaches you observe\n- Describe repeatable workflows as skills (step-by-step procedures)\n- Link related items using parent_id or linkedItemIds\n- Prefer updating an existing related memory when the new information refines or extends it instead of creating a duplicate\n- Use descriptive titles that capture the essence\n- Add context about decisions, reasoning, and outcomes\n\nUse project_id and group_id when you know the active project or group scope. The system learns from patterns - sharing how the user works helps it anticipate their needs.",
471
499
  inputSchema: {
472
500
  type: "object",
473
501
  properties: {
@@ -479,6 +507,10 @@ const TOOL_DEFINITIONS = [
479
507
  type: "string",
480
508
  description: "Optional: Project to add items to. All items created in this request will be linked to this project and appear on its canvas. Get project IDs from the start tool output."
481
509
  },
510
+ group_id: {
511
+ type: "string",
512
+ description: "Optional: Group to add items to. Use this alongside project_id when the current item belongs in a group."
513
+ },
482
514
  add: {
483
515
  type: "object",
484
516
  description: "Add new items. ALWAYS pass 'type' field on each item to skip expensive auto-classification.",
@@ -718,7 +750,7 @@ const TOOL_DEFINITIONS = [
718
750
  {
719
751
  name: "message",
720
752
  title: "Message Assistant",
721
- description: "Primary orchestration tool. Send a request to the Intangle assistant and either wait for one assistant turn to complete or submit asynchronously for later polling. Returns assistant text plus continuity IDs (`session_id`, `conversation_id`) so callers can continue the same thread reliably across turns.",
753
+ description: "Primary way to use Intangle for memory, communicating and collaborating with Tan agent in a shared thread. Send a request and either wait for one assistant turn to complete or submit asynchronously for later polling. External agents should include a stable participant_id, participant_type=`agent`, and participant_label so Tan can identify who is speaking and keep progress updates attached to the right caller. Returns assistant text plus continuity IDs (`session_id`, `conversation_id`) so callers can continue the same thread reliably across turns.",
722
754
  inputSchema: {
723
755
  type: "object",
724
756
  properties: {
@@ -728,7 +760,7 @@ const TOOL_DEFINITIONS = [
728
760
  },
729
761
  content: {
730
762
  type: "string",
731
- description: "User message content to send to the assistant."
763
+ description: "User message content to send to Tan."
732
764
  },
733
765
  provider: {
734
766
  type: "string",
@@ -760,19 +792,19 @@ const TOOL_DEFINITIONS = [
760
792
  },
761
793
  wait_for_response: {
762
794
  type: "boolean",
763
- description: "Optional. Defaults to true. Set false to submit the turn asynchronously and poll the conversation later with fetch_conversation_messages."
795
+ description: "Optional. Defaults to true. Set false to submit the turn asynchronously when you want a quick acknowledgement first and then poll later with list_conversation_inbox or fetch_conversation_messages."
764
796
  },
765
797
  participant_id: {
766
798
  type: "string",
767
- description: "Optional stable participant ID for collaborative/shared chats. Use an explicit `user_*` or `agent_*` style ID when you want the same participant identity reused across calls."
799
+ description: "Optional stable participant ID for collaborative/shared chats. External agents should always send and reuse an explicit `agent_*` style ID so Tan can tell different external agents apart."
768
800
  },
769
801
  participant_type: {
770
802
  type: "string",
771
- description: "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
803
+ description: "Optional participant type. External MCP callers should set this to `agent`. Supported values: `user`, `agent`, `assistant`, `connector`."
772
804
  },
773
805
  participant_label: {
774
806
  type: "string",
775
- description: "Optional display label for the participant in collaborative chat UIs."
807
+ description: "Optional display label for the participant in collaborative chat UIs. External agents should send a clear label that Tan can use in follow-up coordination."
776
808
  },
777
809
  timezone: {
778
810
  type: "string",
@@ -809,6 +841,7 @@ const TOOL_DEFINITIONS = [
809
841
  const READ_ONLY_TOOLS = new Set([
810
842
  "search",
811
843
  "fetch_items",
844
+ "usage_status",
812
845
  "fetch_conversation_messages",
813
846
  "list_conversation_inbox",
814
847
  "start",
@@ -823,6 +856,7 @@ const DESTRUCTIVE_TOOLS = new Set([
823
856
  const IDEMPOTENT_TOOLS = new Set([
824
857
  "search",
825
858
  "fetch_items",
859
+ "usage_status",
826
860
  "fetch_conversation_messages",
827
861
  "list_conversation_inbox",
828
862
  "start",
package/index.ts CHANGED
@@ -45,7 +45,7 @@ try {
45
45
  config({ path: ".env.local", quiet: true })
46
46
 
47
47
  // Base URL for API calls to Next.js app
48
- const API_BASE_URL = process.env.NEXT_APP_URL || "https://intangle.app"
48
+ const API_BASE_URL = process.env.NEXT_APP_URL || "https://app.intangle.app"
49
49
  const MCP_API_KEY = process.env.MCP_API_KEY
50
50
  const VERCEL_BYPASS_TOKEN =
51
51
  process.env.VERCEL_BYPASS_TOKEN || process.env.VERCEL_PROTECTION_BYPASS
@@ -209,6 +209,16 @@ try {
209
209
  return {}
210
210
  }
211
211
 
212
+ if (
213
+ contentType.includes("text/html") ||
214
+ /^\s*<!DOCTYPE\s+html/i.test(responseBody) ||
215
+ /^\s*<html\b/i.test(responseBody)
216
+ ) {
217
+ throw new Error(
218
+ `API call to ${buildApiUrl(endpoint)} returned HTML instead of JSON. Check NEXT_APP_URL and point it at the app host, e.g. https://app.intangle.app.`
219
+ )
220
+ }
221
+
212
222
  return JSON.parse(responseBody)
213
223
  } catch (error: unknown) {
214
224
  if (error instanceof Error && error.name === "AbortError") {
@@ -338,10 +348,13 @@ try {
338
348
  }))
339
349
 
340
350
  async function handleSearchContext(args: any) {
341
- const { space_id, query, topics } = args as {
351
+ const { space_id, query, topics, group_id, project_id, folder_id } = args as {
342
352
  space_id: string
343
353
  query: string
344
354
  topics?: string[]
355
+ group_id?: string
356
+ project_id?: string
357
+ folder_id?: string
345
358
  }
346
359
 
347
360
  // Require space_id
@@ -354,14 +367,25 @@ try {
354
367
  return makeApiCall("search", {
355
368
  space_id,
356
369
  query,
370
+ group_id,
371
+ project_id,
372
+ folder_id,
357
373
  topics,
358
374
  })
359
375
  }
360
376
 
361
377
  async function handleFetch(args: any) {
362
- const { id, ids } = args as { id?: string; ids?: string[] }
378
+ const { id, ids, space_id } = args as {
379
+ id?: string
380
+ ids?: string[]
381
+ space_id?: string
382
+ }
363
383
 
364
- return makeApiCall("fetch", { id, ids })
384
+ return makeApiCall("fetch", { id, ids, space_id })
385
+ }
386
+
387
+ async function handleUsageStatus() {
388
+ return makeApiCall("usage-status", {})
365
389
  }
366
390
 
367
391
  async function handleFetchConversationMessages(args: any) {
@@ -425,7 +449,7 @@ try {
425
449
  )
426
450
  }
427
451
 
428
- return makeApiCall("view-groups", { space_id })
452
+ return makeApiCall("view-groups", args)
429
453
  }
430
454
 
431
455
  async function handleViewProjects(args: any) {
@@ -435,7 +459,7 @@ try {
435
459
  "space_id is required. Use view_spaces to see available options."
436
460
  )
437
461
  }
438
- return makeApiCall("view-projects", { space_id })
462
+ return makeApiCall("view-projects", args)
439
463
  }
440
464
 
441
465
  async function handleViewProject(args: any) {
@@ -457,7 +481,7 @@ try {
457
481
  )
458
482
  }
459
483
 
460
- return makeApiCall("view-project", { project_id, space_id, slug })
484
+ return makeApiCall("view-project", args)
461
485
  }
462
486
 
463
487
  async function handleCreateGroup(args: any) {
@@ -466,7 +490,7 @@ try {
466
490
  throw new Error("space_id and name are required")
467
491
  }
468
492
 
469
- return makeApiCall("create-group", { space_id, name })
493
+ return makeApiCall("create-group", args)
470
494
  }
471
495
 
472
496
  async function handleRenameGroup(args: any) {
@@ -480,7 +504,7 @@ try {
480
504
  throw new Error("space_id, group_id, and name are required")
481
505
  }
482
506
 
483
- return makeApiCall("rename-group", { space_id, group_id, name })
507
+ return makeApiCall("rename-group", args)
484
508
  }
485
509
 
486
510
  async function handleDeleteGroup(args: any) {
@@ -493,7 +517,7 @@ try {
493
517
  throw new Error("space_id and group_id are required")
494
518
  }
495
519
 
496
- return makeApiCall("delete-group", { space_id, group_id })
520
+ return makeApiCall("delete-group", args)
497
521
  }
498
522
 
499
523
  async function handleCreateProject(args: any) {
@@ -507,7 +531,7 @@ try {
507
531
  throw new Error("space_id and name are required")
508
532
  }
509
533
 
510
- return makeApiCall("create-project", { space_id, name, group_id })
534
+ return makeApiCall("create-project", args)
511
535
  }
512
536
 
513
537
  async function handleRenameProject(args: any) {
@@ -521,7 +545,7 @@ try {
521
545
  throw new Error("space_id, project_id, and name are required")
522
546
  }
523
547
 
524
- return makeApiCall("rename-project", { space_id, project_id, name })
548
+ return makeApiCall("rename-project", args)
525
549
  }
526
550
 
527
551
  async function handleDeleteProject(args: any) {
@@ -534,7 +558,7 @@ try {
534
558
  throw new Error("space_id and project_id are required")
535
559
  }
536
560
 
537
- return makeApiCall("delete-project", { space_id, project_id })
561
+ return makeApiCall("delete-project", args)
538
562
  }
539
563
 
540
564
  async function handleAssignProjectToGroup(args: any) {
@@ -548,11 +572,7 @@ try {
548
572
  throw new Error("space_id and project_id are required")
549
573
  }
550
574
 
551
- return makeApiCall("assign-project-to-group", {
552
- space_id,
553
- project_id,
554
- group_id,
555
- })
575
+ return makeApiCall("assign-project-to-group", args)
556
576
  }
557
577
 
558
578
  async function handleCreateSpace(args: any) {
@@ -561,12 +581,12 @@ try {
561
581
 
562
582
  async function handleViewSpace(args: any) {
563
583
  const { space_id } = args as { space_id: string }
564
- return makeApiCall("view-space", { space_id })
584
+ return makeApiCall("view-space", args)
565
585
  }
566
586
 
567
587
  async function handleStart(args: any) {
568
588
  const { space_id } = args as { space_id: string }
569
- return makeApiCall("start", { space_id })
589
+ return makeApiCall("start", args)
570
590
  }
571
591
 
572
592
  async function handleUpdateSpace(args: any) {
@@ -668,6 +688,9 @@ try {
668
688
  case "fetch_items":
669
689
  result = await handleFetch(args)
670
690
  break
691
+ case "usage_status":
692
+ result = await handleUsageStatus()
693
+ break
671
694
  case "fetch_conversation_messages":
672
695
  result = await handleFetchConversationMessages(args)
673
696
  break
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intangle/mcp-server",
3
- "version": "2.6.1",
3
+ "version": "2.6.3",
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",
@@ -18,6 +18,21 @@ const TOOL_DEFINITIONS = [
18
18
  description:
19
19
  "REQUIRED: Space to search in (use view_spaces to see available options)"
20
20
  },
21
+ group_id: {
22
+ type: "string",
23
+ description:
24
+ "Optional: Restrict search to one group inside the chosen space."
25
+ },
26
+ project_id: {
27
+ type: "string",
28
+ description:
29
+ "Optional: Restrict search to one project inside the chosen space."
30
+ },
31
+ folder_id: {
32
+ type: "string",
33
+ description:
34
+ "Optional: Restrict search to one folder inside the chosen space/project."
35
+ },
21
36
  topics: {
22
37
  type: "array",
23
38
  items: { type: "string" },
@@ -35,6 +50,11 @@ const TOOL_DEFINITIONS = [
35
50
  inputSchema: {
36
51
  type: "object",
37
52
  properties: {
53
+ space_id: {
54
+ type: "string",
55
+ description:
56
+ "Optional: Space containing the items. Provide this for shared/org spaces so fetch resolves against the correct graph."
57
+ },
38
58
  id: {
39
59
  type: "string",
40
60
  description:
@@ -49,11 +69,24 @@ const TOOL_DEFINITIONS = [
49
69
  }
50
70
  }
51
71
  },
72
+ {
73
+ name: "usage_status",
74
+ title: "Usage Status",
75
+ description:
76
+ "Return the live shared AI + voice usage status for the authenticated user, including 4-hour and weekly counters and reset times when available.",
77
+ inputSchema: {
78
+ type: "object",
79
+ properties: {}
80
+ },
81
+ annotations: {
82
+ readOnlyHint: true
83
+ }
84
+ },
52
85
  {
53
86
  name: "fetch_conversation_messages",
54
87
  title: "Fetch Conversation Messages",
55
88
  description:
56
- "Read persisted messages from a conversation/chat summary. Useful for polling shared threads after async `message` calls or when multiple participants are collaborating in the same conversation. Returns stable message IDs and timestamps so callers can dedupe and continue from `next_cursor`.",
89
+ "Read persisted messages from a conversation/chat summary. Use this to poll shared threads after async `message` calls and to fetch only new messages via `since_timestamp` instead of reloading the whole thread. External agents should pass stable participant fields so unread counts and subscriptions stay consistent.",
57
90
  inputSchema: {
58
91
  type: "object",
59
92
  properties: {
@@ -90,7 +123,7 @@ const TOOL_DEFINITIONS = [
90
123
  name: "subscribe_conversation",
91
124
  title: "Subscribe Conversation",
92
125
  description:
93
- "Register a participant in a shared conversation so it can use inbox and read-cursor tools without first sending a message. By default the subscription starts caught up at the latest message.",
126
+ "Register a participant in a shared conversation so it can use inbox and read-cursor tools without first sending a message. By default the subscription starts caught up at the latest message. External agents should reuse a stable participant_id, set participant_type to `agent`, and send a clear participant_label so Tan can distinguish callers.",
94
127
  inputSchema: {
95
128
  type: "object",
96
129
  properties: {
@@ -112,17 +145,17 @@ const TOOL_DEFINITIONS = [
112
145
  participant_id: {
113
146
  type: "string",
114
147
  description:
115
- "Optional stable participant ID. Use an explicit `user_*` or `agent_*` style ID when you want the same participant identity reused across calls."
148
+ "Optional stable participant ID. External agents should always reuse the same explicit `agent_*` style ID across message, subscribe_conversation, list_conversation_inbox, fetch_conversation_messages, and mark_conversation_read."
116
149
  },
117
150
  participant_type: {
118
151
  type: "string",
119
152
  description:
120
- "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
153
+ "Optional participant type. External MCP callers should set this to `agent`. Supported values: `user`, `agent`, `assistant`, `connector`."
121
154
  },
122
155
  participant_label: {
123
156
  type: "string",
124
157
  description:
125
- "Optional display label for the participant in collaborative chat UIs."
158
+ "Optional display label for the participant in collaborative chat UIs. External agents should send a human-readable label such as `Claude Research` or `Codex Builder`."
126
159
  },
127
160
  mark_read: {
128
161
  type: "boolean",
@@ -137,7 +170,7 @@ const TOOL_DEFINITIONS = [
137
170
  name: "list_conversation_inbox",
138
171
  title: "List Conversation Inbox",
139
172
  description:
140
- "List the conversations a participant is subscribed to in one space, with unread counts and read cursors. Use this to poll multiple shared threads efficiently.",
173
+ "List the conversations a participant is subscribed to in one space, with unread counts and read cursors. Use this to poll multiple shared threads efficiently without reloading full transcripts. Reuse the same participant_id, participant_type=`agent`, and participant_label so unread state stays attached to the right caller.",
141
174
  inputSchema: {
142
175
  type: "object",
143
176
  properties: {
@@ -154,17 +187,17 @@ const TOOL_DEFINITIONS = [
154
187
  participant_id: {
155
188
  type: "string",
156
189
  description:
157
- "Optional stable participant ID. Use the same value you used for message/subscribe calls to read the same inbox."
190
+ "Optional stable participant ID. Use the exact same value you used for message and subscribe_conversation so the inbox stays tied to one external agent identity."
158
191
  },
159
192
  participant_type: {
160
193
  type: "string",
161
194
  description:
162
- "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
195
+ "Optional participant type. External MCP callers should set this to `agent`. Supported values: `user`, `agent`, `assistant`, `connector`."
163
196
  },
164
197
  participant_label: {
165
198
  type: "string",
166
199
  description:
167
- "Optional display label used when the participant identity is being created for the first time."
200
+ "Optional display label used when the participant identity is being created for the first time. Reuse the same label for the same external agent."
168
201
  },
169
202
  unread_only: {
170
203
  type: "boolean",
@@ -189,7 +222,7 @@ const TOOL_DEFINITIONS = [
189
222
  name: "mark_conversation_read",
190
223
  title: "Mark Conversation Read",
191
224
  description:
192
- "Advance a participant's read cursor for a shared conversation. Use this after processing messages returned from fetch_conversation_messages.",
225
+ "Advance a participant's read cursor for a shared conversation. Use this after processing messages returned from fetch_conversation_messages so unread counts stay accurate for external agents and separate callers do not share one cursor.",
193
226
  inputSchema: {
194
227
  type: "object",
195
228
  properties: {
@@ -211,17 +244,17 @@ const TOOL_DEFINITIONS = [
211
244
  participant_id: {
212
245
  type: "string",
213
246
  description:
214
- "Optional stable participant ID. Use the same value you used for message/subscribe calls."
247
+ "Optional stable participant ID. Use the same value you used for message, subscribe_conversation, and list_conversation_inbox."
215
248
  },
216
249
  participant_type: {
217
250
  type: "string",
218
251
  description:
219
- "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
252
+ "Optional participant type. External MCP callers should set this to `agent`. Supported values: `user`, `agent`, `assistant`, `connector`."
220
253
  },
221
254
  participant_label: {
222
255
  type: "string",
223
256
  description:
224
- "Optional display label used when the participant identity is being created for the first time."
257
+ "Optional display label used when the participant identity is being created for the first time. Reuse the same label for the same external agent."
225
258
  },
226
259
  read_at: {
227
260
  type: "integer",
@@ -451,7 +484,7 @@ const TOOL_DEFINITIONS = [
451
484
  name: "start",
452
485
  title: "Start Space Session",
453
486
  description:
454
- "Load a space fast for a new external caller. Returns assistant-curated current items, useful insights, skills, recent conversations, and assistant preferences so the caller can get oriented quickly and fetch deeper details only when needed.",
487
+ "Load a space fast for a new external caller. Returns assistant-curated current items, useful insights, skills, recent conversations, and assistant preferences so the caller can get oriented quickly and fetch deeper details only when needed. Use this before asking Tan to do work in that space.",
455
488
  inputSchema: {
456
489
  type: "object",
457
490
  properties: {
@@ -467,7 +500,7 @@ const TOOL_DEFINITIONS = [
467
500
  name: "view_spaces",
468
501
  title: "View Spaces",
469
502
  description:
470
- "View all available spaces with their descriptions and metrics.",
503
+ "View the spaces this connector can access so you can choose the right space for memory, communicating and collaborating with Tan agent. If your connector is scoped to one space, you will only see that space. Use the exact space_id from this list for all space-scoped tools.",
471
504
  inputSchema: {
472
505
  type: "object",
473
506
  properties: {}
@@ -509,7 +542,7 @@ const TOOL_DEFINITIONS = [
509
542
  name: "view_space",
510
543
  title: "View Space",
511
544
  description:
512
- "View detailed information about a space including configuration and overview.",
545
+ "View detailed information about a space including configuration and overview. Use this when you need the exact active scope before saving memory or communicating and collaborating with Tan agent.",
513
546
  inputSchema: {
514
547
  type: "object",
515
548
  properties: {
@@ -525,7 +558,7 @@ const TOOL_DEFINITIONS = [
525
558
  name: "update_memory",
526
559
  title: "Update Memory",
527
560
  description:
528
- "Add, update, or delete items in a space. Supports any combination of operations in a single call.\n\nIMPORTANT: Include at least one operation: add, update, or delete.\n\nTIPS FOR RICH MEMORY: The more context you provide, the smarter the system becomes:\n- Include WHY something matters, not just WHAT it is\n- Note user preferences, patterns, and approaches you observe\n- Describe repeatable workflows as skills (step-by-step procedures)\n- Link related items using parent_id or linkedItemIds\n- Use descriptive titles that capture the essence\n- Add context about decisions, reasoning, and outcomes\n\nThe system learns from patterns - sharing how the user works helps it anticipate their needs.",
561
+ "Add, update, or delete items in a space. Supports any combination of operations in a single call.\n\nIMPORTANT: Include at least one operation: add, update, or delete.\n\nTIPS FOR RICH MEMORY: The more context you provide, the smarter the system becomes:\n- Include WHY something matters, not just WHAT it is\n- Note user preferences, patterns, and approaches you observe\n- Describe repeatable workflows as skills (step-by-step procedures)\n- Link related items using parent_id or linkedItemIds\n- Prefer updating an existing related memory when the new information refines or extends it instead of creating a duplicate\n- Use descriptive titles that capture the essence\n- Add context about decisions, reasoning, and outcomes\n\nUse project_id and group_id when you know the active project or group scope. The system learns from patterns - sharing how the user works helps it anticipate their needs.",
529
562
  inputSchema: {
530
563
  type: "object",
531
564
  properties: {
@@ -539,6 +572,11 @@ const TOOL_DEFINITIONS = [
539
572
  description:
540
573
  "Optional: Project to add items to. All items created in this request will be linked to this project and appear on its canvas. Get project IDs from the start tool output."
541
574
  },
575
+ group_id: {
576
+ type: "string",
577
+ description:
578
+ "Optional: Group to add items to. Use this alongside project_id when the current item belongs in a group."
579
+ },
542
580
  add: {
543
581
  type: "object",
544
582
  description:
@@ -800,7 +838,7 @@ const TOOL_DEFINITIONS = [
800
838
  name: "message",
801
839
  title: "Message Assistant",
802
840
  description:
803
- "Primary orchestration tool. Send a request to the Intangle assistant and either wait for one assistant turn to complete or submit asynchronously for later polling. Returns assistant text plus continuity IDs (`session_id`, `conversation_id`) so callers can continue the same thread reliably across turns.",
841
+ "Primary way to use Intangle for memory, communicating and collaborating with Tan agent in a shared thread. Send a request and either wait for one assistant turn to complete or submit asynchronously for later polling. External agents should include a stable participant_id, participant_type=`agent`, and participant_label so Tan can identify who is speaking and keep progress updates attached to the right caller. Returns assistant text plus continuity IDs (`session_id`, `conversation_id`) so callers can continue the same thread reliably across turns.",
804
842
  inputSchema: {
805
843
  type: "object",
806
844
  properties: {
@@ -811,7 +849,7 @@ const TOOL_DEFINITIONS = [
811
849
  },
812
850
  content: {
813
851
  type: "string",
814
- description: "User message content to send to the assistant."
852
+ description: "User message content to send to Tan."
815
853
  },
816
854
  provider: {
817
855
  type: "string",
@@ -849,22 +887,22 @@ const TOOL_DEFINITIONS = [
849
887
  wait_for_response: {
850
888
  type: "boolean",
851
889
  description:
852
- "Optional. Defaults to true. Set false to submit the turn asynchronously and poll the conversation later with fetch_conversation_messages."
890
+ "Optional. Defaults to true. Set false to submit the turn asynchronously when you want a quick acknowledgement first and then poll later with list_conversation_inbox or fetch_conversation_messages."
853
891
  },
854
892
  participant_id: {
855
893
  type: "string",
856
894
  description:
857
- "Optional stable participant ID for collaborative/shared chats. Use an explicit `user_*` or `agent_*` style ID when you want the same participant identity reused across calls."
895
+ "Optional stable participant ID for collaborative/shared chats. External agents should always send and reuse an explicit `agent_*` style ID so Tan can tell different external agents apart."
858
896
  },
859
897
  participant_type: {
860
898
  type: "string",
861
899
  description:
862
- "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
900
+ "Optional participant type. External MCP callers should set this to `agent`. Supported values: `user`, `agent`, `assistant`, `connector`."
863
901
  },
864
902
  participant_label: {
865
903
  type: "string",
866
904
  description:
867
- "Optional display label for the participant in collaborative chat UIs."
905
+ "Optional display label for the participant in collaborative chat UIs. External agents should send a clear label that Tan can use in follow-up coordination."
868
906
  },
869
907
  timezone: {
870
908
  type: "string",
@@ -902,6 +940,7 @@ const TOOL_DEFINITIONS = [
902
940
  const READ_ONLY_TOOLS = new Set([
903
941
  "search",
904
942
  "fetch_items",
943
+ "usage_status",
905
944
  "fetch_conversation_messages",
906
945
  "list_conversation_inbox",
907
946
  "start",
@@ -918,6 +957,7 @@ const DESTRUCTIVE_TOOLS = new Set([
918
957
  const IDEMPOTENT_TOOLS = new Set([
919
958
  "search",
920
959
  "fetch_items",
960
+ "usage_status",
921
961
  "fetch_conversation_messages",
922
962
  "list_conversation_inbox",
923
963
  "start",