@burdenoff/mcp-libs 2026.627.2 → 2026.627.4
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/modules/artistrybase/index.cjs +2112 -0
- package/dist/modules/artistrybase/index.cjs.map +1 -0
- package/dist/modules/artistrybase/index.d.cts +8 -0
- package/dist/modules/artistrybase/index.d.ts +8 -0
- package/dist/modules/artistrybase/index.js +2051 -0
- package/dist/modules/artistrybase/index.js.map +1 -0
- package/dist/modules/brainyrich/index.cjs +2148 -0
- package/dist/modules/brainyrich/index.cjs.map +1 -0
- package/dist/modules/brainyrich/index.d.cts +8 -0
- package/dist/modules/brainyrich/index.d.ts +8 -0
- package/dist/modules/brainyrich/index.js +2087 -0
- package/dist/modules/brainyrich/index.js.map +1 -0
- package/dist/modules/buildmyiq/index.cjs +1562 -0
- package/dist/modules/buildmyiq/index.cjs.map +1 -0
- package/dist/modules/buildmyiq/index.d.cts +8 -0
- package/dist/modules/buildmyiq/index.d.ts +8 -0
- package/dist/modules/buildmyiq/index.js +1501 -0
- package/dist/modules/buildmyiq/index.js.map +1 -0
- package/dist/modules/collabkin/index.cjs +1577 -0
- package/dist/modules/collabkin/index.cjs.map +1 -0
- package/dist/modules/collabkin/index.d.cts +8 -0
- package/dist/modules/collabkin/index.d.ts +8 -0
- package/dist/modules/collabkin/index.js +1516 -0
- package/dist/modules/collabkin/index.js.map +1 -0
- package/dist/modules/comradecircle/index.cjs +1817 -0
- package/dist/modules/comradecircle/index.cjs.map +1 -0
- package/dist/modules/comradecircle/index.d.cts +8 -0
- package/dist/modules/comradecircle/index.d.ts +8 -0
- package/dist/modules/comradecircle/index.js +1756 -0
- package/dist/modules/comradecircle/index.js.map +1 -0
- package/dist/modules/cosmicintersection/index.cjs +1984 -0
- package/dist/modules/cosmicintersection/index.cjs.map +1 -0
- package/dist/modules/cosmicintersection/index.d.cts +8 -0
- package/dist/modules/cosmicintersection/index.d.ts +8 -0
- package/dist/modules/cosmicintersection/index.js +1923 -0
- package/dist/modules/cosmicintersection/index.js.map +1 -0
- package/dist/modules/ecoimpacthub/index.cjs +2042 -0
- package/dist/modules/ecoimpacthub/index.cjs.map +1 -0
- package/dist/modules/ecoimpacthub/index.d.cts +8 -0
- package/dist/modules/ecoimpacthub/index.d.ts +8 -0
- package/dist/modules/ecoimpacthub/index.js +1981 -0
- package/dist/modules/ecoimpacthub/index.js.map +1 -0
- package/dist/modules/govcitizenhub/index.cjs +1686 -0
- package/dist/modules/govcitizenhub/index.cjs.map +1 -0
- package/dist/modules/govcitizenhub/index.d.cts +8 -0
- package/dist/modules/govcitizenhub/index.d.ts +8 -0
- package/dist/modules/govcitizenhub/index.js +1625 -0
- package/dist/modules/govcitizenhub/index.js.map +1 -0
- package/dist/modules/intelwatchtower/index.cjs +1919 -0
- package/dist/modules/intelwatchtower/index.cjs.map +1 -0
- package/dist/modules/intelwatchtower/index.d.cts +8 -0
- package/dist/modules/intelwatchtower/index.d.ts +8 -0
- package/dist/modules/intelwatchtower/index.js +1858 -0
- package/dist/modules/intelwatchtower/index.js.map +1 -0
- package/dist/modules/manufacturedops/index.cjs +1884 -0
- package/dist/modules/manufacturedops/index.cjs.map +1 -0
- package/dist/modules/manufacturedops/index.d.cts +8 -0
- package/dist/modules/manufacturedops/index.d.ts +8 -0
- package/dist/modules/manufacturedops/index.js +1823 -0
- package/dist/modules/manufacturedops/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1516 @@
|
|
|
1
|
+
import { mcpToolName, jsonResult, errorResult, mcpResourceUri, mcpPromptName } from '../../chunk-H2OYWLOA.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
// src/modules/collabkin/operations.ts
|
|
5
|
+
var TREE_FIELDS = `
|
|
6
|
+
id name surname description status tags workspaceId createdAt updatedAt
|
|
7
|
+
coverFile { id }
|
|
8
|
+
`;
|
|
9
|
+
var PERSON_FIELDS = `
|
|
10
|
+
id treeId givenName familyName maidenName nickname gender lifeStatus
|
|
11
|
+
birthDate birthPlace deathDate bio tags linkedUserId workspaceId
|
|
12
|
+
createdAt updatedAt
|
|
13
|
+
photoFile { id }
|
|
14
|
+
`;
|
|
15
|
+
var RELATIONSHIP_FIELDS = `
|
|
16
|
+
id treeId fromPersonId toPersonId type certainty startDate endDate notes
|
|
17
|
+
workspaceId createdAt updatedAt
|
|
18
|
+
`;
|
|
19
|
+
var MEMBERSHIP_FIELDS = `
|
|
20
|
+
id treeId memberUserId personId role status invitedById invitedAt acceptedAt
|
|
21
|
+
workspaceId createdAt updatedAt
|
|
22
|
+
`;
|
|
23
|
+
var COMMENT_FIELDS = `
|
|
24
|
+
id body memoryId storyId workspaceId createdAt updatedAt
|
|
25
|
+
`;
|
|
26
|
+
var MEMORY_FIELDS = `
|
|
27
|
+
id treeId title description kind place takenAt tags visibility personId
|
|
28
|
+
workspaceId createdAt updatedAt
|
|
29
|
+
file { id }
|
|
30
|
+
`;
|
|
31
|
+
var STORY_FIELDS = `
|
|
32
|
+
id treeId title body format narrator personId tags visibility
|
|
33
|
+
workspaceId createdAt updatedAt
|
|
34
|
+
recordingFile { id }
|
|
35
|
+
`;
|
|
36
|
+
var EVENT_FIELDS = `
|
|
37
|
+
id treeId personId type title description date place isApproximate
|
|
38
|
+
workspaceId createdAt updatedAt
|
|
39
|
+
`;
|
|
40
|
+
var PAGE_INFO = `pageInfo { cursor hasMore }`;
|
|
41
|
+
var GET_FAMILY_TREE = `
|
|
42
|
+
query CollabkinGetFamilyTree($id: ID!) {
|
|
43
|
+
colFamilyTree(id: $id) {
|
|
44
|
+
${TREE_FIELDS}
|
|
45
|
+
people { id givenName familyName lifeStatus }
|
|
46
|
+
memberships { id memberUserId role status }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
`;
|
|
50
|
+
var LIST_FAMILY_TREES = `
|
|
51
|
+
query CollabkinListFamilyTrees($pagination: PaginationInput, $status: ColTreeStatus) {
|
|
52
|
+
colListFamilyTrees(pagination: $pagination, status: $status) {
|
|
53
|
+
items { ${TREE_FIELDS} }
|
|
54
|
+
${PAGE_INFO}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
var CREATE_FAMILY_TREE = `
|
|
59
|
+
mutation CollabkinCreateFamilyTree($input: ColCreateFamilyTreeInput!) {
|
|
60
|
+
colCreateFamilyTree(input: $input) {
|
|
61
|
+
${TREE_FIELDS}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
var UPDATE_FAMILY_TREE = `
|
|
66
|
+
mutation CollabkinUpdateFamilyTree($id: ID!, $input: ColUpdateFamilyTreeInput!) {
|
|
67
|
+
colUpdateFamilyTree(id: $id, input: $input) {
|
|
68
|
+
${TREE_FIELDS}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
var ARCHIVE_FAMILY_TREE = `
|
|
73
|
+
mutation CollabkinArchiveFamilyTree($id: ID!) {
|
|
74
|
+
colArchiveFamilyTree(id: $id)
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
var GET_PERSON = `
|
|
78
|
+
query CollabkinGetPerson($id: ID!) {
|
|
79
|
+
colPerson(id: $id) {
|
|
80
|
+
${PERSON_FIELDS}
|
|
81
|
+
relationships { ${RELATIONSHIP_FIELDS} }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
var LIST_PEOPLE = `
|
|
86
|
+
query CollabkinListPeople(
|
|
87
|
+
$treeId: ID
|
|
88
|
+
$lifeStatus: ColLifeStatus
|
|
89
|
+
$pagination: PaginationInput
|
|
90
|
+
) {
|
|
91
|
+
colListPeople(treeId: $treeId, lifeStatus: $lifeStatus, pagination: $pagination) {
|
|
92
|
+
items { ${PERSON_FIELDS} }
|
|
93
|
+
${PAGE_INFO}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
97
|
+
var CREATE_PERSON = `
|
|
98
|
+
mutation CollabkinCreatePerson($input: ColCreatePersonInput!) {
|
|
99
|
+
colCreatePerson(input: $input) {
|
|
100
|
+
${PERSON_FIELDS}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
var UPDATE_PERSON = `
|
|
105
|
+
mutation CollabkinUpdatePerson($id: ID!, $input: ColUpdatePersonInput!) {
|
|
106
|
+
colUpdatePerson(id: $id, input: $input) {
|
|
107
|
+
${PERSON_FIELDS}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
var DELETE_PERSON = `
|
|
112
|
+
mutation CollabkinDeletePerson($id: ID!) {
|
|
113
|
+
colDeletePerson(id: $id)
|
|
114
|
+
}
|
|
115
|
+
`;
|
|
116
|
+
var LIST_RELATIONSHIPS = `
|
|
117
|
+
query CollabkinListRelationships($treeId: ID, $personId: ID) {
|
|
118
|
+
colListRelationships(treeId: $treeId, personId: $personId) {
|
|
119
|
+
${RELATIONSHIP_FIELDS}
|
|
120
|
+
fromPerson { id givenName familyName }
|
|
121
|
+
toPerson { id givenName familyName }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
`;
|
|
125
|
+
var CREATE_RELATIONSHIP = `
|
|
126
|
+
mutation CollabkinCreateRelationship($input: ColCreateRelationshipInput!) {
|
|
127
|
+
colCreateRelationship(input: $input) {
|
|
128
|
+
${RELATIONSHIP_FIELDS}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
var DELETE_RELATIONSHIP = `
|
|
133
|
+
mutation CollabkinDeleteRelationship($id: ID!) {
|
|
134
|
+
colDeleteRelationship(id: $id)
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
var LIST_MEMBERSHIPS = `
|
|
138
|
+
query CollabkinListMemberships($treeId: ID!) {
|
|
139
|
+
colListMemberships(treeId: $treeId) {
|
|
140
|
+
${MEMBERSHIP_FIELDS}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
`;
|
|
144
|
+
var INVITE_MEMBER = `
|
|
145
|
+
mutation CollabkinInviteMember($input: ColInviteMemberInput!) {
|
|
146
|
+
colInviteMember(input: $input) {
|
|
147
|
+
${MEMBERSHIP_FIELDS}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
`;
|
|
151
|
+
var ACCEPT_MEMBERSHIP = `
|
|
152
|
+
mutation CollabkinAcceptMembership($id: ID!) {
|
|
153
|
+
colAcceptMembership(id: $id) {
|
|
154
|
+
${MEMBERSHIP_FIELDS}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
var REMOVE_MEMBER = `
|
|
159
|
+
mutation CollabkinRemoveMember($id: ID!) {
|
|
160
|
+
colRemoveMember(id: $id)
|
|
161
|
+
}
|
|
162
|
+
`;
|
|
163
|
+
var GET_MEMORY = `
|
|
164
|
+
query CollabkinGetMemory($id: ID!) {
|
|
165
|
+
colMemory(id: $id) {
|
|
166
|
+
${MEMORY_FIELDS}
|
|
167
|
+
comments { ${COMMENT_FIELDS} }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
var LIST_MEMORIES = `
|
|
172
|
+
query CollabkinListMemories(
|
|
173
|
+
$treeId: ID
|
|
174
|
+
$personId: ID
|
|
175
|
+
$kind: ColMemoryKind
|
|
176
|
+
$pagination: PaginationInput
|
|
177
|
+
) {
|
|
178
|
+
colListMemories(
|
|
179
|
+
treeId: $treeId
|
|
180
|
+
personId: $personId
|
|
181
|
+
kind: $kind
|
|
182
|
+
pagination: $pagination
|
|
183
|
+
) {
|
|
184
|
+
items { ${MEMORY_FIELDS} }
|
|
185
|
+
${PAGE_INFO}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
`;
|
|
189
|
+
var CREATE_MEMORY = `
|
|
190
|
+
mutation CollabkinCreateMemory($input: ColCreateMemoryInput!) {
|
|
191
|
+
colCreateMemory(input: $input) {
|
|
192
|
+
${MEMORY_FIELDS}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
196
|
+
var UPDATE_MEMORY = `
|
|
197
|
+
mutation CollabkinUpdateMemory($id: ID!, $input: ColUpdateMemoryInput!) {
|
|
198
|
+
colUpdateMemory(id: $id, input: $input) {
|
|
199
|
+
${MEMORY_FIELDS}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
`;
|
|
203
|
+
var DELETE_MEMORY = `
|
|
204
|
+
mutation CollabkinDeleteMemory($id: ID!) {
|
|
205
|
+
colDeleteMemory(id: $id)
|
|
206
|
+
}
|
|
207
|
+
`;
|
|
208
|
+
var GET_STORY = `
|
|
209
|
+
query CollabkinGetStory($id: ID!) {
|
|
210
|
+
colStory(id: $id) {
|
|
211
|
+
${STORY_FIELDS}
|
|
212
|
+
comments { ${COMMENT_FIELDS} }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
`;
|
|
216
|
+
var LIST_STORIES = `
|
|
217
|
+
query CollabkinListStories(
|
|
218
|
+
$treeId: ID
|
|
219
|
+
$personId: ID
|
|
220
|
+
$pagination: PaginationInput
|
|
221
|
+
) {
|
|
222
|
+
colListStories(treeId: $treeId, personId: $personId, pagination: $pagination) {
|
|
223
|
+
items { ${STORY_FIELDS} }
|
|
224
|
+
${PAGE_INFO}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
`;
|
|
228
|
+
var CREATE_STORY = `
|
|
229
|
+
mutation CollabkinCreateStory($input: ColCreateStoryInput!) {
|
|
230
|
+
colCreateStory(input: $input) {
|
|
231
|
+
${STORY_FIELDS}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
var DELETE_STORY = `
|
|
236
|
+
mutation CollabkinDeleteStory($id: ID!) {
|
|
237
|
+
colDeleteStory(id: $id)
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
var LIST_COMMENTS = `
|
|
241
|
+
query CollabkinListComments($memoryId: ID, $storyId: ID) {
|
|
242
|
+
colListComments(memoryId: $memoryId, storyId: $storyId) {
|
|
243
|
+
${COMMENT_FIELDS}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
`;
|
|
247
|
+
var ADD_COMMENT = `
|
|
248
|
+
mutation CollabkinAddComment($input: ColCreateCommentInput!) {
|
|
249
|
+
colAddComment(input: $input) {
|
|
250
|
+
${COMMENT_FIELDS}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
`;
|
|
254
|
+
var DELETE_COMMENT = `
|
|
255
|
+
mutation CollabkinDeleteComment($id: ID!) {
|
|
256
|
+
colDeleteComment(id: $id)
|
|
257
|
+
}
|
|
258
|
+
`;
|
|
259
|
+
var TIMELINE = `
|
|
260
|
+
query CollabkinTimeline($treeId: ID!, $personId: ID, $type: ColEventType) {
|
|
261
|
+
colTimeline(treeId: $treeId, personId: $personId, type: $type) {
|
|
262
|
+
${EVENT_FIELDS}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
`;
|
|
266
|
+
var CREATE_EVENT = `
|
|
267
|
+
mutation CollabkinCreateEvent($input: ColCreateEventInput!) {
|
|
268
|
+
colCreateEvent(input: $input) {
|
|
269
|
+
${EVENT_FIELDS}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
`;
|
|
273
|
+
var DELETE_EVENT = `
|
|
274
|
+
mutation CollabkinDeleteEvent($id: ID!) {
|
|
275
|
+
colDeleteEvent(id: $id)
|
|
276
|
+
}
|
|
277
|
+
`;
|
|
278
|
+
var HEALTH = `
|
|
279
|
+
query CollabkinHealth {
|
|
280
|
+
colHealth
|
|
281
|
+
}
|
|
282
|
+
`;
|
|
283
|
+
var TreeStatusEnum = z.enum(["ACTIVE", "ARCHIVED"]).describe("Lifecycle status of a family tree");
|
|
284
|
+
var GenderEnum = z.enum(["FEMALE", "MALE", "OTHER", "UNKNOWN"]).describe("Gender of a person");
|
|
285
|
+
var LifeStatusEnum = z.enum(["DECEASED", "LIVING", "UNKNOWN"]).describe("Whether a person is living, deceased, or unknown");
|
|
286
|
+
var MemberRoleEnum = z.enum(["CONTRIBUTOR", "OWNER", "STEWARD", "VIEWER"]).describe("A member\u2019s role within a family tree");
|
|
287
|
+
var MemoryKindEnum = z.enum(["ARTIFACT", "AUDIO", "DOCUMENT", "PHOTO", "VIDEO"]).describe("Media type of a memory");
|
|
288
|
+
var VisibilityEnum = z.enum(["PRIVATE", "STEWARDS", "TREE"]).describe("Who can see a memory or story (PRIVATE, STEWARDS, or whole TREE)");
|
|
289
|
+
var EventTypeEnum = z.enum([
|
|
290
|
+
"BIRTH",
|
|
291
|
+
"DEATH",
|
|
292
|
+
"DIVORCE",
|
|
293
|
+
"EDUCATION",
|
|
294
|
+
"IMMIGRATION",
|
|
295
|
+
"MARRIAGE",
|
|
296
|
+
"MILESTONE",
|
|
297
|
+
"MILITARY",
|
|
298
|
+
"OTHER",
|
|
299
|
+
"RESIDENCE"
|
|
300
|
+
]).describe("Type of a timeline event");
|
|
301
|
+
var RelationshipTypeEnum = z.enum([
|
|
302
|
+
"CHILD",
|
|
303
|
+
"GRANDCHILD",
|
|
304
|
+
"GRANDPARENT",
|
|
305
|
+
"GUARDIAN",
|
|
306
|
+
"PARENT",
|
|
307
|
+
"PARTNER",
|
|
308
|
+
"SIBLING",
|
|
309
|
+
"SPOUSE"
|
|
310
|
+
]).describe("Kind of relationship between two people");
|
|
311
|
+
var StoryFormatEnum = z.enum(["AUDIO", "VIDEO", "WRITTEN"]).describe("Format a story is recorded in");
|
|
312
|
+
var CertaintyEnum = z.enum(["CONFIRMED", "DISPUTED", "PROBABLE", "UNVERIFIED"]).describe("Confidence level of a recorded relationship");
|
|
313
|
+
function buildPagination(limit, offset, cursor) {
|
|
314
|
+
const pagination = {};
|
|
315
|
+
if (limit !== void 0) pagination.limit = limit;
|
|
316
|
+
if (offset !== void 0) pagination.offset = offset;
|
|
317
|
+
if (cursor !== void 0) pagination.cursor = cursor;
|
|
318
|
+
return Object.keys(pagination).length > 0 ? pagination : void 0;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/modules/collabkin/tools/trees.ts
|
|
322
|
+
function registerTreeTools(server, ctx, track) {
|
|
323
|
+
server.registerTool(
|
|
324
|
+
track(mcpToolName(ctx.productName, "trees_list")),
|
|
325
|
+
{
|
|
326
|
+
title: "List Family Trees",
|
|
327
|
+
description: "List the family trees in the bound workspace, optionally filtered by status (ACTIVE/ARCHIVED), with cursor/offset pagination.",
|
|
328
|
+
inputSchema: {
|
|
329
|
+
status: TreeStatusEnum.optional().describe("Filter by tree status"),
|
|
330
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
331
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
332
|
+
cursor: z.string().optional().describe("Opaque pagination cursor")
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
async ({ status, limit, offset, cursor }) => {
|
|
336
|
+
try {
|
|
337
|
+
const data = await ctx.gql.workspace.request(LIST_FAMILY_TREES, {
|
|
338
|
+
status,
|
|
339
|
+
pagination: buildPagination(limit, offset, cursor)
|
|
340
|
+
});
|
|
341
|
+
return jsonResult(data.colListFamilyTrees);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
ctx.logger.error("collabkin trees_list failed", { error: String(error) });
|
|
344
|
+
return errorResult(
|
|
345
|
+
`Failed to list family trees: ${error instanceof Error ? error.message : String(error)}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
server.registerTool(
|
|
351
|
+
track(mcpToolName(ctx.productName, "trees_get")),
|
|
352
|
+
{
|
|
353
|
+
title: "Get Family Tree",
|
|
354
|
+
description: "Fetch a single family tree by ID, including a summary of its people and memberships.",
|
|
355
|
+
inputSchema: { id: z.string().describe("Family tree ID") }
|
|
356
|
+
},
|
|
357
|
+
async ({ id }) => {
|
|
358
|
+
try {
|
|
359
|
+
const data = await ctx.gql.workspace.request(GET_FAMILY_TREE, { id });
|
|
360
|
+
if (!data.colFamilyTree) return errorResult(`Family tree ${id} not found`);
|
|
361
|
+
return jsonResult(data.colFamilyTree);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
ctx.logger.error("collabkin trees_get failed", { error: String(error) });
|
|
364
|
+
return errorResult(
|
|
365
|
+
`Failed to get family tree: ${error instanceof Error ? error.message : String(error)}`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
server.registerTool(
|
|
371
|
+
track(mcpToolName(ctx.productName, "trees_create")),
|
|
372
|
+
{
|
|
373
|
+
title: "Create Family Tree",
|
|
374
|
+
description: "Create a new family tree. Only `name` is required; `coverFileId` references a wspace-files-svc File.",
|
|
375
|
+
inputSchema: {
|
|
376
|
+
name: z.string().min(1).describe("Family tree name"),
|
|
377
|
+
surname: z.string().optional().describe("Primary family surname"),
|
|
378
|
+
description: z.string().optional().describe("Tree description"),
|
|
379
|
+
coverFileId: z.string().optional().describe("Cover image File ID (wspace-files-svc)"),
|
|
380
|
+
tags: z.array(z.string()).optional().describe("Discovery tags")
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
async ({ name, surname, description, coverFileId, tags }) => {
|
|
384
|
+
try {
|
|
385
|
+
const input = { name };
|
|
386
|
+
if (surname !== void 0) input.surname = surname;
|
|
387
|
+
if (description !== void 0) input.description = description;
|
|
388
|
+
if (coverFileId !== void 0) input.coverFileId = coverFileId;
|
|
389
|
+
if (tags !== void 0) input.tags = tags;
|
|
390
|
+
const data = await ctx.gql.workspace.request(CREATE_FAMILY_TREE, { input });
|
|
391
|
+
return jsonResult(data.colCreateFamilyTree);
|
|
392
|
+
} catch (error) {
|
|
393
|
+
ctx.logger.error("collabkin trees_create failed", { error: String(error) });
|
|
394
|
+
return errorResult(
|
|
395
|
+
`Failed to create family tree: ${error instanceof Error ? error.message : String(error)}`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
server.registerTool(
|
|
401
|
+
track(mcpToolName(ctx.productName, "trees_update")),
|
|
402
|
+
{
|
|
403
|
+
title: "Update Family Tree",
|
|
404
|
+
description: "Update a family tree. At least one mutable field must be provided.",
|
|
405
|
+
inputSchema: {
|
|
406
|
+
id: z.string().describe("Family tree ID"),
|
|
407
|
+
name: z.string().optional().describe("Updated name"),
|
|
408
|
+
surname: z.string().optional().describe("Updated surname"),
|
|
409
|
+
description: z.string().optional().describe("Updated description"),
|
|
410
|
+
coverFileId: z.string().optional().describe("Updated cover image File ID"),
|
|
411
|
+
tags: z.array(z.string()).optional().describe("Updated tags")
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
async ({ id, name, surname, description, coverFileId, tags }) => {
|
|
415
|
+
try {
|
|
416
|
+
const input = {};
|
|
417
|
+
if (name !== void 0) input.name = name;
|
|
418
|
+
if (surname !== void 0) input.surname = surname;
|
|
419
|
+
if (description !== void 0) input.description = description;
|
|
420
|
+
if (coverFileId !== void 0) input.coverFileId = coverFileId;
|
|
421
|
+
if (tags !== void 0) input.tags = tags;
|
|
422
|
+
if (Object.keys(input).length === 0) {
|
|
423
|
+
return errorResult(
|
|
424
|
+
"No updates specified \u2014 provide at least one of name/surname/description/coverFileId/tags"
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
const data = await ctx.gql.workspace.request(UPDATE_FAMILY_TREE, { id, input });
|
|
428
|
+
return jsonResult(data.colUpdateFamilyTree);
|
|
429
|
+
} catch (error) {
|
|
430
|
+
ctx.logger.error("collabkin trees_update failed", { error: String(error) });
|
|
431
|
+
return errorResult(
|
|
432
|
+
`Failed to update family tree: ${error instanceof Error ? error.message : String(error)}`
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
server.registerTool(
|
|
438
|
+
track(mcpToolName(ctx.productName, "trees_archive")),
|
|
439
|
+
{
|
|
440
|
+
title: "Archive Family Tree",
|
|
441
|
+
description: "Archive a family tree (soft delete \u2014 sets status to ARCHIVED).",
|
|
442
|
+
inputSchema: { id: z.string().describe("Family tree ID") }
|
|
443
|
+
},
|
|
444
|
+
async ({ id }) => {
|
|
445
|
+
try {
|
|
446
|
+
const data = await ctx.gql.workspace.request(
|
|
447
|
+
ARCHIVE_FAMILY_TREE,
|
|
448
|
+
{ id }
|
|
449
|
+
);
|
|
450
|
+
return jsonResult({ success: data.colArchiveFamilyTree, id });
|
|
451
|
+
} catch (error) {
|
|
452
|
+
ctx.logger.error("collabkin trees_archive failed", { error: String(error) });
|
|
453
|
+
return errorResult(
|
|
454
|
+
`Failed to archive family tree: ${error instanceof Error ? error.message : String(error)}`
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
function registerPersonTools(server, ctx, track) {
|
|
461
|
+
server.registerTool(
|
|
462
|
+
track(mcpToolName(ctx.productName, "people_list")),
|
|
463
|
+
{
|
|
464
|
+
title: "List People",
|
|
465
|
+
description: "List people in a family tree, optionally filtered by tree and/or life status, with cursor/offset pagination.",
|
|
466
|
+
inputSchema: {
|
|
467
|
+
treeId: z.string().optional().describe("Filter by family tree ID"),
|
|
468
|
+
lifeStatus: LifeStatusEnum.optional().describe("Filter by life status"),
|
|
469
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
470
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
471
|
+
cursor: z.string().optional().describe("Opaque pagination cursor")
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
async ({ treeId, lifeStatus, limit, offset, cursor }) => {
|
|
475
|
+
try {
|
|
476
|
+
const data = await ctx.gql.workspace.request(LIST_PEOPLE, {
|
|
477
|
+
treeId,
|
|
478
|
+
lifeStatus,
|
|
479
|
+
pagination: buildPagination(limit, offset, cursor)
|
|
480
|
+
});
|
|
481
|
+
return jsonResult(data.colListPeople);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
ctx.logger.error("collabkin people_list failed", { error: String(error) });
|
|
484
|
+
return errorResult(
|
|
485
|
+
`Failed to list people: ${error instanceof Error ? error.message : String(error)}`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
);
|
|
490
|
+
server.registerTool(
|
|
491
|
+
track(mcpToolName(ctx.productName, "people_get")),
|
|
492
|
+
{
|
|
493
|
+
title: "Get Person",
|
|
494
|
+
description: "Fetch a single person by ID, including their relationships.",
|
|
495
|
+
inputSchema: { id: z.string().describe("Person ID") }
|
|
496
|
+
},
|
|
497
|
+
async ({ id }) => {
|
|
498
|
+
try {
|
|
499
|
+
const data = await ctx.gql.workspace.request(GET_PERSON, { id });
|
|
500
|
+
if (!data.colPerson) return errorResult(`Person ${id} not found`);
|
|
501
|
+
return jsonResult(data.colPerson);
|
|
502
|
+
} catch (error) {
|
|
503
|
+
ctx.logger.error("collabkin people_get failed", { error: String(error) });
|
|
504
|
+
return errorResult(
|
|
505
|
+
`Failed to get person: ${error instanceof Error ? error.message : String(error)}`
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
);
|
|
510
|
+
server.registerTool(
|
|
511
|
+
track(mcpToolName(ctx.productName, "people_create")),
|
|
512
|
+
{
|
|
513
|
+
title: "Create Person",
|
|
514
|
+
description: "Add a person to a family tree. `treeId` and `givenName` are required; `linkedUserId` links the person to a platform user, `photoFileId` references a wspace-files-svc File.",
|
|
515
|
+
inputSchema: {
|
|
516
|
+
treeId: z.string().min(1).describe("Owning family tree ID"),
|
|
517
|
+
givenName: z.string().min(1).describe("Given (first) name"),
|
|
518
|
+
familyName: z.string().optional().describe("Family (last) name"),
|
|
519
|
+
maidenName: z.string().optional().describe("Maiden name"),
|
|
520
|
+
nickname: z.string().optional().describe("Nickname"),
|
|
521
|
+
gender: GenderEnum.optional().describe("Gender (default UNKNOWN)"),
|
|
522
|
+
lifeStatus: LifeStatusEnum.optional().describe("Life status (default UNKNOWN)"),
|
|
523
|
+
birthDate: z.string().optional().describe("Birth date (ISO-8601 DateTime)"),
|
|
524
|
+
birthPlace: z.string().optional().describe("Birth place"),
|
|
525
|
+
deathDate: z.string().optional().describe("Death date (ISO-8601 DateTime)"),
|
|
526
|
+
bio: z.string().optional().describe("Biographical notes"),
|
|
527
|
+
linkedUserId: z.string().optional().describe("Linked platform user ID"),
|
|
528
|
+
photoFileId: z.string().optional().describe("Portrait photo File ID"),
|
|
529
|
+
tags: z.array(z.string()).optional().describe("Discovery tags")
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
async ({
|
|
533
|
+
treeId,
|
|
534
|
+
givenName,
|
|
535
|
+
familyName,
|
|
536
|
+
maidenName,
|
|
537
|
+
nickname,
|
|
538
|
+
gender,
|
|
539
|
+
lifeStatus,
|
|
540
|
+
birthDate,
|
|
541
|
+
birthPlace,
|
|
542
|
+
deathDate,
|
|
543
|
+
bio,
|
|
544
|
+
linkedUserId,
|
|
545
|
+
photoFileId,
|
|
546
|
+
tags
|
|
547
|
+
}) => {
|
|
548
|
+
try {
|
|
549
|
+
const input = { treeId, givenName };
|
|
550
|
+
if (familyName !== void 0) input.familyName = familyName;
|
|
551
|
+
if (maidenName !== void 0) input.maidenName = maidenName;
|
|
552
|
+
if (nickname !== void 0) input.nickname = nickname;
|
|
553
|
+
if (gender !== void 0) input.gender = gender;
|
|
554
|
+
if (lifeStatus !== void 0) input.lifeStatus = lifeStatus;
|
|
555
|
+
if (birthDate !== void 0) input.birthDate = birthDate;
|
|
556
|
+
if (birthPlace !== void 0) input.birthPlace = birthPlace;
|
|
557
|
+
if (deathDate !== void 0) input.deathDate = deathDate;
|
|
558
|
+
if (bio !== void 0) input.bio = bio;
|
|
559
|
+
if (linkedUserId !== void 0) input.linkedUserId = linkedUserId;
|
|
560
|
+
if (photoFileId !== void 0) input.photoFileId = photoFileId;
|
|
561
|
+
if (tags !== void 0) input.tags = tags;
|
|
562
|
+
const data = await ctx.gql.workspace.request(CREATE_PERSON, { input });
|
|
563
|
+
return jsonResult(data.colCreatePerson);
|
|
564
|
+
} catch (error) {
|
|
565
|
+
ctx.logger.error("collabkin people_create failed", { error: String(error) });
|
|
566
|
+
return errorResult(
|
|
567
|
+
`Failed to create person: ${error instanceof Error ? error.message : String(error)}`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
);
|
|
572
|
+
server.registerTool(
|
|
573
|
+
track(mcpToolName(ctx.productName, "people_update")),
|
|
574
|
+
{
|
|
575
|
+
title: "Update Person",
|
|
576
|
+
description: "Update a person. At least one mutable field must be provided.",
|
|
577
|
+
inputSchema: {
|
|
578
|
+
id: z.string().describe("Person ID"),
|
|
579
|
+
givenName: z.string().optional().describe("Updated given name"),
|
|
580
|
+
familyName: z.string().optional().describe("Updated family name"),
|
|
581
|
+
maidenName: z.string().optional().describe("Updated maiden name"),
|
|
582
|
+
nickname: z.string().optional().describe("Updated nickname"),
|
|
583
|
+
gender: GenderEnum.optional().describe("Updated gender"),
|
|
584
|
+
lifeStatus: LifeStatusEnum.optional().describe("Updated life status"),
|
|
585
|
+
birthDate: z.string().optional().describe("Updated birth date (ISO-8601 DateTime)"),
|
|
586
|
+
birthPlace: z.string().optional().describe("Updated birth place"),
|
|
587
|
+
deathDate: z.string().optional().describe("Updated death date (ISO-8601 DateTime)"),
|
|
588
|
+
bio: z.string().optional().describe("Updated biographical notes"),
|
|
589
|
+
linkedUserId: z.string().optional().describe("Updated linked platform user ID"),
|
|
590
|
+
photoFileId: z.string().optional().describe("Updated portrait photo File ID"),
|
|
591
|
+
tags: z.array(z.string()).optional().describe("Updated tags")
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
async ({
|
|
595
|
+
id,
|
|
596
|
+
givenName,
|
|
597
|
+
familyName,
|
|
598
|
+
maidenName,
|
|
599
|
+
nickname,
|
|
600
|
+
gender,
|
|
601
|
+
lifeStatus,
|
|
602
|
+
birthDate,
|
|
603
|
+
birthPlace,
|
|
604
|
+
deathDate,
|
|
605
|
+
bio,
|
|
606
|
+
linkedUserId,
|
|
607
|
+
photoFileId,
|
|
608
|
+
tags
|
|
609
|
+
}) => {
|
|
610
|
+
try {
|
|
611
|
+
const input = {};
|
|
612
|
+
if (givenName !== void 0) input.givenName = givenName;
|
|
613
|
+
if (familyName !== void 0) input.familyName = familyName;
|
|
614
|
+
if (maidenName !== void 0) input.maidenName = maidenName;
|
|
615
|
+
if (nickname !== void 0) input.nickname = nickname;
|
|
616
|
+
if (gender !== void 0) input.gender = gender;
|
|
617
|
+
if (lifeStatus !== void 0) input.lifeStatus = lifeStatus;
|
|
618
|
+
if (birthDate !== void 0) input.birthDate = birthDate;
|
|
619
|
+
if (birthPlace !== void 0) input.birthPlace = birthPlace;
|
|
620
|
+
if (deathDate !== void 0) input.deathDate = deathDate;
|
|
621
|
+
if (bio !== void 0) input.bio = bio;
|
|
622
|
+
if (linkedUserId !== void 0) input.linkedUserId = linkedUserId;
|
|
623
|
+
if (photoFileId !== void 0) input.photoFileId = photoFileId;
|
|
624
|
+
if (tags !== void 0) input.tags = tags;
|
|
625
|
+
if (Object.keys(input).length === 0) {
|
|
626
|
+
return errorResult("No updates specified \u2014 provide at least one mutable person field");
|
|
627
|
+
}
|
|
628
|
+
const data = await ctx.gql.workspace.request(UPDATE_PERSON, { id, input });
|
|
629
|
+
return jsonResult(data.colUpdatePerson);
|
|
630
|
+
} catch (error) {
|
|
631
|
+
ctx.logger.error("collabkin people_update failed", { error: String(error) });
|
|
632
|
+
return errorResult(
|
|
633
|
+
`Failed to update person: ${error instanceof Error ? error.message : String(error)}`
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
);
|
|
638
|
+
server.registerTool(
|
|
639
|
+
track(mcpToolName(ctx.productName, "people_delete")),
|
|
640
|
+
{
|
|
641
|
+
title: "Delete Person",
|
|
642
|
+
description: "Delete a person from a family tree.",
|
|
643
|
+
inputSchema: { id: z.string().describe("Person ID") }
|
|
644
|
+
},
|
|
645
|
+
async ({ id }) => {
|
|
646
|
+
try {
|
|
647
|
+
const data = await ctx.gql.workspace.request(
|
|
648
|
+
DELETE_PERSON,
|
|
649
|
+
{ id }
|
|
650
|
+
);
|
|
651
|
+
return jsonResult({ success: data.colDeletePerson, id });
|
|
652
|
+
} catch (error) {
|
|
653
|
+
ctx.logger.error("collabkin people_delete failed", { error: String(error) });
|
|
654
|
+
return errorResult(
|
|
655
|
+
`Failed to delete person: ${error instanceof Error ? error.message : String(error)}`
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
function registerRelationshipTools(server, ctx, track) {
|
|
662
|
+
server.registerTool(
|
|
663
|
+
track(mcpToolName(ctx.productName, "relationships_list")),
|
|
664
|
+
{
|
|
665
|
+
title: "List Relationships",
|
|
666
|
+
description: "List relationships, optionally filtered by family tree and/or a specific person (relationships touching that person).",
|
|
667
|
+
inputSchema: {
|
|
668
|
+
treeId: z.string().optional().describe("Filter by family tree ID"),
|
|
669
|
+
personId: z.string().optional().describe("Filter to relationships involving this person")
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
async ({ treeId, personId }) => {
|
|
673
|
+
try {
|
|
674
|
+
const data = await ctx.gql.workspace.request(LIST_RELATIONSHIPS, { treeId, personId });
|
|
675
|
+
return jsonResult(data.colListRelationships);
|
|
676
|
+
} catch (error) {
|
|
677
|
+
ctx.logger.error("collabkin relationships_list failed", { error: String(error) });
|
|
678
|
+
return errorResult(
|
|
679
|
+
`Failed to list relationships: ${error instanceof Error ? error.message : String(error)}`
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
);
|
|
684
|
+
server.registerTool(
|
|
685
|
+
track(mcpToolName(ctx.productName, "relationships_create")),
|
|
686
|
+
{
|
|
687
|
+
title: "Create Relationship",
|
|
688
|
+
description: "Create a relationship between two people in a family tree (e.g. PARENT, SPOUSE, SIBLING). `fromPersonId`, `toPersonId`, `treeId`, and `type` are required.",
|
|
689
|
+
inputSchema: {
|
|
690
|
+
treeId: z.string().min(1).describe("Owning family tree ID"),
|
|
691
|
+
fromPersonId: z.string().min(1).describe("Source person ID"),
|
|
692
|
+
toPersonId: z.string().min(1).describe("Target person ID"),
|
|
693
|
+
type: RelationshipTypeEnum.describe("Relationship type"),
|
|
694
|
+
certainty: CertaintyEnum.optional().describe("Confidence level (default CONFIRMED)"),
|
|
695
|
+
startDate: z.string().optional().describe("Relationship start (ISO-8601 DateTime)"),
|
|
696
|
+
endDate: z.string().optional().describe("Relationship end (ISO-8601 DateTime)"),
|
|
697
|
+
notes: z.string().optional().describe("Free-text notes")
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
async ({ treeId, fromPersonId, toPersonId, type, certainty, startDate, endDate, notes }) => {
|
|
701
|
+
try {
|
|
702
|
+
const input = { treeId, fromPersonId, toPersonId, type };
|
|
703
|
+
if (certainty !== void 0) input.certainty = certainty;
|
|
704
|
+
if (startDate !== void 0) input.startDate = startDate;
|
|
705
|
+
if (endDate !== void 0) input.endDate = endDate;
|
|
706
|
+
if (notes !== void 0) input.notes = notes;
|
|
707
|
+
const data = await ctx.gql.workspace.request(CREATE_RELATIONSHIP, { input });
|
|
708
|
+
return jsonResult(data.colCreateRelationship);
|
|
709
|
+
} catch (error) {
|
|
710
|
+
ctx.logger.error("collabkin relationships_create failed", { error: String(error) });
|
|
711
|
+
return errorResult(
|
|
712
|
+
`Failed to create relationship: ${error instanceof Error ? error.message : String(error)}`
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
);
|
|
717
|
+
server.registerTool(
|
|
718
|
+
track(mcpToolName(ctx.productName, "relationships_delete")),
|
|
719
|
+
{
|
|
720
|
+
title: "Delete Relationship",
|
|
721
|
+
description: "Delete a relationship between two people.",
|
|
722
|
+
inputSchema: { id: z.string().describe("Relationship ID") }
|
|
723
|
+
},
|
|
724
|
+
async ({ id }) => {
|
|
725
|
+
try {
|
|
726
|
+
const data = await ctx.gql.workspace.request(
|
|
727
|
+
DELETE_RELATIONSHIP,
|
|
728
|
+
{ id }
|
|
729
|
+
);
|
|
730
|
+
return jsonResult({ success: data.colDeleteRelationship, id });
|
|
731
|
+
} catch (error) {
|
|
732
|
+
ctx.logger.error("collabkin relationships_delete failed", { error: String(error) });
|
|
733
|
+
return errorResult(
|
|
734
|
+
`Failed to delete relationship: ${error instanceof Error ? error.message : String(error)}`
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
function registerMembershipTools(server, ctx, track) {
|
|
741
|
+
server.registerTool(
|
|
742
|
+
track(mcpToolName(ctx.productName, "memberships_list")),
|
|
743
|
+
{
|
|
744
|
+
title: "List Memberships",
|
|
745
|
+
description: "List the memberships (invited/active/declined/removed) for a family tree.",
|
|
746
|
+
inputSchema: { treeId: z.string().describe("Family tree ID") }
|
|
747
|
+
},
|
|
748
|
+
async ({ treeId }) => {
|
|
749
|
+
try {
|
|
750
|
+
const data = await ctx.gql.workspace.request(LIST_MEMBERSHIPS, { treeId });
|
|
751
|
+
return jsonResult(data.colListMemberships);
|
|
752
|
+
} catch (error) {
|
|
753
|
+
ctx.logger.error("collabkin memberships_list failed", { error: String(error) });
|
|
754
|
+
return errorResult(
|
|
755
|
+
`Failed to list memberships: ${error instanceof Error ? error.message : String(error)}`
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
);
|
|
760
|
+
server.registerTool(
|
|
761
|
+
track(mcpToolName(ctx.productName, "memberships_invite")),
|
|
762
|
+
{
|
|
763
|
+
title: "Invite Member",
|
|
764
|
+
description: "Invite a platform user to collaborate on a family tree. `treeId` and `memberUserId` are required; `personId` optionally links the member to a person record.",
|
|
765
|
+
inputSchema: {
|
|
766
|
+
treeId: z.string().min(1).describe("Family tree ID"),
|
|
767
|
+
memberUserId: z.string().min(1).describe("Invited platform user ID"),
|
|
768
|
+
personId: z.string().optional().describe("Linked person ID within the tree"),
|
|
769
|
+
role: MemberRoleEnum.optional().describe("Member role (default VIEWER)")
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
async ({ treeId, memberUserId, personId, role }) => {
|
|
773
|
+
try {
|
|
774
|
+
const input = { treeId, memberUserId };
|
|
775
|
+
if (personId !== void 0) input.personId = personId;
|
|
776
|
+
if (role !== void 0) input.role = role;
|
|
777
|
+
const data = await ctx.gql.workspace.request(INVITE_MEMBER, { input });
|
|
778
|
+
return jsonResult(data.colInviteMember);
|
|
779
|
+
} catch (error) {
|
|
780
|
+
ctx.logger.error("collabkin memberships_invite failed", { error: String(error) });
|
|
781
|
+
return errorResult(
|
|
782
|
+
`Failed to invite member: ${error instanceof Error ? error.message : String(error)}`
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
);
|
|
787
|
+
server.registerTool(
|
|
788
|
+
track(mcpToolName(ctx.productName, "memberships_accept")),
|
|
789
|
+
{
|
|
790
|
+
title: "Accept Membership",
|
|
791
|
+
description: "Accept a pending membership invitation (moves it to ACTIVE).",
|
|
792
|
+
inputSchema: { id: z.string().describe("Membership ID") }
|
|
793
|
+
},
|
|
794
|
+
async ({ id }) => {
|
|
795
|
+
try {
|
|
796
|
+
const data = await ctx.gql.workspace.request(ACCEPT_MEMBERSHIP, { id });
|
|
797
|
+
return jsonResult(data.colAcceptMembership);
|
|
798
|
+
} catch (error) {
|
|
799
|
+
ctx.logger.error("collabkin memberships_accept failed", { error: String(error) });
|
|
800
|
+
return errorResult(
|
|
801
|
+
`Failed to accept membership: ${error instanceof Error ? error.message : String(error)}`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
);
|
|
806
|
+
server.registerTool(
|
|
807
|
+
track(mcpToolName(ctx.productName, "memberships_remove")),
|
|
808
|
+
{
|
|
809
|
+
title: "Remove Member",
|
|
810
|
+
description: "Remove a member from a family tree (sets status to REMOVED).",
|
|
811
|
+
inputSchema: { id: z.string().describe("Membership ID") }
|
|
812
|
+
},
|
|
813
|
+
async ({ id }) => {
|
|
814
|
+
try {
|
|
815
|
+
const data = await ctx.gql.workspace.request(
|
|
816
|
+
REMOVE_MEMBER,
|
|
817
|
+
{ id }
|
|
818
|
+
);
|
|
819
|
+
return jsonResult({ success: data.colRemoveMember, id });
|
|
820
|
+
} catch (error) {
|
|
821
|
+
ctx.logger.error("collabkin memberships_remove failed", { error: String(error) });
|
|
822
|
+
return errorResult(
|
|
823
|
+
`Failed to remove member: ${error instanceof Error ? error.message : String(error)}`
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
function registerMemoryTools(server, ctx, track) {
|
|
830
|
+
server.registerTool(
|
|
831
|
+
track(mcpToolName(ctx.productName, "memories_list")),
|
|
832
|
+
{
|
|
833
|
+
title: "List Memories",
|
|
834
|
+
description: "List memories, optionally filtered by tree, person, and/or media kind, with cursor/offset pagination.",
|
|
835
|
+
inputSchema: {
|
|
836
|
+
treeId: z.string().optional().describe("Filter by family tree ID"),
|
|
837
|
+
personId: z.string().optional().describe("Filter by associated person ID"),
|
|
838
|
+
kind: MemoryKindEnum.optional().describe("Filter by media kind"),
|
|
839
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
840
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
841
|
+
cursor: z.string().optional().describe("Opaque pagination cursor")
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
async ({ treeId, personId, kind, limit, offset, cursor }) => {
|
|
845
|
+
try {
|
|
846
|
+
const data = await ctx.gql.workspace.request(LIST_MEMORIES, {
|
|
847
|
+
treeId,
|
|
848
|
+
personId,
|
|
849
|
+
kind,
|
|
850
|
+
pagination: buildPagination(limit, offset, cursor)
|
|
851
|
+
});
|
|
852
|
+
return jsonResult(data.colListMemories);
|
|
853
|
+
} catch (error) {
|
|
854
|
+
ctx.logger.error("collabkin memories_list failed", { error: String(error) });
|
|
855
|
+
return errorResult(
|
|
856
|
+
`Failed to list memories: ${error instanceof Error ? error.message : String(error)}`
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
);
|
|
861
|
+
server.registerTool(
|
|
862
|
+
track(mcpToolName(ctx.productName, "memories_get")),
|
|
863
|
+
{
|
|
864
|
+
title: "Get Memory",
|
|
865
|
+
description: "Fetch a single memory by ID, including its comments.",
|
|
866
|
+
inputSchema: { id: z.string().describe("Memory ID") }
|
|
867
|
+
},
|
|
868
|
+
async ({ id }) => {
|
|
869
|
+
try {
|
|
870
|
+
const data = await ctx.gql.workspace.request(GET_MEMORY, { id });
|
|
871
|
+
if (!data.colMemory) return errorResult(`Memory ${id} not found`);
|
|
872
|
+
return jsonResult(data.colMemory);
|
|
873
|
+
} catch (error) {
|
|
874
|
+
ctx.logger.error("collabkin memories_get failed", { error: String(error) });
|
|
875
|
+
return errorResult(
|
|
876
|
+
`Failed to get memory: ${error instanceof Error ? error.message : String(error)}`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
);
|
|
881
|
+
server.registerTool(
|
|
882
|
+
track(mcpToolName(ctx.productName, "memories_create")),
|
|
883
|
+
{
|
|
884
|
+
title: "Create Memory",
|
|
885
|
+
description: "Add a memory (photo/video/document/artifact) to a family tree. `treeId` and `title` are required; `fileId` references a wspace-files-svc File.",
|
|
886
|
+
inputSchema: {
|
|
887
|
+
treeId: z.string().min(1).describe("Owning family tree ID"),
|
|
888
|
+
title: z.string().min(1).describe("Memory title"),
|
|
889
|
+
description: z.string().optional().describe("Memory description"),
|
|
890
|
+
kind: MemoryKindEnum.optional().describe("Media kind (default PHOTO)"),
|
|
891
|
+
fileId: z.string().optional().describe("Media File ID (wspace-files-svc)"),
|
|
892
|
+
personId: z.string().optional().describe("Associated person ID"),
|
|
893
|
+
place: z.string().optional().describe("Where the memory was captured"),
|
|
894
|
+
takenAt: z.string().optional().describe("When it was captured (ISO-8601 DateTime)"),
|
|
895
|
+
tags: z.array(z.string()).optional().describe("Discovery tags"),
|
|
896
|
+
visibility: VisibilityEnum.optional().describe("Visibility (default TREE)")
|
|
897
|
+
}
|
|
898
|
+
},
|
|
899
|
+
async ({
|
|
900
|
+
treeId,
|
|
901
|
+
title,
|
|
902
|
+
description,
|
|
903
|
+
kind,
|
|
904
|
+
fileId,
|
|
905
|
+
personId,
|
|
906
|
+
place,
|
|
907
|
+
takenAt,
|
|
908
|
+
tags,
|
|
909
|
+
visibility
|
|
910
|
+
}) => {
|
|
911
|
+
try {
|
|
912
|
+
const input = { treeId, title };
|
|
913
|
+
if (description !== void 0) input.description = description;
|
|
914
|
+
if (kind !== void 0) input.kind = kind;
|
|
915
|
+
if (fileId !== void 0) input.fileId = fileId;
|
|
916
|
+
if (personId !== void 0) input.personId = personId;
|
|
917
|
+
if (place !== void 0) input.place = place;
|
|
918
|
+
if (takenAt !== void 0) input.takenAt = takenAt;
|
|
919
|
+
if (tags !== void 0) input.tags = tags;
|
|
920
|
+
if (visibility !== void 0) input.visibility = visibility;
|
|
921
|
+
const data = await ctx.gql.workspace.request(CREATE_MEMORY, { input });
|
|
922
|
+
return jsonResult(data.colCreateMemory);
|
|
923
|
+
} catch (error) {
|
|
924
|
+
ctx.logger.error("collabkin memories_create failed", { error: String(error) });
|
|
925
|
+
return errorResult(
|
|
926
|
+
`Failed to create memory: ${error instanceof Error ? error.message : String(error)}`
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
);
|
|
931
|
+
server.registerTool(
|
|
932
|
+
track(mcpToolName(ctx.productName, "memories_update")),
|
|
933
|
+
{
|
|
934
|
+
title: "Update Memory",
|
|
935
|
+
description: "Update a memory. At least one mutable field must be provided.",
|
|
936
|
+
inputSchema: {
|
|
937
|
+
id: z.string().describe("Memory ID"),
|
|
938
|
+
title: z.string().optional().describe("Updated title"),
|
|
939
|
+
description: z.string().optional().describe("Updated description"),
|
|
940
|
+
kind: MemoryKindEnum.optional().describe("Updated media kind"),
|
|
941
|
+
fileId: z.string().optional().describe("Updated media File ID"),
|
|
942
|
+
personId: z.string().optional().describe("Updated associated person ID"),
|
|
943
|
+
place: z.string().optional().describe("Updated place"),
|
|
944
|
+
takenAt: z.string().optional().describe("Updated capture date (ISO-8601 DateTime)"),
|
|
945
|
+
tags: z.array(z.string()).optional().describe("Updated tags"),
|
|
946
|
+
visibility: VisibilityEnum.optional().describe("Updated visibility")
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
async ({
|
|
950
|
+
id,
|
|
951
|
+
title,
|
|
952
|
+
description,
|
|
953
|
+
kind,
|
|
954
|
+
fileId,
|
|
955
|
+
personId,
|
|
956
|
+
place,
|
|
957
|
+
takenAt,
|
|
958
|
+
tags,
|
|
959
|
+
visibility
|
|
960
|
+
}) => {
|
|
961
|
+
try {
|
|
962
|
+
const input = {};
|
|
963
|
+
if (title !== void 0) input.title = title;
|
|
964
|
+
if (description !== void 0) input.description = description;
|
|
965
|
+
if (kind !== void 0) input.kind = kind;
|
|
966
|
+
if (fileId !== void 0) input.fileId = fileId;
|
|
967
|
+
if (personId !== void 0) input.personId = personId;
|
|
968
|
+
if (place !== void 0) input.place = place;
|
|
969
|
+
if (takenAt !== void 0) input.takenAt = takenAt;
|
|
970
|
+
if (tags !== void 0) input.tags = tags;
|
|
971
|
+
if (visibility !== void 0) input.visibility = visibility;
|
|
972
|
+
if (Object.keys(input).length === 0) {
|
|
973
|
+
return errorResult("No updates specified \u2014 provide at least one mutable memory field");
|
|
974
|
+
}
|
|
975
|
+
const data = await ctx.gql.workspace.request(UPDATE_MEMORY, { id, input });
|
|
976
|
+
return jsonResult(data.colUpdateMemory);
|
|
977
|
+
} catch (error) {
|
|
978
|
+
ctx.logger.error("collabkin memories_update failed", { error: String(error) });
|
|
979
|
+
return errorResult(
|
|
980
|
+
`Failed to update memory: ${error instanceof Error ? error.message : String(error)}`
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
);
|
|
985
|
+
server.registerTool(
|
|
986
|
+
track(mcpToolName(ctx.productName, "memories_delete")),
|
|
987
|
+
{
|
|
988
|
+
title: "Delete Memory",
|
|
989
|
+
description: "Delete a memory from a family tree.",
|
|
990
|
+
inputSchema: { id: z.string().describe("Memory ID") }
|
|
991
|
+
},
|
|
992
|
+
async ({ id }) => {
|
|
993
|
+
try {
|
|
994
|
+
const data = await ctx.gql.workspace.request(
|
|
995
|
+
DELETE_MEMORY,
|
|
996
|
+
{ id }
|
|
997
|
+
);
|
|
998
|
+
return jsonResult({ success: data.colDeleteMemory, id });
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
ctx.logger.error("collabkin memories_delete failed", { error: String(error) });
|
|
1001
|
+
return errorResult(
|
|
1002
|
+
`Failed to delete memory: ${error instanceof Error ? error.message : String(error)}`
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
function registerStoryTools(server, ctx, track) {
|
|
1009
|
+
server.registerTool(
|
|
1010
|
+
track(mcpToolName(ctx.productName, "stories_list")),
|
|
1011
|
+
{
|
|
1012
|
+
title: "List Stories",
|
|
1013
|
+
description: "List stories, optionally filtered by tree and/or person, with cursor/offset pagination.",
|
|
1014
|
+
inputSchema: {
|
|
1015
|
+
treeId: z.string().optional().describe("Filter by family tree ID"),
|
|
1016
|
+
personId: z.string().optional().describe("Filter by associated person ID"),
|
|
1017
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
1018
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
1019
|
+
cursor: z.string().optional().describe("Opaque pagination cursor")
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
1022
|
+
async ({ treeId, personId, limit, offset, cursor }) => {
|
|
1023
|
+
try {
|
|
1024
|
+
const data = await ctx.gql.workspace.request(LIST_STORIES, {
|
|
1025
|
+
treeId,
|
|
1026
|
+
personId,
|
|
1027
|
+
pagination: buildPagination(limit, offset, cursor)
|
|
1028
|
+
});
|
|
1029
|
+
return jsonResult(data.colListStories);
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
ctx.logger.error("collabkin stories_list failed", { error: String(error) });
|
|
1032
|
+
return errorResult(
|
|
1033
|
+
`Failed to list stories: ${error instanceof Error ? error.message : String(error)}`
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
);
|
|
1038
|
+
server.registerTool(
|
|
1039
|
+
track(mcpToolName(ctx.productName, "stories_get")),
|
|
1040
|
+
{
|
|
1041
|
+
title: "Get Story",
|
|
1042
|
+
description: "Fetch a single story by ID, including its comments.",
|
|
1043
|
+
inputSchema: { id: z.string().describe("Story ID") }
|
|
1044
|
+
},
|
|
1045
|
+
async ({ id }) => {
|
|
1046
|
+
try {
|
|
1047
|
+
const data = await ctx.gql.workspace.request(GET_STORY, { id });
|
|
1048
|
+
if (!data.colStory) return errorResult(`Story ${id} not found`);
|
|
1049
|
+
return jsonResult(data.colStory);
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
ctx.logger.error("collabkin stories_get failed", { error: String(error) });
|
|
1052
|
+
return errorResult(
|
|
1053
|
+
`Failed to get story: ${error instanceof Error ? error.message : String(error)}`
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
);
|
|
1058
|
+
server.registerTool(
|
|
1059
|
+
track(mcpToolName(ctx.productName, "stories_create")),
|
|
1060
|
+
{
|
|
1061
|
+
title: "Create Story",
|
|
1062
|
+
description: "Add a story to a family tree. `treeId` and `title` are required; for recorded stories set `format` (AUDIO/VIDEO) and `recordingFileId`.",
|
|
1063
|
+
inputSchema: {
|
|
1064
|
+
treeId: z.string().min(1).describe("Owning family tree ID"),
|
|
1065
|
+
title: z.string().min(1).describe("Story title"),
|
|
1066
|
+
body: z.string().optional().describe("Written story body"),
|
|
1067
|
+
format: StoryFormatEnum.optional().describe("Story format (default WRITTEN)"),
|
|
1068
|
+
narrator: z.string().optional().describe("Narrator name"),
|
|
1069
|
+
personId: z.string().optional().describe("Associated person ID"),
|
|
1070
|
+
recordingFileId: z.string().optional().describe("Recording File ID (wspace-files-svc)"),
|
|
1071
|
+
tags: z.array(z.string()).optional().describe("Discovery tags"),
|
|
1072
|
+
visibility: VisibilityEnum.optional().describe("Visibility (default TREE)")
|
|
1073
|
+
}
|
|
1074
|
+
},
|
|
1075
|
+
async ({
|
|
1076
|
+
treeId,
|
|
1077
|
+
title,
|
|
1078
|
+
body,
|
|
1079
|
+
format,
|
|
1080
|
+
narrator,
|
|
1081
|
+
personId,
|
|
1082
|
+
recordingFileId,
|
|
1083
|
+
tags,
|
|
1084
|
+
visibility
|
|
1085
|
+
}) => {
|
|
1086
|
+
try {
|
|
1087
|
+
const input = { treeId, title };
|
|
1088
|
+
if (body !== void 0) input.body = body;
|
|
1089
|
+
if (format !== void 0) input.format = format;
|
|
1090
|
+
if (narrator !== void 0) input.narrator = narrator;
|
|
1091
|
+
if (personId !== void 0) input.personId = personId;
|
|
1092
|
+
if (recordingFileId !== void 0) input.recordingFileId = recordingFileId;
|
|
1093
|
+
if (tags !== void 0) input.tags = tags;
|
|
1094
|
+
if (visibility !== void 0) input.visibility = visibility;
|
|
1095
|
+
const data = await ctx.gql.workspace.request(CREATE_STORY, { input });
|
|
1096
|
+
return jsonResult(data.colCreateStory);
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
ctx.logger.error("collabkin stories_create failed", { error: String(error) });
|
|
1099
|
+
return errorResult(
|
|
1100
|
+
`Failed to create story: ${error instanceof Error ? error.message : String(error)}`
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
);
|
|
1105
|
+
server.registerTool(
|
|
1106
|
+
track(mcpToolName(ctx.productName, "stories_delete")),
|
|
1107
|
+
{
|
|
1108
|
+
title: "Delete Story",
|
|
1109
|
+
description: "Delete a story from a family tree.",
|
|
1110
|
+
inputSchema: { id: z.string().describe("Story ID") }
|
|
1111
|
+
},
|
|
1112
|
+
async ({ id }) => {
|
|
1113
|
+
try {
|
|
1114
|
+
const data = await ctx.gql.workspace.request(
|
|
1115
|
+
DELETE_STORY,
|
|
1116
|
+
{ id }
|
|
1117
|
+
);
|
|
1118
|
+
return jsonResult({ success: data.colDeleteStory, id });
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
ctx.logger.error("collabkin stories_delete failed", { error: String(error) });
|
|
1121
|
+
return errorResult(
|
|
1122
|
+
`Failed to delete story: ${error instanceof Error ? error.message : String(error)}`
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
function registerCommentTools(server, ctx, track) {
|
|
1129
|
+
server.registerTool(
|
|
1130
|
+
track(mcpToolName(ctx.productName, "comments_list")),
|
|
1131
|
+
{
|
|
1132
|
+
title: "List Comments",
|
|
1133
|
+
description: "List comments for a memory or a story. Provide exactly one of `memoryId` or `storyId`.",
|
|
1134
|
+
inputSchema: {
|
|
1135
|
+
memoryId: z.string().optional().describe("List comments on this memory"),
|
|
1136
|
+
storyId: z.string().optional().describe("List comments on this story")
|
|
1137
|
+
}
|
|
1138
|
+
},
|
|
1139
|
+
async ({ memoryId, storyId }) => {
|
|
1140
|
+
try {
|
|
1141
|
+
if (!memoryId && !storyId) {
|
|
1142
|
+
return errorResult("Provide exactly one of memoryId or storyId");
|
|
1143
|
+
}
|
|
1144
|
+
const data = await ctx.gql.workspace.request(LIST_COMMENTS, { memoryId, storyId });
|
|
1145
|
+
return jsonResult(data.colListComments);
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
ctx.logger.error("collabkin comments_list failed", { error: String(error) });
|
|
1148
|
+
return errorResult(
|
|
1149
|
+
`Failed to list comments: ${error instanceof Error ? error.message : String(error)}`
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
);
|
|
1154
|
+
server.registerTool(
|
|
1155
|
+
track(mcpToolName(ctx.productName, "comments_add")),
|
|
1156
|
+
{
|
|
1157
|
+
title: "Add Comment",
|
|
1158
|
+
description: "Add a comment to a memory or a story. `body` is required; provide exactly one of `memoryId` or `storyId`.",
|
|
1159
|
+
inputSchema: {
|
|
1160
|
+
body: z.string().min(1).describe("Comment body"),
|
|
1161
|
+
memoryId: z.string().optional().describe("Memory to comment on"),
|
|
1162
|
+
storyId: z.string().optional().describe("Story to comment on")
|
|
1163
|
+
}
|
|
1164
|
+
},
|
|
1165
|
+
async ({ body, memoryId, storyId }) => {
|
|
1166
|
+
try {
|
|
1167
|
+
if (!memoryId && !storyId) {
|
|
1168
|
+
return errorResult("Provide exactly one of memoryId or storyId");
|
|
1169
|
+
}
|
|
1170
|
+
const input = { body };
|
|
1171
|
+
if (memoryId !== void 0) input.memoryId = memoryId;
|
|
1172
|
+
if (storyId !== void 0) input.storyId = storyId;
|
|
1173
|
+
const data = await ctx.gql.workspace.request(ADD_COMMENT, { input });
|
|
1174
|
+
return jsonResult(data.colAddComment);
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
ctx.logger.error("collabkin comments_add failed", { error: String(error) });
|
|
1177
|
+
return errorResult(
|
|
1178
|
+
`Failed to add comment: ${error instanceof Error ? error.message : String(error)}`
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
server.registerTool(
|
|
1184
|
+
track(mcpToolName(ctx.productName, "comments_delete")),
|
|
1185
|
+
{
|
|
1186
|
+
title: "Delete Comment",
|
|
1187
|
+
description: "Delete a comment.",
|
|
1188
|
+
inputSchema: { id: z.string().describe("Comment ID") }
|
|
1189
|
+
},
|
|
1190
|
+
async ({ id }) => {
|
|
1191
|
+
try {
|
|
1192
|
+
const data = await ctx.gql.workspace.request(
|
|
1193
|
+
DELETE_COMMENT,
|
|
1194
|
+
{ id }
|
|
1195
|
+
);
|
|
1196
|
+
return jsonResult({ success: data.colDeleteComment, id });
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
ctx.logger.error("collabkin comments_delete failed", { error: String(error) });
|
|
1199
|
+
return errorResult(
|
|
1200
|
+
`Failed to delete comment: ${error instanceof Error ? error.message : String(error)}`
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
function registerTimelineTools(server, ctx, track) {
|
|
1207
|
+
server.registerTool(
|
|
1208
|
+
track(mcpToolName(ctx.productName, "timeline_list")),
|
|
1209
|
+
{
|
|
1210
|
+
title: "List Timeline",
|
|
1211
|
+
description: "The family timeline \u2014 dated events for a tree (optionally one person), ordered most-recent first. `treeId` is required; filter by event `type` if desired.",
|
|
1212
|
+
inputSchema: {
|
|
1213
|
+
treeId: z.string().describe("Family tree ID"),
|
|
1214
|
+
personId: z.string().optional().describe("Restrict to events about this person"),
|
|
1215
|
+
type: EventTypeEnum.optional().describe("Filter by event type")
|
|
1216
|
+
}
|
|
1217
|
+
},
|
|
1218
|
+
async ({ treeId, personId, type }) => {
|
|
1219
|
+
try {
|
|
1220
|
+
const data = await ctx.gql.workspace.request(TIMELINE, { treeId, personId, type });
|
|
1221
|
+
return jsonResult(data.colTimeline);
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
ctx.logger.error("collabkin timeline_list failed", { error: String(error) });
|
|
1224
|
+
return errorResult(
|
|
1225
|
+
`Failed to list timeline: ${error instanceof Error ? error.message : String(error)}`
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
);
|
|
1230
|
+
server.registerTool(
|
|
1231
|
+
track(mcpToolName(ctx.productName, "timeline_create_event")),
|
|
1232
|
+
{
|
|
1233
|
+
title: "Create Event",
|
|
1234
|
+
description: "Add a dated event to the family timeline. `treeId`, `title`, and `date` are required.",
|
|
1235
|
+
inputSchema: {
|
|
1236
|
+
treeId: z.string().min(1).describe("Owning family tree ID"),
|
|
1237
|
+
title: z.string().min(1).describe("Event title"),
|
|
1238
|
+
date: z.string().describe("Event date (ISO-8601 DateTime)"),
|
|
1239
|
+
type: EventTypeEnum.optional().describe("Event type (default MILESTONE)"),
|
|
1240
|
+
description: z.string().optional().describe("Event description"),
|
|
1241
|
+
place: z.string().optional().describe("Where it happened"),
|
|
1242
|
+
personId: z.string().optional().describe("Person the event is about"),
|
|
1243
|
+
isApproximate: z.boolean().optional().describe("Whether the date is approximate (default false)")
|
|
1244
|
+
}
|
|
1245
|
+
},
|
|
1246
|
+
async ({ treeId, title, date, type, description, place, personId, isApproximate }) => {
|
|
1247
|
+
try {
|
|
1248
|
+
const input = { treeId, title, date };
|
|
1249
|
+
if (type !== void 0) input.type = type;
|
|
1250
|
+
if (description !== void 0) input.description = description;
|
|
1251
|
+
if (place !== void 0) input.place = place;
|
|
1252
|
+
if (personId !== void 0) input.personId = personId;
|
|
1253
|
+
if (isApproximate !== void 0) input.isApproximate = isApproximate;
|
|
1254
|
+
const data = await ctx.gql.workspace.request(CREATE_EVENT, { input });
|
|
1255
|
+
return jsonResult(data.colCreateEvent);
|
|
1256
|
+
} catch (error) {
|
|
1257
|
+
ctx.logger.error("collabkin timeline_create_event failed", { error: String(error) });
|
|
1258
|
+
return errorResult(
|
|
1259
|
+
`Failed to create event: ${error instanceof Error ? error.message : String(error)}`
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
);
|
|
1264
|
+
server.registerTool(
|
|
1265
|
+
track(mcpToolName(ctx.productName, "timeline_delete_event")),
|
|
1266
|
+
{
|
|
1267
|
+
title: "Delete Event",
|
|
1268
|
+
description: "Delete an event from the family timeline.",
|
|
1269
|
+
inputSchema: { id: z.string().describe("Event ID") }
|
|
1270
|
+
},
|
|
1271
|
+
async ({ id }) => {
|
|
1272
|
+
try {
|
|
1273
|
+
const data = await ctx.gql.workspace.request(
|
|
1274
|
+
DELETE_EVENT,
|
|
1275
|
+
{ id }
|
|
1276
|
+
);
|
|
1277
|
+
return jsonResult({ success: data.colDeleteEvent, id });
|
|
1278
|
+
} catch (error) {
|
|
1279
|
+
ctx.logger.error("collabkin timeline_delete_event failed", { error: String(error) });
|
|
1280
|
+
return errorResult(
|
|
1281
|
+
`Failed to delete event: ${error instanceof Error ? error.message : String(error)}`
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// src/modules/collabkin/tools/health.ts
|
|
1289
|
+
function registerHealthTools(server, ctx, track) {
|
|
1290
|
+
server.registerTool(
|
|
1291
|
+
track(mcpToolName(ctx.productName, "health")),
|
|
1292
|
+
{
|
|
1293
|
+
title: "Collabkin Health Check",
|
|
1294
|
+
description: "Public health probe for the Collabkin subgraph (colHealth). Requires no authentication.",
|
|
1295
|
+
inputSchema: {}
|
|
1296
|
+
},
|
|
1297
|
+
async () => {
|
|
1298
|
+
try {
|
|
1299
|
+
const data = await ctx.gql.workspace.request(HEALTH, {});
|
|
1300
|
+
return jsonResult({ ok: true, status: data.colHealth });
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
ctx.logger.error("collabkin health failed", { error: String(error) });
|
|
1303
|
+
return errorResult(
|
|
1304
|
+
`Collabkin health check failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/modules/collabkin/resources.ts
|
|
1312
|
+
function registerCollabkinResources(server, ctx, track) {
|
|
1313
|
+
const schemaUri = track(mcpResourceUri(ctx.productName, "schema"));
|
|
1314
|
+
const treesUri = track(mcpResourceUri(ctx.productName, "trees"));
|
|
1315
|
+
server.registerResource(
|
|
1316
|
+
"collabkin-schema",
|
|
1317
|
+
schemaUri,
|
|
1318
|
+
{
|
|
1319
|
+
title: "Collabkin Domain Schema",
|
|
1320
|
+
description: "Overview of the Collabkin family-heritage domain \u2014 entities, enums, and the per-entity tool surface.",
|
|
1321
|
+
mimeType: "text/plain"
|
|
1322
|
+
},
|
|
1323
|
+
async () => {
|
|
1324
|
+
ctx.logger.debug(`Resource ${schemaUri} requested`);
|
|
1325
|
+
const schema = `# Collabkin Domain (Overview)
|
|
1326
|
+
|
|
1327
|
+
A private, collaborative home for a family and its ancestry.
|
|
1328
|
+
|
|
1329
|
+
## Entities
|
|
1330
|
+
- ColFamilyTree \u2014 top-level container (people, memberships, memories, stories, timeline)
|
|
1331
|
+
- ColPerson \u2014 a relative or ancestor within a tree (has relationships)
|
|
1332
|
+
- ColRelationship \u2014 a typed edge between two people (PARENT, SPOUSE, \u2026)
|
|
1333
|
+
- ColMembership \u2014 a family member / invited collaborator + consent state
|
|
1334
|
+
- ColMemory (+ColComment) \u2014 a photo/video/document/artifact + its comments
|
|
1335
|
+
- ColStory (+ColComment) \u2014 a written/recorded family story + its comments
|
|
1336
|
+
- ColEvent \u2014 a dated entry on the family timeline
|
|
1337
|
+
|
|
1338
|
+
## Enums
|
|
1339
|
+
- ColTreeStatus: ACTIVE, ARCHIVED
|
|
1340
|
+
- ColGender: FEMALE, MALE, OTHER, UNKNOWN
|
|
1341
|
+
- ColLifeStatus: DECEASED, LIVING, UNKNOWN
|
|
1342
|
+
- ColMemberRole: CONTRIBUTOR, OWNER, STEWARD, VIEWER
|
|
1343
|
+
- ColMembershipStatus: ACTIVE, DECLINED, INVITED, REMOVED
|
|
1344
|
+
- ColMemoryKind: ARTIFACT, AUDIO, DOCUMENT, PHOTO, VIDEO
|
|
1345
|
+
- ColVisibility: PRIVATE, STEWARDS, TREE
|
|
1346
|
+
- ColEventType: BIRTH, DEATH, DIVORCE, EDUCATION, IMMIGRATION, MARRIAGE, MILESTONE, MILITARY, OTHER, RESIDENCE
|
|
1347
|
+
- ColRelationshipType: CHILD, GRANDCHILD, GRANDPARENT, GUARDIAN, PARENT, PARTNER, SIBLING, SPOUSE
|
|
1348
|
+
- ColStoryFormat: AUDIO, VIDEO, WRITTEN
|
|
1349
|
+
- ColCertainty: CONFIRMED, DISPUTED, PROBABLE, UNVERIFIED
|
|
1350
|
+
|
|
1351
|
+
## Tool groups
|
|
1352
|
+
trees, people, relationships, memberships, memories, stories, comments, timeline.
|
|
1353
|
+
All run against the WORKSPACE gateway (workspace-scoped) except the public health probe.
|
|
1354
|
+
Memory/story/portrait/cover blobs live in wspace-files-svc (referenced by File id).
|
|
1355
|
+
`;
|
|
1356
|
+
return {
|
|
1357
|
+
contents: [{ uri: schemaUri, text: schema, mimeType: "text/plain" }]
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
);
|
|
1361
|
+
server.registerResource(
|
|
1362
|
+
"collabkin-trees",
|
|
1363
|
+
treesUri,
|
|
1364
|
+
{
|
|
1365
|
+
title: "Collabkin Family Tree Directory",
|
|
1366
|
+
description: "The first 50 active family trees in the bound workspace.",
|
|
1367
|
+
mimeType: "application/json"
|
|
1368
|
+
},
|
|
1369
|
+
async () => {
|
|
1370
|
+
ctx.logger.debug(`Resource ${treesUri} requested`);
|
|
1371
|
+
try {
|
|
1372
|
+
const data = await ctx.gql.workspace.request(
|
|
1373
|
+
LIST_FAMILY_TREES,
|
|
1374
|
+
{ status: "ACTIVE", pagination: { limit: 50, offset: 0 } }
|
|
1375
|
+
);
|
|
1376
|
+
return {
|
|
1377
|
+
contents: [
|
|
1378
|
+
{
|
|
1379
|
+
uri: treesUri,
|
|
1380
|
+
text: JSON.stringify(data.colListFamilyTrees, null, 2),
|
|
1381
|
+
mimeType: "application/json"
|
|
1382
|
+
}
|
|
1383
|
+
]
|
|
1384
|
+
};
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
return {
|
|
1387
|
+
contents: [
|
|
1388
|
+
{
|
|
1389
|
+
uri: treesUri,
|
|
1390
|
+
text: JSON.stringify({
|
|
1391
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1392
|
+
}),
|
|
1393
|
+
mimeType: "application/json"
|
|
1394
|
+
}
|
|
1395
|
+
]
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
function registerCollabkinPrompts(server, ctx, track) {
|
|
1402
|
+
const p = ctx.productName;
|
|
1403
|
+
const setupName = track(mcpPromptName(p, "tree-setup"));
|
|
1404
|
+
const heritageName = track(mcpPromptName(p, "heritage-curation"));
|
|
1405
|
+
server.registerPrompt(
|
|
1406
|
+
setupName,
|
|
1407
|
+
{
|
|
1408
|
+
title: "Family Tree Setup",
|
|
1409
|
+
description: "Guide for setting up a new Collabkin family tree end to end: create the tree, add people, connect them with relationships, and invite family members.",
|
|
1410
|
+
argsSchema: {
|
|
1411
|
+
name: z.string().optional().describe("Family tree name"),
|
|
1412
|
+
surname: z.string().optional().describe("Primary family surname")
|
|
1413
|
+
}
|
|
1414
|
+
},
|
|
1415
|
+
({ name, surname }) => {
|
|
1416
|
+
ctx.logger.debug(`Prompt ${setupName} called`, { name, surname });
|
|
1417
|
+
const nameNote = name ? `Use the tree name "${name}".` : "Ask the user for the family tree name.";
|
|
1418
|
+
const surnameNote = surname ? `Use surname "${surname}".` : "";
|
|
1419
|
+
return {
|
|
1420
|
+
messages: [
|
|
1421
|
+
{
|
|
1422
|
+
role: "user",
|
|
1423
|
+
content: {
|
|
1424
|
+
type: "text",
|
|
1425
|
+
text: `Help me set up a new Collabkin family tree.
|
|
1426
|
+
|
|
1427
|
+
1. **Create Tree**: Use ${p}_trees_create. ${nameNote} ${surnameNote}
|
|
1428
|
+
2. **Add People**: Use ${p}_people_create for each relative/ancestor \u2014 set treeId, givenName (required), plus gender, lifeStatus, birthDate/birthPlace, and deathDate for ancestors.
|
|
1429
|
+
3. **Connect Relationships**: Use ${p}_relationships_create to link people (PARENT, SPOUSE, SIBLING, CHILD, \u2026) \u2014 pass treeId, fromPersonId, toPersonId, and type.
|
|
1430
|
+
4. **Invite Family**: Use ${p}_memberships_invite to bring in collaborators (treeId, memberUserId, role=CONTRIBUTOR/VIEWER).
|
|
1431
|
+
5. **Verify**: Use ${p}_people_list and ${p}_relationships_list (filtered by the new treeId) to confirm the shape.
|
|
1432
|
+
|
|
1433
|
+
Show results at each step and confirm before inviting members.`
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
]
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
);
|
|
1440
|
+
server.registerPrompt(
|
|
1441
|
+
heritageName,
|
|
1442
|
+
{
|
|
1443
|
+
title: "Heritage Curation",
|
|
1444
|
+
description: "Workflow for enriching a family tree: add memories and stories, discuss them via comments, and build out the family timeline.",
|
|
1445
|
+
argsSchema: {
|
|
1446
|
+
treeId: z.string().optional().describe("Family tree ID to curate")
|
|
1447
|
+
}
|
|
1448
|
+
},
|
|
1449
|
+
({ treeId }) => {
|
|
1450
|
+
ctx.logger.debug(`Prompt ${heritageName} called`, { treeId });
|
|
1451
|
+
const treeNote = treeId ? `Curate tree "${treeId}".` : `First list trees with ${p}_trees_list and pick one.`;
|
|
1452
|
+
return {
|
|
1453
|
+
messages: [
|
|
1454
|
+
{
|
|
1455
|
+
role: "user",
|
|
1456
|
+
content: {
|
|
1457
|
+
type: "text",
|
|
1458
|
+
text: `Help me curate family heritage in Collabkin. ${treeNote}
|
|
1459
|
+
|
|
1460
|
+
1. **Memories**: Use ${p}_memories_create to add photos/videos/documents/artifacts (treeId, title, kind, optional fileId from wspace-files, personId, visibility).
|
|
1461
|
+
2. **Stories**: Use ${p}_stories_create for written or recorded stories (treeId, title, body or format+recordingFileId, narrator, personId).
|
|
1462
|
+
3. **Discuss**: Use ${p}_comments_add to discuss a memory or story (exactly one of memoryId/storyId).
|
|
1463
|
+
4. **Timeline**: Use ${p}_timeline_create_event to add dated milestones (births, marriages, immigrations, \u2026) and ${p}_timeline_list to review them most-recent first.
|
|
1464
|
+
5. **Review**: Use ${p}_memories_list and ${p}_stories_list to confirm what was captured.
|
|
1465
|
+
|
|
1466
|
+
Summarize what was added after each step and suggest gaps to fill.`
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
]
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// src/modules/collabkin/index.ts
|
|
1476
|
+
var collabkinModule = {
|
|
1477
|
+
id: "collabkin",
|
|
1478
|
+
version: "0.0.1",
|
|
1479
|
+
displayName: "Collabkin",
|
|
1480
|
+
dependsOn: ["auth"],
|
|
1481
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
1482
|
+
register(server, ctx) {
|
|
1483
|
+
const tools = [];
|
|
1484
|
+
const resources = [];
|
|
1485
|
+
const prompts = [];
|
|
1486
|
+
const trackTool = (n) => {
|
|
1487
|
+
tools.push(n);
|
|
1488
|
+
return n;
|
|
1489
|
+
};
|
|
1490
|
+
const trackResource = (uri) => {
|
|
1491
|
+
resources.push(uri);
|
|
1492
|
+
return uri;
|
|
1493
|
+
};
|
|
1494
|
+
const trackPrompt = (n) => {
|
|
1495
|
+
prompts.push(n);
|
|
1496
|
+
return n;
|
|
1497
|
+
};
|
|
1498
|
+
registerTreeTools(server, ctx, trackTool);
|
|
1499
|
+
registerPersonTools(server, ctx, trackTool);
|
|
1500
|
+
registerRelationshipTools(server, ctx, trackTool);
|
|
1501
|
+
registerMembershipTools(server, ctx, trackTool);
|
|
1502
|
+
registerMemoryTools(server, ctx, trackTool);
|
|
1503
|
+
registerStoryTools(server, ctx, trackTool);
|
|
1504
|
+
registerCommentTools(server, ctx, trackTool);
|
|
1505
|
+
registerTimelineTools(server, ctx, trackTool);
|
|
1506
|
+
registerHealthTools(server, ctx, trackTool);
|
|
1507
|
+
registerCollabkinResources(server, ctx, trackResource);
|
|
1508
|
+
registerCollabkinPrompts(server, ctx, trackPrompt);
|
|
1509
|
+
return { tools, resources, prompts };
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
var collabkin_default = collabkinModule;
|
|
1513
|
+
|
|
1514
|
+
export { collabkin_default as default };
|
|
1515
|
+
//# sourceMappingURL=index.js.map
|
|
1516
|
+
//# sourceMappingURL=index.js.map
|