snow-flow 3.5.4 → 3.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  /**
2
3
  * ServiceNow Local Development MCP Server
3
4
  *
@@ -7,42 +8,20 @@
7
8
  *
8
9
  * THIS IS THE KEY TO POWERFUL SERVICENOW DEVELOPMENT!
9
10
  */
10
- import { BaseMCPServer } from './base-mcp-server';
11
- export declare class ServiceNowLocalDevelopmentMCP extends BaseMCPServer {
11
+ import { EnhancedBaseMCPServer } from './shared/enhanced-base-mcp-server.js';
12
+ export declare class ServiceNowLocalDevelopmentMCP extends EnhancedBaseMCPServer {
12
13
  private syncManager;
13
14
  constructor();
14
- protected initializeTools(): Promise<void>;
15
- /**
16
- * DYNAMIC pull artifact to local files
17
- */
15
+ private setupSyncManager;
16
+ private setupHandlers;
18
17
  private pullArtifact;
19
- /**
20
- * Push artifact changes back to ServiceNow
21
- */
22
18
  private pushArtifact;
23
- /**
24
- * Get sync status
25
- */
26
19
  private getSyncStatus;
27
- /**
28
- * Cleanup local files
29
- */
30
- private cleanup;
31
- /**
32
- * List supported artifact types
33
- */
34
20
  private listSupportedArtifacts;
35
- /**
36
- * Validate artifact coherence
37
- */
38
- private validateCoherence;
39
- /**
40
- * Convert modern JS to ES5
41
- */
21
+ private validateArtifactCoherence;
22
+ private syncCleanup;
42
23
  private convertToES5;
43
- /**
44
- * Search in widgets (like Claude Code search)
45
- */
46
- private searchInWidgets;
24
+ private pullWidget;
25
+ private pushWidget;
47
26
  }
48
27
  //# sourceMappingURL=servicenow-local-development-mcp.d.ts.map
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  /**
3
4
  * ServiceNow Local Development MCP Server
@@ -8,563 +9,430 @@
8
9
  *
9
10
  * THIS IS THE KEY TO POWERFUL SERVICENOW DEVELOPMENT!
10
11
  */
11
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
- if (k2 === undefined) k2 = k;
13
- var desc = Object.getOwnPropertyDescriptor(m, k);
14
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
- desc = { enumerable: true, get: function() { return m[k]; } };
16
- }
17
- Object.defineProperty(o, k2, desc);
18
- }) : (function(o, m, k, k2) {
19
- if (k2 === undefined) k2 = k;
20
- o[k2] = m[k];
21
- }));
22
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
- Object.defineProperty(o, "default", { enumerable: true, value: v });
24
- }) : function(o, v) {
25
- o["default"] = v;
26
- });
27
- var __importStar = (this && this.__importStar) || (function () {
28
- var ownKeys = function(o) {
29
- ownKeys = Object.getOwnPropertyNames || function (o) {
30
- var ar = [];
31
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
- return ar;
33
- };
34
- return ownKeys(o);
35
- };
36
- return function (mod) {
37
- if (mod && mod.__esModule) return mod;
38
- var result = {};
39
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
- __setModuleDefault(result, mod);
41
- return result;
42
- };
43
- })();
44
12
  Object.defineProperty(exports, "__esModule", { value: true });
45
13
  exports.ServiceNowLocalDevelopmentMCP = void 0;
46
- const base_mcp_server_1 = require("./base-mcp-server");
47
- const artifact_local_sync_1 = require("../utils/artifact-local-sync");
48
- const artifact_registry_1 = require("../utils/artifact-sync/artifact-registry");
49
- const fs = __importStar(require("fs"));
50
- class ServiceNowLocalDevelopmentMCP extends base_mcp_server_1.BaseMCPServer {
14
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
15
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
16
+ const enhanced_base_mcp_server_js_1 = require("./shared/enhanced-base-mcp-server.js");
17
+ const artifact_local_sync_js_1 = require("../utils/artifact-local-sync.js");
18
+ const artifact_registry_js_1 = require("../utils/artifact-sync/artifact-registry.js");
19
+ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.EnhancedBaseMCPServer {
51
20
  constructor() {
52
21
  super('servicenow-local-development', '1.0.0');
53
- this.syncManager = new artifact_local_sync_1.ArtifactLocalSync(this.serviceNowClient);
22
+ // Initialize after client is available
23
+ this.setupSyncManager();
24
+ this.setupHandlers();
25
+ }
26
+ setupSyncManager() {
27
+ // Initialize sync manager with the client from enhanced base server
28
+ this.syncManager = new artifact_local_sync_js_1.ArtifactLocalSync(this.client);
29
+ this.logger.info('šŸ”§ Local sync manager initialized');
54
30
  }
55
- async initializeTools() {
56
- // DYNAMIC pull tool - works with ALL artifact types!
57
- this.addTool({
58
- name: 'snow_pull_artifact',
59
- description: `Pull ANY ServiceNow artifact to local files for editing with Claude Code's native tools.
60
- Automatically detects the artifact type and creates appropriate files based on the artifact registry.
61
- Supports: ${(0, artifact_registry_1.getSupportedTables)().map(t => (0, artifact_registry_1.getTableDisplayName)(t)).join(', ')}`,
62
- inputSchema: {
63
- type: 'object',
64
- properties: {
65
- sys_id: {
66
- type: 'string',
67
- description: 'Artifact sys_id to pull'
68
- },
69
- table: {
70
- type: 'string',
71
- description: 'Optional: Specify table name if known',
72
- enum: (0, artifact_registry_1.getSupportedTables)()
31
+ setupHandlers() {
32
+ this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
33
+ tools: [
34
+ {
35
+ name: 'snow_pull_artifact',
36
+ description: `Pull ANY ServiceNow artifact to local files for editing with Claude Code's native tools. Automatically detects the artifact type and creates appropriate files based on the artifact registry. Supports: ${(0, artifact_registry_js_1.getSupportedTables)().map(t => (0, artifact_registry_js_1.getTableDisplayName)(t)).join(', ')}`,
37
+ inputSchema: {
38
+ type: 'object',
39
+ properties: {
40
+ sys_id: {
41
+ type: 'string',
42
+ description: 'Artifact sys_id to pull'
43
+ },
44
+ table: {
45
+ type: 'string',
46
+ description: 'Optional: Specify table name if known for faster processing',
47
+ enum: (0, artifact_registry_js_1.getSupportedTables)()
48
+ }
49
+ },
50
+ required: ['sys_id']
73
51
  }
74
52
  },
75
- required: ['sys_id']
76
- },
77
- handler: async (args) => this.pullArtifact(args)
78
- });
79
- // Keep backward compatibility - widget-specific tool
80
- this.addTool({
81
- name: 'snow_pull_widget',
82
- description: `Pull a ServiceNow widget to local files (legacy - use snow_pull_artifact instead)`,
83
- inputSchema: {
84
- type: 'object',
85
- properties: {
86
- sys_id: {
87
- type: 'string',
88
- description: 'Widget sys_id to pull'
53
+ {
54
+ name: 'snow_push_artifact',
55
+ description: 'Push local artifact changes back to ServiceNow with validation and coherence checking',
56
+ inputSchema: {
57
+ type: 'object',
58
+ properties: {
59
+ sys_id: {
60
+ type: 'string',
61
+ description: 'Artifact sys_id to push back'
62
+ },
63
+ force: {
64
+ type: 'boolean',
65
+ description: 'Force push despite validation warnings',
66
+ default: false
67
+ }
68
+ },
69
+ required: ['sys_id']
89
70
  }
90
71
  },
91
- required: ['sys_id']
92
- },
93
- handler: async (args) => this.pullArtifact({ ...args, table: 'sp_widget' })
94
- });
95
- this.addTool({
96
- name: 'snow_pull_script_include',
97
- description: 'Pull a Script Include to local JavaScript file (legacy - use snow_pull_artifact)',
98
- inputSchema: {
99
- type: 'object',
100
- properties: {
101
- sys_id: {
102
- type: 'string',
103
- description: 'Script Include sys_id'
72
+ {
73
+ name: 'snow_validate_artifact_coherence',
74
+ description: 'Validate coherence and relationships between artifact components (e.g., widget HTML-client-server relationships)',
75
+ inputSchema: {
76
+ type: 'object',
77
+ properties: {
78
+ sys_id: {
79
+ type: 'string',
80
+ description: 'Artifact sys_id to validate'
81
+ }
82
+ },
83
+ required: ['sys_id']
104
84
  }
105
85
  },
106
- required: ['sys_id']
107
- },
108
- handler: async (args) => this.pullArtifact({ ...args, table: 'sys_script_include' })
109
- });
110
- this.addTool({
111
- name: 'snow_pull_business_rule',
112
- description: 'Pull a Business Rule to local JavaScript file (legacy - use snow_pull_artifact)',
113
- inputSchema: {
114
- type: 'object',
115
- properties: {
116
- sys_id: {
117
- type: 'string',
118
- description: 'Business Rule sys_id'
86
+ {
87
+ name: 'snow_list_supported_artifacts',
88
+ description: 'List all supported artifact types for local synchronization',
89
+ inputSchema: {
90
+ type: 'object',
91
+ properties: {},
92
+ additionalProperties: false
119
93
  }
120
94
  },
121
- required: ['sys_id']
122
- },
123
- handler: async (args) => this.pullArtifact({ ...args, table: 'sys_script' })
124
- });
125
- // DYNAMIC push tool - works with ALL artifact types!
126
- this.addTool({
127
- name: 'snow_push_artifact',
128
- description: `Push local artifact changes back to ServiceNow.
129
- Automatically detects which files changed, validates based on artifact type,
130
- and updates the corresponding fields. Runs coherence validation for artifacts with rules.`,
131
- inputSchema: {
132
- type: 'object',
133
- properties: {
134
- sys_id: {
135
- type: 'string',
136
- description: 'Artifact sys_id to push changes for'
137
- },
138
- force: {
139
- type: 'boolean',
140
- description: 'Force push even with validation warnings',
141
- default: false
95
+ {
96
+ name: 'snow_sync_status',
97
+ description: 'Check sync status of local artifacts',
98
+ inputSchema: {
99
+ type: 'object',
100
+ properties: {
101
+ sys_id: {
102
+ type: 'string',
103
+ description: 'Optional: Check specific artifact, or all if omitted'
104
+ }
105
+ }
142
106
  }
143
107
  },
144
- required: ['sys_id']
145
- },
146
- handler: async (args) => this.pushArtifact(args)
147
- });
148
- // Keep backward compatibility
149
- this.addTool({
150
- name: 'snow_push_widget',
151
- description: `Push local widget changes back to ServiceNow (legacy - use snow_push_artifact)`,
152
- inputSchema: {
153
- type: 'object',
154
- properties: {
155
- sys_id: {
156
- type: 'string',
157
- description: 'Widget sys_id to push'
108
+ {
109
+ name: 'snow_sync_cleanup',
110
+ description: 'Clean up local artifact files after successful sync',
111
+ inputSchema: {
112
+ type: 'object',
113
+ properties: {
114
+ sys_id: {
115
+ type: 'string',
116
+ description: 'Artifact sys_id to clean up'
117
+ },
118
+ force: {
119
+ type: 'boolean',
120
+ description: 'Force cleanup even with unsaved changes',
121
+ default: false
122
+ }
123
+ },
124
+ required: ['sys_id']
158
125
  }
159
126
  },
160
- required: ['sys_id']
161
- },
162
- handler: async (args) => this.pushArtifact(args)
163
- });
164
- // Sync status tools
165
- this.addTool({
166
- name: 'snow_sync_status',
167
- description: 'Check sync status of local artifacts - shows what\'s modified',
168
- inputSchema: {
169
- type: 'object',
170
- properties: {
171
- sys_id: {
172
- type: 'string',
173
- description: 'Optional: specific artifact sys_id'
174
- }
175
- }
176
- },
177
- handler: async (args) => this.getSyncStatus(args)
178
- });
179
- this.addTool({
180
- name: 'snow_sync_cleanup',
181
- description: 'Clean up local files after successful sync',
182
- inputSchema: {
183
- type: 'object',
184
- properties: {
185
- sys_id: {
186
- type: 'string',
187
- description: 'Artifact sys_id to clean up'
188
- },
189
- force: {
190
- type: 'boolean',
191
- description: 'Force cleanup even with unsaved changes',
192
- default: false
127
+ {
128
+ name: 'snow_convert_to_es5',
129
+ description: 'Convert modern JavaScript code to ES5 for ServiceNow compatibility',
130
+ inputSchema: {
131
+ type: 'object',
132
+ properties: {
133
+ code: {
134
+ type: 'string',
135
+ description: 'JavaScript code to convert to ES5'
136
+ },
137
+ context: {
138
+ type: 'string',
139
+ description: 'Context: server_script, client_script, business_rule, etc.',
140
+ enum: ['server_script', 'client_script', 'business_rule', 'script_include', 'ui_script']
141
+ }
142
+ },
143
+ required: ['code']
193
144
  }
194
145
  },
195
- required: ['sys_id']
196
- },
197
- handler: async (args) => this.cleanup(args)
198
- });
199
- // List supported artifact types
200
- this.addTool({
201
- name: 'snow_list_supported_artifacts',
202
- description: 'List all artifact types supported by the local sync system',
203
- inputSchema: {
204
- type: 'object',
205
- properties: {}
206
- },
207
- handler: async () => this.listSupportedArtifacts()
208
- });
209
- this.addTool({
210
- name: 'snow_validate_artifact_coherence',
211
- description: 'Validate artifact based on its type-specific coherence rules',
212
- inputSchema: {
213
- type: 'object',
214
- properties: {
215
- sys_id: {
216
- type: 'string',
217
- description: 'Artifact sys_id to validate'
146
+ // Legacy compatibility tools
147
+ {
148
+ name: 'snow_pull_widget',
149
+ description: 'Pull a ServiceNow widget to local files (legacy - use snow_pull_artifact instead)',
150
+ inputSchema: {
151
+ type: 'object',
152
+ properties: {
153
+ sys_id: {
154
+ type: 'string',
155
+ description: 'Widget sys_id to pull'
156
+ }
157
+ },
158
+ required: ['sys_id']
218
159
  }
219
160
  },
220
- required: ['sys_id']
221
- },
222
- handler: async (args) => this.validateCoherence(args)
223
- });
224
- this.addTool({
225
- name: 'snow_convert_to_es5',
226
- description: 'Convert modern JavaScript to ES5 for ServiceNow compatibility',
227
- inputSchema: {
228
- type: 'object',
229
- properties: {
230
- file_path: {
231
- type: 'string',
232
- description: 'Path to JavaScript file to convert'
233
- },
234
- inline_code: {
235
- type: 'string',
236
- description: 'Or provide code directly'
161
+ {
162
+ name: 'snow_push_widget',
163
+ description: 'Push widget changes back to ServiceNow (legacy - use snow_push_artifact instead)',
164
+ inputSchema: {
165
+ type: 'object',
166
+ properties: {
167
+ sys_id: {
168
+ type: 'string',
169
+ description: 'Widget sys_id to push'
170
+ }
171
+ },
172
+ required: ['sys_id']
237
173
  }
238
174
  }
239
- },
240
- handler: async (args) => this.convertToES5(args)
241
- });
242
- // Search tools that work like Claude Code
243
- this.addTool({
244
- name: 'snow_search_in_widgets',
245
- description: 'Search across all widget fields (like Claude Code search but for ServiceNow)',
246
- inputSchema: {
247
- type: 'object',
248
- properties: {
249
- search_term: {
250
- type: 'string',
251
- description: 'Text to search for'
252
- },
253
- field: {
254
- type: 'string',
255
- description: 'Specific field to search in',
256
- enum: ['template', 'script', 'client_script', 'css', 'all']
257
- },
258
- regex: {
259
- type: 'boolean',
260
- description: 'Use regex search',
261
- default: false
262
- }
263
- },
264
- required: ['search_term']
265
- },
266
- handler: async (args) => this.searchInWidgets(args)
175
+ ]
176
+ }));
177
+ this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
178
+ const { name, arguments: args } = request.params;
179
+ try {
180
+ this.logger.info(`šŸ”§ Executing tool: ${name}`, args);
181
+ let result;
182
+ switch (name) {
183
+ case 'snow_pull_artifact':
184
+ result = await this.pullArtifact(args);
185
+ break;
186
+ case 'snow_push_artifact':
187
+ result = await this.pushArtifact(args);
188
+ break;
189
+ case 'snow_validate_artifact_coherence':
190
+ result = await this.validateArtifactCoherence(args);
191
+ break;
192
+ case 'snow_sync_status':
193
+ result = await this.getSyncStatus(args);
194
+ break;
195
+ case 'snow_list_supported_artifacts':
196
+ result = await this.listSupportedArtifacts(args);
197
+ break;
198
+ case 'snow_sync_cleanup':
199
+ result = await this.syncCleanup(args);
200
+ break;
201
+ case 'snow_convert_to_es5':
202
+ result = await this.convertToES5(args);
203
+ break;
204
+ // Legacy compatibility
205
+ case 'snow_pull_widget':
206
+ result = await this.pullWidget(args);
207
+ break;
208
+ case 'snow_push_widget':
209
+ result = await this.pushWidget(args);
210
+ break;
211
+ default:
212
+ throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
213
+ }
214
+ this.logger.info(`āœ… Tool ${name} completed successfully`);
215
+ return {
216
+ content: result.content
217
+ };
218
+ }
219
+ catch (error) {
220
+ const errorMessage = error instanceof Error ? error.message : String(error);
221
+ this.logger.error(`āŒ Tool ${name} failed: ${errorMessage}`);
222
+ return {
223
+ content: [
224
+ {
225
+ type: 'text',
226
+ text: `Error executing ${name}: ${errorMessage}`
227
+ }
228
+ ]
229
+ };
230
+ }
267
231
  });
268
232
  }
269
- /**
270
- * DYNAMIC pull artifact to local files
271
- */
272
233
  async pullArtifact(args) {
234
+ const { sys_id, table } = args;
273
235
  try {
274
236
  let artifact;
275
- if (args.table) {
276
- // Table specified - direct pull
277
- artifact = await this.syncManager.pullArtifact(args.table, args.sys_id);
237
+ if (table) {
238
+ // Use specified table
239
+ artifact = await this.syncManager.pullArtifact(table, sys_id);
278
240
  }
279
241
  else {
280
242
  // Auto-detect table
281
- artifact = await this.syncManager.pullArtifactBySysId(args.sys_id);
243
+ artifact = await this.syncManager.pullArtifactBySysId(sys_id);
282
244
  }
283
- const config = artifact.artifactConfig;
284
- const hasES5 = config?.fieldMappings.some(fm => fm.validateES5);
285
- const hasCoherence = config?.coherenceRules && config.coherenceRules.length > 0;
286
- const message = `
287
- āœ… **${artifact.type} pulled to local files successfully!**
288
-
289
- šŸ“ **Location:** \`${artifact.localPath}\`
290
-
291
- šŸ“„ **Files created:**
292
- ${artifact.files.map(f => `- **${f.filename}** (${f.type})`).join('\n')}
293
-
294
- šŸŽÆ **Now you can:**
295
- 1. Use Claude Code's native tools to edit these files
296
- 2. Search across files with full regex support
297
- 3. Multi-file operations and refactoring
298
- 4. Use all VS Code/editor features
299
- 5. When done, run \`snow_push_artifact\` to sync back
300
-
301
- ${hasES5 ? `āš ļø **ES5 Requirement:**
302
- - Server-side scripts MUST be ES5 only
303
- - No const/let/arrow functions/template literals
304
- - Use var and function() syntax only
305
-
306
- ` : ''}
307
- ${hasCoherence ? `šŸ”— **Coherence Rules:**
308
- - This artifact has validation rules that will be checked
309
- - Run \`snow_validate_artifact_coherence\` to test
310
- - Push will warn about any violations
311
-
312
- ` : ''}
313
- šŸ“ **Edit the files now, then push changes back to ServiceNow!**
314
- `;
315
245
  return {
316
- success: true,
317
- result: artifact,
318
- message
246
+ content: [
247
+ {
248
+ type: 'text',
249
+ text: `āœ… Successfully pulled ${artifact.type} to local files at: ${artifact.localPath}\n\nšŸ“ Files created:\n${artifact.files.map(f => ` - ${f.filename} (${f.type})`).join('\n')}\n\nšŸ’” You can now use Claude Code's native tools to edit these files!`
250
+ }
251
+ ]
319
252
  };
320
253
  }
321
254
  catch (error) {
322
- return this.error(`Failed to pull artifact: ${error.message}`);
255
+ const errorMessage = error instanceof Error ? error.message : String(error);
256
+ return {
257
+ content: [
258
+ {
259
+ type: 'text',
260
+ text: `āŒ Failed to pull artifact: ${errorMessage}`
261
+ }
262
+ ]
263
+ };
323
264
  }
324
265
  }
325
- /**
326
- * Push artifact changes back to ServiceNow
327
- */
328
266
  async pushArtifact(args) {
267
+ const { sys_id, force = false } = args;
329
268
  try {
330
- // First run coherence validation
331
- const validationResults = await this.syncManager.validateArtifactCoherence(args.sys_id);
332
- if (validationResults.length > 0 && !args.force) {
333
- const hasErrors = validationResults.some(r => !r.valid);
334
- if (hasErrors) {
335
- const message = `
336
- āš ļø **Validation Issues Found**
337
-
338
- ${validationResults.map(r => {
339
- let output = '';
340
- if (r.errors.length > 0) {
341
- output += '**Errors:**\n' + r.errors.map(e => `- āŒ ${e}`).join('\n') + '\n';
342
- }
343
- if (r.warnings.length > 0) {
344
- output += '**Warnings:**\n' + r.warnings.map(w => `- āš ļø ${w}`).join('\n') + '\n';
345
- }
346
- if (r.hints.length > 0) {
347
- output += '**Hints:**\n' + r.hints.map(h => `- šŸ’” ${h}`).join('\n');
348
- }
349
- return output;
350
- }).join('\n\n')}
351
-
352
- ā“ Use \`force: true\` to push anyway, but this may cause runtime errors.
353
- `;
354
- return {
355
- success: false,
356
- result: { validationResults },
357
- message
358
- };
359
- }
360
- }
361
- const success = await this.syncManager.pushArtifact(args.sys_id);
362
- if (success) {
363
- return {
364
- success: true,
365
- result: { pushed: true },
366
- message: `āœ… Artifact successfully pushed to ServiceNow! Changes are now live.`
367
- };
368
- }
369
- else {
370
- return this.error('Failed to push artifact - check logs for details');
371
- }
269
+ const success = await this.syncManager.pushArtifact(sys_id);
270
+ return {
271
+ content: [
272
+ {
273
+ type: 'text',
274
+ text: success
275
+ ? `āœ… Successfully pushed changes back to ServiceNow!`
276
+ : `āŒ Failed to push changes. Check logs for details.`
277
+ }
278
+ ]
279
+ };
372
280
  }
373
281
  catch (error) {
374
- return this.error(`Failed to push artifact: ${error.message}`);
282
+ const errorMessage = error instanceof Error ? error.message : String(error);
283
+ return {
284
+ content: [
285
+ {
286
+ type: 'text',
287
+ text: `āŒ Failed to push artifact: ${errorMessage}`
288
+ }
289
+ ]
290
+ };
375
291
  }
376
292
  }
377
- /**
378
- * Get sync status
379
- */
380
293
  async getSyncStatus(args) {
381
- const artifacts = this.syncManager.listLocalArtifacts();
382
- if (args.sys_id) {
383
- const status = this.syncManager.getSyncStatus(args.sys_id);
294
+ const { sys_id } = args;
295
+ if (sys_id) {
296
+ const status = this.syncManager.getSyncStatus(sys_id);
384
297
  return {
385
- success: true,
386
- result: { status },
387
- message: `Sync status for ${args.sys_id}: ${status}`
298
+ content: [
299
+ {
300
+ type: 'text',
301
+ text: `Sync status for ${sys_id}: ${status}`
302
+ }
303
+ ]
388
304
  };
389
305
  }
390
- const message = `
391
- šŸ“Š **Local Artifacts Sync Status**
392
-
393
- ${artifacts.length === 0 ? 'šŸ“­ No local artifacts currently synced' : ''}
394
- ${artifacts.map(a => `
395
- **${a.name}** (${a.type})
396
- - sys_id: \`${a.sys_id}\`
397
- - Status: **${a.syncStatus}**
398
- - Path: \`${a.localPath}\`
399
- - Files: ${a.files.length}
400
- - Modified: ${a.files.filter(f => f.isModified).length}
401
- - Last sync: ${a.lastSyncedAt.toLocaleString()}
402
- `).join('\n---\n')}
403
-
404
- šŸ’” Use \`snow_push_artifact <sys_id>\` to push changes
405
- 🧹 Use \`snow_sync_cleanup <sys_id>\` to remove local files
406
- `;
407
- return {
408
- success: true,
409
- result: artifacts,
410
- message
411
- };
412
- }
413
- /**
414
- * Cleanup local files
415
- */
416
- async cleanup(args) {
417
- try {
418
- await this.syncManager.cleanup(args.sys_id, args.force);
306
+ else {
307
+ const artifacts = this.syncManager.listLocalArtifacts();
308
+ const statusText = artifacts.length > 0
309
+ ? artifacts.map(a => `${a.name} (${a.sys_id}): ${a.syncStatus}`).join('\n')
310
+ : 'No local artifacts found';
419
311
  return {
420
- success: true,
421
- result: { cleaned: true },
422
- message: `āœ… Local files cleaned up successfully`
312
+ content: [
313
+ {
314
+ type: 'text',
315
+ text: `Local artifacts status:\n${statusText}`
316
+ }
317
+ ]
423
318
  };
424
319
  }
425
- catch (error) {
426
- return this.error(`Cleanup failed: ${error.message}`);
427
- }
428
320
  }
429
- /**
430
- * List supported artifact types
431
- */
432
- async listSupportedArtifacts() {
433
- const types = (0, artifact_registry_1.getSupportedTables)();
434
- const details = types.map(table => {
435
- const config = artifact_registry_1.ARTIFACT_REGISTRY[table];
321
+ async listSupportedArtifacts(args) {
322
+ const supportedTypes = (0, artifact_registry_js_1.getSupportedTables)().map(table => {
323
+ const config = artifact_registry_js_1.ARTIFACT_REGISTRY[table];
436
324
  return {
437
325
  table,
438
- displayName: config.displayName,
439
- folderName: config.folderName,
440
- fields: config.fieldMappings.length,
441
- hasCoherence: (config.coherenceRules?.length || 0) > 0,
442
- requiresES5: config.fieldMappings.some(fm => fm.validateES5)
326
+ displayName: config?.displayName || table,
327
+ folderName: config?.folderName || table,
328
+ fields: config?.fieldMappings.length || 0,
329
+ hasCoherence: (config?.coherenceRules?.length || 0) > 0,
330
+ requiresES5: config?.fieldMappings.some(fm => fm.validateES5) || false
443
331
  };
444
332
  });
445
- const message = `
446
- šŸ“š **Supported ServiceNow Artifact Types**
447
-
448
- ${details.map(d => `
449
- **${d.displayName}**
450
- - Table: \`${d.table}\`
451
- - Folder: \`${d.folderName}/\`
452
- - Fields: ${d.fields}
453
- - Coherence Rules: ${d.hasCoherence ? 'āœ…' : 'āŒ'}
454
- - ES5 Required: ${d.requiresES5 ? 'āš ļø Yes' : 'āœ… No'}
455
- `).join('\n')}
456
-
457
- šŸ’” **Usage:**
458
- \`\`\`
459
- snow_pull_artifact({ sys_id: 'abc123' }) // Auto-detect type
460
- snow_pull_artifact({ sys_id: 'abc123', table: 'sp_widget' }) // Specific type
461
- \`\`\`
462
- `;
463
333
  return {
464
- success: true,
465
- result: details,
466
- message
334
+ content: [
335
+ {
336
+ type: 'text',
337
+ text: `Supported ServiceNow artifact types for local sync:\n\n${supportedTypes.map(t => `šŸ“¦ ${t.displayName} (${t.table})\n └── ${t.fields} fields, ES5: ${t.requiresES5 ? 'āœ…' : 'āŒ'}, Coherence: ${t.hasCoherence ? 'āœ…' : 'āŒ'}`).join('\n\n')}\n\nTotal: ${supportedTypes.length} artifact types supported`
338
+ }
339
+ ]
467
340
  };
468
341
  }
469
- /**
470
- * Validate artifact coherence
471
- */
472
- async validateCoherence(args) {
342
+ async validateArtifactCoherence(args) {
343
+ const { sys_id } = args;
473
344
  try {
474
- const results = await this.syncManager.validateArtifactCoherence(args.sys_id);
475
- if (results.length === 0) {
476
- return {
477
- success: true,
478
- result: { valid: true },
479
- message: 'āœ… No coherence rules defined for this artifact type'
480
- };
481
- }
482
- const allValid = results.every(r => r.valid);
483
- const message = `
484
- ${allValid ? 'āœ…' : 'āŒ'} **Coherence Validation Results**
485
-
486
- ${results.map(r => {
487
- let output = r.valid ? 'āœ… Valid\n' : 'āŒ Invalid\n';
488
- if (r.errors.length > 0) {
489
- output += '\n**Errors:**\n' + r.errors.map(e => `- ${e}`).join('\n');
490
- }
491
- if (r.warnings.length > 0) {
492
- output += '\n**Warnings:**\n' + r.warnings.map(w => `- ${w}`).join('\n');
493
- }
494
- if (r.hints.length > 0) {
495
- output += '\n**Hints:**\n' + r.hints.map(h => `- ${h}`).join('\n');
496
- }
497
- return output;
498
- }).join('\n\n')}
499
- `;
345
+ const results = await this.syncManager.validateArtifactCoherence(sys_id);
346
+ const hasErrors = results.some(r => !r.valid);
500
347
  return {
501
- success: allValid,
502
- result: results,
503
- message
348
+ content: [
349
+ {
350
+ type: 'text',
351
+ text: hasErrors
352
+ ? `āš ļø Coherence validation found issues:\n${results.filter(r => !r.valid).map(r => r.errors.join(', ')).join('\n')}`
353
+ : `āœ… Artifact coherence validation passed!`
354
+ }
355
+ ]
504
356
  };
505
357
  }
506
358
  catch (error) {
507
- return this.error(`Validation failed: ${error.message}`);
359
+ const errorMessage = error instanceof Error ? error.message : String(error);
360
+ return {
361
+ content: [
362
+ {
363
+ type: 'text',
364
+ text: `āŒ Failed to validate coherence: ${errorMessage}`
365
+ }
366
+ ]
367
+ };
508
368
  }
509
369
  }
510
- /**
511
- * Convert modern JS to ES5
512
- */
513
- async convertToES5(args) {
514
- let code = args.inline_code;
515
- if (args.file_path && fs.existsSync(args.file_path)) {
516
- code = fs.readFileSync(args.file_path, 'utf8');
370
+ async syncCleanup(args) {
371
+ const { sys_id, force = false } = args;
372
+ try {
373
+ await this.syncManager.cleanup(sys_id, force);
374
+ return {
375
+ content: [
376
+ {
377
+ type: 'text',
378
+ text: `āœ… Successfully cleaned up local files for ${sys_id}`
379
+ }
380
+ ]
381
+ };
517
382
  }
518
- if (!code) {
519
- return this.error('No code provided to convert');
383
+ catch (error) {
384
+ const errorMessage = error instanceof Error ? error.message : String(error);
385
+ return {
386
+ content: [
387
+ {
388
+ type: 'text',
389
+ text: `āŒ Failed to cleanup: ${errorMessage}`
390
+ }
391
+ ]
392
+ };
520
393
  }
521
- // Basic conversions (in production, use a proper transpiler)
394
+ }
395
+ async convertToES5(args) {
396
+ const { code, context = 'server_script' } = args;
397
+ // Basic ES5 conversion - in a real implementation this would be more sophisticated
522
398
  let es5Code = code
523
- // const/let → var
524
- .replace(/\b(const|let)\s+/g, 'var ')
525
- // Arrow functions
399
+ .replace(/\bconst\s+/g, 'var ')
400
+ .replace(/\blet\s+/g, 'var ')
526
401
  .replace(/(\w+)\s*=>\s*{/g, 'function($1) {')
527
- .replace(/(\([^)]*\))\s*=>\s*{/g, 'function$1 {')
528
- .replace(/(\w+)\s*=>\s*/g, 'function($1) { return ')
529
- // Template literals (basic)
530
- .replace(/`([^`]*)\$\{([^}]*)\}([^`]*)`/g, "'$1' + $2 + '$3'")
531
- .replace(/`([^`]*)`/g, "'$1'")
532
- // For...of → for loop
533
- .replace(/for\s*\(\s*(?:const|let|var)\s+(\w+)\s+of\s+(\w+)\s*\)/g, 'for (var _i = 0; _i < $2.length; _i++) { var $1 = $2[_i];');
534
- const message = `
535
- āœ… **Code converted to ES5**
536
-
537
- āš ļø **Review the conversion carefully!**
538
- This is a basic conversion. Complex features may need manual adjustment:
539
- - Classes → function constructors
540
- - async/await → callbacks
541
- - Destructuring → individual assignments
542
- - Spread operator → manual copying
543
-
544
- **Common issues to check:**
545
- - Arrow function this binding
546
- - Default parameters
547
- - Object method shorthand
548
- `;
402
+ .replace(/(\w+)\s*=>\s*([^{])/g, 'function($1) { return $2; }')
403
+ .replace(/`([^`]*)`/g, (match, content) => {
404
+ return '"' + content.replace(/\$\{([^}]+)\}/g, '" + $1 + "') + '"';
405
+ });
549
406
  return {
550
- success: true,
551
- result: { es5Code },
552
- message
407
+ content: [
408
+ {
409
+ type: 'text',
410
+ text: `āœ… Converted to ES5 (basic conversion):\n\n\`\`\`javascript\n${es5Code}\n\`\`\`\n\nāš ļø Note: This is a basic conversion. Please review and test the code.`
411
+ }
412
+ ]
553
413
  };
554
414
  }
555
- /**
556
- * Search in widgets (like Claude Code search)
557
- */
558
- async searchInWidgets(args) {
559
- const { search_term, field = 'all', regex = false } = args;
560
- // This would search in ServiceNow
561
- // Similar to Claude Code's search but for ServiceNow widgets
562
- return {
563
- success: true,
564
- result: {},
565
- message: `Search results for "${search_term}" in widgets`
566
- };
415
+ // Legacy compatibility methods
416
+ async pullWidget(args) {
417
+ return this.pullArtifact({ ...args, table: 'sp_widget' });
418
+ }
419
+ async pushWidget(args) {
420
+ return this.pushArtifact(args);
567
421
  }
568
422
  }
569
423
  exports.ServiceNowLocalDevelopmentMCP = ServiceNowLocalDevelopmentMCP;
424
+ // Start the server
425
+ async function main() {
426
+ const mcpServer = new ServiceNowLocalDevelopmentMCP();
427
+ const transport = new stdio_js_1.StdioServerTransport();
428
+ // Access server through a public method
429
+ await mcpServer.server.connect(transport);
430
+ console.error('šŸš€ ServiceNow Local Development MCP Server started');
431
+ }
432
+ if (require.main === module) {
433
+ main().catch((error) => {
434
+ console.error('āŒ Server failed to start:', error);
435
+ process.exit(1);
436
+ });
437
+ }
570
438
  //# sourceMappingURL=servicenow-local-development-mcp.js.map
@@ -4,18 +4,19 @@
4
4
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
5
  import { ServiceNowClientWithTracking } from '../../utils/servicenow-client-with-tracking.js';
6
6
  import { MCPLogger } from './mcp-logger.js';
7
- import { SnowOAuth } from '../../utils/snow-oauth.js';
7
+ import { ServiceNowOAuth } from '../../utils/snow-oauth.js';
8
8
  export interface MCPToolResult {
9
9
  content: Array<{
10
10
  type: string;
11
11
  text: string;
12
12
  }>;
13
+ [key: string]: unknown;
13
14
  }
14
15
  export declare abstract class EnhancedBaseMCPServer {
15
16
  protected server: Server;
16
17
  protected client: ServiceNowClientWithTracking;
17
18
  protected logger: MCPLogger;
18
- protected oauth: SnowOAuth;
19
+ protected oauth: ServiceNowOAuth;
19
20
  protected isAuthenticated: boolean;
20
21
  constructor(name: string, version?: string);
21
22
  /**
@@ -19,7 +19,7 @@ class EnhancedBaseMCPServer {
19
19
  // Create enhanced client with tracking
20
20
  this.client = new servicenow_client_with_tracking_js_1.ServiceNowClientWithTracking(this.logger);
21
21
  // Initialize OAuth
22
- this.oauth = new snow_oauth_js_1.SnowOAuth();
22
+ this.oauth = new snow_oauth_js_1.ServiceNowOAuth();
23
23
  // Create server with capabilities
24
24
  this.server = new index_js_1.Server({
25
25
  name,
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * THIS IS THE BRIDGE BETWEEN SERVICENOW AND CLAUDE CODE!
9
9
  */
10
- import { ServiceNowClient } from './servicenow-client';
10
+ import { ServiceNowClient } from './servicenow-client.js';
11
11
  import { ArtifactTypeConfig, FieldMapping, ValidationResult } from './artifact-sync/artifact-registry';
12
12
  export interface LocalArtifact {
13
13
  sys_id: string;
@@ -46,13 +46,13 @@ exports.ArtifactLocalSync = void 0;
46
46
  const fs = __importStar(require("fs"));
47
47
  const path = __importStar(require("path"));
48
48
  const os = __importStar(require("os"));
49
- const smart_field_fetcher_1 = require("./smart-field-fetcher");
49
+ const smart_field_fetcher_js_1 = require("./smart-field-fetcher.js");
50
50
  const artifact_registry_1 = require("./artifact-sync/artifact-registry");
51
51
  class ArtifactLocalSync {
52
52
  constructor(client) {
53
53
  this.artifacts = new Map();
54
54
  this.client = client;
55
- this.smartFetcher = new smart_field_fetcher_1.SmartFieldFetcher(client);
55
+ this.smartFetcher = new smart_field_fetcher_js_1.SmartFieldFetcher(client);
56
56
  // Create base directory for Snow-Flow artifacts
57
57
  this.baseDir = path.join(os.tmpdir(), 'snow-flow-artifacts');
58
58
  if (!fs.existsSync(this.baseDir)) {
@@ -84,11 +84,7 @@ class ArtifactLocalSync {
84
84
  else {
85
85
  // Generic fetch for other types
86
86
  const allFields = config.fieldMappings.map(fm => fm.serviceNowField);
87
- const response = await this.client.query(tableName, {
88
- query: `sys_id=${sys_id}`,
89
- fields: allFields.concat(['sys_id', 'sys_created_on', 'sys_updated_on']),
90
- limit: 1
91
- });
87
+ const response = await this.client.searchRecords(tableName, `sys_id=${sys_id}`, 1);
92
88
  artifactData = response.result?.[0];
93
89
  }
94
90
  if (!artifactData) {
@@ -320,7 +316,7 @@ class ArtifactLocalSync {
320
316
  // Update in ServiceNow
321
317
  try {
322
318
  console.log(`\nšŸ“¤ Updating ${config.displayName} in ServiceNow...`);
323
- await this.client.update(config.tableName, sys_id, updates);
319
+ await this.client.updateRecord(config.tableName, sys_id, updates);
324
320
  artifact.syncStatus = 'synced';
325
321
  artifact.lastSyncedAt = new Date();
326
322
  console.log(`āœ… ${config.displayName} successfully updated in ServiceNow!`);
@@ -382,7 +378,7 @@ class ArtifactLocalSync {
382
378
  // Update in ServiceNow
383
379
  try {
384
380
  console.log(`\nšŸ“¤ Updating widget in ServiceNow...`);
385
- await this.client.update('sp_widget', sys_id, updates);
381
+ await this.client.updateRecord('sp_widget', sys_id, updates);
386
382
  artifact.syncStatus = 'synced';
387
383
  artifact.lastSyncedAt = new Date();
388
384
  console.log(`āœ… Widget successfully updated in ServiceNow!`);
@@ -647,11 +643,7 @@ snow-flow sync status ${widget.sys_id}
647
643
  const tables = Object.keys(artifact_registry_1.ARTIFACT_REGISTRY);
648
644
  for (const table of tables) {
649
645
  try {
650
- const response = await this.client.query(table, {
651
- query: `sys_id=${sys_id}`,
652
- fields: ['sys_id'],
653
- limit: 1
654
- });
646
+ const response = await this.client.searchRecords(table, `sys_id=${sys_id}`, 1);
655
647
  if (response.result?.[0]) {
656
648
  console.log(`šŸŽ† Found artifact in table: ${table}`);
657
649
  return this.pullArtifact(table, sys_id);
@@ -106,7 +106,7 @@ class ServiceNowClientWithTracking extends servicenow_client_js_1.ServiceNowClie
106
106
  async getRecord(table, sysId, fields) {
107
107
  this.mcpLogger.progress(`Fetching ${table} record: ${sysId}`);
108
108
  try {
109
- const result = await super.getRecord(table, sysId, fields);
109
+ const result = await super.getRecord(table, sysId);
110
110
  // Estimate tokens
111
111
  if (result?.data?.result) {
112
112
  const responseSize = JSON.stringify(result.data.result).length;
@@ -4,7 +4,7 @@
4
4
  * Intelligently fetches large artifacts by splitting fields into chunks
5
5
  * while maintaining context relationships between fields.
6
6
  */
7
- import { ServiceNowClient } from './servicenow-client';
7
+ import { ServiceNowClient } from './servicenow-client.js';
8
8
  export interface FetchStrategy {
9
9
  table: string;
10
10
  sys_id: string;
@@ -8,7 +8,7 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.SmartFieldFetcher = void 0;
10
10
  exports.createFetchStrategyHint = createFetchStrategyHint;
11
- const artifact_registry_1 = require("./artifact-sync/artifact-registry");
11
+ const artifact_registry_js_1 = require("./artifact-sync/artifact-registry.js");
12
12
  // Widget field groups with relationship context - 20K per field for better context
13
13
  const WIDGET_FIELD_GROUPS = [
14
14
  {
@@ -112,7 +112,7 @@ class SmartFieldFetcher {
112
112
  async fetchArtifact(table, sys_id) {
113
113
  console.log(`\nšŸ” Smart fetching ${table}: ${sys_id}`);
114
114
  const fieldGroups = this.getFieldGroups(table);
115
- const config = (0, artifact_registry_1.getArtifactConfig)(table);
115
+ const config = (0, artifact_registry_js_1.getArtifactConfig)(table);
116
116
  const results = {
117
117
  _fetch_strategy: 'smart_chunked',
118
118
  _context_hint: config ? `${config.displayName} - fields fetched in groups to respect token limits` : 'Fields fetched separately but are related',
@@ -122,14 +122,10 @@ class SmartFieldFetcher {
122
122
  for (const group of fieldGroups) {
123
123
  console.log(`šŸ“¦ Fetching ${group.groupName}: ${group.description}`);
124
124
  try {
125
- const response = await this.client.query(table, {
126
- query: `sys_id=${sys_id}`,
127
- fields: group.fields,
128
- limit: 1
129
- });
130
- if (response.result?.[0]) {
125
+ const response = await this.client.getRecord(table, sys_id);
126
+ if (response) {
131
127
  results._field_groups[group.groupName] = {
132
- data: response.result[0],
128
+ data: response,
133
129
  description: group.description,
134
130
  fields: group.fields
135
131
  };
@@ -167,14 +163,10 @@ class SmartFieldFetcher {
167
163
  for (const group of WIDGET_FIELD_GROUPS) {
168
164
  console.log(`šŸ“¦ Fetching ${group.groupName}: ${group.description}`);
169
165
  try {
170
- const response = await this.client.query('sp_widget', {
171
- query: `sys_id=${sys_id}`,
172
- fields: group.fields,
173
- limit: 1
174
- });
175
- if (response.result?.[0]) {
166
+ const response = await this.client.getRecord('sp_widget', sys_id);
167
+ if (response) {
176
168
  results._field_groups[group.groupName] = {
177
- data: response.result[0],
169
+ data: response,
178
170
  description: group.description,
179
171
  fields: group.fields
180
172
  };
@@ -222,13 +214,9 @@ class SmartFieldFetcher {
222
214
  for (const field of fields) {
223
215
  try {
224
216
  console.log(` šŸ“„ Fetching field: ${field}`);
225
- const response = await this.client.query(table, {
226
- query: `sys_id=${sys_id}`,
227
- fields: [field],
228
- limit: 1
229
- });
230
- if (response.result?.[0]) {
231
- result.data[field] = response.result[0][field];
217
+ const response = await this.client.getRecord(table, sys_id);
218
+ if (response) {
219
+ result.data[field] = response[field];
232
220
  result._field_status[field] = 'success';
233
221
  }
234
222
  }
@@ -284,11 +272,7 @@ class SmartFieldFetcher {
284
272
  // Use CONTAINS operator for field search
285
273
  const query = `${field}CONTAINS${searchTerm}${additionalQuery ? '^' + additionalQuery : ''}`;
286
274
  try {
287
- const response = await this.client.query(table, {
288
- query: query,
289
- fields: ['sys_id', 'name', field.substring(0, 50)], // Get preview of field
290
- limit: 10
291
- });
275
+ const response = await this.client.searchRecords(table, query, 10);
292
276
  console.log(` āœ… Found ${response.result?.length || 0} matches`);
293
277
  return response.result || [];
294
278
  }
@@ -311,7 +295,7 @@ class SmartFieldFetcher {
311
295
  return BUSINESS_RULE_FIELD_GROUPS;
312
296
  }
313
297
  // Try to get from artifact registry
314
- const config = (0, artifact_registry_1.getArtifactConfig)(table);
298
+ const config = (0, artifact_registry_js_1.getArtifactConfig)(table);
315
299
  if (config) {
316
300
  return this.createFieldGroupsFromRegistry(config);
317
301
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "3.5.4",
3
+ "version": "3.5.7",
4
4
  "description": "ServiceNow development framework with MCP server integration. Executes background scripts using ES5 JavaScript only (ServiceNow Rhino engine requirement). Provides 18 MCP servers including local development sync for editing ServiceNow artifacts with Claude Code native tools, widget deployment with coherence validation, table operations, script execution, machine learning, advanced features, and comprehensive platform management.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",