@qikdev/mcp 6.6.48 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/build/src/tools/campaign.d.ts +41 -0
  2. package/build/src/tools/campaign.d.ts.map +1 -0
  3. package/build/src/tools/campaign.js +331 -0
  4. package/build/src/tools/campaign.js.map +1 -0
  5. package/build/src/tools/checkin.d.ts +25 -0
  6. package/build/src/tools/checkin.d.ts.map +1 -0
  7. package/build/src/tools/checkin.js +199 -0
  8. package/build/src/tools/checkin.js.map +1 -0
  9. package/build/src/tools/definition.d.ts +90 -0
  10. package/build/src/tools/definition.d.ts.map +1 -0
  11. package/build/src/tools/definition.js +803 -0
  12. package/build/src/tools/definition.js.map +1 -0
  13. package/build/src/tools/filters.d.ts +11 -0
  14. package/build/src/tools/filters.d.ts.map +1 -0
  15. package/build/src/tools/filters.js +146 -0
  16. package/build/src/tools/filters.js.map +1 -0
  17. package/build/src/tools/glossary.d.ts +9 -0
  18. package/build/src/tools/glossary.d.ts.map +1 -1
  19. package/build/src/tools/glossary.js +200 -23
  20. package/build/src/tools/glossary.js.map +1 -1
  21. package/build/src/tools/index.d.ts.map +1 -1
  22. package/build/src/tools/index.js +56 -1
  23. package/build/src/tools/index.js.map +1 -1
  24. package/build/src/tools/list.d.ts.map +1 -1
  25. package/build/src/tools/list.js +41 -1
  26. package/build/src/tools/list.js.map +1 -1
  27. package/build/src/tools/relationships.d.ts +11 -0
  28. package/build/src/tools/relationships.d.ts.map +1 -0
  29. package/build/src/tools/relationships.js +130 -0
  30. package/build/src/tools/relationships.js.map +1 -0
  31. package/build/src/tools/reporting.d.ts +22 -0
  32. package/build/src/tools/reporting.d.ts.map +1 -0
  33. package/build/src/tools/reporting.js +219 -0
  34. package/build/src/tools/reporting.js.map +1 -0
  35. package/build/src/tools/sms.d.ts +13 -0
  36. package/build/src/tools/sms.d.ts.map +1 -0
  37. package/build/src/tools/sms.js +128 -0
  38. package/build/src/tools/sms.js.map +1 -0
  39. package/package.json +1 -1
@@ -0,0 +1,803 @@
1
+ import { ConfigManager } from "../config.js";
2
+ import { getUserSessionData, getAvailableScopes, getScopeTitle } from "./user.js";
3
+ // ============================================================================
4
+ // TOOL DEFINITIONS
5
+ // ============================================================================
6
+ export const getDefinitionSchemaTool = {
7
+ name: "get_definition_schema",
8
+ description: "Get the complete schema reference for creating definitions - includes valid field types, widgets, base types, and validation options. Call this before creating definitions to understand what's available.",
9
+ inputSchema: {
10
+ type: "object",
11
+ properties: {},
12
+ },
13
+ };
14
+ export const searchSimilarDefinitionsTool = {
15
+ name: "search_similar_definitions",
16
+ description: "Search existing definitions to find similar content types that might be reused or extended. Use this before creating new definitions to check for existing types.",
17
+ inputSchema: {
18
+ type: "object",
19
+ properties: {
20
+ query: {
21
+ type: "string",
22
+ description: "Natural language description of what you're looking for (e.g., 'inventory tracking', 'product catalog', 'customer records')"
23
+ },
24
+ category: {
25
+ type: "string",
26
+ description: "Optional category to filter by"
27
+ }
28
+ },
29
+ required: ["query"]
30
+ },
31
+ };
32
+ export const createDefinitionTool = {
33
+ name: "create_definition",
34
+ description: "Create a new content type definition in Qik. Definitions are blueprints that specify the fields, validation rules, and behavior of content you store.",
35
+ inputSchema: {
36
+ type: "object",
37
+ properties: {
38
+ title: {
39
+ type: "string",
40
+ description: "Singular title for the content type (e.g., 'Car Part', 'Supplier', 'Invoice')"
41
+ },
42
+ plural: {
43
+ type: "string",
44
+ description: "Plural title (e.g., 'Car Parts', 'Suppliers'). Auto-generated if not provided."
45
+ },
46
+ definesType: {
47
+ type: "string",
48
+ enum: ["article", "submission", "scope", "event", "profile", "email", "integration", "workflowcard", "roster"],
49
+ description: "The base content type to extend. Use 'article' for most custom content, 'submission' for forms with payments, 'event' for calendar items."
50
+ },
51
+ category: {
52
+ type: "string",
53
+ description: "Category for organizing in admin menus (e.g., 'Inventory', 'Sales', 'People')"
54
+ },
55
+ fields: {
56
+ type: "array",
57
+ description: "Array of field definitions",
58
+ items: {
59
+ type: "object",
60
+ properties: {
61
+ title: { type: "string", description: "Display name for the field" },
62
+ key: { type: "string", description: "Database key (camelCase). Auto-generated from title if not provided." },
63
+ type: {
64
+ type: "string",
65
+ enum: ["string", "number", "integer", "boolean", "date", "email", "url", "reference", "object", "group"],
66
+ description: "Data type for the field"
67
+ },
68
+ widget: {
69
+ type: "string",
70
+ description: "UI widget type (input, textarea, select, button, richtext, datepicker, upload, currency, checkbox, switch, code)"
71
+ },
72
+ description: { type: "string", description: "Help text shown to users" },
73
+ minimum: { type: "number", description: "Minimum required answers. 0 = optional, 1+ = required." },
74
+ maximum: { type: "number", description: "Maximum answers allowed. 0 = unlimited, 1 = single value." },
75
+ referenceType: { type: "string", description: "For reference fields - the content type key to link to" },
76
+ options: {
77
+ type: "array",
78
+ description: "For select/button widgets - predefined choices",
79
+ items: {
80
+ type: "object",
81
+ properties: {
82
+ title: { type: "string" },
83
+ value: { type: "string" }
84
+ }
85
+ }
86
+ },
87
+ defaultValues: {
88
+ type: "array",
89
+ description: "Default values for the field"
90
+ }
91
+ },
92
+ required: ["title", "type"]
93
+ }
94
+ },
95
+ scope: {
96
+ type: "string",
97
+ description: "Scope ID to create the definition in"
98
+ },
99
+ defaultScopes: {
100
+ type: "array",
101
+ items: { type: "string" },
102
+ description: "Default scopes for content created with this definition"
103
+ }
104
+ },
105
+ required: ["title", "definesType"]
106
+ },
107
+ };
108
+ export const updateDefinitionTool = {
109
+ name: "update_definition",
110
+ description: "Update an existing definition - add/remove fields, change settings, or modify configuration",
111
+ inputSchema: {
112
+ type: "object",
113
+ properties: {
114
+ definitionId: {
115
+ type: "string",
116
+ description: "The ID of the definition to update"
117
+ },
118
+ definitionKey: {
119
+ type: "string",
120
+ description: "Alternatively, the key of the definition to update (e.g., 'carPart')"
121
+ },
122
+ changes: {
123
+ type: "object",
124
+ description: "The changes to apply",
125
+ properties: {
126
+ title: { type: "string", description: "New title" },
127
+ plural: { type: "string", description: "New plural title" },
128
+ category: { type: "string", description: "New category" },
129
+ addFields: {
130
+ type: "array",
131
+ description: "New fields to add (same format as create_definition fields)"
132
+ },
133
+ removeFields: {
134
+ type: "array",
135
+ items: { type: "string" },
136
+ description: "Field keys to remove"
137
+ },
138
+ updateFields: {
139
+ type: "array",
140
+ description: "Fields to modify (matched by key)"
141
+ }
142
+ }
143
+ }
144
+ },
145
+ required: ["changes"]
146
+ },
147
+ };
148
+ export const listDefinitionsTool = {
149
+ name: "list_definitions",
150
+ description: "List and search custom definitions (content types) in the platform",
151
+ inputSchema: {
152
+ type: "object",
153
+ properties: {
154
+ search: {
155
+ type: "string",
156
+ description: "Search term to filter definitions by title, key, or description"
157
+ },
158
+ definesType: {
159
+ type: "string",
160
+ description: "Filter by base type (e.g., 'article', 'submission', 'event')"
161
+ },
162
+ category: {
163
+ type: "string",
164
+ description: "Filter by category"
165
+ },
166
+ includeFields: {
167
+ type: "boolean",
168
+ description: "Include field definitions in response (default: false)"
169
+ },
170
+ page: {
171
+ type: "object",
172
+ description: "Pagination settings",
173
+ properties: {
174
+ size: { type: "number", description: "Items per page (default: 20)" },
175
+ index: { type: "number", description: "Page number, 1-based (default: 1)" }
176
+ }
177
+ }
178
+ }
179
+ },
180
+ };
181
+ // ============================================================================
182
+ // HANDLERS
183
+ // ============================================================================
184
+ export async function handleGetDefinitionSchema() {
185
+ const schema = {
186
+ baseTypes: [
187
+ { key: "article", description: "General content items with title, body, tags. Use for most custom content types." },
188
+ { key: "submission", description: "Form submissions with payment support, automation, and profile linking." },
189
+ { key: "event", description: "Calendar events with start/end dates, recurrence, and scheduling." },
190
+ { key: "profile", description: "People/contacts with name, contact info, address, and relationships." },
191
+ { key: "scope", description: "Organizational units for multi-tenancy and hierarchical permissions." },
192
+ { key: "email", description: "Email templates with variables and formatting." },
193
+ { key: "integration", description: "Third-party service integrations and webhooks." },
194
+ { key: "workflowcard", description: "Process automation templates with state machines." },
195
+ { key: "roster", description: "Scheduling and staffing management." }
196
+ ],
197
+ fieldTypes: [
198
+ { type: "string", description: "Basic text", widgets: ["input", "textarea", "select", "button", "richtext", "code"] },
199
+ { type: "number", description: "Decimal numbers", widgets: ["input", "slider", "currency"] },
200
+ { type: "integer", description: "Whole numbers", widgets: ["input", "slider"] },
201
+ { type: "boolean", description: "True/false", widgets: ["checkbox", "switch"] },
202
+ { type: "date", description: "Date/time values", widgets: ["datepicker", "dateobject"] },
203
+ { type: "email", description: "Email addresses", widgets: ["input"] },
204
+ { type: "url", description: "URLs/links", widgets: ["input"] },
205
+ { type: "reference", description: "Links to other content", widgets: ["select", "button", "upload", "form"] },
206
+ { type: "object", description: "Complex/nested data", widgets: ["filter", "dateobject", "location"] },
207
+ { type: "group", description: "Visual field grouping (no data storage)", widgets: null }
208
+ ],
209
+ widgetDetails: {
210
+ input: "Single-line text input",
211
+ textarea: "Multi-line text area",
212
+ select: "Dropdown selection",
213
+ button: "Button-style selection (radio or checkbox)",
214
+ richtext: "Rich text editor with formatting",
215
+ code: "Code editor with syntax highlighting",
216
+ datepicker: "Date and/or time picker",
217
+ dateobject: "Structured date object with separate fields",
218
+ upload: "File/image upload",
219
+ currency: "Currency input with formatting",
220
+ checkbox: "Checkbox for boolean",
221
+ switch: "Toggle switch for boolean",
222
+ slider: "Slider for number ranges",
223
+ filter: "Complex filter builder",
224
+ location: "Location/address picker",
225
+ form: "Embedded form for references"
226
+ },
227
+ validationOptions: {
228
+ minimum: "Minimum required answers (0 = optional, 1+ = required)",
229
+ maximum: "Maximum answers allowed (0 = unlimited)",
230
+ minValue: "For numbers - minimum value allowed",
231
+ maxValue: "For numbers - maximum value allowed",
232
+ allowedValues: "Restrict to specific values only"
233
+ },
234
+ referenceOptions: {
235
+ referenceType: "Content type key to link to (e.g., 'profile', 'partCategory')",
236
+ lockFilter: "Filter to restrict which items can be selected",
237
+ searchable: "Allow searching within reference items"
238
+ },
239
+ examples: {
240
+ simpleTextField: {
241
+ title: "Name",
242
+ type: "string",
243
+ widget: "input",
244
+ minimum: 1,
245
+ maximum: 1
246
+ },
247
+ optionalTextArea: {
248
+ title: "Description",
249
+ type: "string",
250
+ widget: "textarea",
251
+ minimum: 0,
252
+ maximum: 1
253
+ },
254
+ currencyField: {
255
+ title: "Price",
256
+ type: "number",
257
+ widget: "currency",
258
+ minimum: 1
259
+ },
260
+ selectField: {
261
+ title: "Status",
262
+ type: "string",
263
+ widget: "select",
264
+ minimum: 1,
265
+ options: [
266
+ { title: "Active", value: "active" },
267
+ { title: "Inactive", value: "inactive" },
268
+ { title: "Archived", value: "archived" }
269
+ ]
270
+ },
271
+ referenceField: {
272
+ title: "Category",
273
+ type: "reference",
274
+ widget: "select",
275
+ referenceType: "category",
276
+ minimum: 1
277
+ },
278
+ dateField: {
279
+ title: "Due Date",
280
+ type: "date",
281
+ widget: "datepicker",
282
+ minimum: 0
283
+ }
284
+ }
285
+ };
286
+ return {
287
+ content: [{
288
+ type: "text",
289
+ text: `# Definition Schema Reference
290
+
291
+ ## Base Types
292
+
293
+ ${schema.baseTypes.map(t => `**${t.key}**: ${t.description}`).join('\n')}
294
+
295
+ ## Field Types
296
+
297
+ ${schema.fieldTypes.map(t => `**${t.type}**: ${t.description}${t.widgets ? ` (widgets: ${t.widgets.join(', ')})` : ''}`).join('\n')}
298
+
299
+ ## Widget Details
300
+
301
+ ${Object.entries(schema.widgetDetails).map(([k, v]) => `**${k}**: ${v}`).join('\n')}
302
+
303
+ ## Validation Options
304
+
305
+ ${Object.entries(schema.validationOptions).map(([k, v]) => `**${k}**: ${v}`).join('\n')}
306
+
307
+ ## Reference Field Options
308
+
309
+ ${Object.entries(schema.referenceOptions).map(([k, v]) => `**${k}**: ${v}`).join('\n')}
310
+
311
+ ## Field Examples
312
+
313
+ \`\`\`json
314
+ ${JSON.stringify(schema.examples, null, 2)}
315
+ \`\`\`
316
+
317
+ ---
318
+ Use this schema to construct valid field definitions when calling create_definition.`
319
+ }]
320
+ };
321
+ }
322
+ export async function handleSearchSimilarDefinitions(args) {
323
+ try {
324
+ const configManager = new ConfigManager();
325
+ const config = await configManager.loadConfig();
326
+ if (!config) {
327
+ throw new Error('Qik MCP server not configured. Run setup first.');
328
+ }
329
+ // Fetch all definitions from glossary
330
+ const response = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/glossary/ai`, {
331
+ headers: {
332
+ 'Authorization': `Bearer ${config.accessToken}`,
333
+ 'Content-Type': 'application/json',
334
+ },
335
+ });
336
+ if (!response.ok) {
337
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
338
+ }
339
+ const glossary = await response.json();
340
+ // Filter to only custom definitions (those with definesType set)
341
+ let definitions = glossary.filter(item => item.definesType);
342
+ // Filter by category if provided
343
+ if (args.category) {
344
+ const categoryLower = args.category.toLowerCase();
345
+ definitions = definitions.filter(d => d.category && d.category.toLowerCase().includes(categoryLower));
346
+ }
347
+ // Search and score by relevance
348
+ const searchTerms = args.query.toLowerCase().split(/\s+/);
349
+ const scored = definitions.map(def => {
350
+ let score = 0;
351
+ const title = (def.title || '').toLowerCase();
352
+ const plural = (def.plural || '').toLowerCase();
353
+ const key = (def.key || '').toLowerCase();
354
+ const category = (def.category || '').toLowerCase();
355
+ for (const term of searchTerms) {
356
+ if (title.includes(term))
357
+ score += 10;
358
+ if (plural.includes(term))
359
+ score += 8;
360
+ if (key.includes(term))
361
+ score += 6;
362
+ if (category.includes(term))
363
+ score += 4;
364
+ // Partial matches
365
+ if (term.includes(title) || title.includes(term))
366
+ score += 2;
367
+ }
368
+ return { ...def, score };
369
+ });
370
+ // Sort by score and filter out zero scores
371
+ const matches = scored
372
+ .filter(d => d.score > 0)
373
+ .sort((a, b) => b.score - a.score)
374
+ .slice(0, 10);
375
+ if (matches.length === 0) {
376
+ return {
377
+ content: [{
378
+ type: "text",
379
+ text: `No existing definitions found matching "${args.query}".
380
+
381
+ This means you can create a new definition for this purpose. Use create_definition to create one.`
382
+ }]
383
+ };
384
+ }
385
+ const resultText = matches.map((def, i) => {
386
+ return `**${i + 1}. ${def.title}** (${def.plural})
387
+ Key: \`${def.key}\`
388
+ Extends: ${def.definesType}
389
+ Category: ${def.category || 'None'}
390
+ Relevance: ${Math.round((def.score / 10) * 100)}%`;
391
+ }).join('\n\n');
392
+ return {
393
+ content: [{
394
+ type: "text",
395
+ text: `# Similar Definitions Found
396
+
397
+ ${resultText}
398
+
399
+ ---
400
+ **Options:**
401
+ - Use an existing definition if it matches your needs
402
+ - Create a new definition if none of these fit
403
+ - Use \`get_content_type_details\` with any key above to see full field details`
404
+ }]
405
+ };
406
+ }
407
+ catch (error) {
408
+ return {
409
+ content: [{
410
+ type: "text",
411
+ text: `Failed to search definitions: ${error.message}`
412
+ }]
413
+ };
414
+ }
415
+ }
416
+ export async function handleCreateDefinition(args) {
417
+ try {
418
+ const configManager = new ConfigManager();
419
+ const config = await configManager.loadConfig();
420
+ if (!config) {
421
+ throw new Error('Qik MCP server not configured. Run setup first.');
422
+ }
423
+ // Validate required fields
424
+ if (!args.title) {
425
+ return createErrorResponse('Title is required for creating a definition');
426
+ }
427
+ if (!args.definesType) {
428
+ return createErrorResponse('definesType is required. Use "article" for most custom content types.');
429
+ }
430
+ // Get user session for permissions
431
+ const userSession = await getUserSessionData();
432
+ const createPermission = 'definition.create';
433
+ const availableScopes = getAvailableScopes(userSession, createPermission);
434
+ if (availableScopes.length === 0) {
435
+ return createErrorResponse(`You don't have permission to create definitions. Required permission: ${createPermission}`);
436
+ }
437
+ // Handle scope selection
438
+ const selectedScopeId = args.scope;
439
+ if (!selectedScopeId && availableScopes.length > 1) {
440
+ return createScopeSelectionPrompt('definition', availableScopes, userSession);
441
+ }
442
+ const targetScopeId = selectedScopeId || availableScopes[0];
443
+ if (!availableScopes.includes(targetScopeId)) {
444
+ return createErrorResponse(`Invalid scope "${targetScopeId}". Available scopes: ${availableScopes.map(id => `${getScopeTitle(userSession, id)} (${id})`).join(', ')}`);
445
+ }
446
+ // Generate key from title
447
+ const key = generateKey(args.title);
448
+ // Build definition payload
449
+ const definitionPayload = {
450
+ title: args.title,
451
+ plural: args.plural || `${args.title}s`,
452
+ key: key,
453
+ definesType: args.definesType,
454
+ category: args.category || '',
455
+ fields: normalizeFields(args.fields || []),
456
+ defaultScopes: args.defaultScopes || [],
457
+ restrictScopes: [],
458
+ weight: 0,
459
+ meta: {
460
+ scopes: [targetScopeId],
461
+ status: 'active'
462
+ }
463
+ };
464
+ // Create via API
465
+ const response = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/content/definition/create`, {
466
+ method: 'POST',
467
+ headers: {
468
+ 'Authorization': `Bearer ${config.accessToken}`,
469
+ 'Content-Type': 'application/json',
470
+ },
471
+ body: JSON.stringify(definitionPayload)
472
+ });
473
+ if (!response.ok) {
474
+ const errorText = await response.text();
475
+ throw new Error(`HTTP ${response.status} - ${errorText}`);
476
+ }
477
+ const createdDefinition = await response.json();
478
+ return {
479
+ content: [{
480
+ type: "text",
481
+ text: `Successfully created definition "${args.title}"!
482
+
483
+ **Details:**
484
+ - ID: ${createdDefinition._id}
485
+ - Key: \`${key}\`
486
+ - Base Type: ${args.definesType}
487
+ - Category: ${args.category || 'None'}
488
+ - Fields: ${(args.fields || []).length}
489
+ - Scope: ${getScopeTitle(userSession, targetScopeId)}
490
+
491
+ **Next Steps:**
492
+ - Create content with: \`create_content\` using typeKey: \`${key}\`
493
+ - View full details: \`get_content_type_details\` with key: \`${key}\`
494
+ - Add more fields: \`update_definition\` with definitionKey: \`${key}\``
495
+ }]
496
+ };
497
+ }
498
+ catch (error) {
499
+ return createErrorResponse(`Failed to create definition: ${error.message}`);
500
+ }
501
+ }
502
+ export async function handleUpdateDefinition(args) {
503
+ try {
504
+ const configManager = new ConfigManager();
505
+ const config = await configManager.loadConfig();
506
+ if (!config) {
507
+ throw new Error('Qik MCP server not configured. Run setup first.');
508
+ }
509
+ if (!args.changes) {
510
+ return createErrorResponse('No changes provided');
511
+ }
512
+ // Need either definitionId or definitionKey
513
+ let definitionId = args.definitionId;
514
+ if (!definitionId && args.definitionKey) {
515
+ // Look up the definition by key
516
+ const listResponse = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/content/definition/list`, {
517
+ method: 'POST',
518
+ headers: {
519
+ 'Authorization': `Bearer ${config.accessToken}`,
520
+ 'Content-Type': 'application/json',
521
+ },
522
+ body: JSON.stringify({
523
+ filter: {
524
+ operator: 'and',
525
+ filters: [
526
+ { key: 'key', comparator: 'equal', value: args.definitionKey }
527
+ ]
528
+ },
529
+ page: { size: 1, index: 1 }
530
+ })
531
+ });
532
+ if (!listResponse.ok) {
533
+ throw new Error(`Failed to find definition: HTTP ${listResponse.status}`);
534
+ }
535
+ const listData = await listResponse.json();
536
+ if (!listData.items || listData.items.length === 0) {
537
+ return createErrorResponse(`Definition with key "${args.definitionKey}" not found`);
538
+ }
539
+ definitionId = listData.items[0]._id;
540
+ }
541
+ if (!definitionId) {
542
+ return createErrorResponse('Either definitionId or definitionKey must be provided');
543
+ }
544
+ // First, get the current definition
545
+ const getResponse = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/content/${definitionId}`, {
546
+ headers: {
547
+ 'Authorization': `Bearer ${config.accessToken}`,
548
+ 'Content-Type': 'application/json',
549
+ },
550
+ });
551
+ if (!getResponse.ok) {
552
+ throw new Error(`Failed to get definition: HTTP ${getResponse.status}`);
553
+ }
554
+ const currentDefinition = await getResponse.json();
555
+ // Build update payload
556
+ const updatePayload = { ...currentDefinition };
557
+ if (args.changes.title) {
558
+ updatePayload.title = args.changes.title;
559
+ }
560
+ if (args.changes.plural) {
561
+ updatePayload.plural = args.changes.plural;
562
+ }
563
+ if (args.changes.category) {
564
+ updatePayload.category = args.changes.category;
565
+ }
566
+ // Handle field changes
567
+ let fields = [...(currentDefinition.fields || [])];
568
+ // Remove fields
569
+ if (args.changes.removeFields && args.changes.removeFields.length > 0) {
570
+ fields = fields.filter(f => !args.changes.removeFields.includes(f.key));
571
+ }
572
+ // Update fields
573
+ if (args.changes.updateFields && args.changes.updateFields.length > 0) {
574
+ for (const updateField of args.changes.updateFields) {
575
+ const index = fields.findIndex(f => f.key === updateField.key);
576
+ if (index !== -1) {
577
+ fields[index] = { ...fields[index], ...normalizeField(updateField) };
578
+ }
579
+ }
580
+ }
581
+ // Add fields
582
+ if (args.changes.addFields && args.changes.addFields.length > 0) {
583
+ const normalizedNewFields = args.changes.addFields.map(f => normalizeField(f));
584
+ fields = [...fields, ...normalizedNewFields];
585
+ }
586
+ updatePayload.fields = fields;
587
+ // Update via API
588
+ const response = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/content/${definitionId}`, {
589
+ method: 'PUT',
590
+ headers: {
591
+ 'Authorization': `Bearer ${config.accessToken}`,
592
+ 'Content-Type': 'application/json',
593
+ },
594
+ body: JSON.stringify(updatePayload)
595
+ });
596
+ if (!response.ok) {
597
+ const errorText = await response.text();
598
+ throw new Error(`HTTP ${response.status} - ${errorText}`);
599
+ }
600
+ const updatedDefinition = await response.json();
601
+ return {
602
+ content: [{
603
+ type: "text",
604
+ text: `Successfully updated definition "${updatedDefinition.title}"!
605
+
606
+ **Changes Applied:**
607
+ ${args.changes.title ? `- Title changed to: ${args.changes.title}` : ''}
608
+ ${args.changes.plural ? `- Plural changed to: ${args.changes.plural}` : ''}
609
+ ${args.changes.category ? `- Category changed to: ${args.changes.category}` : ''}
610
+ ${args.changes.addFields ? `- Added ${args.changes.addFields.length} field(s)` : ''}
611
+ ${args.changes.removeFields ? `- Removed ${args.changes.removeFields.length} field(s)` : ''}
612
+ ${args.changes.updateFields ? `- Updated ${args.changes.updateFields.length} field(s)` : ''}
613
+
614
+ **Current Field Count:** ${fields.length}`
615
+ }]
616
+ };
617
+ }
618
+ catch (error) {
619
+ return createErrorResponse(`Failed to update definition: ${error.message}`);
620
+ }
621
+ }
622
+ export async function handleListDefinitions(args) {
623
+ try {
624
+ const configManager = new ConfigManager();
625
+ const config = await configManager.loadConfig();
626
+ if (!config) {
627
+ throw new Error('Qik MCP server not configured. Run setup first.');
628
+ }
629
+ // Build filter
630
+ const filters = [];
631
+ if (args.search) {
632
+ // Search in title and key
633
+ filters.push({
634
+ operator: 'or',
635
+ filters: [
636
+ { key: 'title', comparator: 'contains', value: args.search },
637
+ { key: 'key', comparator: 'contains', value: args.search }
638
+ ]
639
+ });
640
+ }
641
+ if (args.definesType) {
642
+ filters.push({ key: 'definesType', comparator: 'equal', value: args.definesType });
643
+ }
644
+ if (args.category) {
645
+ filters.push({ key: 'category', comparator: 'contains', value: args.category });
646
+ }
647
+ const listPayload = {
648
+ sort: { key: 'title', direction: 'asc', type: 'string' },
649
+ page: {
650
+ size: args.page?.size || 20,
651
+ index: args.page?.index || 1
652
+ }
653
+ };
654
+ if (filters.length > 0) {
655
+ listPayload.filter = {
656
+ operator: 'and',
657
+ filters: filters
658
+ };
659
+ }
660
+ // Select fields based on includeFields option
661
+ if (args.includeFields) {
662
+ listPayload.select = ['_id', 'title', 'plural', 'key', 'definesType', 'category', 'fields', 'meta'];
663
+ }
664
+ else {
665
+ listPayload.select = ['_id', 'title', 'plural', 'key', 'definesType', 'category', 'meta'];
666
+ }
667
+ const response = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/content/definition/list`, {
668
+ method: 'POST',
669
+ headers: {
670
+ 'Authorization': `Bearer ${config.accessToken}`,
671
+ 'Content-Type': 'application/json',
672
+ },
673
+ body: JSON.stringify(listPayload)
674
+ });
675
+ if (!response.ok) {
676
+ const errorText = await response.text();
677
+ throw new Error(`HTTP ${response.status} - ${errorText}`);
678
+ }
679
+ const results = await response.json();
680
+ const items = results.items || [];
681
+ const total = results.total || items.length;
682
+ const currentPage = args.page?.index || 1;
683
+ const pageSize = args.page?.size || 20;
684
+ if (items.length === 0) {
685
+ return {
686
+ content: [{
687
+ type: "text",
688
+ text: `No definitions found matching your criteria.
689
+
690
+ Use create_definition to create a new content type.`
691
+ }]
692
+ };
693
+ }
694
+ let resultText = `# Definitions (${total} total)`;
695
+ if (total > pageSize) {
696
+ const totalPages = Math.ceil(total / pageSize);
697
+ resultText += ` - Page ${currentPage} of ${totalPages}`;
698
+ }
699
+ resultText += '\n\n';
700
+ items.forEach((item, index) => {
701
+ const itemNumber = ((currentPage - 1) * pageSize) + index + 1;
702
+ resultText += `**${itemNumber}. ${item.title}** (${item.plural || item.title + 's'})\n`;
703
+ resultText += ` Key: \`${item.key}\`\n`;
704
+ resultText += ` Extends: ${item.definesType}\n`;
705
+ if (item.category) {
706
+ resultText += ` Category: ${item.category}\n`;
707
+ }
708
+ if (args.includeFields && item.fields) {
709
+ resultText += ` Fields: ${item.fields.length} (${item.fields.slice(0, 3).map((f) => f.title).join(', ')}${item.fields.length > 3 ? '...' : ''})\n`;
710
+ }
711
+ resultText += '\n';
712
+ });
713
+ if (total > pageSize && currentPage < Math.ceil(total / pageSize)) {
714
+ resultText += `\nTo see more: use page: { index: ${currentPage + 1} }`;
715
+ }
716
+ return {
717
+ content: [{
718
+ type: "text",
719
+ text: resultText
720
+ }]
721
+ };
722
+ }
723
+ catch (error) {
724
+ return createErrorResponse(`Failed to list definitions: ${error.message}`);
725
+ }
726
+ }
727
+ // ============================================================================
728
+ // HELPER FUNCTIONS
729
+ // ============================================================================
730
+ function createErrorResponse(message) {
731
+ return {
732
+ content: [{
733
+ type: "text",
734
+ text: `${message}`
735
+ }]
736
+ };
737
+ }
738
+ function createScopeSelectionPrompt(contentType, availableScopes, userSession) {
739
+ const scopeOptions = availableScopes.map(scopeId => {
740
+ const title = getScopeTitle(userSession, scopeId);
741
+ return `- **${title}** (ID: \`${scopeId}\`)`;
742
+ }).join('\n');
743
+ return {
744
+ content: [{
745
+ type: "text",
746
+ text: `You can create this ${contentType} in multiple scopes. Please select one:
747
+
748
+ ${scopeOptions}
749
+
750
+ Call create_definition again with the scope parameter:
751
+ \`\`\`json
752
+ {
753
+ "title": "Your Title",
754
+ "definesType": "article",
755
+ "scope": "scope_id_here"
756
+ }
757
+ \`\`\``
758
+ }]
759
+ };
760
+ }
761
+ function generateKey(title) {
762
+ // Convert "Car Part" to "carPart"
763
+ return title
764
+ .toLowerCase()
765
+ .replace(/[^a-z0-9\s]/g, '')
766
+ .split(/\s+/)
767
+ .map((word, i) => i === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1))
768
+ .join('');
769
+ }
770
+ function normalizeFields(fields) {
771
+ return fields.map(field => normalizeField(field));
772
+ }
773
+ function normalizeField(field) {
774
+ return {
775
+ title: field.title,
776
+ key: field.key || generateKey(field.title),
777
+ type: field.type || 'string',
778
+ widget: field.widget || getDefaultWidget(field.type),
779
+ description: field.description || '',
780
+ minimum: field.minimum ?? 0,
781
+ maximum: field.maximum ?? 1,
782
+ referenceType: field.referenceType,
783
+ options: field.options || [],
784
+ defaultValues: field.defaultValues || [],
785
+ lockFilter: field.lockFilter,
786
+ expressions: field.expressions
787
+ };
788
+ }
789
+ function getDefaultWidget(type) {
790
+ const defaults = {
791
+ 'string': 'input',
792
+ 'number': 'input',
793
+ 'integer': 'input',
794
+ 'boolean': 'checkbox',
795
+ 'date': 'datepicker',
796
+ 'email': 'input',
797
+ 'url': 'input',
798
+ 'reference': 'select',
799
+ 'object': 'input'
800
+ };
801
+ return defaults[type] || 'input';
802
+ }
803
+ //# sourceMappingURL=definition.js.map