@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.
@@ -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 user session first
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 root level for applicable content types)`,
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: 'Content-specific fields (definedFields go here). Structure varies by content type.',
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
- // First, try to find content types that match
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
- // If we can't find any matches, provide helpful suggestions
718
- const availableTypes = Object.entries(this.glossary).map(([key, type]) => {
719
- let title = 'Unknown';
720
- let plural = 'Unknown';
721
- if (type && typeof type === 'object') {
722
- if (type.definition) {
723
- title = type.definition.title || title;
724
- plural = type.definition.plural || plural;
725
- }
726
- else if (type.type) {
727
- title = type.type.title || title;
728
- plural = type.type.plural || plural;
729
- }
730
- else if (type.title) {
731
- title = type.title || title;
732
- plural = type.plural || plural;
733
- }
734
- }
735
- return { key, title, plural };
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: `I couldn't find a content type for "${description}". Here are the available content types:\n\n${availableTypes.map(t => `- ${t.key}: ${t.title} (${t.plural})`).join('\n')}\n\nPlease specify which content type you'd like to create, or I can help you create content using one of these types.`,
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 (contentTypeMatches.length > 1) {
746
- // Multiple matches found - ask for clarification
747
- const matchDetails = contentTypeMatches.map(key => {
748
- const type = this.glossary[key];
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: `I found multiple content types that match "${description}". Please clarify which one you'd like to create:\n\n${matchDetails.map(t => `- ${t.key}: ${t.title} (${t.plural})`).join('\n')}\n\nPlease specify the exact content type key you'd like to use.`,
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
- // Single match found
776
- const contentType = contentTypeMatches[0];
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: `Found content type "${contentType}" but couldn't load its definition.`,
1177
+ text: `❌ Found content type "${contentType}" but couldn't load its definition.`,
783
1178
  }],
784
1179
  isError: true,
785
1180
  };
786
1181
  }
787
- // Analyze the fields to provide guidance
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 typeTitle = 'Unknown';
1217
+ let title = 'Unknown';
1218
+ let description = '';
1219
+ let baseType = '';
790
1220
  if (typeInfo.definition) {
791
1221
  fields = typeInfo.definition.fields || [];
792
- typeTitle = typeInfo.definition.title || typeTitle;
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
- typeTitle = typeInfo.type.title || typeTitle;
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
- typeTitle = typeInfo.title || typeTitle;
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
- let guidance = `I found the content type "${contentType}" (${typeTitle}) for "${description}".\n\n`;
805
- if (requiredFields.length > 0) {
806
- guidance += `Required fields:\n${requiredFields.map(f => `- ${f.key} (${f.title}): ${f.description || 'No description'}`).join('\n')}\n\n`;
807
- }
808
- if (optionalFields.length > 0) {
809
- guidance += `Optional fields:\n${optionalFields.map(f => `- ${f.key} (${f.title}): ${f.description || 'No description'}`).join('\n')}\n\n`;
810
- }
811
- // If additional data was provided, check if we have scopes and try to create the content
812
- if (additionalData && typeof additionalData === 'object' && additionalData.title) {
813
- // Check if scopes are provided
814
- if (!additionalData.meta || !additionalData.meta.scopes || !Array.isArray(additionalData.meta.scopes) || additionalData.meta.scopes.length === 0) {
815
- // Get available scopes
816
- const scopeTree = await this.getAvailableScopes();
817
- if (scopeTree) {
818
- const availableScopes = this.extractScopesWithPermissions(scopeTree, 'create');
819
- if (availableScopes.length === 0) {
820
- return {
821
- content: [{
822
- type: 'text',
823
- text: `You don't have permission to create content in any scopes. Please contact your administrator.`,
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: `To create "${additionalData.title}" as a ${typeTitle}, you need to specify which scope to create it in. You have permission to create content in these scopes:\n\n${availableScopes.map(s => `- ${s.id}: ${s.path}`).join('\n')}\n\nPlease use the qik_create_content tool with:\n- type: "${contentType}"\n- title: "${additionalData.title}"\n- meta: { "scopes": ["scope_id_here"] }\n- data: { /* your field values */ }`,
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
- // Get available scopes for guidance
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\nAvailable scopes you can create content in:\n${availableScopes.map(s => `- ${s.id}: ${s.path}`).join('\n')}\n`;
1297
+ scopeGuidance = `\n**Available Scopes:**\n${availableScopes.map(s => `- **${s.id}**: ${s.path}`).join('\n')}\n`;
854
1298
  }
855
1299
  }
856
- guidance += `To create this content, use the qik_create_content tool with:\n`;
857
- guidance += `- type: "${contentType}"\n`;
858
- guidance += `- title: "Your title here"\n`;
859
- guidance += `- meta: { "scopes": ["scope_id_here"] } (required)\n`;
860
- guidance += `- data: { /* field values */ }\n`;
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
- return {
863
- content: [{
864
- type: 'text',
865
- text: guidance,
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: 'List content items with advanced filtering and search capabilities. Supports complex queries like birthdays, date ranges, and sophisticated business logic.',
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: { type: 'string' },
949
- direction: { type: 'string', enum: ['asc', 'desc'] },
950
- type: { type: 'string', enum: ['string', 'number', 'date'] },
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: { type: 'number', minimum: 1, maximum: 100 },
957
- index: { type: 'number', minimum: 1 },
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: 'Fields to include in response',
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: 'Create new content item. IMPORTANT: Fields are structured based on content type - some go at root level, others in data object.',
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, tags, etc.)',
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 this content',
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 for this content',
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 Qik',
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: 'Intelligently create content by description (e.g., "create an incident report", "make a new event")',
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 (e.g., "incident report", "event", "article")',
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 (scopes, tags, etc.)',
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: `Successfully created ${args.type} content:\n${JSON.stringify(response.data, null, 2)}`,
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
  }