@burdenoff/mcp-libs 2026.627.1 → 2026.627.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.
Files changed (73) hide show
  1. package/dist/modules/artistrybase/index.cjs +2112 -0
  2. package/dist/modules/artistrybase/index.cjs.map +1 -0
  3. package/dist/modules/artistrybase/index.d.cts +8 -0
  4. package/dist/modules/artistrybase/index.d.ts +8 -0
  5. package/dist/modules/artistrybase/index.js +2051 -0
  6. package/dist/modules/artistrybase/index.js.map +1 -0
  7. package/dist/modules/assethandler/index.cjs +1601 -0
  8. package/dist/modules/assethandler/index.cjs.map +1 -0
  9. package/dist/modules/assethandler/index.d.cts +8 -0
  10. package/dist/modules/assethandler/index.d.ts +8 -0
  11. package/dist/modules/assethandler/index.js +1540 -0
  12. package/dist/modules/assethandler/index.js.map +1 -0
  13. package/dist/modules/brainyrich/index.cjs +2148 -0
  14. package/dist/modules/brainyrich/index.cjs.map +1 -0
  15. package/dist/modules/brainyrich/index.d.cts +8 -0
  16. package/dist/modules/brainyrich/index.d.ts +8 -0
  17. package/dist/modules/brainyrich/index.js +2087 -0
  18. package/dist/modules/brainyrich/index.js.map +1 -0
  19. package/dist/modules/buildmyiq/index.cjs +1562 -0
  20. package/dist/modules/buildmyiq/index.cjs.map +1 -0
  21. package/dist/modules/buildmyiq/index.d.cts +8 -0
  22. package/dist/modules/buildmyiq/index.d.ts +8 -0
  23. package/dist/modules/buildmyiq/index.js +1501 -0
  24. package/dist/modules/buildmyiq/index.js.map +1 -0
  25. package/dist/modules/collabkin/index.cjs +1577 -0
  26. package/dist/modules/collabkin/index.cjs.map +1 -0
  27. package/dist/modules/collabkin/index.d.cts +8 -0
  28. package/dist/modules/collabkin/index.d.ts +8 -0
  29. package/dist/modules/collabkin/index.js +1516 -0
  30. package/dist/modules/collabkin/index.js.map +1 -0
  31. package/dist/modules/comradecircle/index.cjs +1817 -0
  32. package/dist/modules/comradecircle/index.cjs.map +1 -0
  33. package/dist/modules/comradecircle/index.d.cts +8 -0
  34. package/dist/modules/comradecircle/index.d.ts +8 -0
  35. package/dist/modules/comradecircle/index.js +1756 -0
  36. package/dist/modules/comradecircle/index.js.map +1 -0
  37. package/dist/modules/eventfullymanaged/index.cjs +3364 -0
  38. package/dist/modules/eventfullymanaged/index.cjs.map +1 -0
  39. package/dist/modules/eventfullymanaged/index.d.cts +8 -0
  40. package/dist/modules/eventfullymanaged/index.d.ts +8 -0
  41. package/dist/modules/eventfullymanaged/index.js +3303 -0
  42. package/dist/modules/eventfullymanaged/index.js.map +1 -0
  43. package/dist/modules/headshotmarketing/index.cjs +5031 -0
  44. package/dist/modules/headshotmarketing/index.cjs.map +1 -0
  45. package/dist/modules/headshotmarketing/index.d.cts +8 -0
  46. package/dist/modules/headshotmarketing/index.d.ts +8 -0
  47. package/dist/modules/headshotmarketing/index.js +4970 -0
  48. package/dist/modules/headshotmarketing/index.js.map +1 -0
  49. package/dist/modules/movethewheels/index.cjs +3915 -0
  50. package/dist/modules/movethewheels/index.cjs.map +1 -0
  51. package/dist/modules/movethewheels/index.d.cts +8 -0
  52. package/dist/modules/movethewheels/index.d.ts +8 -0
  53. package/dist/modules/movethewheels/index.js +3854 -0
  54. package/dist/modules/movethewheels/index.js.map +1 -0
  55. package/dist/modules/planmagnet/index.cjs +6171 -0
  56. package/dist/modules/planmagnet/index.cjs.map +1 -0
  57. package/dist/modules/planmagnet/index.d.cts +8 -0
  58. package/dist/modules/planmagnet/index.d.ts +8 -0
  59. package/dist/modules/planmagnet/index.js +6110 -0
  60. package/dist/modules/planmagnet/index.js.map +1 -0
  61. package/dist/modules/proserviceworld/index.cjs +1404 -0
  62. package/dist/modules/proserviceworld/index.cjs.map +1 -0
  63. package/dist/modules/proserviceworld/index.d.cts +8 -0
  64. package/dist/modules/proserviceworld/index.d.ts +8 -0
  65. package/dist/modules/proserviceworld/index.js +1343 -0
  66. package/dist/modules/proserviceworld/index.js.map +1 -0
  67. package/dist/modules/timecampus/index.cjs +6482 -0
  68. package/dist/modules/timecampus/index.cjs.map +1 -0
  69. package/dist/modules/timecampus/index.d.cts +8 -0
  70. package/dist/modules/timecampus/index.d.ts +8 -0
  71. package/dist/modules/timecampus/index.js +6421 -0
  72. package/dist/modules/timecampus/index.js.map +1 -0
  73. package/package.json +1 -1
@@ -0,0 +1,1817 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ require('graphql-request');
5
+ require('cross-fetch');
6
+ require('@modelcontextprotocol/sdk/server/mcp.js');
7
+ require('@modelcontextprotocol/sdk/types.js');
8
+
9
+ // src/modules/comradecircle/tools/circles.ts
10
+
11
+ // src/core/errors.ts
12
+ var McpInternalError = class extends Error {
13
+ cause;
14
+ constructor(message, cause) {
15
+ super(message);
16
+ this.name = "McpInternalError";
17
+ this.cause = cause;
18
+ }
19
+ };
20
+
21
+ // src/core/result.ts
22
+ function jsonResult(data, structured) {
23
+ const result = {
24
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
25
+ };
26
+ return result;
27
+ }
28
+ function errorResult(message, hint) {
29
+ const text = hint ? `${message}
30
+
31
+ Hint: ${hint}` : message;
32
+ return { content: [{ type: "text", text }], isError: true };
33
+ }
34
+
35
+ // src/core/tool-name.ts
36
+ var MAX_TOOL_NAME_LENGTH = 63;
37
+ function assertSafeName(name, kind) {
38
+ if (name.length > MAX_TOOL_NAME_LENGTH) {
39
+ throw new McpInternalError(
40
+ `Tool name '${name}' exceeds ${MAX_TOOL_NAME_LENGTH} characters (some MCP clients truncate at 64).`
41
+ );
42
+ }
43
+ return name;
44
+ }
45
+ function mcpToolName(productName, ...parts) {
46
+ if (!productName) {
47
+ throw new McpInternalError("mcpToolName: productName is required");
48
+ }
49
+ const name = [productName, ...parts].filter(Boolean).join("_");
50
+ return assertSafeName(name);
51
+ }
52
+ function mcpResourceUri(productName, ...parts) {
53
+ if (!productName) {
54
+ throw new McpInternalError("mcpResourceUri: productName is required");
55
+ }
56
+ return `${productName}://${parts.filter(Boolean).join("/")}`;
57
+ }
58
+ function mcpPromptName(productName, ...parts) {
59
+ if (!productName) {
60
+ throw new McpInternalError("mcpPromptName: productName is required");
61
+ }
62
+ return [productName, ...parts].filter(Boolean).join("-");
63
+ }
64
+
65
+ // src/modules/comradecircle/operations.ts
66
+ var PAGE_INFO_FIELDS = `cursor hasMore`;
67
+ var CIRCLE_FIELDS = `
68
+ id name slug description topic tags status visibility memberCount
69
+ workspaceId createdAt updatedAt
70
+ avatar { id }
71
+ `;
72
+ var CHANNEL_FIELDS = `
73
+ id circleId name slug description type visibility position isArchived
74
+ workspaceId createdAt updatedAt
75
+ `;
76
+ var MEMBERSHIP_FIELDS = `
77
+ id circleId memberId role status joinedAt workspaceId createdAt updatedAt
78
+ member { id }
79
+ `;
80
+ var PROFILE_FIELDS = `
81
+ id memberId displayName headline bio interests skills presence discoverable
82
+ workspaceId createdAt updatedAt
83
+ avatar { id }
84
+ member { id }
85
+ `;
86
+ var COMMENT_FIELDS = `
87
+ id postId parentId authorId body isHidden workspaceId createdAt updatedAt
88
+ author { id }
89
+ `;
90
+ var REACTION_FIELDS = `
91
+ id postId memberId type createdAt workspaceId
92
+ member { id }
93
+ `;
94
+ var POST_FIELDS = `
95
+ id channelId authorId body status tags attachmentFileIds isPinned
96
+ commentCount reactionCount workspaceId createdAt updatedAt
97
+ author { id }
98
+ `;
99
+ var RSVP_FIELDS = `
100
+ id eventId memberId status note workspaceId createdAt updatedAt
101
+ member { id }
102
+ `;
103
+ var EVENT_FIELDS = `
104
+ id circleId hostId title description mode status location joinUrl capacity
105
+ rsvpCount startsAt endsAt tags workspaceId createdAt updatedAt
106
+ host { id }
107
+ `;
108
+ var POLL_OPTION_FIELDS = `
109
+ id pollId label position voteCount workspaceId createdAt
110
+ `;
111
+ var POLL_FIELDS = `
112
+ id circleId question status isAnonymous allowMultiple closesAt
113
+ workspaceId createdAt updatedAt
114
+ options { ${POLL_OPTION_FIELDS} }
115
+ `;
116
+ var FEED_FOLLOW_FIELDS = `
117
+ id followerId targetId targetType muted workspaceId createdAt updatedAt
118
+ `;
119
+ var GET_CIRCLE = `
120
+ query ComradecircleGetCircle($id: ID!) {
121
+ comCircle(id: $id) {
122
+ ${CIRCLE_FIELDS}
123
+ channels { ${CHANNEL_FIELDS} }
124
+ memberships { ${MEMBERSHIP_FIELDS} }
125
+ }
126
+ }
127
+ `;
128
+ var LIST_CIRCLES = `
129
+ query ComradecircleListCircles(
130
+ $pagination: PaginationInput
131
+ $status: ComCircleStatus
132
+ $visibility: ComCircleVisibility
133
+ ) {
134
+ comListCircles(pagination: $pagination, status: $status, visibility: $visibility) {
135
+ items { ${CIRCLE_FIELDS} }
136
+ pageInfo { ${PAGE_INFO_FIELDS} }
137
+ }
138
+ }
139
+ `;
140
+ var CREATE_CIRCLE = `
141
+ mutation ComradecircleCreateCircle($input: ComCreateCircleInput!) {
142
+ comCreateCircle(input: $input) {
143
+ ${CIRCLE_FIELDS}
144
+ }
145
+ }
146
+ `;
147
+ var UPDATE_CIRCLE = `
148
+ mutation ComradecircleUpdateCircle($id: ID!, $input: ComUpdateCircleInput!) {
149
+ comUpdateCircle(id: $id, input: $input) {
150
+ ${CIRCLE_FIELDS}
151
+ }
152
+ }
153
+ `;
154
+ var ARCHIVE_CIRCLE = `
155
+ mutation ComradecircleArchiveCircle($id: ID!) {
156
+ comArchiveCircle(id: $id)
157
+ }
158
+ `;
159
+ var JOIN_CIRCLE = `
160
+ mutation ComradecircleJoinCircle($circleId: ID!) {
161
+ comJoinCircle(circleId: $circleId) {
162
+ ${MEMBERSHIP_FIELDS}
163
+ }
164
+ }
165
+ `;
166
+ var LEAVE_CIRCLE = `
167
+ mutation ComradecircleLeaveCircle($circleId: ID!) {
168
+ comLeaveCircle(circleId: $circleId)
169
+ }
170
+ `;
171
+ var LIST_CHANNELS = `
172
+ query ComradecircleListChannels($circleId: ID!) {
173
+ comListChannels(circleId: $circleId) {
174
+ ${CHANNEL_FIELDS}
175
+ }
176
+ }
177
+ `;
178
+ var CREATE_CHANNEL = `
179
+ mutation ComradecircleCreateChannel($input: ComCreateChannelInput!) {
180
+ comCreateChannel(input: $input) {
181
+ ${CHANNEL_FIELDS}
182
+ }
183
+ }
184
+ `;
185
+ var UPDATE_CHANNEL = `
186
+ mutation ComradecircleUpdateChannel($id: ID!, $input: ComUpdateChannelInput!) {
187
+ comUpdateChannel(id: $id, input: $input) {
188
+ ${CHANNEL_FIELDS}
189
+ }
190
+ }
191
+ `;
192
+ var LIST_MEMBERSHIPS = `
193
+ query ComradecircleListMemberships(
194
+ $circleId: ID
195
+ $pagination: PaginationInput
196
+ $status: ComMembershipStatus
197
+ ) {
198
+ comListMemberships(circleId: $circleId, pagination: $pagination, status: $status) {
199
+ items { ${MEMBERSHIP_FIELDS} }
200
+ pageInfo { ${PAGE_INFO_FIELDS} }
201
+ }
202
+ }
203
+ `;
204
+ var SET_MEMBER_ROLE = `
205
+ mutation ComradecircleSetMemberRole($membershipId: ID!, $role: ComMemberRole!) {
206
+ comSetMemberRole(membershipId: $membershipId, role: $role) {
207
+ ${MEMBERSHIP_FIELDS}
208
+ }
209
+ }
210
+ `;
211
+ var GET_PROFILE = `
212
+ query ComradecircleGetProfile($memberId: ID!) {
213
+ comProfile(memberId: $memberId) {
214
+ ${PROFILE_FIELDS}
215
+ }
216
+ }
217
+ `;
218
+ var LIST_PROFILES = `
219
+ query ComradecircleListProfiles($discoverable: Boolean, $pagination: PaginationInput) {
220
+ comListProfiles(discoverable: $discoverable, pagination: $pagination) {
221
+ items { ${PROFILE_FIELDS} }
222
+ pageInfo { ${PAGE_INFO_FIELDS} }
223
+ }
224
+ }
225
+ `;
226
+ var UPSERT_PROFILE = `
227
+ mutation ComradecircleUpsertProfile($input: ComUpsertProfileInput!) {
228
+ comUpsertProfile(input: $input) {
229
+ ${PROFILE_FIELDS}
230
+ }
231
+ }
232
+ `;
233
+ var GET_POST = `
234
+ query ComradecircleGetPost($id: ID!) {
235
+ comPost(id: $id) {
236
+ ${POST_FIELDS}
237
+ comments { ${COMMENT_FIELDS} }
238
+ reactions { ${REACTION_FIELDS} }
239
+ }
240
+ }
241
+ `;
242
+ var LIST_POSTS = `
243
+ query ComradecircleListPosts(
244
+ $channelId: ID
245
+ $pagination: PaginationInput
246
+ $status: ComPostStatus
247
+ ) {
248
+ comListPosts(channelId: $channelId, pagination: $pagination, status: $status) {
249
+ items { ${POST_FIELDS} }
250
+ pageInfo { ${PAGE_INFO_FIELDS} }
251
+ }
252
+ }
253
+ `;
254
+ var CREATE_POST = `
255
+ mutation ComradecircleCreatePost($input: ComCreatePostInput!) {
256
+ comCreatePost(input: $input) {
257
+ ${POST_FIELDS}
258
+ }
259
+ }
260
+ `;
261
+ var UPDATE_POST = `
262
+ mutation ComradecircleUpdatePost($id: ID!, $input: ComUpdatePostInput!) {
263
+ comUpdatePost(id: $id, input: $input) {
264
+ ${POST_FIELDS}
265
+ }
266
+ }
267
+ `;
268
+ var REMOVE_POST = `
269
+ mutation ComradecircleRemovePost($id: ID!) {
270
+ comRemovePost(id: $id)
271
+ }
272
+ `;
273
+ var REACT_TO_POST = `
274
+ mutation ComradecircleReactToPost($postId: ID!, $type: ComReactionType!) {
275
+ comReactToPost(postId: $postId, type: $type) {
276
+ ${POST_FIELDS}
277
+ reactions { ${REACTION_FIELDS} }
278
+ }
279
+ }
280
+ `;
281
+ var LIST_COMMENTS = `
282
+ query ComradecircleListComments($postId: ID!) {
283
+ comListComments(postId: $postId) {
284
+ ${COMMENT_FIELDS}
285
+ }
286
+ }
287
+ `;
288
+ var ADD_COMMENT = `
289
+ mutation ComradecircleAddComment($input: ComCreateCommentInput!) {
290
+ comAddComment(input: $input) {
291
+ ${COMMENT_FIELDS}
292
+ }
293
+ }
294
+ `;
295
+ var MY_FEED = `
296
+ query ComradecircleMyFeed($pagination: PaginationInput) {
297
+ comMyFeed(pagination: $pagination) {
298
+ items { ${POST_FIELDS} }
299
+ pageInfo { ${PAGE_INFO_FIELDS} }
300
+ }
301
+ }
302
+ `;
303
+ var MY_FOLLOWS = `
304
+ query ComradecircleMyFollows($targetType: ComFollowTargetType) {
305
+ comMyFollows(targetType: $targetType) {
306
+ ${FEED_FOLLOW_FIELDS}
307
+ }
308
+ }
309
+ `;
310
+ var FOLLOW = `
311
+ mutation ComradecircleFollow($input: ComFollowInput!) {
312
+ comFollow(input: $input) {
313
+ ${FEED_FOLLOW_FIELDS}
314
+ }
315
+ }
316
+ `;
317
+ var UNFOLLOW = `
318
+ mutation ComradecircleUnfollow($input: ComFollowInput!) {
319
+ comUnfollow(input: $input)
320
+ }
321
+ `;
322
+ var MUTE_FOLLOW = `
323
+ mutation ComradecircleMuteFollow($id: ID!, $muted: Boolean!) {
324
+ comMuteFollow(id: $id, muted: $muted) {
325
+ ${FEED_FOLLOW_FIELDS}
326
+ }
327
+ }
328
+ `;
329
+ var GET_EVENT = `
330
+ query ComradecircleGetEvent($id: ID!) {
331
+ comEvent(id: $id) {
332
+ ${EVENT_FIELDS}
333
+ rsvps { ${RSVP_FIELDS} }
334
+ }
335
+ }
336
+ `;
337
+ var LIST_EVENTS = `
338
+ query ComradecircleListEvents(
339
+ $circleId: ID
340
+ $pagination: PaginationInput
341
+ $status: ComEventStatus
342
+ ) {
343
+ comListEvents(circleId: $circleId, pagination: $pagination, status: $status) {
344
+ items { ${EVENT_FIELDS} }
345
+ pageInfo { ${PAGE_INFO_FIELDS} }
346
+ }
347
+ }
348
+ `;
349
+ var CREATE_EVENT = `
350
+ mutation ComradecircleCreateEvent($input: ComCreateEventInput!) {
351
+ comCreateEvent(input: $input) {
352
+ ${EVENT_FIELDS}
353
+ }
354
+ }
355
+ `;
356
+ var UPDATE_EVENT = `
357
+ mutation ComradecircleUpdateEvent($id: ID!, $input: ComUpdateEventInput!) {
358
+ comUpdateEvent(id: $id, input: $input) {
359
+ ${EVENT_FIELDS}
360
+ }
361
+ }
362
+ `;
363
+ var TRANSITION_EVENT = `
364
+ mutation ComradecircleTransitionEvent($id: ID!, $status: ComEventStatus!) {
365
+ comTransitionEvent(id: $id, status: $status) {
366
+ ${EVENT_FIELDS}
367
+ }
368
+ }
369
+ `;
370
+ var RSVP_EVENT = `
371
+ mutation ComradecircleRsvpEvent($eventId: ID!, $status: ComRsvpStatus!, $note: String) {
372
+ comRsvpEvent(eventId: $eventId, status: $status, note: $note) {
373
+ ${RSVP_FIELDS}
374
+ }
375
+ }
376
+ `;
377
+ var GET_POLL = `
378
+ query ComradecircleGetPoll($id: ID!) {
379
+ comPoll(id: $id) {
380
+ ${POLL_FIELDS}
381
+ }
382
+ }
383
+ `;
384
+ var LIST_POLLS = `
385
+ query ComradecircleListPolls($circleId: ID!, $status: ComPollStatus) {
386
+ comListPolls(circleId: $circleId, status: $status) {
387
+ ${POLL_FIELDS}
388
+ }
389
+ }
390
+ `;
391
+ var CREATE_POLL = `
392
+ mutation ComradecircleCreatePoll($input: ComCreatePollInput!) {
393
+ comCreatePoll(input: $input) {
394
+ ${POLL_FIELDS}
395
+ }
396
+ }
397
+ `;
398
+ var CLOSE_POLL = `
399
+ mutation ComradecircleClosePoll($id: ID!) {
400
+ comClosePoll(id: $id) {
401
+ ${POLL_FIELDS}
402
+ }
403
+ }
404
+ `;
405
+ var VOTE_POLL = `
406
+ mutation ComradecircleVotePoll($pollId: ID!, $optionId: ID!) {
407
+ comVotePoll(pollId: $pollId, optionId: $optionId) {
408
+ ${POLL_OPTION_FIELDS}
409
+ }
410
+ }
411
+ `;
412
+ var HEALTH = `
413
+ query ComradecircleHealth {
414
+ comHealth
415
+ }
416
+ `;
417
+ var ChannelTypeEnum = zod.z.enum(["ANNOUNCEMENT", "FORUM", "TEXT", "VOICE"]).describe("Kind of channel within a circle");
418
+ var ChannelVisibilityEnum = zod.z.enum(["CIRCLE", "PRIVATE"]).describe("Who can see a channel: CIRCLE-wide or PRIVATE");
419
+ var CircleStatusEnum = zod.z.enum(["ACTIVE", "ARCHIVED", "SUSPENDED"]).describe("Lifecycle/moderation status of a circle");
420
+ var CircleVisibilityEnum = zod.z.enum(["PUBLIC", "PRIVATE", "SECRET"]).describe("Discoverability of a circle: PUBLIC, PRIVATE (join-by-request), or SECRET");
421
+ var EventModeEnum = zod.z.enum(["ONLINE", "IN_PERSON", "HYBRID"]).describe("Delivery mode of an event");
422
+ var EventStatusEnum = zod.z.enum(["SCHEDULED", "LIVE", "COMPLETED", "CANCELLED"]).describe("Lifecycle status of an event");
423
+ var FollowTargetTypeEnum = zod.z.enum(["CIRCLE", "CHANNEL", "MEMBER"]).describe("What a feed-follow targets");
424
+ var MemberRoleEnum = zod.z.enum(["OWNER", "ADMIN", "MODERATOR", "MEMBER"]).describe("Role of a member within a circle");
425
+ var MembershipStatusEnum = zod.z.enum(["ACTIVE", "PENDING", "LEFT", "BANNED"]).describe("Status of a circle membership");
426
+ var PollStatusEnum = zod.z.enum(["OPEN", "CLOSED"]).describe("Lifecycle status of a poll");
427
+ var PostStatusEnum = zod.z.enum(["DRAFT", "PUBLISHED", "HIDDEN", "REMOVED"]).describe("Publication status of a post");
428
+ var PresenceEnum = zod.z.enum(["ONLINE", "AWAY", "BUSY", "OFFLINE"]).describe("Presence indicator of a member profile");
429
+ var ReactionTypeEnum = zod.z.enum(["LIKE", "LOVE", "CELEBRATE", "INSIGHTFUL", "SUPPORT", "LAUGH"]).describe("Typed reaction on a post");
430
+ var RsvpStatusEnum = zod.z.enum(["GOING", "INTERESTED", "NOT_GOING", "WAITLISTED"]).describe("RSVP response to an event");
431
+ function buildPagination(limit, offset, cursor) {
432
+ if (limit === void 0 && offset === void 0 && cursor === void 0) return void 0;
433
+ const pagination = {};
434
+ if (limit !== void 0) pagination.limit = limit;
435
+ if (offset !== void 0) pagination.offset = offset;
436
+ if (cursor !== void 0) pagination.cursor = cursor;
437
+ return pagination;
438
+ }
439
+
440
+ // src/modules/comradecircle/tools/circles.ts
441
+ function registerCircleTools(server, ctx, track) {
442
+ server.registerTool(
443
+ track(mcpToolName(ctx.productName, "circles_get")),
444
+ {
445
+ title: "Get Circle",
446
+ description: "Fetch a single Comradecircle circle (community) by ID, including its channels and memberships.",
447
+ inputSchema: { id: zod.z.string().describe("Circle ID") }
448
+ },
449
+ async ({ id }) => {
450
+ try {
451
+ const data = await ctx.gql.workspace.request(GET_CIRCLE, { id });
452
+ if (!data.comCircle) return errorResult(`Circle ${id} not found`);
453
+ return jsonResult(data.comCircle);
454
+ } catch (error) {
455
+ ctx.logger.error("comradecircle circles_get failed", { error: String(error) });
456
+ return errorResult(
457
+ `Failed to get circle: ${error instanceof Error ? error.message : String(error)}`
458
+ );
459
+ }
460
+ }
461
+ );
462
+ server.registerTool(
463
+ track(mcpToolName(ctx.productName, "circles_list")),
464
+ {
465
+ title: "List Circles",
466
+ description: "List circles in the bound workspace with optional status/visibility filters and pagination.",
467
+ inputSchema: {
468
+ status: CircleStatusEnum.optional().describe("Filter by lifecycle status"),
469
+ visibility: CircleVisibilityEnum.optional().describe("Filter by visibility"),
470
+ limit: zod.z.number().int().optional().describe("Page size (default 50)"),
471
+ offset: zod.z.number().int().optional().describe("Pagination offset (default 0)"),
472
+ cursor: zod.z.string().optional().describe("Opaque pagination cursor")
473
+ }
474
+ },
475
+ async ({ status, visibility, limit, offset, cursor }) => {
476
+ try {
477
+ const data = await ctx.gql.workspace.request(LIST_CIRCLES, {
478
+ status,
479
+ visibility,
480
+ pagination: buildPagination(limit, offset, cursor)
481
+ });
482
+ return jsonResult(data.comListCircles);
483
+ } catch (error) {
484
+ ctx.logger.error("comradecircle circles_list failed", { error: String(error) });
485
+ return errorResult(
486
+ `Failed to list circles: ${error instanceof Error ? error.message : String(error)}`
487
+ );
488
+ }
489
+ }
490
+ );
491
+ server.registerTool(
492
+ track(mcpToolName(ctx.productName, "circles_create")),
493
+ {
494
+ title: "Create Circle",
495
+ description: "Create a new circle (community). `slug` must be unique within the workspace. The caller is enrolled as the OWNER.",
496
+ inputSchema: {
497
+ name: zod.z.string().min(1).describe("Circle display name"),
498
+ slug: zod.z.string().min(1).describe("URL slug, unique within the workspace"),
499
+ description: zod.z.string().optional().describe("Circle description"),
500
+ topic: zod.z.string().optional().describe("Short topic/tagline"),
501
+ tags: zod.z.array(zod.z.string()).optional().describe("Free-form topical tags"),
502
+ visibility: CircleVisibilityEnum.optional().describe("Visibility (default PUBLIC)"),
503
+ avatarFileId: zod.z.string().optional().describe("wspace-files-svc File ID for the avatar")
504
+ }
505
+ },
506
+ async ({ name, slug, description, topic, tags, visibility, avatarFileId }) => {
507
+ try {
508
+ const input = { name, slug };
509
+ if (description !== void 0) input.description = description;
510
+ if (topic !== void 0) input.topic = topic;
511
+ if (tags !== void 0) input.tags = tags;
512
+ if (visibility !== void 0) input.visibility = visibility;
513
+ if (avatarFileId !== void 0) input.avatarFileId = avatarFileId;
514
+ const data = await ctx.gql.workspace.request(CREATE_CIRCLE, { input });
515
+ return jsonResult(data.comCreateCircle);
516
+ } catch (error) {
517
+ ctx.logger.error("comradecircle circles_create failed", { error: String(error) });
518
+ return errorResult(
519
+ `Failed to create circle: ${error instanceof Error ? error.message : String(error)}`
520
+ );
521
+ }
522
+ }
523
+ );
524
+ server.registerTool(
525
+ track(mcpToolName(ctx.productName, "circles_update")),
526
+ {
527
+ title: "Update Circle",
528
+ description: "Update a circle. At least one mutable field must be provided. Use the archive tool to soft-delete.",
529
+ inputSchema: {
530
+ id: zod.z.string().describe("Circle ID"),
531
+ name: zod.z.string().optional().describe("Updated display name"),
532
+ description: zod.z.string().optional().describe("Updated description"),
533
+ topic: zod.z.string().optional().describe("Updated topic/tagline"),
534
+ tags: zod.z.array(zod.z.string()).optional().describe("Updated topical tags"),
535
+ visibility: CircleVisibilityEnum.optional().describe("Updated visibility"),
536
+ avatarFileId: zod.z.string().optional().describe("Updated avatar File ID")
537
+ }
538
+ },
539
+ async ({ id, name, description, topic, tags, visibility, avatarFileId }) => {
540
+ try {
541
+ const input = {};
542
+ if (name !== void 0) input.name = name;
543
+ if (description !== void 0) input.description = description;
544
+ if (topic !== void 0) input.topic = topic;
545
+ if (tags !== void 0) input.tags = tags;
546
+ if (visibility !== void 0) input.visibility = visibility;
547
+ if (avatarFileId !== void 0) input.avatarFileId = avatarFileId;
548
+ if (Object.keys(input).length === 0) {
549
+ return errorResult(
550
+ "No updates specified \u2014 provide at least one of name/description/topic/tags/visibility/avatarFileId"
551
+ );
552
+ }
553
+ const data = await ctx.gql.workspace.request(UPDATE_CIRCLE, { id, input });
554
+ return jsonResult(data.comUpdateCircle);
555
+ } catch (error) {
556
+ ctx.logger.error("comradecircle circles_update failed", { error: String(error) });
557
+ return errorResult(
558
+ `Failed to update circle: ${error instanceof Error ? error.message : String(error)}`
559
+ );
560
+ }
561
+ }
562
+ );
563
+ server.registerTool(
564
+ track(mcpToolName(ctx.productName, "circles_archive")),
565
+ {
566
+ title: "Archive Circle",
567
+ description: "Archive (soft-delete) a circle. Returns true on success.",
568
+ inputSchema: { id: zod.z.string().describe("Circle ID") }
569
+ },
570
+ async ({ id }) => {
571
+ try {
572
+ const data = await ctx.gql.workspace.request(
573
+ ARCHIVE_CIRCLE,
574
+ { id }
575
+ );
576
+ return jsonResult({ archived: data.comArchiveCircle });
577
+ } catch (error) {
578
+ ctx.logger.error("comradecircle circles_archive failed", { error: String(error) });
579
+ return errorResult(
580
+ `Failed to archive circle: ${error instanceof Error ? error.message : String(error)}`
581
+ );
582
+ }
583
+ }
584
+ );
585
+ }
586
+ function registerChannelTools(server, ctx, track) {
587
+ server.registerTool(
588
+ track(mcpToolName(ctx.productName, "channels_list")),
589
+ {
590
+ title: "List Channels",
591
+ description: "List the channels belonging to a circle, ordered by position.",
592
+ inputSchema: { circleId: zod.z.string().describe("Circle ID") }
593
+ },
594
+ async ({ circleId }) => {
595
+ try {
596
+ const data = await ctx.gql.workspace.request(LIST_CHANNELS, { circleId });
597
+ return jsonResult(data.comListChannels);
598
+ } catch (error) {
599
+ ctx.logger.error("comradecircle channels_list failed", { error: String(error) });
600
+ return errorResult(
601
+ `Failed to list channels: ${error instanceof Error ? error.message : String(error)}`
602
+ );
603
+ }
604
+ }
605
+ );
606
+ server.registerTool(
607
+ track(mcpToolName(ctx.productName, "channels_create")),
608
+ {
609
+ title: "Create Channel",
610
+ description: "Create a channel within a circle. `slug` must be unique within the circle. `type` defaults to TEXT; `visibility` defaults to CIRCLE.",
611
+ inputSchema: {
612
+ circleId: zod.z.string().min(1).describe("Parent circle ID"),
613
+ name: zod.z.string().min(1).describe("Channel display name"),
614
+ slug: zod.z.string().min(1).describe("URL slug, unique within the circle"),
615
+ description: zod.z.string().optional().describe("Channel description"),
616
+ type: ChannelTypeEnum.optional().describe("Channel type (default TEXT)"),
617
+ visibility: ChannelVisibilityEnum.optional().describe(
618
+ "Channel visibility (default CIRCLE)"
619
+ ),
620
+ position: zod.z.number().int().optional().describe("Sort position within the circle")
621
+ }
622
+ },
623
+ async ({ circleId, name, slug, description, type, visibility, position }) => {
624
+ try {
625
+ const input = { circleId, name, slug };
626
+ if (description !== void 0) input.description = description;
627
+ if (type !== void 0) input.type = type;
628
+ if (visibility !== void 0) input.visibility = visibility;
629
+ if (position !== void 0) input.position = position;
630
+ const data = await ctx.gql.workspace.request(CREATE_CHANNEL, { input });
631
+ return jsonResult(data.comCreateChannel);
632
+ } catch (error) {
633
+ ctx.logger.error("comradecircle channels_create failed", { error: String(error) });
634
+ return errorResult(
635
+ `Failed to create channel: ${error instanceof Error ? error.message : String(error)}`
636
+ );
637
+ }
638
+ }
639
+ );
640
+ server.registerTool(
641
+ track(mcpToolName(ctx.productName, "channels_update")),
642
+ {
643
+ title: "Update Channel",
644
+ description: "Update a channel. At least one mutable field must be provided. Use `isArchived` to archive/unarchive.",
645
+ inputSchema: {
646
+ id: zod.z.string().describe("Channel ID"),
647
+ name: zod.z.string().optional().describe("Updated display name"),
648
+ description: zod.z.string().optional().describe("Updated description"),
649
+ type: ChannelTypeEnum.optional().describe("Updated channel type"),
650
+ visibility: ChannelVisibilityEnum.optional().describe("Updated visibility"),
651
+ position: zod.z.number().int().optional().describe("Updated sort position"),
652
+ isArchived: zod.z.boolean().optional().describe("Archive (true) or unarchive (false)")
653
+ }
654
+ },
655
+ async ({ id, name, description, type, visibility, position, isArchived }) => {
656
+ try {
657
+ const input = {};
658
+ if (name !== void 0) input.name = name;
659
+ if (description !== void 0) input.description = description;
660
+ if (type !== void 0) input.type = type;
661
+ if (visibility !== void 0) input.visibility = visibility;
662
+ if (position !== void 0) input.position = position;
663
+ if (isArchived !== void 0) input.isArchived = isArchived;
664
+ if (Object.keys(input).length === 0) {
665
+ return errorResult(
666
+ "No updates specified \u2014 provide at least one of name/description/type/visibility/position/isArchived"
667
+ );
668
+ }
669
+ const data = await ctx.gql.workspace.request(UPDATE_CHANNEL, { id, input });
670
+ return jsonResult(data.comUpdateChannel);
671
+ } catch (error) {
672
+ ctx.logger.error("comradecircle channels_update failed", { error: String(error) });
673
+ return errorResult(
674
+ `Failed to update channel: ${error instanceof Error ? error.message : String(error)}`
675
+ );
676
+ }
677
+ }
678
+ );
679
+ }
680
+ function registerMembershipTools(server, ctx, track) {
681
+ server.registerTool(
682
+ track(mcpToolName(ctx.productName, "memberships_join")),
683
+ {
684
+ title: "Join Circle",
685
+ description: "Join a circle as the calling member (idempotent \u2014 re-activates a LEFT membership). Returns the membership.",
686
+ inputSchema: { circleId: zod.z.string().describe("Circle ID to join") }
687
+ },
688
+ async ({ circleId }) => {
689
+ try {
690
+ const data = await ctx.gql.workspace.request(JOIN_CIRCLE, { circleId });
691
+ return jsonResult(data.comJoinCircle);
692
+ } catch (error) {
693
+ ctx.logger.error("comradecircle memberships_join failed", { error: String(error) });
694
+ return errorResult(
695
+ `Failed to join circle: ${error instanceof Error ? error.message : String(error)}`
696
+ );
697
+ }
698
+ }
699
+ );
700
+ server.registerTool(
701
+ track(mcpToolName(ctx.productName, "memberships_leave")),
702
+ {
703
+ title: "Leave Circle",
704
+ description: "Leave a circle as the calling member. Returns true on success.",
705
+ inputSchema: { circleId: zod.z.string().describe("Circle ID to leave") }
706
+ },
707
+ async ({ circleId }) => {
708
+ try {
709
+ const data = await ctx.gql.workspace.request(
710
+ LEAVE_CIRCLE,
711
+ {
712
+ circleId
713
+ }
714
+ );
715
+ return jsonResult({ left: data.comLeaveCircle });
716
+ } catch (error) {
717
+ ctx.logger.error("comradecircle memberships_leave failed", { error: String(error) });
718
+ return errorResult(
719
+ `Failed to leave circle: ${error instanceof Error ? error.message : String(error)}`
720
+ );
721
+ }
722
+ }
723
+ );
724
+ server.registerTool(
725
+ track(mcpToolName(ctx.productName, "memberships_list")),
726
+ {
727
+ title: "List Memberships",
728
+ description: "List circle memberships, optionally filtered by circle and/or status, with pagination.",
729
+ inputSchema: {
730
+ circleId: zod.z.string().optional().describe("Filter by circle ID"),
731
+ status: MembershipStatusEnum.optional().describe("Filter by membership status"),
732
+ limit: zod.z.number().int().optional().describe("Page size (default 50)"),
733
+ offset: zod.z.number().int().optional().describe("Pagination offset (default 0)"),
734
+ cursor: zod.z.string().optional().describe("Opaque pagination cursor")
735
+ }
736
+ },
737
+ async ({ circleId, status, limit, offset, cursor }) => {
738
+ try {
739
+ const data = await ctx.gql.workspace.request(LIST_MEMBERSHIPS, {
740
+ circleId,
741
+ status,
742
+ pagination: buildPagination(limit, offset, cursor)
743
+ });
744
+ return jsonResult(data.comListMemberships);
745
+ } catch (error) {
746
+ ctx.logger.error("comradecircle memberships_list failed", { error: String(error) });
747
+ return errorResult(
748
+ `Failed to list memberships: ${error instanceof Error ? error.message : String(error)}`
749
+ );
750
+ }
751
+ }
752
+ );
753
+ server.registerTool(
754
+ track(mcpToolName(ctx.productName, "memberships_set_role")),
755
+ {
756
+ title: "Set Member Role",
757
+ description: "Change a member\u2019s role within a circle (OWNER/ADMIN/MODERATOR/MEMBER). Returns the updated membership.",
758
+ inputSchema: {
759
+ membershipId: zod.z.string().describe("Membership ID"),
760
+ role: MemberRoleEnum.describe("New role to assign")
761
+ }
762
+ },
763
+ async ({ membershipId, role }) => {
764
+ try {
765
+ const data = await ctx.gql.workspace.request(SET_MEMBER_ROLE, { membershipId, role });
766
+ return jsonResult(data.comSetMemberRole);
767
+ } catch (error) {
768
+ ctx.logger.error("comradecircle memberships_set_role failed", { error: String(error) });
769
+ return errorResult(
770
+ `Failed to set member role: ${error instanceof Error ? error.message : String(error)}`
771
+ );
772
+ }
773
+ }
774
+ );
775
+ }
776
+ function registerProfileTools(server, ctx, track) {
777
+ server.registerTool(
778
+ track(mcpToolName(ctx.productName, "profiles_get")),
779
+ {
780
+ title: "Get Member Profile",
781
+ description: "Fetch a member\u2019s Comradecircle profile by their member (user) ID.",
782
+ inputSchema: { memberId: zod.z.string().describe("Member (user) ID") }
783
+ },
784
+ async ({ memberId }) => {
785
+ try {
786
+ const data = await ctx.gql.workspace.request(GET_PROFILE, { memberId });
787
+ if (!data.comProfile) return errorResult(`Profile for member ${memberId} not found`);
788
+ return jsonResult(data.comProfile);
789
+ } catch (error) {
790
+ ctx.logger.error("comradecircle profiles_get failed", { error: String(error) });
791
+ return errorResult(
792
+ `Failed to get profile: ${error instanceof Error ? error.message : String(error)}`
793
+ );
794
+ }
795
+ }
796
+ );
797
+ server.registerTool(
798
+ track(mcpToolName(ctx.productName, "profiles_list")),
799
+ {
800
+ title: "List Member Profiles",
801
+ description: "List member profiles in the bound workspace, optionally restricted to discoverable profiles, with pagination.",
802
+ inputSchema: {
803
+ discoverable: zod.z.boolean().optional().describe("Only profiles that opted into discovery"),
804
+ limit: zod.z.number().int().optional().describe("Page size (default 50)"),
805
+ offset: zod.z.number().int().optional().describe("Pagination offset (default 0)"),
806
+ cursor: zod.z.string().optional().describe("Opaque pagination cursor")
807
+ }
808
+ },
809
+ async ({ discoverable, limit, offset, cursor }) => {
810
+ try {
811
+ const data = await ctx.gql.workspace.request(LIST_PROFILES, {
812
+ discoverable,
813
+ pagination: buildPagination(limit, offset, cursor)
814
+ });
815
+ return jsonResult(data.comListProfiles);
816
+ } catch (error) {
817
+ ctx.logger.error("comradecircle profiles_list failed", { error: String(error) });
818
+ return errorResult(
819
+ `Failed to list profiles: ${error instanceof Error ? error.message : String(error)}`
820
+ );
821
+ }
822
+ }
823
+ );
824
+ server.registerTool(
825
+ track(mcpToolName(ctx.productName, "profiles_upsert")),
826
+ {
827
+ title: "Upsert My Profile",
828
+ description: "Create or update the calling member\u2019s profile (one per member per workspace). `displayName` is required.",
829
+ inputSchema: {
830
+ displayName: zod.z.string().min(1).describe("Display name"),
831
+ headline: zod.z.string().optional().describe("Short headline/title"),
832
+ bio: zod.z.string().optional().describe("Profile bio"),
833
+ interests: zod.z.array(zod.z.string()).optional().describe("Interest tags"),
834
+ skills: zod.z.array(zod.z.string()).optional().describe("Skill tags"),
835
+ presence: PresenceEnum.optional().describe("Presence indicator"),
836
+ discoverable: zod.z.boolean().optional().describe("Opt into member discovery"),
837
+ avatarFileId: zod.z.string().optional().describe("wspace-files-svc File ID for the avatar")
838
+ }
839
+ },
840
+ async ({
841
+ displayName,
842
+ headline,
843
+ bio,
844
+ interests,
845
+ skills,
846
+ presence,
847
+ discoverable,
848
+ avatarFileId
849
+ }) => {
850
+ try {
851
+ const input = { displayName };
852
+ if (headline !== void 0) input.headline = headline;
853
+ if (bio !== void 0) input.bio = bio;
854
+ if (interests !== void 0) input.interests = interests;
855
+ if (skills !== void 0) input.skills = skills;
856
+ if (presence !== void 0) input.presence = presence;
857
+ if (discoverable !== void 0) input.discoverable = discoverable;
858
+ if (avatarFileId !== void 0) input.avatarFileId = avatarFileId;
859
+ const data = await ctx.gql.workspace.request(UPSERT_PROFILE, { input });
860
+ return jsonResult(data.comUpsertProfile);
861
+ } catch (error) {
862
+ ctx.logger.error("comradecircle profiles_upsert failed", { error: String(error) });
863
+ return errorResult(
864
+ `Failed to upsert profile: ${error instanceof Error ? error.message : String(error)}`
865
+ );
866
+ }
867
+ }
868
+ );
869
+ }
870
+ function registerPostTools(server, ctx, track) {
871
+ server.registerTool(
872
+ track(mcpToolName(ctx.productName, "posts_get")),
873
+ {
874
+ title: "Get Post",
875
+ description: "Fetch a single post by ID, including its comments and reactions.",
876
+ inputSchema: { id: zod.z.string().describe("Post ID") }
877
+ },
878
+ async ({ id }) => {
879
+ try {
880
+ const data = await ctx.gql.workspace.request(GET_POST, { id });
881
+ if (!data.comPost) return errorResult(`Post ${id} not found`);
882
+ return jsonResult(data.comPost);
883
+ } catch (error) {
884
+ ctx.logger.error("comradecircle posts_get failed", { error: String(error) });
885
+ return errorResult(
886
+ `Failed to get post: ${error instanceof Error ? error.message : String(error)}`
887
+ );
888
+ }
889
+ }
890
+ );
891
+ server.registerTool(
892
+ track(mcpToolName(ctx.productName, "posts_list")),
893
+ {
894
+ title: "List Posts",
895
+ description: "List posts, optionally filtered by channel and/or status, newest first, with pagination.",
896
+ inputSchema: {
897
+ channelId: zod.z.string().optional().describe("Filter by channel ID"),
898
+ status: PostStatusEnum.optional().describe("Filter by post status"),
899
+ limit: zod.z.number().int().optional().describe("Page size (default 50)"),
900
+ offset: zod.z.number().int().optional().describe("Pagination offset (default 0)"),
901
+ cursor: zod.z.string().optional().describe("Opaque pagination cursor")
902
+ }
903
+ },
904
+ async ({ channelId, status, limit, offset, cursor }) => {
905
+ try {
906
+ const data = await ctx.gql.workspace.request(LIST_POSTS, {
907
+ channelId,
908
+ status,
909
+ pagination: buildPagination(limit, offset, cursor)
910
+ });
911
+ return jsonResult(data.comListPosts);
912
+ } catch (error) {
913
+ ctx.logger.error("comradecircle posts_list failed", { error: String(error) });
914
+ return errorResult(
915
+ `Failed to list posts: ${error instanceof Error ? error.message : String(error)}`
916
+ );
917
+ }
918
+ }
919
+ );
920
+ server.registerTool(
921
+ track(mcpToolName(ctx.productName, "posts_create")),
922
+ {
923
+ title: "Create Post",
924
+ description: "Create a post in a channel. `status` defaults to PUBLISHED. Attach files by their wspace-files-svc File IDs.",
925
+ inputSchema: {
926
+ channelId: zod.z.string().min(1).describe("Target channel ID"),
927
+ body: zod.z.string().min(1).describe("Post body"),
928
+ status: PostStatusEnum.optional().describe("Initial status (default PUBLISHED)"),
929
+ tags: zod.z.array(zod.z.string()).optional().describe("Topical tags"),
930
+ attachmentFileIds: zod.z.array(zod.z.string()).optional().describe("wspace-files-svc File IDs to attach")
931
+ }
932
+ },
933
+ async ({ channelId, body, status, tags, attachmentFileIds }) => {
934
+ try {
935
+ const input = { channelId, body };
936
+ if (status !== void 0) input.status = status;
937
+ if (tags !== void 0) input.tags = tags;
938
+ if (attachmentFileIds !== void 0) input.attachmentFileIds = attachmentFileIds;
939
+ const data = await ctx.gql.workspace.request(CREATE_POST, { input });
940
+ return jsonResult(data.comCreatePost);
941
+ } catch (error) {
942
+ ctx.logger.error("comradecircle posts_create failed", { error: String(error) });
943
+ return errorResult(
944
+ `Failed to create post: ${error instanceof Error ? error.message : String(error)}`
945
+ );
946
+ }
947
+ }
948
+ );
949
+ server.registerTool(
950
+ track(mcpToolName(ctx.productName, "posts_update")),
951
+ {
952
+ title: "Update Post",
953
+ description: "Update a post. At least one mutable field must be provided. Use `isPinned` to pin/unpin and `status` to hide/publish.",
954
+ inputSchema: {
955
+ id: zod.z.string().describe("Post ID"),
956
+ body: zod.z.string().optional().describe("Updated body"),
957
+ status: PostStatusEnum.optional().describe("Updated status"),
958
+ tags: zod.z.array(zod.z.string()).optional().describe("Updated tags"),
959
+ isPinned: zod.z.boolean().optional().describe("Pin (true) or unpin (false)")
960
+ }
961
+ },
962
+ async ({ id, body, status, tags, isPinned }) => {
963
+ try {
964
+ const input = {};
965
+ if (body !== void 0) input.body = body;
966
+ if (status !== void 0) input.status = status;
967
+ if (tags !== void 0) input.tags = tags;
968
+ if (isPinned !== void 0) input.isPinned = isPinned;
969
+ if (Object.keys(input).length === 0) {
970
+ return errorResult(
971
+ "No updates specified \u2014 provide at least one of body/status/tags/isPinned"
972
+ );
973
+ }
974
+ const data = await ctx.gql.workspace.request(UPDATE_POST, { id, input });
975
+ return jsonResult(data.comUpdatePost);
976
+ } catch (error) {
977
+ ctx.logger.error("comradecircle posts_update failed", { error: String(error) });
978
+ return errorResult(
979
+ `Failed to update post: ${error instanceof Error ? error.message : String(error)}`
980
+ );
981
+ }
982
+ }
983
+ );
984
+ server.registerTool(
985
+ track(mcpToolName(ctx.productName, "posts_remove")),
986
+ {
987
+ title: "Remove Post",
988
+ description: "Remove (soft-delete) a post. Returns true on success.",
989
+ inputSchema: { id: zod.z.string().describe("Post ID") }
990
+ },
991
+ async ({ id }) => {
992
+ try {
993
+ const data = await ctx.gql.workspace.request(REMOVE_POST, {
994
+ id
995
+ });
996
+ return jsonResult({ removed: data.comRemovePost });
997
+ } catch (error) {
998
+ ctx.logger.error("comradecircle posts_remove failed", { error: String(error) });
999
+ return errorResult(
1000
+ `Failed to remove post: ${error instanceof Error ? error.message : String(error)}`
1001
+ );
1002
+ }
1003
+ }
1004
+ );
1005
+ server.registerTool(
1006
+ track(mcpToolName(ctx.productName, "posts_react")),
1007
+ {
1008
+ title: "React to Post",
1009
+ description: "Toggle a typed reaction on a post for the calling member (idempotent add/remove). Returns the updated post.",
1010
+ inputSchema: {
1011
+ postId: zod.z.string().describe("Post ID"),
1012
+ type: ReactionTypeEnum.describe("Reaction type to toggle")
1013
+ }
1014
+ },
1015
+ async ({ postId, type }) => {
1016
+ try {
1017
+ const data = await ctx.gql.workspace.request(REACT_TO_POST, { postId, type });
1018
+ return jsonResult(data.comReactToPost);
1019
+ } catch (error) {
1020
+ ctx.logger.error("comradecircle posts_react failed", { error: String(error) });
1021
+ return errorResult(
1022
+ `Failed to react to post: ${error instanceof Error ? error.message : String(error)}`
1023
+ );
1024
+ }
1025
+ }
1026
+ );
1027
+ }
1028
+ function registerCommentTools(server, ctx, track) {
1029
+ server.registerTool(
1030
+ track(mcpToolName(ctx.productName, "comments_list")),
1031
+ {
1032
+ title: "List Comments",
1033
+ description: "List the comments on a post (threaded via each comment\u2019s parentId).",
1034
+ inputSchema: { postId: zod.z.string().describe("Post ID") }
1035
+ },
1036
+ async ({ postId }) => {
1037
+ try {
1038
+ const data = await ctx.gql.workspace.request(LIST_COMMENTS, { postId });
1039
+ return jsonResult(data.comListComments);
1040
+ } catch (error) {
1041
+ ctx.logger.error("comradecircle comments_list failed", { error: String(error) });
1042
+ return errorResult(
1043
+ `Failed to list comments: ${error instanceof Error ? error.message : String(error)}`
1044
+ );
1045
+ }
1046
+ }
1047
+ );
1048
+ server.registerTool(
1049
+ track(mcpToolName(ctx.productName, "comments_add")),
1050
+ {
1051
+ title: "Add Comment",
1052
+ description: "Add a comment to a post as the calling member. Pass `parentId` to reply to an existing comment.",
1053
+ inputSchema: {
1054
+ postId: zod.z.string().min(1).describe("Post being commented on"),
1055
+ body: zod.z.string().min(1).describe("Comment body"),
1056
+ parentId: zod.z.string().optional().describe("Parent comment ID (for replies)")
1057
+ }
1058
+ },
1059
+ async ({ postId, body, parentId }) => {
1060
+ try {
1061
+ const input = { postId, body };
1062
+ if (parentId !== void 0) input.parentId = parentId;
1063
+ const data = await ctx.gql.workspace.request(ADD_COMMENT, { input });
1064
+ return jsonResult(data.comAddComment);
1065
+ } catch (error) {
1066
+ ctx.logger.error("comradecircle comments_add failed", { error: String(error) });
1067
+ return errorResult(
1068
+ `Failed to add comment: ${error instanceof Error ? error.message : String(error)}`
1069
+ );
1070
+ }
1071
+ }
1072
+ );
1073
+ }
1074
+ function registerFeedTools(server, ctx, track) {
1075
+ server.registerTool(
1076
+ track(mcpToolName(ctx.productName, "feed_my_feed")),
1077
+ {
1078
+ title: "My Home Feed",
1079
+ description: "The calling member\u2019s unified home feed \u2014 published posts from every channel, member, and circle they follow, newest first.",
1080
+ inputSchema: {
1081
+ limit: zod.z.number().int().optional().describe("Page size (default 50)"),
1082
+ offset: zod.z.number().int().optional().describe("Pagination offset (default 0)"),
1083
+ cursor: zod.z.string().optional().describe("Opaque pagination cursor")
1084
+ }
1085
+ },
1086
+ async ({ limit, offset, cursor }) => {
1087
+ try {
1088
+ const data = await ctx.gql.workspace.request(MY_FEED, { pagination: buildPagination(limit, offset, cursor) });
1089
+ return jsonResult(data.comMyFeed);
1090
+ } catch (error) {
1091
+ ctx.logger.error("comradecircle feed_my_feed failed", { error: String(error) });
1092
+ return errorResult(
1093
+ `Failed to load home feed: ${error instanceof Error ? error.message : String(error)}`
1094
+ );
1095
+ }
1096
+ }
1097
+ );
1098
+ server.registerTool(
1099
+ track(mcpToolName(ctx.productName, "feed_my_follows")),
1100
+ {
1101
+ title: "List My Follows",
1102
+ description: "List the calling member\u2019s feed-follows, optionally filtered by target type (CIRCLE/CHANNEL/MEMBER).",
1103
+ inputSchema: {
1104
+ targetType: FollowTargetTypeEnum.optional().describe("Filter by follow target type")
1105
+ }
1106
+ },
1107
+ async ({ targetType }) => {
1108
+ try {
1109
+ const data = await ctx.gql.workspace.request(MY_FOLLOWS, { targetType });
1110
+ return jsonResult(data.comMyFollows);
1111
+ } catch (error) {
1112
+ ctx.logger.error("comradecircle feed_my_follows failed", { error: String(error) });
1113
+ return errorResult(
1114
+ `Failed to list follows: ${error instanceof Error ? error.message : String(error)}`
1115
+ );
1116
+ }
1117
+ }
1118
+ );
1119
+ server.registerTool(
1120
+ track(mcpToolName(ctx.productName, "feed_follow")),
1121
+ {
1122
+ title: "Follow Target",
1123
+ description: "Follow a circle, channel, or member so their posts appear in the calling member\u2019s home feed.",
1124
+ inputSchema: {
1125
+ targetId: zod.z.string().min(1).describe("ID of the circle/channel/member to follow"),
1126
+ targetType: FollowTargetTypeEnum.describe("What is being followed")
1127
+ }
1128
+ },
1129
+ async ({ targetId, targetType }) => {
1130
+ try {
1131
+ const data = await ctx.gql.workspace.request(FOLLOW, { input: { targetId, targetType } });
1132
+ return jsonResult(data.comFollow);
1133
+ } catch (error) {
1134
+ ctx.logger.error("comradecircle feed_follow failed", { error: String(error) });
1135
+ return errorResult(
1136
+ `Failed to follow target: ${error instanceof Error ? error.message : String(error)}`
1137
+ );
1138
+ }
1139
+ }
1140
+ );
1141
+ server.registerTool(
1142
+ track(mcpToolName(ctx.productName, "feed_unfollow")),
1143
+ {
1144
+ title: "Unfollow Target",
1145
+ description: "Stop following a circle, channel, or member. Returns true on success.",
1146
+ inputSchema: {
1147
+ targetId: zod.z.string().min(1).describe("ID of the circle/channel/member to unfollow"),
1148
+ targetType: FollowTargetTypeEnum.describe("What is being unfollowed")
1149
+ }
1150
+ },
1151
+ async ({ targetId, targetType }) => {
1152
+ try {
1153
+ const data = await ctx.gql.workspace.request(UNFOLLOW, {
1154
+ input: { targetId, targetType }
1155
+ });
1156
+ return jsonResult({ unfollowed: data.comUnfollow });
1157
+ } catch (error) {
1158
+ ctx.logger.error("comradecircle feed_unfollow failed", { error: String(error) });
1159
+ return errorResult(
1160
+ `Failed to unfollow target: ${error instanceof Error ? error.message : String(error)}`
1161
+ );
1162
+ }
1163
+ }
1164
+ );
1165
+ server.registerTool(
1166
+ track(mcpToolName(ctx.productName, "feed_mute_follow")),
1167
+ {
1168
+ title: "Mute Follow",
1169
+ description: "Mute or unmute a feed-follow \u2014 muted targets stay followed but are suppressed from the home feed.",
1170
+ inputSchema: {
1171
+ id: zod.z.string().describe("Feed-follow ID"),
1172
+ muted: zod.z.boolean().describe("Mute (true) or unmute (false)")
1173
+ }
1174
+ },
1175
+ async ({ id, muted }) => {
1176
+ try {
1177
+ const data = await ctx.gql.workspace.request(MUTE_FOLLOW, { id, muted });
1178
+ return jsonResult(data.comMuteFollow);
1179
+ } catch (error) {
1180
+ ctx.logger.error("comradecircle feed_mute_follow failed", { error: String(error) });
1181
+ return errorResult(
1182
+ `Failed to mute follow: ${error instanceof Error ? error.message : String(error)}`
1183
+ );
1184
+ }
1185
+ }
1186
+ );
1187
+ }
1188
+ function registerEventTools(server, ctx, track) {
1189
+ server.registerTool(
1190
+ track(mcpToolName(ctx.productName, "events_get")),
1191
+ {
1192
+ title: "Get Event",
1193
+ description: "Fetch a single event by ID, including its RSVPs.",
1194
+ inputSchema: { id: zod.z.string().describe("Event ID") }
1195
+ },
1196
+ async ({ id }) => {
1197
+ try {
1198
+ const data = await ctx.gql.workspace.request(GET_EVENT, { id });
1199
+ if (!data.comEvent) return errorResult(`Event ${id} not found`);
1200
+ return jsonResult(data.comEvent);
1201
+ } catch (error) {
1202
+ ctx.logger.error("comradecircle events_get failed", { error: String(error) });
1203
+ return errorResult(
1204
+ `Failed to get event: ${error instanceof Error ? error.message : String(error)}`
1205
+ );
1206
+ }
1207
+ }
1208
+ );
1209
+ server.registerTool(
1210
+ track(mcpToolName(ctx.productName, "events_list")),
1211
+ {
1212
+ title: "List Events",
1213
+ description: "List events, optionally filtered by circle and/or status, with pagination.",
1214
+ inputSchema: {
1215
+ circleId: zod.z.string().optional().describe("Filter by circle ID"),
1216
+ status: EventStatusEnum.optional().describe("Filter by event status"),
1217
+ limit: zod.z.number().int().optional().describe("Page size (default 50)"),
1218
+ offset: zod.z.number().int().optional().describe("Pagination offset (default 0)"),
1219
+ cursor: zod.z.string().optional().describe("Opaque pagination cursor")
1220
+ }
1221
+ },
1222
+ async ({ circleId, status, limit, offset, cursor }) => {
1223
+ try {
1224
+ const data = await ctx.gql.workspace.request(LIST_EVENTS, {
1225
+ circleId,
1226
+ status,
1227
+ pagination: buildPagination(limit, offset, cursor)
1228
+ });
1229
+ return jsonResult(data.comListEvents);
1230
+ } catch (error) {
1231
+ ctx.logger.error("comradecircle events_list failed", { error: String(error) });
1232
+ return errorResult(
1233
+ `Failed to list events: ${error instanceof Error ? error.message : String(error)}`
1234
+ );
1235
+ }
1236
+ }
1237
+ );
1238
+ server.registerTool(
1239
+ track(mcpToolName(ctx.productName, "events_create")),
1240
+ {
1241
+ title: "Create Event",
1242
+ description: "Schedule an event in a circle. `startsAt` is required (ISO-8601 DateTime); `mode` defaults to ONLINE.",
1243
+ inputSchema: {
1244
+ circleId: zod.z.string().min(1).describe("Host circle ID"),
1245
+ title: zod.z.string().min(1).describe("Event title"),
1246
+ startsAt: zod.z.string().min(1).describe("Start time (ISO-8601 DateTime)"),
1247
+ description: zod.z.string().optional().describe("Event description"),
1248
+ mode: EventModeEnum.optional().describe("Delivery mode (default ONLINE)"),
1249
+ location: zod.z.string().optional().describe("Physical location (for IN_PERSON/HYBRID)"),
1250
+ joinUrl: zod.z.string().optional().describe("Join URL (for ONLINE/HYBRID)"),
1251
+ capacity: zod.z.number().int().optional().describe("Maximum attendees"),
1252
+ endsAt: zod.z.string().optional().describe("End time (ISO-8601 DateTime)"),
1253
+ tags: zod.z.array(zod.z.string()).optional().describe("Topical tags")
1254
+ }
1255
+ },
1256
+ async ({
1257
+ circleId,
1258
+ title,
1259
+ startsAt,
1260
+ description,
1261
+ mode,
1262
+ location,
1263
+ joinUrl,
1264
+ capacity,
1265
+ endsAt,
1266
+ tags
1267
+ }) => {
1268
+ try {
1269
+ const input = { circleId, title, startsAt };
1270
+ if (description !== void 0) input.description = description;
1271
+ if (mode !== void 0) input.mode = mode;
1272
+ if (location !== void 0) input.location = location;
1273
+ if (joinUrl !== void 0) input.joinUrl = joinUrl;
1274
+ if (capacity !== void 0) input.capacity = capacity;
1275
+ if (endsAt !== void 0) input.endsAt = endsAt;
1276
+ if (tags !== void 0) input.tags = tags;
1277
+ const data = await ctx.gql.workspace.request(CREATE_EVENT, { input });
1278
+ return jsonResult(data.comCreateEvent);
1279
+ } catch (error) {
1280
+ ctx.logger.error("comradecircle events_create failed", { error: String(error) });
1281
+ return errorResult(
1282
+ `Failed to create event: ${error instanceof Error ? error.message : String(error)}`
1283
+ );
1284
+ }
1285
+ }
1286
+ );
1287
+ server.registerTool(
1288
+ track(mcpToolName(ctx.productName, "events_update")),
1289
+ {
1290
+ title: "Update Event",
1291
+ description: "Update an event\u2019s details. At least one mutable field must be provided. Use the transition tool to change status.",
1292
+ inputSchema: {
1293
+ id: zod.z.string().describe("Event ID"),
1294
+ title: zod.z.string().optional().describe("Updated title"),
1295
+ description: zod.z.string().optional().describe("Updated description"),
1296
+ mode: EventModeEnum.optional().describe("Updated delivery mode"),
1297
+ location: zod.z.string().optional().describe("Updated location"),
1298
+ joinUrl: zod.z.string().optional().describe("Updated join URL"),
1299
+ capacity: zod.z.number().int().optional().describe("Updated capacity"),
1300
+ startsAt: zod.z.string().optional().describe("Updated start time (ISO-8601 DateTime)"),
1301
+ endsAt: zod.z.string().optional().describe("Updated end time (ISO-8601 DateTime)"),
1302
+ tags: zod.z.array(zod.z.string()).optional().describe("Updated tags")
1303
+ }
1304
+ },
1305
+ async ({
1306
+ id,
1307
+ title,
1308
+ description,
1309
+ mode,
1310
+ location,
1311
+ joinUrl,
1312
+ capacity,
1313
+ startsAt,
1314
+ endsAt,
1315
+ tags
1316
+ }) => {
1317
+ try {
1318
+ const input = {};
1319
+ if (title !== void 0) input.title = title;
1320
+ if (description !== void 0) input.description = description;
1321
+ if (mode !== void 0) input.mode = mode;
1322
+ if (location !== void 0) input.location = location;
1323
+ if (joinUrl !== void 0) input.joinUrl = joinUrl;
1324
+ if (capacity !== void 0) input.capacity = capacity;
1325
+ if (startsAt !== void 0) input.startsAt = startsAt;
1326
+ if (endsAt !== void 0) input.endsAt = endsAt;
1327
+ if (tags !== void 0) input.tags = tags;
1328
+ if (Object.keys(input).length === 0) {
1329
+ return errorResult(
1330
+ "No updates specified \u2014 provide at least one of title/description/mode/location/joinUrl/capacity/startsAt/endsAt/tags"
1331
+ );
1332
+ }
1333
+ const data = await ctx.gql.workspace.request(UPDATE_EVENT, { id, input });
1334
+ return jsonResult(data.comUpdateEvent);
1335
+ } catch (error) {
1336
+ ctx.logger.error("comradecircle events_update failed", { error: String(error) });
1337
+ return errorResult(
1338
+ `Failed to update event: ${error instanceof Error ? error.message : String(error)}`
1339
+ );
1340
+ }
1341
+ }
1342
+ );
1343
+ server.registerTool(
1344
+ track(mcpToolName(ctx.productName, "events_transition")),
1345
+ {
1346
+ title: "Transition Event",
1347
+ description: "Advance an event through its lifecycle (SCHEDULED \u2192 LIVE \u2192 COMPLETED; CANCELLED from any non-terminal state).",
1348
+ inputSchema: {
1349
+ id: zod.z.string().describe("Event ID"),
1350
+ status: EventStatusEnum.describe("Target status")
1351
+ }
1352
+ },
1353
+ async ({ id, status }) => {
1354
+ try {
1355
+ const data = await ctx.gql.workspace.request(TRANSITION_EVENT, { id, status });
1356
+ return jsonResult(data.comTransitionEvent);
1357
+ } catch (error) {
1358
+ ctx.logger.error("comradecircle events_transition failed", { error: String(error) });
1359
+ return errorResult(
1360
+ `Failed to transition event: ${error instanceof Error ? error.message : String(error)}`
1361
+ );
1362
+ }
1363
+ }
1364
+ );
1365
+ server.registerTool(
1366
+ track(mcpToolName(ctx.productName, "events_rsvp")),
1367
+ {
1368
+ title: "RSVP to Event",
1369
+ description: "RSVP to an event as the calling member (idempotent \u2014 updates an existing RSVP). Returns the RSVP.",
1370
+ inputSchema: {
1371
+ eventId: zod.z.string().describe("Event ID"),
1372
+ status: RsvpStatusEnum.describe("RSVP response"),
1373
+ note: zod.z.string().optional().describe("Optional note to the host")
1374
+ }
1375
+ },
1376
+ async ({ eventId, status, note }) => {
1377
+ try {
1378
+ const data = await ctx.gql.workspace.request(RSVP_EVENT, { eventId, status, note });
1379
+ return jsonResult(data.comRsvpEvent);
1380
+ } catch (error) {
1381
+ ctx.logger.error("comradecircle events_rsvp failed", { error: String(error) });
1382
+ return errorResult(
1383
+ `Failed to RSVP to event: ${error instanceof Error ? error.message : String(error)}`
1384
+ );
1385
+ }
1386
+ }
1387
+ );
1388
+ }
1389
+ function registerPollTools(server, ctx, track) {
1390
+ server.registerTool(
1391
+ track(mcpToolName(ctx.productName, "polls_get")),
1392
+ {
1393
+ title: "Get Poll",
1394
+ description: "Fetch a single poll by ID, including its options and vote counts.",
1395
+ inputSchema: { id: zod.z.string().describe("Poll ID") }
1396
+ },
1397
+ async ({ id }) => {
1398
+ try {
1399
+ const data = await ctx.gql.workspace.request(GET_POLL, { id });
1400
+ if (!data.comPoll) return errorResult(`Poll ${id} not found`);
1401
+ return jsonResult(data.comPoll);
1402
+ } catch (error) {
1403
+ ctx.logger.error("comradecircle polls_get failed", { error: String(error) });
1404
+ return errorResult(
1405
+ `Failed to get poll: ${error instanceof Error ? error.message : String(error)}`
1406
+ );
1407
+ }
1408
+ }
1409
+ );
1410
+ server.registerTool(
1411
+ track(mcpToolName(ctx.productName, "polls_list")),
1412
+ {
1413
+ title: "List Polls",
1414
+ description: "List the polls in a circle, optionally filtered by status (OPEN/CLOSED).",
1415
+ inputSchema: {
1416
+ circleId: zod.z.string().describe("Circle ID"),
1417
+ status: PollStatusEnum.optional().describe("Filter by poll status")
1418
+ }
1419
+ },
1420
+ async ({ circleId, status }) => {
1421
+ try {
1422
+ const data = await ctx.gql.workspace.request(LIST_POLLS, { circleId, status });
1423
+ return jsonResult(data.comListPolls);
1424
+ } catch (error) {
1425
+ ctx.logger.error("comradecircle polls_list failed", { error: String(error) });
1426
+ return errorResult(
1427
+ `Failed to list polls: ${error instanceof Error ? error.message : String(error)}`
1428
+ );
1429
+ }
1430
+ }
1431
+ );
1432
+ server.registerTool(
1433
+ track(mcpToolName(ctx.productName, "polls_create")),
1434
+ {
1435
+ title: "Create Poll",
1436
+ description: "Create a poll in a circle with two or more options. `allowMultiple` and `isAnonymous` default to false.",
1437
+ inputSchema: {
1438
+ circleId: zod.z.string().min(1).describe("Host circle ID"),
1439
+ question: zod.z.string().min(1).describe("Poll question"),
1440
+ options: zod.z.array(zod.z.string()).min(2).describe("Option labels (at least two)"),
1441
+ allowMultiple: zod.z.boolean().optional().describe("Allow selecting multiple options"),
1442
+ isAnonymous: zod.z.boolean().optional().describe("Hide who voted for what"),
1443
+ closesAt: zod.z.string().optional().describe("Auto-close time (ISO-8601 DateTime)")
1444
+ }
1445
+ },
1446
+ async ({ circleId, question, options, allowMultiple, isAnonymous, closesAt }) => {
1447
+ try {
1448
+ const input = { circleId, question, options };
1449
+ if (allowMultiple !== void 0) input.allowMultiple = allowMultiple;
1450
+ if (isAnonymous !== void 0) input.isAnonymous = isAnonymous;
1451
+ if (closesAt !== void 0) input.closesAt = closesAt;
1452
+ const data = await ctx.gql.workspace.request(CREATE_POLL, { input });
1453
+ return jsonResult(data.comCreatePoll);
1454
+ } catch (error) {
1455
+ ctx.logger.error("comradecircle polls_create failed", { error: String(error) });
1456
+ return errorResult(
1457
+ `Failed to create poll: ${error instanceof Error ? error.message : String(error)}`
1458
+ );
1459
+ }
1460
+ }
1461
+ );
1462
+ server.registerTool(
1463
+ track(mcpToolName(ctx.productName, "polls_close")),
1464
+ {
1465
+ title: "Close Poll",
1466
+ description: "Close a poll so no further votes can be cast. Returns the updated poll.",
1467
+ inputSchema: { id: zod.z.string().describe("Poll ID") }
1468
+ },
1469
+ async ({ id }) => {
1470
+ try {
1471
+ const data = await ctx.gql.workspace.request(CLOSE_POLL, { id });
1472
+ return jsonResult(data.comClosePoll);
1473
+ } catch (error) {
1474
+ ctx.logger.error("comradecircle polls_close failed", { error: String(error) });
1475
+ return errorResult(
1476
+ `Failed to close poll: ${error instanceof Error ? error.message : String(error)}`
1477
+ );
1478
+ }
1479
+ }
1480
+ );
1481
+ server.registerTool(
1482
+ track(mcpToolName(ctx.productName, "polls_vote")),
1483
+ {
1484
+ title: "Vote on Poll",
1485
+ description: "Cast a vote for a poll option as the calling member (one vote per option). Returns the updated option.",
1486
+ inputSchema: {
1487
+ pollId: zod.z.string().describe("Poll ID"),
1488
+ optionId: zod.z.string().describe("Poll option ID to vote for")
1489
+ }
1490
+ },
1491
+ async ({ pollId, optionId }) => {
1492
+ try {
1493
+ const data = await ctx.gql.workspace.request(VOTE_POLL, { pollId, optionId });
1494
+ return jsonResult(data.comVotePoll);
1495
+ } catch (error) {
1496
+ ctx.logger.error("comradecircle polls_vote failed", { error: String(error) });
1497
+ return errorResult(
1498
+ `Failed to vote on poll: ${error instanceof Error ? error.message : String(error)}`
1499
+ );
1500
+ }
1501
+ }
1502
+ );
1503
+ }
1504
+
1505
+ // src/modules/comradecircle/tools/health.ts
1506
+ function registerHealthTools(server, ctx, track) {
1507
+ server.registerTool(
1508
+ track(mcpToolName(ctx.productName, "health")),
1509
+ {
1510
+ title: "Comradecircle Health Check",
1511
+ description: "Public health probe for the Comradecircle subgraph (comHealth). Requires no authentication.",
1512
+ inputSchema: {}
1513
+ },
1514
+ async () => {
1515
+ try {
1516
+ const data = await ctx.gql.workspace.request(HEALTH, {});
1517
+ return jsonResult({ ok: true, status: data.comHealth });
1518
+ } catch (error) {
1519
+ ctx.logger.error("comradecircle health failed", { error: String(error) });
1520
+ return errorResult(
1521
+ `Comradecircle health check failed: ${error instanceof Error ? error.message : String(error)}`
1522
+ );
1523
+ }
1524
+ }
1525
+ );
1526
+ }
1527
+
1528
+ // src/modules/comradecircle/resources.ts
1529
+ function registerComradecircleResources(server, ctx, track) {
1530
+ const schemaUri = track(mcpResourceUri(ctx.productName, "schema"));
1531
+ const circlesUri = track(mcpResourceUri(ctx.productName, "circles"));
1532
+ const feedUri = track(mcpResourceUri(ctx.productName, "feed"));
1533
+ const profilesUri = track(mcpResourceUri(ctx.productName, "profiles"));
1534
+ server.registerResource(
1535
+ "comradecircle-schema",
1536
+ schemaUri,
1537
+ {
1538
+ title: "Comradecircle Domain Schema",
1539
+ description: "Overview of the Comradecircle community domain \u2014 entities, enums, and the per-entity tool surface.",
1540
+ mimeType: "text/plain"
1541
+ },
1542
+ async () => {
1543
+ ctx.logger.debug(`Resource ${schemaUri} requested`);
1544
+ const schema = `# Comradecircle Domain (Overview)
1545
+
1546
+ Unified social, communication & engagement platform for workspaces.
1547
+
1548
+ ## Entities
1549
+ - ComCircle \u2014 a community (channels, memberships, polls, events)
1550
+ - ComChannel \u2014 a channel within a circle (TEXT/VOICE/FORUM/ANNOUNCEMENT)
1551
+ - ComMembership \u2014 a member's membership of a circle (role + status)
1552
+ - ComProfile \u2014 a member's social profile (one per member per workspace)
1553
+ - ComPost (+ComComment, +ComReaction) \u2014 channel posts, threaded comments, typed reactions
1554
+ - ComFeedFollow \u2014 a follow edge feeding the unified home feed
1555
+ - ComEvent (+ComEventRsvp) \u2014 circle events + RSVPs
1556
+ - ComPoll (+ComPollOption) \u2014 circle polls + options/votes
1557
+
1558
+ ## Enums
1559
+ - ComCircleVisibility: PUBLIC, PRIVATE, SECRET
1560
+ - ComCircleStatus: ACTIVE, ARCHIVED, SUSPENDED
1561
+ - ComChannelType: TEXT, VOICE, FORUM, ANNOUNCEMENT
1562
+ - ComChannelVisibility: CIRCLE, PRIVATE
1563
+ - ComMemberRole: OWNER, ADMIN, MODERATOR, MEMBER
1564
+ - ComMembershipStatus: ACTIVE, PENDING, LEFT, BANNED
1565
+ - ComPostStatus: DRAFT, PUBLISHED, HIDDEN, REMOVED
1566
+ - ComReactionType: LIKE, LOVE, CELEBRATE, INSIGHTFUL, SUPPORT, LAUGH
1567
+ - ComFollowTargetType: CIRCLE, CHANNEL, MEMBER
1568
+ - ComEventMode: ONLINE, IN_PERSON, HYBRID
1569
+ - ComEventStatus: SCHEDULED, LIVE, COMPLETED, CANCELLED
1570
+ - ComRsvpStatus: GOING, INTERESTED, NOT_GOING, WAITLISTED
1571
+ - ComPollStatus: OPEN, CLOSED
1572
+ - ComPresence: ONLINE, AWAY, BUSY, OFFLINE
1573
+
1574
+ ## Tool groups
1575
+ circles, channels, memberships, profiles, posts, comments, feed, events, polls.
1576
+ All run against the WORKSPACE gateway (workspace-scoped) except the public health probe.
1577
+ `;
1578
+ return {
1579
+ contents: [{ uri: schemaUri, text: schema, mimeType: "text/plain" }]
1580
+ };
1581
+ }
1582
+ );
1583
+ server.registerResource(
1584
+ "comradecircle-circles",
1585
+ circlesUri,
1586
+ {
1587
+ title: "Comradecircle Circle Directory",
1588
+ description: "The first page of circles in the bound workspace.",
1589
+ mimeType: "application/json"
1590
+ },
1591
+ async () => {
1592
+ ctx.logger.debug(`Resource ${circlesUri} requested`);
1593
+ try {
1594
+ const data = await ctx.gql.workspace.request(LIST_CIRCLES, {
1595
+ pagination: { limit: 50, offset: 0 }
1596
+ });
1597
+ return {
1598
+ contents: [
1599
+ {
1600
+ uri: circlesUri,
1601
+ text: JSON.stringify(data.comListCircles, null, 2),
1602
+ mimeType: "application/json"
1603
+ }
1604
+ ]
1605
+ };
1606
+ } catch (error) {
1607
+ return {
1608
+ contents: [
1609
+ {
1610
+ uri: circlesUri,
1611
+ text: JSON.stringify({
1612
+ error: error instanceof Error ? error.message : String(error)
1613
+ }),
1614
+ mimeType: "application/json"
1615
+ }
1616
+ ]
1617
+ };
1618
+ }
1619
+ }
1620
+ );
1621
+ server.registerResource(
1622
+ "comradecircle-feed",
1623
+ feedUri,
1624
+ {
1625
+ title: "Comradecircle Home Feed",
1626
+ description: "The calling member\u2019s unified home feed (first page).",
1627
+ mimeType: "application/json"
1628
+ },
1629
+ async () => {
1630
+ ctx.logger.debug(`Resource ${feedUri} requested`);
1631
+ try {
1632
+ const data = await ctx.gql.workspace.request(MY_FEED, {
1633
+ pagination: { limit: 50, offset: 0 }
1634
+ });
1635
+ return {
1636
+ contents: [
1637
+ {
1638
+ uri: feedUri,
1639
+ text: JSON.stringify(data.comMyFeed, null, 2),
1640
+ mimeType: "application/json"
1641
+ }
1642
+ ]
1643
+ };
1644
+ } catch (error) {
1645
+ return {
1646
+ contents: [
1647
+ {
1648
+ uri: feedUri,
1649
+ text: JSON.stringify({
1650
+ error: error instanceof Error ? error.message : String(error)
1651
+ }),
1652
+ mimeType: "application/json"
1653
+ }
1654
+ ]
1655
+ };
1656
+ }
1657
+ }
1658
+ );
1659
+ server.registerResource(
1660
+ "comradecircle-profiles",
1661
+ profilesUri,
1662
+ {
1663
+ title: "Comradecircle Member Directory",
1664
+ description: "The first page of discoverable member profiles in the bound workspace.",
1665
+ mimeType: "application/json"
1666
+ },
1667
+ async () => {
1668
+ ctx.logger.debug(`Resource ${profilesUri} requested`);
1669
+ try {
1670
+ const data = await ctx.gql.workspace.request(LIST_PROFILES, {
1671
+ discoverable: true,
1672
+ pagination: { limit: 50, offset: 0 }
1673
+ });
1674
+ return {
1675
+ contents: [
1676
+ {
1677
+ uri: profilesUri,
1678
+ text: JSON.stringify(data.comListProfiles, null, 2),
1679
+ mimeType: "application/json"
1680
+ }
1681
+ ]
1682
+ };
1683
+ } catch (error) {
1684
+ return {
1685
+ contents: [
1686
+ {
1687
+ uri: profilesUri,
1688
+ text: JSON.stringify({
1689
+ error: error instanceof Error ? error.message : String(error)
1690
+ }),
1691
+ mimeType: "application/json"
1692
+ }
1693
+ ]
1694
+ };
1695
+ }
1696
+ }
1697
+ );
1698
+ }
1699
+ function registerComradecirclePrompts(server, ctx, track) {
1700
+ const p = ctx.productName;
1701
+ const setupName = track(mcpPromptName(p, "community-setup"));
1702
+ const engagementName = track(mcpPromptName(p, "community-engagement"));
1703
+ server.registerPrompt(
1704
+ setupName,
1705
+ {
1706
+ title: "Community Setup",
1707
+ description: "Guide for standing up a new Comradecircle community end to end: create the circle, add channels, set up your profile, and seed the first post.",
1708
+ argsSchema: {
1709
+ name: zod.z.string().optional().describe("Circle/community name"),
1710
+ visibility: zod.z.string().optional().describe("Circle visibility (PUBLIC/PRIVATE/SECRET)")
1711
+ }
1712
+ },
1713
+ ({ name, visibility }) => {
1714
+ ctx.logger.debug(`Prompt ${setupName} called`, { name, visibility });
1715
+ const nameNote = name ? `Use the community name "${name}".` : "Ask the user for the community name.";
1716
+ const visibilityNote = visibility ? `Set visibility ${visibility}.` : "Help the user pick a visibility (PUBLIC, PRIVATE, or SECRET).";
1717
+ return {
1718
+ messages: [
1719
+ {
1720
+ role: "user",
1721
+ content: {
1722
+ type: "text",
1723
+ text: `Help me stand up a new Comradecircle community.
1724
+
1725
+ 1. **Create Circle**: Use ${p}_circles_create. ${nameNote} ${visibilityNote} Provide a unique slug; the caller becomes the OWNER.
1726
+ 2. **Add Channels**: Use ${p}_channels_create for each channel \u2014 pick a type (TEXT/VOICE/FORUM/ANNOUNCEMENT) and a unique slug per circle. Start with a TEXT "general" and an ANNOUNCEMENT channel.
1727
+ 3. **Set Up Profile**: Use ${p}_profiles_upsert to set your displayName, headline, interests, and skills.
1728
+ 4. **Seed First Post**: Use ${p}_posts_create in the general channel to welcome members.
1729
+ 5. **Verify**: Use ${p}_channels_list and ${p}_posts_list to confirm the setup.
1730
+
1731
+ Show results at each step and confirm before posting.`
1732
+ }
1733
+ }
1734
+ ]
1735
+ };
1736
+ }
1737
+ );
1738
+ server.registerPrompt(
1739
+ engagementName,
1740
+ {
1741
+ title: "Community Engagement",
1742
+ description: "Daily engagement workflow: review the home feed, moderate posts, run events and polls, and grow the follow graph.",
1743
+ argsSchema: {
1744
+ circleId: zod.z.string().optional().describe("Circle ID to operate")
1745
+ }
1746
+ },
1747
+ ({ circleId }) => {
1748
+ ctx.logger.debug(`Prompt ${engagementName} called`, { circleId });
1749
+ const circleNote = circleId ? `Operate circle "${circleId}".` : `First list circles with ${p}_circles_list and pick one.`;
1750
+ return {
1751
+ messages: [
1752
+ {
1753
+ role: "user",
1754
+ content: {
1755
+ type: "text",
1756
+ text: `Run daily Comradecircle community engagement. ${circleNote}
1757
+
1758
+ 1. **Home Feed**: Use ${p}_feed_my to scan the latest activity from followed circles/channels/members.
1759
+ 2. **Memberships**: Use ${p}_memberships_list to review the roster; use ${p}_memberships_set_role to promote moderators.
1760
+ 3. **Moderate Posts**: Use ${p}_posts_list filtered by channel; pin highlights with ${p}_posts_update (isPinned=true) and hide/remove abuse with ${p}_posts_update (status=HIDDEN) or ${p}_posts_remove.
1761
+ 4. **Engage**: Reply with ${p}_comments_add and acknowledge with ${p}_posts_react.
1762
+ 5. **Events**: Use ${p}_events_list and ${p}_events_create to schedule meetups; advance live events with ${p}_events_transition.
1763
+ 6. **Polls**: Use ${p}_polls_create to gather input; close stale ones with ${p}_polls_close.
1764
+ 7. **Grow the Graph**: Use ${p}_feed_follow to follow active members and channels.
1765
+
1766
+ Summarize what needs attention after each step.`
1767
+ }
1768
+ }
1769
+ ]
1770
+ };
1771
+ }
1772
+ );
1773
+ }
1774
+
1775
+ // src/modules/comradecircle/index.ts
1776
+ var comradecircleModule = {
1777
+ id: "comradecircle",
1778
+ version: "0.0.1",
1779
+ displayName: "Comradecircle",
1780
+ dependsOn: ["auth"],
1781
+ capabilities: { tools: {}, resources: {}, prompts: {} },
1782
+ register(server, ctx) {
1783
+ const tools = [];
1784
+ const resources = [];
1785
+ const prompts = [];
1786
+ const trackTool = (n) => {
1787
+ tools.push(n);
1788
+ return n;
1789
+ };
1790
+ const trackResource = (uri) => {
1791
+ resources.push(uri);
1792
+ return uri;
1793
+ };
1794
+ const trackPrompt = (n) => {
1795
+ prompts.push(n);
1796
+ return n;
1797
+ };
1798
+ registerCircleTools(server, ctx, trackTool);
1799
+ registerChannelTools(server, ctx, trackTool);
1800
+ registerMembershipTools(server, ctx, trackTool);
1801
+ registerProfileTools(server, ctx, trackTool);
1802
+ registerPostTools(server, ctx, trackTool);
1803
+ registerCommentTools(server, ctx, trackTool);
1804
+ registerFeedTools(server, ctx, trackTool);
1805
+ registerEventTools(server, ctx, trackTool);
1806
+ registerPollTools(server, ctx, trackTool);
1807
+ registerHealthTools(server, ctx, trackTool);
1808
+ registerComradecircleResources(server, ctx, trackResource);
1809
+ registerComradecirclePrompts(server, ctx, trackPrompt);
1810
+ return { tools, resources, prompts };
1811
+ }
1812
+ };
1813
+ var comradecircle_default = comradecircleModule;
1814
+
1815
+ module.exports = comradecircle_default;
1816
+ //# sourceMappingURL=index.cjs.map
1817
+ //# sourceMappingURL=index.cjs.map