@qikdev/mcp 6.6.6 → 6.6.8

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.
@@ -49,6 +49,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } f
49
49
  import axios from 'axios';
50
50
  import FormData from 'form-data';
51
51
  import { ConfigManager } from './config.js';
52
+ import { QikDocumentationHelper, QIK_DOCUMENTATION } from './documentation.js';
52
53
  // Environment variables
53
54
  const QIK_API_URL = process.env.QIK_API_URL || 'https://api.qik.dev';
54
55
  const QIK_ACCESS_TOKEN = process.env.QIK_ACCESS_TOKEN;
@@ -199,7 +200,7 @@ export class QikMCPServer {
199
200
  }
200
201
  return { valid: true, info };
201
202
  }
202
- // Helper methods for field analysis and processing
203
+ // Enhanced glossary-driven field analysis methods
203
204
  isFieldRequired(field) {
204
205
  return field.minimum !== undefined && field.minimum > 0;
205
206
  }
@@ -209,6 +210,369 @@ export class QikMCPServer {
209
210
  getMinimumArrayLength(field) {
210
211
  return this.isFieldArray(field) ? (field.minimum || 0) : 0;
211
212
  }
213
+ /**
214
+ * Analyzes field placement rules directly from glossary structure
215
+ * This is the authoritative source for field placement decisions
216
+ */
217
+ analyzeFieldPlacementFromGlossary(contentType) {
218
+ const typeInfo = this.glossary[contentType];
219
+ if (!typeInfo) {
220
+ throw new Error(`Content type ${contentType} not found in glossary`);
221
+ }
222
+ // Check if this is a custom definition (has definesType)
223
+ const isCustomDefinition = !!typeInfo.definesType;
224
+ const baseType = typeInfo.definesType || null;
225
+ // Extract fields from the glossary structure
226
+ const fields = typeInfo.fields || [];
227
+ const definedFields = typeInfo.definedFields || [];
228
+ // Create widget mapping for enhanced validation
229
+ const widgetFields = new Map();
230
+ // Process fields for widget information
231
+ const processFieldsForWidgets = (fieldList) => {
232
+ for (const field of fieldList) {
233
+ if (field.widget) {
234
+ widgetFields.set(field.key, field.widget);
235
+ }
236
+ // Process nested fields in groups
237
+ if (field.fields && Array.isArray(field.fields)) {
238
+ processFieldsForWidgets(field.fields);
239
+ }
240
+ }
241
+ };
242
+ processFieldsForWidgets(fields);
243
+ processFieldsForWidgets(definedFields);
244
+ return {
245
+ rootLevelFields: new Set(fields.map(f => f.key)),
246
+ dataObjectFields: new Set(definedFields.map((f) => f.key)),
247
+ baseType,
248
+ isCustomDefinition,
249
+ allFields: fields,
250
+ allDefinedFields: definedFields,
251
+ widgetFields
252
+ };
253
+ }
254
+ /**
255
+ * Validates field placement based on glossary structure
256
+ */
257
+ validateFieldPlacementFromGlossary(args, contentType) {
258
+ const analysis = this.analyzeFieldPlacementFromGlossary(contentType);
259
+ const errors = [];
260
+ const warnings = [];
261
+ // Check for fields in data object that should be at root level
262
+ if (args.data && typeof args.data === 'object') {
263
+ for (const [key, value] of Object.entries(args.data)) {
264
+ if (analysis.rootLevelFields.has(key)) {
265
+ errors.push(`Field "${key}" is defined in glossary "fields" array and must be at ROOT LEVEL, not in data object`);
266
+ }
267
+ // Widget validation for data object fields
268
+ if (analysis.widgetFields.has(key)) {
269
+ const widgetValidation = this.validateWidgetValue(key, value, analysis.widgetFields.get(key));
270
+ if (!widgetValidation.valid) {
271
+ errors.push(...widgetValidation.errors);
272
+ }
273
+ if (widgetValidation.warnings.length > 0) {
274
+ warnings.push(...widgetValidation.warnings);
275
+ }
276
+ }
277
+ }
278
+ }
279
+ // Check for fields at root level that should be in data object
280
+ for (const [key, value] of Object.entries(args)) {
281
+ if (key !== 'type' && key !== 'title' && key !== 'meta' && key !== 'data' &&
282
+ key !== 'reference' && key !== 'referenceType' && key !== 'body' && key !== 'organisation') {
283
+ if (analysis.dataObjectFields.has(key)) {
284
+ errors.push(`Field "${key}" is defined in glossary "definedFields" array and must be in DATA OBJECT, not at root level`);
285
+ }
286
+ // Widget validation for root level fields
287
+ if (analysis.widgetFields.has(key)) {
288
+ const widgetValidation = this.validateWidgetValue(key, value, analysis.widgetFields.get(key));
289
+ if (!widgetValidation.valid) {
290
+ errors.push(...widgetValidation.errors);
291
+ }
292
+ if (widgetValidation.warnings.length > 0) {
293
+ warnings.push(...widgetValidation.warnings);
294
+ }
295
+ }
296
+ }
297
+ }
298
+ return { valid: errors.length === 0, errors, warnings, analysis };
299
+ }
300
+ /**
301
+ * Automatically restructures payload based on glossary field placement rules
302
+ */
303
+ restructurePayloadFromGlossary(args, contentType) {
304
+ const analysis = this.analyzeFieldPlacementFromGlossary(contentType);
305
+ const payload = {};
306
+ // Always include title if provided
307
+ if (args.title)
308
+ payload.title = args.title;
309
+ // Handle meta separately (always root level)
310
+ if (args.meta)
311
+ payload.meta = args.meta;
312
+ // Handle standard root-level fields
313
+ const standardRootFields = ['reference', 'referenceType', 'body', 'organisation'];
314
+ for (const fieldKey of standardRootFields) {
315
+ if (args[fieldKey] !== undefined) {
316
+ payload[fieldKey] = args[fieldKey];
317
+ }
318
+ }
319
+ // Process provided data and separate into correct locations
320
+ const rootData = {};
321
+ const dataObjectData = {};
322
+ if (args.data && typeof args.data === 'object') {
323
+ for (const [key, value] of Object.entries(args.data)) {
324
+ if (analysis.rootLevelFields.has(key)) {
325
+ rootData[key] = value; // Move to root level
326
+ }
327
+ else {
328
+ dataObjectData[key] = value; // Keep in data object
329
+ }
330
+ }
331
+ }
332
+ // Add any root-level fields that were incorrectly placed at top level
333
+ for (const [key, value] of Object.entries(args)) {
334
+ if (key !== 'type' && key !== 'title' && key !== 'meta' && key !== 'data' &&
335
+ !standardRootFields.includes(key)) {
336
+ if (analysis.dataObjectFields.has(key)) {
337
+ dataObjectData[key] = value; // Move to data object
338
+ }
339
+ else if (analysis.rootLevelFields.has(key)) {
340
+ rootData[key] = value; // Keep at root level
341
+ }
342
+ }
343
+ }
344
+ // Add root level fields to payload
345
+ Object.assign(payload, rootData);
346
+ // Add data object if there are any data fields
347
+ if (Object.keys(dataObjectData).length > 0) {
348
+ payload.data = dataObjectData;
349
+ }
350
+ return payload;
351
+ }
352
+ /**
353
+ * Generates comprehensive error message based on glossary structure with widget guidance
354
+ */
355
+ generateFieldPlacementErrorFromGlossary(contentType, errors, warnings = []) {
356
+ const analysis = this.analyzeFieldPlacementFromGlossary(contentType);
357
+ const rootFieldsList = Array.from(analysis.rootLevelFields).join(', ');
358
+ const dataFieldsList = Array.from(analysis.dataObjectFields).join(', ');
359
+ let errorMessage = `🚫 **FIELD PLACEMENT ERRORS for ${contentType.toUpperCase()}**
360
+
361
+ ${errors.join('\n')}`;
362
+ if (warnings.length > 0) {
363
+ errorMessage += `\n\n⚠️ **WARNINGS:**\n${warnings.join('\n')}`;
364
+ }
365
+ errorMessage += `\n\n**ACCORDING TO GLOSSARY DEFINITION:**
366
+ ${analysis.isCustomDefinition ? `- Extends: ${analysis.baseType}` : '- Primitive type'}
367
+ - Total fields in "fields" array: ${analysis.allFields.length}
368
+ - Total fields in "definedFields" array: ${analysis.allDefinedFields.length}`;
369
+ // Add widget information if present
370
+ if (analysis.widgetFields.size > 0) {
371
+ errorMessage += `\n- Widget types detected: ${analysis.widgetFields.size} fields with specific widgets`;
372
+ const widgetInfo = [];
373
+ for (const [fieldKey, widgetType] of analysis.widgetFields.entries()) {
374
+ if (widgetType === 'dateobject') {
375
+ widgetInfo.push(` • ${fieldKey}: dateobject widget (requires {hour, minute, day, month, year} format)`);
376
+ }
377
+ else {
378
+ widgetInfo.push(` • ${fieldKey}: ${widgetType} widget`);
379
+ }
380
+ }
381
+ if (widgetInfo.length > 0) {
382
+ errorMessage += `\n${widgetInfo.join('\n')}`;
383
+ }
384
+ }
385
+ errorMessage += `\n\n**ROOT LEVEL FIELDS (from glossary "fields" array):**
386
+ ${rootFieldsList || 'none defined'}
387
+
388
+ **DATA OBJECT FIELDS (from glossary "definedFields" array):**
389
+ ${dataFieldsList || 'none defined'}
390
+
391
+ **CORRECT STRUCTURE:**
392
+ \`\`\`json
393
+ {
394
+ "type": "${contentType}",
395
+ "title": "Your Title Here",`;
396
+ // Add sample root level fields with widget-aware examples
397
+ const rootFields = Array.from(analysis.rootLevelFields).filter(f => f !== 'title' && f !== '_id' && f !== 'meta');
398
+ if (rootFields.length > 0) {
399
+ rootFields.slice(0, 2).forEach(fieldKey => {
400
+ const widgetType = analysis.widgetFields.get(fieldKey);
401
+ let exampleValue = '"value"';
402
+ if (widgetType === 'dateobject') {
403
+ exampleValue = '{"hour": 14, "minute": 30, "day": 15, "month": 1, "year": 2025}';
404
+ }
405
+ else if (widgetType === 'date') {
406
+ exampleValue = '"2025-01-15"';
407
+ }
408
+ else if (widgetType === 'time') {
409
+ exampleValue = '"14:30"';
410
+ }
411
+ else if (widgetType === 'datetime') {
412
+ exampleValue = '"2025-01-15T14:30:00Z"';
413
+ }
414
+ errorMessage += `\n "${fieldKey}": ${exampleValue},`;
415
+ });
416
+ }
417
+ errorMessage += `\n "meta": { "scopes": ["scope-id-here"] }`;
418
+ // Add data object with widget-aware examples
419
+ if (analysis.dataObjectFields.size > 0) {
420
+ errorMessage += `,\n "data": {`;
421
+ const dataFields = Array.from(analysis.dataObjectFields).slice(0, 3);
422
+ dataFields.forEach((fieldKey, index) => {
423
+ const widgetType = analysis.widgetFields.get(fieldKey);
424
+ let exampleValue = '"value"';
425
+ if (widgetType === 'dateobject') {
426
+ exampleValue = '{"hour": 9, "minute": 0, "day": 22, "month": 3, "year": 2024}';
427
+ }
428
+ else if (widgetType === 'date') {
429
+ exampleValue = '"2024-03-22"';
430
+ }
431
+ else if (widgetType === 'time') {
432
+ exampleValue = '"09:00"';
433
+ }
434
+ else if (widgetType === 'datetime') {
435
+ exampleValue = '"2024-03-22T09:00:00Z"';
436
+ }
437
+ const comma = index < dataFields.length - 1 ? ',' : '';
438
+ errorMessage += `\n "${fieldKey}": ${exampleValue}${comma}`;
439
+ });
440
+ errorMessage += `\n }`;
441
+ }
442
+ errorMessage += `\n}
443
+ \`\`\`
444
+
445
+ **FIELD PLACEMENT RULE:**
446
+ - Fields in glossary "fields" array → ROOT LEVEL
447
+ - Fields in glossary "definedFields" array → DATA OBJECT`;
448
+ // Add widget-specific guidance
449
+ if (analysis.widgetFields.size > 0) {
450
+ errorMessage += `\n\n**WIDGET-SPECIFIC GUIDANCE:**\n`;
451
+ for (const [fieldKey, widgetType] of analysis.widgetFields.entries()) {
452
+ if (widgetType === 'dateobject') {
453
+ errorMessage += `- **${fieldKey}** (dateobject): Use format {"hour": 14, "minute": 30, "day": 15, "month": 1, "year": 2025}\n`;
454
+ }
455
+ else if (widgetType === 'date') {
456
+ errorMessage += `- **${fieldKey}** (date): Use ISO date format "2025-01-15"\n`;
457
+ }
458
+ else if (widgetType === 'time') {
459
+ errorMessage += `- **${fieldKey}** (time): Use time format "14:30"\n`;
460
+ }
461
+ else if (widgetType === 'datetime') {
462
+ errorMessage += `- **${fieldKey}** (datetime): Use ISO datetime format "2025-01-15T14:30:00Z"\n`;
463
+ }
464
+ }
465
+ }
466
+ return errorMessage;
467
+ }
468
+ /**
469
+ * Validates widget-specific field values
470
+ */
471
+ validateWidgetValue(fieldKey, value, widgetType) {
472
+ const errors = [];
473
+ const warnings = [];
474
+ switch (widgetType) {
475
+ case 'dateobject':
476
+ return this.validateDateObjectWidget(fieldKey, value);
477
+ case 'date':
478
+ if (typeof value === 'string') {
479
+ const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
480
+ if (!dateRegex.test(value)) {
481
+ errors.push(`Field "${fieldKey}" with date widget must use ISO date format (YYYY-MM-DD), got: ${value}`);
482
+ }
483
+ }
484
+ else {
485
+ errors.push(`Field "${fieldKey}" with date widget must be a string in ISO date format (YYYY-MM-DD)`);
486
+ }
487
+ break;
488
+ case 'time':
489
+ if (typeof value === 'string') {
490
+ const timeRegex = /^\d{2}:\d{2}(:\d{2})?$/;
491
+ if (!timeRegex.test(value)) {
492
+ errors.push(`Field "${fieldKey}" with time widget must use time format (HH:MM or HH:MM:SS), got: ${value}`);
493
+ }
494
+ }
495
+ else {
496
+ errors.push(`Field "${fieldKey}" with time widget must be a string in time format (HH:MM)`);
497
+ }
498
+ break;
499
+ case 'datetime':
500
+ if (typeof value === 'string') {
501
+ try {
502
+ new Date(value);
503
+ }
504
+ catch (e) {
505
+ errors.push(`Field "${fieldKey}" with datetime widget must be a valid ISO datetime string, got: ${value}`);
506
+ }
507
+ }
508
+ else {
509
+ errors.push(`Field "${fieldKey}" with datetime widget must be a string in ISO datetime format`);
510
+ }
511
+ break;
512
+ default:
513
+ // No specific validation for other widget types
514
+ break;
515
+ }
516
+ return { valid: errors.length === 0, errors, warnings };
517
+ }
518
+ /**
519
+ * Validates dateobject widget values
520
+ */
521
+ validateDateObjectWidget(fieldKey, value) {
522
+ const errors = [];
523
+ const warnings = [];
524
+ if (!value || typeof value !== 'object') {
525
+ errors.push(`Field "${fieldKey}" with dateobject widget must be an object with hour, minute, day, month, year properties`);
526
+ return { valid: false, errors, warnings };
527
+ }
528
+ const requiredProps = ['hour', 'minute', 'day', 'month', 'year'];
529
+ const missingProps = requiredProps.filter(prop => value[prop] === undefined || value[prop] === null);
530
+ if (missingProps.length > 0) {
531
+ errors.push(`Field "${fieldKey}" dateobject is missing required properties: ${missingProps.join(', ')}`);
532
+ }
533
+ // Validate ranges
534
+ if (typeof value.hour === 'number') {
535
+ if (value.hour < 0 || value.hour > 23) {
536
+ errors.push(`Field "${fieldKey}" dateobject hour must be between 0-23, got: ${value.hour}`);
537
+ }
538
+ }
539
+ else if (value.hour !== undefined) {
540
+ errors.push(`Field "${fieldKey}" dateobject hour must be a number`);
541
+ }
542
+ if (typeof value.minute === 'number') {
543
+ if (value.minute < 0 || value.minute > 59) {
544
+ errors.push(`Field "${fieldKey}" dateobject minute must be between 0-59, got: ${value.minute}`);
545
+ }
546
+ }
547
+ else if (value.minute !== undefined) {
548
+ errors.push(`Field "${fieldKey}" dateobject minute must be a number`);
549
+ }
550
+ if (typeof value.day === 'number') {
551
+ if (value.day < 1 || value.day > 31) {
552
+ errors.push(`Field "${fieldKey}" dateobject day must be between 1-31, got: ${value.day}`);
553
+ }
554
+ }
555
+ else if (value.day !== undefined) {
556
+ errors.push(`Field "${fieldKey}" dateobject day must be a number`);
557
+ }
558
+ if (typeof value.month === 'number') {
559
+ if (value.month < 1 || value.month > 12) {
560
+ errors.push(`Field "${fieldKey}" dateobject month must be between 1-12, got: ${value.month}`);
561
+ }
562
+ }
563
+ else if (value.month !== undefined) {
564
+ errors.push(`Field "${fieldKey}" dateobject month must be a number`);
565
+ }
566
+ if (typeof value.year === 'number') {
567
+ if (value.year < 1900 || value.year > 2100) {
568
+ warnings.push(`Field "${fieldKey}" dateobject year ${value.year} seems unusual (expected 1900-2100)`);
569
+ }
570
+ }
571
+ else if (value.year !== undefined) {
572
+ errors.push(`Field "${fieldKey}" dateobject year must be a number`);
573
+ }
574
+ return { valid: errors.length === 0, errors, warnings };
575
+ }
212
576
  generateFieldPath(field, isDefinedField, groupPath = '') {
213
577
  let basePath = isDefinedField ? 'data' : '';
214
578
  if (groupPath) {
@@ -1638,6 +2002,46 @@ Use \`qik_get_content_definition\` with any of the type keys above to see their
1638
2002
  required: ['firstName', 'lastName'],
1639
2003
  },
1640
2004
  },
2005
+ {
2006
+ name: 'qik_get_profile_timeline',
2007
+ description: this.generateToolDescription('Get a profile timeline with chronological activity data - provides much richer information than basic profile details including recent actions, content created, events attended, workflow activities, and other activity logs', '📅'),
2008
+ inputSchema: {
2009
+ type: 'object',
2010
+ properties: {
2011
+ id: {
2012
+ type: 'string',
2013
+ description: 'Profile ID to get timeline for',
2014
+ },
2015
+ },
2016
+ required: ['id'],
2017
+ },
2018
+ },
2019
+ {
2020
+ name: 'qik_get_profile_info',
2021
+ description: this.generateToolDescription('Intelligently get profile information with disambiguation between basic details and activity timeline. Asks clarifying questions when the request is ambiguous to provide the most relevant information', '🤔'),
2022
+ inputSchema: {
2023
+ type: 'object',
2024
+ properties: {
2025
+ query: {
2026
+ type: 'string',
2027
+ description: 'Natural language query about a person (e.g., "Tell me about Jeff", "What has John been up to?", "Get Sarah\'s contact info")',
2028
+ },
2029
+ profileId: {
2030
+ type: 'string',
2031
+ description: 'Profile ID if known (optional - can search by name if not provided)',
2032
+ },
2033
+ profileName: {
2034
+ type: 'string',
2035
+ description: 'Person\'s name to search for if ID not provided (optional)',
2036
+ },
2037
+ includeTimeline: {
2038
+ type: 'boolean',
2039
+ description: 'Whether to include timeline/activity data (optional - will be determined from query if not specified)',
2040
+ },
2041
+ },
2042
+ required: ['query'],
2043
+ },
2044
+ },
1641
2045
  // Form Management
1642
2046
  {
1643
2047
  name: 'qik_get_form',
@@ -1863,6 +2267,10 @@ Use \`qik_get_content_definition\` with any of the type keys above to see their
1863
2267
  return await this.getScopes();
1864
2268
  case 'qik_get_smartlist':
1865
2269
  return await this.getSmartlist(request.params.arguments);
2270
+ case 'qik_get_profile_timeline':
2271
+ return await this.getProfileTimeline(request.params.arguments);
2272
+ case 'qik_get_profile_info':
2273
+ return await this.getProfileInfo(request.params.arguments);
1866
2274
  case 'qik_create_content_intelligent':
1867
2275
  return await this.intelligentContentCreation(request.params.arguments.description, {
1868
2276
  title: request.params.arguments.title,
@@ -2124,164 +2532,55 @@ ${JSON.stringify({
2124
2532
  isError: true,
2125
2533
  };
2126
2534
  }
2127
- // Get content type info to understand field structure
2128
- const contentTypeInfo = validation.info;
2129
- let fields = [];
2130
- let definedFields = [];
2131
- let baseType = null;
2132
- // Extract fields and definedFields from the content type definition
2133
- if (contentTypeInfo.fields) {
2134
- fields = contentTypeInfo.fields;
2135
- definedFields = contentTypeInfo.definedFields || [];
2136
- baseType = contentTypeInfo.definesType;
2137
- }
2138
- else if (contentTypeInfo.definition) {
2139
- fields = contentTypeInfo.definition.fields || [];
2140
- definedFields = contentTypeInfo.definition.definedFields || [];
2141
- baseType = contentTypeInfo.definition.definesType;
2142
- }
2143
- else if (contentTypeInfo.type) {
2144
- fields = contentTypeInfo.type.fields || [];
2145
- definedFields = contentTypeInfo.type.definedFields || [];
2146
- baseType = contentTypeInfo.type.definesType;
2147
- }
2148
- // ENHANCED FIELD PLACEMENT VALIDATION
2149
- const fieldPlacementErrors = [];
2150
- const rootFieldKeys = new Set(fields.map(f => f.key));
2151
- const dataFieldKeys = new Set(definedFields.map(f => f.key));
2152
- // Check for common misplaced fields in data object
2153
- if (args.data && typeof args.data === 'object') {
2154
- const commonRootFields = ['title', 'plural', 'reference', 'referenceType', 'body', 'organisation'];
2155
- for (const fieldKey of commonRootFields) {
2156
- if (args.data[fieldKey] !== undefined) {
2157
- fieldPlacementErrors.push(`❌ FIELD PLACEMENT ERROR: "${fieldKey}" should be at ROOT LEVEL, not in data object`);
2158
- }
2159
- }
2160
- // Check for fields that should be at root level but are in data
2161
- for (const [key, value] of Object.entries(args.data)) {
2162
- if (rootFieldKeys.has(key)) {
2163
- fieldPlacementErrors.push(`❌ FIELD PLACEMENT ERROR: "${key}" is defined as a "field" in the glossary and should be at ROOT LEVEL, not in data object`);
2164
- }
2165
- }
2166
- }
2167
- // Check for fields that should be in data object but are at root level
2168
- for (const [key, value] of Object.entries(args)) {
2169
- if (key !== 'type' && key !== 'title' && key !== 'meta' && key !== 'data' &&
2170
- key !== 'reference' && key !== 'referenceType' && key !== 'body' && key !== 'organisation') {
2171
- if (dataFieldKeys.has(key)) {
2172
- fieldPlacementErrors.push(`❌ FIELD PLACEMENT ERROR: "${key}" is defined as a "definedField" in the glossary and should be in the DATA OBJECT, not at root level`);
2173
- }
2174
- }
2175
- }
2176
- // If there are field placement errors, return detailed guidance
2177
- if (fieldPlacementErrors.length > 0) {
2178
- const rootFieldsList = Array.from(rootFieldKeys).join(', ');
2179
- const dataFieldsList = Array.from(dataFieldKeys).join(', ');
2180
- return {
2181
- content: [{
2182
- type: 'text',
2183
- text: `🚫 **FIELD PLACEMENT ERRORS DETECTED**
2184
-
2185
- ${fieldPlacementErrors.join('\n')}
2186
-
2187
- **CORRECT FIELD PLACEMENT FOR ${args.type.toUpperCase()}:**
2188
-
2189
- **ROOT LEVEL FIELDS (never put in data object):**
2190
- - Standard fields: title, reference, referenceType, body, organisation, meta
2191
- - Content type "fields": ${rootFieldsList || 'none defined'}
2192
-
2193
- **DATA OBJECT FIELDS (always put in data object):**
2194
- - Content type "definedFields": ${dataFieldsList || 'none defined'}
2195
-
2196
- **CORRECT STRUCTURE EXAMPLE:**
2197
- \`\`\`json
2198
- {
2199
- "type": "${args.type}",
2200
- "title": "Your Title Here",
2201
- "reference": "optional-reference-id",
2202
- "referenceType": "optional-reference-type",
2203
- "meta": {
2204
- "scopes": ["scope-id-here"]
2205
- },
2206
- "data": {
2207
- ${Array.from(dataFieldKeys).map(field => `"${field}": "your-value-here"`).join(',\n ')}
2208
- }
2209
- }
2210
- \`\`\`
2211
-
2212
- **WHAT TO FIX:**
2213
- 1. Move any fields mentioned in the errors above to their correct location
2214
- 2. Use qik_get_content_definition with type "${args.type}" to see the complete field structure
2215
- 3. Remember: "fields" go at root level, "definedFields" go in data object`,
2216
- }],
2217
- isError: true,
2218
- };
2219
- }
2220
- // Structure payload correctly based on field definitions
2221
- const payload = {
2222
- title: args.title,
2223
- meta: args.meta || {},
2224
- };
2225
- // Separate fields into root level vs data object
2226
- const rootLevelData = {};
2227
- const dataObjectData = {};
2228
- // Handle direct field assignments from args (for fields like reference, referenceType, body)
2229
- const directFields = ['reference', 'referenceType', 'body', 'organisation'];
2230
- for (const fieldKey of directFields) {
2231
- if (args[fieldKey] !== undefined) {
2232
- rootLevelData[fieldKey] = args[fieldKey];
2535
+ // Use the new glossary-driven field placement validation
2536
+ try {
2537
+ const fieldValidation = this.validateFieldPlacementFromGlossary(args, args.type);
2538
+ // If there are field placement errors, return detailed guidance
2539
+ if (!fieldValidation.valid) {
2540
+ return {
2541
+ content: [{
2542
+ type: 'text',
2543
+ text: this.generateFieldPlacementErrorFromGlossary(args.type, fieldValidation.errors),
2544
+ }],
2545
+ isError: true,
2546
+ };
2233
2547
  }
2234
- }
2235
- if (args.data && typeof args.data === 'object') {
2236
- // Separate the provided data based on field definitions
2237
- for (const [key, value] of Object.entries(args.data)) {
2238
- if (rootFieldKeys.has(key)) {
2239
- // This field should go at root level (from 'fields')
2240
- rootLevelData[key] = value;
2241
- }
2242
- else if (dataFieldKeys.has(key)) {
2243
- // This field should go in data object (from 'definedFields')
2244
- dataObjectData[key] = value;
2548
+ // Automatically restructure the payload based on glossary rules
2549
+ const payload = this.restructurePayloadFromGlossary(args, args.type);
2550
+ // Handle scope inheritance for comment types
2551
+ const analysis = fieldValidation.analysis;
2552
+ const isCommentType = analysis.baseType === 'comment' || args.type === 'comment' || args.type.includes('Comment');
2553
+ if (isCommentType && payload.reference && (!payload.meta?.scopes || payload.meta.scopes.length === 0)) {
2554
+ try {
2555
+ // Fetch the referenced item to get its scopes
2556
+ const referencedItem = await this.axiosInstance.get(`/content/${payload.reference}`);
2557
+ if (referencedItem.data && referencedItem.data.meta && referencedItem.data.meta.scopes) {
2558
+ if (!payload.meta)
2559
+ payload.meta = {};
2560
+ payload.meta.scopes = referencedItem.data.meta.scopes;
2561
+ this.log(`Inherited scopes from referenced item ${payload.reference}: ${payload.meta.scopes.join(', ')}`);
2562
+ }
2245
2563
  }
2246
- else {
2247
- // Unknown field - put in data object as fallback
2248
- dataObjectData[key] = value;
2564
+ catch (error) {
2565
+ this.log(`Failed to fetch referenced item ${payload.reference} for scope inheritance: ${this.formatError(error)}`);
2566
+ // Continue without scope inheritance if we can't fetch the referenced item
2249
2567
  }
2250
2568
  }
2251
- }
2252
- // Add root level fields to payload
2253
- Object.assign(payload, rootLevelData);
2254
- // Add data object fields if any exist
2255
- if (Object.keys(dataObjectData).length > 0) {
2256
- payload.data = dataObjectData;
2257
- }
2258
- // Handle scope inheritance for comment types
2259
- const isCommentType = baseType === 'comment' || args.type === 'comment' || args.type.includes('Comment');
2260
- if (isCommentType && payload.reference && (!payload.meta.scopes || payload.meta.scopes.length === 0)) {
2261
- try {
2262
- // Fetch the referenced item to get its scopes
2263
- const referencedItem = await this.axiosInstance.get(`/content/${payload.reference}`);
2264
- if (referencedItem.data && referencedItem.data.meta && referencedItem.data.meta.scopes) {
2265
- payload.meta.scopes = referencedItem.data.meta.scopes;
2266
- this.log(`Inherited scopes from referenced item ${payload.reference}: ${payload.meta.scopes.join(', ')}`);
2267
- }
2268
- }
2269
- catch (error) {
2270
- this.log(`Failed to fetch referenced item ${payload.reference} for scope inheritance: ${this.formatError(error)}`);
2271
- // Continue without scope inheritance if we can't fetch the referenced item
2272
- }
2273
- }
2274
- try {
2275
2569
  const response = await this.axiosInstance.post(`/content/${args.type}/create`, payload);
2276
2570
  return {
2277
2571
  content: [{
2278
2572
  type: 'text',
2279
2573
  text: `✅ **SUCCESSFULLY CREATED ${args.type.toUpperCase()} CONTENT**
2280
2574
 
2281
- The content was created with proper field placement:
2575
+ The content was created using glossary-driven field placement:
2282
2576
  - Root level fields: ${Object.keys(payload).filter(k => k !== 'data' && k !== 'meta').join(', ')}
2283
2577
  - Data object fields: ${payload.data ? Object.keys(payload.data).join(', ') : 'none'}
2284
- - Meta fields: ${Object.keys(payload.meta).join(', ')}
2578
+ - Meta fields: ${payload.meta ? Object.keys(payload.meta).join(', ') : 'none'}
2579
+
2580
+ **Field Placement Analysis:**
2581
+ - Content type: ${analysis.isCustomDefinition ? `Custom definition extending ${analysis.baseType}` : 'Primitive type'}
2582
+ - Root level fields from glossary: ${Array.from(analysis.rootLevelFields).join(', ')}
2583
+ - Data object fields from glossary: ${Array.from(analysis.dataObjectFields).join(', ')}
2285
2584
 
2286
2585
  **Created Content:**
2287
2586
  ${JSON.stringify(response.data, null, 2)}`,
@@ -2391,6 +2690,180 @@ ${JSON.stringify(response.data, null, 2)}`,
2391
2690
  };
2392
2691
  }
2393
2692
  }
2693
+ async getProfileTimeline(args) {
2694
+ try {
2695
+ const response = await this.axiosInstance.get(`/profile/${args.id}/timeline`);
2696
+ return {
2697
+ content: [{
2698
+ type: 'text',
2699
+ text: `📅 **PROFILE TIMELINE FOR ${args.id}**
2700
+
2701
+ This timeline provides chronological activity data including recent actions, content created, events attended, workflow activities, and other activity logs.
2702
+
2703
+ **Timeline Data:**
2704
+ ${JSON.stringify(response.data, null, 2)}`,
2705
+ }],
2706
+ };
2707
+ }
2708
+ catch (error) {
2709
+ return {
2710
+ content: [{
2711
+ type: 'text',
2712
+ text: `Failed to get profile timeline for ${args.id}: ${this.formatError(error)}`,
2713
+ }],
2714
+ isError: true,
2715
+ };
2716
+ }
2717
+ }
2718
+ async getProfileInfo(args) {
2719
+ const normalizedQuery = args.query.toLowerCase().trim();
2720
+ // Analyze the query to determine intent
2721
+ const timelineKeywords = [
2722
+ 'activity', 'timeline', 'been up to', 'doing', 'recent', 'lately',
2723
+ 'actions', 'history', 'events', 'workflow', 'progress'
2724
+ ];
2725
+ const basicInfoKeywords = [
2726
+ 'contact', 'email', 'phone', 'details', 'information', 'about'
2727
+ ];
2728
+ const wantsTimeline = args.includeTimeline !== undefined
2729
+ ? args.includeTimeline
2730
+ : timelineKeywords.some(keyword => normalizedQuery.includes(keyword));
2731
+ const wantsBasicInfo = basicInfoKeywords.some(keyword => normalizedQuery.includes(keyword));
2732
+ // If the intent is ambiguous, ask for clarification
2733
+ if (!wantsTimeline && !wantsBasicInfo && args.includeTimeline === undefined) {
2734
+ const personName = args.profileName || (args.profileId ? `person (ID: ${args.profileId})` : 'this person');
2735
+ return {
2736
+ content: [{
2737
+ type: 'text',
2738
+ text: `🤔 **CLARIFICATION NEEDED**
2739
+
2740
+ You asked: "${args.query}"
2741
+
2742
+ I can provide different types of information about ${personName}:
2743
+
2744
+ **OPTION 1: Basic Profile Details** 📋
2745
+ - Contact information (email, phone)
2746
+ - Basic demographic data
2747
+ - Profile fields and custom data
2748
+ → Use: \`qik_get_content\` or \`qik_list_profiles\`
2749
+
2750
+ **OPTION 2: Activity Timeline** 📅
2751
+ - Recent actions and activities
2752
+ - Content they've created or been involved with
2753
+ - Workflow progress and events attended
2754
+ - Much richer, more colorful activity data
2755
+ → Use: \`qik_get_profile_timeline\`
2756
+
2757
+ **Which would be more helpful for your needs?**
2758
+
2759
+ To get both, you can:
2760
+ 1. First get basic profile info with \`qik_list_profiles\` (search by name)
2761
+ 2. Then get timeline data with \`qik_get_profile_timeline\` (using the profile ID)`,
2762
+ }],
2763
+ };
2764
+ }
2765
+ let profileId = args.profileId;
2766
+ // If no profile ID provided, try to find the profile by name
2767
+ if (!profileId && args.profileName) {
2768
+ try {
2769
+ const searchResponse = await this.axiosInstance.post('/content/profile/list', {
2770
+ search: args.profileName,
2771
+ page: { size: 5, index: 1 },
2772
+ });
2773
+ if (searchResponse.data.items && searchResponse.data.items.length > 0) {
2774
+ if (searchResponse.data.items.length === 1) {
2775
+ profileId = searchResponse.data.items[0]._id;
2776
+ }
2777
+ else {
2778
+ // Multiple matches found
2779
+ const matches = searchResponse.data.items.map((p) => `- **${p._id}**: ${p.firstName} ${p.lastName} (${p.emails?.[0] || 'no email'})`).join('\n');
2780
+ return {
2781
+ content: [{
2782
+ type: 'text',
2783
+ text: `🔍 **MULTIPLE PROFILES FOUND**
2784
+
2785
+ Found ${searchResponse.data.items.length} profiles matching "${args.profileName}":
2786
+
2787
+ ${matches}
2788
+
2789
+ Please specify which profile you want by using the profile ID with \`qik_get_profile_timeline\` or \`qik_get_content\`.`,
2790
+ }],
2791
+ };
2792
+ }
2793
+ }
2794
+ else {
2795
+ return {
2796
+ content: [{
2797
+ type: 'text',
2798
+ text: `❌ **PROFILE NOT FOUND**
2799
+
2800
+ No profiles found matching "${args.profileName}".
2801
+
2802
+ Try:
2803
+ - Using \`qik_list_profiles\` with a broader search
2804
+ - Checking the spelling of the name
2805
+ - Using the exact profile ID if you have it`,
2806
+ }],
2807
+ isError: true,
2808
+ };
2809
+ }
2810
+ }
2811
+ catch (error) {
2812
+ return {
2813
+ content: [{
2814
+ type: 'text',
2815
+ text: `Failed to search for profile "${args.profileName}": ${this.formatError(error)}`,
2816
+ }],
2817
+ isError: true,
2818
+ };
2819
+ }
2820
+ }
2821
+ // Now we have a profile ID, get the appropriate information
2822
+ if (wantsTimeline && profileId) {
2823
+ return await this.getProfileTimeline({ id: profileId });
2824
+ }
2825
+ else if (profileId) {
2826
+ // Get basic profile information
2827
+ try {
2828
+ const response = await this.axiosInstance.get(`/content/${profileId}`);
2829
+ return {
2830
+ content: [{
2831
+ type: 'text',
2832
+ text: `👤 **PROFILE INFORMATION**
2833
+
2834
+ **Basic Details:**
2835
+ ${JSON.stringify(response.data, null, 2)}
2836
+
2837
+ 💡 **Want more activity details?** Use \`qik_get_profile_timeline\` with ID: ${profileId} to see their recent activities, workflow progress, and timeline data.`,
2838
+ }],
2839
+ };
2840
+ }
2841
+ catch (error) {
2842
+ return {
2843
+ content: [{
2844
+ type: 'text',
2845
+ text: `Failed to get profile information for ${profileId}: ${this.formatError(error)}`,
2846
+ }],
2847
+ isError: true,
2848
+ };
2849
+ }
2850
+ }
2851
+ else {
2852
+ return {
2853
+ content: [{
2854
+ type: 'text',
2855
+ text: `❌ **MISSING PROFILE IDENTIFIER**
2856
+
2857
+ To get profile information, I need either:
2858
+ - **profileId**: The exact profile ID
2859
+ - **profileName**: The person's name to search for
2860
+
2861
+ Please provide one of these and try again.`,
2862
+ }],
2863
+ isError: true,
2864
+ };
2865
+ }
2866
+ }
2394
2867
  async getForm(args) {
2395
2868
  try {
2396
2869
  const response = await this.axiosInstance.get(`/form/${args.id}`);
@@ -2535,6 +3008,395 @@ ${JSON.stringify(response.data, null, 2)}`,
2535
3008
  };
2536
3009
  }
2537
3010
  }
3011
+ // Documentation-focused tool implementations
3012
+ async getDocumentation(args) {
3013
+ try {
3014
+ const documentation = QikDocumentationHelper.getTopicDocumentation(args.topic);
3015
+ if (!documentation) {
3016
+ return {
3017
+ content: [{
3018
+ type: 'text',
3019
+ text: `❌ **DOCUMENTATION NOT FOUND**
3020
+
3021
+ No documentation found for topic: "${args.topic}"
3022
+
3023
+ **Available topics:**
3024
+ - authentication (token types, methods, error codes)
3025
+ - endpoints (API endpoint documentation)
3026
+ - contentTypes (content type specific guidance)
3027
+ - filterSyntax (filter operators and comparators)
3028
+ - concepts (key concepts like field placement, scopes, workflows)
3029
+ - examples (code examples for common use cases)
3030
+ - troubleshooting (error resolution guides)
3031
+
3032
+ **Example usage:**
3033
+ - \`qik_get_documentation\` with topic: "concepts.field_placement"
3034
+ - \`qik_get_documentation\` with topic: "authentication.tokenTypes"
3035
+ - \`qik_get_documentation\` with topic: "troubleshooting.field_placement_errors"`,
3036
+ }],
3037
+ isError: true,
3038
+ };
3039
+ }
3040
+ let result = `📖 **DOCUMENTATION: ${args.topic.toUpperCase()}**\n\n`;
3041
+ if (args.subtopic && documentation[args.subtopic]) {
3042
+ result += `**Subtopic: ${args.subtopic}**\n\n`;
3043
+ result += JSON.stringify(documentation[args.subtopic], null, 2);
3044
+ }
3045
+ else {
3046
+ result += JSON.stringify(documentation, null, 2);
3047
+ }
3048
+ return {
3049
+ content: [{
3050
+ type: 'text',
3051
+ text: result,
3052
+ }],
3053
+ };
3054
+ }
3055
+ catch (error) {
3056
+ return {
3057
+ content: [{
3058
+ type: 'text',
3059
+ text: `Failed to get documentation for ${args.topic}: ${this.formatError(error)}`,
3060
+ }],
3061
+ isError: true,
3062
+ };
3063
+ }
3064
+ }
3065
+ async searchDocumentation(args) {
3066
+ try {
3067
+ const results = QikDocumentationHelper.searchDocumentation(args.query);
3068
+ const limit = args.limit || 10;
3069
+ const limitedResults = results.slice(0, limit);
3070
+ if (limitedResults.length === 0) {
3071
+ return {
3072
+ content: [{
3073
+ type: 'text',
3074
+ text: `🔍 **NO DOCUMENTATION FOUND**
3075
+
3076
+ No documentation found for query: "${args.query}"
3077
+
3078
+ **Try searching for:**
3079
+ - "field placement" - for field structure guidance
3080
+ - "authentication" - for token and auth help
3081
+ - "filter" - for filtering and query syntax
3082
+ - "workflow" - for workflow system concepts
3083
+ - "birthday" - for anniversary/birthday queries
3084
+ - "scope" - for permission and scope guidance
3085
+ - "troubleshooting" - for error resolution
3086
+
3087
+ **Available documentation sections:**
3088
+ - Authentication & Tokens
3089
+ - API Endpoints
3090
+ - Content Types
3091
+ - Filter Syntax
3092
+ - Core Concepts
3093
+ - Code Examples
3094
+ - Troubleshooting Guides`,
3095
+ }],
3096
+ };
3097
+ }
3098
+ let result = `🔍 **DOCUMENTATION SEARCH RESULTS**\n\n`;
3099
+ result += `Found ${limitedResults.length} results for "${args.query}":\n\n`;
3100
+ limitedResults.forEach((item, index) => {
3101
+ result += `**${index + 1}. ${item.path}** (${item.type})\n`;
3102
+ if (typeof item.content === 'string') {
3103
+ const preview = item.content.length > 200
3104
+ ? item.content.substring(0, 200) + '...'
3105
+ : item.content;
3106
+ result += `${preview}\n\n`;
3107
+ }
3108
+ else {
3109
+ result += `${JSON.stringify(item.content).substring(0, 200)}...\n\n`;
3110
+ }
3111
+ });
3112
+ result += `💡 **Need more specific help?** Use \`qik_get_documentation\` with a specific topic for detailed information.`;
3113
+ return {
3114
+ content: [{
3115
+ type: 'text',
3116
+ text: result,
3117
+ }],
3118
+ };
3119
+ }
3120
+ catch (error) {
3121
+ return {
3122
+ content: [{
3123
+ type: 'text',
3124
+ text: `Failed to search documentation: ${this.formatError(error)}`,
3125
+ }],
3126
+ isError: true,
3127
+ };
3128
+ }
3129
+ }
3130
+ async getTroubleshooting(args) {
3131
+ try {
3132
+ let troubleshootingInfo;
3133
+ if (args.issueType) {
3134
+ // Get specific issue type
3135
+ troubleshootingInfo = QIK_DOCUMENTATION.troubleshooting[args.issueType];
3136
+ if (troubleshootingInfo) {
3137
+ troubleshootingInfo = { issue: args.issueType, ...troubleshootingInfo };
3138
+ }
3139
+ }
3140
+ else {
3141
+ // Auto-detect issue type from error message
3142
+ troubleshootingInfo = QikDocumentationHelper.getTroubleshootingInfo(args.errorMessage);
3143
+ }
3144
+ if (!troubleshootingInfo) {
3145
+ return {
3146
+ content: [{
3147
+ type: 'text',
3148
+ text: `🔧 **NO SPECIFIC TROUBLESHOOTING FOUND**
3149
+
3150
+ I couldn't find specific troubleshooting information for: "${args.errorMessage}"
3151
+
3152
+ **Available troubleshooting categories:**
3153
+ - **field_placement_errors**: Field structure and placement issues
3154
+ - **authentication_failures**: Token and permission problems
3155
+ - **filter_syntax_errors**: Query and filter syntax issues
3156
+ - **scope_permission_issues**: Access and permission problems
3157
+ - **workflow_confusion**: Workflow definition vs card confusion
3158
+ - **date_handling_issues**: Date format and timezone problems
3159
+ - **performance_issues**: Slow queries and optimization
3160
+
3161
+ **General troubleshooting steps:**
3162
+ 1. Check the error message for specific field names or codes
3163
+ 2. Verify your access token and permissions
3164
+ 3. Ensure proper field placement (root vs data object)
3165
+ 4. Validate filter syntax and comparators
3166
+ 5. Check scope permissions and hierarchy
3167
+
3168
+ **Need more help?** Try:
3169
+ - \`qik_search_documentation\` with keywords from your error
3170
+ - \`qik_get_documentation\` with topic: "troubleshooting"
3171
+ - \`qik_validate_field_placement\` for content creation issues`,
3172
+ }],
3173
+ };
3174
+ }
3175
+ let result = `🔧 **TROUBLESHOOTING: ${troubleshootingInfo.issue.toUpperCase().replace(/_/g, ' ')}**\n\n`;
3176
+ result += `**Your Error:** "${args.errorMessage}"\n\n`;
3177
+ if (troubleshootingInfo.symptoms && troubleshootingInfo.symptoms.length > 0) {
3178
+ result += `**Common Symptoms:**\n`;
3179
+ result += troubleshootingInfo.symptoms.map((s) => `- ${s}`).join('\n');
3180
+ result += '\n\n';
3181
+ }
3182
+ if (troubleshootingInfo.causes && troubleshootingInfo.causes.length > 0) {
3183
+ result += `**Likely Causes:**\n`;
3184
+ result += troubleshootingInfo.causes.map((c) => `- ${c}`).join('\n');
3185
+ result += '\n\n';
3186
+ }
3187
+ if (troubleshootingInfo.solutions && troubleshootingInfo.solutions.length > 0) {
3188
+ result += `**Solutions:**\n`;
3189
+ result += troubleshootingInfo.solutions.map((s) => `- ${s}`).join('\n');
3190
+ result += '\n\n';
3191
+ }
3192
+ if (troubleshootingInfo.prevention && troubleshootingInfo.prevention.length > 0) {
3193
+ result += `**Prevention:**\n`;
3194
+ result += troubleshootingInfo.prevention.map((p) => `- ${p}`).join('\n');
3195
+ result += '\n\n';
3196
+ }
3197
+ if (troubleshootingInfo.relatedIssues && troubleshootingInfo.relatedIssues.length > 0) {
3198
+ result += `**Related Issues:**\n`;
3199
+ result += troubleshootingInfo.relatedIssues.map((r) => `- ${r}`).join('\n');
3200
+ }
3201
+ return {
3202
+ content: [{
3203
+ type: 'text',
3204
+ text: result,
3205
+ }],
3206
+ };
3207
+ }
3208
+ catch (error) {
3209
+ return {
3210
+ content: [{
3211
+ type: 'text',
3212
+ text: `Failed to get troubleshooting information: ${this.formatError(error)}`,
3213
+ }],
3214
+ isError: true,
3215
+ };
3216
+ }
3217
+ }
3218
+ async getExamples(args) {
3219
+ try {
3220
+ if (args.category === 'all') {
3221
+ let result = `💡 **ALL CODE EXAMPLES**\n\n`;
3222
+ for (const [category, examples] of Object.entries(QIK_DOCUMENTATION.examples)) {
3223
+ result += `## ${category.toUpperCase()}\n\n`;
3224
+ for (const [exampleKey, example] of Object.entries(examples)) {
3225
+ const typedExample = example;
3226
+ result += `### ${typedExample.title}\n`;
3227
+ result += `${typedExample.description}\n\n`;
3228
+ result += `\`\`\`json\n${JSON.stringify(typedExample.code, null, 2)}\n\`\`\`\n\n`;
3229
+ result += `**Explanation:** ${typedExample.explanation}\n\n`;
3230
+ if (typedExample.variations && typedExample.variations.length > 0) {
3231
+ result += `**Variations:**\n`;
3232
+ typedExample.variations.forEach((variation) => {
3233
+ result += `- ${variation.description}\n`;
3234
+ result += ` \`\`\`json\n ${JSON.stringify(variation.code, null, 2)}\n \`\`\`\n`;
3235
+ });
3236
+ result += '\n';
3237
+ }
3238
+ }
3239
+ }
3240
+ return {
3241
+ content: [{
3242
+ type: 'text',
3243
+ text: result,
3244
+ }],
3245
+ };
3246
+ }
3247
+ const examples = QikDocumentationHelper.getExamples(args.category, args.example);
3248
+ if (!examples) {
3249
+ return {
3250
+ content: [{
3251
+ type: 'text',
3252
+ text: `💡 **NO EXAMPLES FOUND**
3253
+
3254
+ No examples found for category: "${args.category}"
3255
+
3256
+ **Available example categories:**
3257
+ - **authentication**: Token usage and auth examples
3258
+ - **content_creation**: Creating different content types
3259
+ - **filtering**: Advanced filtering and search examples
3260
+
3261
+ **Example usage:**
3262
+ - \`qik_get_examples\` with category: "authentication"
3263
+ - \`qik_get_examples\` with category: "filtering" and example: "birthday_search"
3264
+ - \`qik_get_examples\` with category: "all" (shows all examples)`,
3265
+ }],
3266
+ isError: true,
3267
+ };
3268
+ }
3269
+ let result = `💡 **CODE EXAMPLES: ${args.category.toUpperCase()}**\n\n`;
3270
+ if (args.example && examples[args.example]) {
3271
+ // Show specific example
3272
+ const example = examples[args.example];
3273
+ result += `### ${example.title}\n`;
3274
+ result += `${example.description}\n\n`;
3275
+ result += `\`\`\`json\n${JSON.stringify(example.code, null, 2)}\n\`\`\`\n\n`;
3276
+ result += `**Explanation:** ${example.explanation}\n\n`;
3277
+ if (example.variations && example.variations.length > 0) {
3278
+ result += `**Variations:**\n`;
3279
+ example.variations.forEach((variation) => {
3280
+ result += `- ${variation.description}\n`;
3281
+ result += ` \`\`\`json\n ${JSON.stringify(variation.code, null, 2)}\n \`\`\`\n`;
3282
+ });
3283
+ }
3284
+ }
3285
+ else {
3286
+ // Show all examples in category
3287
+ for (const [exampleKey, example] of Object.entries(examples)) {
3288
+ const typedExample = example;
3289
+ result += `### ${typedExample.title}\n`;
3290
+ result += `${typedExample.description}\n\n`;
3291
+ result += `\`\`\`json\n${JSON.stringify(typedExample.code, null, 2)}\n\`\`\`\n\n`;
3292
+ result += `**Explanation:** ${typedExample.explanation}\n\n`;
3293
+ if (typedExample.variations && typedExample.variations.length > 0) {
3294
+ result += `**Variations:**\n`;
3295
+ typedExample.variations.forEach((variation) => {
3296
+ result += `- ${variation.description}\n`;
3297
+ result += ` \`\`\`json\n ${JSON.stringify(variation.code, null, 2)}\n \`\`\`\n`;
3298
+ });
3299
+ result += '\n';
3300
+ }
3301
+ }
3302
+ }
3303
+ return {
3304
+ content: [{
3305
+ type: 'text',
3306
+ text: result,
3307
+ }],
3308
+ };
3309
+ }
3310
+ catch (error) {
3311
+ return {
3312
+ content: [{
3313
+ type: 'text',
3314
+ text: `Failed to get examples: ${this.formatError(error)}`,
3315
+ }],
3316
+ isError: true,
3317
+ };
3318
+ }
3319
+ }
3320
+ async validateFieldPlacement(args) {
3321
+ try {
3322
+ const validation = this.validateContentType(args.contentType);
3323
+ if (!validation.valid) {
3324
+ return {
3325
+ content: [{
3326
+ type: 'text',
3327
+ text: validation.error,
3328
+ }],
3329
+ isError: true,
3330
+ };
3331
+ }
3332
+ // Use both the documentation helper and glossary-driven validation
3333
+ const docValidation = QikDocumentationHelper.validateFieldPlacement(args.contentType, args.payload);
3334
+ const glossaryValidation = this.validateFieldPlacementFromGlossary(args.payload, args.contentType);
3335
+ let result = `✅ **FIELD PLACEMENT VALIDATION: ${args.contentType.toUpperCase()}**\n\n`;
3336
+ if (glossaryValidation.valid && docValidation.valid) {
3337
+ result += `🎉 **VALIDATION PASSED**\n\n`;
3338
+ result += `Your payload has correct field placement!\n\n`;
3339
+ const analysis = glossaryValidation.analysis;
3340
+ result += `**Field Analysis:**\n`;
3341
+ result += `- Root level fields: ${Object.keys(args.payload).filter(k => k !== 'data' && k !== 'meta').join(', ') || 'none'}\n`;
3342
+ result += `- Data object fields: ${args.payload.data ? Object.keys(args.payload.data).join(', ') : 'none'}\n`;
3343
+ result += `- Meta fields: ${args.payload.meta ? Object.keys(args.payload.meta).join(', ') : 'none'}\n\n`;
3344
+ result += `**Glossary Structure:**\n`;
3345
+ result += `- Expected root fields: ${Array.from(analysis.rootLevelFields).join(', ') || 'none'}\n`;
3346
+ result += `- Expected data fields: ${Array.from(analysis.dataObjectFields).join(', ') || 'none'}\n`;
3347
+ }
3348
+ else {
3349
+ result += `❌ **VALIDATION FAILED**\n\n`;
3350
+ const allErrors = [...(docValidation.errors || []), ...(glossaryValidation.errors || [])];
3351
+ if (allErrors.length > 0) {
3352
+ result += `**Errors Found:**\n`;
3353
+ allErrors.forEach(error => {
3354
+ result += `- ${error}\n`;
3355
+ });
3356
+ result += '\n';
3357
+ }
3358
+ if (glossaryValidation.analysis) {
3359
+ const analysis = glossaryValidation.analysis;
3360
+ result += `**Correct Structure:**\n`;
3361
+ result += `\`\`\`json\n`;
3362
+ result += `{\n`;
3363
+ result += ` "type": "${args.contentType}",\n`;
3364
+ result += ` "title": "Your Title Here",\n`;
3365
+ const rootFields = Array.from(analysis.rootLevelFields).filter(f => f !== 'title' && f !== '_id' && f !== 'meta');
3366
+ if (rootFields.length > 0) {
3367
+ rootFields.slice(0, 2).forEach(field => {
3368
+ result += ` "${field}": "value",\n`;
3369
+ });
3370
+ }
3371
+ result += ` "meta": { "scopes": ["scope-id-here"] }`;
3372
+ if (analysis.dataObjectFields.size > 0) {
3373
+ result += `,\n "data": {\n`;
3374
+ Array.from(analysis.dataObjectFields).slice(0, 3).forEach((field, index, arr) => {
3375
+ result += ` "${field}": "value"${index < arr.length - 1 ? ',' : ''}\n`;
3376
+ });
3377
+ result += ` }`;
3378
+ }
3379
+ result += `\n}\n\`\`\`\n`;
3380
+ }
3381
+ }
3382
+ return {
3383
+ content: [{
3384
+ type: 'text',
3385
+ text: result,
3386
+ }],
3387
+ isError: !glossaryValidation.valid || !docValidation.valid,
3388
+ };
3389
+ }
3390
+ catch (error) {
3391
+ return {
3392
+ content: [{
3393
+ type: 'text',
3394
+ text: `Failed to validate field placement: ${this.formatError(error)}`,
3395
+ }],
3396
+ isError: true,
3397
+ };
3398
+ }
3399
+ }
2538
3400
  async run() {
2539
3401
  const transport = new StdioServerTransport();
2540
3402
  await this.server.connect(transport);