snow-flow 3.1.2 ā 3.2.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/dist/dynamic-version.js +1 -1
- package/dist/mcp/servicenow-automation-mcp.js +523 -0
- package/dist/mcp/servicenow-change-virtualagent-pa-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-change-virtualagent-pa-mcp.js +1232 -0
- package/dist/mcp/servicenow-cmdb-event-hr-csm-devops-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-cmdb-event-hr-csm-devops-mcp.js +1416 -0
- package/dist/mcp/servicenow-flow-workspace-mobile-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-flow-workspace-mobile-mcp.js +1192 -0
- package/dist/mcp/servicenow-knowledge-catalog-mcp.d.ts +8 -0
- package/dist/mcp/servicenow-knowledge-catalog-mcp.js +1009 -0
- package/package.json +2 -2
|
@@ -0,0 +1,1009 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* ServiceNow Knowledge Management & Service Catalog MCP Server
|
|
5
|
+
* Handles knowledge articles, service catalog items, and related operations
|
|
6
|
+
* Uses official ServiceNow REST APIs for kb_knowledge and sc_cat_item tables
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
10
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
11
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
12
|
+
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
|
|
13
|
+
const mcp_auth_middleware_js_1 = require("../utils/mcp-auth-middleware.js");
|
|
14
|
+
const mcp_config_manager_js_1 = require("../utils/mcp-config-manager.js");
|
|
15
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
16
|
+
class ServiceNowKnowledgeCatalogMCP {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.server = new index_js_1.Server({
|
|
19
|
+
name: 'servicenow-knowledge-catalog',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
}, {
|
|
22
|
+
capabilities: {
|
|
23
|
+
tools: {},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
this.client = new servicenow_client_js_1.ServiceNowClient();
|
|
27
|
+
this.logger = new logger_js_1.Logger('ServiceNowKnowledgeCatalogMCP');
|
|
28
|
+
this.config = mcp_config_manager_js_1.mcpConfig.getConfig();
|
|
29
|
+
this.setupHandlers();
|
|
30
|
+
}
|
|
31
|
+
setupHandlers() {
|
|
32
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
33
|
+
tools: [
|
|
34
|
+
// Knowledge Management Tools
|
|
35
|
+
{
|
|
36
|
+
name: 'snow_create_knowledge_article',
|
|
37
|
+
description: 'Creates a knowledge article in ServiceNow Knowledge Base. Articles can contain solutions, how-to guides, or reference information.',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
short_description: { type: 'string', description: 'Article title' },
|
|
42
|
+
text: { type: 'string', description: 'Article content (HTML supported)' },
|
|
43
|
+
kb_knowledge_base: { type: 'string', description: 'Knowledge base sys_id or name' },
|
|
44
|
+
kb_category: { type: 'string', description: 'Category sys_id or name' },
|
|
45
|
+
article_type: { type: 'string', description: 'Type: text, html, wiki' },
|
|
46
|
+
workflow_state: { type: 'string', description: 'State: draft, review, published, retired' },
|
|
47
|
+
valid_to: { type: 'string', description: 'Expiration date (YYYY-MM-DD)' },
|
|
48
|
+
meta_description: { type: 'string', description: 'SEO meta description' },
|
|
49
|
+
keywords: { type: 'array', items: { type: 'string' }, description: 'Search keywords' },
|
|
50
|
+
author: { type: 'string', description: 'Author user sys_id or username' }
|
|
51
|
+
},
|
|
52
|
+
required: ['short_description', 'text']
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'snow_search_knowledge',
|
|
57
|
+
description: 'Searches knowledge articles using keywords, categories, or filters. Returns relevant articles with snippets.',
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
query: { type: 'string', description: 'Search query text' },
|
|
62
|
+
kb_knowledge_base: { type: 'string', description: 'Filter by knowledge base' },
|
|
63
|
+
kb_category: { type: 'string', description: 'Filter by category' },
|
|
64
|
+
workflow_state: { type: 'string', description: 'Filter by state (published, draft, etc.)' },
|
|
65
|
+
limit: { type: 'number', description: 'Maximum results to return', default: 10 },
|
|
66
|
+
include_content: { type: 'boolean', description: 'Include full article content', default: false }
|
|
67
|
+
},
|
|
68
|
+
required: ['query']
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'snow_update_knowledge_article',
|
|
73
|
+
description: 'Updates an existing knowledge article. Can modify content, metadata, or workflow state.',
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: {
|
|
77
|
+
sys_id: { type: 'string', description: 'Article sys_id to update' },
|
|
78
|
+
short_description: { type: 'string', description: 'Updated title' },
|
|
79
|
+
text: { type: 'string', description: 'Updated content' },
|
|
80
|
+
workflow_state: { type: 'string', description: 'New state' },
|
|
81
|
+
valid_to: { type: 'string', description: 'New expiration date' },
|
|
82
|
+
meta_description: { type: 'string', description: 'Updated SEO description' },
|
|
83
|
+
keywords: { type: 'array', items: { type: 'string' }, description: 'Updated keywords' }
|
|
84
|
+
},
|
|
85
|
+
required: ['sys_id']
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'snow_retire_knowledge_article',
|
|
90
|
+
description: 'Retires a knowledge article, making it unavailable for general use while preserving history.',
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
sys_id: { type: 'string', description: 'Article sys_id to retire' },
|
|
95
|
+
retirement_reason: { type: 'string', description: 'Reason for retirement' },
|
|
96
|
+
replacement_article: { type: 'string', description: 'Replacement article sys_id (optional)' }
|
|
97
|
+
},
|
|
98
|
+
required: ['sys_id']
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'snow_create_knowledge_base',
|
|
103
|
+
description: 'Creates a new knowledge base for organizing articles by topic, department, or audience.',
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
title: { type: 'string', description: 'Knowledge base title' },
|
|
108
|
+
description: { type: 'string', description: 'Knowledge base description' },
|
|
109
|
+
owner: { type: 'string', description: 'Owner user or group' },
|
|
110
|
+
managers: { type: 'array', items: { type: 'string' }, description: 'Manager users or groups' },
|
|
111
|
+
kb_version: { type: 'string', description: 'Version number' },
|
|
112
|
+
active: { type: 'boolean', description: 'Active status', default: true }
|
|
113
|
+
},
|
|
114
|
+
required: ['title']
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'snow_discover_knowledge_bases',
|
|
119
|
+
description: 'Discovers available knowledge bases and their categories in the ServiceNow instance.',
|
|
120
|
+
inputSchema: {
|
|
121
|
+
type: 'object',
|
|
122
|
+
properties: {
|
|
123
|
+
active_only: { type: 'boolean', description: 'Show only active knowledge bases', default: true }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
// Service Catalog Tools
|
|
128
|
+
{
|
|
129
|
+
name: 'snow_create_catalog_item',
|
|
130
|
+
description: 'Creates a service catalog item for user self-service requests. Includes forms, workflows, and fulfillment.',
|
|
131
|
+
inputSchema: {
|
|
132
|
+
type: 'object',
|
|
133
|
+
properties: {
|
|
134
|
+
name: { type: 'string', description: 'Catalog item name' },
|
|
135
|
+
short_description: { type: 'string', description: 'Brief description' },
|
|
136
|
+
description: { type: 'string', description: 'Full description (HTML)' },
|
|
137
|
+
category: { type: 'string', description: 'Category sys_id or name' },
|
|
138
|
+
sc_catalogs: { type: 'string', description: 'Catalog sys_id or name' },
|
|
139
|
+
price: { type: 'string', description: 'Item price' },
|
|
140
|
+
recurring_price: { type: 'string', description: 'Recurring price' },
|
|
141
|
+
recurring_frequency: { type: 'string', description: 'Frequency: monthly, yearly' },
|
|
142
|
+
workflow: { type: 'string', description: 'Fulfillment workflow name' },
|
|
143
|
+
delivery_time: { type: 'string', description: 'Expected delivery time' },
|
|
144
|
+
active: { type: 'boolean', description: 'Active status', default: true },
|
|
145
|
+
billable: { type: 'boolean', description: 'Is billable', default: false },
|
|
146
|
+
mobile_hide_price: { type: 'boolean', description: 'Hide price on mobile', default: false }
|
|
147
|
+
},
|
|
148
|
+
required: ['name', 'short_description']
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'snow_create_catalog_variable',
|
|
153
|
+
description: 'Adds a variable (form field) to a catalog item for collecting user input during ordering.',
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: 'object',
|
|
156
|
+
properties: {
|
|
157
|
+
cat_item: { type: 'string', description: 'Catalog item sys_id' },
|
|
158
|
+
name: { type: 'string', description: 'Variable name' },
|
|
159
|
+
question_text: { type: 'string', description: 'Question to display' },
|
|
160
|
+
type: { type: 'string', description: 'Type: single_line_text, multi_line_text, select_box, checkbox, reference, etc.' },
|
|
161
|
+
order: { type: 'number', description: 'Display order' },
|
|
162
|
+
mandatory: { type: 'boolean', description: 'Is required', default: false },
|
|
163
|
+
default_value: { type: 'string', description: 'Default value' },
|
|
164
|
+
help_text: { type: 'string', description: 'Help text for users' },
|
|
165
|
+
reference: { type: 'string', description: 'Reference table (for reference type)' },
|
|
166
|
+
choice_table: { type: 'string', description: 'Choice list name (for select_box)' }
|
|
167
|
+
},
|
|
168
|
+
required: ['cat_item', 'name', 'question_text', 'type']
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'snow_create_catalog_ui_policy',
|
|
173
|
+
description: 'Creates UI policies for catalog items to control form behavior based on user input.',
|
|
174
|
+
inputSchema: {
|
|
175
|
+
type: 'object',
|
|
176
|
+
properties: {
|
|
177
|
+
cat_item: { type: 'string', description: 'Catalog item sys_id' },
|
|
178
|
+
short_description: { type: 'string', description: 'Policy name' },
|
|
179
|
+
condition: { type: 'string', description: 'Condition script' },
|
|
180
|
+
applies_to: { type: 'string', description: 'Applies to: item, set, or variable' },
|
|
181
|
+
active: { type: 'boolean', description: 'Active status', default: true },
|
|
182
|
+
on_load: { type: 'boolean', description: 'Run on form load', default: true },
|
|
183
|
+
reverse_if_false: { type: 'boolean', description: 'Reverse actions if false', default: true }
|
|
184
|
+
},
|
|
185
|
+
required: ['cat_item', 'short_description', 'condition']
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: 'snow_create_catalog_client_script',
|
|
190
|
+
description: 'Creates client scripts for catalog items to add custom JavaScript behavior to forms.',
|
|
191
|
+
inputSchema: {
|
|
192
|
+
type: 'object',
|
|
193
|
+
properties: {
|
|
194
|
+
cat_item: { type: 'string', description: 'Catalog item sys_id' },
|
|
195
|
+
name: { type: 'string', description: 'Script name' },
|
|
196
|
+
script: { type: 'string', description: 'JavaScript code' },
|
|
197
|
+
type: { type: 'string', description: 'Type: onLoad, onChange, onSubmit, onCellEdit' },
|
|
198
|
+
applies_to: { type: 'string', description: 'Applies to: item, set, variable' },
|
|
199
|
+
variable: { type: 'string', description: 'Variable name (for onChange)' },
|
|
200
|
+
active: { type: 'boolean', description: 'Active status', default: true }
|
|
201
|
+
},
|
|
202
|
+
required: ['cat_item', 'name', 'script', 'type']
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: 'snow_search_catalog',
|
|
207
|
+
description: 'Searches service catalog for items, categories, or catalogs. Returns available items for ordering.',
|
|
208
|
+
inputSchema: {
|
|
209
|
+
type: 'object',
|
|
210
|
+
properties: {
|
|
211
|
+
query: { type: 'string', description: 'Search query' },
|
|
212
|
+
category: { type: 'string', description: 'Filter by category' },
|
|
213
|
+
catalog: { type: 'string', description: 'Filter by catalog' },
|
|
214
|
+
active_only: { type: 'boolean', description: 'Show only active items', default: true },
|
|
215
|
+
include_variables: { type: 'boolean', description: 'Include item variables', default: false },
|
|
216
|
+
limit: { type: 'number', description: 'Maximum results', default: 20 }
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'snow_order_catalog_item',
|
|
222
|
+
description: 'Orders a catalog item programmatically, creating a request (RITM) with specified variable values.',
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: 'object',
|
|
225
|
+
properties: {
|
|
226
|
+
cat_item: { type: 'string', description: 'Catalog item sys_id' },
|
|
227
|
+
requested_for: { type: 'string', description: 'User sys_id or username' },
|
|
228
|
+
variables: { type: 'object', description: 'Variable name-value pairs' },
|
|
229
|
+
quantity: { type: 'number', description: 'Quantity to order', default: 1 },
|
|
230
|
+
delivery_address: { type: 'string', description: 'Delivery address' },
|
|
231
|
+
special_instructions: { type: 'string', description: 'Special instructions' }
|
|
232
|
+
},
|
|
233
|
+
required: ['cat_item', 'requested_for']
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'snow_get_catalog_item_details',
|
|
238
|
+
description: 'Gets detailed information about a catalog item including variables, pricing, and availability.',
|
|
239
|
+
inputSchema: {
|
|
240
|
+
type: 'object',
|
|
241
|
+
properties: {
|
|
242
|
+
sys_id: { type: 'string', description: 'Catalog item sys_id' },
|
|
243
|
+
include_variables: { type: 'boolean', description: 'Include all variables', default: true },
|
|
244
|
+
include_ui_policies: { type: 'boolean', description: 'Include UI policies', default: false },
|
|
245
|
+
include_client_scripts: { type: 'boolean', description: 'Include client scripts', default: false }
|
|
246
|
+
},
|
|
247
|
+
required: ['sys_id']
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'snow_discover_catalogs',
|
|
252
|
+
description: 'Discovers available service catalogs and their categories in the ServiceNow instance.',
|
|
253
|
+
inputSchema: {
|
|
254
|
+
type: 'object',
|
|
255
|
+
properties: {
|
|
256
|
+
include_categories: { type: 'boolean', description: 'Include category tree', default: true },
|
|
257
|
+
active_only: { type: 'boolean', description: 'Show only active catalogs', default: true }
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}));
|
|
263
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
264
|
+
try {
|
|
265
|
+
const { name, arguments: args } = request.params;
|
|
266
|
+
const authResult = await mcp_auth_middleware_js_1.mcpAuth.ensureAuthenticated();
|
|
267
|
+
if (!authResult.success) {
|
|
268
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, authResult.error || 'Authentication required');
|
|
269
|
+
}
|
|
270
|
+
switch (name) {
|
|
271
|
+
// Knowledge Management
|
|
272
|
+
case 'snow_create_knowledge_article':
|
|
273
|
+
return await this.createKnowledgeArticle(args);
|
|
274
|
+
case 'snow_search_knowledge':
|
|
275
|
+
return await this.searchKnowledge(args);
|
|
276
|
+
case 'snow_update_knowledge_article':
|
|
277
|
+
return await this.updateKnowledgeArticle(args);
|
|
278
|
+
case 'snow_retire_knowledge_article':
|
|
279
|
+
return await this.retireKnowledgeArticle(args);
|
|
280
|
+
case 'snow_create_knowledge_base':
|
|
281
|
+
return await this.createKnowledgeBase(args);
|
|
282
|
+
case 'snow_discover_knowledge_bases':
|
|
283
|
+
return await this.discoverKnowledgeBases(args);
|
|
284
|
+
// Service Catalog
|
|
285
|
+
case 'snow_create_catalog_item':
|
|
286
|
+
return await this.createCatalogItem(args);
|
|
287
|
+
case 'snow_create_catalog_variable':
|
|
288
|
+
return await this.createCatalogVariable(args);
|
|
289
|
+
case 'snow_create_catalog_ui_policy':
|
|
290
|
+
return await this.createCatalogUIPolicy(args);
|
|
291
|
+
case 'snow_create_catalog_client_script':
|
|
292
|
+
return await this.createCatalogClientScript(args);
|
|
293
|
+
case 'snow_search_catalog':
|
|
294
|
+
return await this.searchCatalog(args);
|
|
295
|
+
case 'snow_order_catalog_item':
|
|
296
|
+
return await this.orderCatalogItem(args);
|
|
297
|
+
case 'snow_get_catalog_item_details':
|
|
298
|
+
return await this.getCatalogItemDetails(args);
|
|
299
|
+
case 'snow_discover_catalogs':
|
|
300
|
+
return await this.discoverCatalogs(args);
|
|
301
|
+
default:
|
|
302
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
this.logger.error(`Error in ${request.params.name}:`, error);
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Create Knowledge Article
|
|
313
|
+
* Uses kb_knowledge table
|
|
314
|
+
*/
|
|
315
|
+
async createKnowledgeArticle(args) {
|
|
316
|
+
try {
|
|
317
|
+
this.logger.info('Creating knowledge article...');
|
|
318
|
+
// Find knowledge base if name provided
|
|
319
|
+
let kbId = args.kb_knowledge_base;
|
|
320
|
+
if (kbId && !kbId.match(/^[a-f0-9]{32}$/)) {
|
|
321
|
+
const kbResponse = await this.client.searchRecords('kb_knowledge_base', `title=${kbId}`, 1);
|
|
322
|
+
if (kbResponse.success && kbResponse.data.result.length) {
|
|
323
|
+
kbId = kbResponse.data.result[0].sys_id;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// Find category if name provided
|
|
327
|
+
let categoryId = args.kb_category;
|
|
328
|
+
if (categoryId && !categoryId.match(/^[a-f0-9]{32}$/)) {
|
|
329
|
+
const catResponse = await this.client.searchRecords('kb_category', `label=${categoryId}`, 1);
|
|
330
|
+
if (catResponse.success && catResponse.data.result.length) {
|
|
331
|
+
categoryId = catResponse.data.result[0].sys_id;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const articleData = {
|
|
335
|
+
short_description: args.short_description,
|
|
336
|
+
text: args.text,
|
|
337
|
+
kb_knowledge_base: kbId || '',
|
|
338
|
+
kb_category: categoryId || '',
|
|
339
|
+
article_type: args.article_type || 'text',
|
|
340
|
+
workflow_state: args.workflow_state || 'draft',
|
|
341
|
+
valid_to: args.valid_to || '',
|
|
342
|
+
meta_description: args.meta_description || '',
|
|
343
|
+
keywords: args.keywords ? args.keywords.join(',') : '',
|
|
344
|
+
author: args.author || ''
|
|
345
|
+
};
|
|
346
|
+
const updateSetResult = await this.client.ensureUpdateSet();
|
|
347
|
+
const response = await this.client.createRecord('kb_knowledge', articleData);
|
|
348
|
+
if (!response.success) {
|
|
349
|
+
throw new Error(`Failed to create knowledge article: ${response.error}`);
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
content: [{
|
|
353
|
+
type: 'text',
|
|
354
|
+
text: `ā
Knowledge Article created successfully!
|
|
355
|
+
|
|
356
|
+
š **${args.short_description}**
|
|
357
|
+
š sys_id: ${response.data.sys_id}
|
|
358
|
+
š Knowledge Base: ${args.kb_knowledge_base || 'Default'}
|
|
359
|
+
š·ļø Category: ${args.kb_category || 'Uncategorized'}
|
|
360
|
+
š Type: ${args.article_type || 'text'}
|
|
361
|
+
š State: ${args.workflow_state || 'draft'}
|
|
362
|
+
${args.keywords ? `š Keywords: ${args.keywords.join(', ')}` : ''}
|
|
363
|
+
${args.valid_to ? `š
Valid Until: ${args.valid_to}` : ''}
|
|
364
|
+
|
|
365
|
+
⨠Article created and ready for review!`
|
|
366
|
+
}]
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
this.logger.error('Failed to create knowledge article:', error);
|
|
371
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create knowledge article: ${error}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Search Knowledge Articles
|
|
376
|
+
* Uses kb_knowledge table with text search
|
|
377
|
+
*/
|
|
378
|
+
async searchKnowledge(args) {
|
|
379
|
+
try {
|
|
380
|
+
this.logger.info('Searching knowledge articles...');
|
|
381
|
+
let query = `short_descriptionLIKE${args.query}^ORtextLIKE${args.query}`;
|
|
382
|
+
if (args.kb_knowledge_base) {
|
|
383
|
+
query += `^kb_knowledge_base=${args.kb_knowledge_base}`;
|
|
384
|
+
}
|
|
385
|
+
if (args.kb_category) {
|
|
386
|
+
query += `^kb_category=${args.kb_category}`;
|
|
387
|
+
}
|
|
388
|
+
if (args.workflow_state) {
|
|
389
|
+
query += `^workflow_state=${args.workflow_state}`;
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
query += '^workflow_state=published'; // Default to published only
|
|
393
|
+
}
|
|
394
|
+
const limit = args.limit || 10;
|
|
395
|
+
const response = await this.client.searchRecords('kb_knowledge', query, limit);
|
|
396
|
+
if (!response.success) {
|
|
397
|
+
throw new Error('Failed to search knowledge articles');
|
|
398
|
+
}
|
|
399
|
+
const articles = response.data.result;
|
|
400
|
+
if (!articles.length) {
|
|
401
|
+
return {
|
|
402
|
+
content: [{
|
|
403
|
+
type: 'text',
|
|
404
|
+
text: `ā No knowledge articles found matching "${args.query}"`
|
|
405
|
+
}]
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
const articleList = articles.map((article) => {
|
|
409
|
+
const snippet = args.include_content ?
|
|
410
|
+
article.text?.substring(0, 200) + '...' :
|
|
411
|
+
article.short_description;
|
|
412
|
+
return `š **${article.short_description}**
|
|
413
|
+
š ${article.sys_id}
|
|
414
|
+
š State: ${article.workflow_state}
|
|
415
|
+
š
Updated: ${article.sys_updated_on}
|
|
416
|
+
${args.include_content ? `š ${snippet}` : ''}`;
|
|
417
|
+
}).join('\n\n');
|
|
418
|
+
return {
|
|
419
|
+
content: [{
|
|
420
|
+
type: 'text',
|
|
421
|
+
text: `š Knowledge Search Results for "${args.query}":
|
|
422
|
+
|
|
423
|
+
${articleList}
|
|
424
|
+
|
|
425
|
+
⨠Found ${articles.length} article(s)`
|
|
426
|
+
}]
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
this.logger.error('Failed to search knowledge:', error);
|
|
431
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search knowledge: ${error}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Update Knowledge Article
|
|
436
|
+
*/
|
|
437
|
+
async updateKnowledgeArticle(args) {
|
|
438
|
+
try {
|
|
439
|
+
this.logger.info('Updating knowledge article...');
|
|
440
|
+
const updateData = {};
|
|
441
|
+
if (args.short_description)
|
|
442
|
+
updateData.short_description = args.short_description;
|
|
443
|
+
if (args.text)
|
|
444
|
+
updateData.text = args.text;
|
|
445
|
+
if (args.workflow_state)
|
|
446
|
+
updateData.workflow_state = args.workflow_state;
|
|
447
|
+
if (args.valid_to)
|
|
448
|
+
updateData.valid_to = args.valid_to;
|
|
449
|
+
if (args.meta_description)
|
|
450
|
+
updateData.meta_description = args.meta_description;
|
|
451
|
+
if (args.keywords)
|
|
452
|
+
updateData.keywords = args.keywords.join(',');
|
|
453
|
+
const response = await this.client.updateRecord('kb_knowledge', args.sys_id, updateData);
|
|
454
|
+
if (!response.success) {
|
|
455
|
+
throw new Error(`Failed to update knowledge article: ${response.error}`);
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
content: [{
|
|
459
|
+
type: 'text',
|
|
460
|
+
text: `ā
Knowledge Article updated successfully!
|
|
461
|
+
|
|
462
|
+
š sys_id: ${args.sys_id}
|
|
463
|
+
${args.short_description ? `š New Title: ${args.short_description}` : ''}
|
|
464
|
+
${args.workflow_state ? `š New State: ${args.workflow_state}` : ''}
|
|
465
|
+
${args.valid_to ? `š
Valid Until: ${args.valid_to}` : ''}
|
|
466
|
+
|
|
467
|
+
⨠Article updated!`
|
|
468
|
+
}]
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
catch (error) {
|
|
472
|
+
this.logger.error('Failed to update knowledge article:', error);
|
|
473
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to update knowledge article: ${error}`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Retire Knowledge Article
|
|
478
|
+
*/
|
|
479
|
+
async retireKnowledgeArticle(args) {
|
|
480
|
+
try {
|
|
481
|
+
this.logger.info('Retiring knowledge article...');
|
|
482
|
+
const updateData = {
|
|
483
|
+
workflow_state: 'retired',
|
|
484
|
+
retirement_date: new Date().toISOString(),
|
|
485
|
+
retirement_reason: args.retirement_reason || 'Retired via API'
|
|
486
|
+
};
|
|
487
|
+
if (args.replacement_article) {
|
|
488
|
+
updateData['replacement_article'] = args.replacement_article;
|
|
489
|
+
}
|
|
490
|
+
const response = await this.client.updateRecord('kb_knowledge', args.sys_id, updateData);
|
|
491
|
+
if (!response.success) {
|
|
492
|
+
throw new Error(`Failed to retire knowledge article: ${response.error}`);
|
|
493
|
+
}
|
|
494
|
+
return {
|
|
495
|
+
content: [{
|
|
496
|
+
type: 'text',
|
|
497
|
+
text: `ā
Knowledge Article retired successfully!
|
|
498
|
+
|
|
499
|
+
š sys_id: ${args.sys_id}
|
|
500
|
+
š State: Retired
|
|
501
|
+
š Reason: ${args.retirement_reason || 'Retired via API'}
|
|
502
|
+
${args.replacement_article ? `š Replacement: ${args.replacement_article}` : ''}
|
|
503
|
+
|
|
504
|
+
⨠Article retired and archived!`
|
|
505
|
+
}]
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
this.logger.error('Failed to retire knowledge article:', error);
|
|
510
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to retire knowledge article: ${error}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Create Knowledge Base
|
|
515
|
+
*/
|
|
516
|
+
async createKnowledgeBase(args) {
|
|
517
|
+
try {
|
|
518
|
+
this.logger.info('Creating knowledge base...');
|
|
519
|
+
const kbData = {
|
|
520
|
+
title: args.title,
|
|
521
|
+
description: args.description || '',
|
|
522
|
+
owner: args.owner || '',
|
|
523
|
+
kb_version: args.kb_version || '1.0',
|
|
524
|
+
active: args.active !== false
|
|
525
|
+
};
|
|
526
|
+
const updateSetResult = await this.client.ensureUpdateSet();
|
|
527
|
+
const response = await this.client.createRecord('kb_knowledge_base', kbData);
|
|
528
|
+
if (!response.success) {
|
|
529
|
+
throw new Error(`Failed to create knowledge base: ${response.error}`);
|
|
530
|
+
}
|
|
531
|
+
return {
|
|
532
|
+
content: [{
|
|
533
|
+
type: 'text',
|
|
534
|
+
text: `ā
Knowledge Base created successfully!
|
|
535
|
+
|
|
536
|
+
š **${args.title}**
|
|
537
|
+
š sys_id: ${response.data.sys_id}
|
|
538
|
+
š Description: ${args.description || 'No description'}
|
|
539
|
+
š¤ Owner: ${args.owner || 'Not specified'}
|
|
540
|
+
š¢ Version: ${args.kb_version || '1.0'}
|
|
541
|
+
š Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
542
|
+
|
|
543
|
+
⨠Knowledge base ready for articles!`
|
|
544
|
+
}]
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
catch (error) {
|
|
548
|
+
this.logger.error('Failed to create knowledge base:', error);
|
|
549
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create knowledge base: ${error}`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Discover Knowledge Bases
|
|
554
|
+
*/
|
|
555
|
+
async discoverKnowledgeBases(args) {
|
|
556
|
+
try {
|
|
557
|
+
this.logger.info('Discovering knowledge bases...');
|
|
558
|
+
let query = '';
|
|
559
|
+
if (args.active_only) {
|
|
560
|
+
query = 'active=true';
|
|
561
|
+
}
|
|
562
|
+
const kbResponse = await this.client.searchRecords('kb_knowledge_base', query, 50);
|
|
563
|
+
if (!kbResponse.success) {
|
|
564
|
+
throw new Error('Failed to discover knowledge bases');
|
|
565
|
+
}
|
|
566
|
+
const knowledgeBases = kbResponse.data.result;
|
|
567
|
+
// Get categories for each knowledge base
|
|
568
|
+
const kbWithCategories = await Promise.all(knowledgeBases.map(async (kb) => {
|
|
569
|
+
const catResponse = await this.client.searchRecords('kb_category', `kb_knowledge_base=${kb.sys_id}`, 20);
|
|
570
|
+
const categories = catResponse.success ? catResponse.data.result : [];
|
|
571
|
+
return { ...kb, categories };
|
|
572
|
+
}));
|
|
573
|
+
const kbText = kbWithCategories.map((kb) => {
|
|
574
|
+
const categoryList = kb.categories.map((cat) => ` - ${cat.label}`).join('\n');
|
|
575
|
+
return `š **${kb.title}** ${kb.active ? 'ā
' : 'ā'}
|
|
576
|
+
š ${kb.sys_id}
|
|
577
|
+
š ${kb.description || 'No description'}
|
|
578
|
+
š Categories:
|
|
579
|
+
${categoryList || ' No categories'}`;
|
|
580
|
+
}).join('\n\n');
|
|
581
|
+
return {
|
|
582
|
+
content: [{
|
|
583
|
+
type: 'text',
|
|
584
|
+
text: `š Discovered Knowledge Bases:
|
|
585
|
+
|
|
586
|
+
${kbText}
|
|
587
|
+
|
|
588
|
+
⨠Found ${knowledgeBases.length} knowledge base(s)`
|
|
589
|
+
}]
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
catch (error) {
|
|
593
|
+
this.logger.error('Failed to discover knowledge bases:', error);
|
|
594
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover knowledge bases: ${error}`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Create Catalog Item
|
|
599
|
+
* Uses sc_cat_item table
|
|
600
|
+
*/
|
|
601
|
+
async createCatalogItem(args) {
|
|
602
|
+
try {
|
|
603
|
+
this.logger.info('Creating catalog item...');
|
|
604
|
+
// Find category if name provided
|
|
605
|
+
let categoryId = args.category;
|
|
606
|
+
if (categoryId && !categoryId.match(/^[a-f0-9]{32}$/)) {
|
|
607
|
+
const catResponse = await this.client.searchRecords('sc_category', `title=${categoryId}`, 1);
|
|
608
|
+
if (catResponse.success && catResponse.data.result.length) {
|
|
609
|
+
categoryId = catResponse.data.result[0].sys_id;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
// Find catalog if name provided
|
|
613
|
+
let catalogId = args.sc_catalogs;
|
|
614
|
+
if (catalogId && !catalogId.match(/^[a-f0-9]{32}$/)) {
|
|
615
|
+
const catalogResponse = await this.client.searchRecords('sc_catalog', `title=${catalogId}`, 1);
|
|
616
|
+
if (catalogResponse.success && catalogResponse.data.result.length) {
|
|
617
|
+
catalogId = catalogResponse.data.result[0].sys_id;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const itemData = {
|
|
621
|
+
name: args.name,
|
|
622
|
+
short_description: args.short_description,
|
|
623
|
+
description: args.description || '',
|
|
624
|
+
category: categoryId || '',
|
|
625
|
+
sc_catalogs: catalogId || '',
|
|
626
|
+
price: args.price || '0',
|
|
627
|
+
recurring_price: args.recurring_price || '0',
|
|
628
|
+
recurring_frequency: args.recurring_frequency || '',
|
|
629
|
+
workflow: args.workflow || '',
|
|
630
|
+
delivery_time: args.delivery_time || '3 business days',
|
|
631
|
+
active: args.active !== false,
|
|
632
|
+
billable: args.billable || false,
|
|
633
|
+
mobile_hide_price: args.mobile_hide_price || false
|
|
634
|
+
};
|
|
635
|
+
const updateSetResult = await this.client.ensureUpdateSet();
|
|
636
|
+
const response = await this.client.createRecord('sc_cat_item', itemData);
|
|
637
|
+
if (!response.success) {
|
|
638
|
+
throw new Error(`Failed to create catalog item: ${response.error}`);
|
|
639
|
+
}
|
|
640
|
+
return {
|
|
641
|
+
content: [{
|
|
642
|
+
type: 'text',
|
|
643
|
+
text: `ā
Catalog Item created successfully!
|
|
644
|
+
|
|
645
|
+
šļø **${args.name}**
|
|
646
|
+
š sys_id: ${response.data.sys_id}
|
|
647
|
+
š ${args.short_description}
|
|
648
|
+
${args.category ? `š Category: ${args.category}` : ''}
|
|
649
|
+
${args.price && args.price !== '0' ? `š° Price: $${args.price}` : ''}
|
|
650
|
+
${args.recurring_price && args.recurring_price !== '0' ? `š Recurring: $${args.recurring_price} ${args.recurring_frequency || ''}` : ''}
|
|
651
|
+
š¦ Delivery: ${args.delivery_time || '3 business days'}
|
|
652
|
+
š Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
653
|
+
|
|
654
|
+
⨠Catalog item ready for ordering!`
|
|
655
|
+
}]
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
this.logger.error('Failed to create catalog item:', error);
|
|
660
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog item: ${error}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Create Catalog Variable
|
|
665
|
+
* Uses item_option_new table
|
|
666
|
+
*/
|
|
667
|
+
async createCatalogVariable(args) {
|
|
668
|
+
try {
|
|
669
|
+
this.logger.info('Creating catalog variable...');
|
|
670
|
+
const variableData = {
|
|
671
|
+
cat_item: args.cat_item,
|
|
672
|
+
name: args.name,
|
|
673
|
+
question_text: args.question_text,
|
|
674
|
+
type: args.type,
|
|
675
|
+
order: args.order || 100,
|
|
676
|
+
mandatory: args.mandatory || false,
|
|
677
|
+
default_value: args.default_value || '',
|
|
678
|
+
help_text: args.help_text || '',
|
|
679
|
+
reference: args.reference || '',
|
|
680
|
+
choice_table: args.choice_table || ''
|
|
681
|
+
};
|
|
682
|
+
const response = await this.client.createRecord('item_option_new', variableData);
|
|
683
|
+
if (!response.success) {
|
|
684
|
+
throw new Error(`Failed to create catalog variable: ${response.error}`);
|
|
685
|
+
}
|
|
686
|
+
return {
|
|
687
|
+
content: [{
|
|
688
|
+
type: 'text',
|
|
689
|
+
text: `ā
Catalog Variable created successfully!
|
|
690
|
+
|
|
691
|
+
š **${args.question_text}**
|
|
692
|
+
š sys_id: ${response.data.sys_id}
|
|
693
|
+
š·ļø Name: ${args.name}
|
|
694
|
+
š Type: ${args.type}
|
|
695
|
+
š¢ Order: ${args.order || 100}
|
|
696
|
+
${args.mandatory ? 'ā ļø Required: Yes' : 'ā
Required: No'}
|
|
697
|
+
${args.default_value ? `š Default: ${args.default_value}` : ''}
|
|
698
|
+
${args.help_text ? `ā Help: ${args.help_text}` : ''}
|
|
699
|
+
|
|
700
|
+
⨠Variable added to catalog item!`
|
|
701
|
+
}]
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
this.logger.error('Failed to create catalog variable:', error);
|
|
706
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog variable: ${error}`);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Create Catalog UI Policy
|
|
711
|
+
* Uses catalog_ui_policy table
|
|
712
|
+
*/
|
|
713
|
+
async createCatalogUIPolicy(args) {
|
|
714
|
+
try {
|
|
715
|
+
this.logger.info('Creating catalog UI policy...');
|
|
716
|
+
const policyData = {
|
|
717
|
+
catalog_item: args.cat_item,
|
|
718
|
+
short_description: args.short_description,
|
|
719
|
+
catalog_conditions: args.condition,
|
|
720
|
+
applies_catalog: args.applies_to || 'item',
|
|
721
|
+
active: args.active !== false,
|
|
722
|
+
applies_on_load: args.on_load !== false,
|
|
723
|
+
reverse_if_false: args.reverse_if_false !== false
|
|
724
|
+
};
|
|
725
|
+
const response = await this.client.createRecord('catalog_ui_policy', policyData);
|
|
726
|
+
if (!response.success) {
|
|
727
|
+
throw new Error(`Failed to create catalog UI policy: ${response.error}`);
|
|
728
|
+
}
|
|
729
|
+
return {
|
|
730
|
+
content: [{
|
|
731
|
+
type: 'text',
|
|
732
|
+
text: `ā
Catalog UI Policy created successfully!
|
|
733
|
+
|
|
734
|
+
š **${args.short_description}**
|
|
735
|
+
š sys_id: ${response.data.sys_id}
|
|
736
|
+
šÆ Applies to: ${args.applies_to || 'item'}
|
|
737
|
+
š Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
738
|
+
ā” On Load: ${args.on_load !== false ? 'Yes' : 'No'}
|
|
739
|
+
š Reverse if False: ${args.reverse_if_false !== false ? 'Yes' : 'No'}
|
|
740
|
+
|
|
741
|
+
⨠UI policy configured!`
|
|
742
|
+
}]
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
catch (error) {
|
|
746
|
+
this.logger.error('Failed to create catalog UI policy:', error);
|
|
747
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog UI policy: ${error}`);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Create Catalog Client Script
|
|
752
|
+
* Uses catalog_script_client table
|
|
753
|
+
*/
|
|
754
|
+
async createCatalogClientScript(args) {
|
|
755
|
+
try {
|
|
756
|
+
this.logger.info('Creating catalog client script...');
|
|
757
|
+
const scriptData = {
|
|
758
|
+
cat_item: args.cat_item,
|
|
759
|
+
name: args.name,
|
|
760
|
+
script: args.script,
|
|
761
|
+
type: args.type,
|
|
762
|
+
applies_to: args.applies_to || 'item',
|
|
763
|
+
cat_variable: args.variable || '',
|
|
764
|
+
active: args.active !== false
|
|
765
|
+
};
|
|
766
|
+
const response = await this.client.createRecord('catalog_script_client', scriptData);
|
|
767
|
+
if (!response.success) {
|
|
768
|
+
throw new Error(`Failed to create catalog client script: ${response.error}`);
|
|
769
|
+
}
|
|
770
|
+
return {
|
|
771
|
+
content: [{
|
|
772
|
+
type: 'text',
|
|
773
|
+
text: `ā
Catalog Client Script created successfully!
|
|
774
|
+
|
|
775
|
+
š **${args.name}**
|
|
776
|
+
š sys_id: ${response.data.sys_id}
|
|
777
|
+
šÆ Type: ${args.type}
|
|
778
|
+
š Applies to: ${args.applies_to || 'item'}
|
|
779
|
+
${args.variable ? `š Variable: ${args.variable}` : ''}
|
|
780
|
+
š Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
781
|
+
|
|
782
|
+
⨠Client script added to catalog item!`
|
|
783
|
+
}]
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
catch (error) {
|
|
787
|
+
this.logger.error('Failed to create catalog client script:', error);
|
|
788
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog client script: ${error}`);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Search Service Catalog
|
|
793
|
+
*/
|
|
794
|
+
async searchCatalog(args) {
|
|
795
|
+
try {
|
|
796
|
+
this.logger.info('Searching service catalog...');
|
|
797
|
+
let query = args.query ? `nameLIKE${args.query}^ORshort_descriptionLIKE${args.query}` : '';
|
|
798
|
+
if (args.category) {
|
|
799
|
+
query += query ? '^' : '';
|
|
800
|
+
query += `category=${args.category}`;
|
|
801
|
+
}
|
|
802
|
+
if (args.catalog) {
|
|
803
|
+
query += query ? '^' : '';
|
|
804
|
+
query += `sc_catalogs=${args.catalog}`;
|
|
805
|
+
}
|
|
806
|
+
if (args.active_only) {
|
|
807
|
+
query += query ? '^' : '';
|
|
808
|
+
query += 'active=true';
|
|
809
|
+
}
|
|
810
|
+
const limit = args.limit || 20;
|
|
811
|
+
const response = await this.client.searchRecords('sc_cat_item', query, limit);
|
|
812
|
+
if (!response.success) {
|
|
813
|
+
throw new Error('Failed to search catalog');
|
|
814
|
+
}
|
|
815
|
+
const items = response.data.result;
|
|
816
|
+
if (!items.length) {
|
|
817
|
+
return {
|
|
818
|
+
content: [{
|
|
819
|
+
type: 'text',
|
|
820
|
+
text: `ā No catalog items found${args.query ? ` matching "${args.query}"` : ''}`
|
|
821
|
+
}]
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
const itemList = items.map((item) => {
|
|
825
|
+
return `šļø **${item.name}**
|
|
826
|
+
š ${item.sys_id}
|
|
827
|
+
š ${item.short_description}
|
|
828
|
+
${item.price && item.price !== '0' ? `š° Price: $${item.price}` : ''}
|
|
829
|
+
š Active: ${item.active ? 'Yes' : 'No'}`;
|
|
830
|
+
}).join('\n\n');
|
|
831
|
+
return {
|
|
832
|
+
content: [{
|
|
833
|
+
type: 'text',
|
|
834
|
+
text: `š Catalog Search Results${args.query ? ` for "${args.query}"` : ''}:
|
|
835
|
+
|
|
836
|
+
${itemList}
|
|
837
|
+
|
|
838
|
+
⨠Found ${items.length} catalog item(s)`
|
|
839
|
+
}]
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
catch (error) {
|
|
843
|
+
this.logger.error('Failed to search catalog:', error);
|
|
844
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search catalog: ${error}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Order Catalog Item
|
|
849
|
+
* Creates sc_request and sc_req_item records
|
|
850
|
+
*/
|
|
851
|
+
async orderCatalogItem(args) {
|
|
852
|
+
try {
|
|
853
|
+
this.logger.info('Ordering catalog item...');
|
|
854
|
+
// Create service catalog request
|
|
855
|
+
const requestData = {
|
|
856
|
+
requested_for: args.requested_for,
|
|
857
|
+
opened_by: args.requested_for,
|
|
858
|
+
special_instructions: args.special_instructions || ''
|
|
859
|
+
};
|
|
860
|
+
const requestResponse = await this.client.createRecord('sc_request', requestData);
|
|
861
|
+
if (!requestResponse.success) {
|
|
862
|
+
throw new Error(`Failed to create request: ${requestResponse.error}`);
|
|
863
|
+
}
|
|
864
|
+
const requestId = requestResponse.data.sys_id;
|
|
865
|
+
// Create requested item (RITM)
|
|
866
|
+
const ritmData = {
|
|
867
|
+
request: requestId,
|
|
868
|
+
cat_item: args.cat_item,
|
|
869
|
+
requested_for: args.requested_for,
|
|
870
|
+
quantity: args.quantity || 1,
|
|
871
|
+
delivery_address: args.delivery_address || ''
|
|
872
|
+
};
|
|
873
|
+
const ritmResponse = await this.client.createRecord('sc_req_item', ritmData);
|
|
874
|
+
if (!ritmResponse.success) {
|
|
875
|
+
throw new Error(`Failed to create requested item: ${ritmResponse.error}`);
|
|
876
|
+
}
|
|
877
|
+
const ritmId = ritmResponse.data.sys_id;
|
|
878
|
+
const ritmNumber = ritmResponse.data.number;
|
|
879
|
+
// Set variable values if provided
|
|
880
|
+
if (args.variables) {
|
|
881
|
+
for (const [varName, varValue] of Object.entries(args.variables)) {
|
|
882
|
+
const varData = {
|
|
883
|
+
request_item: ritmId,
|
|
884
|
+
name: varName,
|
|
885
|
+
value: varValue
|
|
886
|
+
};
|
|
887
|
+
await this.client.createRecord('sc_item_option_mtom', varData);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return {
|
|
891
|
+
content: [{
|
|
892
|
+
type: 'text',
|
|
893
|
+
text: `ā
Catalog Item ordered successfully!
|
|
894
|
+
|
|
895
|
+
šļø **Order Placed**
|
|
896
|
+
š Request: ${requestId}
|
|
897
|
+
š¦ RITM: ${ritmNumber}
|
|
898
|
+
š¤ Requested For: ${args.requested_for}
|
|
899
|
+
š Quantity: ${args.quantity || 1}
|
|
900
|
+
${args.delivery_address ? `š Delivery: ${args.delivery_address}` : ''}
|
|
901
|
+
${args.special_instructions ? `š Instructions: ${args.special_instructions}` : ''}
|
|
902
|
+
|
|
903
|
+
⨠Order submitted for fulfillment!`
|
|
904
|
+
}]
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
catch (error) {
|
|
908
|
+
this.logger.error('Failed to order catalog item:', error);
|
|
909
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to order catalog item: ${error}`);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Get Catalog Item Details
|
|
914
|
+
*/
|
|
915
|
+
async getCatalogItemDetails(args) {
|
|
916
|
+
try {
|
|
917
|
+
this.logger.info('Getting catalog item details...');
|
|
918
|
+
const itemResponse = await this.client.getRecord('sc_cat_item', args.sys_id);
|
|
919
|
+
if (!itemResponse.success) {
|
|
920
|
+
throw new Error('Catalog item not found');
|
|
921
|
+
}
|
|
922
|
+
const item = itemResponse.data;
|
|
923
|
+
let details = `šļø **${item.name}**
|
|
924
|
+
š sys_id: ${item.sys_id}
|
|
925
|
+
š ${item.short_description}
|
|
926
|
+
š ${item.description || 'No detailed description'}
|
|
927
|
+
${item.price && item.price !== '0' ? `š° Price: $${item.price}` : ''}
|
|
928
|
+
${item.recurring_price && item.recurring_price !== '0' ? `š Recurring: $${item.recurring_price}` : ''}
|
|
929
|
+
š¦ Delivery: ${item.delivery_time || '3 business days'}
|
|
930
|
+
š Active: ${item.active ? 'Yes' : 'No'}`;
|
|
931
|
+
// Get variables if requested
|
|
932
|
+
if (args.include_variables) {
|
|
933
|
+
const varResponse = await this.client.searchRecords('item_option_new', `cat_item=${args.sys_id}`, 50);
|
|
934
|
+
if (varResponse.success && varResponse.data.result.length) {
|
|
935
|
+
const variables = varResponse.data.result.map((v) => ` - ${v.question_text} (${v.type})${v.mandatory ? ' *Required' : ''}`).join('\n');
|
|
936
|
+
details += `\n\nš **Variables:**\n${variables}`;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
return {
|
|
940
|
+
content: [{
|
|
941
|
+
type: 'text',
|
|
942
|
+
text: details + '\n\n⨠Catalog item details retrieved!'
|
|
943
|
+
}]
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
catch (error) {
|
|
947
|
+
this.logger.error('Failed to get catalog item details:', error);
|
|
948
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get catalog item details: ${error}`);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Discover Service Catalogs
|
|
953
|
+
*/
|
|
954
|
+
async discoverCatalogs(args) {
|
|
955
|
+
try {
|
|
956
|
+
this.logger.info('Discovering service catalogs...');
|
|
957
|
+
let query = '';
|
|
958
|
+
if (args.active_only) {
|
|
959
|
+
query = 'active=true';
|
|
960
|
+
}
|
|
961
|
+
const catalogResponse = await this.client.searchRecords('sc_catalog', query, 50);
|
|
962
|
+
if (!catalogResponse.success) {
|
|
963
|
+
throw new Error('Failed to discover catalogs');
|
|
964
|
+
}
|
|
965
|
+
const catalogs = catalogResponse.data.result;
|
|
966
|
+
// Get categories if requested
|
|
967
|
+
const catalogsWithDetails = await Promise.all(catalogs.map(async (catalog) => {
|
|
968
|
+
if (args.include_categories) {
|
|
969
|
+
const catResponse = await this.client.searchRecords('sc_category', `sc_catalog=${catalog.sys_id}`, 20);
|
|
970
|
+
const categories = catResponse.success ? catResponse.data.result : [];
|
|
971
|
+
return { ...catalog, categories };
|
|
972
|
+
}
|
|
973
|
+
return catalog;
|
|
974
|
+
}));
|
|
975
|
+
const catalogText = catalogsWithDetails.map((catalog) => {
|
|
976
|
+
let text = `šļø **${catalog.title}** ${catalog.active ? 'ā
' : 'ā'}
|
|
977
|
+
š ${catalog.sys_id}
|
|
978
|
+
š ${catalog.description || 'No description'}`;
|
|
979
|
+
if (catalog.categories) {
|
|
980
|
+
const categoryList = catalog.categories.map((cat) => ` - ${cat.title}`).join('\n');
|
|
981
|
+
text += `\nš Categories:\n${categoryList || ' No categories'}`;
|
|
982
|
+
}
|
|
983
|
+
return text;
|
|
984
|
+
}).join('\n\n');
|
|
985
|
+
return {
|
|
986
|
+
content: [{
|
|
987
|
+
type: 'text',
|
|
988
|
+
text: `š Discovered Service Catalogs:
|
|
989
|
+
|
|
990
|
+
${catalogText}
|
|
991
|
+
|
|
992
|
+
⨠Found ${catalogs.length} catalog(s)`
|
|
993
|
+
}]
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
catch (error) {
|
|
997
|
+
this.logger.error('Failed to discover catalogs:', error);
|
|
998
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover catalogs: ${error}`);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
async run() {
|
|
1002
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
1003
|
+
await this.server.connect(transport);
|
|
1004
|
+
this.logger.info('ServiceNow Knowledge & Catalog MCP Server running on stdio');
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
const server = new ServiceNowKnowledgeCatalogMCP();
|
|
1008
|
+
server.run().catch(console.error);
|
|
1009
|
+
//# sourceMappingURL=servicenow-knowledge-catalog-mcp.js.map
|