@qikdev/mcp 6.6.6 → 6.6.9
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/build/src/documentation.d.ts +193 -0
- package/build/src/documentation.d.ts.map +1 -0
- package/build/src/documentation.js +1200 -0
- package/build/src/documentation.js.map +1 -0
- package/build/src/index.d.ts +19 -0
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +796 -194
- package/build/src/index.js.map +1 -1
- package/package.json +1 -1
package/build/src/index.js
CHANGED
|
@@ -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
|
-
//
|
|
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,114 @@ export class QikMCPServer {
|
|
|
209
210
|
getMinimumArrayLength(field) {
|
|
210
211
|
return this.isFieldArray(field) ? (field.minimum || 0) : 0;
|
|
211
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Validates widget-specific field values
|
|
215
|
+
*/
|
|
216
|
+
validateWidgetValue(fieldKey, value, widgetType) {
|
|
217
|
+
const errors = [];
|
|
218
|
+
const warnings = [];
|
|
219
|
+
switch (widgetType) {
|
|
220
|
+
case 'dateobject':
|
|
221
|
+
return this.validateDateObjectWidget(fieldKey, value);
|
|
222
|
+
case 'date':
|
|
223
|
+
if (typeof value === 'string') {
|
|
224
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
225
|
+
if (!dateRegex.test(value)) {
|
|
226
|
+
errors.push(`Field "${fieldKey}" with date widget must use ISO date format (YYYY-MM-DD), got: ${value}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
errors.push(`Field "${fieldKey}" with date widget must be a string in ISO date format (YYYY-MM-DD)`);
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
case 'time':
|
|
234
|
+
if (typeof value === 'string') {
|
|
235
|
+
const timeRegex = /^\d{2}:\d{2}(:\d{2})?$/;
|
|
236
|
+
if (!timeRegex.test(value)) {
|
|
237
|
+
errors.push(`Field "${fieldKey}" with time widget must use time format (HH:MM or HH:MM:SS), got: ${value}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
errors.push(`Field "${fieldKey}" with time widget must be a string in time format (HH:MM)`);
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
case 'datetime':
|
|
245
|
+
if (typeof value === 'string') {
|
|
246
|
+
try {
|
|
247
|
+
new Date(value);
|
|
248
|
+
}
|
|
249
|
+
catch (e) {
|
|
250
|
+
errors.push(`Field "${fieldKey}" with datetime widget must be a valid ISO datetime string, got: ${value}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
errors.push(`Field "${fieldKey}" with datetime widget must be a string in ISO datetime format`);
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
default:
|
|
258
|
+
// No specific validation for other widget types
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Validates dateobject widget values
|
|
265
|
+
*/
|
|
266
|
+
validateDateObjectWidget(fieldKey, value) {
|
|
267
|
+
const errors = [];
|
|
268
|
+
const warnings = [];
|
|
269
|
+
if (!value || typeof value !== 'object') {
|
|
270
|
+
errors.push(`Field "${fieldKey}" with dateobject widget must be an object with hour, minute, day, month, year properties`);
|
|
271
|
+
return { valid: false, errors, warnings };
|
|
272
|
+
}
|
|
273
|
+
const requiredProps = ['hour', 'minute', 'day', 'month', 'year'];
|
|
274
|
+
const missingProps = requiredProps.filter(prop => value[prop] === undefined || value[prop] === null);
|
|
275
|
+
if (missingProps.length > 0) {
|
|
276
|
+
errors.push(`Field "${fieldKey}" dateobject is missing required properties: ${missingProps.join(', ')}`);
|
|
277
|
+
}
|
|
278
|
+
// Validate ranges
|
|
279
|
+
if (typeof value.hour === 'number') {
|
|
280
|
+
if (value.hour < 0 || value.hour > 23) {
|
|
281
|
+
errors.push(`Field "${fieldKey}" dateobject hour must be between 0-23, got: ${value.hour}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
else if (value.hour !== undefined) {
|
|
285
|
+
errors.push(`Field "${fieldKey}" dateobject hour must be a number`);
|
|
286
|
+
}
|
|
287
|
+
if (typeof value.minute === 'number') {
|
|
288
|
+
if (value.minute < 0 || value.minute > 59) {
|
|
289
|
+
errors.push(`Field "${fieldKey}" dateobject minute must be between 0-59, got: ${value.minute}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else if (value.minute !== undefined) {
|
|
293
|
+
errors.push(`Field "${fieldKey}" dateobject minute must be a number`);
|
|
294
|
+
}
|
|
295
|
+
if (typeof value.day === 'number') {
|
|
296
|
+
if (value.day < 1 || value.day > 31) {
|
|
297
|
+
errors.push(`Field "${fieldKey}" dateobject day must be between 1-31, got: ${value.day}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else if (value.day !== undefined) {
|
|
301
|
+
errors.push(`Field "${fieldKey}" dateobject day must be a number`);
|
|
302
|
+
}
|
|
303
|
+
if (typeof value.month === 'number') {
|
|
304
|
+
if (value.month < 1 || value.month > 12) {
|
|
305
|
+
errors.push(`Field "${fieldKey}" dateobject month must be between 1-12, got: ${value.month}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
else if (value.month !== undefined) {
|
|
309
|
+
errors.push(`Field "${fieldKey}" dateobject month must be a number`);
|
|
310
|
+
}
|
|
311
|
+
if (typeof value.year === 'number') {
|
|
312
|
+
if (value.year < 1900 || value.year > 2100) {
|
|
313
|
+
warnings.push(`Field "${fieldKey}" dateobject year ${value.year} seems unusual (expected 1900-2100)`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else if (value.year !== undefined) {
|
|
317
|
+
errors.push(`Field "${fieldKey}" dateobject year must be a number`);
|
|
318
|
+
}
|
|
319
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
320
|
+
}
|
|
212
321
|
generateFieldPath(field, isDefinedField, groupPath = '') {
|
|
213
322
|
let basePath = isDefinedField ? 'data' : '';
|
|
214
323
|
if (groupPath) {
|
|
@@ -363,57 +472,83 @@ export class QikMCPServer {
|
|
|
363
472
|
return 'string';
|
|
364
473
|
}
|
|
365
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Generates completely dynamic properties based on the loaded glossary
|
|
477
|
+
* This creates schema properties for ALL fields from ALL content types
|
|
478
|
+
*/
|
|
366
479
|
generateDynamicContentProperties() {
|
|
367
|
-
// Generate dynamic properties that show field structure for common content types
|
|
368
480
|
const properties = {};
|
|
369
|
-
//
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
481
|
+
// Collect all unique fields from all content types in the glossary
|
|
482
|
+
const allRootFields = new Set();
|
|
483
|
+
const allDataFields = new Set();
|
|
484
|
+
const fieldSchemas = new Map();
|
|
485
|
+
// Process each content type in the glossary
|
|
486
|
+
for (const [contentTypeKey, contentType] of Object.entries(this.glossary)) {
|
|
487
|
+
if (!contentType || typeof contentType !== 'object')
|
|
488
|
+
continue;
|
|
489
|
+
// Get fields from the content type (these go at root level)
|
|
490
|
+
const fields = contentType.fields || [];
|
|
491
|
+
for (const field of fields) {
|
|
492
|
+
if (field.key && field.key !== '_id') { // Skip internal ID field
|
|
493
|
+
allRootFields.add(field.key);
|
|
494
|
+
// Generate schema for this field
|
|
495
|
+
if (!fieldSchemas.has(field.key)) {
|
|
496
|
+
fieldSchemas.set(field.key, this.generateFieldSchema(field));
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// Get definedFields from the content type (these go in data object)
|
|
501
|
+
const definedFields = contentType.definedFields || [];
|
|
502
|
+
for (const field of definedFields) {
|
|
503
|
+
if (field.key) {
|
|
504
|
+
allDataFields.add(field.key);
|
|
505
|
+
// Generate schema for this field
|
|
506
|
+
if (!fieldSchemas.has(field.key)) {
|
|
507
|
+
fieldSchemas.set(field.key, this.generateFieldSchema(field));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
376
511
|
}
|
|
377
|
-
// Add
|
|
512
|
+
// Add all root-level fields as properties
|
|
513
|
+
for (const fieldKey of allRootFields) {
|
|
514
|
+
if (fieldKey !== 'title' && fieldKey !== 'meta') { // These are handled separately
|
|
515
|
+
const schema = fieldSchemas.get(fieldKey);
|
|
516
|
+
if (schema) {
|
|
517
|
+
properties[fieldKey] = {
|
|
518
|
+
...schema,
|
|
519
|
+
description: `${schema.description || fieldKey} (ROOT LEVEL field from glossary)`
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// Create comprehensive data object with all possible data fields
|
|
525
|
+
const dataProperties = {};
|
|
526
|
+
for (const fieldKey of allDataFields) {
|
|
527
|
+
const schema = fieldSchemas.get(fieldKey);
|
|
528
|
+
if (schema) {
|
|
529
|
+
dataProperties[fieldKey] = {
|
|
530
|
+
...schema,
|
|
531
|
+
description: `${schema.description || fieldKey} (DATA OBJECT field from glossary)`
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
// Add the data object with dynamic properties
|
|
378
536
|
properties.data = {
|
|
379
537
|
type: 'object',
|
|
380
|
-
description: `
|
|
538
|
+
description: `DYNAMIC FIELD PLACEMENT (based on glossary):
|
|
381
539
|
|
|
382
|
-
**ROOT LEVEL FIELDS (
|
|
383
|
-
|
|
384
|
-
- These are "fields" in the glossary definition
|
|
540
|
+
**ROOT LEVEL FIELDS (from glossary "fields" array):**
|
|
541
|
+
${Array.from(allRootFields).sort().join(', ')}
|
|
385
542
|
|
|
386
|
-
**DATA OBJECT FIELDS (
|
|
387
|
-
|
|
388
|
-
- Content-type specific fields like wasAnyoneHurt, customField1, etc.
|
|
543
|
+
**DATA OBJECT FIELDS (from glossary "definedFields" array):**
|
|
544
|
+
${Array.from(allDataFields).sort().join(', ')}
|
|
389
545
|
|
|
390
|
-
**
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
"title": "My Title", // ❌ WRONG - title goes at root level
|
|
394
|
-
"plural": "My Items", // ❌ WRONG - plural goes at root level
|
|
395
|
-
"customField": "value"
|
|
396
|
-
}
|
|
397
|
-
}
|
|
546
|
+
**FIELD PLACEMENT RULE:**
|
|
547
|
+
- Fields in glossary "fields" array → ROOT LEVEL
|
|
548
|
+
- Fields in glossary "definedFields" array → DATA OBJECT
|
|
398
549
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
"title": "My Title", // ✅ CORRECT - at root level
|
|
402
|
-
"data": {
|
|
403
|
-
"customField": "value" // ✅ CORRECT - custom fields in data object
|
|
404
|
-
}
|
|
405
|
-
}`,
|
|
406
|
-
properties: {
|
|
407
|
-
wasAnyoneHurt: {
|
|
408
|
-
type: 'string',
|
|
409
|
-
description: 'For incident reports - whether anyone was hurt (this is a definedField, so it goes in data object)',
|
|
410
|
-
enum: ['yes', 'no']
|
|
411
|
-
},
|
|
412
|
-
whatTimeDidItHappen: {
|
|
413
|
-
type: 'string',
|
|
414
|
-
description: 'For incident reports - when the incident occurred (this is a definedField, so it goes in data object)'
|
|
415
|
-
}
|
|
416
|
-
}
|
|
550
|
+
The MCP server automatically determines correct placement based on the glossary definition for each content type.`,
|
|
551
|
+
properties: dataProperties
|
|
417
552
|
};
|
|
418
553
|
return properties;
|
|
419
554
|
}
|
|
@@ -1638,6 +1773,46 @@ Use \`qik_get_content_definition\` with any of the type keys above to see their
|
|
|
1638
1773
|
required: ['firstName', 'lastName'],
|
|
1639
1774
|
},
|
|
1640
1775
|
},
|
|
1776
|
+
{
|
|
1777
|
+
name: 'qik_get_profile_timeline',
|
|
1778
|
+
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', '📅'),
|
|
1779
|
+
inputSchema: {
|
|
1780
|
+
type: 'object',
|
|
1781
|
+
properties: {
|
|
1782
|
+
id: {
|
|
1783
|
+
type: 'string',
|
|
1784
|
+
description: 'Profile ID to get timeline for',
|
|
1785
|
+
},
|
|
1786
|
+
},
|
|
1787
|
+
required: ['id'],
|
|
1788
|
+
},
|
|
1789
|
+
},
|
|
1790
|
+
{
|
|
1791
|
+
name: 'qik_get_profile_info',
|
|
1792
|
+
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', '🤔'),
|
|
1793
|
+
inputSchema: {
|
|
1794
|
+
type: 'object',
|
|
1795
|
+
properties: {
|
|
1796
|
+
query: {
|
|
1797
|
+
type: 'string',
|
|
1798
|
+
description: 'Natural language query about a person (e.g., "Tell me about Jeff", "What has John been up to?", "Get Sarah\'s contact info")',
|
|
1799
|
+
},
|
|
1800
|
+
profileId: {
|
|
1801
|
+
type: 'string',
|
|
1802
|
+
description: 'Profile ID if known (optional - can search by name if not provided)',
|
|
1803
|
+
},
|
|
1804
|
+
profileName: {
|
|
1805
|
+
type: 'string',
|
|
1806
|
+
description: 'Person\'s name to search for if ID not provided (optional)',
|
|
1807
|
+
},
|
|
1808
|
+
includeTimeline: {
|
|
1809
|
+
type: 'boolean',
|
|
1810
|
+
description: 'Whether to include timeline/activity data (optional - will be determined from query if not specified)',
|
|
1811
|
+
},
|
|
1812
|
+
},
|
|
1813
|
+
required: ['query'],
|
|
1814
|
+
},
|
|
1815
|
+
},
|
|
1641
1816
|
// Form Management
|
|
1642
1817
|
{
|
|
1643
1818
|
name: 'qik_get_form',
|
|
@@ -1863,6 +2038,10 @@ Use \`qik_get_content_definition\` with any of the type keys above to see their
|
|
|
1863
2038
|
return await this.getScopes();
|
|
1864
2039
|
case 'qik_get_smartlist':
|
|
1865
2040
|
return await this.getSmartlist(request.params.arguments);
|
|
2041
|
+
case 'qik_get_profile_timeline':
|
|
2042
|
+
return await this.getProfileTimeline(request.params.arguments);
|
|
2043
|
+
case 'qik_get_profile_info':
|
|
2044
|
+
return await this.getProfileInfo(request.params.arguments);
|
|
1866
2045
|
case 'qik_create_content_intelligent':
|
|
1867
2046
|
return await this.intelligentContentCreation(request.params.arguments.description, {
|
|
1868
2047
|
title: request.params.arguments.title,
|
|
@@ -2124,175 +2303,50 @@ ${JSON.stringify({
|
|
|
2124
2303
|
isError: true,
|
|
2125
2304
|
};
|
|
2126
2305
|
}
|
|
2127
|
-
//
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
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];
|
|
2233
|
-
}
|
|
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;
|
|
2306
|
+
// Simplified approach - send data as provided and let server handle validation
|
|
2307
|
+
try {
|
|
2308
|
+
// Create payload from args as-is
|
|
2309
|
+
const payload = { ...args };
|
|
2310
|
+
// Handle scope inheritance for comment types
|
|
2311
|
+
const isCommentType = args.type === 'comment' || args.type.includes('Comment');
|
|
2312
|
+
if (isCommentType && payload.reference && (!payload.meta?.scopes || payload.meta.scopes.length === 0)) {
|
|
2313
|
+
try {
|
|
2314
|
+
// Fetch the referenced item to get its scopes
|
|
2315
|
+
const referencedItem = await this.axiosInstance.get(`/content/${payload.reference}`);
|
|
2316
|
+
if (referencedItem.data && referencedItem.data.meta && referencedItem.data.meta.scopes) {
|
|
2317
|
+
if (!payload.meta)
|
|
2318
|
+
payload.meta = {};
|
|
2319
|
+
payload.meta.scopes = referencedItem.data.meta.scopes;
|
|
2320
|
+
this.log(`Inherited scopes from referenced item ${payload.reference}: ${payload.meta.scopes.join(', ')}`);
|
|
2321
|
+
}
|
|
2245
2322
|
}
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2323
|
+
catch (error) {
|
|
2324
|
+
this.log(`Failed to fetch referenced item ${payload.reference} for scope inheritance: ${this.formatError(error)}`);
|
|
2325
|
+
// Continue without scope inheritance if we can't fetch the referenced item
|
|
2249
2326
|
}
|
|
2250
2327
|
}
|
|
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
2328
|
const response = await this.axiosInstance.post(`/content/${args.type}/create`, payload);
|
|
2276
2329
|
return {
|
|
2277
2330
|
content: [{
|
|
2278
2331
|
type: 'text',
|
|
2279
2332
|
text: `✅ **SUCCESSFULLY CREATED ${args.type.toUpperCase()} CONTENT**
|
|
2280
2333
|
|
|
2281
|
-
The content was created with proper field placement:
|
|
2282
|
-
- Root level fields: ${Object.keys(payload).filter(k => k !== 'data' && k !== 'meta').join(', ')}
|
|
2283
|
-
- Data object fields: ${payload.data ? Object.keys(payload.data).join(', ') : 'none'}
|
|
2284
|
-
- Meta fields: ${Object.keys(payload.meta).join(', ')}
|
|
2285
|
-
|
|
2286
2334
|
**Created Content:**
|
|
2287
2335
|
${JSON.stringify(response.data, null, 2)}`,
|
|
2288
2336
|
}],
|
|
2289
2337
|
};
|
|
2290
2338
|
}
|
|
2291
2339
|
catch (error) {
|
|
2340
|
+
// Pass through server errors directly - they contain the authoritative validation messages
|
|
2292
2341
|
return {
|
|
2293
2342
|
content: [{
|
|
2294
2343
|
type: 'text',
|
|
2295
|
-
text: `Failed to create ${args.type}: ${this.formatError(error)}
|
|
2344
|
+
text: `Failed to create ${args.type}: ${this.formatError(error)}
|
|
2345
|
+
|
|
2346
|
+
💡 **Field Placement Guidance:**
|
|
2347
|
+
- **Root Level**: title, reference, referenceType, body, organisation, meta
|
|
2348
|
+
- **Data Object**: Custom fields defined in content type's "definedFields" array
|
|
2349
|
+
- Use \`qik_get_content_definition\` to see the exact field structure for this content type`,
|
|
2296
2350
|
}],
|
|
2297
2351
|
isError: true,
|
|
2298
2352
|
};
|
|
@@ -2391,6 +2445,180 @@ ${JSON.stringify(response.data, null, 2)}`,
|
|
|
2391
2445
|
};
|
|
2392
2446
|
}
|
|
2393
2447
|
}
|
|
2448
|
+
async getProfileTimeline(args) {
|
|
2449
|
+
try {
|
|
2450
|
+
const response = await this.axiosInstance.get(`/profile/${args.id}/timeline`);
|
|
2451
|
+
return {
|
|
2452
|
+
content: [{
|
|
2453
|
+
type: 'text',
|
|
2454
|
+
text: `📅 **PROFILE TIMELINE FOR ${args.id}**
|
|
2455
|
+
|
|
2456
|
+
This timeline provides chronological activity data including recent actions, content created, events attended, workflow activities, and other activity logs.
|
|
2457
|
+
|
|
2458
|
+
**Timeline Data:**
|
|
2459
|
+
${JSON.stringify(response.data, null, 2)}`,
|
|
2460
|
+
}],
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
catch (error) {
|
|
2464
|
+
return {
|
|
2465
|
+
content: [{
|
|
2466
|
+
type: 'text',
|
|
2467
|
+
text: `Failed to get profile timeline for ${args.id}: ${this.formatError(error)}`,
|
|
2468
|
+
}],
|
|
2469
|
+
isError: true,
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
async getProfileInfo(args) {
|
|
2474
|
+
const normalizedQuery = args.query.toLowerCase().trim();
|
|
2475
|
+
// Analyze the query to determine intent
|
|
2476
|
+
const timelineKeywords = [
|
|
2477
|
+
'activity', 'timeline', 'been up to', 'doing', 'recent', 'lately',
|
|
2478
|
+
'actions', 'history', 'events', 'workflow', 'progress'
|
|
2479
|
+
];
|
|
2480
|
+
const basicInfoKeywords = [
|
|
2481
|
+
'contact', 'email', 'phone', 'details', 'information', 'about'
|
|
2482
|
+
];
|
|
2483
|
+
const wantsTimeline = args.includeTimeline !== undefined
|
|
2484
|
+
? args.includeTimeline
|
|
2485
|
+
: timelineKeywords.some(keyword => normalizedQuery.includes(keyword));
|
|
2486
|
+
const wantsBasicInfo = basicInfoKeywords.some(keyword => normalizedQuery.includes(keyword));
|
|
2487
|
+
// If the intent is ambiguous, ask for clarification
|
|
2488
|
+
if (!wantsTimeline && !wantsBasicInfo && args.includeTimeline === undefined) {
|
|
2489
|
+
const personName = args.profileName || (args.profileId ? `person (ID: ${args.profileId})` : 'this person');
|
|
2490
|
+
return {
|
|
2491
|
+
content: [{
|
|
2492
|
+
type: 'text',
|
|
2493
|
+
text: `🤔 **CLARIFICATION NEEDED**
|
|
2494
|
+
|
|
2495
|
+
You asked: "${args.query}"
|
|
2496
|
+
|
|
2497
|
+
I can provide different types of information about ${personName}:
|
|
2498
|
+
|
|
2499
|
+
**OPTION 1: Basic Profile Details** 📋
|
|
2500
|
+
- Contact information (email, phone)
|
|
2501
|
+
- Basic demographic data
|
|
2502
|
+
- Profile fields and custom data
|
|
2503
|
+
→ Use: \`qik_get_content\` or \`qik_list_profiles\`
|
|
2504
|
+
|
|
2505
|
+
**OPTION 2: Activity Timeline** 📅
|
|
2506
|
+
- Recent actions and activities
|
|
2507
|
+
- Content they've created or been involved with
|
|
2508
|
+
- Workflow progress and events attended
|
|
2509
|
+
- Much richer, more colorful activity data
|
|
2510
|
+
→ Use: \`qik_get_profile_timeline\`
|
|
2511
|
+
|
|
2512
|
+
**Which would be more helpful for your needs?**
|
|
2513
|
+
|
|
2514
|
+
To get both, you can:
|
|
2515
|
+
1. First get basic profile info with \`qik_list_profiles\` (search by name)
|
|
2516
|
+
2. Then get timeline data with \`qik_get_profile_timeline\` (using the profile ID)`,
|
|
2517
|
+
}],
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
let profileId = args.profileId;
|
|
2521
|
+
// If no profile ID provided, try to find the profile by name
|
|
2522
|
+
if (!profileId && args.profileName) {
|
|
2523
|
+
try {
|
|
2524
|
+
const searchResponse = await this.axiosInstance.post('/content/profile/list', {
|
|
2525
|
+
search: args.profileName,
|
|
2526
|
+
page: { size: 5, index: 1 },
|
|
2527
|
+
});
|
|
2528
|
+
if (searchResponse.data.items && searchResponse.data.items.length > 0) {
|
|
2529
|
+
if (searchResponse.data.items.length === 1) {
|
|
2530
|
+
profileId = searchResponse.data.items[0]._id;
|
|
2531
|
+
}
|
|
2532
|
+
else {
|
|
2533
|
+
// Multiple matches found
|
|
2534
|
+
const matches = searchResponse.data.items.map((p) => `- **${p._id}**: ${p.firstName} ${p.lastName} (${p.emails?.[0] || 'no email'})`).join('\n');
|
|
2535
|
+
return {
|
|
2536
|
+
content: [{
|
|
2537
|
+
type: 'text',
|
|
2538
|
+
text: `🔍 **MULTIPLE PROFILES FOUND**
|
|
2539
|
+
|
|
2540
|
+
Found ${searchResponse.data.items.length} profiles matching "${args.profileName}":
|
|
2541
|
+
|
|
2542
|
+
${matches}
|
|
2543
|
+
|
|
2544
|
+
Please specify which profile you want by using the profile ID with \`qik_get_profile_timeline\` or \`qik_get_content\`.`,
|
|
2545
|
+
}],
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
else {
|
|
2550
|
+
return {
|
|
2551
|
+
content: [{
|
|
2552
|
+
type: 'text',
|
|
2553
|
+
text: `❌ **PROFILE NOT FOUND**
|
|
2554
|
+
|
|
2555
|
+
No profiles found matching "${args.profileName}".
|
|
2556
|
+
|
|
2557
|
+
Try:
|
|
2558
|
+
- Using \`qik_list_profiles\` with a broader search
|
|
2559
|
+
- Checking the spelling of the name
|
|
2560
|
+
- Using the exact profile ID if you have it`,
|
|
2561
|
+
}],
|
|
2562
|
+
isError: true,
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
catch (error) {
|
|
2567
|
+
return {
|
|
2568
|
+
content: [{
|
|
2569
|
+
type: 'text',
|
|
2570
|
+
text: `Failed to search for profile "${args.profileName}": ${this.formatError(error)}`,
|
|
2571
|
+
}],
|
|
2572
|
+
isError: true,
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
// Now we have a profile ID, get the appropriate information
|
|
2577
|
+
if (wantsTimeline && profileId) {
|
|
2578
|
+
return await this.getProfileTimeline({ id: profileId });
|
|
2579
|
+
}
|
|
2580
|
+
else if (profileId) {
|
|
2581
|
+
// Get basic profile information
|
|
2582
|
+
try {
|
|
2583
|
+
const response = await this.axiosInstance.get(`/content/${profileId}`);
|
|
2584
|
+
return {
|
|
2585
|
+
content: [{
|
|
2586
|
+
type: 'text',
|
|
2587
|
+
text: `👤 **PROFILE INFORMATION**
|
|
2588
|
+
|
|
2589
|
+
**Basic Details:**
|
|
2590
|
+
${JSON.stringify(response.data, null, 2)}
|
|
2591
|
+
|
|
2592
|
+
💡 **Want more activity details?** Use \`qik_get_profile_timeline\` with ID: ${profileId} to see their recent activities, workflow progress, and timeline data.`,
|
|
2593
|
+
}],
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
catch (error) {
|
|
2597
|
+
return {
|
|
2598
|
+
content: [{
|
|
2599
|
+
type: 'text',
|
|
2600
|
+
text: `Failed to get profile information for ${profileId}: ${this.formatError(error)}`,
|
|
2601
|
+
}],
|
|
2602
|
+
isError: true,
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
else {
|
|
2607
|
+
return {
|
|
2608
|
+
content: [{
|
|
2609
|
+
type: 'text',
|
|
2610
|
+
text: `❌ **MISSING PROFILE IDENTIFIER**
|
|
2611
|
+
|
|
2612
|
+
To get profile information, I need either:
|
|
2613
|
+
- **profileId**: The exact profile ID
|
|
2614
|
+
- **profileName**: The person's name to search for
|
|
2615
|
+
|
|
2616
|
+
Please provide one of these and try again.`,
|
|
2617
|
+
}],
|
|
2618
|
+
isError: true,
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2394
2622
|
async getForm(args) {
|
|
2395
2623
|
try {
|
|
2396
2624
|
const response = await this.axiosInstance.get(`/form/${args.id}`);
|
|
@@ -2535,6 +2763,380 @@ ${JSON.stringify(response.data, null, 2)}`,
|
|
|
2535
2763
|
};
|
|
2536
2764
|
}
|
|
2537
2765
|
}
|
|
2766
|
+
// Documentation-focused tool implementations
|
|
2767
|
+
async getDocumentation(args) {
|
|
2768
|
+
try {
|
|
2769
|
+
const documentation = QikDocumentationHelper.getTopicDocumentation(args.topic);
|
|
2770
|
+
if (!documentation) {
|
|
2771
|
+
return {
|
|
2772
|
+
content: [{
|
|
2773
|
+
type: 'text',
|
|
2774
|
+
text: `❌ **DOCUMENTATION NOT FOUND**
|
|
2775
|
+
|
|
2776
|
+
No documentation found for topic: "${args.topic}"
|
|
2777
|
+
|
|
2778
|
+
**Available topics:**
|
|
2779
|
+
- authentication (token types, methods, error codes)
|
|
2780
|
+
- endpoints (API endpoint documentation)
|
|
2781
|
+
- contentTypes (content type specific guidance)
|
|
2782
|
+
- filterSyntax (filter operators and comparators)
|
|
2783
|
+
- concepts (key concepts like field placement, scopes, workflows)
|
|
2784
|
+
- examples (code examples for common use cases)
|
|
2785
|
+
- troubleshooting (error resolution guides)
|
|
2786
|
+
|
|
2787
|
+
**Example usage:**
|
|
2788
|
+
- \`qik_get_documentation\` with topic: "concepts.field_placement"
|
|
2789
|
+
- \`qik_get_documentation\` with topic: "authentication.tokenTypes"
|
|
2790
|
+
- \`qik_get_documentation\` with topic: "troubleshooting.field_placement_errors"`,
|
|
2791
|
+
}],
|
|
2792
|
+
isError: true,
|
|
2793
|
+
};
|
|
2794
|
+
}
|
|
2795
|
+
let result = `📖 **DOCUMENTATION: ${args.topic.toUpperCase()}**\n\n`;
|
|
2796
|
+
if (args.subtopic && documentation[args.subtopic]) {
|
|
2797
|
+
result += `**Subtopic: ${args.subtopic}**\n\n`;
|
|
2798
|
+
result += JSON.stringify(documentation[args.subtopic], null, 2);
|
|
2799
|
+
}
|
|
2800
|
+
else {
|
|
2801
|
+
result += JSON.stringify(documentation, null, 2);
|
|
2802
|
+
}
|
|
2803
|
+
return {
|
|
2804
|
+
content: [{
|
|
2805
|
+
type: 'text',
|
|
2806
|
+
text: result,
|
|
2807
|
+
}],
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
catch (error) {
|
|
2811
|
+
return {
|
|
2812
|
+
content: [{
|
|
2813
|
+
type: 'text',
|
|
2814
|
+
text: `Failed to get documentation for ${args.topic}: ${this.formatError(error)}`,
|
|
2815
|
+
}],
|
|
2816
|
+
isError: true,
|
|
2817
|
+
};
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
async searchDocumentation(args) {
|
|
2821
|
+
try {
|
|
2822
|
+
const results = QikDocumentationHelper.searchDocumentation(args.query);
|
|
2823
|
+
const limit = args.limit || 10;
|
|
2824
|
+
const limitedResults = results.slice(0, limit);
|
|
2825
|
+
if (limitedResults.length === 0) {
|
|
2826
|
+
return {
|
|
2827
|
+
content: [{
|
|
2828
|
+
type: 'text',
|
|
2829
|
+
text: `🔍 **NO DOCUMENTATION FOUND**
|
|
2830
|
+
|
|
2831
|
+
No documentation found for query: "${args.query}"
|
|
2832
|
+
|
|
2833
|
+
**Try searching for:**
|
|
2834
|
+
- "field placement" - for field structure guidance
|
|
2835
|
+
- "authentication" - for token and auth help
|
|
2836
|
+
- "filter" - for filtering and query syntax
|
|
2837
|
+
- "workflow" - for workflow system concepts
|
|
2838
|
+
- "birthday" - for anniversary/birthday queries
|
|
2839
|
+
- "scope" - for permission and scope guidance
|
|
2840
|
+
- "troubleshooting" - for error resolution
|
|
2841
|
+
|
|
2842
|
+
**Available documentation sections:**
|
|
2843
|
+
- Authentication & Tokens
|
|
2844
|
+
- API Endpoints
|
|
2845
|
+
- Content Types
|
|
2846
|
+
- Filter Syntax
|
|
2847
|
+
- Core Concepts
|
|
2848
|
+
- Code Examples
|
|
2849
|
+
- Troubleshooting Guides`,
|
|
2850
|
+
}],
|
|
2851
|
+
};
|
|
2852
|
+
}
|
|
2853
|
+
let result = `🔍 **DOCUMENTATION SEARCH RESULTS**\n\n`;
|
|
2854
|
+
result += `Found ${limitedResults.length} results for "${args.query}":\n\n`;
|
|
2855
|
+
limitedResults.forEach((item, index) => {
|
|
2856
|
+
result += `**${index + 1}. ${item.path}** (${item.type})\n`;
|
|
2857
|
+
if (typeof item.content === 'string') {
|
|
2858
|
+
const preview = item.content.length > 200
|
|
2859
|
+
? item.content.substring(0, 200) + '...'
|
|
2860
|
+
: item.content;
|
|
2861
|
+
result += `${preview}\n\n`;
|
|
2862
|
+
}
|
|
2863
|
+
else {
|
|
2864
|
+
result += `${JSON.stringify(item.content).substring(0, 200)}...\n\n`;
|
|
2865
|
+
}
|
|
2866
|
+
});
|
|
2867
|
+
result += `💡 **Need more specific help?** Use \`qik_get_documentation\` with a specific topic for detailed information.`;
|
|
2868
|
+
return {
|
|
2869
|
+
content: [{
|
|
2870
|
+
type: 'text',
|
|
2871
|
+
text: result,
|
|
2872
|
+
}],
|
|
2873
|
+
};
|
|
2874
|
+
}
|
|
2875
|
+
catch (error) {
|
|
2876
|
+
return {
|
|
2877
|
+
content: [{
|
|
2878
|
+
type: 'text',
|
|
2879
|
+
text: `Failed to search documentation: ${this.formatError(error)}`,
|
|
2880
|
+
}],
|
|
2881
|
+
isError: true,
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
async getTroubleshooting(args) {
|
|
2886
|
+
try {
|
|
2887
|
+
let troubleshootingInfo;
|
|
2888
|
+
if (args.issueType) {
|
|
2889
|
+
// Get specific issue type
|
|
2890
|
+
troubleshootingInfo = QIK_DOCUMENTATION.troubleshooting[args.issueType];
|
|
2891
|
+
if (troubleshootingInfo) {
|
|
2892
|
+
troubleshootingInfo = { issue: args.issueType, ...troubleshootingInfo };
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
else {
|
|
2896
|
+
// Auto-detect issue type from error message
|
|
2897
|
+
troubleshootingInfo = QikDocumentationHelper.getTroubleshootingInfo(args.errorMessage);
|
|
2898
|
+
}
|
|
2899
|
+
if (!troubleshootingInfo) {
|
|
2900
|
+
return {
|
|
2901
|
+
content: [{
|
|
2902
|
+
type: 'text',
|
|
2903
|
+
text: `🔧 **NO SPECIFIC TROUBLESHOOTING FOUND**
|
|
2904
|
+
|
|
2905
|
+
I couldn't find specific troubleshooting information for: "${args.errorMessage}"
|
|
2906
|
+
|
|
2907
|
+
**Available troubleshooting categories:**
|
|
2908
|
+
- **field_placement_errors**: Field structure and placement issues
|
|
2909
|
+
- **authentication_failures**: Token and permission problems
|
|
2910
|
+
- **filter_syntax_errors**: Query and filter syntax issues
|
|
2911
|
+
- **scope_permission_issues**: Access and permission problems
|
|
2912
|
+
- **workflow_confusion**: Workflow definition vs card confusion
|
|
2913
|
+
- **date_handling_issues**: Date format and timezone problems
|
|
2914
|
+
- **performance_issues**: Slow queries and optimization
|
|
2915
|
+
|
|
2916
|
+
**General troubleshooting steps:**
|
|
2917
|
+
1. Check the error message for specific field names or codes
|
|
2918
|
+
2. Verify your access token and permissions
|
|
2919
|
+
3. Ensure proper field placement (root vs data object)
|
|
2920
|
+
4. Validate filter syntax and comparators
|
|
2921
|
+
5. Check scope permissions and hierarchy
|
|
2922
|
+
|
|
2923
|
+
**Need more help?** Try:
|
|
2924
|
+
- \`qik_search_documentation\` with keywords from your error
|
|
2925
|
+
- \`qik_get_documentation\` with topic: "troubleshooting"
|
|
2926
|
+
- \`qik_validate_field_placement\` for content creation issues`,
|
|
2927
|
+
}],
|
|
2928
|
+
};
|
|
2929
|
+
}
|
|
2930
|
+
let result = `🔧 **TROUBLESHOOTING: ${troubleshootingInfo.issue.toUpperCase().replace(/_/g, ' ')}**\n\n`;
|
|
2931
|
+
result += `**Your Error:** "${args.errorMessage}"\n\n`;
|
|
2932
|
+
if (troubleshootingInfo.symptoms && troubleshootingInfo.symptoms.length > 0) {
|
|
2933
|
+
result += `**Common Symptoms:**\n`;
|
|
2934
|
+
result += troubleshootingInfo.symptoms.map((s) => `- ${s}`).join('\n');
|
|
2935
|
+
result += '\n\n';
|
|
2936
|
+
}
|
|
2937
|
+
if (troubleshootingInfo.causes && troubleshootingInfo.causes.length > 0) {
|
|
2938
|
+
result += `**Likely Causes:**\n`;
|
|
2939
|
+
result += troubleshootingInfo.causes.map((c) => `- ${c}`).join('\n');
|
|
2940
|
+
result += '\n\n';
|
|
2941
|
+
}
|
|
2942
|
+
if (troubleshootingInfo.solutions && troubleshootingInfo.solutions.length > 0) {
|
|
2943
|
+
result += `**Solutions:**\n`;
|
|
2944
|
+
result += troubleshootingInfo.solutions.map((s) => `- ${s}`).join('\n');
|
|
2945
|
+
result += '\n\n';
|
|
2946
|
+
}
|
|
2947
|
+
if (troubleshootingInfo.prevention && troubleshootingInfo.prevention.length > 0) {
|
|
2948
|
+
result += `**Prevention:**\n`;
|
|
2949
|
+
result += troubleshootingInfo.prevention.map((p) => `- ${p}`).join('\n');
|
|
2950
|
+
result += '\n\n';
|
|
2951
|
+
}
|
|
2952
|
+
if (troubleshootingInfo.relatedIssues && troubleshootingInfo.relatedIssues.length > 0) {
|
|
2953
|
+
result += `**Related Issues:**\n`;
|
|
2954
|
+
result += troubleshootingInfo.relatedIssues.map((r) => `- ${r}`).join('\n');
|
|
2955
|
+
}
|
|
2956
|
+
return {
|
|
2957
|
+
content: [{
|
|
2958
|
+
type: 'text',
|
|
2959
|
+
text: result,
|
|
2960
|
+
}],
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
catch (error) {
|
|
2964
|
+
return {
|
|
2965
|
+
content: [{
|
|
2966
|
+
type: 'text',
|
|
2967
|
+
text: `Failed to get troubleshooting information: ${this.formatError(error)}`,
|
|
2968
|
+
}],
|
|
2969
|
+
isError: true,
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
async getExamples(args) {
|
|
2974
|
+
try {
|
|
2975
|
+
if (args.category === 'all') {
|
|
2976
|
+
let result = `💡 **ALL CODE EXAMPLES**\n\n`;
|
|
2977
|
+
for (const [category, examples] of Object.entries(QIK_DOCUMENTATION.examples)) {
|
|
2978
|
+
result += `## ${category.toUpperCase()}\n\n`;
|
|
2979
|
+
for (const [exampleKey, example] of Object.entries(examples)) {
|
|
2980
|
+
const typedExample = example;
|
|
2981
|
+
result += `### ${typedExample.title}\n`;
|
|
2982
|
+
result += `${typedExample.description}\n\n`;
|
|
2983
|
+
result += `\`\`\`json\n${JSON.stringify(typedExample.code, null, 2)}\n\`\`\`\n\n`;
|
|
2984
|
+
result += `**Explanation:** ${typedExample.explanation}\n\n`;
|
|
2985
|
+
if (typedExample.variations && typedExample.variations.length > 0) {
|
|
2986
|
+
result += `**Variations:**\n`;
|
|
2987
|
+
typedExample.variations.forEach((variation) => {
|
|
2988
|
+
result += `- ${variation.description}\n`;
|
|
2989
|
+
result += ` \`\`\`json\n ${JSON.stringify(variation.code, null, 2)}\n \`\`\`\n`;
|
|
2990
|
+
});
|
|
2991
|
+
result += '\n';
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
return {
|
|
2996
|
+
content: [{
|
|
2997
|
+
type: 'text',
|
|
2998
|
+
text: result,
|
|
2999
|
+
}],
|
|
3000
|
+
};
|
|
3001
|
+
}
|
|
3002
|
+
const examples = QikDocumentationHelper.getExamples(args.category, args.example);
|
|
3003
|
+
if (!examples) {
|
|
3004
|
+
return {
|
|
3005
|
+
content: [{
|
|
3006
|
+
type: 'text',
|
|
3007
|
+
text: `💡 **NO EXAMPLES FOUND**
|
|
3008
|
+
|
|
3009
|
+
No examples found for category: "${args.category}"
|
|
3010
|
+
|
|
3011
|
+
**Available example categories:**
|
|
3012
|
+
- **authentication**: Token usage and auth examples
|
|
3013
|
+
- **content_creation**: Creating different content types
|
|
3014
|
+
- **filtering**: Advanced filtering and search examples
|
|
3015
|
+
|
|
3016
|
+
**Example usage:**
|
|
3017
|
+
- \`qik_get_examples\` with category: "authentication"
|
|
3018
|
+
- \`qik_get_examples\` with category: "filtering" and example: "birthday_search"
|
|
3019
|
+
- \`qik_get_examples\` with category: "all" (shows all examples)`,
|
|
3020
|
+
}],
|
|
3021
|
+
isError: true,
|
|
3022
|
+
};
|
|
3023
|
+
}
|
|
3024
|
+
let result = `💡 **CODE EXAMPLES: ${args.category.toUpperCase()}**\n\n`;
|
|
3025
|
+
if (args.example && examples[args.example]) {
|
|
3026
|
+
// Show specific example
|
|
3027
|
+
const example = examples[args.example];
|
|
3028
|
+
result += `### ${example.title}\n`;
|
|
3029
|
+
result += `${example.description}\n\n`;
|
|
3030
|
+
result += `\`\`\`json\n${JSON.stringify(example.code, null, 2)}\n\`\`\`\n\n`;
|
|
3031
|
+
result += `**Explanation:** ${example.explanation}\n\n`;
|
|
3032
|
+
if (example.variations && example.variations.length > 0) {
|
|
3033
|
+
result += `**Variations:**\n`;
|
|
3034
|
+
example.variations.forEach((variation) => {
|
|
3035
|
+
result += `- ${variation.description}\n`;
|
|
3036
|
+
result += ` \`\`\`json\n ${JSON.stringify(variation.code, null, 2)}\n \`\`\`\n`;
|
|
3037
|
+
});
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
else {
|
|
3041
|
+
// Show all examples in category
|
|
3042
|
+
for (const [exampleKey, example] of Object.entries(examples)) {
|
|
3043
|
+
const typedExample = example;
|
|
3044
|
+
result += `### ${typedExample.title}\n`;
|
|
3045
|
+
result += `${typedExample.description}\n\n`;
|
|
3046
|
+
result += `\`\`\`json\n${JSON.stringify(typedExample.code, null, 2)}\n\`\`\`\n\n`;
|
|
3047
|
+
result += `**Explanation:** ${typedExample.explanation}\n\n`;
|
|
3048
|
+
if (typedExample.variations && typedExample.variations.length > 0) {
|
|
3049
|
+
result += `**Variations:**\n`;
|
|
3050
|
+
typedExample.variations.forEach((variation) => {
|
|
3051
|
+
result += `- ${variation.description}\n`;
|
|
3052
|
+
result += ` \`\`\`json\n ${JSON.stringify(variation.code, null, 2)}\n \`\`\`\n`;
|
|
3053
|
+
});
|
|
3054
|
+
result += '\n';
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
return {
|
|
3059
|
+
content: [{
|
|
3060
|
+
type: 'text',
|
|
3061
|
+
text: result,
|
|
3062
|
+
}],
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
catch (error) {
|
|
3066
|
+
return {
|
|
3067
|
+
content: [{
|
|
3068
|
+
type: 'text',
|
|
3069
|
+
text: `Failed to get examples: ${this.formatError(error)}`,
|
|
3070
|
+
}],
|
|
3071
|
+
isError: true,
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
async validateFieldPlacement(args) {
|
|
3076
|
+
try {
|
|
3077
|
+
const validation = this.validateContentType(args.contentType);
|
|
3078
|
+
if (!validation.valid) {
|
|
3079
|
+
return {
|
|
3080
|
+
content: [{
|
|
3081
|
+
type: 'text',
|
|
3082
|
+
text: validation.error,
|
|
3083
|
+
}],
|
|
3084
|
+
isError: true,
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3087
|
+
// Simplified validation using documentation helper only
|
|
3088
|
+
const docValidation = QikDocumentationHelper.validateFieldPlacement(args.contentType, args.payload);
|
|
3089
|
+
let result = `✅ **FIELD PLACEMENT VALIDATION: ${args.contentType.toUpperCase()}**\n\n`;
|
|
3090
|
+
if (docValidation.valid) {
|
|
3091
|
+
result += `🎉 **VALIDATION PASSED**\n\n`;
|
|
3092
|
+
result += `Your payload has correct field placement!\n\n`;
|
|
3093
|
+
result += `**Field Analysis:**\n`;
|
|
3094
|
+
result += `- Root level fields: ${Object.keys(args.payload).filter(k => k !== 'data' && k !== 'meta').join(', ') || 'none'}\n`;
|
|
3095
|
+
result += `- Data object fields: ${args.payload.data ? Object.keys(args.payload.data).join(', ') : 'none'}\n`;
|
|
3096
|
+
result += `- Meta fields: ${args.payload.meta ? Object.keys(args.payload.meta).join(', ') : 'none'}\n\n`;
|
|
3097
|
+
}
|
|
3098
|
+
else {
|
|
3099
|
+
result += `❌ **VALIDATION FAILED**\n\n`;
|
|
3100
|
+
if (docValidation.errors && docValidation.errors.length > 0) {
|
|
3101
|
+
result += `**Errors Found:**\n`;
|
|
3102
|
+
docValidation.errors.forEach(error => {
|
|
3103
|
+
result += `- ${error}\n`;
|
|
3104
|
+
});
|
|
3105
|
+
result += '\n';
|
|
3106
|
+
}
|
|
3107
|
+
result += `**General Field Placement Rules:**\n`;
|
|
3108
|
+
result += `- **Root Level**: title, reference, referenceType, body, organisation, meta\n`;
|
|
3109
|
+
result += `- **Data Object**: Custom fields defined in content type's "definedFields" array\n`;
|
|
3110
|
+
result += `- Use \`qik_get_content_definition\` to see the exact field structure for this content type\n\n`;
|
|
3111
|
+
result += `**Correct Structure Example:**\n`;
|
|
3112
|
+
result += `\`\`\`json\n`;
|
|
3113
|
+
result += `{\n`;
|
|
3114
|
+
result += ` "type": "${args.contentType}",\n`;
|
|
3115
|
+
result += ` "title": "Your Title Here",\n`;
|
|
3116
|
+
result += ` "meta": { "scopes": ["scope-id-here"] },\n`;
|
|
3117
|
+
result += ` "data": {\n`;
|
|
3118
|
+
result += ` "customField": "value"\n`;
|
|
3119
|
+
result += ` }\n`;
|
|
3120
|
+
result += `}\n\`\`\`\n`;
|
|
3121
|
+
}
|
|
3122
|
+
return {
|
|
3123
|
+
content: [{
|
|
3124
|
+
type: 'text',
|
|
3125
|
+
text: result,
|
|
3126
|
+
}],
|
|
3127
|
+
isError: !docValidation.valid,
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
catch (error) {
|
|
3131
|
+
return {
|
|
3132
|
+
content: [{
|
|
3133
|
+
type: 'text',
|
|
3134
|
+
text: `Failed to validate field placement: ${this.formatError(error)}`,
|
|
3135
|
+
}],
|
|
3136
|
+
isError: true,
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
2538
3140
|
async run() {
|
|
2539
3141
|
const transport = new StdioServerTransport();
|
|
2540
3142
|
await this.server.connect(transport);
|