@burdenoff/mcp-libs 2026.628.1 → 2026.629.1
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/adaptercloud/index.cjs +1841 -0
- package/dist/modules/adaptercloud/index.cjs.map +1 -0
- package/dist/modules/adaptercloud/index.d.cts +8 -0
- package/dist/modules/adaptercloud/index.d.ts +8 -0
- package/dist/modules/adaptercloud/index.js +1780 -0
- package/dist/modules/adaptercloud/index.js.map +1 -0
- package/dist/modules/atheletebridge/index.cjs +1707 -0
- package/dist/modules/atheletebridge/index.cjs.map +1 -0
- package/dist/modules/atheletebridge/index.d.cts +8 -0
- package/dist/modules/atheletebridge/index.d.ts +8 -0
- package/dist/modules/atheletebridge/index.js +1646 -0
- package/dist/modules/atheletebridge/index.js.map +1 -0
- package/dist/modules/coolandlovely/index.cjs +1935 -0
- package/dist/modules/coolandlovely/index.cjs.map +1 -0
- package/dist/modules/coolandlovely/index.d.cts +8 -0
- package/dist/modules/coolandlovely/index.d.ts +8 -0
- package/dist/modules/coolandlovely/index.js +1874 -0
- package/dist/modules/coolandlovely/index.js.map +1 -0
- package/dist/modules/hookmovies/index.cjs +2024 -0
- package/dist/modules/hookmovies/index.cjs.map +1 -0
- package/dist/modules/hookmovies/index.d.cts +8 -0
- package/dist/modules/hookmovies/index.d.ts +8 -0
- package/dist/modules/hookmovies/index.js +1963 -0
- package/dist/modules/hookmovies/index.js.map +1 -0
- package/dist/modules/labsofscience/index.cjs +2039 -0
- package/dist/modules/labsofscience/index.cjs.map +1 -0
- package/dist/modules/labsofscience/index.d.cts +8 -0
- package/dist/modules/labsofscience/index.d.ts +8 -0
- package/dist/modules/labsofscience/index.js +1978 -0
- package/dist/modules/labsofscience/index.js.map +1 -0
- package/dist/modules/subscriberbot/index.cjs +2037 -0
- package/dist/modules/subscriberbot/index.cjs.map +1 -0
- package/dist/modules/subscriberbot/index.d.cts +8 -0
- package/dist/modules/subscriberbot/index.d.ts +8 -0
- package/dist/modules/subscriberbot/index.js +1976 -0
- package/dist/modules/subscriberbot/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1874 @@
|
|
|
1
|
+
import { mcpToolName, errorResult, jsonResult, mcpResourceUri, mcpPromptName } from '../../chunk-H2OYWLOA.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
// src/modules/coolandlovely/operations.ts
|
|
5
|
+
var BRAND_FIELDS = `
|
|
6
|
+
id name handle tagline bio status aesthetics tags isFeatured websiteUrl
|
|
7
|
+
workspaceId createdAt updatedAt logo { id } cover { id }
|
|
8
|
+
`;
|
|
9
|
+
var COLLECTION_FIELDS = `
|
|
10
|
+
id name description brandId season status tags publishAt endAt
|
|
11
|
+
workspaceId createdAt updatedAt cover { id }
|
|
12
|
+
`;
|
|
13
|
+
var STYLE_ITEM_FIELDS = `
|
|
14
|
+
id name description brandId collectionId category status sku price currency
|
|
15
|
+
colours materials sizes tags workspaceId createdAt updatedAt image { id }
|
|
16
|
+
`;
|
|
17
|
+
var LOOK_ITEM_FIELDS = `
|
|
18
|
+
id lookId styleItemId position styling workspaceId createdAt updatedAt
|
|
19
|
+
`;
|
|
20
|
+
var LOOK_FIELDS = `
|
|
21
|
+
id title caption authorId occasion visibility tags isFeatured likeCount
|
|
22
|
+
saveCount collaborationId publishedAt workspaceId createdAt updatedAt
|
|
23
|
+
image { id } items { ${LOOK_ITEM_FIELDS} }
|
|
24
|
+
`;
|
|
25
|
+
var LOOKBOOK_ENTRY_FIELDS = `
|
|
26
|
+
id lookbookId lookId styleItemId position note workspaceId createdAt updatedAt
|
|
27
|
+
`;
|
|
28
|
+
var LOOKBOOK_FIELDS = `
|
|
29
|
+
id name description ownerId profileId visibility tags workspaceId
|
|
30
|
+
createdAt updatedAt cover { id } entries { ${LOOKBOOK_ENTRY_FIELDS} }
|
|
31
|
+
`;
|
|
32
|
+
var COLLABORATION_FIELDS = `
|
|
33
|
+
id title brief brandId creatorId collectionId role status compensation currency
|
|
34
|
+
tags startDate endDate publishedAt workspaceId createdAt updatedAt
|
|
35
|
+
`;
|
|
36
|
+
var WARDROBE_ITEM_FIELDS = `
|
|
37
|
+
id name brandName category colour size source notes profileId styleItemId tags
|
|
38
|
+
timesWorn lastWornAt workspaceId createdAt updatedAt image { id }
|
|
39
|
+
`;
|
|
40
|
+
var STYLE_PROFILE_FIELDS = `
|
|
41
|
+
id ownerId displayName bio palette stylePreferences favouriteBrands sizes
|
|
42
|
+
visibility workspaceId createdAt updatedAt
|
|
43
|
+
`;
|
|
44
|
+
var GET_BRAND = `
|
|
45
|
+
query CoolandlovelyGetBrand($id: ID!) {
|
|
46
|
+
cooBrand(id: $id) {
|
|
47
|
+
${BRAND_FIELDS}
|
|
48
|
+
collections { id name status season }
|
|
49
|
+
items { id name status price currency }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
var LIST_BRANDS = `
|
|
54
|
+
query CoolandlovelyListBrands(
|
|
55
|
+
$featured: Boolean
|
|
56
|
+
$status: CooBrandStatus
|
|
57
|
+
$pagination: PaginationInput
|
|
58
|
+
) {
|
|
59
|
+
cooListBrands(featured: $featured, status: $status, pagination: $pagination) {
|
|
60
|
+
items { ${BRAND_FIELDS} }
|
|
61
|
+
pageInfo { cursor hasMore }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
var CREATE_BRAND = `
|
|
66
|
+
mutation CoolandlovelyCreateBrand($input: CooCreateBrandInput!) {
|
|
67
|
+
cooCreateBrand(input: $input) {
|
|
68
|
+
${BRAND_FIELDS}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
var UPDATE_BRAND = `
|
|
73
|
+
mutation CoolandlovelyUpdateBrand($id: ID!, $input: CooUpdateBrandInput!) {
|
|
74
|
+
cooUpdateBrand(id: $id, input: $input) {
|
|
75
|
+
${BRAND_FIELDS}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
var TRANSITION_BRAND = `
|
|
80
|
+
mutation CoolandlovelyTransitionBrand($id: ID!, $status: CooBrandStatus!) {
|
|
81
|
+
cooTransitionBrand(id: $id, status: $status) {
|
|
82
|
+
${BRAND_FIELDS}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
var ARCHIVE_BRAND = `
|
|
87
|
+
mutation CoolandlovelyArchiveBrand($id: ID!) {
|
|
88
|
+
cooArchiveBrand(id: $id)
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
var GET_COLLECTION = `
|
|
92
|
+
query CoolandlovelyGetCollection($id: ID!) {
|
|
93
|
+
cooCollection(id: $id) {
|
|
94
|
+
${COLLECTION_FIELDS}
|
|
95
|
+
items { id name status price currency }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
`;
|
|
99
|
+
var LIST_COLLECTIONS = `
|
|
100
|
+
query CoolandlovelyListCollections($brandId: ID, $status: CooCollectionStatus) {
|
|
101
|
+
cooListCollections(brandId: $brandId, status: $status) {
|
|
102
|
+
${COLLECTION_FIELDS}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
var CREATE_COLLECTION = `
|
|
107
|
+
mutation CoolandlovelyCreateCollection($input: CooCreateCollectionInput!) {
|
|
108
|
+
cooCreateCollection(input: $input) {
|
|
109
|
+
${COLLECTION_FIELDS}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
`;
|
|
113
|
+
var UPDATE_COLLECTION = `
|
|
114
|
+
mutation CoolandlovelyUpdateCollection($id: ID!, $input: CooUpdateCollectionInput!) {
|
|
115
|
+
cooUpdateCollection(id: $id, input: $input) {
|
|
116
|
+
${COLLECTION_FIELDS}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
var PUBLISH_COLLECTION = `
|
|
121
|
+
mutation CoolandlovelyPublishCollection($id: ID!) {
|
|
122
|
+
cooPublishCollection(id: $id) {
|
|
123
|
+
${COLLECTION_FIELDS}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
var GET_STYLE_ITEM = `
|
|
128
|
+
query CoolandlovelyGetStyleItem($id: ID!) {
|
|
129
|
+
cooStyleItem(id: $id) {
|
|
130
|
+
${STYLE_ITEM_FIELDS}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
var LIST_STYLE_ITEMS = `
|
|
135
|
+
query CoolandlovelyListStyleItems(
|
|
136
|
+
$brandId: ID
|
|
137
|
+
$category: CooStyleCategory
|
|
138
|
+
$collectionId: ID
|
|
139
|
+
$status: CooStyleItemStatus
|
|
140
|
+
$pagination: PaginationInput
|
|
141
|
+
) {
|
|
142
|
+
cooListStyleItems(
|
|
143
|
+
brandId: $brandId
|
|
144
|
+
category: $category
|
|
145
|
+
collectionId: $collectionId
|
|
146
|
+
status: $status
|
|
147
|
+
pagination: $pagination
|
|
148
|
+
) {
|
|
149
|
+
items { ${STYLE_ITEM_FIELDS} }
|
|
150
|
+
pageInfo { cursor hasMore }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
154
|
+
var CREATE_STYLE_ITEM = `
|
|
155
|
+
mutation CoolandlovelyCreateStyleItem($input: CooCreateStyleItemInput!) {
|
|
156
|
+
cooCreateStyleItem(input: $input) {
|
|
157
|
+
${STYLE_ITEM_FIELDS}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
var UPDATE_STYLE_ITEM = `
|
|
162
|
+
mutation CoolandlovelyUpdateStyleItem($id: ID!, $input: CooUpdateStyleItemInput!) {
|
|
163
|
+
cooUpdateStyleItem(id: $id, input: $input) {
|
|
164
|
+
${STYLE_ITEM_FIELDS}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
`;
|
|
168
|
+
var ARCHIVE_STYLE_ITEM = `
|
|
169
|
+
mutation CoolandlovelyArchiveStyleItem($id: ID!) {
|
|
170
|
+
cooArchiveStyleItem(id: $id)
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
173
|
+
var GET_LOOK = `
|
|
174
|
+
query CoolandlovelyGetLook($id: ID!) {
|
|
175
|
+
cooLook(id: $id) {
|
|
176
|
+
${LOOK_FIELDS}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
180
|
+
var LIST_LOOKS = `
|
|
181
|
+
query CoolandlovelyListLooks(
|
|
182
|
+
$authorId: ID
|
|
183
|
+
$featured: Boolean
|
|
184
|
+
$occasion: CooLookOccasion
|
|
185
|
+
$pagination: PaginationInput
|
|
186
|
+
) {
|
|
187
|
+
cooListLooks(
|
|
188
|
+
authorId: $authorId
|
|
189
|
+
featured: $featured
|
|
190
|
+
occasion: $occasion
|
|
191
|
+
pagination: $pagination
|
|
192
|
+
) {
|
|
193
|
+
items { ${LOOK_FIELDS} }
|
|
194
|
+
pageInfo { cursor hasMore }
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
var CREATE_LOOK = `
|
|
199
|
+
mutation CoolandlovelyCreateLook($input: CooCreateLookInput!) {
|
|
200
|
+
cooCreateLook(input: $input) {
|
|
201
|
+
${LOOK_FIELDS}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
var UPDATE_LOOK = `
|
|
206
|
+
mutation CoolandlovelyUpdateLook($id: ID!, $input: CooUpdateLookInput!) {
|
|
207
|
+
cooUpdateLook(id: $id, input: $input) {
|
|
208
|
+
${LOOK_FIELDS}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
`;
|
|
212
|
+
var PUBLISH_LOOK = `
|
|
213
|
+
mutation CoolandlovelyPublishLook($id: ID!) {
|
|
214
|
+
cooPublishLook(id: $id) {
|
|
215
|
+
${LOOK_FIELDS}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
`;
|
|
219
|
+
var LIKE_LOOK = `
|
|
220
|
+
mutation CoolandlovelyLikeLook($id: ID!) {
|
|
221
|
+
cooLikeLook(id: $id) {
|
|
222
|
+
${LOOK_FIELDS}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
`;
|
|
226
|
+
var DELETE_LOOK = `
|
|
227
|
+
mutation CoolandlovelyDeleteLook($id: ID!) {
|
|
228
|
+
cooDeleteLook(id: $id)
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
var GET_LOOKBOOK = `
|
|
232
|
+
query CoolandlovelyGetLookbook($id: ID!) {
|
|
233
|
+
cooLookbook(id: $id) {
|
|
234
|
+
${LOOKBOOK_FIELDS}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
`;
|
|
238
|
+
var LIST_LOOKBOOKS = `
|
|
239
|
+
query CoolandlovelyListLookbooks($ownerId: ID) {
|
|
240
|
+
cooListLookbooks(ownerId: $ownerId) {
|
|
241
|
+
${LOOKBOOK_FIELDS}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
`;
|
|
245
|
+
var CREATE_LOOKBOOK = `
|
|
246
|
+
mutation CoolandlovelyCreateLookbook($input: CooCreateLookbookInput!) {
|
|
247
|
+
cooCreateLookbook(input: $input) {
|
|
248
|
+
${LOOKBOOK_FIELDS}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
`;
|
|
252
|
+
var ADD_LOOKBOOK_ENTRY = `
|
|
253
|
+
mutation CoolandlovelyAddLookbookEntry($input: CooAddLookbookEntryInput!) {
|
|
254
|
+
cooAddLookbookEntry(input: $input) {
|
|
255
|
+
${LOOKBOOK_ENTRY_FIELDS}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
`;
|
|
259
|
+
var REMOVE_LOOKBOOK_ENTRY = `
|
|
260
|
+
mutation CoolandlovelyRemoveLookbookEntry($id: ID!) {
|
|
261
|
+
cooRemoveLookbookEntry(id: $id)
|
|
262
|
+
}
|
|
263
|
+
`;
|
|
264
|
+
var GET_COLLABORATION = `
|
|
265
|
+
query CoolandlovelyGetCollaboration($id: ID!) {
|
|
266
|
+
cooCollaboration(id: $id) {
|
|
267
|
+
${COLLABORATION_FIELDS}
|
|
268
|
+
brand { id name handle status }
|
|
269
|
+
looks { id title visibility }
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
`;
|
|
273
|
+
var LIST_COLLABORATIONS = `
|
|
274
|
+
query CoolandlovelyListCollaborations(
|
|
275
|
+
$brandId: ID
|
|
276
|
+
$creatorId: ID
|
|
277
|
+
$status: CooCollaborationStatus
|
|
278
|
+
) {
|
|
279
|
+
cooListCollaborations(brandId: $brandId, creatorId: $creatorId, status: $status) {
|
|
280
|
+
${COLLABORATION_FIELDS}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
`;
|
|
284
|
+
var CREATE_COLLABORATION = `
|
|
285
|
+
mutation CoolandlovelyCreateCollaboration($input: CooCreateCollaborationInput!) {
|
|
286
|
+
cooCreateCollaboration(input: $input) {
|
|
287
|
+
${COLLABORATION_FIELDS}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
`;
|
|
291
|
+
var TRANSITION_COLLABORATION = `
|
|
292
|
+
mutation CoolandlovelyTransitionCollaboration(
|
|
293
|
+
$id: ID!
|
|
294
|
+
$status: CooCollaborationStatus!
|
|
295
|
+
) {
|
|
296
|
+
cooTransitionCollaboration(id: $id, status: $status) {
|
|
297
|
+
${COLLABORATION_FIELDS}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
`;
|
|
301
|
+
var LIST_WARDROBE_ITEMS = `
|
|
302
|
+
query CoolandlovelyListWardrobeItems(
|
|
303
|
+
$category: CooStyleCategory
|
|
304
|
+
$source: CooWardrobeSource
|
|
305
|
+
) {
|
|
306
|
+
cooListWardrobeItems(category: $category, source: $source) {
|
|
307
|
+
${WARDROBE_ITEM_FIELDS}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
`;
|
|
311
|
+
var CREATE_WARDROBE_ITEM = `
|
|
312
|
+
mutation CoolandlovelyCreateWardrobeItem($input: CooCreateWardrobeItemInput!) {
|
|
313
|
+
cooCreateWardrobeItem(input: $input) {
|
|
314
|
+
${WARDROBE_ITEM_FIELDS}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
`;
|
|
318
|
+
var UPDATE_WARDROBE_ITEM = `
|
|
319
|
+
mutation CoolandlovelyUpdateWardrobeItem(
|
|
320
|
+
$id: ID!
|
|
321
|
+
$input: CooUpdateWardrobeItemInput!
|
|
322
|
+
) {
|
|
323
|
+
cooUpdateWardrobeItem(id: $id, input: $input) {
|
|
324
|
+
${WARDROBE_ITEM_FIELDS}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
`;
|
|
328
|
+
var LOG_WARDROBE_WEAR = `
|
|
329
|
+
mutation CoolandlovelyLogWardrobeWear($id: ID!) {
|
|
330
|
+
cooLogWardrobeWear(id: $id) {
|
|
331
|
+
${WARDROBE_ITEM_FIELDS}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
`;
|
|
335
|
+
var REMOVE_WARDROBE_ITEM = `
|
|
336
|
+
mutation CoolandlovelyRemoveWardrobeItem($id: ID!) {
|
|
337
|
+
cooRemoveWardrobeItem(id: $id)
|
|
338
|
+
}
|
|
339
|
+
`;
|
|
340
|
+
var MY_STYLE_PROFILE = `
|
|
341
|
+
query CoolandlovelyMyStyleProfile {
|
|
342
|
+
cooMyStyleProfile {
|
|
343
|
+
${STYLE_PROFILE_FIELDS}
|
|
344
|
+
lookbooks { id name visibility }
|
|
345
|
+
wardrobeItems { id name category }
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
`;
|
|
349
|
+
var UPSERT_STYLE_PROFILE = `
|
|
350
|
+
mutation CoolandlovelyUpsertStyleProfile($input: CooUpsertStyleProfileInput!) {
|
|
351
|
+
cooUpsertStyleProfile(input: $input) {
|
|
352
|
+
${STYLE_PROFILE_FIELDS}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
`;
|
|
356
|
+
var HEALTH = `
|
|
357
|
+
query CoolandlovelyHealth {
|
|
358
|
+
cooHealth
|
|
359
|
+
}
|
|
360
|
+
`;
|
|
361
|
+
var BrandStatusEnum = z.enum(["ARCHIVED", "DRAFT", "PENDING_REVIEW", "PUBLISHED", "SUSPENDED"]).describe("Lifecycle/moderation status of a brand storefront");
|
|
362
|
+
var CollaborationStatusEnum = z.enum(["ACCEPTED", "CANCELLED", "DECLINED", "IN_PROGRESS", "PROPOSED", "PUBLISHED"]).describe("Lifecycle status of a brand\u2194creator collaboration");
|
|
363
|
+
var CollaboratorRoleEnum = z.enum(["BRAND", "CREATOR", "PHOTOGRAPHER", "STYLIST"]).describe("Role a participant plays in a collaboration");
|
|
364
|
+
var CollectionStatusEnum = z.enum(["ARCHIVED", "DRAFT", "ENDED", "LIVE", "SCHEDULED"]).describe("Lifecycle status of a seasonal collection");
|
|
365
|
+
var LookOccasionEnum = z.enum(["ACTIVE", "EVENING", "EVERYDAY", "OTHER", "PARTY", "VACATION", "WEDDING_GUEST", "WORK"]).describe("The occasion a styled look is intended for");
|
|
366
|
+
var SeasonEnum = z.enum(["ALL_SEASON", "AUTUMN", "RESORT", "SPRING", "SUMMER", "WINTER"]).describe("Fashion season of a collection");
|
|
367
|
+
var StyleCategoryEnum = z.enum([
|
|
368
|
+
"ACCESSORIES",
|
|
369
|
+
"BAGS",
|
|
370
|
+
"BEAUTY",
|
|
371
|
+
"BOTTOMS",
|
|
372
|
+
"DRESSES",
|
|
373
|
+
"FOOTWEAR",
|
|
374
|
+
"JEWELLERY",
|
|
375
|
+
"OTHER",
|
|
376
|
+
"OUTERWEAR",
|
|
377
|
+
"TOPS"
|
|
378
|
+
]).describe("Top-level fashion category for a style/wardrobe item");
|
|
379
|
+
var StyleItemStatusEnum = z.enum(["ACTIVE", "DRAFT", "OUT_OF_STOCK", "RETIRED"]).describe("Availability/lifecycle status of a catalog style item");
|
|
380
|
+
var VisibilityEnum = z.enum(["FOLLOWERS", "PRIVATE", "PUBLIC"]).describe("Who can see a look, lookbook, or style profile");
|
|
381
|
+
var WardrobeSourceEnum = z.enum(["GIFTED", "OWNED", "PURCHASED_ON_PLATFORM", "WISHLIST"]).describe("How a shopper acquired a wardrobe piece");
|
|
382
|
+
var JsonObject = z.record(z.string(), z.unknown());
|
|
383
|
+
var LookItemInput = z.object({
|
|
384
|
+
styleItemId: z.string().min(1).describe("Style item placed in the look"),
|
|
385
|
+
position: z.number().int().optional().describe("Ordering position within the look"),
|
|
386
|
+
styling: z.string().optional().describe("Styling note for this piece")
|
|
387
|
+
}).describe("A style item placed within a look");
|
|
388
|
+
|
|
389
|
+
// src/modules/coolandlovely/tools/brands.ts
|
|
390
|
+
function registerBrandTools(server, ctx, track) {
|
|
391
|
+
server.registerTool(
|
|
392
|
+
track(mcpToolName(ctx.productName, "brands_get")),
|
|
393
|
+
{
|
|
394
|
+
title: "Get Brand",
|
|
395
|
+
description: "Fetch a single Coolandlovely brand storefront by ID, including its collections and catalog items.",
|
|
396
|
+
inputSchema: { id: z.string().describe("Brand ID") }
|
|
397
|
+
},
|
|
398
|
+
async ({ id }) => {
|
|
399
|
+
try {
|
|
400
|
+
const data = await ctx.gql.workspace.request(GET_BRAND, { id });
|
|
401
|
+
if (!data.cooBrand) return errorResult(`Brand ${id} not found`);
|
|
402
|
+
return jsonResult(data.cooBrand);
|
|
403
|
+
} catch (error) {
|
|
404
|
+
ctx.logger.error("coolandlovely brands_get failed", { error: String(error) });
|
|
405
|
+
return errorResult(
|
|
406
|
+
`Failed to get brand: ${error instanceof Error ? error.message : String(error)}`
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
);
|
|
411
|
+
server.registerTool(
|
|
412
|
+
track(mcpToolName(ctx.productName, "brands_list")),
|
|
413
|
+
{
|
|
414
|
+
title: "List Brands",
|
|
415
|
+
description: "List brand storefronts in the bound workspace with optional featured/status filters and pagination. Returns a connection ({ items, pageInfo }).",
|
|
416
|
+
inputSchema: {
|
|
417
|
+
featured: z.boolean().optional().describe("Only featured storefronts"),
|
|
418
|
+
status: BrandStatusEnum.optional().describe("Filter by lifecycle status"),
|
|
419
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
420
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
421
|
+
cursor: z.string().optional().describe("Opaque pagination cursor")
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
async ({ featured, status, limit, offset, cursor }) => {
|
|
425
|
+
try {
|
|
426
|
+
const pagination = { limit: limit ?? 50, offset: offset ?? 0 };
|
|
427
|
+
if (cursor !== void 0) pagination.cursor = cursor;
|
|
428
|
+
const data = await ctx.gql.workspace.request(LIST_BRANDS, { featured, status, pagination });
|
|
429
|
+
return jsonResult(data.cooListBrands);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
ctx.logger.error("coolandlovely brands_list failed", { error: String(error) });
|
|
432
|
+
return errorResult(
|
|
433
|
+
`Failed to list brands: ${error instanceof Error ? error.message : String(error)}`
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
server.registerTool(
|
|
439
|
+
track(mcpToolName(ctx.productName, "brands_create")),
|
|
440
|
+
{
|
|
441
|
+
title: "Create Brand",
|
|
442
|
+
description: "Create a new brand storefront. `handle` must be unique within the workspace. Reference logo/cover images by wspace-files-svc File IDs.",
|
|
443
|
+
inputSchema: {
|
|
444
|
+
name: z.string().min(1).describe("Brand display name"),
|
|
445
|
+
handle: z.string().min(1).describe("URL handle, unique within the workspace"),
|
|
446
|
+
tagline: z.string().optional().describe("Short brand tagline"),
|
|
447
|
+
bio: z.string().optional().describe("Longer brand bio/description"),
|
|
448
|
+
aesthetics: z.array(z.string()).optional().describe("Aesthetic keywords"),
|
|
449
|
+
tags: z.array(z.string()).optional().describe("Discovery tags"),
|
|
450
|
+
websiteUrl: z.string().optional().describe("External website URL"),
|
|
451
|
+
logoFileId: z.string().optional().describe("wspace-files-svc File ID for the logo"),
|
|
452
|
+
coverFileId: z.string().optional().describe("wspace-files-svc File ID for the cover")
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
async ({
|
|
456
|
+
name,
|
|
457
|
+
handle,
|
|
458
|
+
tagline,
|
|
459
|
+
bio,
|
|
460
|
+
aesthetics,
|
|
461
|
+
tags,
|
|
462
|
+
websiteUrl,
|
|
463
|
+
logoFileId,
|
|
464
|
+
coverFileId
|
|
465
|
+
}) => {
|
|
466
|
+
try {
|
|
467
|
+
const input = { name, handle };
|
|
468
|
+
if (tagline !== void 0) input.tagline = tagline;
|
|
469
|
+
if (bio !== void 0) input.bio = bio;
|
|
470
|
+
if (aesthetics !== void 0) input.aesthetics = aesthetics;
|
|
471
|
+
if (tags !== void 0) input.tags = tags;
|
|
472
|
+
if (websiteUrl !== void 0) input.websiteUrl = websiteUrl;
|
|
473
|
+
if (logoFileId !== void 0) input.logoFileId = logoFileId;
|
|
474
|
+
if (coverFileId !== void 0) input.coverFileId = coverFileId;
|
|
475
|
+
const data = await ctx.gql.workspace.request(CREATE_BRAND, { input });
|
|
476
|
+
return jsonResult(data.cooCreateBrand);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
ctx.logger.error("coolandlovely brands_create failed", { error: String(error) });
|
|
479
|
+
return errorResult(
|
|
480
|
+
`Failed to create brand: ${error instanceof Error ? error.message : String(error)}`
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
);
|
|
485
|
+
server.registerTool(
|
|
486
|
+
track(mcpToolName(ctx.productName, "brands_update")),
|
|
487
|
+
{
|
|
488
|
+
title: "Update Brand",
|
|
489
|
+
description: "Update a brand storefront. At least one mutable field must be provided. Use `isFeatured` to feature the storefront.",
|
|
490
|
+
inputSchema: {
|
|
491
|
+
id: z.string().describe("Brand ID"),
|
|
492
|
+
name: z.string().optional().describe("Updated display name"),
|
|
493
|
+
tagline: z.string().optional().describe("Updated tagline"),
|
|
494
|
+
bio: z.string().optional().describe("Updated bio"),
|
|
495
|
+
aesthetics: z.array(z.string()).optional().describe("Updated aesthetic keywords"),
|
|
496
|
+
tags: z.array(z.string()).optional().describe("Updated tags"),
|
|
497
|
+
websiteUrl: z.string().optional().describe("Updated website URL"),
|
|
498
|
+
isFeatured: z.boolean().optional().describe("Toggle featured status"),
|
|
499
|
+
logoFileId: z.string().optional().describe("Updated logo File ID"),
|
|
500
|
+
coverFileId: z.string().optional().describe("Updated cover File ID")
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
async ({
|
|
504
|
+
id,
|
|
505
|
+
name,
|
|
506
|
+
tagline,
|
|
507
|
+
bio,
|
|
508
|
+
aesthetics,
|
|
509
|
+
tags,
|
|
510
|
+
websiteUrl,
|
|
511
|
+
isFeatured,
|
|
512
|
+
logoFileId,
|
|
513
|
+
coverFileId
|
|
514
|
+
}) => {
|
|
515
|
+
try {
|
|
516
|
+
const input = {};
|
|
517
|
+
if (name !== void 0) input.name = name;
|
|
518
|
+
if (tagline !== void 0) input.tagline = tagline;
|
|
519
|
+
if (bio !== void 0) input.bio = bio;
|
|
520
|
+
if (aesthetics !== void 0) input.aesthetics = aesthetics;
|
|
521
|
+
if (tags !== void 0) input.tags = tags;
|
|
522
|
+
if (websiteUrl !== void 0) input.websiteUrl = websiteUrl;
|
|
523
|
+
if (isFeatured !== void 0) input.isFeatured = isFeatured;
|
|
524
|
+
if (logoFileId !== void 0) input.logoFileId = logoFileId;
|
|
525
|
+
if (coverFileId !== void 0) input.coverFileId = coverFileId;
|
|
526
|
+
if (Object.keys(input).length === 0) {
|
|
527
|
+
return errorResult("No updates specified \u2014 provide at least one mutable brand field");
|
|
528
|
+
}
|
|
529
|
+
const data = await ctx.gql.workspace.request(UPDATE_BRAND, { id, input });
|
|
530
|
+
return jsonResult(data.cooUpdateBrand);
|
|
531
|
+
} catch (error) {
|
|
532
|
+
ctx.logger.error("coolandlovely brands_update failed", { error: String(error) });
|
|
533
|
+
return errorResult(
|
|
534
|
+
`Failed to update brand: ${error instanceof Error ? error.message : String(error)}`
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
);
|
|
539
|
+
server.registerTool(
|
|
540
|
+
track(mcpToolName(ctx.productName, "brands_transition")),
|
|
541
|
+
{
|
|
542
|
+
title: "Transition Brand",
|
|
543
|
+
description: "Advance a brand storefront through its publish lifecycle with a validated status transition (DRAFT \u2192 PENDING_REVIEW \u2192 PUBLISHED, SUSPENDED, ARCHIVED).",
|
|
544
|
+
inputSchema: {
|
|
545
|
+
id: z.string().describe("Brand ID"),
|
|
546
|
+
status: BrandStatusEnum.describe("Target lifecycle status")
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
async ({ id, status }) => {
|
|
550
|
+
try {
|
|
551
|
+
const data = await ctx.gql.workspace.request(TRANSITION_BRAND, { id, status });
|
|
552
|
+
return jsonResult(data.cooTransitionBrand);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
ctx.logger.error("coolandlovely brands_transition failed", { error: String(error) });
|
|
555
|
+
return errorResult(
|
|
556
|
+
`Failed to transition brand: ${error instanceof Error ? error.message : String(error)}`
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
);
|
|
561
|
+
server.registerTool(
|
|
562
|
+
track(mcpToolName(ctx.productName, "brands_archive")),
|
|
563
|
+
{
|
|
564
|
+
title: "Archive Brand",
|
|
565
|
+
description: "Archive a brand storefront (soft delete). Returns true on success.",
|
|
566
|
+
inputSchema: { id: z.string().describe("Brand ID") }
|
|
567
|
+
},
|
|
568
|
+
async ({ id }) => {
|
|
569
|
+
try {
|
|
570
|
+
const data = await ctx.gql.workspace.request(
|
|
571
|
+
ARCHIVE_BRAND,
|
|
572
|
+
{ id }
|
|
573
|
+
);
|
|
574
|
+
return jsonResult({ archived: data.cooArchiveBrand });
|
|
575
|
+
} catch (error) {
|
|
576
|
+
ctx.logger.error("coolandlovely brands_archive failed", { error: String(error) });
|
|
577
|
+
return errorResult(
|
|
578
|
+
`Failed to archive brand: ${error instanceof Error ? error.message : String(error)}`
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
function registerCollectionTools(server, ctx, track) {
|
|
585
|
+
server.registerTool(
|
|
586
|
+
track(mcpToolName(ctx.productName, "collections_get")),
|
|
587
|
+
{
|
|
588
|
+
title: "Get Collection",
|
|
589
|
+
description: "Fetch a single seasonal collection by ID, including its style items.",
|
|
590
|
+
inputSchema: { id: z.string().describe("Collection ID") }
|
|
591
|
+
},
|
|
592
|
+
async ({ id }) => {
|
|
593
|
+
try {
|
|
594
|
+
const data = await ctx.gql.workspace.request(GET_COLLECTION, { id });
|
|
595
|
+
if (!data.cooCollection) return errorResult(`Collection ${id} not found`);
|
|
596
|
+
return jsonResult(data.cooCollection);
|
|
597
|
+
} catch (error) {
|
|
598
|
+
ctx.logger.error("coolandlovely collections_get failed", { error: String(error) });
|
|
599
|
+
return errorResult(
|
|
600
|
+
`Failed to get collection: ${error instanceof Error ? error.message : String(error)}`
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
);
|
|
605
|
+
server.registerTool(
|
|
606
|
+
track(mcpToolName(ctx.productName, "collections_list")),
|
|
607
|
+
{
|
|
608
|
+
title: "List Collections",
|
|
609
|
+
description: "List seasonal collections, optionally filtered by brand and/or status.",
|
|
610
|
+
inputSchema: {
|
|
611
|
+
brandId: z.string().optional().describe("Filter by brand ID"),
|
|
612
|
+
status: CollectionStatusEnum.optional().describe("Filter by collection status")
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
async ({ brandId, status }) => {
|
|
616
|
+
try {
|
|
617
|
+
const data = await ctx.gql.workspace.request(LIST_COLLECTIONS, { brandId, status });
|
|
618
|
+
return jsonResult(data.cooListCollections);
|
|
619
|
+
} catch (error) {
|
|
620
|
+
ctx.logger.error("coolandlovely collections_list failed", { error: String(error) });
|
|
621
|
+
return errorResult(
|
|
622
|
+
`Failed to list collections: ${error instanceof Error ? error.message : String(error)}`
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
);
|
|
627
|
+
server.registerTool(
|
|
628
|
+
track(mcpToolName(ctx.productName, "collections_create")),
|
|
629
|
+
{
|
|
630
|
+
title: "Create Collection",
|
|
631
|
+
description: "Create a seasonal collection for a brand. Set `publishAt`/`endAt` (ISO-8601) to schedule the drop.",
|
|
632
|
+
inputSchema: {
|
|
633
|
+
brandId: z.string().min(1).describe("Owning brand ID"),
|
|
634
|
+
name: z.string().min(1).describe("Collection name"),
|
|
635
|
+
description: z.string().optional().describe("Collection description"),
|
|
636
|
+
season: SeasonEnum.optional().describe("Fashion season (default ALL_SEASON)"),
|
|
637
|
+
publishAt: z.string().optional().describe("Scheduled publish time (ISO-8601 DateTime)"),
|
|
638
|
+
endAt: z.string().optional().describe("Scheduled end time (ISO-8601 DateTime)"),
|
|
639
|
+
coverFileId: z.string().optional().describe("wspace-files-svc File ID for the cover"),
|
|
640
|
+
tags: z.array(z.string()).optional().describe("Discovery tags")
|
|
641
|
+
}
|
|
642
|
+
},
|
|
643
|
+
async ({ brandId, name, description, season, publishAt, endAt, coverFileId, tags }) => {
|
|
644
|
+
try {
|
|
645
|
+
const input = { brandId, name };
|
|
646
|
+
if (description !== void 0) input.description = description;
|
|
647
|
+
if (season !== void 0) input.season = season;
|
|
648
|
+
if (publishAt !== void 0) input.publishAt = publishAt;
|
|
649
|
+
if (endAt !== void 0) input.endAt = endAt;
|
|
650
|
+
if (coverFileId !== void 0) input.coverFileId = coverFileId;
|
|
651
|
+
if (tags !== void 0) input.tags = tags;
|
|
652
|
+
const data = await ctx.gql.workspace.request(CREATE_COLLECTION, { input });
|
|
653
|
+
return jsonResult(data.cooCreateCollection);
|
|
654
|
+
} catch (error) {
|
|
655
|
+
ctx.logger.error("coolandlovely collections_create failed", { error: String(error) });
|
|
656
|
+
return errorResult(
|
|
657
|
+
`Failed to create collection: ${error instanceof Error ? error.message : String(error)}`
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
);
|
|
662
|
+
server.registerTool(
|
|
663
|
+
track(mcpToolName(ctx.productName, "collections_update")),
|
|
664
|
+
{
|
|
665
|
+
title: "Update Collection",
|
|
666
|
+
description: "Update a seasonal collection. At least one mutable field must be provided.",
|
|
667
|
+
inputSchema: {
|
|
668
|
+
id: z.string().describe("Collection ID"),
|
|
669
|
+
name: z.string().optional().describe("Updated name"),
|
|
670
|
+
description: z.string().optional().describe("Updated description"),
|
|
671
|
+
season: SeasonEnum.optional().describe("Updated season"),
|
|
672
|
+
publishAt: z.string().optional().describe("Updated publish time (ISO-8601)"),
|
|
673
|
+
endAt: z.string().optional().describe("Updated end time (ISO-8601)"),
|
|
674
|
+
coverFileId: z.string().optional().describe("Updated cover File ID"),
|
|
675
|
+
tags: z.array(z.string()).optional().describe("Updated tags")
|
|
676
|
+
}
|
|
677
|
+
},
|
|
678
|
+
async ({ id, name, description, season, publishAt, endAt, coverFileId, tags }) => {
|
|
679
|
+
try {
|
|
680
|
+
const input = {};
|
|
681
|
+
if (name !== void 0) input.name = name;
|
|
682
|
+
if (description !== void 0) input.description = description;
|
|
683
|
+
if (season !== void 0) input.season = season;
|
|
684
|
+
if (publishAt !== void 0) input.publishAt = publishAt;
|
|
685
|
+
if (endAt !== void 0) input.endAt = endAt;
|
|
686
|
+
if (coverFileId !== void 0) input.coverFileId = coverFileId;
|
|
687
|
+
if (tags !== void 0) input.tags = tags;
|
|
688
|
+
if (Object.keys(input).length === 0) {
|
|
689
|
+
return errorResult(
|
|
690
|
+
"No updates specified \u2014 provide at least one mutable collection field"
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
const data = await ctx.gql.workspace.request(UPDATE_COLLECTION, { id, input });
|
|
694
|
+
return jsonResult(data.cooUpdateCollection);
|
|
695
|
+
} catch (error) {
|
|
696
|
+
ctx.logger.error("coolandlovely collections_update failed", { error: String(error) });
|
|
697
|
+
return errorResult(
|
|
698
|
+
`Failed to update collection: ${error instanceof Error ? error.message : String(error)}`
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
);
|
|
703
|
+
server.registerTool(
|
|
704
|
+
track(mcpToolName(ctx.productName, "collections_publish")),
|
|
705
|
+
{
|
|
706
|
+
title: "Publish Collection",
|
|
707
|
+
description: "Publish a collection so it goes LIVE.",
|
|
708
|
+
inputSchema: { id: z.string().describe("Collection ID") }
|
|
709
|
+
},
|
|
710
|
+
async ({ id }) => {
|
|
711
|
+
try {
|
|
712
|
+
const data = await ctx.gql.workspace.request(PUBLISH_COLLECTION, { id });
|
|
713
|
+
return jsonResult(data.cooPublishCollection);
|
|
714
|
+
} catch (error) {
|
|
715
|
+
ctx.logger.error("coolandlovely collections_publish failed", { error: String(error) });
|
|
716
|
+
return errorResult(
|
|
717
|
+
`Failed to publish collection: ${error instanceof Error ? error.message : String(error)}`
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
function registerStyleItemTools(server, ctx, track) {
|
|
724
|
+
server.registerTool(
|
|
725
|
+
track(mcpToolName(ctx.productName, "style_items_get")),
|
|
726
|
+
{
|
|
727
|
+
title: "Get Style Item",
|
|
728
|
+
description: "Fetch a single catalog style item by ID.",
|
|
729
|
+
inputSchema: { id: z.string().describe("Style item ID") }
|
|
730
|
+
},
|
|
731
|
+
async ({ id }) => {
|
|
732
|
+
try {
|
|
733
|
+
const data = await ctx.gql.workspace.request(GET_STYLE_ITEM, { id });
|
|
734
|
+
if (!data.cooStyleItem) return errorResult(`Style item ${id} not found`);
|
|
735
|
+
return jsonResult(data.cooStyleItem);
|
|
736
|
+
} catch (error) {
|
|
737
|
+
ctx.logger.error("coolandlovely style_items_get failed", { error: String(error) });
|
|
738
|
+
return errorResult(
|
|
739
|
+
`Failed to get style item: ${error instanceof Error ? error.message : String(error)}`
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
);
|
|
744
|
+
server.registerTool(
|
|
745
|
+
track(mcpToolName(ctx.productName, "style_items_list")),
|
|
746
|
+
{
|
|
747
|
+
title: "List Style Items",
|
|
748
|
+
description: "List catalog style items with optional brand/category/collection/status filters and pagination. Returns a connection ({ items, pageInfo }).",
|
|
749
|
+
inputSchema: {
|
|
750
|
+
brandId: z.string().optional().describe("Filter by brand ID"),
|
|
751
|
+
category: StyleCategoryEnum.optional().describe("Filter by fashion category"),
|
|
752
|
+
collectionId: z.string().optional().describe("Filter by collection ID"),
|
|
753
|
+
status: StyleItemStatusEnum.optional().describe("Filter by item status"),
|
|
754
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
755
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
756
|
+
cursor: z.string().optional().describe("Opaque pagination cursor")
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
async ({ brandId, category, collectionId, status, limit, offset, cursor }) => {
|
|
760
|
+
try {
|
|
761
|
+
const pagination = { limit: limit ?? 50, offset: offset ?? 0 };
|
|
762
|
+
if (cursor !== void 0) pagination.cursor = cursor;
|
|
763
|
+
const data = await ctx.gql.workspace.request(LIST_STYLE_ITEMS, { brandId, category, collectionId, status, pagination });
|
|
764
|
+
return jsonResult(data.cooListStyleItems);
|
|
765
|
+
} catch (error) {
|
|
766
|
+
ctx.logger.error("coolandlovely style_items_list failed", { error: String(error) });
|
|
767
|
+
return errorResult(
|
|
768
|
+
`Failed to list style items: ${error instanceof Error ? error.message : String(error)}`
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
);
|
|
773
|
+
server.registerTool(
|
|
774
|
+
track(mcpToolName(ctx.productName, "style_items_create")),
|
|
775
|
+
{
|
|
776
|
+
title: "Create Style Item",
|
|
777
|
+
description: "Create a catalog style item for a brand. Optionally attach it to a collection and set price/currency, materials, colours, and sizes.",
|
|
778
|
+
inputSchema: {
|
|
779
|
+
brandId: z.string().min(1).describe("Owning brand ID"),
|
|
780
|
+
name: z.string().min(1).describe("Item display name"),
|
|
781
|
+
category: StyleCategoryEnum.optional().describe("Fashion category (default OTHER)"),
|
|
782
|
+
collectionId: z.string().optional().describe("Collection to attach the item to"),
|
|
783
|
+
description: z.string().optional().describe("Item description"),
|
|
784
|
+
sku: z.string().optional().describe("Stock-keeping unit"),
|
|
785
|
+
price: z.number().optional().describe("Price"),
|
|
786
|
+
currency: z.string().optional().describe('ISO currency code (e.g. "USD")'),
|
|
787
|
+
colours: z.array(z.string()).optional().describe("Available colours"),
|
|
788
|
+
materials: z.array(z.string()).optional().describe("Materials/fabric composition"),
|
|
789
|
+
sizes: z.array(z.string()).optional().describe("Available sizes"),
|
|
790
|
+
imageFileId: z.string().optional().describe("wspace-files-svc File ID for the item image"),
|
|
791
|
+
tags: z.array(z.string()).optional().describe("Discovery tags")
|
|
792
|
+
}
|
|
793
|
+
},
|
|
794
|
+
async ({
|
|
795
|
+
brandId,
|
|
796
|
+
name,
|
|
797
|
+
category,
|
|
798
|
+
collectionId,
|
|
799
|
+
description,
|
|
800
|
+
sku,
|
|
801
|
+
price,
|
|
802
|
+
currency,
|
|
803
|
+
colours,
|
|
804
|
+
materials,
|
|
805
|
+
sizes,
|
|
806
|
+
imageFileId,
|
|
807
|
+
tags
|
|
808
|
+
}) => {
|
|
809
|
+
try {
|
|
810
|
+
const input = { brandId, name };
|
|
811
|
+
if (category !== void 0) input.category = category;
|
|
812
|
+
if (collectionId !== void 0) input.collectionId = collectionId;
|
|
813
|
+
if (description !== void 0) input.description = description;
|
|
814
|
+
if (sku !== void 0) input.sku = sku;
|
|
815
|
+
if (price !== void 0) input.price = price;
|
|
816
|
+
if (currency !== void 0) input.currency = currency;
|
|
817
|
+
if (colours !== void 0) input.colours = colours;
|
|
818
|
+
if (materials !== void 0) input.materials = materials;
|
|
819
|
+
if (sizes !== void 0) input.sizes = sizes;
|
|
820
|
+
if (imageFileId !== void 0) input.imageFileId = imageFileId;
|
|
821
|
+
if (tags !== void 0) input.tags = tags;
|
|
822
|
+
const data = await ctx.gql.workspace.request(CREATE_STYLE_ITEM, { input });
|
|
823
|
+
return jsonResult(data.cooCreateStyleItem);
|
|
824
|
+
} catch (error) {
|
|
825
|
+
ctx.logger.error("coolandlovely style_items_create failed", { error: String(error) });
|
|
826
|
+
return errorResult(
|
|
827
|
+
`Failed to create style item: ${error instanceof Error ? error.message : String(error)}`
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
);
|
|
832
|
+
server.registerTool(
|
|
833
|
+
track(mcpToolName(ctx.productName, "style_items_update")),
|
|
834
|
+
{
|
|
835
|
+
title: "Update Style Item",
|
|
836
|
+
description: "Update a catalog style item. At least one mutable field must be provided.",
|
|
837
|
+
inputSchema: {
|
|
838
|
+
id: z.string().describe("Style item ID"),
|
|
839
|
+
name: z.string().optional().describe("Updated name"),
|
|
840
|
+
category: StyleCategoryEnum.optional().describe("Updated category"),
|
|
841
|
+
collectionId: z.string().optional().describe("Updated collection ID"),
|
|
842
|
+
description: z.string().optional().describe("Updated description"),
|
|
843
|
+
sku: z.string().optional().describe("Updated SKU"),
|
|
844
|
+
price: z.number().optional().describe("Updated price"),
|
|
845
|
+
currency: z.string().optional().describe("Updated currency code"),
|
|
846
|
+
colours: z.array(z.string()).optional().describe("Updated colours"),
|
|
847
|
+
materials: z.array(z.string()).optional().describe("Updated materials"),
|
|
848
|
+
sizes: z.array(z.string()).optional().describe("Updated sizes"),
|
|
849
|
+
imageFileId: z.string().optional().describe("Updated image File ID"),
|
|
850
|
+
tags: z.array(z.string()).optional().describe("Updated tags")
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
async ({
|
|
854
|
+
id,
|
|
855
|
+
name,
|
|
856
|
+
category,
|
|
857
|
+
collectionId,
|
|
858
|
+
description,
|
|
859
|
+
sku,
|
|
860
|
+
price,
|
|
861
|
+
currency,
|
|
862
|
+
colours,
|
|
863
|
+
materials,
|
|
864
|
+
sizes,
|
|
865
|
+
imageFileId,
|
|
866
|
+
tags
|
|
867
|
+
}) => {
|
|
868
|
+
try {
|
|
869
|
+
const input = {};
|
|
870
|
+
if (name !== void 0) input.name = name;
|
|
871
|
+
if (category !== void 0) input.category = category;
|
|
872
|
+
if (collectionId !== void 0) input.collectionId = collectionId;
|
|
873
|
+
if (description !== void 0) input.description = description;
|
|
874
|
+
if (sku !== void 0) input.sku = sku;
|
|
875
|
+
if (price !== void 0) input.price = price;
|
|
876
|
+
if (currency !== void 0) input.currency = currency;
|
|
877
|
+
if (colours !== void 0) input.colours = colours;
|
|
878
|
+
if (materials !== void 0) input.materials = materials;
|
|
879
|
+
if (sizes !== void 0) input.sizes = sizes;
|
|
880
|
+
if (imageFileId !== void 0) input.imageFileId = imageFileId;
|
|
881
|
+
if (tags !== void 0) input.tags = tags;
|
|
882
|
+
if (Object.keys(input).length === 0) {
|
|
883
|
+
return errorResult(
|
|
884
|
+
"No updates specified \u2014 provide at least one mutable style-item field"
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
const data = await ctx.gql.workspace.request(UPDATE_STYLE_ITEM, { id, input });
|
|
888
|
+
return jsonResult(data.cooUpdateStyleItem);
|
|
889
|
+
} catch (error) {
|
|
890
|
+
ctx.logger.error("coolandlovely style_items_update failed", { error: String(error) });
|
|
891
|
+
return errorResult(
|
|
892
|
+
`Failed to update style item: ${error instanceof Error ? error.message : String(error)}`
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
);
|
|
897
|
+
server.registerTool(
|
|
898
|
+
track(mcpToolName(ctx.productName, "style_items_archive")),
|
|
899
|
+
{
|
|
900
|
+
title: "Archive Style Item",
|
|
901
|
+
description: "Archive (retire) a catalog style item. Returns true on success.",
|
|
902
|
+
inputSchema: { id: z.string().describe("Style item ID") }
|
|
903
|
+
},
|
|
904
|
+
async ({ id }) => {
|
|
905
|
+
try {
|
|
906
|
+
const data = await ctx.gql.workspace.request(
|
|
907
|
+
ARCHIVE_STYLE_ITEM,
|
|
908
|
+
{ id }
|
|
909
|
+
);
|
|
910
|
+
return jsonResult({ archived: data.cooArchiveStyleItem });
|
|
911
|
+
} catch (error) {
|
|
912
|
+
ctx.logger.error("coolandlovely style_items_archive failed", { error: String(error) });
|
|
913
|
+
return errorResult(
|
|
914
|
+
`Failed to archive style item: ${error instanceof Error ? error.message : String(error)}`
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
function registerLookTools(server, ctx, track) {
|
|
921
|
+
server.registerTool(
|
|
922
|
+
track(mcpToolName(ctx.productName, "looks_get")),
|
|
923
|
+
{
|
|
924
|
+
title: "Get Look",
|
|
925
|
+
description: "Fetch a single styled look by ID, including its line items.",
|
|
926
|
+
inputSchema: { id: z.string().describe("Look ID") }
|
|
927
|
+
},
|
|
928
|
+
async ({ id }) => {
|
|
929
|
+
try {
|
|
930
|
+
const data = await ctx.gql.workspace.request(GET_LOOK, { id });
|
|
931
|
+
if (!data.cooLook) return errorResult(`Look ${id} not found`);
|
|
932
|
+
return jsonResult(data.cooLook);
|
|
933
|
+
} catch (error) {
|
|
934
|
+
ctx.logger.error("coolandlovely looks_get failed", { error: String(error) });
|
|
935
|
+
return errorResult(
|
|
936
|
+
`Failed to get look: ${error instanceof Error ? error.message : String(error)}`
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
);
|
|
941
|
+
server.registerTool(
|
|
942
|
+
track(mcpToolName(ctx.productName, "looks_list")),
|
|
943
|
+
{
|
|
944
|
+
title: "List Looks",
|
|
945
|
+
description: "List styled looks with optional author/featured/occasion filters and pagination. Returns a connection ({ items, pageInfo }).",
|
|
946
|
+
inputSchema: {
|
|
947
|
+
authorId: z.string().optional().describe("Filter by author user ID"),
|
|
948
|
+
featured: z.boolean().optional().describe("Only featured looks"),
|
|
949
|
+
occasion: LookOccasionEnum.optional().describe("Filter by occasion"),
|
|
950
|
+
limit: z.number().int().optional().describe("Page size (default 50)"),
|
|
951
|
+
offset: z.number().int().optional().describe("Pagination offset (default 0)"),
|
|
952
|
+
cursor: z.string().optional().describe("Opaque pagination cursor")
|
|
953
|
+
}
|
|
954
|
+
},
|
|
955
|
+
async ({ authorId, featured, occasion, limit, offset, cursor }) => {
|
|
956
|
+
try {
|
|
957
|
+
const pagination = { limit: limit ?? 50, offset: offset ?? 0 };
|
|
958
|
+
if (cursor !== void 0) pagination.cursor = cursor;
|
|
959
|
+
const data = await ctx.gql.workspace.request(LIST_LOOKS, { authorId, featured, occasion, pagination });
|
|
960
|
+
return jsonResult(data.cooListLooks);
|
|
961
|
+
} catch (error) {
|
|
962
|
+
ctx.logger.error("coolandlovely looks_list failed", { error: String(error) });
|
|
963
|
+
return errorResult(
|
|
964
|
+
`Failed to list looks: ${error instanceof Error ? error.message : String(error)}`
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
);
|
|
969
|
+
server.registerTool(
|
|
970
|
+
track(mcpToolName(ctx.productName, "looks_create")),
|
|
971
|
+
{
|
|
972
|
+
title: "Create Look",
|
|
973
|
+
description: "Create a styled look. Attach style items via `items` (each references a styleItemId with optional position/styling).",
|
|
974
|
+
inputSchema: {
|
|
975
|
+
title: z.string().min(1).describe("Look title"),
|
|
976
|
+
caption: z.string().optional().describe("Look caption/description"),
|
|
977
|
+
occasion: LookOccasionEnum.optional().describe("Occasion (default EVERYDAY)"),
|
|
978
|
+
visibility: VisibilityEnum.optional().describe("Visibility (default PUBLIC)"),
|
|
979
|
+
collaborationId: z.string().optional().describe("Related collaboration ID"),
|
|
980
|
+
imageFileId: z.string().optional().describe("wspace-files-svc File ID for the look photo"),
|
|
981
|
+
items: z.array(LookItemInput).optional().describe("Style items placed in the look"),
|
|
982
|
+
tags: z.array(z.string()).optional().describe("Discovery tags")
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
async ({ title, caption, occasion, visibility, collaborationId, imageFileId, items, tags }) => {
|
|
986
|
+
try {
|
|
987
|
+
const input = { title };
|
|
988
|
+
if (caption !== void 0) input.caption = caption;
|
|
989
|
+
if (occasion !== void 0) input.occasion = occasion;
|
|
990
|
+
if (visibility !== void 0) input.visibility = visibility;
|
|
991
|
+
if (collaborationId !== void 0) input.collaborationId = collaborationId;
|
|
992
|
+
if (imageFileId !== void 0) input.imageFileId = imageFileId;
|
|
993
|
+
if (items !== void 0) input.items = items;
|
|
994
|
+
if (tags !== void 0) input.tags = tags;
|
|
995
|
+
const data = await ctx.gql.workspace.request(CREATE_LOOK, { input });
|
|
996
|
+
return jsonResult(data.cooCreateLook);
|
|
997
|
+
} catch (error) {
|
|
998
|
+
ctx.logger.error("coolandlovely looks_create failed", { error: String(error) });
|
|
999
|
+
return errorResult(
|
|
1000
|
+
`Failed to create look: ${error instanceof Error ? error.message : String(error)}`
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
);
|
|
1005
|
+
server.registerTool(
|
|
1006
|
+
track(mcpToolName(ctx.productName, "looks_update")),
|
|
1007
|
+
{
|
|
1008
|
+
title: "Update Look",
|
|
1009
|
+
description: "Update a styled look. At least one mutable field must be provided.",
|
|
1010
|
+
inputSchema: {
|
|
1011
|
+
id: z.string().describe("Look ID"),
|
|
1012
|
+
title: z.string().optional().describe("Updated title"),
|
|
1013
|
+
caption: z.string().optional().describe("Updated caption"),
|
|
1014
|
+
occasion: LookOccasionEnum.optional().describe("Updated occasion"),
|
|
1015
|
+
visibility: VisibilityEnum.optional().describe("Updated visibility"),
|
|
1016
|
+
imageFileId: z.string().optional().describe("Updated look photo File ID"),
|
|
1017
|
+
tags: z.array(z.string()).optional().describe("Updated tags")
|
|
1018
|
+
}
|
|
1019
|
+
},
|
|
1020
|
+
async ({ id, title, caption, occasion, visibility, imageFileId, tags }) => {
|
|
1021
|
+
try {
|
|
1022
|
+
const input = {};
|
|
1023
|
+
if (title !== void 0) input.title = title;
|
|
1024
|
+
if (caption !== void 0) input.caption = caption;
|
|
1025
|
+
if (occasion !== void 0) input.occasion = occasion;
|
|
1026
|
+
if (visibility !== void 0) input.visibility = visibility;
|
|
1027
|
+
if (imageFileId !== void 0) input.imageFileId = imageFileId;
|
|
1028
|
+
if (tags !== void 0) input.tags = tags;
|
|
1029
|
+
if (Object.keys(input).length === 0) {
|
|
1030
|
+
return errorResult("No updates specified \u2014 provide at least one mutable look field");
|
|
1031
|
+
}
|
|
1032
|
+
const data = await ctx.gql.workspace.request(UPDATE_LOOK, { id, input });
|
|
1033
|
+
return jsonResult(data.cooUpdateLook);
|
|
1034
|
+
} catch (error) {
|
|
1035
|
+
ctx.logger.error("coolandlovely looks_update failed", { error: String(error) });
|
|
1036
|
+
return errorResult(
|
|
1037
|
+
`Failed to update look: ${error instanceof Error ? error.message : String(error)}`
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
);
|
|
1042
|
+
server.registerTool(
|
|
1043
|
+
track(mcpToolName(ctx.productName, "looks_publish")),
|
|
1044
|
+
{
|
|
1045
|
+
title: "Publish Look",
|
|
1046
|
+
description: "Publish a look so it becomes visible per its visibility setting.",
|
|
1047
|
+
inputSchema: { id: z.string().describe("Look ID") }
|
|
1048
|
+
},
|
|
1049
|
+
async ({ id }) => {
|
|
1050
|
+
try {
|
|
1051
|
+
const data = await ctx.gql.workspace.request(PUBLISH_LOOK, { id });
|
|
1052
|
+
return jsonResult(data.cooPublishLook);
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
ctx.logger.error("coolandlovely looks_publish failed", { error: String(error) });
|
|
1055
|
+
return errorResult(
|
|
1056
|
+
`Failed to publish look: ${error instanceof Error ? error.message : String(error)}`
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
);
|
|
1061
|
+
server.registerTool(
|
|
1062
|
+
track(mcpToolName(ctx.productName, "looks_like")),
|
|
1063
|
+
{
|
|
1064
|
+
title: "Like Look",
|
|
1065
|
+
description: "Like a published look (increments its likeCount).",
|
|
1066
|
+
inputSchema: { id: z.string().describe("Look ID") }
|
|
1067
|
+
},
|
|
1068
|
+
async ({ id }) => {
|
|
1069
|
+
try {
|
|
1070
|
+
const data = await ctx.gql.workspace.request(LIKE_LOOK, { id });
|
|
1071
|
+
return jsonResult(data.cooLikeLook);
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
ctx.logger.error("coolandlovely looks_like failed", { error: String(error) });
|
|
1074
|
+
return errorResult(
|
|
1075
|
+
`Failed to like look: ${error instanceof Error ? error.message : String(error)}`
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
);
|
|
1080
|
+
server.registerTool(
|
|
1081
|
+
track(mcpToolName(ctx.productName, "looks_delete")),
|
|
1082
|
+
{
|
|
1083
|
+
title: "Delete Look",
|
|
1084
|
+
description: "Delete a styled look. Returns true on success.",
|
|
1085
|
+
inputSchema: { id: z.string().describe("Look ID") }
|
|
1086
|
+
},
|
|
1087
|
+
async ({ id }) => {
|
|
1088
|
+
try {
|
|
1089
|
+
const data = await ctx.gql.workspace.request(DELETE_LOOK, {
|
|
1090
|
+
id
|
|
1091
|
+
});
|
|
1092
|
+
return jsonResult({ deleted: data.cooDeleteLook });
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
ctx.logger.error("coolandlovely looks_delete failed", { error: String(error) });
|
|
1095
|
+
return errorResult(
|
|
1096
|
+
`Failed to delete look: ${error instanceof Error ? error.message : String(error)}`
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
function registerLookbookTools(server, ctx, track) {
|
|
1103
|
+
server.registerTool(
|
|
1104
|
+
track(mcpToolName(ctx.productName, "lookbooks_get")),
|
|
1105
|
+
{
|
|
1106
|
+
title: "Get Lookbook",
|
|
1107
|
+
description: "Fetch a single lookbook by ID, including its entries.",
|
|
1108
|
+
inputSchema: { id: z.string().describe("Lookbook ID") }
|
|
1109
|
+
},
|
|
1110
|
+
async ({ id }) => {
|
|
1111
|
+
try {
|
|
1112
|
+
const data = await ctx.gql.workspace.request(GET_LOOKBOOK, { id });
|
|
1113
|
+
if (!data.cooLookbook) return errorResult(`Lookbook ${id} not found`);
|
|
1114
|
+
return jsonResult(data.cooLookbook);
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
ctx.logger.error("coolandlovely lookbooks_get failed", { error: String(error) });
|
|
1117
|
+
return errorResult(
|
|
1118
|
+
`Failed to get lookbook: ${error instanceof Error ? error.message : String(error)}`
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
);
|
|
1123
|
+
server.registerTool(
|
|
1124
|
+
track(mcpToolName(ctx.productName, "lookbooks_list")),
|
|
1125
|
+
{
|
|
1126
|
+
title: "List Lookbooks",
|
|
1127
|
+
description: "List lookbooks, optionally filtered by owner.",
|
|
1128
|
+
inputSchema: {
|
|
1129
|
+
ownerId: z.string().optional().describe("Filter by owner user ID")
|
|
1130
|
+
}
|
|
1131
|
+
},
|
|
1132
|
+
async ({ ownerId }) => {
|
|
1133
|
+
try {
|
|
1134
|
+
const data = await ctx.gql.workspace.request(LIST_LOOKBOOKS, { ownerId });
|
|
1135
|
+
return jsonResult(data.cooListLookbooks);
|
|
1136
|
+
} catch (error) {
|
|
1137
|
+
ctx.logger.error("coolandlovely lookbooks_list failed", { error: String(error) });
|
|
1138
|
+
return errorResult(
|
|
1139
|
+
`Failed to list lookbooks: ${error instanceof Error ? error.message : String(error)}`
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
);
|
|
1144
|
+
server.registerTool(
|
|
1145
|
+
track(mcpToolName(ctx.productName, "lookbooks_create")),
|
|
1146
|
+
{
|
|
1147
|
+
title: "Create Lookbook",
|
|
1148
|
+
description: "Create a curated lookbook board. `visibility` defaults to PRIVATE.",
|
|
1149
|
+
inputSchema: {
|
|
1150
|
+
name: z.string().min(1).describe("Lookbook name"),
|
|
1151
|
+
description: z.string().optional().describe("Lookbook description"),
|
|
1152
|
+
visibility: VisibilityEnum.optional().describe("Visibility (default PRIVATE)"),
|
|
1153
|
+
coverFileId: z.string().optional().describe("wspace-files-svc File ID for the cover"),
|
|
1154
|
+
tags: z.array(z.string()).optional().describe("Discovery tags")
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
async ({ name, description, visibility, coverFileId, tags }) => {
|
|
1158
|
+
try {
|
|
1159
|
+
const input = { name };
|
|
1160
|
+
if (description !== void 0) input.description = description;
|
|
1161
|
+
if (visibility !== void 0) input.visibility = visibility;
|
|
1162
|
+
if (coverFileId !== void 0) input.coverFileId = coverFileId;
|
|
1163
|
+
if (tags !== void 0) input.tags = tags;
|
|
1164
|
+
const data = await ctx.gql.workspace.request(CREATE_LOOKBOOK, { input });
|
|
1165
|
+
return jsonResult(data.cooCreateLookbook);
|
|
1166
|
+
} catch (error) {
|
|
1167
|
+
ctx.logger.error("coolandlovely lookbooks_create failed", { error: String(error) });
|
|
1168
|
+
return errorResult(
|
|
1169
|
+
`Failed to create lookbook: ${error instanceof Error ? error.message : String(error)}`
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
);
|
|
1174
|
+
server.registerTool(
|
|
1175
|
+
track(mcpToolName(ctx.productName, "lookbooks_add_entry")),
|
|
1176
|
+
{
|
|
1177
|
+
title: "Add Lookbook Entry",
|
|
1178
|
+
description: "Add an entry to a lookbook \u2014 reference either a look (`lookId`) or a style item (`styleItemId`), with an optional position and note.",
|
|
1179
|
+
inputSchema: {
|
|
1180
|
+
lookbookId: z.string().min(1).describe("Target lookbook ID"),
|
|
1181
|
+
lookId: z.string().optional().describe("Look to add (mutually exclusive with styleItemId)"),
|
|
1182
|
+
styleItemId: z.string().optional().describe("Style item to add (mutually exclusive with lookId)"),
|
|
1183
|
+
position: z.number().int().optional().describe("Ordering position within the lookbook"),
|
|
1184
|
+
note: z.string().optional().describe("Curator note for the entry")
|
|
1185
|
+
}
|
|
1186
|
+
},
|
|
1187
|
+
async ({ lookbookId, lookId, styleItemId, position, note }) => {
|
|
1188
|
+
try {
|
|
1189
|
+
const input = { lookbookId };
|
|
1190
|
+
if (lookId !== void 0) input.lookId = lookId;
|
|
1191
|
+
if (styleItemId !== void 0) input.styleItemId = styleItemId;
|
|
1192
|
+
if (position !== void 0) input.position = position;
|
|
1193
|
+
if (note !== void 0) input.note = note;
|
|
1194
|
+
const data = await ctx.gql.workspace.request(ADD_LOOKBOOK_ENTRY, { input });
|
|
1195
|
+
return jsonResult(data.cooAddLookbookEntry);
|
|
1196
|
+
} catch (error) {
|
|
1197
|
+
ctx.logger.error("coolandlovely lookbooks_add_entry failed", { error: String(error) });
|
|
1198
|
+
return errorResult(
|
|
1199
|
+
`Failed to add lookbook entry: ${error instanceof Error ? error.message : String(error)}`
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
);
|
|
1204
|
+
server.registerTool(
|
|
1205
|
+
track(mcpToolName(ctx.productName, "lookbooks_remove_entry")),
|
|
1206
|
+
{
|
|
1207
|
+
title: "Remove Lookbook Entry",
|
|
1208
|
+
description: "Remove an entry from a lookbook by entry ID. Returns true on success.",
|
|
1209
|
+
inputSchema: { id: z.string().describe("Lookbook entry ID") }
|
|
1210
|
+
},
|
|
1211
|
+
async ({ id }) => {
|
|
1212
|
+
try {
|
|
1213
|
+
const data = await ctx.gql.workspace.request(
|
|
1214
|
+
REMOVE_LOOKBOOK_ENTRY,
|
|
1215
|
+
{ id }
|
|
1216
|
+
);
|
|
1217
|
+
return jsonResult({ removed: data.cooRemoveLookbookEntry });
|
|
1218
|
+
} catch (error) {
|
|
1219
|
+
ctx.logger.error("coolandlovely lookbooks_remove_entry failed", { error: String(error) });
|
|
1220
|
+
return errorResult(
|
|
1221
|
+
`Failed to remove lookbook entry: ${error instanceof Error ? error.message : String(error)}`
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
function registerCollaborationTools(server, ctx, track) {
|
|
1228
|
+
server.registerTool(
|
|
1229
|
+
track(mcpToolName(ctx.productName, "collaborations_get")),
|
|
1230
|
+
{
|
|
1231
|
+
title: "Get Collaboration",
|
|
1232
|
+
description: "Fetch a single collaboration by ID, including its brand and resulting looks.",
|
|
1233
|
+
inputSchema: { id: z.string().describe("Collaboration ID") }
|
|
1234
|
+
},
|
|
1235
|
+
async ({ id }) => {
|
|
1236
|
+
try {
|
|
1237
|
+
const data = await ctx.gql.workspace.request(GET_COLLABORATION, { id });
|
|
1238
|
+
if (!data.cooCollaboration) return errorResult(`Collaboration ${id} not found`);
|
|
1239
|
+
return jsonResult(data.cooCollaboration);
|
|
1240
|
+
} catch (error) {
|
|
1241
|
+
ctx.logger.error("coolandlovely collaborations_get failed", { error: String(error) });
|
|
1242
|
+
return errorResult(
|
|
1243
|
+
`Failed to get collaboration: ${error instanceof Error ? error.message : String(error)}`
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
);
|
|
1248
|
+
server.registerTool(
|
|
1249
|
+
track(mcpToolName(ctx.productName, "collaborations_list")),
|
|
1250
|
+
{
|
|
1251
|
+
title: "List Collaborations",
|
|
1252
|
+
description: "List collaborations, optionally filtered by brand, creator, and/or status.",
|
|
1253
|
+
inputSchema: {
|
|
1254
|
+
brandId: z.string().optional().describe("Filter by brand ID"),
|
|
1255
|
+
creatorId: z.string().optional().describe("Filter by creator user ID"),
|
|
1256
|
+
status: CollaborationStatusEnum.optional().describe("Filter by collaboration status")
|
|
1257
|
+
}
|
|
1258
|
+
},
|
|
1259
|
+
async ({ brandId, creatorId, status }) => {
|
|
1260
|
+
try {
|
|
1261
|
+
const data = await ctx.gql.workspace.request(LIST_COLLABORATIONS, { brandId, creatorId, status });
|
|
1262
|
+
return jsonResult(data.cooListCollaborations);
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
ctx.logger.error("coolandlovely collaborations_list failed", { error: String(error) });
|
|
1265
|
+
return errorResult(
|
|
1266
|
+
`Failed to list collaborations: ${error instanceof Error ? error.message : String(error)}`
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
);
|
|
1271
|
+
server.registerTool(
|
|
1272
|
+
track(mcpToolName(ctx.productName, "collaborations_create")),
|
|
1273
|
+
{
|
|
1274
|
+
title: "Create Collaboration",
|
|
1275
|
+
description: "Propose a brand \u2194 creator collaboration. `role` defaults to CREATOR. Optionally set compensation/currency, a collection, and start/end dates (ISO-8601).",
|
|
1276
|
+
inputSchema: {
|
|
1277
|
+
brandId: z.string().min(1).describe("Brand ID"),
|
|
1278
|
+
creatorId: z.string().min(1).describe("Creator user ID"),
|
|
1279
|
+
title: z.string().min(1).describe("Collaboration title"),
|
|
1280
|
+
role: CollaboratorRoleEnum.optional().describe("Collaborator role (default CREATOR)"),
|
|
1281
|
+
brief: z.string().optional().describe("Collaboration brief"),
|
|
1282
|
+
collectionId: z.string().optional().describe("Related collection ID"),
|
|
1283
|
+
compensation: z.number().optional().describe("Compensation amount"),
|
|
1284
|
+
currency: z.string().optional().describe("ISO currency code"),
|
|
1285
|
+
startDate: z.string().optional().describe("Start date (ISO-8601 DateTime)"),
|
|
1286
|
+
endDate: z.string().optional().describe("End date (ISO-8601 DateTime)"),
|
|
1287
|
+
tags: z.array(z.string()).optional().describe("Discovery tags")
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
async ({
|
|
1291
|
+
brandId,
|
|
1292
|
+
creatorId,
|
|
1293
|
+
title,
|
|
1294
|
+
role,
|
|
1295
|
+
brief,
|
|
1296
|
+
collectionId,
|
|
1297
|
+
compensation,
|
|
1298
|
+
currency,
|
|
1299
|
+
startDate,
|
|
1300
|
+
endDate,
|
|
1301
|
+
tags
|
|
1302
|
+
}) => {
|
|
1303
|
+
try {
|
|
1304
|
+
const input = { brandId, creatorId, title };
|
|
1305
|
+
if (role !== void 0) input.role = role;
|
|
1306
|
+
if (brief !== void 0) input.brief = brief;
|
|
1307
|
+
if (collectionId !== void 0) input.collectionId = collectionId;
|
|
1308
|
+
if (compensation !== void 0) input.compensation = compensation;
|
|
1309
|
+
if (currency !== void 0) input.currency = currency;
|
|
1310
|
+
if (startDate !== void 0) input.startDate = startDate;
|
|
1311
|
+
if (endDate !== void 0) input.endDate = endDate;
|
|
1312
|
+
if (tags !== void 0) input.tags = tags;
|
|
1313
|
+
const data = await ctx.gql.workspace.request(CREATE_COLLABORATION, { input });
|
|
1314
|
+
return jsonResult(data.cooCreateCollaboration);
|
|
1315
|
+
} catch (error) {
|
|
1316
|
+
ctx.logger.error("coolandlovely collaborations_create failed", { error: String(error) });
|
|
1317
|
+
return errorResult(
|
|
1318
|
+
`Failed to create collaboration: ${error instanceof Error ? error.message : String(error)}`
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
);
|
|
1323
|
+
server.registerTool(
|
|
1324
|
+
track(mcpToolName(ctx.productName, "collaborations_transition")),
|
|
1325
|
+
{
|
|
1326
|
+
title: "Transition Collaboration",
|
|
1327
|
+
description: "Advance a collaboration through its lifecycle with a validated status transition (PROPOSED \u2192 ACCEPTED/DECLINED \u2192 IN_PROGRESS \u2192 PUBLISHED, or CANCELLED).",
|
|
1328
|
+
inputSchema: {
|
|
1329
|
+
id: z.string().describe("Collaboration ID"),
|
|
1330
|
+
status: CollaborationStatusEnum.describe("Target lifecycle status")
|
|
1331
|
+
}
|
|
1332
|
+
},
|
|
1333
|
+
async ({ id, status }) => {
|
|
1334
|
+
try {
|
|
1335
|
+
const data = await ctx.gql.workspace.request(TRANSITION_COLLABORATION, { id, status });
|
|
1336
|
+
return jsonResult(data.cooTransitionCollaboration);
|
|
1337
|
+
} catch (error) {
|
|
1338
|
+
ctx.logger.error("coolandlovely collaborations_transition failed", {
|
|
1339
|
+
error: String(error)
|
|
1340
|
+
});
|
|
1341
|
+
return errorResult(
|
|
1342
|
+
`Failed to transition collaboration: ${error instanceof Error ? error.message : String(error)}`
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
function registerWardrobeTools(server, ctx, track) {
|
|
1349
|
+
server.registerTool(
|
|
1350
|
+
track(mcpToolName(ctx.productName, "wardrobe_list")),
|
|
1351
|
+
{
|
|
1352
|
+
title: "List Wardrobe Items",
|
|
1353
|
+
description: "List the calling shopper's own wardrobe items, optionally filtered by category and/or source.",
|
|
1354
|
+
inputSchema: {
|
|
1355
|
+
category: StyleCategoryEnum.optional().describe("Filter by category"),
|
|
1356
|
+
source: WardrobeSourceEnum.optional().describe("Filter by how the piece was acquired")
|
|
1357
|
+
}
|
|
1358
|
+
},
|
|
1359
|
+
async ({ category, source }) => {
|
|
1360
|
+
try {
|
|
1361
|
+
const data = await ctx.gql.workspace.request(LIST_WARDROBE_ITEMS, { category, source });
|
|
1362
|
+
return jsonResult(data.cooListWardrobeItems);
|
|
1363
|
+
} catch (error) {
|
|
1364
|
+
ctx.logger.error("coolandlovely wardrobe_list failed", { error: String(error) });
|
|
1365
|
+
return errorResult(
|
|
1366
|
+
`Failed to list wardrobe items: ${error instanceof Error ? error.message : String(error)}`
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
);
|
|
1371
|
+
server.registerTool(
|
|
1372
|
+
track(mcpToolName(ctx.productName, "wardrobe_create")),
|
|
1373
|
+
{
|
|
1374
|
+
title: "Create Wardrobe Item",
|
|
1375
|
+
description: "Add a piece to the shopper's wardrobe. Optionally link it to a catalog style item via `styleItemId`.",
|
|
1376
|
+
inputSchema: {
|
|
1377
|
+
name: z.string().min(1).describe("Item name"),
|
|
1378
|
+
category: StyleCategoryEnum.optional().describe("Category (default OTHER)"),
|
|
1379
|
+
source: WardrobeSourceEnum.optional().describe("How it was acquired (default OWNED)"),
|
|
1380
|
+
brandName: z.string().optional().describe("Free-text brand name"),
|
|
1381
|
+
colour: z.string().optional().describe("Colour"),
|
|
1382
|
+
size: z.string().optional().describe("Size"),
|
|
1383
|
+
notes: z.string().optional().describe("Personal notes"),
|
|
1384
|
+
styleItemId: z.string().optional().describe("Linked catalog style item ID"),
|
|
1385
|
+
imageFileId: z.string().optional().describe("wspace-files-svc File ID for the shopper's photo"),
|
|
1386
|
+
tags: z.array(z.string()).optional().describe("Personal tags")
|
|
1387
|
+
}
|
|
1388
|
+
},
|
|
1389
|
+
async ({
|
|
1390
|
+
name,
|
|
1391
|
+
category,
|
|
1392
|
+
source,
|
|
1393
|
+
brandName,
|
|
1394
|
+
colour,
|
|
1395
|
+
size,
|
|
1396
|
+
notes,
|
|
1397
|
+
styleItemId,
|
|
1398
|
+
imageFileId,
|
|
1399
|
+
tags
|
|
1400
|
+
}) => {
|
|
1401
|
+
try {
|
|
1402
|
+
const input = { name };
|
|
1403
|
+
if (category !== void 0) input.category = category;
|
|
1404
|
+
if (source !== void 0) input.source = source;
|
|
1405
|
+
if (brandName !== void 0) input.brandName = brandName;
|
|
1406
|
+
if (colour !== void 0) input.colour = colour;
|
|
1407
|
+
if (size !== void 0) input.size = size;
|
|
1408
|
+
if (notes !== void 0) input.notes = notes;
|
|
1409
|
+
if (styleItemId !== void 0) input.styleItemId = styleItemId;
|
|
1410
|
+
if (imageFileId !== void 0) input.imageFileId = imageFileId;
|
|
1411
|
+
if (tags !== void 0) input.tags = tags;
|
|
1412
|
+
const data = await ctx.gql.workspace.request(CREATE_WARDROBE_ITEM, { input });
|
|
1413
|
+
return jsonResult(data.cooCreateWardrobeItem);
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
ctx.logger.error("coolandlovely wardrobe_create failed", { error: String(error) });
|
|
1416
|
+
return errorResult(
|
|
1417
|
+
`Failed to create wardrobe item: ${error instanceof Error ? error.message : String(error)}`
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
);
|
|
1422
|
+
server.registerTool(
|
|
1423
|
+
track(mcpToolName(ctx.productName, "wardrobe_update")),
|
|
1424
|
+
{
|
|
1425
|
+
title: "Update Wardrobe Item",
|
|
1426
|
+
description: "Update a wardrobe item. At least one mutable field must be provided.",
|
|
1427
|
+
inputSchema: {
|
|
1428
|
+
id: z.string().describe("Wardrobe item ID"),
|
|
1429
|
+
name: z.string().optional().describe("Updated name"),
|
|
1430
|
+
category: StyleCategoryEnum.optional().describe("Updated category"),
|
|
1431
|
+
source: WardrobeSourceEnum.optional().describe("Updated source"),
|
|
1432
|
+
brandName: z.string().optional().describe("Updated brand name"),
|
|
1433
|
+
colour: z.string().optional().describe("Updated colour"),
|
|
1434
|
+
size: z.string().optional().describe("Updated size"),
|
|
1435
|
+
notes: z.string().optional().describe("Updated notes"),
|
|
1436
|
+
imageFileId: z.string().optional().describe("Updated photo File ID"),
|
|
1437
|
+
tags: z.array(z.string()).optional().describe("Updated tags")
|
|
1438
|
+
}
|
|
1439
|
+
},
|
|
1440
|
+
async ({ id, name, category, source, brandName, colour, size, notes, imageFileId, tags }) => {
|
|
1441
|
+
try {
|
|
1442
|
+
const input = {};
|
|
1443
|
+
if (name !== void 0) input.name = name;
|
|
1444
|
+
if (category !== void 0) input.category = category;
|
|
1445
|
+
if (source !== void 0) input.source = source;
|
|
1446
|
+
if (brandName !== void 0) input.brandName = brandName;
|
|
1447
|
+
if (colour !== void 0) input.colour = colour;
|
|
1448
|
+
if (size !== void 0) input.size = size;
|
|
1449
|
+
if (notes !== void 0) input.notes = notes;
|
|
1450
|
+
if (imageFileId !== void 0) input.imageFileId = imageFileId;
|
|
1451
|
+
if (tags !== void 0) input.tags = tags;
|
|
1452
|
+
if (Object.keys(input).length === 0) {
|
|
1453
|
+
return errorResult("No updates specified \u2014 provide at least one mutable wardrobe field");
|
|
1454
|
+
}
|
|
1455
|
+
const data = await ctx.gql.workspace.request(UPDATE_WARDROBE_ITEM, { id, input });
|
|
1456
|
+
return jsonResult(data.cooUpdateWardrobeItem);
|
|
1457
|
+
} catch (error) {
|
|
1458
|
+
ctx.logger.error("coolandlovely wardrobe_update failed", { error: String(error) });
|
|
1459
|
+
return errorResult(
|
|
1460
|
+
`Failed to update wardrobe item: ${error instanceof Error ? error.message : String(error)}`
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
);
|
|
1465
|
+
server.registerTool(
|
|
1466
|
+
track(mcpToolName(ctx.productName, "wardrobe_log_wear")),
|
|
1467
|
+
{
|
|
1468
|
+
title: "Log Wardrobe Wear",
|
|
1469
|
+
description: "Log that the shopper wore a wardrobe piece (increments timesWorn).",
|
|
1470
|
+
inputSchema: { id: z.string().describe("Wardrobe item ID") }
|
|
1471
|
+
},
|
|
1472
|
+
async ({ id }) => {
|
|
1473
|
+
try {
|
|
1474
|
+
const data = await ctx.gql.workspace.request(LOG_WARDROBE_WEAR, { id });
|
|
1475
|
+
return jsonResult(data.cooLogWardrobeWear);
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
ctx.logger.error("coolandlovely wardrobe_log_wear failed", { error: String(error) });
|
|
1478
|
+
return errorResult(
|
|
1479
|
+
`Failed to log wardrobe wear: ${error instanceof Error ? error.message : String(error)}`
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
);
|
|
1484
|
+
server.registerTool(
|
|
1485
|
+
track(mcpToolName(ctx.productName, "wardrobe_remove")),
|
|
1486
|
+
{
|
|
1487
|
+
title: "Remove Wardrobe Item",
|
|
1488
|
+
description: "Remove a wardrobe item. Returns true on success.",
|
|
1489
|
+
inputSchema: { id: z.string().describe("Wardrobe item ID") }
|
|
1490
|
+
},
|
|
1491
|
+
async ({ id }) => {
|
|
1492
|
+
try {
|
|
1493
|
+
const data = await ctx.gql.workspace.request(
|
|
1494
|
+
REMOVE_WARDROBE_ITEM,
|
|
1495
|
+
{ id }
|
|
1496
|
+
);
|
|
1497
|
+
return jsonResult({ removed: data.cooRemoveWardrobeItem });
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
ctx.logger.error("coolandlovely wardrobe_remove failed", { error: String(error) });
|
|
1500
|
+
return errorResult(
|
|
1501
|
+
`Failed to remove wardrobe item: ${error instanceof Error ? error.message : String(error)}`
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1507
|
+
function registerProfileTools(server, ctx, track) {
|
|
1508
|
+
server.registerTool(
|
|
1509
|
+
track(mcpToolName(ctx.productName, "profile_get")),
|
|
1510
|
+
{
|
|
1511
|
+
title: "Get My Style Profile",
|
|
1512
|
+
description: "Fetch the calling shopper's own style profile (workspace-scoped), including lookbooks and wardrobe items.",
|
|
1513
|
+
inputSchema: {}
|
|
1514
|
+
},
|
|
1515
|
+
async () => {
|
|
1516
|
+
try {
|
|
1517
|
+
const data = await ctx.gql.workspace.request(MY_STYLE_PROFILE, {});
|
|
1518
|
+
if (!data.cooMyStyleProfile)
|
|
1519
|
+
return errorResult("No style profile exists yet for this shopper");
|
|
1520
|
+
return jsonResult(data.cooMyStyleProfile);
|
|
1521
|
+
} catch (error) {
|
|
1522
|
+
ctx.logger.error("coolandlovely profile_get failed", { error: String(error) });
|
|
1523
|
+
return errorResult(
|
|
1524
|
+
`Failed to get style profile: ${error instanceof Error ? error.message : String(error)}`
|
|
1525
|
+
);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
);
|
|
1529
|
+
server.registerTool(
|
|
1530
|
+
track(mcpToolName(ctx.productName, "profile_upsert")),
|
|
1531
|
+
{
|
|
1532
|
+
title: "Upsert My Style Profile",
|
|
1533
|
+
description: "Create or update the calling shopper's style profile (one per workspace). `sizes` is free-form JSON (e.g. { top: 'M', shoe: '8' }).",
|
|
1534
|
+
inputSchema: {
|
|
1535
|
+
displayName: z.string().optional().describe("Public display name"),
|
|
1536
|
+
bio: z.string().optional().describe("Profile bio"),
|
|
1537
|
+
palette: z.array(z.string()).optional().describe("Preferred colour palette"),
|
|
1538
|
+
stylePreferences: z.array(z.string()).optional().describe("Style preference keywords"),
|
|
1539
|
+
favouriteBrands: z.array(z.string()).optional().describe("Favourite brand names"),
|
|
1540
|
+
sizes: JsonObject.optional().describe("Size profile JSON (e.g. { top, bottom, shoe })"),
|
|
1541
|
+
visibility: VisibilityEnum.optional().describe("Profile visibility (default PRIVATE)")
|
|
1542
|
+
}
|
|
1543
|
+
},
|
|
1544
|
+
async ({ displayName, bio, palette, stylePreferences, favouriteBrands, sizes, visibility }) => {
|
|
1545
|
+
try {
|
|
1546
|
+
const input = {};
|
|
1547
|
+
if (displayName !== void 0) input.displayName = displayName;
|
|
1548
|
+
if (bio !== void 0) input.bio = bio;
|
|
1549
|
+
if (palette !== void 0) input.palette = palette;
|
|
1550
|
+
if (stylePreferences !== void 0) input.stylePreferences = stylePreferences;
|
|
1551
|
+
if (favouriteBrands !== void 0) input.favouriteBrands = favouriteBrands;
|
|
1552
|
+
if (sizes !== void 0) input.sizes = sizes;
|
|
1553
|
+
if (visibility !== void 0) input.visibility = visibility;
|
|
1554
|
+
const data = await ctx.gql.workspace.request(UPSERT_STYLE_PROFILE, { input });
|
|
1555
|
+
return jsonResult(data.cooUpsertStyleProfile);
|
|
1556
|
+
} catch (error) {
|
|
1557
|
+
ctx.logger.error("coolandlovely profile_upsert failed", { error: String(error) });
|
|
1558
|
+
return errorResult(
|
|
1559
|
+
`Failed to upsert style profile: ${error instanceof Error ? error.message : String(error)}`
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
// src/modules/coolandlovely/tools/health.ts
|
|
1567
|
+
function registerHealthTools(server, ctx, track) {
|
|
1568
|
+
server.registerTool(
|
|
1569
|
+
track(mcpToolName(ctx.productName, "health")),
|
|
1570
|
+
{
|
|
1571
|
+
title: "Coolandlovely Health Check",
|
|
1572
|
+
description: "Public liveness/identity probe for the Coolandlovely subgraph (cooHealth). Requires no authentication.",
|
|
1573
|
+
inputSchema: {}
|
|
1574
|
+
},
|
|
1575
|
+
async () => {
|
|
1576
|
+
try {
|
|
1577
|
+
const data = await ctx.gql.workspace.request(HEALTH, {});
|
|
1578
|
+
return jsonResult({ ok: true, status: data.cooHealth });
|
|
1579
|
+
} catch (error) {
|
|
1580
|
+
ctx.logger.error("coolandlovely health failed", { error: String(error) });
|
|
1581
|
+
return errorResult(
|
|
1582
|
+
`Coolandlovely health check failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// src/modules/coolandlovely/resources.ts
|
|
1590
|
+
function registerCoolandlovelyResources(server, ctx, track) {
|
|
1591
|
+
const schemaUri = track(mcpResourceUri(ctx.productName, "schema"));
|
|
1592
|
+
const brandsUri = track(mcpResourceUri(ctx.productName, "brands"));
|
|
1593
|
+
const looksUri = track(mcpResourceUri(ctx.productName, "looks"));
|
|
1594
|
+
const styleItemsUri = track(mcpResourceUri(ctx.productName, "style-items"));
|
|
1595
|
+
server.registerResource(
|
|
1596
|
+
"coolandlovely-schema",
|
|
1597
|
+
schemaUri,
|
|
1598
|
+
{
|
|
1599
|
+
title: "Coolandlovely Domain Schema",
|
|
1600
|
+
description: "Overview of the Coolandlovely fashion & lifestyle domain \u2014 entities, enums, and the per-entity tool surface.",
|
|
1601
|
+
mimeType: "text/plain"
|
|
1602
|
+
},
|
|
1603
|
+
async () => {
|
|
1604
|
+
ctx.logger.debug(`Resource ${schemaUri} requested`);
|
|
1605
|
+
const schema = `# Coolandlovely Domain (Overview)
|
|
1606
|
+
|
|
1607
|
+
Fashion & lifestyle platform \u2014 brand storefronts, seasonal collections,
|
|
1608
|
+
catalog items, styled looks, lookbooks, creator collaborations, and personal
|
|
1609
|
+
wardrobes/style profiles.
|
|
1610
|
+
|
|
1611
|
+
## Entities
|
|
1612
|
+
- CooBrand \u2014 a brand storefront (collections, style items)
|
|
1613
|
+
- CooCollection \u2014 a seasonal collection within a brand
|
|
1614
|
+
- CooStyleItem \u2014 a catalog item (product) within a brand/collection
|
|
1615
|
+
- CooLook (+CooLookItem) \u2014 a styled look + the items placed in it
|
|
1616
|
+
- CooLookbook (+CooLookbookEntry) \u2014 a curated board of looks/items
|
|
1617
|
+
- CooCollaboration \u2014 a brand \u2194 creator partnership
|
|
1618
|
+
- CooWardrobeItem \u2014 a shopper's personal closet piece
|
|
1619
|
+
- CooStyleProfile \u2014 the calling shopper's style profile (one per workspace)
|
|
1620
|
+
|
|
1621
|
+
## Enums
|
|
1622
|
+
- CooBrandStatus: DRAFT, PENDING_REVIEW, PUBLISHED, SUSPENDED, ARCHIVED
|
|
1623
|
+
- CooCollectionStatus: DRAFT, SCHEDULED, LIVE, ENDED, ARCHIVED
|
|
1624
|
+
- CooStyleItemStatus: DRAFT, ACTIVE, OUT_OF_STOCK, RETIRED
|
|
1625
|
+
- CooStyleCategory: TOPS, BOTTOMS, DRESSES, OUTERWEAR, FOOTWEAR, BAGS, ACCESSORIES, JEWELLERY, BEAUTY, OTHER
|
|
1626
|
+
- CooSeason: ALL_SEASON, SPRING, SUMMER, AUTUMN, WINTER, RESORT
|
|
1627
|
+
- CooLookOccasion: EVERYDAY, WORK, EVENING, PARTY, WEDDING_GUEST, VACATION, ACTIVE, OTHER
|
|
1628
|
+
- CooVisibility: PUBLIC, FOLLOWERS, PRIVATE
|
|
1629
|
+
- CooCollaborationStatus: PROPOSED, ACCEPTED, DECLINED, IN_PROGRESS, PUBLISHED, CANCELLED
|
|
1630
|
+
- CooCollaboratorRole: BRAND, CREATOR, STYLIST, PHOTOGRAPHER
|
|
1631
|
+
- CooWardrobeSource: OWNED, PURCHASED_ON_PLATFORM, GIFTED, WISHLIST
|
|
1632
|
+
|
|
1633
|
+
## Tool groups
|
|
1634
|
+
brands, collections, style_items, looks, lookbooks, collaborations, wardrobe, profile.
|
|
1635
|
+
All run against the WORKSPACE gateway (workspace-scoped) except the public health probe.
|
|
1636
|
+
`;
|
|
1637
|
+
return {
|
|
1638
|
+
contents: [{ uri: schemaUri, text: schema, mimeType: "text/plain" }]
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
);
|
|
1642
|
+
server.registerResource(
|
|
1643
|
+
"coolandlovely-brands",
|
|
1644
|
+
brandsUri,
|
|
1645
|
+
{
|
|
1646
|
+
title: "Coolandlovely Brand Directory",
|
|
1647
|
+
description: "The first 50 brand storefronts in the bound workspace.",
|
|
1648
|
+
mimeType: "application/json"
|
|
1649
|
+
},
|
|
1650
|
+
async () => {
|
|
1651
|
+
ctx.logger.debug(`Resource ${brandsUri} requested`);
|
|
1652
|
+
try {
|
|
1653
|
+
const data = await ctx.gql.workspace.request(LIST_BRANDS, {
|
|
1654
|
+
pagination: { limit: 50, offset: 0 }
|
|
1655
|
+
});
|
|
1656
|
+
return {
|
|
1657
|
+
contents: [
|
|
1658
|
+
{
|
|
1659
|
+
uri: brandsUri,
|
|
1660
|
+
text: JSON.stringify(data.cooListBrands, null, 2),
|
|
1661
|
+
mimeType: "application/json"
|
|
1662
|
+
}
|
|
1663
|
+
]
|
|
1664
|
+
};
|
|
1665
|
+
} catch (error) {
|
|
1666
|
+
return {
|
|
1667
|
+
contents: [
|
|
1668
|
+
{
|
|
1669
|
+
uri: brandsUri,
|
|
1670
|
+
text: JSON.stringify({
|
|
1671
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1672
|
+
}),
|
|
1673
|
+
mimeType: "application/json"
|
|
1674
|
+
}
|
|
1675
|
+
]
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
);
|
|
1680
|
+
server.registerResource(
|
|
1681
|
+
"coolandlovely-looks",
|
|
1682
|
+
looksUri,
|
|
1683
|
+
{
|
|
1684
|
+
title: "Coolandlovely Looks Feed",
|
|
1685
|
+
description: "The first 50 styled looks in the bound workspace.",
|
|
1686
|
+
mimeType: "application/json"
|
|
1687
|
+
},
|
|
1688
|
+
async () => {
|
|
1689
|
+
ctx.logger.debug(`Resource ${looksUri} requested`);
|
|
1690
|
+
try {
|
|
1691
|
+
const data = await ctx.gql.workspace.request(LIST_LOOKS, {
|
|
1692
|
+
pagination: { limit: 50, offset: 0 }
|
|
1693
|
+
});
|
|
1694
|
+
return {
|
|
1695
|
+
contents: [
|
|
1696
|
+
{
|
|
1697
|
+
uri: looksUri,
|
|
1698
|
+
text: JSON.stringify(data.cooListLooks, null, 2),
|
|
1699
|
+
mimeType: "application/json"
|
|
1700
|
+
}
|
|
1701
|
+
]
|
|
1702
|
+
};
|
|
1703
|
+
} catch (error) {
|
|
1704
|
+
return {
|
|
1705
|
+
contents: [
|
|
1706
|
+
{
|
|
1707
|
+
uri: looksUri,
|
|
1708
|
+
text: JSON.stringify({
|
|
1709
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1710
|
+
}),
|
|
1711
|
+
mimeType: "application/json"
|
|
1712
|
+
}
|
|
1713
|
+
]
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
);
|
|
1718
|
+
server.registerResource(
|
|
1719
|
+
"coolandlovely-style-items",
|
|
1720
|
+
styleItemsUri,
|
|
1721
|
+
{
|
|
1722
|
+
title: "Coolandlovely Catalog",
|
|
1723
|
+
description: "The first 50 catalog style items in the bound workspace.",
|
|
1724
|
+
mimeType: "application/json"
|
|
1725
|
+
},
|
|
1726
|
+
async () => {
|
|
1727
|
+
ctx.logger.debug(`Resource ${styleItemsUri} requested`);
|
|
1728
|
+
try {
|
|
1729
|
+
const data = await ctx.gql.workspace.request(
|
|
1730
|
+
LIST_STYLE_ITEMS,
|
|
1731
|
+
{ pagination: { limit: 50, offset: 0 } }
|
|
1732
|
+
);
|
|
1733
|
+
return {
|
|
1734
|
+
contents: [
|
|
1735
|
+
{
|
|
1736
|
+
uri: styleItemsUri,
|
|
1737
|
+
text: JSON.stringify(data.cooListStyleItems, null, 2),
|
|
1738
|
+
mimeType: "application/json"
|
|
1739
|
+
}
|
|
1740
|
+
]
|
|
1741
|
+
};
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
return {
|
|
1744
|
+
contents: [
|
|
1745
|
+
{
|
|
1746
|
+
uri: styleItemsUri,
|
|
1747
|
+
text: JSON.stringify({
|
|
1748
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1749
|
+
}),
|
|
1750
|
+
mimeType: "application/json"
|
|
1751
|
+
}
|
|
1752
|
+
]
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1758
|
+
function registerCoolandlovelyPrompts(server, ctx, track) {
|
|
1759
|
+
const p = ctx.productName;
|
|
1760
|
+
const onboardingName = track(mcpPromptName(p, "brand-onboarding"));
|
|
1761
|
+
const stylingName = track(mcpPromptName(p, "styling-workflow"));
|
|
1762
|
+
server.registerPrompt(
|
|
1763
|
+
onboardingName,
|
|
1764
|
+
{
|
|
1765
|
+
title: "Brand Onboarding",
|
|
1766
|
+
description: "Guide for onboarding a new Coolandlovely brand storefront end to end: create the brand, open a seasonal collection, add catalog style items, and publish.",
|
|
1767
|
+
argsSchema: {
|
|
1768
|
+
name: z.string().optional().describe("Brand name"),
|
|
1769
|
+
season: z.string().optional().describe("Launch season")
|
|
1770
|
+
}
|
|
1771
|
+
},
|
|
1772
|
+
({ name, season }) => {
|
|
1773
|
+
ctx.logger.debug(`Prompt ${onboardingName} called`, { name, season });
|
|
1774
|
+
const nameNote = name ? `Use the brand name "${name}".` : "Ask the user for the brand name.";
|
|
1775
|
+
const seasonNote = season ? `Use season ${season}.` : "Help the user pick a CooSeason.";
|
|
1776
|
+
return {
|
|
1777
|
+
messages: [
|
|
1778
|
+
{
|
|
1779
|
+
role: "user",
|
|
1780
|
+
content: {
|
|
1781
|
+
type: "text",
|
|
1782
|
+
text: `Help me onboard a new Coolandlovely brand storefront.
|
|
1783
|
+
|
|
1784
|
+
1. **Create Brand**: Use ${p}_brands_create. ${nameNote} Provide a unique handle, plus optional tagline/bio/aesthetics and logo/cover File IDs.
|
|
1785
|
+
2. **Open Collection**: Use ${p}_collections_create for the new brandId. ${seasonNote} Set publishAt/endAt to schedule the drop.
|
|
1786
|
+
3. **Add Style Items**: Use ${p}_style_items_create for each catalog piece \u2014 set category, price/currency, colours/materials/sizes, and attach the collectionId.
|
|
1787
|
+
4. **Verify Catalog**: Use ${p}_style_items_list filtered by the new brandId.
|
|
1788
|
+
5. **Publish**: Use ${p}_collections_publish to take the collection LIVE, then ${p}_brands_transition with status=PUBLISHED.
|
|
1789
|
+
|
|
1790
|
+
Show results at each step and confirm before publishing.`
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
]
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
);
|
|
1797
|
+
server.registerPrompt(
|
|
1798
|
+
stylingName,
|
|
1799
|
+
{
|
|
1800
|
+
title: "Styling Workflow",
|
|
1801
|
+
description: "Guide a shopper's styling journey: set up a style profile, build the wardrobe, create looks, and curate lookbooks.",
|
|
1802
|
+
argsSchema: {
|
|
1803
|
+
occasion: z.string().optional().describe("Target occasion for the look")
|
|
1804
|
+
}
|
|
1805
|
+
},
|
|
1806
|
+
({ occasion }) => {
|
|
1807
|
+
ctx.logger.debug(`Prompt ${stylingName} called`, { occasion });
|
|
1808
|
+
const occasionNote = occasion ? `Style for the ${occasion} occasion.` : `Ask the shopper which CooLookOccasion they're styling for.`;
|
|
1809
|
+
return {
|
|
1810
|
+
messages: [
|
|
1811
|
+
{
|
|
1812
|
+
role: "user",
|
|
1813
|
+
content: {
|
|
1814
|
+
type: "text",
|
|
1815
|
+
text: `Run a Coolandlovely styling workflow. ${occasionNote}
|
|
1816
|
+
|
|
1817
|
+
1. **Style Profile**: Use ${p}_profile_get to load the shopper's profile; if missing, create it with ${p}_profile_upsert (palette, stylePreferences, sizes).
|
|
1818
|
+
2. **Wardrobe**: Use ${p}_wardrobe_list to review owned pieces; add new ones with ${p}_wardrobe_create.
|
|
1819
|
+
3. **Discover Items**: Use ${p}_style_items_list (and ${p}_brands_list) to find catalog pieces that fit the profile/occasion.
|
|
1820
|
+
4. **Create Look**: Use ${p}_looks_create with the chosen items, then ${p}_looks_publish.
|
|
1821
|
+
5. **Curate Lookbook**: Use ${p}_lookbooks_create and ${p}_lookbooks_add_entry to save looks/items to a board.
|
|
1822
|
+
6. **Log Wear**: When the shopper wears a piece, use ${p}_wardrobe_log_wear.
|
|
1823
|
+
|
|
1824
|
+
Summarize the styled look and saved lookbook after each step.`
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
]
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// src/modules/coolandlovely/index.ts
|
|
1834
|
+
var coolandlovelyModule = {
|
|
1835
|
+
id: "coolandlovely",
|
|
1836
|
+
version: "0.0.1",
|
|
1837
|
+
displayName: "Coolandlovely",
|
|
1838
|
+
dependsOn: ["auth"],
|
|
1839
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
1840
|
+
register(server, ctx) {
|
|
1841
|
+
const tools = [];
|
|
1842
|
+
const resources = [];
|
|
1843
|
+
const prompts = [];
|
|
1844
|
+
const trackTool = (n) => {
|
|
1845
|
+
tools.push(n);
|
|
1846
|
+
return n;
|
|
1847
|
+
};
|
|
1848
|
+
const trackResource = (uri) => {
|
|
1849
|
+
resources.push(uri);
|
|
1850
|
+
return uri;
|
|
1851
|
+
};
|
|
1852
|
+
const trackPrompt = (n) => {
|
|
1853
|
+
prompts.push(n);
|
|
1854
|
+
return n;
|
|
1855
|
+
};
|
|
1856
|
+
registerBrandTools(server, ctx, trackTool);
|
|
1857
|
+
registerCollectionTools(server, ctx, trackTool);
|
|
1858
|
+
registerStyleItemTools(server, ctx, trackTool);
|
|
1859
|
+
registerLookTools(server, ctx, trackTool);
|
|
1860
|
+
registerLookbookTools(server, ctx, trackTool);
|
|
1861
|
+
registerCollaborationTools(server, ctx, trackTool);
|
|
1862
|
+
registerWardrobeTools(server, ctx, trackTool);
|
|
1863
|
+
registerProfileTools(server, ctx, trackTool);
|
|
1864
|
+
registerHealthTools(server, ctx, trackTool);
|
|
1865
|
+
registerCoolandlovelyResources(server, ctx, trackResource);
|
|
1866
|
+
registerCoolandlovelyPrompts(server, ctx, trackPrompt);
|
|
1867
|
+
return { tools, resources, prompts };
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
var coolandlovely_default = coolandlovelyModule;
|
|
1871
|
+
|
|
1872
|
+
export { coolandlovely_default as default };
|
|
1873
|
+
//# sourceMappingURL=index.js.map
|
|
1874
|
+
//# sourceMappingURL=index.js.map
|