@intangle/mcp-server 2.5.6 → 2.6.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
@@ -272,9 +272,44 @@ try {
272
272
  const { id, ids } = args;
273
273
  return makeApiCall("fetch", { id, ids });
274
274
  }
275
+ async function handleFetchConversationMessages(args) {
276
+ const { space_id, conversation_id } = args;
277
+ if (!space_id || !conversation_id) {
278
+ throw new Error("space_id and conversation_id are required");
279
+ }
280
+ return makeApiCall("conversation-messages", args);
281
+ }
282
+ async function handleSubscribeConversation(args) {
283
+ const { space_id, conversation_id } = args;
284
+ if (!space_id || !conversation_id) {
285
+ throw new Error("space_id and conversation_id are required");
286
+ }
287
+ return makeApiCall("conversation-subscribe", args);
288
+ }
289
+ async function handleConversationInbox(args) {
290
+ const { space_id } = args;
291
+ if (!space_id) {
292
+ throw new Error("space_id is required");
293
+ }
294
+ return makeApiCall("conversation-inbox", args);
295
+ }
296
+ async function handleMarkConversationRead(args) {
297
+ const { space_id, conversation_id } = args;
298
+ if (!space_id || !conversation_id) {
299
+ throw new Error("space_id and conversation_id are required");
300
+ }
301
+ return makeApiCall("conversation-read", args);
302
+ }
275
303
  async function handleViewSpaces() {
276
304
  return makeApiCall("list-spaces", {});
277
305
  }
306
+ async function handleViewGroups(args) {
307
+ const { space_id } = args;
308
+ if (!space_id) {
309
+ throw new Error("space_id is required. Use view_spaces to see available options.");
310
+ }
311
+ return makeApiCall("view-groups", { space_id });
312
+ }
278
313
  async function handleViewProjects(args) {
279
314
  const { space_id } = args;
280
315
  if (!space_id) {
@@ -287,11 +322,64 @@ try {
287
322
  if (!project_id && !slug) {
288
323
  throw new Error("Either project_id or slug (with space_id) is required. Use view_projects to get valid IDs.");
289
324
  }
290
- if (slug && !space_id) {
291
- throw new Error("space_id is required when using slug. Use view_spaces to see available options.");
325
+ if (!space_id) {
326
+ throw new Error("space_id is required. Use view_spaces to see available options.");
292
327
  }
293
328
  return makeApiCall("view-project", { project_id, space_id, slug });
294
329
  }
330
+ async function handleCreateGroup(args) {
331
+ const { space_id, name } = args;
332
+ if (!space_id || !name) {
333
+ throw new Error("space_id and name are required");
334
+ }
335
+ return makeApiCall("create-group", { space_id, name });
336
+ }
337
+ async function handleRenameGroup(args) {
338
+ const { space_id, group_id, name } = args;
339
+ if (!space_id || !group_id || !name) {
340
+ throw new Error("space_id, group_id, and name are required");
341
+ }
342
+ return makeApiCall("rename-group", { space_id, group_id, name });
343
+ }
344
+ async function handleDeleteGroup(args) {
345
+ const { space_id, group_id } = args;
346
+ if (!space_id || !group_id) {
347
+ throw new Error("space_id and group_id are required");
348
+ }
349
+ return makeApiCall("delete-group", { space_id, group_id });
350
+ }
351
+ async function handleCreateProject(args) {
352
+ const { space_id, name, group_id } = args;
353
+ if (!space_id || !name) {
354
+ throw new Error("space_id and name are required");
355
+ }
356
+ return makeApiCall("create-project", { space_id, name, group_id });
357
+ }
358
+ async function handleRenameProject(args) {
359
+ const { space_id, project_id, name } = args;
360
+ if (!space_id || !project_id || !name) {
361
+ throw new Error("space_id, project_id, and name are required");
362
+ }
363
+ return makeApiCall("rename-project", { space_id, project_id, name });
364
+ }
365
+ async function handleDeleteProject(args) {
366
+ const { space_id, project_id } = args;
367
+ if (!space_id || !project_id) {
368
+ throw new Error("space_id and project_id are required");
369
+ }
370
+ return makeApiCall("delete-project", { space_id, project_id });
371
+ }
372
+ async function handleAssignProjectToGroup(args) {
373
+ const { space_id, project_id, group_id } = args;
374
+ if (!space_id || !project_id) {
375
+ throw new Error("space_id and project_id are required");
376
+ }
377
+ return makeApiCall("assign-project-to-group", {
378
+ space_id,
379
+ project_id,
380
+ group_id,
381
+ });
382
+ }
295
383
  async function handleCreateSpace(args) {
296
384
  return makeApiCall("create-space", args);
297
385
  }
@@ -374,16 +462,51 @@ try {
374
462
  case "fetch_items":
375
463
  result = await handleFetch(args);
376
464
  break;
465
+ case "fetch_conversation_messages":
466
+ result = await handleFetchConversationMessages(args);
467
+ break;
468
+ case "subscribe_conversation":
469
+ result = await handleSubscribeConversation(args);
470
+ break;
471
+ case "list_conversation_inbox":
472
+ result = await handleConversationInbox(args);
473
+ break;
474
+ case "mark_conversation_read":
475
+ result = await handleMarkConversationRead(args);
476
+ break;
377
477
  case "view_spaces":
378
478
  result = await handleViewSpaces();
379
479
  break;
380
- // PROJECTS DISABLED - Projects have been disabled. May be re-enabled later.
381
- // case "view_projects":
382
- // result = await handleViewProjects(args)
383
- // break
384
- // case "view_project":
385
- // result = await handleViewProject(args)
386
- // break
480
+ case "view_groups":
481
+ result = await handleViewGroups(args);
482
+ break;
483
+ case "create_group":
484
+ result = await handleCreateGroup(args);
485
+ break;
486
+ case "rename_group":
487
+ result = await handleRenameGroup(args);
488
+ break;
489
+ case "delete_group":
490
+ result = await handleDeleteGroup(args);
491
+ break;
492
+ case "view_projects":
493
+ result = await handleViewProjects(args);
494
+ break;
495
+ case "view_project":
496
+ result = await handleViewProject(args);
497
+ break;
498
+ case "create_project":
499
+ result = await handleCreateProject(args);
500
+ break;
501
+ case "rename_project":
502
+ result = await handleRenameProject(args);
503
+ break;
504
+ case "delete_project":
505
+ result = await handleDeleteProject(args);
506
+ break;
507
+ case "assign_project_to_group":
508
+ result = await handleAssignProjectToGroup(args);
509
+ break;
387
510
  case "create_space":
388
511
  result = await handleCreateSpace(args);
389
512
  break;
@@ -43,50 +43,360 @@ const TOOL_DEFINITIONS = [
43
43
  }
44
44
  }
45
45
  },
46
- // PROJECTS DISABLED - Projects have been disabled. May be re-enabled later.
47
- // {
48
- // name: "view_projects",
49
- // title: "View Projects",
50
- // description:
51
- // "List all projects in a space. Projects are user-created canvases that organize context, tasks, documents, and skills. Use this to discover available projects before viewing their contents.",
52
- // inputSchema: {
53
- // type: "object",
54
- // properties: {
55
- // space_id: {
56
- // type: "string",
57
- // description:
58
- // "REQUIRED: Space to list projects from (use view_spaces to see available options)"
59
- // }
60
- // },
61
- // required: ["space_id"]
62
- // }
63
- // },
64
- // {
65
- // name: "view_project",
66
- // title: "View Project",
67
- // description:
68
- // "View a specific project with all its items (context, tasks, documents, skills). Returns item metadata (IDs, titles, types, status) for each item in the project. Use fetch_items to get full content of specific items.",
69
- // inputSchema: {
70
- // type: "object",
71
- // properties: {
72
- // project_id: {
73
- // type: "string",
74
- // description:
75
- // "Project ID to view (e.g., 'proj_123...'). Get project IDs from view_projects."
76
- // },
77
- // space_id: {
78
- // type: "string",
79
- // description:
80
- // "Optional: Space ID (required if using slug instead of project_id)"
81
- // },
82
- // slug: {
83
- // type: "string",
84
- // description:
85
- // "Optional: Project slug (URL-friendly name). Requires space_id if used."
86
- // }
87
- // }
88
- // }
89
- // },
46
+ {
47
+ name: "fetch_conversation_messages",
48
+ 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`.",
50
+ inputSchema: {
51
+ type: "object",
52
+ properties: {
53
+ space_id: {
54
+ type: "string",
55
+ description: "REQUIRED: Space containing the conversation (use view_spaces first)."
56
+ },
57
+ conversation_id: {
58
+ type: "string",
59
+ description: "REQUIRED: Chat summary / conversation ID to read (e.g. 'summary_123...')."
60
+ },
61
+ organization_id: {
62
+ type: "string",
63
+ description: "Optional Clerk org ID when the space belongs to an organization graph."
64
+ },
65
+ since_timestamp: {
66
+ type: "integer",
67
+ description: "Optional millisecond cursor. Only messages newer than this timestamp are returned."
68
+ },
69
+ limit: {
70
+ type: "integer",
71
+ description: "Optional page size from 1 to 200 (default 50)."
72
+ }
73
+ },
74
+ required: ["space_id", "conversation_id"]
75
+ }
76
+ },
77
+ {
78
+ name: "subscribe_conversation",
79
+ 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.",
81
+ inputSchema: {
82
+ type: "object",
83
+ properties: {
84
+ space_id: {
85
+ type: "string",
86
+ description: "REQUIRED: Space containing the conversation (use view_spaces first)."
87
+ },
88
+ conversation_id: {
89
+ type: "string",
90
+ description: "REQUIRED: Chat summary / conversation ID to subscribe to."
91
+ },
92
+ organization_id: {
93
+ type: "string",
94
+ description: "Optional Clerk org ID when the space belongs to an organization graph."
95
+ },
96
+ participant_id: {
97
+ 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."
99
+ },
100
+ participant_type: {
101
+ type: "string",
102
+ description: "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
103
+ },
104
+ participant_label: {
105
+ type: "string",
106
+ description: "Optional display label for the participant in collaborative chat UIs."
107
+ },
108
+ mark_read: {
109
+ type: "boolean",
110
+ description: "Optional. Defaults to true. When true, the subscription starts caught up at the current latest message."
111
+ }
112
+ },
113
+ required: ["space_id", "conversation_id"]
114
+ }
115
+ },
116
+ {
117
+ name: "list_conversation_inbox",
118
+ 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.",
120
+ inputSchema: {
121
+ type: "object",
122
+ properties: {
123
+ space_id: {
124
+ type: "string",
125
+ description: "REQUIRED: Space to list inbox conversations from (use view_spaces first)."
126
+ },
127
+ organization_id: {
128
+ type: "string",
129
+ description: "Optional Clerk org ID when the space belongs to an organization graph."
130
+ },
131
+ participant_id: {
132
+ type: "string",
133
+ description: "Optional stable participant ID. Use the same value you used for message/subscribe calls to read the same inbox."
134
+ },
135
+ participant_type: {
136
+ type: "string",
137
+ description: "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
138
+ },
139
+ participant_label: {
140
+ type: "string",
141
+ description: "Optional display label used when the participant identity is being created for the first time."
142
+ },
143
+ unread_only: {
144
+ type: "boolean",
145
+ description: "Optional. When true, only return conversations that currently have unread messages."
146
+ },
147
+ since_timestamp: {
148
+ type: "integer",
149
+ description: "Optional millisecond cursor. Only conversations updated after this timestamp are returned."
150
+ },
151
+ limit: {
152
+ type: "integer",
153
+ description: "Optional page size from 1 to 200 (default 50)."
154
+ }
155
+ },
156
+ required: ["space_id"]
157
+ }
158
+ },
159
+ {
160
+ name: "mark_conversation_read",
161
+ 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.",
163
+ inputSchema: {
164
+ type: "object",
165
+ properties: {
166
+ space_id: {
167
+ type: "string",
168
+ description: "REQUIRED: Space containing the conversation (use view_spaces first)."
169
+ },
170
+ conversation_id: {
171
+ type: "string",
172
+ description: "REQUIRED: Chat summary / conversation ID to mark as read."
173
+ },
174
+ organization_id: {
175
+ type: "string",
176
+ description: "Optional Clerk org ID when the space belongs to an organization graph."
177
+ },
178
+ participant_id: {
179
+ type: "string",
180
+ description: "Optional stable participant ID. Use the same value you used for message/subscribe calls."
181
+ },
182
+ participant_type: {
183
+ type: "string",
184
+ description: "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
185
+ },
186
+ participant_label: {
187
+ type: "string",
188
+ description: "Optional display label used when the participant identity is being created for the first time."
189
+ },
190
+ read_at: {
191
+ type: "integer",
192
+ description: "Optional millisecond cursor to advance to. If omitted, the conversation is marked read through its latest message."
193
+ }
194
+ },
195
+ required: ["space_id", "conversation_id"]
196
+ }
197
+ },
198
+ {
199
+ name: "view_groups",
200
+ title: "View Groups",
201
+ description: "List all groups in a space, including the projects currently inside each group plus any ungrouped projects.",
202
+ inputSchema: {
203
+ type: "object",
204
+ properties: {
205
+ space_id: {
206
+ type: "string",
207
+ description: "REQUIRED: Space to list groups from (use view_spaces to see available options)"
208
+ }
209
+ },
210
+ required: ["space_id"]
211
+ }
212
+ },
213
+ {
214
+ name: "create_group",
215
+ title: "Create Group",
216
+ description: "Create a new group inside a space.",
217
+ inputSchema: {
218
+ type: "object",
219
+ properties: {
220
+ space_id: {
221
+ type: "string",
222
+ description: "REQUIRED: Space to create the group in."
223
+ },
224
+ name: {
225
+ type: "string",
226
+ description: "Name for the new group."
227
+ }
228
+ },
229
+ required: ["space_id", "name"]
230
+ }
231
+ },
232
+ {
233
+ name: "rename_group",
234
+ title: "Rename Group",
235
+ description: "Rename an existing group.",
236
+ inputSchema: {
237
+ type: "object",
238
+ properties: {
239
+ space_id: {
240
+ type: "string",
241
+ description: "REQUIRED: Space containing the group."
242
+ },
243
+ group_id: {
244
+ type: "string",
245
+ description: "REQUIRED: Group ID to rename."
246
+ },
247
+ name: {
248
+ type: "string",
249
+ description: "REQUIRED: New group name."
250
+ }
251
+ },
252
+ required: ["space_id", "group_id", "name"]
253
+ }
254
+ },
255
+ {
256
+ name: "delete_group",
257
+ title: "Delete Group",
258
+ description: "Delete a group. Projects inside it are preserved and become ungrouped.",
259
+ inputSchema: {
260
+ type: "object",
261
+ properties: {
262
+ space_id: {
263
+ type: "string",
264
+ description: "REQUIRED: Space containing the group."
265
+ },
266
+ group_id: {
267
+ type: "string",
268
+ description: "REQUIRED: Group ID to delete."
269
+ }
270
+ },
271
+ required: ["space_id", "group_id"]
272
+ }
273
+ },
274
+ {
275
+ name: "view_projects",
276
+ title: "View Projects",
277
+ description: "List all projects in a space, including which group each project belongs to.",
278
+ inputSchema: {
279
+ type: "object",
280
+ properties: {
281
+ space_id: {
282
+ type: "string",
283
+ description: "REQUIRED: Space to list projects from (use view_spaces to see available options)"
284
+ }
285
+ },
286
+ required: ["space_id"]
287
+ }
288
+ },
289
+ {
290
+ name: "view_project",
291
+ title: "View Project",
292
+ description: "View a specific project with all its items. Use fetch_items to retrieve full content for specific item IDs.",
293
+ inputSchema: {
294
+ type: "object",
295
+ properties: {
296
+ project_id: {
297
+ type: "string",
298
+ description: "Project ID to view (e.g., 'proj_123...'). Get project IDs from view_projects."
299
+ },
300
+ space_id: {
301
+ type: "string",
302
+ description: "REQUIRED: Space containing the project."
303
+ },
304
+ slug: {
305
+ type: "string",
306
+ description: "Optional: Project slug (URL-friendly name)."
307
+ }
308
+ },
309
+ required: ["space_id"]
310
+ }
311
+ },
312
+ {
313
+ name: "create_project",
314
+ title: "Create Project",
315
+ description: "Create a new project in a space. Optionally place it inside a group immediately.",
316
+ inputSchema: {
317
+ type: "object",
318
+ properties: {
319
+ space_id: {
320
+ type: "string",
321
+ description: "REQUIRED: Space to create the project in."
322
+ },
323
+ name: {
324
+ type: "string",
325
+ description: "REQUIRED: Project name."
326
+ },
327
+ group_id: {
328
+ type: "string",
329
+ description: "Optional: Group ID to place the project inside."
330
+ }
331
+ },
332
+ required: ["space_id", "name"]
333
+ }
334
+ },
335
+ {
336
+ name: "rename_project",
337
+ title: "Rename Project",
338
+ description: "Rename an existing project.",
339
+ inputSchema: {
340
+ type: "object",
341
+ properties: {
342
+ space_id: {
343
+ type: "string",
344
+ description: "REQUIRED: Space containing the project."
345
+ },
346
+ project_id: {
347
+ type: "string",
348
+ description: "REQUIRED: Project ID to rename."
349
+ },
350
+ name: {
351
+ type: "string",
352
+ description: "REQUIRED: New project name."
353
+ }
354
+ },
355
+ required: ["space_id", "project_id", "name"]
356
+ }
357
+ },
358
+ {
359
+ name: "delete_project",
360
+ title: "Delete Project",
361
+ description: "Delete an existing project from a space.",
362
+ inputSchema: {
363
+ type: "object",
364
+ properties: {
365
+ space_id: {
366
+ type: "string",
367
+ description: "REQUIRED: Space containing the project."
368
+ },
369
+ project_id: {
370
+ type: "string",
371
+ description: "REQUIRED: Project ID to delete."
372
+ }
373
+ },
374
+ required: ["space_id", "project_id"]
375
+ }
376
+ },
377
+ {
378
+ name: "assign_project_to_group",
379
+ title: "Assign Project To Group",
380
+ description: "Move a project into a group. Omit group_id to make the project ungrouped.",
381
+ inputSchema: {
382
+ type: "object",
383
+ properties: {
384
+ space_id: {
385
+ type: "string",
386
+ description: "REQUIRED: Space containing the project."
387
+ },
388
+ project_id: {
389
+ type: "string",
390
+ description: "REQUIRED: Project ID to move."
391
+ },
392
+ group_id: {
393
+ type: "string",
394
+ description: "Optional: Target group ID. Omit this to ungroup the project."
395
+ }
396
+ },
397
+ required: ["space_id", "project_id"]
398
+ }
399
+ },
90
400
  {
91
401
  name: "start",
92
402
  title: "Start Space Session",
@@ -416,7 +726,7 @@ const TOOL_DEFINITIONS = [
416
726
  {
417
727
  name: "message",
418
728
  title: "Message Assistant",
419
- 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.",
729
+ 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.",
420
730
  inputSchema: {
421
731
  type: "object",
422
732
  properties: {
@@ -452,6 +762,22 @@ const TOOL_DEFINITIONS = [
452
762
  type: "integer",
453
763
  description: "Optional timeout in milliseconds (10,000 to 300,000; default 120,000)."
454
764
  },
765
+ wait_for_response: {
766
+ type: "boolean",
767
+ description: "Optional. Defaults to true. Set false to submit the turn asynchronously and poll the conversation later with fetch_conversation_messages."
768
+ },
769
+ participant_id: {
770
+ type: "string",
771
+ 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."
772
+ },
773
+ participant_type: {
774
+ type: "string",
775
+ description: "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
776
+ },
777
+ participant_label: {
778
+ type: "string",
779
+ description: "Optional display label for the participant in collaborative chat UIs."
780
+ },
455
781
  timezone: {
456
782
  type: "string",
457
783
  description: "Optional IANA timezone for assistant context."
@@ -487,6 +813,8 @@ const TOOL_DEFINITIONS = [
487
813
  const READ_ONLY_TOOLS = new Set([
488
814
  "search",
489
815
  "fetch_items",
816
+ "fetch_conversation_messages",
817
+ "list_conversation_inbox",
490
818
  "start",
491
819
  "view_spaces",
492
820
  "view_space"
@@ -499,6 +827,8 @@ const DESTRUCTIVE_TOOLS = new Set([
499
827
  const IDEMPOTENT_TOOLS = new Set([
500
828
  "search",
501
829
  "fetch_items",
830
+ "fetch_conversation_messages",
831
+ "list_conversation_inbox",
502
832
  "start",
503
833
  "view_spaces",
504
834
  "view_space"
package/index.ts CHANGED
@@ -364,10 +364,70 @@ try {
364
364
  return makeApiCall("fetch", { id, ids })
365
365
  }
366
366
 
367
+ async function handleFetchConversationMessages(args: any) {
368
+ const { space_id, conversation_id } = args as {
369
+ space_id?: string
370
+ conversation_id?: string
371
+ }
372
+
373
+ if (!space_id || !conversation_id) {
374
+ throw new Error("space_id and conversation_id are required")
375
+ }
376
+
377
+ return makeApiCall("conversation-messages", args)
378
+ }
379
+
380
+ async function handleSubscribeConversation(args: any) {
381
+ const { space_id, conversation_id } = args as {
382
+ space_id?: string
383
+ conversation_id?: string
384
+ }
385
+
386
+ if (!space_id || !conversation_id) {
387
+ throw new Error("space_id and conversation_id are required")
388
+ }
389
+
390
+ return makeApiCall("conversation-subscribe", args)
391
+ }
392
+
393
+ async function handleConversationInbox(args: any) {
394
+ const { space_id } = args as { space_id?: string }
395
+
396
+ if (!space_id) {
397
+ throw new Error("space_id is required")
398
+ }
399
+
400
+ return makeApiCall("conversation-inbox", args)
401
+ }
402
+
403
+ async function handleMarkConversationRead(args: any) {
404
+ const { space_id, conversation_id } = args as {
405
+ space_id?: string
406
+ conversation_id?: string
407
+ }
408
+
409
+ if (!space_id || !conversation_id) {
410
+ throw new Error("space_id and conversation_id are required")
411
+ }
412
+
413
+ return makeApiCall("conversation-read", args)
414
+ }
415
+
367
416
  async function handleViewSpaces() {
368
417
  return makeApiCall("list-spaces", {})
369
418
  }
370
419
 
420
+ async function handleViewGroups(args: any) {
421
+ const { space_id } = args as { space_id?: string }
422
+ if (!space_id) {
423
+ throw new Error(
424
+ "space_id is required. Use view_spaces to see available options."
425
+ )
426
+ }
427
+
428
+ return makeApiCall("view-groups", { space_id })
429
+ }
430
+
371
431
  async function handleViewProjects(args: any) {
372
432
  const { space_id } = args as { space_id: string }
373
433
  if (!space_id) {
@@ -391,15 +451,110 @@ try {
391
451
  )
392
452
  }
393
453
 
394
- if (slug && !space_id) {
454
+ if (!space_id) {
395
455
  throw new Error(
396
- "space_id is required when using slug. Use view_spaces to see available options."
456
+ "space_id is required. Use view_spaces to see available options."
397
457
  )
398
458
  }
399
459
 
400
460
  return makeApiCall("view-project", { project_id, space_id, slug })
401
461
  }
402
462
 
463
+ async function handleCreateGroup(args: any) {
464
+ const { space_id, name } = args as { space_id?: string; name?: string }
465
+ if (!space_id || !name) {
466
+ throw new Error("space_id and name are required")
467
+ }
468
+
469
+ return makeApiCall("create-group", { space_id, name })
470
+ }
471
+
472
+ async function handleRenameGroup(args: any) {
473
+ const { space_id, group_id, name } = args as {
474
+ space_id?: string
475
+ group_id?: string
476
+ name?: string
477
+ }
478
+
479
+ if (!space_id || !group_id || !name) {
480
+ throw new Error("space_id, group_id, and name are required")
481
+ }
482
+
483
+ return makeApiCall("rename-group", { space_id, group_id, name })
484
+ }
485
+
486
+ async function handleDeleteGroup(args: any) {
487
+ const { space_id, group_id } = args as {
488
+ space_id?: string
489
+ group_id?: string
490
+ }
491
+
492
+ if (!space_id || !group_id) {
493
+ throw new Error("space_id and group_id are required")
494
+ }
495
+
496
+ return makeApiCall("delete-group", { space_id, group_id })
497
+ }
498
+
499
+ async function handleCreateProject(args: any) {
500
+ const { space_id, name, group_id } = args as {
501
+ space_id?: string
502
+ name?: string
503
+ group_id?: string
504
+ }
505
+
506
+ if (!space_id || !name) {
507
+ throw new Error("space_id and name are required")
508
+ }
509
+
510
+ return makeApiCall("create-project", { space_id, name, group_id })
511
+ }
512
+
513
+ async function handleRenameProject(args: any) {
514
+ const { space_id, project_id, name } = args as {
515
+ space_id?: string
516
+ project_id?: string
517
+ name?: string
518
+ }
519
+
520
+ if (!space_id || !project_id || !name) {
521
+ throw new Error("space_id, project_id, and name are required")
522
+ }
523
+
524
+ return makeApiCall("rename-project", { space_id, project_id, name })
525
+ }
526
+
527
+ async function handleDeleteProject(args: any) {
528
+ const { space_id, project_id } = args as {
529
+ space_id?: string
530
+ project_id?: string
531
+ }
532
+
533
+ if (!space_id || !project_id) {
534
+ throw new Error("space_id and project_id are required")
535
+ }
536
+
537
+ return makeApiCall("delete-project", { space_id, project_id })
538
+ }
539
+
540
+ async function handleAssignProjectToGroup(args: any) {
541
+ const { space_id, project_id, group_id } = args as {
542
+ space_id?: string
543
+ project_id?: string
544
+ group_id?: string | null
545
+ }
546
+
547
+ if (!space_id || !project_id) {
548
+ throw new Error("space_id and project_id are required")
549
+ }
550
+
551
+ return makeApiCall("assign-project-to-group", {
552
+ space_id,
553
+ project_id,
554
+ group_id,
555
+ })
556
+ }
557
+
403
558
  async function handleCreateSpace(args: any) {
404
559
  return makeApiCall("create-space", args)
405
560
  }
@@ -513,16 +668,51 @@ try {
513
668
  case "fetch_items":
514
669
  result = await handleFetch(args)
515
670
  break
671
+ case "fetch_conversation_messages":
672
+ result = await handleFetchConversationMessages(args)
673
+ break
674
+ case "subscribe_conversation":
675
+ result = await handleSubscribeConversation(args)
676
+ break
677
+ case "list_conversation_inbox":
678
+ result = await handleConversationInbox(args)
679
+ break
680
+ case "mark_conversation_read":
681
+ result = await handleMarkConversationRead(args)
682
+ break
516
683
  case "view_spaces":
517
684
  result = await handleViewSpaces()
518
685
  break
519
- // PROJECTS DISABLED - Projects have been disabled. May be re-enabled later.
520
- // case "view_projects":
521
- // result = await handleViewProjects(args)
522
- // break
523
- // case "view_project":
524
- // result = await handleViewProject(args)
525
- // break
686
+ case "view_groups":
687
+ result = await handleViewGroups(args)
688
+ break
689
+ case "create_group":
690
+ result = await handleCreateGroup(args)
691
+ break
692
+ case "rename_group":
693
+ result = await handleRenameGroup(args)
694
+ break
695
+ case "delete_group":
696
+ result = await handleDeleteGroup(args)
697
+ break
698
+ case "view_projects":
699
+ result = await handleViewProjects(args)
700
+ break
701
+ case "view_project":
702
+ result = await handleViewProject(args)
703
+ break
704
+ case "create_project":
705
+ result = await handleCreateProject(args)
706
+ break
707
+ case "rename_project":
708
+ result = await handleRenameProject(args)
709
+ break
710
+ case "delete_project":
711
+ result = await handleDeleteProject(args)
712
+ break
713
+ case "assign_project_to_group":
714
+ result = await handleAssignProjectToGroup(args)
715
+ break
526
716
  case "create_space":
527
717
  result = await handleCreateSpace(args)
528
718
  break
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intangle/mcp-server",
3
- "version": "2.5.6",
3
+ "version": "2.6.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",
@@ -49,50 +49,404 @@ const TOOL_DEFINITIONS = [
49
49
  }
50
50
  }
51
51
  },
52
- // PROJECTS DISABLED - Projects have been disabled. May be re-enabled later.
53
- // {
54
- // name: "view_projects",
55
- // title: "View Projects",
56
- // description:
57
- // "List all projects in a space. Projects are user-created canvases that organize context, tasks, documents, and skills. Use this to discover available projects before viewing their contents.",
58
- // inputSchema: {
59
- // type: "object",
60
- // properties: {
61
- // space_id: {
62
- // type: "string",
63
- // description:
64
- // "REQUIRED: Space to list projects from (use view_spaces to see available options)"
65
- // }
66
- // },
67
- // required: ["space_id"]
68
- // }
69
- // },
70
- // {
71
- // name: "view_project",
72
- // title: "View Project",
73
- // description:
74
- // "View a specific project with all its items (context, tasks, documents, skills). Returns item metadata (IDs, titles, types, status) for each item in the project. Use fetch_items to get full content of specific items.",
75
- // inputSchema: {
76
- // type: "object",
77
- // properties: {
78
- // project_id: {
79
- // type: "string",
80
- // description:
81
- // "Project ID to view (e.g., 'proj_123...'). Get project IDs from view_projects."
82
- // },
83
- // space_id: {
84
- // type: "string",
85
- // description:
86
- // "Optional: Space ID (required if using slug instead of project_id)"
87
- // },
88
- // slug: {
89
- // type: "string",
90
- // description:
91
- // "Optional: Project slug (URL-friendly name). Requires space_id if used."
92
- // }
93
- // }
94
- // }
95
- // },
52
+ {
53
+ name: "fetch_conversation_messages",
54
+ title: "Fetch Conversation Messages",
55
+ 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`.",
57
+ inputSchema: {
58
+ type: "object",
59
+ properties: {
60
+ space_id: {
61
+ type: "string",
62
+ description:
63
+ "REQUIRED: Space containing the conversation (use view_spaces first)."
64
+ },
65
+ conversation_id: {
66
+ type: "string",
67
+ description:
68
+ "REQUIRED: Chat summary / conversation ID to read (e.g. 'summary_123...')."
69
+ },
70
+ organization_id: {
71
+ type: "string",
72
+ description:
73
+ "Optional Clerk org ID when the space belongs to an organization graph."
74
+ },
75
+ since_timestamp: {
76
+ type: "integer",
77
+ description:
78
+ "Optional millisecond cursor. Only messages newer than this timestamp are returned."
79
+ },
80
+ limit: {
81
+ type: "integer",
82
+ description:
83
+ "Optional page size from 1 to 200 (default 50)."
84
+ }
85
+ },
86
+ required: ["space_id", "conversation_id"]
87
+ }
88
+ },
89
+ {
90
+ name: "subscribe_conversation",
91
+ title: "Subscribe Conversation",
92
+ 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.",
94
+ inputSchema: {
95
+ type: "object",
96
+ properties: {
97
+ space_id: {
98
+ type: "string",
99
+ description:
100
+ "REQUIRED: Space containing the conversation (use view_spaces first)."
101
+ },
102
+ conversation_id: {
103
+ type: "string",
104
+ description:
105
+ "REQUIRED: Chat summary / conversation ID to subscribe to."
106
+ },
107
+ organization_id: {
108
+ type: "string",
109
+ description:
110
+ "Optional Clerk org ID when the space belongs to an organization graph."
111
+ },
112
+ participant_id: {
113
+ type: "string",
114
+ description:
115
+ "Optional stable participant ID. Use an explicit `user_*` or `agent_*` style ID when you want the same participant identity reused across calls."
116
+ },
117
+ participant_type: {
118
+ type: "string",
119
+ description:
120
+ "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
121
+ },
122
+ participant_label: {
123
+ type: "string",
124
+ description:
125
+ "Optional display label for the participant in collaborative chat UIs."
126
+ },
127
+ mark_read: {
128
+ type: "boolean",
129
+ description:
130
+ "Optional. Defaults to true. When true, the subscription starts caught up at the current latest message."
131
+ }
132
+ },
133
+ required: ["space_id", "conversation_id"]
134
+ }
135
+ },
136
+ {
137
+ name: "list_conversation_inbox",
138
+ title: "List Conversation Inbox",
139
+ 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.",
141
+ inputSchema: {
142
+ type: "object",
143
+ properties: {
144
+ space_id: {
145
+ type: "string",
146
+ description:
147
+ "REQUIRED: Space to list inbox conversations from (use view_spaces first)."
148
+ },
149
+ organization_id: {
150
+ type: "string",
151
+ description:
152
+ "Optional Clerk org ID when the space belongs to an organization graph."
153
+ },
154
+ participant_id: {
155
+ type: "string",
156
+ description:
157
+ "Optional stable participant ID. Use the same value you used for message/subscribe calls to read the same inbox."
158
+ },
159
+ participant_type: {
160
+ type: "string",
161
+ description:
162
+ "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
163
+ },
164
+ participant_label: {
165
+ type: "string",
166
+ description:
167
+ "Optional display label used when the participant identity is being created for the first time."
168
+ },
169
+ unread_only: {
170
+ type: "boolean",
171
+ description:
172
+ "Optional. When true, only return conversations that currently have unread messages."
173
+ },
174
+ since_timestamp: {
175
+ type: "integer",
176
+ description:
177
+ "Optional millisecond cursor. Only conversations updated after this timestamp are returned."
178
+ },
179
+ limit: {
180
+ type: "integer",
181
+ description:
182
+ "Optional page size from 1 to 200 (default 50)."
183
+ }
184
+ },
185
+ required: ["space_id"]
186
+ }
187
+ },
188
+ {
189
+ name: "mark_conversation_read",
190
+ title: "Mark Conversation Read",
191
+ description:
192
+ "Advance a participant's read cursor for a shared conversation. Use this after processing messages returned from fetch_conversation_messages.",
193
+ inputSchema: {
194
+ type: "object",
195
+ properties: {
196
+ space_id: {
197
+ type: "string",
198
+ description:
199
+ "REQUIRED: Space containing the conversation (use view_spaces first)."
200
+ },
201
+ conversation_id: {
202
+ type: "string",
203
+ description:
204
+ "REQUIRED: Chat summary / conversation ID to mark as read."
205
+ },
206
+ organization_id: {
207
+ type: "string",
208
+ description:
209
+ "Optional Clerk org ID when the space belongs to an organization graph."
210
+ },
211
+ participant_id: {
212
+ type: "string",
213
+ description:
214
+ "Optional stable participant ID. Use the same value you used for message/subscribe calls."
215
+ },
216
+ participant_type: {
217
+ type: "string",
218
+ description:
219
+ "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
220
+ },
221
+ participant_label: {
222
+ type: "string",
223
+ description:
224
+ "Optional display label used when the participant identity is being created for the first time."
225
+ },
226
+ read_at: {
227
+ type: "integer",
228
+ description:
229
+ "Optional millisecond cursor to advance to. If omitted, the conversation is marked read through its latest message."
230
+ }
231
+ },
232
+ required: ["space_id", "conversation_id"]
233
+ }
234
+ },
235
+ {
236
+ name: "view_groups",
237
+ title: "View Groups",
238
+ description:
239
+ "List all groups in a space, including the projects currently inside each group plus any ungrouped projects.",
240
+ inputSchema: {
241
+ type: "object",
242
+ properties: {
243
+ space_id: {
244
+ type: "string",
245
+ description:
246
+ "REQUIRED: Space to list groups from (use view_spaces to see available options)"
247
+ }
248
+ },
249
+ required: ["space_id"]
250
+ }
251
+ },
252
+ {
253
+ name: "create_group",
254
+ title: "Create Group",
255
+ description: "Create a new group inside a space.",
256
+ inputSchema: {
257
+ type: "object",
258
+ properties: {
259
+ space_id: {
260
+ type: "string",
261
+ description:
262
+ "REQUIRED: Space to create the group in."
263
+ },
264
+ name: {
265
+ type: "string",
266
+ description: "Name for the new group."
267
+ }
268
+ },
269
+ required: ["space_id", "name"]
270
+ }
271
+ },
272
+ {
273
+ name: "rename_group",
274
+ title: "Rename Group",
275
+ description: "Rename an existing group.",
276
+ inputSchema: {
277
+ type: "object",
278
+ properties: {
279
+ space_id: {
280
+ type: "string",
281
+ description: "REQUIRED: Space containing the group."
282
+ },
283
+ group_id: {
284
+ type: "string",
285
+ description: "REQUIRED: Group ID to rename."
286
+ },
287
+ name: {
288
+ type: "string",
289
+ description: "REQUIRED: New group name."
290
+ }
291
+ },
292
+ required: ["space_id", "group_id", "name"]
293
+ }
294
+ },
295
+ {
296
+ name: "delete_group",
297
+ title: "Delete Group",
298
+ description:
299
+ "Delete a group. Projects inside it are preserved and become ungrouped.",
300
+ inputSchema: {
301
+ type: "object",
302
+ properties: {
303
+ space_id: {
304
+ type: "string",
305
+ description: "REQUIRED: Space containing the group."
306
+ },
307
+ group_id: {
308
+ type: "string",
309
+ description: "REQUIRED: Group ID to delete."
310
+ }
311
+ },
312
+ required: ["space_id", "group_id"]
313
+ }
314
+ },
315
+ {
316
+ name: "view_projects",
317
+ title: "View Projects",
318
+ description:
319
+ "List all projects in a space, including which group each project belongs to.",
320
+ inputSchema: {
321
+ type: "object",
322
+ properties: {
323
+ space_id: {
324
+ type: "string",
325
+ description:
326
+ "REQUIRED: Space to list projects from (use view_spaces to see available options)"
327
+ }
328
+ },
329
+ required: ["space_id"]
330
+ }
331
+ },
332
+ {
333
+ name: "view_project",
334
+ title: "View Project",
335
+ description:
336
+ "View a specific project with all its items. Use fetch_items to retrieve full content for specific item IDs.",
337
+ inputSchema: {
338
+ type: "object",
339
+ properties: {
340
+ project_id: {
341
+ type: "string",
342
+ description:
343
+ "Project ID to view (e.g., 'proj_123...'). Get project IDs from view_projects."
344
+ },
345
+ space_id: {
346
+ type: "string",
347
+ description:
348
+ "REQUIRED: Space containing the project."
349
+ },
350
+ slug: {
351
+ type: "string",
352
+ description:
353
+ "Optional: Project slug (URL-friendly name)."
354
+ }
355
+ },
356
+ required: ["space_id"]
357
+ }
358
+ },
359
+ {
360
+ name: "create_project",
361
+ title: "Create Project",
362
+ description:
363
+ "Create a new project in a space. Optionally place it inside a group immediately.",
364
+ inputSchema: {
365
+ type: "object",
366
+ properties: {
367
+ space_id: {
368
+ type: "string",
369
+ description: "REQUIRED: Space to create the project in."
370
+ },
371
+ name: {
372
+ type: "string",
373
+ description: "REQUIRED: Project name."
374
+ },
375
+ group_id: {
376
+ type: "string",
377
+ description: "Optional: Group ID to place the project inside."
378
+ }
379
+ },
380
+ required: ["space_id", "name"]
381
+ }
382
+ },
383
+ {
384
+ name: "rename_project",
385
+ title: "Rename Project",
386
+ description: "Rename an existing project.",
387
+ inputSchema: {
388
+ type: "object",
389
+ properties: {
390
+ space_id: {
391
+ type: "string",
392
+ description: "REQUIRED: Space containing the project."
393
+ },
394
+ project_id: {
395
+ type: "string",
396
+ description: "REQUIRED: Project ID to rename."
397
+ },
398
+ name: {
399
+ type: "string",
400
+ description: "REQUIRED: New project name."
401
+ }
402
+ },
403
+ required: ["space_id", "project_id", "name"]
404
+ }
405
+ },
406
+ {
407
+ name: "delete_project",
408
+ title: "Delete Project",
409
+ description: "Delete an existing project from a space.",
410
+ inputSchema: {
411
+ type: "object",
412
+ properties: {
413
+ space_id: {
414
+ type: "string",
415
+ description: "REQUIRED: Space containing the project."
416
+ },
417
+ project_id: {
418
+ type: "string",
419
+ description: "REQUIRED: Project ID to delete."
420
+ }
421
+ },
422
+ required: ["space_id", "project_id"]
423
+ }
424
+ },
425
+ {
426
+ name: "assign_project_to_group",
427
+ title: "Assign Project To Group",
428
+ description:
429
+ "Move a project into a group. Omit group_id to make the project ungrouped.",
430
+ inputSchema: {
431
+ type: "object",
432
+ properties: {
433
+ space_id: {
434
+ type: "string",
435
+ description: "REQUIRED: Space containing the project."
436
+ },
437
+ project_id: {
438
+ type: "string",
439
+ description: "REQUIRED: Project ID to move."
440
+ },
441
+ group_id: {
442
+ type: "string",
443
+ description:
444
+ "Optional: Target group ID. Omit this to ungroup the project."
445
+ }
446
+ },
447
+ required: ["space_id", "project_id"]
448
+ }
449
+ },
96
450
  {
97
451
  name: "start",
98
452
  title: "Start Space Session",
@@ -454,7 +808,7 @@ const TOOL_DEFINITIONS = [
454
808
  name: "message",
455
809
  title: "Message Assistant",
456
810
  description:
457
- "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.",
811
+ "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.",
458
812
  inputSchema: {
459
813
  type: "object",
460
814
  properties: {
@@ -495,6 +849,26 @@ const TOOL_DEFINITIONS = [
495
849
  description:
496
850
  "Optional timeout in milliseconds (10,000 to 300,000; default 120,000)."
497
851
  },
852
+ wait_for_response: {
853
+ type: "boolean",
854
+ description:
855
+ "Optional. Defaults to true. Set false to submit the turn asynchronously and poll the conversation later with fetch_conversation_messages."
856
+ },
857
+ participant_id: {
858
+ type: "string",
859
+ description:
860
+ "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."
861
+ },
862
+ participant_type: {
863
+ type: "string",
864
+ description:
865
+ "Optional participant type. Defaults to `user`. Supported values: `user`, `agent`, `assistant`, `connector`."
866
+ },
867
+ participant_label: {
868
+ type: "string",
869
+ description:
870
+ "Optional display label for the participant in collaborative chat UIs."
871
+ },
498
872
  timezone: {
499
873
  type: "string",
500
874
  description: "Optional IANA timezone for assistant context."
@@ -531,6 +905,8 @@ const TOOL_DEFINITIONS = [
531
905
  const READ_ONLY_TOOLS = new Set([
532
906
  "search",
533
907
  "fetch_items",
908
+ "fetch_conversation_messages",
909
+ "list_conversation_inbox",
534
910
  "start",
535
911
  "view_spaces",
536
912
  "view_space"
@@ -545,6 +921,8 @@ const DESTRUCTIVE_TOOLS = new Set([
545
921
  const IDEMPOTENT_TOOLS = new Set([
546
922
  "search",
547
923
  "fetch_items",
924
+ "fetch_conversation_messages",
925
+ "list_conversation_inbox",
548
926
  "start",
549
927
  "view_spaces",
550
928
  "view_space"