snow-flow 10.0.21 → 10.0.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "10.0.21",
3
+ "version": "10.0.23",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -1284,6 +1284,7 @@ function DialogAuthEnterpriseCombined() {
1284
1284
  | "code"
1285
1285
  | "verifying-enterprise"
1286
1286
  | "checking-portal-sn"
1287
+ | "select-sn-instance"
1287
1288
  | "sn-method"
1288
1289
  | "sn-instance"
1289
1290
  | "sn-oauth-clientid"
@@ -1309,6 +1310,18 @@ function DialogAuthEnterpriseCombined() {
1309
1310
  clientSecret?: string
1310
1311
  } | null>(null)
1311
1312
 
1313
+ // ServiceNow instances from enterprise portal (for multi-instance selection)
1314
+ const [snInstances, setSnInstances] = createSignal<
1315
+ Array<{
1316
+ id: number
1317
+ instanceName: string
1318
+ instanceUrl: string
1319
+ environmentType: string
1320
+ isDefault: boolean
1321
+ enabled: boolean
1322
+ }>
1323
+ >([])
1324
+
1312
1325
  // ServiceNow state
1313
1326
  const [snMethod, setSnMethod] = createSignal<"oauth" | "basic">("oauth")
1314
1327
  const [snInstance, setSnInstance] = createSignal("")
@@ -1369,6 +1382,9 @@ function DialogAuthEnterpriseCombined() {
1369
1382
  setSessionId("")
1370
1383
  setAuthCode("")
1371
1384
  setTimeout(() => subdomainInput?.focus(), 10)
1385
+ } else if (currentStep === "select-sn-instance") {
1386
+ // Can't re-do enterprise auth, go back to main auth menu
1387
+ dialog.replace(() => <DialogAuth />)
1372
1388
  } else if (currentStep === "sn-method") {
1373
1389
  // Can't go back to enterprise flow, go to main menu
1374
1390
  dialog.replace(() => <DialogAuth />)
@@ -1520,28 +1536,42 @@ function DialogAuthEnterpriseCombined() {
1520
1536
  duration: 3000,
1521
1537
  })
1522
1538
 
1523
- // Check if enterprise portal has ServiceNow credentials
1539
+ // Check if enterprise portal has ServiceNow instances
1524
1540
  setStep("checking-portal-sn")
1525
- const portalCreds = await fetchPortalSnCredentials(portalUrl, data.token)
1541
+ const instances = await fetchPortalSnInstances(portalUrl, data.token)
1526
1542
 
1527
- if (portalCreds) {
1528
- // Portal has ServiceNow credentials - use them directly
1529
- setPortalSnCredentials(portalCreds)
1543
+ if (instances.length === 0) {
1544
+ // No instances manual setup
1545
+ toast.show({
1546
+ variant: "info",
1547
+ message: "No ServiceNow instances found on portal. Please configure manually.",
1548
+ duration: 3000,
1549
+ })
1550
+ setStep("sn-method")
1551
+ } else if (instances.length === 1) {
1552
+ // Single instance — fetch creds and proceed
1530
1553
  toast.show({
1531
1554
  variant: "info",
1532
- message: "ServiceNow credentials found in enterprise portal!",
1555
+ message: `ServiceNow instance found: ${instances[0].instanceName}`,
1533
1556
  duration: 3000,
1534
1557
  })
1535
- // Skip manual ServiceNow input, go directly to completing
1536
- await startBothMcpServersWithPortalCreds(portalUrl, data.token, portalCreds, data.user)
1558
+ const creds = await fetchPortalSnInstanceById(portalUrl, data.token, instances[0].id)
1559
+ if (creds) {
1560
+ setPortalSnCredentials(creds)
1561
+ await startBothMcpServersWithPortalCreds(portalUrl, data.token, creds, data.user)
1562
+ } else {
1563
+ toast.show({ variant: "error", message: "Failed to fetch instance credentials.", duration: 3000 })
1564
+ setStep("sn-method")
1565
+ }
1537
1566
  } else {
1538
- // No ServiceNow credentials on portal - ask user to input manually
1567
+ // Multiple instances show selection
1568
+ setSnInstances(instances)
1539
1569
  toast.show({
1540
1570
  variant: "info",
1541
- message: "No ServiceNow credentials found on portal. Please configure manually.",
1571
+ message: `${instances.length} ServiceNow instances found. Please select one.`,
1542
1572
  duration: 3000,
1543
1573
  })
1544
- setStep("sn-method")
1574
+ setStep("select-sn-instance")
1545
1575
  }
1546
1576
  } catch (e) {
1547
1577
  toast.show({ variant: "error", message: e instanceof Error ? e.message : "Verification failed" })
@@ -1552,38 +1582,58 @@ function DialogAuthEnterpriseCombined() {
1552
1582
 
1553
1583
  // === Portal ServiceNow Credentials ===
1554
1584
 
1555
- const fetchPortalSnCredentials = async (
1585
+ const fetchPortalSnInstances = async (
1556
1586
  portalUrl: string,
1557
- token: string
1558
- ): Promise<{ instanceUrl: string; clientId: string; clientSecret: string } | null> => {
1587
+ token: string,
1588
+ ): Promise<
1589
+ Array<{
1590
+ id: number
1591
+ instanceName: string
1592
+ instanceUrl: string
1593
+ environmentType: string
1594
+ isDefault: boolean
1595
+ enabled: boolean
1596
+ }>
1597
+ > => {
1559
1598
  try {
1560
- const response = await fetch(`${portalUrl}/api/user-credentials/servicenow/default`, {
1599
+ const response = await fetch(`${portalUrl}/api/user-credentials/servicenow/instances`, {
1561
1600
  method: "GET",
1562
1601
  headers: {
1563
1602
  Authorization: `Bearer ${token}`,
1564
1603
  Accept: "application/json",
1565
1604
  },
1566
1605
  })
1567
-
1568
- if (!response.ok) {
1569
- return null
1570
- }
1571
-
1606
+ if (!response.ok) return []
1572
1607
  const data = await response.json()
1608
+ if (!data.success || !Array.isArray(data.instances)) return []
1609
+ return data.instances.filter((i: any) => i.enabled)
1610
+ } catch {
1611
+ return []
1612
+ }
1613
+ }
1573
1614
 
1574
- if (!data.success || !data.instance) {
1575
- return null
1576
- }
1577
-
1578
- const instance = data.instance
1579
- if (!instance.instanceUrl || !instance.clientId || !instance.clientSecret) {
1580
- return null
1581
- }
1582
-
1615
+ const fetchPortalSnInstanceById = async (
1616
+ portalUrl: string,
1617
+ token: string,
1618
+ instanceId: number,
1619
+ ): Promise<{ instanceUrl: string; clientId: string; clientSecret: string } | null> => {
1620
+ try {
1621
+ const response = await fetch(`${portalUrl}/api/user-credentials/servicenow/instances/${instanceId}`, {
1622
+ method: "GET",
1623
+ headers: {
1624
+ Authorization: `Bearer ${token}`,
1625
+ Accept: "application/json",
1626
+ },
1627
+ })
1628
+ if (!response.ok) return null
1629
+ const data = await response.json()
1630
+ if (!data.success || !data.instance) return null
1631
+ const inst = data.instance
1632
+ if (!inst.instanceUrl || !inst.clientId || !inst.clientSecret) return null
1583
1633
  return {
1584
- instanceUrl: instance.instanceUrl,
1585
- clientId: instance.clientId,
1586
- clientSecret: instance.clientSecret,
1634
+ instanceUrl: inst.instanceUrl,
1635
+ clientId: inst.clientId,
1636
+ clientSecret: inst.clientSecret,
1587
1637
  }
1588
1638
  } catch {
1589
1639
  return null
@@ -1938,6 +1988,33 @@ function DialogAuthEnterpriseCombined() {
1938
1988
  </box>
1939
1989
  </Show>
1940
1990
 
1991
+ {/* Step 4c: Select ServiceNow instance */}
1992
+ <Show when={step() === "select-sn-instance"}>
1993
+ <DialogSelect
1994
+ title="Select ServiceNow Instance"
1995
+ options={snInstances().map((inst) => ({
1996
+ title: inst.instanceName,
1997
+ value: String(inst.id),
1998
+ description: inst.instanceUrl,
1999
+ footer: inst.environmentType + (inst.isDefault ? " (default)" : ""),
2000
+ category: "ServiceNow Instances",
2001
+ onSelect: async () => {
2002
+ setStep("completing")
2003
+ const portalUrl = `https://${subdomain().trim().toLowerCase()}.snow-flow.dev`
2004
+ const token = enterpriseData().token!
2005
+ const creds = await fetchPortalSnInstanceById(portalUrl, token, inst.id)
2006
+ if (creds) {
2007
+ setPortalSnCredentials(creds)
2008
+ await startBothMcpServersWithPortalCreds(portalUrl, token, creds, enterpriseData().user)
2009
+ } else {
2010
+ toast.show({ variant: "error", message: "Failed to fetch instance credentials.", duration: 3000 })
2011
+ setStep("sn-method")
2012
+ }
2013
+ },
2014
+ }))}
2015
+ />
2016
+ </Show>
2017
+
1941
2018
  {/* Step 5: Choose ServiceNow method */}
1942
2019
  <Show when={step() === "sn-method"}>
1943
2020
  <box gap={1}>
@@ -125,6 +125,12 @@ await activity_start({
125
125
  updateSetUrl: "https://instance.service-now.com/sys_update_set.do?sys_id=abc123..."
126
126
  });
127
127
 
128
+ await activity_update({
129
+ activityId: activityId,
130
+ status: "in_progress", // or "review", "failed", "cancelled"
131
+ summary: "Progress update: completed widget creation, starting business rule"
132
+ });
133
+
128
134
  await activity_add_artifact({
129
135
  activityId: activityId,
130
136
  artifactType: "widget",
@@ -82,6 +82,7 @@ import * as integrationTools from '../tools/integration/index.js';
82
82
  import * as platformTools from '../tools/platform/index.js';
83
83
  import * as devopsTools from '../tools/devops/index.js';
84
84
  import * as virtualAgentTools from '../tools/virtual-agent/index.js';
85
+ import * as pluginsTools from '../tools/plugins/index.js';
85
86
 
86
87
  // ES Module compatible __dirname
87
88
  const __filename = fileURLToPath(import.meta.url);
@@ -148,6 +149,7 @@ const STATIC_TOOL_MODULES: Record<string, any> = {
148
149
  'platform': platformTools,
149
150
  'devops': devopsTools,
150
151
  'virtual-agent': virtualAgentTools,
152
+ 'plugins': pluginsTools,
151
153
  };
152
154
 
153
155
  export class ToolRegistry {
@@ -166,7 +166,7 @@ export * from './mappers/index.js';
166
166
  // Decoders (1 tool)
167
167
  export * from './decoders/index.js';
168
168
 
169
- // Plugins (3 tools)
169
+ // Plugins (1 tool - unified plugin management)
170
170
  export * from './plugins/index.js';
171
171
 
172
172
  // Addons (3 tools)
@@ -1,3 +1 @@
1
- export { toolDefinition as snow_custom_plugin_def, execute as snow_custom_plugin_exec } from './snow_custom_plugin.js';
2
- export { toolDefinition as snow_activate_plugin_def, execute as snow_activate_plugin_exec } from './snow_activate_plugin.js';
3
- export { toolDefinition as snow_list_plugins_def, execute as snow_list_plugins_exec } from './snow_list_plugins.js';
1
+ export { toolDefinition as snow_plugin_manage_def, execute as snow_plugin_manage_exec } from './snow_plugin_manage.js'
@@ -0,0 +1,295 @@
1
+ /**
2
+ * snow_plugin_manage - Unified Plugin Management
3
+ *
4
+ * Single tool for all ServiceNow plugin operations:
5
+ * list, check, activate, deactivate.
6
+ */
7
+
8
+ import { MCPToolDefinition, ServiceNowContext, ToolResult } from '../../shared/types.js'
9
+ import { getAuthenticatedClient } from '../../shared/auth.js'
10
+ import { createSuccessResult, createErrorResult } from '../../shared/error-handler.js'
11
+
12
+ export const toolDefinition: MCPToolDefinition = {
13
+ name: 'snow_plugin_manage',
14
+ description: `Manage ServiceNow plugins: list, check status, activate, or deactivate.
15
+
16
+ Actions:
17
+ • list — Search and list plugins. Filter by name/ID, show only active.
18
+ • check — Get detailed info on a specific plugin (status, version, dependencies).
19
+ • activate — Activate a plugin (admin only). Uses CICD API with table fallback.
20
+ • deactivate — Deactivate a plugin (admin only). Uses CICD API with table fallback.
21
+
22
+ Examples:
23
+ • { action: "list", search: "incident" }
24
+ • { action: "check", plugin_id: "com.snc.incident" }
25
+ • { action: "activate", plugin_id: "com.snc.incident" }`,
26
+ category: 'advanced',
27
+ subcategory: 'administration',
28
+ use_cases: ['plugin-discovery', 'plugin-management', 'plugin-status', 'activation', 'deactivation'],
29
+ complexity: 'intermediate',
30
+ frequency: 'low',
31
+ permission: 'write',
32
+ allowedRoles: ['developer', 'admin'],
33
+ inputSchema: {
34
+ type: 'object',
35
+ properties: {
36
+ action: {
37
+ type: 'string',
38
+ description: 'Operation to perform',
39
+ enum: ['list', 'check', 'activate', 'deactivate'],
40
+ },
41
+ plugin_id: {
42
+ type: 'string',
43
+ description: '[check/activate/deactivate] Plugin identifier (e.g. "com.snc.incident") or sys_id',
44
+ },
45
+ search: {
46
+ type: 'string',
47
+ description: '[list] Filter by plugin name or ID (e.g. "incident", "com.snc")',
48
+ },
49
+ active_only: {
50
+ type: 'boolean',
51
+ description: '[list] Only show active plugins',
52
+ default: false,
53
+ },
54
+ limit: {
55
+ type: 'number',
56
+ description: '[list] Max results to return (default 50)',
57
+ default: 50,
58
+ },
59
+ },
60
+ required: ['action'],
61
+ },
62
+ }
63
+
64
+ export async function execute(args: any, context: ServiceNowContext): Promise<ToolResult> {
65
+ const { action } = args
66
+ try {
67
+ switch (action) {
68
+ case 'list':
69
+ return await executeList(args, context)
70
+ case 'check':
71
+ return await executeCheck(args, context)
72
+ case 'activate':
73
+ return await executeActivate(args, context)
74
+ case 'deactivate':
75
+ return await executeDeactivate(args, context)
76
+ default:
77
+ return createErrorResult(
78
+ 'Unknown action: ' + action + '. Valid actions: list, check, activate, deactivate'
79
+ )
80
+ }
81
+ } catch (error: any) {
82
+ return createErrorResult(error.message)
83
+ }
84
+ }
85
+
86
+ // ==================== LIST ====================
87
+ async function executeList(args: any, context: ServiceNowContext): Promise<ToolResult> {
88
+ const { search, active_only = false, limit = 50 } = args
89
+ const client = await getAuthenticatedClient(context)
90
+
91
+ const queryParts: string[] = []
92
+ if (search) {
93
+ queryParts.push('nameLIKE' + search + '^ORidLIKE' + search)
94
+ }
95
+ if (active_only) {
96
+ queryParts.push('active=true')
97
+ }
98
+
99
+ const response = await client.get('/api/now/table/v_plugin', {
100
+ params: {
101
+ sysparm_query: queryParts.join('^'),
102
+ sysparm_fields: 'sys_id,id,name,active,version,description',
103
+ sysparm_limit: limit,
104
+ sysparm_display_value: 'true',
105
+ },
106
+ })
107
+
108
+ const plugins = response.data.result || []
109
+ const activeCount = plugins.filter((p: any) => p.active === 'true' || p.active === true).length
110
+ const inactiveCount = plugins.length - activeCount
111
+
112
+ const summary =
113
+ 'Found ' +
114
+ plugins.length +
115
+ ' plugin(s)' +
116
+ (search ? ' matching "' + search + '"' : '') +
117
+ '. Active: ' +
118
+ activeCount +
119
+ ', Inactive: ' +
120
+ inactiveCount +
121
+ '.'
122
+
123
+ return createSuccessResult(
124
+ { action: 'list', plugins, count: plugins.length },
125
+ { active_count: activeCount, inactive_count: inactiveCount },
126
+ summary
127
+ )
128
+ }
129
+
130
+ // ==================== CHECK ====================
131
+ async function executeCheck(args: any, context: ServiceNowContext): Promise<ToolResult> {
132
+ const { plugin_id } = args
133
+ if (!plugin_id) return createErrorResult('plugin_id is required for check action')
134
+
135
+ const client = await getAuthenticatedClient(context)
136
+
137
+ const response = await client.get('/api/now/table/v_plugin', {
138
+ params: {
139
+ sysparm_query: 'id=' + plugin_id + '^ORsys_id=' + plugin_id,
140
+ sysparm_fields: 'sys_id,id,name,active,version,description,parent,optional,licensable',
141
+ sysparm_limit: 1,
142
+ sysparm_display_value: 'true',
143
+ },
144
+ })
145
+
146
+ const results = response.data.result || []
147
+ if (results.length === 0) {
148
+ return createErrorResult('Plugin not found: ' + plugin_id)
149
+ }
150
+
151
+ const plugin = results[0]
152
+ const active = plugin.active === 'true' || plugin.active === true
153
+ const summary =
154
+ 'Plugin "' +
155
+ plugin.name +
156
+ '" (' +
157
+ plugin.id +
158
+ ') is ' +
159
+ (active ? 'ACTIVE' : 'INACTIVE') +
160
+ '. Version: ' +
161
+ (plugin.version || 'unknown') +
162
+ '.'
163
+
164
+ return createSuccessResult(
165
+ { action: 'check', plugin },
166
+ { status: active ? 'active' : 'inactive' },
167
+ summary
168
+ )
169
+ }
170
+
171
+ // ==================== ACTIVATE ====================
172
+ async function executeActivate(args: any, context: ServiceNowContext): Promise<ToolResult> {
173
+ const { plugin_id } = args
174
+ if (!plugin_id) return createErrorResult('plugin_id is required for activate action')
175
+
176
+ const client = await getAuthenticatedClient(context)
177
+ const plugin = await lookupPlugin(client, plugin_id)
178
+ if (!plugin) return createErrorResult('Plugin not found: ' + plugin_id)
179
+
180
+ if (plugin.active === 'true' || plugin.active === true) {
181
+ return createSuccessResult(
182
+ { action: 'activate', plugin, activated: false, already_active: true },
183
+ {},
184
+ 'Plugin "' + plugin.name + '" (' + plugin.id + ') is already active.'
185
+ )
186
+ }
187
+
188
+ const { success, method, error } = await togglePlugin(client, plugin, 'activate')
189
+ if (!success) return createErrorResult(error!)
190
+
191
+ const verified = await verifyPluginState(client, plugin.sys_id, true)
192
+ const summary = verified
193
+ ? 'Plugin "' + plugin.name + '" (' + plugin.id + ') activated successfully via ' + method + '.'
194
+ : 'Activation request sent for "' + plugin.name + '" via ' + method + '. May take a moment to fully activate.'
195
+
196
+ return createSuccessResult({ action: 'activate', plugin, activated: true, verified, method }, {}, summary)
197
+ }
198
+
199
+ // ==================== DEACTIVATE ====================
200
+ async function executeDeactivate(args: any, context: ServiceNowContext): Promise<ToolResult> {
201
+ const { plugin_id } = args
202
+ if (!plugin_id) return createErrorResult('plugin_id is required for deactivate action')
203
+
204
+ const client = await getAuthenticatedClient(context)
205
+ const plugin = await lookupPlugin(client, plugin_id)
206
+ if (!plugin) return createErrorResult('Plugin not found: ' + plugin_id)
207
+
208
+ if (plugin.active !== 'true' && plugin.active !== true) {
209
+ return createSuccessResult(
210
+ { action: 'deactivate', plugin, deactivated: false, already_inactive: true },
211
+ {},
212
+ 'Plugin "' + plugin.name + '" (' + plugin.id + ') is already inactive.'
213
+ )
214
+ }
215
+
216
+ const { success, method, error } = await togglePlugin(client, plugin, 'deactivate')
217
+ if (!success) return createErrorResult(error!)
218
+
219
+ const verified = await verifyPluginState(client, plugin.sys_id, false)
220
+ const summary = verified
221
+ ? 'Plugin "' + plugin.name + '" (' + plugin.id + ') deactivated successfully via ' + method + '.'
222
+ : 'Deactivation request sent for "' + plugin.name + '" via ' + method + '. May take a moment to fully deactivate.'
223
+
224
+ return createSuccessResult({ action: 'deactivate', plugin, deactivated: true, verified, method }, {}, summary)
225
+ }
226
+
227
+ // ==================== HELPERS ====================
228
+
229
+ async function lookupPlugin(client: any, pluginId: string): Promise<any | null> {
230
+ const response = await client.get('/api/now/table/v_plugin', {
231
+ params: {
232
+ sysparm_query: 'id=' + pluginId + '^ORsys_id=' + pluginId,
233
+ sysparm_fields: 'sys_id,id,name,active',
234
+ sysparm_limit: 1,
235
+ },
236
+ })
237
+ const results = response.data.result || []
238
+ return results.length > 0 ? results[0] : null
239
+ }
240
+
241
+ async function togglePlugin(
242
+ client: any,
243
+ plugin: any,
244
+ operation: 'activate' | 'deactivate'
245
+ ): Promise<{ success: boolean; method: string; error?: string }> {
246
+ // Try CICD Plugin API first
247
+ try {
248
+ await client.post('/api/sn_cicd/plugin/' + encodeURIComponent(plugin.id) + '/' + operation)
249
+ return { success: true, method: 'cicd_api' }
250
+ } catch (cicdError: any) {
251
+ const status = cicdError.response ? cicdError.response.status : 0
252
+ if (status === 404 || status === 400) {
253
+ // Fallback to sys_plugins PATCH
254
+ try {
255
+ const activeValue = operation === 'activate' ? 'true' : 'false'
256
+ await client.patch('/api/now/table/sys_plugins/' + plugin.sys_id, { active: activeValue })
257
+ return { success: true, method: 'table_api' }
258
+ } catch (patchError: any) {
259
+ return {
260
+ success: false,
261
+ method: '',
262
+ error:
263
+ 'Failed to ' +
264
+ operation +
265
+ ' plugin via both CICD API and Table API. CICD: ' +
266
+ (cicdError.message || 'unknown') +
267
+ '. Table API: ' +
268
+ (patchError.message || 'unknown'),
269
+ }
270
+ }
271
+ }
272
+ return { success: false, method: '', error: 'CICD Plugin API error: ' + (cicdError.message || 'unknown') }
273
+ }
274
+ }
275
+
276
+ async function verifyPluginState(client: any, sysId: string, expectActive: boolean): Promise<boolean> {
277
+ try {
278
+ const response = await client.get('/api/now/table/v_plugin', {
279
+ params: {
280
+ sysparm_query: 'sys_id=' + sysId,
281
+ sysparm_fields: 'active',
282
+ sysparm_limit: 1,
283
+ },
284
+ })
285
+ const results = response.data.result || []
286
+ if (results.length === 0) return false
287
+ const isActive = results[0].active === 'true' || results[0].active === true
288
+ return expectActive ? isActive : !isActive
289
+ } catch {
290
+ return false
291
+ }
292
+ }
293
+
294
+ export const version = '1.0.0'
295
+ export const author = 'Snow-Flow'
@@ -1,47 +0,0 @@
1
- /**
2
- * snow_activate_plugin
3
- */
4
-
5
- import { MCPToolDefinition, ServiceNowContext, ToolResult } from '../../shared/types.js';
6
- import { getAuthenticatedClient } from '../../shared/auth.js';
7
- import { createSuccessResult, createErrorResult } from '../../shared/error-handler.js';
8
-
9
- export const toolDefinition: MCPToolDefinition = {
10
- name: 'snow_activate_plugin',
11
- description: 'Activate ServiceNow plugin',
12
- // Metadata for tool discovery (not sent to LLM)
13
- category: 'advanced',
14
- subcategory: 'administration',
15
- use_cases: ['plugin-management', 'activation', 'configuration'],
16
- complexity: 'intermediate',
17
- frequency: 'low',
18
-
19
- // Permission enforcement
20
- // Classification: WRITE - Write operation based on name pattern
21
- permission: 'write',
22
- allowedRoles: ['developer', 'admin'],
23
- inputSchema: {
24
- type: 'object',
25
- properties: {
26
- plugin_id: { type: 'string', description: 'Plugin ID' }
27
- },
28
- required: ['plugin_id']
29
- }
30
- };
31
-
32
- export async function execute(args: any, context: ServiceNowContext): Promise<ToolResult> {
33
- const { plugin_id } = args;
34
- try {
35
- const client = await getAuthenticatedClient(context);
36
- const response = await client.post('/api/now/table/v_plugin', {
37
- id: plugin_id,
38
- active: true
39
- });
40
- return createSuccessResult({ activated: true, plugin: response.data.result });
41
- } catch (error: any) {
42
- return createErrorResult(error.message);
43
- }
44
- }
45
-
46
- export const version = '1.0.0';
47
- export const author = 'Snow-Flow SDK Migration';
@@ -1,46 +0,0 @@
1
- /**
2
- * snow_custom_plugin
3
- */
4
-
5
- import { MCPToolDefinition, ServiceNowContext, ToolResult } from '../../shared/types.js';
6
- import { createSuccessResult, createErrorResult } from '../../shared/error-handler.js';
7
-
8
- export const toolDefinition: MCPToolDefinition = {
9
- name: 'snow_custom_plugin',
10
- description: 'Execute custom plugin logic',
11
- // Metadata for tool discovery (not sent to LLM)
12
- category: 'advanced',
13
- subcategory: 'administration',
14
- use_cases: ['plugin-customization', 'extensions', 'custom-logic'],
15
- complexity: 'advanced',
16
- frequency: 'low',
17
-
18
- // Permission enforcement
19
- // Classification: WRITE - Plugin function - executes custom plugin which can modify data
20
- permission: 'write',
21
- allowedRoles: ['developer', 'admin'],
22
- inputSchema: {
23
- type: 'object',
24
- properties: {
25
- plugin_name: { type: 'string', description: 'Plugin name' },
26
- parameters: { type: 'object', description: 'Plugin parameters' }
27
- },
28
- required: ['plugin_name']
29
- }
30
- };
31
-
32
- export async function execute(args: any, context: ServiceNowContext): Promise<ToolResult> {
33
- const { plugin_name, parameters = {} } = args;
34
- try {
35
- return createSuccessResult({
36
- executed: true,
37
- plugin_name,
38
- parameters
39
- });
40
- } catch (error: any) {
41
- return createErrorResult(error.message);
42
- }
43
- }
44
-
45
- export const version = '1.0.0';
46
- export const author = 'Snow-Flow SDK Migration';
@@ -1,52 +0,0 @@
1
- /**
2
- * snow_list_plugins
3
- */
4
-
5
- import { MCPToolDefinition, ServiceNowContext, ToolResult } from '../../shared/types.js';
6
- import { getAuthenticatedClient } from '../../shared/auth.js';
7
- import { createSuccessResult, createErrorResult } from '../../shared/error-handler.js';
8
-
9
- export const toolDefinition: MCPToolDefinition = {
10
- name: 'snow_list_plugins',
11
- description: 'List installed plugins',
12
- // Metadata for tool discovery (not sent to LLM)
13
- category: 'advanced',
14
- subcategory: 'administration',
15
- use_cases: ['plugin-discovery', 'plugin-management', 'administration'],
16
- complexity: 'beginner',
17
- frequency: 'low',
18
-
19
- // Permission enforcement
20
- // Classification: READ - Read-only operation based on name pattern
21
- permission: 'read',
22
- allowedRoles: ['developer', 'stakeholder', 'admin'],
23
- inputSchema: {
24
- type: 'object',
25
- properties: {
26
- active_only: { type: 'boolean', default: false }
27
- }
28
- }
29
- };
30
-
31
- export async function execute(args: any, context: ServiceNowContext): Promise<ToolResult> {
32
- const { active_only = false } = args;
33
- try {
34
- const client = await getAuthenticatedClient(context);
35
- const query = active_only ? 'active=true' : '';
36
- const response = await client.get('/api/now/table/v_plugin', {
37
- params: {
38
- sysparm_query: query,
39
- sysparm_limit: 100
40
- }
41
- });
42
- return createSuccessResult({
43
- plugins: response.data.result,
44
- count: response.data.result.length
45
- });
46
- } catch (error: any) {
47
- return createErrorResult(error.message);
48
- }
49
- }
50
-
51
- export const version = '1.0.0';
52
- export const author = 'Snow-Flow SDK Migration';