snow-flow 3.3.0 → 3.3.2

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.
@@ -55,7 +55,177 @@ class ServiceNowKnowledgeCatalogMCPEnhanced extends enhanced_base_mcp_server_js_
55
55
  required: ['query']
56
56
  }
57
57
  },
58
- // ... other tools omitted for brevity
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
+ }
59
229
  ]
60
230
  }));
61
231
  this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
@@ -68,6 +238,32 @@ class ServiceNowKnowledgeCatalogMCPEnhanced extends enhanced_base_mcp_server_js_
68
238
  return await this.createKnowledgeArticle(args);
69
239
  case 'snow_search_knowledge':
70
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);
71
267
  default:
72
268
  throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
73
269
  }
@@ -189,6 +385,312 @@ ${articleList}
189
385
 
190
386
  ✨ Found ${articles.length} article(s)`);
191
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
+ }
192
694
  async start() {
193
695
  const transport = new stdio_js_1.StdioServerTransport();
194
696
  await this.server.connect(transport);
@@ -41,6 +41,14 @@ export declare abstract class EnhancedBaseMCPServer {
41
41
  * Create record with tracking
42
42
  */
43
43
  protected createRecord(table: string, data: any): Promise<any>;
44
+ /**
45
+ * Update record with tracking
46
+ */
47
+ protected updateRecord(table: string, sysId: string, data: any): Promise<any>;
48
+ /**
49
+ * Get record with tracking
50
+ */
51
+ protected getRecord(table: string, sysId: string): Promise<any>;
44
52
  /**
45
53
  * Get client for direct use
46
54
  */
@@ -36,6 +36,8 @@ class EnhancedBaseMCPServer {
36
36
  * Execute tool with enhanced tracking
37
37
  */
38
38
  async executeTool(toolName, handler) {
39
+ // Reset tokens at start of each operation to avoid accumulation
40
+ this.logger.resetTokens();
39
41
  // Start operation tracking
40
42
  this.logger.operationStart(toolName);
41
43
  try {
@@ -151,6 +153,28 @@ class EnhancedBaseMCPServer {
151
153
  }
152
154
  return result;
153
155
  }
156
+ /**
157
+ * Update record with tracking
158
+ */
159
+ async updateRecord(table, sysId, data) {
160
+ this.logger.progress(`Updating ${table} record ${sysId}...`);
161
+ const result = await this.client.updateRecord(table, sysId, data);
162
+ if (result?.success) {
163
+ this.logger.info(`āœ… Updated ${table} record: ${sysId}`);
164
+ }
165
+ return result;
166
+ }
167
+ /**
168
+ * Get record with tracking
169
+ */
170
+ async getRecord(table, sysId) {
171
+ this.logger.progress(`Getting ${table} record ${sysId}...`);
172
+ const result = await this.client.getRecord(table, sysId);
173
+ if (result?.success) {
174
+ this.logger.info(`āœ… Retrieved ${table} record: ${sysId}`);
175
+ }
176
+ return result;
177
+ }
154
178
  /**
155
179
  * Get client for direct use
156
180
  */
@@ -131,7 +131,7 @@ class MCPLogger {
131
131
  */
132
132
  operationStart(operation, params) {
133
133
  this.startTime = Date.now();
134
- this.info(`šŸš€ Starting: ${operation}`, params);
134
+ this.info(`šŸš€ Starting: ${operation} (tokens reset)`, params);
135
135
  }
136
136
  /**
137
137
  * Log operation complete
@@ -166,6 +166,7 @@ class MCPLogger {
166
166
  */
167
167
  resetTokens() {
168
168
  this.tokenUsage = { input: 0, output: 0, total: 0 };
169
+ console.error(`šŸ”„ [${this.name}] Token counter reset for new operation`);
169
170
  }
170
171
  }
171
172
  exports.MCPLogger = MCPLogger;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "3.3.0",
4
- "description": "Snow-Flow v3.3.0: ServiceNow development platform with 180+ MCP tools. Includes API operation logging and token usage tracking. Supports ATF Testing, Knowledge Management, Service Catalog, Change Management, Virtual Agent, Performance Analytics, Flow Designer, Agent Workspace, Mobile, CMDB/Discovery, Event Management, HR Service Delivery, Customer Service Management, and DevOps integration. All tools use official ServiceNow REST APIs across 17 specialized MCP servers.",
3
+ "version": "3.3.2",
4
+ "description": "Snow-Flow v3.3.2: ServiceNow development platform with 180+ MCP tools. Enhanced MCP servers with real-time progress indicators, per-operation token tracking (tokens reset per call), and comprehensive operation logging. Supports ATF Testing, Knowledge Management, Service Catalog, Change Management, Virtual Agent, Performance Analytics, Flow Designer, Agent Workspace, Mobile, CMDB/Discovery, Event Management, HR Service Delivery, Customer Service Management, and DevOps integration. All tools use official ServiceNow REST APIs across 17 specialized MCP servers with full visibility into API operations.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
7
7
  "bin": {