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.
- package/dist/mcp/servicenow-local-development-mcp.d.ts +9 -30
- package/dist/mcp/servicenow-local-development-mcp.js +351 -483
- package/dist/mcp/shared/enhanced-base-mcp-server.d.ts +3 -2
- package/dist/mcp/shared/enhanced-base-mcp-server.js +1 -1
- package/dist/utils/artifact-local-sync.d.ts +1 -1
- package/dist/utils/artifact-local-sync.js +6 -14
- package/dist/utils/servicenow-client-with-tracking.js +1 -1
- package/dist/utils/smart-field-fetcher.d.ts +1 -1
- package/dist/utils/smart-field-fetcher.js +13 -29
- package/package.json +1 -1
|
@@ -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 {
|
|
11
|
-
export declare class ServiceNowLocalDevelopmentMCP extends
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
*/
|
|
38
|
-
private validateCoherence;
|
|
39
|
-
/**
|
|
40
|
-
* Convert modern JS to ES5
|
|
41
|
-
*/
|
|
21
|
+
private validateArtifactCoherence;
|
|
22
|
+
private syncCleanup;
|
|
42
23
|
private convertToES5;
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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 (
|
|
276
|
-
//
|
|
277
|
-
artifact = await this.syncManager.pullArtifact(
|
|
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(
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
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
|
|
382
|
-
if (
|
|
383
|
-
const status = this.syncManager.getSyncStatus(
|
|
294
|
+
const { sys_id } = args;
|
|
295
|
+
if (sys_id) {
|
|
296
|
+
const status = this.syncManager.getSyncStatus(sys_id);
|
|
384
297
|
return {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
298
|
+
content: [
|
|
299
|
+
{
|
|
300
|
+
type: 'text',
|
|
301
|
+
text: `Sync status for ${sys_id}: ${status}`
|
|
302
|
+
}
|
|
303
|
+
]
|
|
388
304
|
};
|
|
389
305
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
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
|
|
439
|
-
folderName: config
|
|
440
|
-
fields: config
|
|
441
|
-
hasCoherence: (config
|
|
442
|
-
requiresES5: config
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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
|
-
|
|
471
|
-
*/
|
|
472
|
-
async validateCoherence(args) {
|
|
342
|
+
async validateArtifactCoherence(args) {
|
|
343
|
+
const { sys_id } = args;
|
|
473
344
|
try {
|
|
474
|
-
const results = await this.syncManager.validateArtifactCoherence(
|
|
475
|
-
|
|
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
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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
|
-
|
|
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
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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
|
-
|
|
519
|
-
|
|
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
|
-
|
|
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
|
-
|
|
524
|
-
.replace(/\
|
|
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(/(\
|
|
528
|
-
.replace(
|
|
529
|
-
|
|
530
|
-
|
|
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
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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 {
|
|
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:
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
126
|
-
|
|
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
|
|
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.
|
|
171
|
-
|
|
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
|
|
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.
|
|
226
|
-
|
|
227
|
-
|
|
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.
|
|
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,
|
|
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.
|
|
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",
|