@qikdev/mcp 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -175
- package/build/src/index.d.ts +20 -10
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +517 -149
- package/build/src/index.js.map +1 -1
- package/package.json +2 -2
package/build/src/index.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Qik Platform MCP Server
|
|
3
|
+
* Qik Platform MCP Server - Enhanced Version
|
|
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,
|
|
7
7
|
* user management, forms, files, and more.
|
|
8
8
|
*
|
|
9
|
-
* Features:
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
16
|
-
* - Scope and permission management
|
|
9
|
+
* Key Features:
|
|
10
|
+
* - Glossary-driven architecture for dynamic content type discovery
|
|
11
|
+
* - Smart validation based on content type definitions
|
|
12
|
+
* - Context-aware error handling and suggestions
|
|
13
|
+
* - Dynamic tool schema generation
|
|
14
|
+
* - Comprehensive API coverage
|
|
15
|
+
* - Intelligent request building and field validation
|
|
17
16
|
*/
|
|
18
17
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
19
18
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -27,13 +26,16 @@ export class QikMCPServer {
|
|
|
27
26
|
server;
|
|
28
27
|
axiosInstance;
|
|
29
28
|
glossary = {};
|
|
29
|
+
userSession = null;
|
|
30
|
+
lastGlossaryUpdate = 0;
|
|
31
|
+
GLOSSARY_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
30
32
|
constructor() {
|
|
31
33
|
if (!QIK_ACCESS_TOKEN) {
|
|
32
34
|
throw new Error('QIK_ACCESS_TOKEN environment variable is required. Run "qik-mcp-server setup" to configure.');
|
|
33
35
|
}
|
|
34
36
|
this.server = new Server({
|
|
35
37
|
name: "qik-mcp-server",
|
|
36
|
-
version: "
|
|
38
|
+
version: "2.0.0",
|
|
37
39
|
}, {
|
|
38
40
|
capabilities: {
|
|
39
41
|
tools: {},
|
|
@@ -49,7 +51,7 @@ export class QikMCPServer {
|
|
|
49
51
|
timeout: 30000,
|
|
50
52
|
});
|
|
51
53
|
this.setupToolHandlers();
|
|
52
|
-
this.
|
|
54
|
+
this.initializeServer();
|
|
53
55
|
// Error handling
|
|
54
56
|
this.server.onerror = (error) => console.error('[MCP Error]', error);
|
|
55
57
|
process.on('SIGINT', async () => {
|
|
@@ -57,10 +59,37 @@ export class QikMCPServer {
|
|
|
57
59
|
process.exit(0);
|
|
58
60
|
});
|
|
59
61
|
}
|
|
60
|
-
async
|
|
62
|
+
async initializeServer() {
|
|
63
|
+
try {
|
|
64
|
+
// Load user session first
|
|
65
|
+
await this.loadUserSession();
|
|
66
|
+
// Then load glossary
|
|
67
|
+
await this.loadGlossary();
|
|
68
|
+
console.error(`[Qik MCP] Initialized with ${Object.keys(this.glossary).length} content types`);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error('[Qik MCP] Failed to initialize server:', this.formatError(error));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async loadUserSession() {
|
|
75
|
+
try {
|
|
76
|
+
const response = await this.axiosInstance.get('/user');
|
|
77
|
+
this.userSession = response.data.session || response.data;
|
|
78
|
+
console.error(`[Qik MCP] Authenticated as ${this.userSession?.firstName} ${this.userSession?.lastName}`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error('[Qik MCP] Failed to load user session:', this.formatError(error));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async loadGlossary(force = false) {
|
|
85
|
+
const now = Date.now();
|
|
86
|
+
if (!force && this.lastGlossaryUpdate && (now - this.lastGlossaryUpdate) < this.GLOSSARY_CACHE_TTL) {
|
|
87
|
+
return; // Use cached version
|
|
88
|
+
}
|
|
61
89
|
try {
|
|
62
90
|
const response = await this.axiosInstance.get('/glossary');
|
|
63
91
|
this.glossary = response.data || {};
|
|
92
|
+
this.lastGlossaryUpdate = now;
|
|
64
93
|
console.error(`[Qik MCP] Loaded ${Object.keys(this.glossary).length} content types from glossary`);
|
|
65
94
|
}
|
|
66
95
|
catch (error) {
|
|
@@ -71,15 +100,128 @@ export class QikMCPServer {
|
|
|
71
100
|
if (axios.isAxiosError(error)) {
|
|
72
101
|
const axiosError = error;
|
|
73
102
|
if (axiosError.response) {
|
|
74
|
-
|
|
103
|
+
const status = axiosError.response.status;
|
|
104
|
+
const data = axiosError.response.data;
|
|
105
|
+
return `HTTP ${status}: ${JSON.stringify(data)}`;
|
|
75
106
|
}
|
|
76
107
|
return `Network error: ${axiosError.message}`;
|
|
77
108
|
}
|
|
78
109
|
return error.message || String(error);
|
|
79
110
|
}
|
|
111
|
+
getContentTypeInfo(type) {
|
|
112
|
+
return this.glossary[type] || null;
|
|
113
|
+
}
|
|
114
|
+
validateContentType(type) {
|
|
115
|
+
const info = this.getContentTypeInfo(type);
|
|
116
|
+
if (!info) {
|
|
117
|
+
const availableTypes = Object.keys(this.glossary).join(', ');
|
|
118
|
+
return {
|
|
119
|
+
valid: false,
|
|
120
|
+
error: `Content type '${type}' not found. Available types: ${availableTypes}`
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return { valid: true, info };
|
|
124
|
+
}
|
|
125
|
+
validateFieldData(data, fields, contentType) {
|
|
126
|
+
const errors = [];
|
|
127
|
+
for (const field of fields) {
|
|
128
|
+
const value = data[field.key];
|
|
129
|
+
// Check required fields
|
|
130
|
+
if (field.minimum && field.minimum > 0 && (!value || (Array.isArray(value) && value.length === 0))) {
|
|
131
|
+
errors.push(`Field '${field.key}' (${field.title}) is required for ${contentType}`);
|
|
132
|
+
}
|
|
133
|
+
// Type validation
|
|
134
|
+
if (value !== undefined && value !== null) {
|
|
135
|
+
switch (field.type) {
|
|
136
|
+
case 'string':
|
|
137
|
+
if (typeof value !== 'string') {
|
|
138
|
+
errors.push(`Field '${field.key}' must be a string, got ${typeof value}`);
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
case 'number':
|
|
142
|
+
case 'integer':
|
|
143
|
+
if (typeof value !== 'number') {
|
|
144
|
+
errors.push(`Field '${field.key}' must be a number, got ${typeof value}`);
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
case 'boolean':
|
|
148
|
+
if (typeof value !== 'boolean') {
|
|
149
|
+
errors.push(`Field '${field.key}' must be a boolean, got ${typeof value}`);
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
case 'array':
|
|
153
|
+
if (!Array.isArray(value)) {
|
|
154
|
+
errors.push(`Field '${field.key}' must be an array, got ${typeof value}`);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
case 'reference':
|
|
158
|
+
if (field.referenceType && typeof value === 'string') {
|
|
159
|
+
// Basic validation - could be enhanced to check if referenced item exists
|
|
160
|
+
}
|
|
161
|
+
else if (Array.isArray(value) && field.maximum !== 1) {
|
|
162
|
+
// Array of references
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
errors.push(`Field '${field.key}' must be a valid reference to ${field.referenceType || 'content'}`);
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return { valid: errors.length === 0, errors };
|
|
172
|
+
}
|
|
173
|
+
generateFieldSchema(field) {
|
|
174
|
+
const schema = {
|
|
175
|
+
type: this.mapFieldTypeToJsonSchema(field.type),
|
|
176
|
+
description: field.description || field.title,
|
|
177
|
+
};
|
|
178
|
+
if (field.options && field.options.length > 0) {
|
|
179
|
+
schema.enum = field.options.map(opt => opt.value);
|
|
180
|
+
}
|
|
181
|
+
if (field.type === 'array' && field.fields) {
|
|
182
|
+
schema.items = {
|
|
183
|
+
type: 'object',
|
|
184
|
+
properties: this.generateFieldsSchema(field.fields),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (field.type === 'reference') {
|
|
188
|
+
schema.description += field.referenceType ? ` (references ${field.referenceType})` : ' (content reference)';
|
|
189
|
+
}
|
|
190
|
+
return schema;
|
|
191
|
+
}
|
|
192
|
+
generateFieldsSchema(fields) {
|
|
193
|
+
const properties = {};
|
|
194
|
+
for (const field of fields) {
|
|
195
|
+
properties[field.key] = this.generateFieldSchema(field);
|
|
196
|
+
}
|
|
197
|
+
return properties;
|
|
198
|
+
}
|
|
199
|
+
mapFieldTypeToJsonSchema(fieldType) {
|
|
200
|
+
switch (fieldType) {
|
|
201
|
+
case 'string':
|
|
202
|
+
case 'email':
|
|
203
|
+
case 'url':
|
|
204
|
+
case 'date':
|
|
205
|
+
return 'string';
|
|
206
|
+
case 'number':
|
|
207
|
+
case 'integer':
|
|
208
|
+
return 'number';
|
|
209
|
+
case 'boolean':
|
|
210
|
+
return 'boolean';
|
|
211
|
+
case 'array':
|
|
212
|
+
return 'array';
|
|
213
|
+
case 'group':
|
|
214
|
+
case 'reference':
|
|
215
|
+
return 'object';
|
|
216
|
+
default:
|
|
217
|
+
return 'string';
|
|
218
|
+
}
|
|
219
|
+
}
|
|
80
220
|
setupToolHandlers() {
|
|
81
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () =>
|
|
82
|
-
|
|
221
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
222
|
+
// Ensure glossary is loaded
|
|
223
|
+
await this.loadGlossary();
|
|
224
|
+
const tools = [
|
|
83
225
|
// Authentication & Session
|
|
84
226
|
{
|
|
85
227
|
name: 'qik_get_user_session',
|
|
@@ -107,6 +249,7 @@ export class QikMCPServer {
|
|
|
107
249
|
type: {
|
|
108
250
|
type: 'string',
|
|
109
251
|
description: 'Content type key (e.g., "article", "profile", "event")',
|
|
252
|
+
enum: Object.keys(this.glossary),
|
|
110
253
|
},
|
|
111
254
|
},
|
|
112
255
|
required: ['type'],
|
|
@@ -142,7 +285,8 @@ export class QikMCPServer {
|
|
|
142
285
|
properties: {
|
|
143
286
|
type: {
|
|
144
287
|
type: 'string',
|
|
145
|
-
description: 'Content type to list
|
|
288
|
+
description: 'Content type to list',
|
|
289
|
+
enum: Object.keys(this.glossary),
|
|
146
290
|
},
|
|
147
291
|
search: {
|
|
148
292
|
type: 'string',
|
|
@@ -185,6 +329,7 @@ export class QikMCPServer {
|
|
|
185
329
|
type: {
|
|
186
330
|
type: 'string',
|
|
187
331
|
description: 'Content type to create',
|
|
332
|
+
enum: Object.keys(this.glossary),
|
|
188
333
|
},
|
|
189
334
|
title: {
|
|
190
335
|
type: 'string',
|
|
@@ -197,6 +342,23 @@ export class QikMCPServer {
|
|
|
197
342
|
meta: {
|
|
198
343
|
type: 'object',
|
|
199
344
|
description: 'Meta information (scopes, tags, etc.)',
|
|
345
|
+
properties: {
|
|
346
|
+
scopes: {
|
|
347
|
+
type: 'array',
|
|
348
|
+
items: { type: 'string' },
|
|
349
|
+
description: 'Scope IDs where this content should be stored',
|
|
350
|
+
},
|
|
351
|
+
tags: {
|
|
352
|
+
type: 'array',
|
|
353
|
+
items: { type: 'string' },
|
|
354
|
+
description: 'Tag IDs for this content',
|
|
355
|
+
},
|
|
356
|
+
security: {
|
|
357
|
+
type: 'string',
|
|
358
|
+
enum: ['public', 'secure', 'private'],
|
|
359
|
+
description: 'Security level for this content',
|
|
360
|
+
},
|
|
361
|
+
},
|
|
200
362
|
},
|
|
201
363
|
},
|
|
202
364
|
required: ['type', 'title'],
|
|
@@ -384,7 +546,10 @@ export class QikMCPServer {
|
|
|
384
546
|
},
|
|
385
547
|
types: {
|
|
386
548
|
type: 'array',
|
|
387
|
-
items: {
|
|
549
|
+
items: {
|
|
550
|
+
type: 'string',
|
|
551
|
+
enum: Object.keys(this.glossary),
|
|
552
|
+
},
|
|
388
553
|
description: 'Content types to search in',
|
|
389
554
|
},
|
|
390
555
|
limit: {
|
|
@@ -420,10 +585,13 @@ export class QikMCPServer {
|
|
|
420
585
|
required: ['id'],
|
|
421
586
|
},
|
|
422
587
|
},
|
|
423
|
-
]
|
|
424
|
-
|
|
588
|
+
];
|
|
589
|
+
return { tools };
|
|
590
|
+
});
|
|
425
591
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
426
592
|
try {
|
|
593
|
+
// Ensure glossary is fresh
|
|
594
|
+
await this.loadGlossary();
|
|
427
595
|
switch (request.params.name) {
|
|
428
596
|
case 'qik_get_user_session':
|
|
429
597
|
return await this.getUserSession();
|
|
@@ -503,114 +671,247 @@ export class QikMCPServer {
|
|
|
503
671
|
}
|
|
504
672
|
});
|
|
505
673
|
}
|
|
506
|
-
//
|
|
674
|
+
// Enhanced tool implementations
|
|
507
675
|
async getUserSession() {
|
|
508
|
-
|
|
676
|
+
if (!this.userSession) {
|
|
677
|
+
await this.loadUserSession();
|
|
678
|
+
}
|
|
509
679
|
return {
|
|
510
680
|
content: [{
|
|
511
681
|
type: 'text',
|
|
512
|
-
text: JSON.stringify(
|
|
682
|
+
text: JSON.stringify(this.userSession, null, 2),
|
|
513
683
|
}],
|
|
514
684
|
};
|
|
515
685
|
}
|
|
516
686
|
async getGlossary() {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
687
|
+
await this.loadGlossary(true); // Force refresh
|
|
688
|
+
const summary = Object.entries(this.glossary).map(([key, contentType]) => ({
|
|
689
|
+
key,
|
|
690
|
+
title: contentType.definition?.title || contentType.type.title,
|
|
691
|
+
plural: contentType.definition?.plural || contentType.type.plural,
|
|
692
|
+
fieldCount: (contentType.definition?.fields || contentType.type.fields || []).length,
|
|
693
|
+
definesType: contentType.definition?.definesType || contentType.type.key,
|
|
694
|
+
}));
|
|
520
695
|
return {
|
|
521
696
|
content: [{
|
|
522
697
|
type: 'text',
|
|
523
|
-
text: JSON.stringify(
|
|
698
|
+
text: JSON.stringify({
|
|
699
|
+
summary,
|
|
700
|
+
totalTypes: Object.keys(this.glossary).length,
|
|
701
|
+
fullGlossary: this.glossary,
|
|
702
|
+
}, null, 2),
|
|
524
703
|
}],
|
|
525
704
|
};
|
|
526
705
|
}
|
|
527
706
|
async getContentDefinition(args) {
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
707
|
+
const validation = this.validateContentType(args.type);
|
|
708
|
+
if (!validation.valid) {
|
|
709
|
+
return {
|
|
710
|
+
content: [{
|
|
711
|
+
type: 'text',
|
|
712
|
+
text: validation.error,
|
|
713
|
+
}],
|
|
714
|
+
isError: true,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
try {
|
|
718
|
+
const response = await this.axiosInstance.get(`/content/${args.type}/definition`);
|
|
719
|
+
return {
|
|
720
|
+
content: [{
|
|
721
|
+
type: 'text',
|
|
722
|
+
text: JSON.stringify(response.data, null, 2),
|
|
723
|
+
}],
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
return {
|
|
728
|
+
content: [{
|
|
729
|
+
type: 'text',
|
|
730
|
+
text: `Failed to fetch definition for '${args.type}': ${this.formatError(error)}`,
|
|
731
|
+
}],
|
|
732
|
+
isError: true,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
535
735
|
}
|
|
536
736
|
async getContent(args) {
|
|
537
737
|
let response;
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
738
|
+
try {
|
|
739
|
+
if (args.id) {
|
|
740
|
+
response = await this.axiosInstance.get(`/content/${args.id}`);
|
|
741
|
+
}
|
|
742
|
+
else if (args.slug) {
|
|
743
|
+
response = await this.axiosInstance.get(`/content/slug/${args.slug}`);
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
throw new Error('Either id or slug must be provided');
|
|
747
|
+
}
|
|
748
|
+
return {
|
|
749
|
+
content: [{
|
|
750
|
+
type: 'text',
|
|
751
|
+
text: JSON.stringify(response.data, null, 2),
|
|
752
|
+
}],
|
|
753
|
+
};
|
|
543
754
|
}
|
|
544
|
-
|
|
545
|
-
|
|
755
|
+
catch (error) {
|
|
756
|
+
const identifier = args.id || args.slug;
|
|
757
|
+
return {
|
|
758
|
+
content: [{
|
|
759
|
+
type: 'text',
|
|
760
|
+
text: `Failed to fetch content '${identifier}': ${this.formatError(error)}`,
|
|
761
|
+
}],
|
|
762
|
+
isError: true,
|
|
763
|
+
};
|
|
546
764
|
}
|
|
547
|
-
return {
|
|
548
|
-
content: [{
|
|
549
|
-
type: 'text',
|
|
550
|
-
text: JSON.stringify(response.data, null, 2),
|
|
551
|
-
}],
|
|
552
|
-
};
|
|
553
765
|
}
|
|
554
766
|
async listContent(args) {
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
767
|
+
const validation = this.validateContentType(args.type);
|
|
768
|
+
if (!validation.valid) {
|
|
769
|
+
return {
|
|
770
|
+
content: [{
|
|
771
|
+
type: 'text',
|
|
772
|
+
text: validation.error,
|
|
773
|
+
}],
|
|
774
|
+
isError: true,
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
try {
|
|
778
|
+
const response = await this.axiosInstance.post(`/content/${args.type}/list`, {
|
|
779
|
+
search: args.search || '',
|
|
780
|
+
filter: args.filter || {},
|
|
781
|
+
sort: args.sort || { key: 'meta.created', direction: 'desc', type: 'date' },
|
|
782
|
+
page: args.page || { size: 20, index: 1 },
|
|
783
|
+
select: args.select || [],
|
|
784
|
+
});
|
|
785
|
+
return {
|
|
786
|
+
content: [{
|
|
787
|
+
type: 'text',
|
|
788
|
+
text: JSON.stringify(response.data, null, 2),
|
|
789
|
+
}],
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
catch (error) {
|
|
793
|
+
return {
|
|
794
|
+
content: [{
|
|
795
|
+
type: 'text',
|
|
796
|
+
text: `Failed to list ${args.type} content: ${this.formatError(error)}`,
|
|
797
|
+
}],
|
|
798
|
+
isError: true,
|
|
799
|
+
};
|
|
800
|
+
}
|
|
568
801
|
}
|
|
569
802
|
async createContent(args) {
|
|
803
|
+
const validation = this.validateContentType(args.type);
|
|
804
|
+
if (!validation.valid) {
|
|
805
|
+
return {
|
|
806
|
+
content: [{
|
|
807
|
+
type: 'text',
|
|
808
|
+
text: validation.error,
|
|
809
|
+
}],
|
|
810
|
+
isError: true,
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
// Validate field data if available
|
|
814
|
+
if (args.data && validation.info) {
|
|
815
|
+
const fields = validation.info.definition?.fields || validation.info.type.fields || [];
|
|
816
|
+
const fieldValidation = this.validateFieldData(args.data, fields, args.type);
|
|
817
|
+
if (!fieldValidation.valid) {
|
|
818
|
+
return {
|
|
819
|
+
content: [{
|
|
820
|
+
type: 'text',
|
|
821
|
+
text: `Validation errors for ${args.type}:\n${fieldValidation.errors.join('\n')}`,
|
|
822
|
+
}],
|
|
823
|
+
isError: true,
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
}
|
|
570
827
|
const payload = {
|
|
571
828
|
title: args.title,
|
|
572
829
|
data: args.data || {},
|
|
573
830
|
meta: args.meta || {},
|
|
574
831
|
};
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
832
|
+
try {
|
|
833
|
+
const response = await this.axiosInstance.post(`/content/${args.type}/create`, payload);
|
|
834
|
+
return {
|
|
835
|
+
content: [{
|
|
836
|
+
type: 'text',
|
|
837
|
+
text: JSON.stringify(response.data, null, 2),
|
|
838
|
+
}],
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
catch (error) {
|
|
842
|
+
return {
|
|
843
|
+
content: [{
|
|
844
|
+
type: 'text',
|
|
845
|
+
text: `Failed to create ${args.type}: ${this.formatError(error)}`,
|
|
846
|
+
}],
|
|
847
|
+
isError: true,
|
|
848
|
+
};
|
|
849
|
+
}
|
|
582
850
|
}
|
|
583
851
|
async updateContent(args) {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
852
|
+
try {
|
|
853
|
+
const method = args.replace ? 'put' : 'patch';
|
|
854
|
+
const response = await this.axiosInstance[method](`/content/${args.id}`, args.data);
|
|
855
|
+
return {
|
|
856
|
+
content: [{
|
|
857
|
+
type: 'text',
|
|
858
|
+
text: JSON.stringify(response.data, null, 2),
|
|
859
|
+
}],
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
catch (error) {
|
|
863
|
+
return {
|
|
864
|
+
content: [{
|
|
865
|
+
type: 'text',
|
|
866
|
+
text: `Failed to update content ${args.id}: ${this.formatError(error)}`,
|
|
867
|
+
}],
|
|
868
|
+
isError: true,
|
|
869
|
+
};
|
|
870
|
+
}
|
|
592
871
|
}
|
|
593
872
|
async deleteContent(args) {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
873
|
+
try {
|
|
874
|
+
await this.axiosInstance.delete(`/content/${args.id}`);
|
|
875
|
+
return {
|
|
876
|
+
content: [{
|
|
877
|
+
type: 'text',
|
|
878
|
+
text: `Content ${args.id} deleted successfully`,
|
|
879
|
+
}],
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
catch (error) {
|
|
883
|
+
return {
|
|
884
|
+
content: [{
|
|
885
|
+
type: 'text',
|
|
886
|
+
text: `Failed to delete content ${args.id}: ${this.formatError(error)}`,
|
|
887
|
+
}],
|
|
888
|
+
isError: true,
|
|
889
|
+
};
|
|
890
|
+
}
|
|
601
891
|
}
|
|
602
892
|
async listProfiles(args) {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
893
|
+
try {
|
|
894
|
+
const response = await this.axiosInstance.post('/content/profile/list', {
|
|
895
|
+
search: args.search || '',
|
|
896
|
+
filter: args.filter || {},
|
|
897
|
+
page: args.page || { size: 20, index: 1 },
|
|
898
|
+
});
|
|
899
|
+
return {
|
|
900
|
+
content: [{
|
|
901
|
+
type: 'text',
|
|
902
|
+
text: JSON.stringify(response.data, null, 2),
|
|
903
|
+
}],
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
catch (error) {
|
|
907
|
+
return {
|
|
908
|
+
content: [{
|
|
909
|
+
type: 'text',
|
|
910
|
+
text: `Failed to list profiles: ${this.formatError(error)}`,
|
|
911
|
+
}],
|
|
912
|
+
isError: true,
|
|
913
|
+
};
|
|
914
|
+
}
|
|
614
915
|
}
|
|
615
916
|
async createProfile(args) {
|
|
616
917
|
const payload = {
|
|
@@ -621,59 +922,104 @@ export class QikMCPServer {
|
|
|
621
922
|
data: args.data || {},
|
|
622
923
|
meta: args.meta || {},
|
|
623
924
|
};
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
925
|
+
try {
|
|
926
|
+
const response = await this.axiosInstance.post('/content/profile/create', payload);
|
|
927
|
+
return {
|
|
928
|
+
content: [{
|
|
929
|
+
type: 'text',
|
|
930
|
+
text: JSON.stringify(response.data, null, 2),
|
|
931
|
+
}],
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
catch (error) {
|
|
935
|
+
return {
|
|
936
|
+
content: [{
|
|
937
|
+
type: 'text',
|
|
938
|
+
text: `Failed to create profile: ${this.formatError(error)}`,
|
|
939
|
+
}],
|
|
940
|
+
isError: true,
|
|
941
|
+
};
|
|
942
|
+
}
|
|
631
943
|
}
|
|
632
944
|
async getForm(args) {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
945
|
+
try {
|
|
946
|
+
const response = await this.axiosInstance.get(`/form/${args.id}`);
|
|
947
|
+
return {
|
|
948
|
+
content: [{
|
|
949
|
+
type: 'text',
|
|
950
|
+
text: JSON.stringify(response.data, null, 2),
|
|
951
|
+
}],
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
catch (error) {
|
|
955
|
+
return {
|
|
956
|
+
content: [{
|
|
957
|
+
type: 'text',
|
|
958
|
+
text: `Failed to get form ${args.id}: ${this.formatError(error)}`,
|
|
959
|
+
}],
|
|
960
|
+
isError: true,
|
|
961
|
+
};
|
|
962
|
+
}
|
|
640
963
|
}
|
|
641
964
|
async submitForm(args) {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
965
|
+
try {
|
|
966
|
+
const response = await this.axiosInstance.post(`/form/${args.id}`, args.data);
|
|
967
|
+
return {
|
|
968
|
+
content: [{
|
|
969
|
+
type: 'text',
|
|
970
|
+
text: JSON.stringify(response.data, null, 2),
|
|
971
|
+
}],
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
catch (error) {
|
|
975
|
+
return {
|
|
976
|
+
content: [{
|
|
977
|
+
type: 'text',
|
|
978
|
+
text: `Failed to submit form ${args.id}: ${this.formatError(error)}`,
|
|
979
|
+
}],
|
|
980
|
+
isError: true,
|
|
981
|
+
};
|
|
982
|
+
}
|
|
649
983
|
}
|
|
650
984
|
async uploadFile(args) {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
985
|
+
try {
|
|
986
|
+
const formData = new FormData();
|
|
987
|
+
// Convert base64 to buffer
|
|
988
|
+
const fileBuffer = Buffer.from(args.fileData, 'base64');
|
|
989
|
+
formData.append('file', fileBuffer, args.fileName);
|
|
990
|
+
const jsonData = {
|
|
991
|
+
title: args.title,
|
|
992
|
+
meta: args.meta || {},
|
|
993
|
+
};
|
|
994
|
+
formData.append('json', JSON.stringify(jsonData));
|
|
995
|
+
const response = await this.axiosInstance.post('/file/upload', formData, {
|
|
996
|
+
headers: {
|
|
997
|
+
...formData.getHeaders(),
|
|
998
|
+
'Authorization': `Bearer ${QIK_ACCESS_TOKEN}`,
|
|
999
|
+
},
|
|
1000
|
+
});
|
|
1001
|
+
return {
|
|
1002
|
+
content: [{
|
|
1003
|
+
type: 'text',
|
|
1004
|
+
text: JSON.stringify(response.data, null, 2),
|
|
1005
|
+
}],
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
catch (error) {
|
|
1009
|
+
return {
|
|
1010
|
+
content: [{
|
|
1011
|
+
type: 'text',
|
|
1012
|
+
text: `Failed to upload file: ${this.formatError(error)}`,
|
|
1013
|
+
}],
|
|
1014
|
+
isError: true,
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
672
1017
|
}
|
|
673
1018
|
async searchContent(args) {
|
|
674
1019
|
const results = [];
|
|
675
1020
|
const types = args.types || Object.keys(this.glossary);
|
|
676
|
-
|
|
1021
|
+
// Limit to 5 types to avoid too many requests
|
|
1022
|
+
for (const type of types.slice(0, 5)) {
|
|
677
1023
|
try {
|
|
678
1024
|
const response = await this.axiosInstance.post(`/content/${type}/list`, {
|
|
679
1025
|
search: args.query,
|
|
@@ -700,22 +1046,44 @@ export class QikMCPServer {
|
|
|
700
1046
|
};
|
|
701
1047
|
}
|
|
702
1048
|
async getScopes() {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
1049
|
+
try {
|
|
1050
|
+
const response = await this.axiosInstance.get('/scope/tree');
|
|
1051
|
+
return {
|
|
1052
|
+
content: [{
|
|
1053
|
+
type: 'text',
|
|
1054
|
+
text: JSON.stringify(response.data, null, 2),
|
|
1055
|
+
}],
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
catch (error) {
|
|
1059
|
+
return {
|
|
1060
|
+
content: [{
|
|
1061
|
+
type: 'text',
|
|
1062
|
+
text: `Failed to get scopes: ${this.formatError(error)}`,
|
|
1063
|
+
}],
|
|
1064
|
+
isError: true,
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
710
1067
|
}
|
|
711
1068
|
async getSmartlist(args) {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1069
|
+
try {
|
|
1070
|
+
const response = await this.axiosInstance.get(`/smartlist/${args.id}`);
|
|
1071
|
+
return {
|
|
1072
|
+
content: [{
|
|
1073
|
+
type: 'text',
|
|
1074
|
+
text: JSON.stringify(response.data, null, 2),
|
|
1075
|
+
}],
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
catch (error) {
|
|
1079
|
+
return {
|
|
1080
|
+
content: [{
|
|
1081
|
+
type: 'text',
|
|
1082
|
+
text: `Failed to get smartlist ${args.id}: ${this.formatError(error)}`,
|
|
1083
|
+
}],
|
|
1084
|
+
isError: true,
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
719
1087
|
}
|
|
720
1088
|
async run() {
|
|
721
1089
|
const transport = new StdioServerTransport();
|