@qikdev/mcp 6.6.3 → 6.6.6
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/index.d.ts +77 -1
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +793 -149
- package/build/src/index.js.map +1 -1
- package/package.json +1 -1
package/build/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Qik Platform MCP Server - Enhanced Version
|
|
3
|
+
* Qik Platform MCP Server - Enhanced Version with Comprehensive Documentation
|
|
4
4
|
*
|
|
5
5
|
* This MCP server provides comprehensive integration with the Qik platform,
|
|
6
6
|
* enabling AI assistants to interact with Qik's content management system,
|
|
@@ -13,12 +13,42 @@
|
|
|
13
13
|
* - Dynamic tool schema generation
|
|
14
14
|
* - Comprehensive API coverage
|
|
15
15
|
* - Intelligent request building and field validation
|
|
16
|
+
* - Advanced disambiguation logic for definitions vs instances
|
|
17
|
+
* - Workflow system documentation and automation
|
|
18
|
+
* - Comprehensive scope and permission management
|
|
19
|
+
*
|
|
20
|
+
* IMPORTANT QIK CONCEPTS:
|
|
21
|
+
*
|
|
22
|
+
* 1. DEFINITIONS vs INSTANCES:
|
|
23
|
+
* - Definitions: Templates that define structure (e.g., "workflow definition", "content type definition")
|
|
24
|
+
* - Instances: Actual content items created from definitions (e.g., "workflow card", "article instance")
|
|
25
|
+
* - When user says "create a workflow" they usually mean create a workflow DEFINITION
|
|
26
|
+
* - When user says "add Jim to workflow X" they mean create a workflow CARD instance
|
|
27
|
+
*
|
|
28
|
+
* 2. WORKFLOW SYSTEM:
|
|
29
|
+
* - Workflow Definitions: Define the structure with columns, steps, automation
|
|
30
|
+
* - Workflow Cards: Individual items that move through the workflow
|
|
31
|
+
* - Columns: Represent stages in the workflow (e.g., "To Do", "In Progress", "Done")
|
|
32
|
+
* - Steps: Specific positions within columns where cards can be placed
|
|
33
|
+
* - Automation: Entry/exit/success/fail functions that run when cards move
|
|
34
|
+
*
|
|
35
|
+
* 3. SCOPE SYSTEM:
|
|
36
|
+
* - Hierarchical permission structure (like folders)
|
|
37
|
+
* - Every content item must belong to at least one scope
|
|
38
|
+
* - Users need appropriate permissions within scopes to perform actions
|
|
39
|
+
* - Scopes can inherit permissions from parent scopes
|
|
40
|
+
*
|
|
41
|
+
* 4. CONTENT TYPE SYSTEM:
|
|
42
|
+
* - Base types: Core Qik types (article, profile, event, etc.)
|
|
43
|
+
* - Extended types: Custom types that extend base types with additional fields
|
|
44
|
+
* - Fields vs DefinedFields: Fields go at root level, definedFields go in data object
|
|
16
45
|
*/
|
|
17
46
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
18
47
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
48
|
import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
20
49
|
import axios from 'axios';
|
|
21
50
|
import FormData from 'form-data';
|
|
51
|
+
import { ConfigManager } from './config.js';
|
|
22
52
|
// Environment variables
|
|
23
53
|
const QIK_API_URL = process.env.QIK_API_URL || 'https://api.qik.dev';
|
|
24
54
|
const QIK_ACCESS_TOKEN = process.env.QIK_ACCESS_TOKEN;
|
|
@@ -29,6 +59,7 @@ export class QikMCPServer {
|
|
|
29
59
|
userSession = null;
|
|
30
60
|
lastGlossaryUpdate = 0;
|
|
31
61
|
GLOSSARY_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
62
|
+
serverName = 'Qik'; // Default fallback
|
|
32
63
|
constructor() {
|
|
33
64
|
if (!QIK_ACCESS_TOKEN) {
|
|
34
65
|
throw new Error('QIK_ACCESS_TOKEN environment variable is required. Run "qik-mcp-server setup" to configure.');
|
|
@@ -68,7 +99,9 @@ export class QikMCPServer {
|
|
|
68
99
|
}
|
|
69
100
|
async initializeServer() {
|
|
70
101
|
try {
|
|
71
|
-
// Load
|
|
102
|
+
// Load server name from config first
|
|
103
|
+
await this.loadServerName();
|
|
104
|
+
// Load user session
|
|
72
105
|
await this.loadUserSession();
|
|
73
106
|
// Then load glossary
|
|
74
107
|
await this.loadGlossary();
|
|
@@ -78,6 +111,23 @@ export class QikMCPServer {
|
|
|
78
111
|
this.log(`Failed to initialize server: ${this.formatError(error)}`);
|
|
79
112
|
}
|
|
80
113
|
}
|
|
114
|
+
async loadServerName() {
|
|
115
|
+
try {
|
|
116
|
+
const configManager = new ConfigManager();
|
|
117
|
+
const config = await configManager.loadConfig();
|
|
118
|
+
if (config && config.serverName) {
|
|
119
|
+
this.serverName = config.serverName;
|
|
120
|
+
this.log(`Loaded server name: ${this.serverName}`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
this.log(`No server name found in config, using default: ${this.serverName}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
this.log(`Failed to load server name from config: ${this.formatError(error)}`);
|
|
128
|
+
// Keep default fallback value
|
|
129
|
+
}
|
|
130
|
+
}
|
|
81
131
|
async loadUserSession() {
|
|
82
132
|
try {
|
|
83
133
|
const response = await this.axiosInstance.get('/user');
|
|
@@ -321,22 +371,47 @@ export class QikMCPServer {
|
|
|
321
371
|
for (const fieldKey of commonRootFields) {
|
|
322
372
|
properties[fieldKey] = {
|
|
323
373
|
type: 'string',
|
|
324
|
-
description: `${fieldKey} field (goes at
|
|
374
|
+
description: `${fieldKey} field (ALWAYS goes at ROOT LEVEL, never in data object)`,
|
|
325
375
|
};
|
|
326
376
|
}
|
|
327
|
-
// Add a data object for definedFields
|
|
377
|
+
// Add a data object for definedFields with clear explanation
|
|
328
378
|
properties.data = {
|
|
329
379
|
type: 'object',
|
|
330
|
-
description:
|
|
380
|
+
description: `CRITICAL FIELD PLACEMENT RULE:
|
|
381
|
+
|
|
382
|
+
**ROOT LEVEL FIELDS (never put these in data object):**
|
|
383
|
+
- title, reference, referenceType, body, organisation, meta
|
|
384
|
+
- These are "fields" in the glossary definition
|
|
385
|
+
|
|
386
|
+
**DATA OBJECT FIELDS (always put these in data object):**
|
|
387
|
+
- Custom fields defined as "definedFields" in the glossary
|
|
388
|
+
- Content-type specific fields like wasAnyoneHurt, customField1, etc.
|
|
389
|
+
|
|
390
|
+
**WRONG EXAMPLE:**
|
|
391
|
+
{
|
|
392
|
+
"data": {
|
|
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
|
+
}
|
|
398
|
+
|
|
399
|
+
**CORRECT EXAMPLE:**
|
|
400
|
+
{
|
|
401
|
+
"title": "My Title", // ✅ CORRECT - at root level
|
|
402
|
+
"data": {
|
|
403
|
+
"customField": "value" // ✅ CORRECT - custom fields in data object
|
|
404
|
+
}
|
|
405
|
+
}`,
|
|
331
406
|
properties: {
|
|
332
407
|
wasAnyoneHurt: {
|
|
333
408
|
type: 'string',
|
|
334
|
-
description: 'For incident reports - whether anyone was hurt',
|
|
409
|
+
description: 'For incident reports - whether anyone was hurt (this is a definedField, so it goes in data object)',
|
|
335
410
|
enum: ['yes', 'no']
|
|
336
411
|
},
|
|
337
412
|
whatTimeDidItHappen: {
|
|
338
413
|
type: 'string',
|
|
339
|
-
description: 'For incident reports - when the incident occurred'
|
|
414
|
+
description: 'For incident reports - when the incident occurred (this is a definedField, so it goes in data object)'
|
|
340
415
|
}
|
|
341
416
|
}
|
|
342
417
|
};
|
|
@@ -710,161 +785,541 @@ EXAMPLES:
|
|
|
710
785
|
}
|
|
711
786
|
};
|
|
712
787
|
}
|
|
788
|
+
/**
|
|
789
|
+
* Enhanced intelligent content creation with advanced disambiguation logic
|
|
790
|
+
*
|
|
791
|
+
* This method provides sophisticated analysis of user intent to distinguish between:
|
|
792
|
+
* - Creating workflow DEFINITIONS vs workflow CARD instances
|
|
793
|
+
* - Creating content type DEFINITIONS vs content INSTANCES
|
|
794
|
+
* - Understanding context clues like "add person to workflow" vs "create new workflow"
|
|
795
|
+
*/
|
|
713
796
|
async intelligentContentCreation(description, additionalData) {
|
|
714
|
-
//
|
|
797
|
+
// STEP 1: Advanced Intent Analysis with Disambiguation Logic
|
|
798
|
+
const intentAnalysis = this.analyzeUserIntent(description, additionalData);
|
|
799
|
+
// Handle workflow-specific disambiguation
|
|
800
|
+
if (intentAnalysis.isWorkflowRelated) {
|
|
801
|
+
return await this.handleWorkflowDisambiguation(description, additionalData, intentAnalysis);
|
|
802
|
+
}
|
|
803
|
+
// STEP 2: Standard content type matching
|
|
715
804
|
const contentTypeMatches = this.findContentTypesByDescription(description);
|
|
716
805
|
if (contentTypeMatches.length === 0) {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
806
|
+
return await this.handleNoContentTypeMatches(description);
|
|
807
|
+
}
|
|
808
|
+
if (contentTypeMatches.length > 1) {
|
|
809
|
+
return await this.handleMultipleContentTypeMatches(description, contentTypeMatches);
|
|
810
|
+
}
|
|
811
|
+
// STEP 3: Single match found - provide comprehensive guidance
|
|
812
|
+
const contentType = contentTypeMatches[0];
|
|
813
|
+
return await this.handleSingleContentTypeMatch(contentType, description, additionalData);
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Analyzes user intent to distinguish between different types of content creation
|
|
817
|
+
*/
|
|
818
|
+
analyzeUserIntent(description, additionalData) {
|
|
819
|
+
const normalizedDesc = description.toLowerCase().trim();
|
|
820
|
+
const contextClues = [];
|
|
821
|
+
// Workflow-related keywords
|
|
822
|
+
const workflowKeywords = ['workflow', 'kanban', 'board', 'column', 'step', 'process', 'pipeline'];
|
|
823
|
+
const isWorkflowRelated = workflowKeywords.some(keyword => normalizedDesc.includes(keyword));
|
|
824
|
+
// Definition creation indicators
|
|
825
|
+
const definitionIndicators = [
|
|
826
|
+
'create a new', 'create new', 'make a new', 'design a', 'set up a', 'build a',
|
|
827
|
+
'define a', 'establish a', 'configure a'
|
|
828
|
+
];
|
|
829
|
+
const isDefinitionCreation = definitionIndicators.some(indicator => normalizedDesc.includes(indicator));
|
|
830
|
+
// Instance creation indicators
|
|
831
|
+
const instanceIndicators = [
|
|
832
|
+
'add', 'assign', 'put', 'move', 'place', 'insert', 'include'
|
|
833
|
+
];
|
|
834
|
+
const isInstanceCreation = instanceIndicators.some(indicator => normalizedDesc.includes(indicator));
|
|
835
|
+
// Person assignment indicators
|
|
836
|
+
const personIndicators = [
|
|
837
|
+
'add person', 'assign person', 'add user', 'assign user', 'add someone', 'assign someone',
|
|
838
|
+
'add jim', 'add john', 'add sarah', 'put person', 'move person'
|
|
839
|
+
];
|
|
840
|
+
const isPersonAssignment = personIndicators.some(indicator => normalizedDesc.includes(indicator));
|
|
841
|
+
// Collect context clues
|
|
842
|
+
if (isWorkflowRelated)
|
|
843
|
+
contextClues.push('workflow-related');
|
|
844
|
+
if (isDefinitionCreation)
|
|
845
|
+
contextClues.push('definition-creation');
|
|
846
|
+
if (isInstanceCreation)
|
|
847
|
+
contextClues.push('instance-creation');
|
|
848
|
+
if (isPersonAssignment)
|
|
849
|
+
contextClues.push('person-assignment');
|
|
850
|
+
// Calculate confidence based on clarity of intent
|
|
851
|
+
let confidence = 0.5; // Base confidence
|
|
852
|
+
if (isDefinitionCreation && !isInstanceCreation)
|
|
853
|
+
confidence = 0.9;
|
|
854
|
+
if (isInstanceCreation && !isDefinitionCreation)
|
|
855
|
+
confidence = 0.9;
|
|
856
|
+
if (isPersonAssignment)
|
|
857
|
+
confidence = 0.95;
|
|
858
|
+
return {
|
|
859
|
+
isWorkflowRelated,
|
|
860
|
+
isDefinitionCreation,
|
|
861
|
+
isInstanceCreation,
|
|
862
|
+
isPersonAssignment,
|
|
863
|
+
confidence,
|
|
864
|
+
contextClues
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Handles workflow-specific disambiguation with comprehensive guidance
|
|
869
|
+
*/
|
|
870
|
+
async handleWorkflowDisambiguation(description, additionalData, intentAnalysis) {
|
|
871
|
+
const normalizedDesc = description.toLowerCase().trim();
|
|
872
|
+
// Check if user wants to create a workflow DEFINITION
|
|
873
|
+
if (intentAnalysis.isDefinitionCreation ||
|
|
874
|
+
normalizedDesc.includes('create a workflow') ||
|
|
875
|
+
normalizedDesc.includes('new workflow') ||
|
|
876
|
+
normalizedDesc.includes('design workflow')) {
|
|
737
877
|
return {
|
|
738
878
|
content: [{
|
|
739
879
|
type: 'text',
|
|
740
|
-
text:
|
|
880
|
+
text: `🔧 **WORKFLOW DEFINITION CREATION**
|
|
881
|
+
|
|
882
|
+
You want to create a new workflow definition (template). This defines the structure, columns, steps, and automation rules.
|
|
883
|
+
|
|
884
|
+
**WORKFLOW DEFINITION STRUCTURE:**
|
|
885
|
+
|
|
886
|
+
A workflow definition includes:
|
|
887
|
+
- **Columns**: Stages like "To Do", "In Progress", "Review", "Done"
|
|
888
|
+
- **Steps**: Specific positions within columns where cards can be placed
|
|
889
|
+
- **Automation**: Functions that run when cards enter/exit steps
|
|
890
|
+
- **Due Date Behavior**: How due dates are calculated and managed
|
|
891
|
+
- **Completion Criteria**: Rules that determine when workflow is complete
|
|
892
|
+
|
|
893
|
+
**EXAMPLE WORKFLOW DEFINITION:**
|
|
894
|
+
\`\`\`json
|
|
895
|
+
{
|
|
896
|
+
"type": "definition",
|
|
897
|
+
"title": "New Student Induction Workflow",
|
|
898
|
+
"definesType": "workflowcard",
|
|
899
|
+
"workflow": [
|
|
900
|
+
{
|
|
901
|
+
"title": "Enrollment",
|
|
902
|
+
"description": "Initial enrollment and documentation",
|
|
903
|
+
"steps": [
|
|
904
|
+
{
|
|
905
|
+
"title": "Application Received",
|
|
906
|
+
"type": "step",
|
|
907
|
+
"description": "Student application has been received",
|
|
908
|
+
"duration": 1440,
|
|
909
|
+
"assignees": [],
|
|
910
|
+
"entryFunction": "// Code to run when card enters this step",
|
|
911
|
+
"exitFunction": "// Code to run when card exits this step"
|
|
912
|
+
}
|
|
913
|
+
]
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
"title": "Processing",
|
|
917
|
+
"description": "Review and approval process",
|
|
918
|
+
"steps": [
|
|
919
|
+
{
|
|
920
|
+
"title": "Document Review",
|
|
921
|
+
"type": "step",
|
|
922
|
+
"description": "Review all submitted documents"
|
|
923
|
+
}
|
|
924
|
+
]
|
|
925
|
+
}
|
|
926
|
+
]
|
|
927
|
+
}
|
|
928
|
+
\`\`\`
|
|
929
|
+
|
|
930
|
+
To create this workflow definition, use:
|
|
931
|
+
\`qik_create_content\` with type: "definition" and the workflow structure in the data field.
|
|
932
|
+
|
|
933
|
+
Would you like me to help you create a specific workflow definition?`,
|
|
741
934
|
}],
|
|
742
|
-
isError: true,
|
|
743
935
|
};
|
|
744
936
|
}
|
|
745
|
-
if (
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
let title = 'Unknown';
|
|
750
|
-
let plural = 'Unknown';
|
|
751
|
-
if (type && typeof type === 'object') {
|
|
752
|
-
if (type.definition) {
|
|
753
|
-
title = type.definition.title || title;
|
|
754
|
-
plural = type.definition.plural || plural;
|
|
755
|
-
}
|
|
756
|
-
else if (type.type) {
|
|
757
|
-
title = type.type.title || title;
|
|
758
|
-
plural = type.type.plural || plural;
|
|
759
|
-
}
|
|
760
|
-
else if (type.title) {
|
|
761
|
-
title = type.title || title;
|
|
762
|
-
plural = type.plural || plural;
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
return { key, title, plural };
|
|
766
|
-
});
|
|
937
|
+
// Check if user wants to add someone to an existing workflow (create workflow CARD)
|
|
938
|
+
if (intentAnalysis.isPersonAssignment ||
|
|
939
|
+
normalizedDesc.includes('add') && normalizedDesc.includes('to workflow') ||
|
|
940
|
+
normalizedDesc.includes('assign') && normalizedDesc.includes('workflow')) {
|
|
767
941
|
return {
|
|
768
942
|
content: [{
|
|
769
943
|
type: 'text',
|
|
770
|
-
text:
|
|
944
|
+
text: `👤 **WORKFLOW CARD CREATION (Person Assignment)**
|
|
945
|
+
|
|
946
|
+
You want to add a person to an existing workflow by creating a workflow card instance.
|
|
947
|
+
|
|
948
|
+
**WORKFLOW CARD vs WORKFLOW DEFINITION:**
|
|
949
|
+
- **Workflow Definition**: The template/structure (columns, steps, rules)
|
|
950
|
+
- **Workflow Card**: Individual items that move through the workflow
|
|
951
|
+
|
|
952
|
+
**TO ADD SOMEONE TO A WORKFLOW:**
|
|
953
|
+
|
|
954
|
+
1. **Find the workflow definition ID** first using:
|
|
955
|
+
\`qik_list_content\` with type: "definition" and search for your workflow name
|
|
956
|
+
|
|
957
|
+
2. **Create a workflow card** using:
|
|
958
|
+
\`qik_create_content\` with type: "workflowcard"
|
|
959
|
+
|
|
960
|
+
**EXAMPLE WORKFLOW CARD:**
|
|
961
|
+
\`\`\`json
|
|
962
|
+
{
|
|
963
|
+
"type": "workflowcard",
|
|
964
|
+
"title": "John Smith - Student Induction",
|
|
965
|
+
"reference": "PROFILE_ID_HERE",
|
|
966
|
+
"referenceType": "profile",
|
|
967
|
+
"data": {
|
|
968
|
+
"workflowDefinition": "WORKFLOW_DEFINITION_ID_HERE",
|
|
969
|
+
"currentStep": "application-received",
|
|
970
|
+
"assignedTo": ["USER_ID_HERE"],
|
|
971
|
+
"dueDate": "2024-01-15T09:00:00.000Z"
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
\`\`\`
|
|
975
|
+
|
|
976
|
+
**NEED MORE HELP?**
|
|
977
|
+
- What's the name of the workflow you want to add someone to?
|
|
978
|
+
- Who do you want to add to the workflow?
|
|
979
|
+
- Do you have the workflow definition ID?`,
|
|
771
980
|
}],
|
|
772
|
-
isError: true,
|
|
773
981
|
};
|
|
774
982
|
}
|
|
775
|
-
//
|
|
776
|
-
|
|
983
|
+
// General workflow guidance
|
|
984
|
+
return {
|
|
985
|
+
content: [{
|
|
986
|
+
type: 'text',
|
|
987
|
+
text: `🔄 **WORKFLOW SYSTEM GUIDANCE**
|
|
988
|
+
|
|
989
|
+
I detected you're working with workflows. Please clarify your intent:
|
|
990
|
+
|
|
991
|
+
**OPTION 1: Create Workflow Definition (Template)**
|
|
992
|
+
- "Create a new workflow"
|
|
993
|
+
- "Design a student onboarding workflow"
|
|
994
|
+
- "Set up a project management workflow"
|
|
995
|
+
→ Creates the structure, columns, steps, and rules
|
|
996
|
+
|
|
997
|
+
**OPTION 2: Add Person to Existing Workflow**
|
|
998
|
+
- "Add Jim to the student workflow"
|
|
999
|
+
- "Assign Sarah to project workflow"
|
|
1000
|
+
- "Put John in the onboarding process"
|
|
1001
|
+
→ Creates a workflow card instance for a person
|
|
1002
|
+
|
|
1003
|
+
**WORKFLOW CONCEPTS:**
|
|
1004
|
+
- **Definition**: The template (like a Kanban board layout)
|
|
1005
|
+
- **Card**: Individual items moving through the workflow
|
|
1006
|
+
- **Columns**: Stages (To Do, In Progress, Done)
|
|
1007
|
+
- **Steps**: Specific positions within columns
|
|
1008
|
+
- **Automation**: Code that runs when cards move
|
|
1009
|
+
|
|
1010
|
+
**AVAILABLE WORKFLOW CONTENT TYPES:**
|
|
1011
|
+
- \`definition\`: For creating workflow templates
|
|
1012
|
+
- \`workflowcard\`: For individual workflow instances
|
|
1013
|
+
- \`object\`: For custom workflow-related objects
|
|
1014
|
+
|
|
1015
|
+
Please specify: Are you creating a new workflow template, or adding someone to an existing workflow?`,
|
|
1016
|
+
}],
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Handles cases where no content types match the description
|
|
1021
|
+
*/
|
|
1022
|
+
async handleNoContentTypeMatches(description) {
|
|
1023
|
+
const availableTypes = Object.entries(this.glossary).map(([key, type]) => {
|
|
1024
|
+
let title = 'Unknown';
|
|
1025
|
+
let plural = 'Unknown';
|
|
1026
|
+
if (type && typeof type === 'object') {
|
|
1027
|
+
if (type.definition) {
|
|
1028
|
+
title = type.definition.title || title;
|
|
1029
|
+
plural = type.definition.plural || plural;
|
|
1030
|
+
}
|
|
1031
|
+
else if (type.type) {
|
|
1032
|
+
title = type.type.title || title;
|
|
1033
|
+
plural = type.type.plural || plural;
|
|
1034
|
+
}
|
|
1035
|
+
else if (type.title) {
|
|
1036
|
+
title = type.title || title;
|
|
1037
|
+
plural = type.plural || plural;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
return { key, title, plural };
|
|
1041
|
+
});
|
|
1042
|
+
// Group types by category for better organization
|
|
1043
|
+
const categorizedTypes = this.categorizeContentTypes(availableTypes);
|
|
1044
|
+
return {
|
|
1045
|
+
content: [{
|
|
1046
|
+
type: 'text',
|
|
1047
|
+
text: `❌ **NO MATCHING CONTENT TYPE FOUND**
|
|
1048
|
+
|
|
1049
|
+
I couldn't find a content type for "${description}".
|
|
1050
|
+
|
|
1051
|
+
**AVAILABLE CONTENT TYPES BY CATEGORY:**
|
|
1052
|
+
|
|
1053
|
+
${categorizedTypes}
|
|
1054
|
+
|
|
1055
|
+
**SUGGESTIONS:**
|
|
1056
|
+
- Try using more specific terms (e.g., "incident report" instead of "report")
|
|
1057
|
+
- Check if you meant to create a workflow definition or workflow card
|
|
1058
|
+
- Use the exact content type key from the list above
|
|
1059
|
+
|
|
1060
|
+
**NEED HELP?**
|
|
1061
|
+
- Use \`qik_get_glossary\` to see all available types with descriptions
|
|
1062
|
+
- Use \`qik_get_content_definition\` with a specific type to see its fields`,
|
|
1063
|
+
}],
|
|
1064
|
+
isError: true,
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Categorizes content types for better organization in help text
|
|
1069
|
+
*/
|
|
1070
|
+
categorizeContentTypes(types) {
|
|
1071
|
+
const categories = {
|
|
1072
|
+
'Core Content': [],
|
|
1073
|
+
'People & Profiles': [],
|
|
1074
|
+
'Workflows & Processes': [],
|
|
1075
|
+
'Communication': [],
|
|
1076
|
+
'Media & Files': [],
|
|
1077
|
+
'System & Admin': [],
|
|
1078
|
+
'Other': []
|
|
1079
|
+
};
|
|
1080
|
+
for (const type of types) {
|
|
1081
|
+
const key = type.key.toLowerCase();
|
|
1082
|
+
const title = type.title.toLowerCase();
|
|
1083
|
+
if (key.includes('profile') || key.includes('person') || key.includes('user')) {
|
|
1084
|
+
categories['People & Profiles'].push(type);
|
|
1085
|
+
}
|
|
1086
|
+
else if (key.includes('workflow') || key.includes('definition') || key.includes('process')) {
|
|
1087
|
+
categories['Workflows & Processes'].push(type);
|
|
1088
|
+
}
|
|
1089
|
+
else if (key.includes('comment') || key.includes('message') || key.includes('notification') || key.includes('email')) {
|
|
1090
|
+
categories['Communication'].push(type);
|
|
1091
|
+
}
|
|
1092
|
+
else if (key.includes('file') || key.includes('image') || key.includes('video') || key.includes('audio')) {
|
|
1093
|
+
categories['Media & Files'].push(type);
|
|
1094
|
+
}
|
|
1095
|
+
else if (key.includes('scope') || key.includes('role') || key.includes('policy') || key.includes('variable')) {
|
|
1096
|
+
categories['System & Admin'].push(type);
|
|
1097
|
+
}
|
|
1098
|
+
else if (['article', 'event', 'object'].includes(key)) {
|
|
1099
|
+
categories['Core Content'].push(type);
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
categories['Other'].push(type);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
let result = '';
|
|
1106
|
+
for (const [category, categoryTypes] of Object.entries(categories)) {
|
|
1107
|
+
if (categoryTypes.length > 0) {
|
|
1108
|
+
result += `\n**${category}:**\n`;
|
|
1109
|
+
result += categoryTypes.map(t => `- ${t.key}: ${t.title} (${t.plural})`).join('\n');
|
|
1110
|
+
result += '\n';
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
return result;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Handles cases where multiple content types match
|
|
1117
|
+
*/
|
|
1118
|
+
async handleMultipleContentTypeMatches(description, contentTypeMatches) {
|
|
1119
|
+
const matchDetails = contentTypeMatches.map(key => {
|
|
1120
|
+
const type = this.glossary[key];
|
|
1121
|
+
let title = 'Unknown';
|
|
1122
|
+
let plural = 'Unknown';
|
|
1123
|
+
let baseType = '';
|
|
1124
|
+
if (type && typeof type === 'object') {
|
|
1125
|
+
if (type.definition) {
|
|
1126
|
+
title = type.definition.title || title;
|
|
1127
|
+
plural = type.definition.plural || plural;
|
|
1128
|
+
baseType = type.definition.definesType || '';
|
|
1129
|
+
}
|
|
1130
|
+
else if (type.type) {
|
|
1131
|
+
title = type.type.title || title;
|
|
1132
|
+
plural = type.type.plural || plural;
|
|
1133
|
+
}
|
|
1134
|
+
else if (type.title) {
|
|
1135
|
+
title = type.title || title;
|
|
1136
|
+
plural = type.plural || plural;
|
|
1137
|
+
baseType = type.definesType || '';
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return { key, title, plural, baseType };
|
|
1141
|
+
});
|
|
1142
|
+
return {
|
|
1143
|
+
content: [{
|
|
1144
|
+
type: 'text',
|
|
1145
|
+
text: `🔍 **MULTIPLE CONTENT TYPES FOUND**
|
|
1146
|
+
|
|
1147
|
+
I found multiple content types that match "${description}". Please clarify which one you'd like to create:
|
|
1148
|
+
|
|
1149
|
+
${matchDetails.map(t => {
|
|
1150
|
+
let description = `- **${t.key}**: ${t.title} (${t.plural})`;
|
|
1151
|
+
if (t.baseType) {
|
|
1152
|
+
description += ` - extends ${t.baseType}`;
|
|
1153
|
+
}
|
|
1154
|
+
return description;
|
|
1155
|
+
}).join('\n')}
|
|
1156
|
+
|
|
1157
|
+
**TO PROCEED:**
|
|
1158
|
+
1. Choose the exact content type key from above
|
|
1159
|
+
2. Use \`qik_create_content\` with your chosen type
|
|
1160
|
+
3. Or use \`qik_get_content_definition\` to see field details first
|
|
1161
|
+
|
|
1162
|
+
**NEED MORE INFO?**
|
|
1163
|
+
Use \`qik_get_content_definition\` with any of the type keys above to see their specific fields and requirements.`,
|
|
1164
|
+
}],
|
|
1165
|
+
isError: true,
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Handles single content type match with comprehensive guidance
|
|
1170
|
+
*/
|
|
1171
|
+
async handleSingleContentTypeMatch(contentType, description, additionalData) {
|
|
777
1172
|
const typeInfo = this.getContentTypeInfo(contentType);
|
|
778
1173
|
if (!typeInfo) {
|
|
779
1174
|
return {
|
|
780
1175
|
content: [{
|
|
781
1176
|
type: 'text',
|
|
782
|
-
text:
|
|
1177
|
+
text: `❌ Found content type "${contentType}" but couldn't load its definition.`,
|
|
783
1178
|
}],
|
|
784
1179
|
isError: true,
|
|
785
1180
|
};
|
|
786
1181
|
}
|
|
787
|
-
//
|
|
1182
|
+
// Extract comprehensive type information
|
|
1183
|
+
const typeAnalysis = this.analyzeContentTypeStructure(typeInfo);
|
|
1184
|
+
let guidance = `✅ **CONTENT TYPE FOUND: ${contentType.toUpperCase()}**\n\n`;
|
|
1185
|
+
guidance += `**Type**: ${typeAnalysis.title}\n`;
|
|
1186
|
+
guidance += `**Description**: ${typeAnalysis.description || 'No description available'}\n`;
|
|
1187
|
+
if (typeAnalysis.baseType) {
|
|
1188
|
+
guidance += `**Extends**: ${typeAnalysis.baseType}\n`;
|
|
1189
|
+
}
|
|
1190
|
+
guidance += `\n**FIELD STRUCTURE:**\n`;
|
|
1191
|
+
if (typeAnalysis.requiredFields.length > 0) {
|
|
1192
|
+
guidance += `\n**Required Fields:**\n`;
|
|
1193
|
+
guidance += typeAnalysis.requiredFields.map(f => `- **${f.key}** (${f.title}): ${f.description || 'No description'}`).join('\n');
|
|
1194
|
+
}
|
|
1195
|
+
if (typeAnalysis.optionalFields.length > 0) {
|
|
1196
|
+
guidance += `\n\n**Optional Fields:**\n`;
|
|
1197
|
+
guidance += typeAnalysis.optionalFields.map(f => `- **${f.key}** (${f.title}): ${f.description || 'No description'}`).join('\n');
|
|
1198
|
+
}
|
|
1199
|
+
// Handle creation if data provided
|
|
1200
|
+
if (additionalData && typeof additionalData === 'object' && additionalData.title) {
|
|
1201
|
+
return await this.handleContentCreationWithData(contentType, additionalData, typeAnalysis);
|
|
1202
|
+
}
|
|
1203
|
+
// Provide creation guidance
|
|
1204
|
+
guidance += await this.generateCreationGuidance(contentType, typeAnalysis);
|
|
1205
|
+
return {
|
|
1206
|
+
content: [{
|
|
1207
|
+
type: 'text',
|
|
1208
|
+
text: guidance,
|
|
1209
|
+
}],
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Analyzes content type structure for comprehensive information
|
|
1214
|
+
*/
|
|
1215
|
+
analyzeContentTypeStructure(typeInfo) {
|
|
788
1216
|
let fields = [];
|
|
789
|
-
let
|
|
1217
|
+
let title = 'Unknown';
|
|
1218
|
+
let description = '';
|
|
1219
|
+
let baseType = '';
|
|
790
1220
|
if (typeInfo.definition) {
|
|
791
1221
|
fields = typeInfo.definition.fields || [];
|
|
792
|
-
|
|
1222
|
+
title = typeInfo.definition.title || title;
|
|
1223
|
+
description = typeInfo.definition.description || '';
|
|
1224
|
+
baseType = typeInfo.definition.definesType || '';
|
|
793
1225
|
}
|
|
794
1226
|
else if (typeInfo.type) {
|
|
795
1227
|
fields = typeInfo.type.fields || [];
|
|
796
|
-
|
|
1228
|
+
title = typeInfo.type.title || title;
|
|
1229
|
+
description = typeInfo.type.description || '';
|
|
797
1230
|
}
|
|
798
1231
|
else if (typeInfo.fields) {
|
|
799
1232
|
fields = typeInfo.fields || [];
|
|
800
|
-
|
|
1233
|
+
title = typeInfo.title || title;
|
|
1234
|
+
description = typeInfo.description || '';
|
|
1235
|
+
baseType = typeInfo.definesType || '';
|
|
801
1236
|
}
|
|
802
1237
|
const requiredFields = fields.filter((f) => f.minimum && f.minimum > 0);
|
|
803
1238
|
const optionalFields = fields.filter((f) => !f.minimum || f.minimum === 0);
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
}],
|
|
825
|
-
isError: true,
|
|
826
|
-
};
|
|
827
|
-
}
|
|
1239
|
+
return {
|
|
1240
|
+
title,
|
|
1241
|
+
description,
|
|
1242
|
+
baseType,
|
|
1243
|
+
fields,
|
|
1244
|
+
requiredFields,
|
|
1245
|
+
optionalFields,
|
|
1246
|
+
fieldCount: fields.length
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Handles content creation when data is provided
|
|
1251
|
+
*/
|
|
1252
|
+
async handleContentCreationWithData(contentType, additionalData, typeAnalysis) {
|
|
1253
|
+
// Check if scopes are provided
|
|
1254
|
+
if (!additionalData.meta || !additionalData.meta.scopes || !Array.isArray(additionalData.meta.scopes) || additionalData.meta.scopes.length === 0) {
|
|
1255
|
+
const scopeTree = await this.getAvailableScopes();
|
|
1256
|
+
if (scopeTree) {
|
|
1257
|
+
const availableScopes = this.extractScopesWithPermissions(scopeTree, 'create');
|
|
1258
|
+
if (availableScopes.length === 0) {
|
|
828
1259
|
return {
|
|
829
1260
|
content: [{
|
|
830
1261
|
type: 'text',
|
|
831
|
-
text:
|
|
1262
|
+
text: `🚫 **PERMISSION DENIED**\n\nYou don't have permission to create content in any scopes. Please contact your administrator.`,
|
|
832
1263
|
}],
|
|
1264
|
+
isError: true,
|
|
833
1265
|
};
|
|
834
1266
|
}
|
|
1267
|
+
return {
|
|
1268
|
+
content: [{
|
|
1269
|
+
type: 'text',
|
|
1270
|
+
text: `📍 **SCOPE SELECTION REQUIRED**\n\nTo create "${additionalData.title}" as a ${typeAnalysis.title}, you need to specify which scope to create it in.\n\n**Available Scopes:**\n${availableScopes.map(s => `- **${s.id}**: ${s.path}`).join('\n')}\n\n**TO CREATE:**\nUse \`qik_create_content\` with:\n- type: "${contentType}"\n- title: "${additionalData.title}"\n- meta: { "scopes": ["scope_id_here"] }\n- data: { /* your field values */ }`,
|
|
1271
|
+
}],
|
|
1272
|
+
};
|
|
835
1273
|
}
|
|
836
|
-
// Scopes are provided, proceed with creation
|
|
837
|
-
const title = additionalData.title || `New ${typeTitle}`;
|
|
838
|
-
const data = additionalData.data || {};
|
|
839
|
-
const meta = additionalData.meta || {};
|
|
840
|
-
return await this.createContent({
|
|
841
|
-
type: contentType,
|
|
842
|
-
title,
|
|
843
|
-
data,
|
|
844
|
-
meta,
|
|
845
|
-
});
|
|
846
1274
|
}
|
|
847
|
-
//
|
|
1275
|
+
// Proceed with creation
|
|
1276
|
+
const title = additionalData.title || `New ${typeAnalysis.title}`;
|
|
1277
|
+
const data = additionalData.data || {};
|
|
1278
|
+
const meta = additionalData.meta || {};
|
|
1279
|
+
return await this.createContent({
|
|
1280
|
+
type: contentType,
|
|
1281
|
+
title,
|
|
1282
|
+
data,
|
|
1283
|
+
meta,
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Generates comprehensive creation guidance
|
|
1288
|
+
*/
|
|
1289
|
+
async generateCreationGuidance(contentType, typeAnalysis) {
|
|
1290
|
+
let guidance = `\n\n**CREATION GUIDANCE:**\n`;
|
|
1291
|
+
// Get available scopes
|
|
848
1292
|
const scopeTree = await this.getAvailableScopes();
|
|
849
1293
|
let scopeGuidance = '';
|
|
850
1294
|
if (scopeTree) {
|
|
851
1295
|
const availableScopes = this.extractScopesWithPermissions(scopeTree, 'create');
|
|
852
1296
|
if (availableScopes.length > 0) {
|
|
853
|
-
scopeGuidance = `\n
|
|
1297
|
+
scopeGuidance = `\n**Available Scopes:**\n${availableScopes.map(s => `- **${s.id}**: ${s.path}`).join('\n')}\n`;
|
|
854
1298
|
}
|
|
855
1299
|
}
|
|
856
|
-
guidance +=
|
|
857
|
-
guidance +=
|
|
858
|
-
guidance += `-
|
|
859
|
-
guidance += `-
|
|
860
|
-
guidance += `-
|
|
1300
|
+
guidance += `\n**TO CREATE THIS CONTENT:**\n`;
|
|
1301
|
+
guidance += `Use \`qik_create_content\` with:\n`;
|
|
1302
|
+
guidance += `- **type**: "${contentType}"\n`;
|
|
1303
|
+
guidance += `- **title**: "Your title here"\n`;
|
|
1304
|
+
guidance += `- **meta**: { "scopes": ["scope_id_here"] } *(required)*\n`;
|
|
1305
|
+
guidance += `- **data**: { /* field values based on structure above */ }\n`;
|
|
861
1306
|
guidance += scopeGuidance;
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1307
|
+
// Add specific guidance for common types
|
|
1308
|
+
if (contentType === 'definition') {
|
|
1309
|
+
guidance += `\n**WORKFLOW DEFINITION EXAMPLE:**\n`;
|
|
1310
|
+
guidance += `For workflow definitions, include the workflow structure in the data field with columns, steps, and automation rules.\n`;
|
|
1311
|
+
}
|
|
1312
|
+
if (contentType.includes('comment') || contentType.includes('Comment')) {
|
|
1313
|
+
guidance += `\n**COMMENT CREATION:**\n`;
|
|
1314
|
+
guidance += `Comments require a reference to the item being commented on. Include:\n`;
|
|
1315
|
+
guidance += `- **reference**: ID of the item to comment on\n`;
|
|
1316
|
+
guidance += `- **referenceType**: Type of the referenced item\n`;
|
|
1317
|
+
}
|
|
1318
|
+
return guidance;
|
|
1319
|
+
}
|
|
1320
|
+
generateToolDescription(baseDescription, emoji = '') {
|
|
1321
|
+
const prefix = emoji ? `${emoji} ` : '';
|
|
1322
|
+
return `${prefix}${baseDescription.replace(/Qik/g, this.serverName)}`;
|
|
868
1323
|
}
|
|
869
1324
|
setupToolHandlers() {
|
|
870
1325
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -874,7 +1329,7 @@ EXAMPLES:
|
|
|
874
1329
|
// Authentication & Session
|
|
875
1330
|
{
|
|
876
1331
|
name: 'qik_get_user_session',
|
|
877
|
-
description: 'Get current user session information',
|
|
1332
|
+
description: this.generateToolDescription('Get current user session information', '👤'),
|
|
878
1333
|
inputSchema: {
|
|
879
1334
|
type: 'object',
|
|
880
1335
|
properties: {},
|
|
@@ -883,7 +1338,7 @@ EXAMPLES:
|
|
|
883
1338
|
// Content Type Discovery
|
|
884
1339
|
{
|
|
885
1340
|
name: 'qik_get_glossary',
|
|
886
|
-
description: 'Get all available content types and their definitions',
|
|
1341
|
+
description: this.generateToolDescription('Get all available content types and their definitions', '📚'),
|
|
887
1342
|
inputSchema: {
|
|
888
1343
|
type: 'object',
|
|
889
1344
|
properties: {},
|
|
@@ -891,7 +1346,7 @@ EXAMPLES:
|
|
|
891
1346
|
},
|
|
892
1347
|
{
|
|
893
1348
|
name: 'qik_get_content_definition',
|
|
894
|
-
description: 'Get definition for a specific content type',
|
|
1349
|
+
description: this.generateToolDescription('Get definition for a specific content type', '🔍'),
|
|
895
1350
|
inputSchema: {
|
|
896
1351
|
type: 'object',
|
|
897
1352
|
properties: {
|
|
@@ -907,7 +1362,7 @@ EXAMPLES:
|
|
|
907
1362
|
// Content Management
|
|
908
1363
|
{
|
|
909
1364
|
name: 'qik_get_content',
|
|
910
|
-
description: 'Get content item by ID or slug',
|
|
1365
|
+
description: this.generateToolDescription('Get content item by ID or slug', '📄'),
|
|
911
1366
|
inputSchema: {
|
|
912
1367
|
type: 'object',
|
|
913
1368
|
properties: {
|
|
@@ -928,7 +1383,27 @@ EXAMPLES:
|
|
|
928
1383
|
},
|
|
929
1384
|
{
|
|
930
1385
|
name: 'qik_list_content',
|
|
931
|
-
description:
|
|
1386
|
+
description: this.generateToolDescription(`List content items with advanced filtering and search capabilities. Supports complex queries like birthdays, date ranges, and sophisticated business logic.
|
|
1387
|
+
|
|
1388
|
+
**ENHANCED FILTER CAPABILITIES:**
|
|
1389
|
+
- 40+ comparators for dates, strings, numbers, and arrays
|
|
1390
|
+
- Hierarchical filters with 'and', 'or', 'nor' operators
|
|
1391
|
+
- Anniversary and birthday queries (anniversarynext, anniversarypast)
|
|
1392
|
+
- Date range filtering (datebetween, datepast, datenext)
|
|
1393
|
+
- String matching (contains, startswith, endswith, equal)
|
|
1394
|
+
- Numeric comparisons (greater, lesser, between)
|
|
1395
|
+
- Array operations (in, notin, valuesgreater)
|
|
1396
|
+
|
|
1397
|
+
**COMMON USE CASES:**
|
|
1398
|
+
- Find birthdays in next 10 days: {"operator":"and","filters":[{"key":"dob","comparator":"anniversarynext","value":10,"value2":"days"}]}
|
|
1399
|
+
- Recent content: {"operator":"and","filters":[{"key":"meta.created","comparator":"datepast","value":30,"value2":"days"}]}
|
|
1400
|
+
- Gender filtering: {"operator":"and","filters":[{"key":"gender","comparator":"equal","value":"male"}]}
|
|
1401
|
+
- Complex queries with OR logic for multiple conditions
|
|
1402
|
+
|
|
1403
|
+
**FIELD TARGETING:**
|
|
1404
|
+
- Use dot notation for nested fields: "meta.created", "data.customField"
|
|
1405
|
+
- Target specific profile fields: "firstName", "lastName", "emails"
|
|
1406
|
+
- Filter by metadata: "meta.scopes", "meta.tags", "meta.security"`, '📋'),
|
|
932
1407
|
inputSchema: {
|
|
933
1408
|
type: 'object',
|
|
934
1409
|
properties: {
|
|
@@ -939,28 +1414,50 @@ EXAMPLES:
|
|
|
939
1414
|
},
|
|
940
1415
|
search: {
|
|
941
1416
|
type: 'string',
|
|
942
|
-
description: 'Search keywords',
|
|
1417
|
+
description: 'Search keywords - searches within title, tags, and text areas',
|
|
943
1418
|
},
|
|
944
1419
|
filter: this.generateEnhancedFilterSchema(),
|
|
945
1420
|
sort: {
|
|
946
1421
|
type: 'object',
|
|
1422
|
+
description: 'Sorting configuration for results',
|
|
947
1423
|
properties: {
|
|
948
|
-
key: {
|
|
949
|
-
|
|
950
|
-
|
|
1424
|
+
key: {
|
|
1425
|
+
type: 'string',
|
|
1426
|
+
description: 'Field to sort by (e.g., "title", "meta.created", "data.customField")'
|
|
1427
|
+
},
|
|
1428
|
+
direction: {
|
|
1429
|
+
type: 'string',
|
|
1430
|
+
enum: ['asc', 'desc'],
|
|
1431
|
+
description: 'Sort direction: ascending or descending'
|
|
1432
|
+
},
|
|
1433
|
+
type: {
|
|
1434
|
+
type: 'string',
|
|
1435
|
+
enum: ['string', 'number', 'date'],
|
|
1436
|
+
description: 'Data type for proper sorting behavior'
|
|
1437
|
+
},
|
|
951
1438
|
},
|
|
952
1439
|
},
|
|
953
1440
|
page: {
|
|
954
1441
|
type: 'object',
|
|
1442
|
+
description: 'Pagination settings',
|
|
955
1443
|
properties: {
|
|
956
|
-
size: {
|
|
957
|
-
|
|
1444
|
+
size: {
|
|
1445
|
+
type: 'number',
|
|
1446
|
+
minimum: 1,
|
|
1447
|
+
maximum: 100,
|
|
1448
|
+
description: 'Number of items per page (1-100)'
|
|
1449
|
+
},
|
|
1450
|
+
index: {
|
|
1451
|
+
type: 'number',
|
|
1452
|
+
minimum: 1,
|
|
1453
|
+
description: 'Page number to retrieve (starts at 1)'
|
|
1454
|
+
},
|
|
958
1455
|
},
|
|
959
1456
|
},
|
|
960
1457
|
select: {
|
|
961
1458
|
type: 'array',
|
|
962
1459
|
items: { type: 'string' },
|
|
963
|
-
description: '
|
|
1460
|
+
description: 'Specific fields to include in response (e.g., ["title", "data.make", "meta.created"])',
|
|
964
1461
|
},
|
|
965
1462
|
},
|
|
966
1463
|
required: ['type'],
|
|
@@ -968,39 +1465,67 @@ EXAMPLES:
|
|
|
968
1465
|
},
|
|
969
1466
|
{
|
|
970
1467
|
name: 'qik_create_content',
|
|
971
|
-
description:
|
|
1468
|
+
description: this.generateToolDescription(`Create new content item with intelligent field structure handling.
|
|
1469
|
+
|
|
1470
|
+
**FIELD STRUCTURE INTELLIGENCE:**
|
|
1471
|
+
- Automatically separates root-level fields from data object fields
|
|
1472
|
+
- Handles comment inheritance (comments inherit scopes from referenced items)
|
|
1473
|
+
- Validates field requirements based on content type definitions
|
|
1474
|
+
- Supports workflow definitions, workflow cards, and all content types
|
|
1475
|
+
|
|
1476
|
+
**FIELD PLACEMENT RULES:**
|
|
1477
|
+
- **Root Level**: reference, referenceType, body, organisation, title, meta
|
|
1478
|
+
- **Data Object**: Custom fields defined in content type definitions (definedFields)
|
|
1479
|
+
- **Meta Object**: scopes (required), tags, security, personaAuthor, etc.
|
|
1480
|
+
|
|
1481
|
+
**CONTENT TYPE EXAMPLES:**
|
|
1482
|
+
- **Comments**: Require reference + referenceType, inherit scopes automatically
|
|
1483
|
+
- **Workflow Definitions**: Use data object for workflow structure (columns, steps, automation)
|
|
1484
|
+
- **Workflow Cards**: Reference profiles, link to workflow definitions
|
|
1485
|
+
- **Profiles**: firstName, lastName at root, custom fields in data object
|
|
1486
|
+
- **Articles**: body at root level, custom article fields in data object
|
|
1487
|
+
|
|
1488
|
+
**SCOPE INHERITANCE:**
|
|
1489
|
+
- Comments automatically inherit scopes from referenced items
|
|
1490
|
+
- Other content types require explicit scope assignment
|
|
1491
|
+
- Use qik_get_scopes to find available scopes with permissions
|
|
1492
|
+
|
|
1493
|
+
**VALIDATION:**
|
|
1494
|
+
- Checks content type exists and user has access
|
|
1495
|
+
- Validates required fields based on content type definition
|
|
1496
|
+
- Ensures proper field placement (root vs data object)`, '✨'),
|
|
972
1497
|
inputSchema: {
|
|
973
1498
|
type: 'object',
|
|
974
1499
|
properties: {
|
|
975
1500
|
type: {
|
|
976
1501
|
type: 'string',
|
|
977
|
-
description: 'Content type to create',
|
|
1502
|
+
description: 'Content type to create (use qik_get_glossary to see all available types)',
|
|
978
1503
|
enum: Object.keys(this.glossary),
|
|
979
1504
|
},
|
|
980
1505
|
title: {
|
|
981
1506
|
type: 'string',
|
|
982
|
-
description: 'Content title',
|
|
1507
|
+
description: 'Content title (required for all content types)',
|
|
983
1508
|
},
|
|
984
1509
|
// Generate dynamic properties based on content types
|
|
985
1510
|
...this.generateDynamicContentProperties(),
|
|
986
1511
|
meta: {
|
|
987
1512
|
type: 'object',
|
|
988
|
-
description: 'Meta information (scopes
|
|
1513
|
+
description: 'Meta information (scopes required for most content types)',
|
|
989
1514
|
properties: {
|
|
990
1515
|
scopes: {
|
|
991
1516
|
type: 'array',
|
|
992
1517
|
items: { type: 'string' },
|
|
993
|
-
description: 'Scope IDs where this content should be stored',
|
|
1518
|
+
description: 'Scope IDs where this content should be stored (REQUIRED - use qik_get_scopes to find available)',
|
|
994
1519
|
},
|
|
995
1520
|
tags: {
|
|
996
1521
|
type: 'array',
|
|
997
1522
|
items: { type: 'string' },
|
|
998
|
-
description: 'Tag IDs for
|
|
1523
|
+
description: 'Tag IDs for categorization and search',
|
|
999
1524
|
},
|
|
1000
1525
|
security: {
|
|
1001
1526
|
type: 'string',
|
|
1002
1527
|
enum: ['public', 'secure', 'private'],
|
|
1003
|
-
description: 'Security level
|
|
1528
|
+
description: 'Security level: public (everyone), secure (authenticated), private (restricted)',
|
|
1004
1529
|
},
|
|
1005
1530
|
},
|
|
1006
1531
|
},
|
|
@@ -1010,7 +1535,7 @@ EXAMPLES:
|
|
|
1010
1535
|
},
|
|
1011
1536
|
{
|
|
1012
1537
|
name: 'qik_update_content',
|
|
1013
|
-
description: 'Update existing content item',
|
|
1538
|
+
description: this.generateToolDescription('Update existing content item', '✏️'),
|
|
1014
1539
|
inputSchema: {
|
|
1015
1540
|
type: 'object',
|
|
1016
1541
|
properties: {
|
|
@@ -1033,7 +1558,7 @@ EXAMPLES:
|
|
|
1033
1558
|
},
|
|
1034
1559
|
{
|
|
1035
1560
|
name: 'qik_delete_content',
|
|
1036
|
-
description: 'Delete content item',
|
|
1561
|
+
description: this.generateToolDescription('Delete content item', '🗑️'),
|
|
1037
1562
|
inputSchema: {
|
|
1038
1563
|
type: 'object',
|
|
1039
1564
|
properties: {
|
|
@@ -1048,7 +1573,7 @@ EXAMPLES:
|
|
|
1048
1573
|
// Profile Management
|
|
1049
1574
|
{
|
|
1050
1575
|
name: 'qik_list_profiles',
|
|
1051
|
-
description: 'Search and list profiles/people',
|
|
1576
|
+
description: this.generateToolDescription('Search and list profiles/people', '👥'),
|
|
1052
1577
|
inputSchema: {
|
|
1053
1578
|
type: 'object',
|
|
1054
1579
|
properties: {
|
|
@@ -1072,7 +1597,7 @@ EXAMPLES:
|
|
|
1072
1597
|
},
|
|
1073
1598
|
{
|
|
1074
1599
|
name: 'qik_create_profile',
|
|
1075
|
-
description: 'Create new profile/person',
|
|
1600
|
+
description: this.generateToolDescription('Create new profile/person', '👤'),
|
|
1076
1601
|
inputSchema: {
|
|
1077
1602
|
type: 'object',
|
|
1078
1603
|
properties: {
|
|
@@ -1116,7 +1641,7 @@ EXAMPLES:
|
|
|
1116
1641
|
// Form Management
|
|
1117
1642
|
{
|
|
1118
1643
|
name: 'qik_get_form',
|
|
1119
|
-
description: 'Get form definition',
|
|
1644
|
+
description: this.generateToolDescription('Get form definition', '📝'),
|
|
1120
1645
|
inputSchema: {
|
|
1121
1646
|
type: 'object',
|
|
1122
1647
|
properties: {
|
|
@@ -1130,7 +1655,7 @@ EXAMPLES:
|
|
|
1130
1655
|
},
|
|
1131
1656
|
{
|
|
1132
1657
|
name: 'qik_submit_form',
|
|
1133
|
-
description: 'Submit form data',
|
|
1658
|
+
description: this.generateToolDescription('Submit form data', '📤'),
|
|
1134
1659
|
inputSchema: {
|
|
1135
1660
|
type: 'object',
|
|
1136
1661
|
properties: {
|
|
@@ -1149,7 +1674,7 @@ EXAMPLES:
|
|
|
1149
1674
|
// File Management
|
|
1150
1675
|
{
|
|
1151
1676
|
name: 'qik_upload_file',
|
|
1152
|
-
description: 'Upload file to
|
|
1677
|
+
description: this.generateToolDescription('Upload file to platform', '📁'),
|
|
1153
1678
|
inputSchema: {
|
|
1154
1679
|
type: 'object',
|
|
1155
1680
|
properties: {
|
|
@@ -1180,7 +1705,7 @@ EXAMPLES:
|
|
|
1180
1705
|
// Search & Discovery
|
|
1181
1706
|
{
|
|
1182
1707
|
name: 'qik_search_content',
|
|
1183
|
-
description: 'Global content search across all types',
|
|
1708
|
+
description: this.generateToolDescription('Global content search across all types', '🔎'),
|
|
1184
1709
|
inputSchema: {
|
|
1185
1710
|
type: 'object',
|
|
1186
1711
|
properties: {
|
|
@@ -1208,7 +1733,7 @@ EXAMPLES:
|
|
|
1208
1733
|
},
|
|
1209
1734
|
{
|
|
1210
1735
|
name: 'qik_get_scopes',
|
|
1211
|
-
description: 'Get available scopes/permissions tree',
|
|
1736
|
+
description: this.generateToolDescription('Get available scopes/permissions tree', '🔐'),
|
|
1212
1737
|
inputSchema: {
|
|
1213
1738
|
type: 'object',
|
|
1214
1739
|
properties: {},
|
|
@@ -1217,7 +1742,7 @@ EXAMPLES:
|
|
|
1217
1742
|
// Utility Tools
|
|
1218
1743
|
{
|
|
1219
1744
|
name: 'qik_get_smartlist',
|
|
1220
|
-
description: 'Execute a smartlist query',
|
|
1745
|
+
description: this.generateToolDescription('Execute a smartlist query', '📊'),
|
|
1221
1746
|
inputSchema: {
|
|
1222
1747
|
type: 'object',
|
|
1223
1748
|
properties: {
|
|
@@ -1229,28 +1754,70 @@ EXAMPLES:
|
|
|
1229
1754
|
required: ['id'],
|
|
1230
1755
|
},
|
|
1231
1756
|
},
|
|
1232
|
-
// Intelligent Content Creation
|
|
1757
|
+
// Intelligent Content Creation with Advanced Disambiguation
|
|
1233
1758
|
{
|
|
1234
1759
|
name: 'qik_create_content_intelligent',
|
|
1235
|
-
description:
|
|
1760
|
+
description: this.generateToolDescription(`Intelligently create content with advanced disambiguation logic.
|
|
1761
|
+
|
|
1762
|
+
**WORKFLOW DISAMBIGUATION:**
|
|
1763
|
+
- "create a workflow" → Creates workflow DEFINITION (template)
|
|
1764
|
+
- "add Jim to workflow X" → Creates workflow CARD (instance)
|
|
1765
|
+
- "design student onboarding workflow" → Creates workflow DEFINITION
|
|
1766
|
+
- "assign Sarah to project workflow" → Creates workflow CARD
|
|
1767
|
+
|
|
1768
|
+
**CONTENT TYPE DISAMBIGUATION:**
|
|
1769
|
+
- Automatically detects intent between definitions vs instances
|
|
1770
|
+
- Provides comprehensive guidance for workflow systems
|
|
1771
|
+
- Categorizes content types for better organization
|
|
1772
|
+
- Handles scope permissions and requirements
|
|
1773
|
+
|
|
1774
|
+
**EXAMPLES:**
|
|
1775
|
+
- "create an incident report" → Finds incident report content type
|
|
1776
|
+
- "make a new workflow" → Guides through workflow definition creation
|
|
1777
|
+
- "add person to existing workflow" → Guides through workflow card creation
|
|
1778
|
+
- "design a student induction process" → Creates workflow definition
|
|
1779
|
+
|
|
1780
|
+
**WORKFLOW CONCEPTS EXPLAINED:**
|
|
1781
|
+
- **Workflow Definition**: Template with columns, steps, automation rules
|
|
1782
|
+
- **Workflow Card**: Individual items that move through the workflow
|
|
1783
|
+
- **Columns**: Stages like "To Do", "In Progress", "Done"
|
|
1784
|
+
- **Steps**: Specific positions within columns
|
|
1785
|
+
- **Automation**: Entry/exit/success/fail functions`, '🧠'),
|
|
1236
1786
|
inputSchema: {
|
|
1237
1787
|
type: 'object',
|
|
1238
1788
|
properties: {
|
|
1239
1789
|
description: {
|
|
1240
1790
|
type: 'string',
|
|
1241
|
-
description: 'Natural language description of what to create
|
|
1791
|
+
description: 'Natural language description of what to create. Examples: "create an incident report", "make a new workflow", "add Jim to student workflow", "design onboarding process"',
|
|
1242
1792
|
},
|
|
1243
1793
|
title: {
|
|
1244
1794
|
type: 'string',
|
|
1245
|
-
description: 'Title for the content',
|
|
1795
|
+
description: 'Title for the content (optional - will be requested if needed)',
|
|
1246
1796
|
},
|
|
1247
1797
|
data: {
|
|
1248
1798
|
type: 'object',
|
|
1249
|
-
description: 'Content data fields',
|
|
1799
|
+
description: 'Content data fields (optional - will be guided through structure)',
|
|
1250
1800
|
},
|
|
1251
1801
|
meta: {
|
|
1252
1802
|
type: 'object',
|
|
1253
|
-
description: 'Meta information
|
|
1803
|
+
description: 'Meta information including scopes, tags, security level (will be guided through requirements)',
|
|
1804
|
+
properties: {
|
|
1805
|
+
scopes: {
|
|
1806
|
+
type: 'array',
|
|
1807
|
+
items: { type: 'string' },
|
|
1808
|
+
description: 'Scope IDs where content should be created'
|
|
1809
|
+
},
|
|
1810
|
+
tags: {
|
|
1811
|
+
type: 'array',
|
|
1812
|
+
items: { type: 'string' },
|
|
1813
|
+
description: 'Tag IDs for categorization'
|
|
1814
|
+
},
|
|
1815
|
+
security: {
|
|
1816
|
+
type: 'string',
|
|
1817
|
+
enum: ['public', 'secure', 'private'],
|
|
1818
|
+
description: 'Security level for the content'
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1254
1821
|
},
|
|
1255
1822
|
},
|
|
1256
1823
|
required: ['description'],
|
|
@@ -1578,6 +2145,78 @@ ${JSON.stringify({
|
|
|
1578
2145
|
definedFields = contentTypeInfo.type.definedFields || [];
|
|
1579
2146
|
baseType = contentTypeInfo.type.definesType;
|
|
1580
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
|
+
}
|
|
1581
2220
|
// Structure payload correctly based on field definitions
|
|
1582
2221
|
const payload = {
|
|
1583
2222
|
title: args.title,
|
|
@@ -1594,9 +2233,6 @@ ${JSON.stringify({
|
|
|
1594
2233
|
}
|
|
1595
2234
|
}
|
|
1596
2235
|
if (args.data && typeof args.data === 'object') {
|
|
1597
|
-
// Create field key maps for quick lookup
|
|
1598
|
-
const rootFieldKeys = new Set(fields.map(f => f.key));
|
|
1599
|
-
const dataFieldKeys = new Set(definedFields.map(f => f.key));
|
|
1600
2236
|
// Separate the provided data based on field definitions
|
|
1601
2237
|
for (const [key, value] of Object.entries(args.data)) {
|
|
1602
2238
|
if (rootFieldKeys.has(key)) {
|
|
@@ -1640,7 +2276,15 @@ ${JSON.stringify({
|
|
|
1640
2276
|
return {
|
|
1641
2277
|
content: [{
|
|
1642
2278
|
type: 'text',
|
|
1643
|
-
text:
|
|
2279
|
+
text: `✅ **SUCCESSFULLY CREATED ${args.type.toUpperCase()} CONTENT**
|
|
2280
|
+
|
|
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
|
+
**Created Content:**
|
|
2287
|
+
${JSON.stringify(response.data, null, 2)}`,
|
|
1644
2288
|
}],
|
|
1645
2289
|
};
|
|
1646
2290
|
}
|