snow-flow 3.2.3 → 3.3.1
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 +14 -0
- package/dist/dynamic-version.js +1 -1
- package/dist/mcp/servicenow-change-virtualagent-pa-mcp-enhanced.d.ts +7 -0
- package/dist/mcp/servicenow-change-virtualagent-pa-mcp-enhanced.js +816 -0
- package/dist/mcp/servicenow-cmdb-event-hr-csm-devops-mcp-enhanced.d.ts +7 -0
- package/dist/mcp/servicenow-cmdb-event-hr-csm-devops-mcp-enhanced.js +1112 -0
- package/dist/mcp/servicenow-flow-workspace-mobile-mcp-enhanced.d.ts +7 -0
- package/dist/mcp/servicenow-flow-workspace-mobile-mcp-enhanced.js +860 -0
- package/dist/mcp/servicenow-knowledge-catalog-mcp-enhanced.d.ts +7 -0
- package/dist/mcp/servicenow-knowledge-catalog-mcp-enhanced.js +709 -0
- package/dist/mcp/shared/enhanced-base-mcp-server.d.ts +53 -0
- package/dist/mcp/shared/enhanced-base-mcp-server.js +168 -0
- package/dist/mcp/shared/mcp-logger.d.ts +80 -0
- package/dist/mcp/shared/mcp-logger.js +190 -0
- package/dist/utils/servicenow-client-with-tracking.d.ts +38 -0
- package/dist/utils/servicenow-client-with-tracking.js +137 -0
- package/package.json +2 -2
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* ServiceNow Knowledge Management & Service Catalog MCP Server - ENHANCED VERSION
|
|
5
|
+
* With logging, token tracking, and progress indicators
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
10
|
+
const enhanced_base_mcp_server_js_1 = require("./shared/enhanced-base-mcp-server.js");
|
|
11
|
+
const mcp_config_manager_js_1 = require("../utils/mcp-config-manager.js");
|
|
12
|
+
class ServiceNowKnowledgeCatalogMCPEnhanced extends enhanced_base_mcp_server_js_1.EnhancedBaseMCPServer {
|
|
13
|
+
constructor() {
|
|
14
|
+
super('servicenow-knowledge-catalog-enhanced', '2.0.0');
|
|
15
|
+
this.config = mcp_config_manager_js_1.mcpConfig.getConfig();
|
|
16
|
+
this.setupHandlers();
|
|
17
|
+
}
|
|
18
|
+
setupHandlers() {
|
|
19
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
20
|
+
tools: [
|
|
21
|
+
// Knowledge Management Tools
|
|
22
|
+
{
|
|
23
|
+
name: 'snow_create_knowledge_article',
|
|
24
|
+
description: 'Creates a knowledge article in ServiceNow Knowledge Base using kb_knowledge table.',
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
short_description: { type: 'string', description: 'Article title' },
|
|
29
|
+
text: { type: 'string', description: 'Article content (HTML supported)' },
|
|
30
|
+
kb_knowledge_base: { type: 'string', description: 'Knowledge base sys_id or name' },
|
|
31
|
+
kb_category: { type: 'string', description: 'Category sys_id or name' },
|
|
32
|
+
article_type: { type: 'string', description: 'Type: text, html, wiki' },
|
|
33
|
+
workflow_state: { type: 'string', description: 'State: draft, review, published, retired' },
|
|
34
|
+
valid_to: { type: 'string', description: 'Expiration date (YYYY-MM-DD)' },
|
|
35
|
+
meta_description: { type: 'string', description: 'SEO meta description' },
|
|
36
|
+
keywords: { type: 'array', items: { type: 'string' }, description: 'Search keywords' },
|
|
37
|
+
author: { type: 'string', description: 'Author user sys_id or username' }
|
|
38
|
+
},
|
|
39
|
+
required: ['short_description', 'text']
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'snow_search_knowledge',
|
|
44
|
+
description: 'Searches knowledge articles in kb_knowledge table with full-text search.',
|
|
45
|
+
inputSchema: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
properties: {
|
|
48
|
+
query: { type: 'string', description: 'Search query text' },
|
|
49
|
+
kb_knowledge_base: { type: 'string', description: 'Filter by knowledge base' },
|
|
50
|
+
kb_category: { type: 'string', description: 'Filter by category' },
|
|
51
|
+
workflow_state: { type: 'string', description: 'Filter by state (published, draft, etc.)' },
|
|
52
|
+
limit: { type: 'number', description: 'Maximum results to return', default: 10 },
|
|
53
|
+
include_content: { type: 'boolean', description: 'Include full article content', default: false }
|
|
54
|
+
},
|
|
55
|
+
required: ['query']
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'snow_update_knowledge_article',
|
|
60
|
+
description: 'Updates existing knowledge article in kb_knowledge table.',
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: {
|
|
64
|
+
sys_id: { type: 'string', description: 'Article sys_id to update' },
|
|
65
|
+
short_description: { type: 'string', description: 'Article title' },
|
|
66
|
+
text: { type: 'string', description: 'Article content' },
|
|
67
|
+
workflow_state: { type: 'string', description: 'State: draft, review, published' },
|
|
68
|
+
valid_to: { type: 'string', description: 'Expiration date' },
|
|
69
|
+
keywords: { type: 'array', items: { type: 'string' } }
|
|
70
|
+
},
|
|
71
|
+
required: ['sys_id']
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'snow_retire_knowledge_article',
|
|
76
|
+
description: 'Retires knowledge article by setting workflow_state to retired in kb_knowledge table.',
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
sys_id: { type: 'string', description: 'Article sys_id' },
|
|
81
|
+
retirement_reason: { type: 'string', description: 'Reason for retirement' }
|
|
82
|
+
},
|
|
83
|
+
required: ['sys_id']
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'snow_create_knowledge_base',
|
|
88
|
+
description: 'Creates new knowledge base using kb_knowledge_base table.',
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
title: { type: 'string', description: 'Knowledge base name' },
|
|
93
|
+
description: { type: 'string', description: 'KB description' },
|
|
94
|
+
owner: { type: 'string', description: 'Owner user/group' },
|
|
95
|
+
kb_managers: { type: 'array', items: { type: 'string' } }
|
|
96
|
+
},
|
|
97
|
+
required: ['title']
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'snow_discover_knowledge_bases',
|
|
102
|
+
description: 'Lists all knowledge bases from kb_knowledge_base table.',
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
properties: {
|
|
106
|
+
active_only: { type: 'boolean', default: true }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'snow_get_knowledge_stats',
|
|
112
|
+
description: 'Gets statistics for knowledge articles from kb_knowledge table.',
|
|
113
|
+
inputSchema: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: {
|
|
116
|
+
kb_knowledge_base: { type: 'string', description: 'Filter by KB' },
|
|
117
|
+
date_range: { type: 'string', description: 'Date range filter' }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'snow_knowledge_feedback',
|
|
123
|
+
description: 'Manages feedback for knowledge articles using kb_feedback table.',
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: 'object',
|
|
126
|
+
properties: {
|
|
127
|
+
article_id: { type: 'string', description: 'Article sys_id' },
|
|
128
|
+
rating: { type: 'number', description: 'Rating 1-5' },
|
|
129
|
+
comments: { type: 'string', description: 'Feedback comments' }
|
|
130
|
+
},
|
|
131
|
+
required: ['article_id']
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
// Service Catalog Tools
|
|
135
|
+
{
|
|
136
|
+
name: 'snow_create_catalog_item',
|
|
137
|
+
description: 'Creates service catalog item using sc_cat_item table.',
|
|
138
|
+
inputSchema: {
|
|
139
|
+
type: 'object',
|
|
140
|
+
properties: {
|
|
141
|
+
name: { type: 'string', description: 'Catalog item name' },
|
|
142
|
+
short_description: { type: 'string', description: 'Brief description' },
|
|
143
|
+
category: { type: 'string', description: 'Category sys_id' },
|
|
144
|
+
price: { type: 'string', description: 'Item price' },
|
|
145
|
+
workflow: { type: 'string', description: 'Fulfillment workflow' }
|
|
146
|
+
},
|
|
147
|
+
required: ['name', 'short_description']
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: 'snow_create_catalog_variable',
|
|
152
|
+
description: 'Creates variables for catalog items using item_option_new table.',
|
|
153
|
+
inputSchema: {
|
|
154
|
+
type: 'object',
|
|
155
|
+
properties: {
|
|
156
|
+
cat_item: { type: 'string', description: 'Catalog item sys_id' },
|
|
157
|
+
name: { type: 'string', description: 'Variable name' },
|
|
158
|
+
question_text: { type: 'string', description: 'Question to display' },
|
|
159
|
+
type: { type: 'string', description: 'Variable type' },
|
|
160
|
+
mandatory: { type: 'boolean', default: false }
|
|
161
|
+
},
|
|
162
|
+
required: ['cat_item', 'name', 'question_text']
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'snow_create_catalog_ui_policy',
|
|
167
|
+
description: 'Creates UI policies for catalog items using catalog_ui_policy table.',
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
cat_item: { type: 'string', description: 'Catalog item sys_id' },
|
|
172
|
+
short_description: { type: 'string', description: 'Policy name' },
|
|
173
|
+
condition: { type: 'string', description: 'When to apply' },
|
|
174
|
+
actions: { type: 'array', items: { type: 'object' } }
|
|
175
|
+
},
|
|
176
|
+
required: ['cat_item', 'short_description']
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'snow_order_catalog_item',
|
|
181
|
+
description: 'Submits catalog item order using sc_req_item table.',
|
|
182
|
+
inputSchema: {
|
|
183
|
+
type: 'object',
|
|
184
|
+
properties: {
|
|
185
|
+
cat_item: { type: 'string', description: 'Catalog item sys_id' },
|
|
186
|
+
requested_for: { type: 'string', description: 'User sys_id' },
|
|
187
|
+
variables: { type: 'object', description: 'Variable values' },
|
|
188
|
+
quantity: { type: 'number', default: 1 }
|
|
189
|
+
},
|
|
190
|
+
required: ['cat_item']
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'snow_search_catalog',
|
|
195
|
+
description: 'Searches service catalog items in sc_cat_item table.',
|
|
196
|
+
inputSchema: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
query: { type: 'string', description: 'Search text' },
|
|
200
|
+
category: { type: 'string', description: 'Filter by category' },
|
|
201
|
+
active_only: { type: 'boolean', default: true },
|
|
202
|
+
limit: { type: 'number', default: 10 }
|
|
203
|
+
},
|
|
204
|
+
required: ['query']
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'snow_get_catalog_item_details',
|
|
209
|
+
description: 'Gets full details of catalog item from sc_cat_item table.',
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
properties: {
|
|
213
|
+
sys_id: { type: 'string', description: 'Catalog item sys_id' },
|
|
214
|
+
include_variables: { type: 'boolean', default: true }
|
|
215
|
+
},
|
|
216
|
+
required: ['sys_id']
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: 'snow_discover_catalogs',
|
|
221
|
+
description: 'Discovers catalog structure from sc_catalog table.',
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: 'object',
|
|
224
|
+
properties: {
|
|
225
|
+
include_categories: { type: 'boolean', default: true }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
}));
|
|
231
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
232
|
+
try {
|
|
233
|
+
const { name, arguments: args } = request.params;
|
|
234
|
+
// Execute with enhanced tracking
|
|
235
|
+
return await this.executeTool(name, async () => {
|
|
236
|
+
switch (name) {
|
|
237
|
+
case 'snow_create_knowledge_article':
|
|
238
|
+
return await this.createKnowledgeArticle(args);
|
|
239
|
+
case 'snow_search_knowledge':
|
|
240
|
+
return await this.searchKnowledge(args);
|
|
241
|
+
case 'snow_update_knowledge_article':
|
|
242
|
+
return await this.updateKnowledgeArticle(args);
|
|
243
|
+
case 'snow_retire_knowledge_article':
|
|
244
|
+
return await this.retireKnowledgeArticle(args);
|
|
245
|
+
case 'snow_create_knowledge_base':
|
|
246
|
+
return await this.createKnowledgeBase(args);
|
|
247
|
+
case 'snow_discover_knowledge_bases':
|
|
248
|
+
return await this.discoverKnowledgeBases(args);
|
|
249
|
+
case 'snow_get_knowledge_stats':
|
|
250
|
+
return await this.getKnowledgeStats(args);
|
|
251
|
+
case 'snow_knowledge_feedback':
|
|
252
|
+
return await this.knowledgeFeedback(args);
|
|
253
|
+
case 'snow_create_catalog_item':
|
|
254
|
+
return await this.createCatalogItem(args);
|
|
255
|
+
case 'snow_create_catalog_variable':
|
|
256
|
+
return await this.createCatalogVariable(args);
|
|
257
|
+
case 'snow_create_catalog_ui_policy':
|
|
258
|
+
return await this.createCatalogUIPolicy(args);
|
|
259
|
+
case 'snow_order_catalog_item':
|
|
260
|
+
return await this.orderCatalogItem(args);
|
|
261
|
+
case 'snow_search_catalog':
|
|
262
|
+
return await this.searchCatalog(args);
|
|
263
|
+
case 'snow_get_catalog_item_details':
|
|
264
|
+
return await this.getCatalogItemDetails(args);
|
|
265
|
+
case 'snow_discover_catalogs':
|
|
266
|
+
return await this.discoverCatalogs(args);
|
|
267
|
+
default:
|
|
268
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
if (error instanceof types_js_1.McpError)
|
|
274
|
+
throw error;
|
|
275
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Tool execution failed: ${error}`);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Create Knowledge Article with enhanced tracking
|
|
281
|
+
*/
|
|
282
|
+
async createKnowledgeArticle(args) {
|
|
283
|
+
this.logger.info('Creating knowledge article...', {
|
|
284
|
+
title: args.short_description,
|
|
285
|
+
hasContent: !!args.text,
|
|
286
|
+
contentLength: args.text?.length
|
|
287
|
+
});
|
|
288
|
+
// Validate connection
|
|
289
|
+
const connCheck = await this.validateConnection();
|
|
290
|
+
if (!connCheck.success) {
|
|
291
|
+
return this.createResponse(`❌ Connection failed: ${connCheck.error}`);
|
|
292
|
+
}
|
|
293
|
+
// Progress indicator
|
|
294
|
+
this.logger.progress('Building knowledge article data...');
|
|
295
|
+
const articleData = {
|
|
296
|
+
short_description: args.short_description,
|
|
297
|
+
text: args.text,
|
|
298
|
+
kb_knowledge_base: args.kb_knowledge_base || '',
|
|
299
|
+
kb_category: args.kb_category || '',
|
|
300
|
+
article_type: args.article_type || 'text',
|
|
301
|
+
workflow_state: args.workflow_state || 'draft',
|
|
302
|
+
valid_to: args.valid_to || '',
|
|
303
|
+
meta_description: args.meta_description || '',
|
|
304
|
+
keywords: args.keywords?.join(',') || '',
|
|
305
|
+
author: args.author || ''
|
|
306
|
+
};
|
|
307
|
+
this.logger.progress('Creating article in ServiceNow...');
|
|
308
|
+
// Create with tracking
|
|
309
|
+
const response = await this.createRecord('kb_knowledge', articleData);
|
|
310
|
+
if (!response.success) {
|
|
311
|
+
this.logger.error('Failed to create knowledge article', response.error);
|
|
312
|
+
return this.createResponse(`❌ Failed to create article: ${response.error}`);
|
|
313
|
+
}
|
|
314
|
+
// Success with details
|
|
315
|
+
const result = response.data;
|
|
316
|
+
this.logger.info('✅ Knowledge article created successfully', {
|
|
317
|
+
sys_id: result.sys_id,
|
|
318
|
+
number: result.number,
|
|
319
|
+
title: args.short_description
|
|
320
|
+
});
|
|
321
|
+
return this.createResponse(`✅ Knowledge Article created successfully!
|
|
322
|
+
|
|
323
|
+
📚 **${args.short_description}**
|
|
324
|
+
🆔 sys_id: ${result.sys_id}
|
|
325
|
+
📋 Number: ${result.number}
|
|
326
|
+
📊 State: ${args.workflow_state || 'draft'}
|
|
327
|
+
📝 Type: ${args.article_type || 'text'}
|
|
328
|
+
${args.kb_knowledge_base ? `📁 Knowledge Base: ${args.kb_knowledge_base}` : ''}
|
|
329
|
+
${args.kb_category ? `🏷️ Category: ${args.kb_category}` : ''}
|
|
330
|
+
${args.valid_to ? `📅 Valid Until: ${args.valid_to}` : ''}
|
|
331
|
+
|
|
332
|
+
✨ Article created and ready for review!`);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Search Knowledge with enhanced tracking
|
|
336
|
+
*/
|
|
337
|
+
async searchKnowledge(args) {
|
|
338
|
+
this.logger.info('Searching knowledge articles...', {
|
|
339
|
+
query: args.query,
|
|
340
|
+
limit: args.limit || 10,
|
|
341
|
+
includeContent: args.include_content
|
|
342
|
+
});
|
|
343
|
+
// Build query
|
|
344
|
+
let query = `short_descriptionLIKE${args.query}^ORtextLIKE${args.query}`;
|
|
345
|
+
if (args.kb_knowledge_base) {
|
|
346
|
+
query += `^kb_knowledge_base=${args.kb_knowledge_base}`;
|
|
347
|
+
}
|
|
348
|
+
if (args.kb_category) {
|
|
349
|
+
query += `^kb_category=${args.kb_category}`;
|
|
350
|
+
}
|
|
351
|
+
if (args.workflow_state) {
|
|
352
|
+
query += `^workflow_state=${args.workflow_state}`;
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
query += '^workflow_state=published'; // Default to published only
|
|
356
|
+
}
|
|
357
|
+
this.logger.progress(`Searching kb_knowledge table for: "${args.query}"...`);
|
|
358
|
+
// Search with tracking
|
|
359
|
+
const limit = args.limit || 10;
|
|
360
|
+
const response = await this.queryTable('kb_knowledge', query, limit);
|
|
361
|
+
if (!response.success) {
|
|
362
|
+
this.logger.error('Knowledge search failed', response.error);
|
|
363
|
+
return this.createResponse(`❌ Search failed: ${response.error}`);
|
|
364
|
+
}
|
|
365
|
+
const articles = response.data.result;
|
|
366
|
+
if (!articles.length) {
|
|
367
|
+
this.logger.info('No articles found', { query: args.query });
|
|
368
|
+
return this.createResponse(`❌ No knowledge articles found matching "${args.query}"`);
|
|
369
|
+
}
|
|
370
|
+
this.logger.info(`Found ${articles.length} knowledge articles`);
|
|
371
|
+
// Format results
|
|
372
|
+
const articleList = articles.map((article) => {
|
|
373
|
+
const snippet = args.include_content ?
|
|
374
|
+
article.text?.substring(0, 200) + '...' :
|
|
375
|
+
article.short_description;
|
|
376
|
+
return `📄 **${article.short_description}**
|
|
377
|
+
🆔 ${article.sys_id}
|
|
378
|
+
📊 State: ${article.workflow_state}
|
|
379
|
+
📅 Updated: ${article.sys_updated_on}
|
|
380
|
+
${args.include_content ? `📝 ${snippet}` : ''}`;
|
|
381
|
+
}).join('\n\n');
|
|
382
|
+
return this.createResponse(`🔍 Knowledge Search Results for "${args.query}":
|
|
383
|
+
|
|
384
|
+
${articleList}
|
|
385
|
+
|
|
386
|
+
✨ Found ${articles.length} article(s)`);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Update Knowledge Article
|
|
390
|
+
*/
|
|
391
|
+
async updateKnowledgeArticle(args) {
|
|
392
|
+
this.logger.info('Updating knowledge article...', { sys_id: args.sys_id });
|
|
393
|
+
const updateData = {};
|
|
394
|
+
if (args.short_description)
|
|
395
|
+
updateData.short_description = args.short_description;
|
|
396
|
+
if (args.text)
|
|
397
|
+
updateData.text = args.text;
|
|
398
|
+
if (args.workflow_state)
|
|
399
|
+
updateData.workflow_state = args.workflow_state;
|
|
400
|
+
if (args.valid_to)
|
|
401
|
+
updateData.valid_to = args.valid_to;
|
|
402
|
+
if (args.keywords)
|
|
403
|
+
updateData.keywords = args.keywords.join(',');
|
|
404
|
+
this.logger.progress('Updating article in ServiceNow...');
|
|
405
|
+
const response = await this.updateRecord('kb_knowledge', args.sys_id, updateData);
|
|
406
|
+
if (!response.success) {
|
|
407
|
+
return this.createResponse(`❌ Failed to update article: ${response.error}`);
|
|
408
|
+
}
|
|
409
|
+
this.logger.info('✅ Article updated successfully');
|
|
410
|
+
return this.createResponse(`✅ Knowledge article updated successfully!\n🆔 sys_id: ${args.sys_id}`);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Retire Knowledge Article
|
|
414
|
+
*/
|
|
415
|
+
async retireKnowledgeArticle(args) {
|
|
416
|
+
this.logger.info('Retiring knowledge article...', { sys_id: args.sys_id });
|
|
417
|
+
const updateData = {
|
|
418
|
+
workflow_state: 'retired',
|
|
419
|
+
u_retirement_reason: args.retirement_reason || 'Retired via API'
|
|
420
|
+
};
|
|
421
|
+
const response = await this.updateRecord('kb_knowledge', args.sys_id, updateData);
|
|
422
|
+
if (!response.success) {
|
|
423
|
+
return this.createResponse(`❌ Failed to retire article: ${response.error}`);
|
|
424
|
+
}
|
|
425
|
+
this.logger.info('✅ Article retired successfully');
|
|
426
|
+
return this.createResponse(`✅ Knowledge article retired!\n🆔 sys_id: ${args.sys_id}`);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Create Knowledge Base
|
|
430
|
+
*/
|
|
431
|
+
async createKnowledgeBase(args) {
|
|
432
|
+
this.logger.info('Creating knowledge base...', { title: args.title });
|
|
433
|
+
const kbData = {
|
|
434
|
+
title: args.title,
|
|
435
|
+
description: args.description || '',
|
|
436
|
+
owner: args.owner || '',
|
|
437
|
+
kb_managers: args.kb_managers?.join(',') || ''
|
|
438
|
+
};
|
|
439
|
+
this.logger.progress('Creating KB in ServiceNow...');
|
|
440
|
+
const response = await this.createRecord('kb_knowledge_base', kbData);
|
|
441
|
+
if (!response.success) {
|
|
442
|
+
return this.createResponse(`❌ Failed to create KB: ${response.error}`);
|
|
443
|
+
}
|
|
444
|
+
const result = response.data;
|
|
445
|
+
this.logger.info('✅ Knowledge base created', { sys_id: result.sys_id });
|
|
446
|
+
return this.createResponse(`✅ Knowledge Base created!\n📚 **${args.title}**\n🆔 sys_id: ${result.sys_id}`);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Discover Knowledge Bases
|
|
450
|
+
*/
|
|
451
|
+
async discoverKnowledgeBases(args) {
|
|
452
|
+
this.logger.info('Discovering knowledge bases...');
|
|
453
|
+
const query = args.active_only ? 'active=true' : '';
|
|
454
|
+
const response = await this.queryTable('kb_knowledge_base', query, 50);
|
|
455
|
+
if (!response.success) {
|
|
456
|
+
return this.createResponse(`❌ Failed to discover KBs: ${response.error}`);
|
|
457
|
+
}
|
|
458
|
+
const kbs = response.data.result;
|
|
459
|
+
this.logger.info(`Found ${kbs.length} knowledge bases`);
|
|
460
|
+
const kbList = kbs.map((kb) => `📚 **${kb.title}**\n🆔 ${kb.sys_id}\n📝 ${kb.description || 'No description'}`).join('\n\n');
|
|
461
|
+
return this.createResponse(`📚 Knowledge Bases Found:\n\n${kbList}\n\n✨ Total: ${kbs.length} knowledge base(s)`);
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Get Knowledge Stats
|
|
465
|
+
*/
|
|
466
|
+
async getKnowledgeStats(args) {
|
|
467
|
+
this.logger.info('Getting knowledge statistics...');
|
|
468
|
+
let query = 'active=true';
|
|
469
|
+
if (args.kb_knowledge_base) {
|
|
470
|
+
query += `^kb_knowledge_base=${args.kb_knowledge_base}`;
|
|
471
|
+
}
|
|
472
|
+
this.logger.progress('Gathering statistics...');
|
|
473
|
+
// Get article counts by state
|
|
474
|
+
const states = ['draft', 'review', 'published', 'retired'];
|
|
475
|
+
const stats = { total: 0, by_state: {} };
|
|
476
|
+
for (const state of states) {
|
|
477
|
+
const stateQuery = `${query}^workflow_state=${state}`;
|
|
478
|
+
const response = await this.queryTable('kb_knowledge', stateQuery, 1);
|
|
479
|
+
if (response.success && response.data.headers) {
|
|
480
|
+
const count = parseInt(response.data.headers['x-total-count'] || '0');
|
|
481
|
+
stats.by_state[state] = count;
|
|
482
|
+
stats.total += count;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
this.logger.info('✅ Statistics gathered', stats);
|
|
486
|
+
return this.createResponse(`📊 Knowledge Base Statistics:\n\n` +
|
|
487
|
+
`📚 Total Articles: ${stats.total}\n` +
|
|
488
|
+
`📝 Draft: ${stats.by_state.draft || 0}\n` +
|
|
489
|
+
`👁️ In Review: ${stats.by_state.review || 0}\n` +
|
|
490
|
+
`✅ Published: ${stats.by_state.published || 0}\n` +
|
|
491
|
+
`🗄️ Retired: ${stats.by_state.retired || 0}`);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Knowledge Feedback
|
|
495
|
+
*/
|
|
496
|
+
async knowledgeFeedback(args) {
|
|
497
|
+
this.logger.info('Managing knowledge feedback...', { article_id: args.article_id });
|
|
498
|
+
if (args.rating || args.comments) {
|
|
499
|
+
// Create feedback
|
|
500
|
+
const feedbackData = {
|
|
501
|
+
article: args.article_id,
|
|
502
|
+
rating: args.rating || 0,
|
|
503
|
+
comments: args.comments || '',
|
|
504
|
+
user: 'api_user'
|
|
505
|
+
};
|
|
506
|
+
this.logger.progress('Submitting feedback...');
|
|
507
|
+
const response = await this.createRecord('kb_feedback', feedbackData);
|
|
508
|
+
if (!response.success) {
|
|
509
|
+
return this.createResponse(`❌ Failed to submit feedback: ${response.error}`);
|
|
510
|
+
}
|
|
511
|
+
this.logger.info('✅ Feedback submitted');
|
|
512
|
+
return this.createResponse(`✅ Feedback submitted!\n⭐ Rating: ${args.rating || 'N/A'}`);
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
// Get feedback for article
|
|
516
|
+
const query = `article=${args.article_id}`;
|
|
517
|
+
const response = await this.queryTable('kb_feedback', query, 10);
|
|
518
|
+
if (!response.success) {
|
|
519
|
+
return this.createResponse(`❌ Failed to get feedback: ${response.error}`);
|
|
520
|
+
}
|
|
521
|
+
const feedback = response.data.result;
|
|
522
|
+
const avgRating = feedback.length > 0 ?
|
|
523
|
+
(feedback.reduce((sum, f) => sum + (f.rating || 0), 0) / feedback.length).toFixed(1) :
|
|
524
|
+
'N/A';
|
|
525
|
+
return this.createResponse(`📊 Article Feedback:\n⭐ Average Rating: ${avgRating}\n💬 ${feedback.length} feedback entries`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Create Catalog Item
|
|
530
|
+
*/
|
|
531
|
+
async createCatalogItem(args) {
|
|
532
|
+
this.logger.info('Creating catalog item...', { name: args.name });
|
|
533
|
+
const itemData = {
|
|
534
|
+
name: args.name,
|
|
535
|
+
short_description: args.short_description,
|
|
536
|
+
category: args.category || '',
|
|
537
|
+
price: args.price || '0',
|
|
538
|
+
workflow: args.workflow || '',
|
|
539
|
+
active: true
|
|
540
|
+
};
|
|
541
|
+
this.logger.progress('Creating item in ServiceNow...');
|
|
542
|
+
const response = await this.createRecord('sc_cat_item', itemData);
|
|
543
|
+
if (!response.success) {
|
|
544
|
+
return this.createResponse(`❌ Failed to create catalog item: ${response.error}`);
|
|
545
|
+
}
|
|
546
|
+
const result = response.data;
|
|
547
|
+
this.logger.info('✅ Catalog item created', { sys_id: result.sys_id });
|
|
548
|
+
return this.createResponse(`✅ Catalog Item created!\n🛍️ **${args.name}**\n🆔 sys_id: ${result.sys_id}\n💰 Price: ${args.price || '0'}`);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Create Catalog Variable
|
|
552
|
+
*/
|
|
553
|
+
async createCatalogVariable(args) {
|
|
554
|
+
this.logger.info('Creating catalog variable...', { name: args.name });
|
|
555
|
+
const varData = {
|
|
556
|
+
cat_item: args.cat_item,
|
|
557
|
+
name: args.name,
|
|
558
|
+
question_text: args.question_text,
|
|
559
|
+
type: args.type || '6', // Single line text
|
|
560
|
+
mandatory: args.mandatory || false,
|
|
561
|
+
order: 100
|
|
562
|
+
};
|
|
563
|
+
this.logger.progress('Creating variable...');
|
|
564
|
+
const response = await this.createRecord('item_option_new', varData);
|
|
565
|
+
if (!response.success) {
|
|
566
|
+
return this.createResponse(`❌ Failed to create variable: ${response.error}`);
|
|
567
|
+
}
|
|
568
|
+
this.logger.info('✅ Variable created');
|
|
569
|
+
return this.createResponse(`✅ Catalog variable created!\n📝 ${args.question_text}\n🔤 Name: ${args.name}`);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Create Catalog UI Policy
|
|
573
|
+
*/
|
|
574
|
+
async createCatalogUIPolicy(args) {
|
|
575
|
+
this.logger.info('Creating catalog UI policy...', { short_description: args.short_description });
|
|
576
|
+
const policyData = {
|
|
577
|
+
cat_item: args.cat_item,
|
|
578
|
+
short_description: args.short_description,
|
|
579
|
+
condition: args.condition || '',
|
|
580
|
+
active: true
|
|
581
|
+
};
|
|
582
|
+
this.logger.progress('Creating UI policy...');
|
|
583
|
+
const response = await this.createRecord('catalog_ui_policy', policyData);
|
|
584
|
+
if (!response.success) {
|
|
585
|
+
return this.createResponse(`❌ Failed to create UI policy: ${response.error}`);
|
|
586
|
+
}
|
|
587
|
+
this.logger.info('✅ UI policy created');
|
|
588
|
+
return this.createResponse(`✅ Catalog UI Policy created!\n📋 ${args.short_description}\n🆔 sys_id: ${response.data.sys_id}`);
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Order Catalog Item
|
|
592
|
+
*/
|
|
593
|
+
async orderCatalogItem(args) {
|
|
594
|
+
this.logger.info('Ordering catalog item...', { cat_item: args.cat_item });
|
|
595
|
+
const orderData = {
|
|
596
|
+
cat_item: args.cat_item,
|
|
597
|
+
requested_for: args.requested_for || 'current_user',
|
|
598
|
+
quantity: args.quantity || 1,
|
|
599
|
+
variables: JSON.stringify(args.variables || {})
|
|
600
|
+
};
|
|
601
|
+
this.logger.progress('Submitting order...');
|
|
602
|
+
const response = await this.createRecord('sc_req_item', orderData);
|
|
603
|
+
if (!response.success) {
|
|
604
|
+
return this.createResponse(`❌ Failed to order item: ${response.error}`);
|
|
605
|
+
}
|
|
606
|
+
const result = response.data;
|
|
607
|
+
this.logger.info('✅ Order submitted', { number: result.number });
|
|
608
|
+
return this.createResponse(`✅ Catalog item ordered!\n📦 Request: ${result.number}\n🆔 sys_id: ${result.sys_id}\n📊 Status: ${result.state || 'Submitted'}`);
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Search Catalog
|
|
612
|
+
*/
|
|
613
|
+
async searchCatalog(args) {
|
|
614
|
+
this.logger.info('Searching service catalog...', { query: args.query });
|
|
615
|
+
let query = `nameLIKE${args.query}^ORshort_descriptionLIKE${args.query}`;
|
|
616
|
+
if (args.category) {
|
|
617
|
+
query += `^category=${args.category}`;
|
|
618
|
+
}
|
|
619
|
+
if (args.active_only) {
|
|
620
|
+
query += '^active=true';
|
|
621
|
+
}
|
|
622
|
+
this.logger.progress('Searching catalog...');
|
|
623
|
+
const response = await this.queryTable('sc_cat_item', query, args.limit || 10);
|
|
624
|
+
if (!response.success) {
|
|
625
|
+
return this.createResponse(`❌ Search failed: ${response.error}`);
|
|
626
|
+
}
|
|
627
|
+
const items = response.data.result;
|
|
628
|
+
if (!items.length) {
|
|
629
|
+
return this.createResponse(`❌ No catalog items found matching "${args.query}"`);
|
|
630
|
+
}
|
|
631
|
+
this.logger.info(`Found ${items.length} catalog items`);
|
|
632
|
+
const itemList = items.map((item) => `🛍️ **${item.name}**\n📝 ${item.short_description}\n💰 ${item.price || '0'}\n🆔 ${item.sys_id}`).join('\n\n');
|
|
633
|
+
return this.createResponse(`🔍 Catalog Search Results:\n\n${itemList}\n\n✨ Found ${items.length} item(s)`);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Get Catalog Item Details
|
|
637
|
+
*/
|
|
638
|
+
async getCatalogItemDetails(args) {
|
|
639
|
+
this.logger.info('Getting catalog item details...', { sys_id: args.sys_id });
|
|
640
|
+
const response = await this.getRecord('sc_cat_item', args.sys_id);
|
|
641
|
+
if (!response.success) {
|
|
642
|
+
return this.createResponse(`❌ Failed to get item details: ${response.error}`);
|
|
643
|
+
}
|
|
644
|
+
const item = response.data;
|
|
645
|
+
let details = `🛍️ **${item.name}**\n` +
|
|
646
|
+
`📝 ${item.short_description}\n` +
|
|
647
|
+
`💰 Price: ${item.price || '0'}\n` +
|
|
648
|
+
`📊 Active: ${item.active}\n` +
|
|
649
|
+
`🆔 sys_id: ${item.sys_id}`;
|
|
650
|
+
if (args.include_variables) {
|
|
651
|
+
// Get variables
|
|
652
|
+
const varQuery = `cat_item=${args.sys_id}`;
|
|
653
|
+
const varResponse = await this.queryTable('item_option_new', varQuery, 50);
|
|
654
|
+
if (varResponse.success && varResponse.data.result.length > 0) {
|
|
655
|
+
const variables = varResponse.data.result;
|
|
656
|
+
details += `\n\n📋 Variables (${variables.length}):\n`;
|
|
657
|
+
variables.forEach((v) => {
|
|
658
|
+
details += ` • ${v.question_text} (${v.name})\n`;
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
this.logger.info('✅ Retrieved item details');
|
|
663
|
+
return this.createResponse(details);
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Discover Catalogs
|
|
667
|
+
*/
|
|
668
|
+
async discoverCatalogs(args) {
|
|
669
|
+
this.logger.info('Discovering service catalogs...');
|
|
670
|
+
const response = await this.queryTable('sc_catalog', 'active=true', 20);
|
|
671
|
+
if (!response.success) {
|
|
672
|
+
return this.createResponse(`❌ Failed to discover catalogs: ${response.error}`);
|
|
673
|
+
}
|
|
674
|
+
const catalogs = response.data.result;
|
|
675
|
+
this.logger.info(`Found ${catalogs.length} catalogs`);
|
|
676
|
+
let catalogInfo = `📚 Service Catalogs:\n\n`;
|
|
677
|
+
for (const catalog of catalogs) {
|
|
678
|
+
catalogInfo += `🛍️ **${catalog.title}**\n🆔 ${catalog.sys_id}\n`;
|
|
679
|
+
if (args.include_categories) {
|
|
680
|
+
// Get categories for this catalog
|
|
681
|
+
const catQuery = `sc_catalog=${catalog.sys_id}^active=true`;
|
|
682
|
+
const catResponse = await this.queryTable('sc_category', catQuery, 10);
|
|
683
|
+
if (catResponse.success && catResponse.data.result.length > 0) {
|
|
684
|
+
catalogInfo += `📁 Categories:\n`;
|
|
685
|
+
catResponse.data.result.forEach((cat) => {
|
|
686
|
+
catalogInfo += ` • ${cat.title}\n`;
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
catalogInfo += '\n';
|
|
691
|
+
}
|
|
692
|
+
return this.createResponse(`${catalogInfo}✨ Total: ${catalogs.length} catalog(s) discovered`);
|
|
693
|
+
}
|
|
694
|
+
async start() {
|
|
695
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
696
|
+
await this.server.connect(transport);
|
|
697
|
+
// Log ready state
|
|
698
|
+
this.logger.info('🚀 ServiceNow Knowledge & Catalog MCP Server (Enhanced) running');
|
|
699
|
+
this.logger.info('📊 Token tracking enabled');
|
|
700
|
+
this.logger.info('⏳ Progress indicators active');
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
// Start the enhanced server
|
|
704
|
+
const server = new ServiceNowKnowledgeCatalogMCPEnhanced();
|
|
705
|
+
server.start().catch((error) => {
|
|
706
|
+
console.error('Failed to start enhanced server:', error);
|
|
707
|
+
process.exit(1);
|
|
708
|
+
});
|
|
709
|
+
//# sourceMappingURL=servicenow-knowledge-catalog-mcp-enhanced.js.map
|