@sonisoft/now-sdk-ext-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +393 -0
  2. package/dist/common/connection.d.ts +42 -0
  3. package/dist/common/connection.d.ts.map +1 -0
  4. package/dist/common/connection.js +119 -0
  5. package/dist/common/connection.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +108 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/tools/aggregate.d.ts +20 -0
  11. package/dist/tools/aggregate.d.ts.map +1 -0
  12. package/dist/tools/aggregate.js +271 -0
  13. package/dist/tools/aggregate.js.map +1 -0
  14. package/dist/tools/app-manager.d.ts +50 -0
  15. package/dist/tools/app-manager.d.ts.map +1 -0
  16. package/dist/tools/app-manager.js +756 -0
  17. package/dist/tools/app-manager.js.map +1 -0
  18. package/dist/tools/atf.d.ts +16 -0
  19. package/dist/tools/atf.d.ts.map +1 -0
  20. package/dist/tools/atf.js +246 -0
  21. package/dist/tools/atf.js.map +1 -0
  22. package/dist/tools/attachment.d.ts +20 -0
  23. package/dist/tools/attachment.d.ts.map +1 -0
  24. package/dist/tools/attachment.js +223 -0
  25. package/dist/tools/attachment.js.map +1 -0
  26. package/dist/tools/batch.d.ts +15 -0
  27. package/dist/tools/batch.d.ts.map +1 -0
  28. package/dist/tools/batch.js +159 -0
  29. package/dist/tools/batch.js.map +1 -0
  30. package/dist/tools/cmdb.d.ts +14 -0
  31. package/dist/tools/cmdb.d.ts.map +1 -0
  32. package/dist/tools/cmdb.js +199 -0
  33. package/dist/tools/cmdb.js.map +1 -0
  34. package/dist/tools/codesearch.d.ts +31 -0
  35. package/dist/tools/codesearch.d.ts.map +1 -0
  36. package/dist/tools/codesearch.js +371 -0
  37. package/dist/tools/codesearch.js.map +1 -0
  38. package/dist/tools/discovery.d.ts +15 -0
  39. package/dist/tools/discovery.d.ts.map +1 -0
  40. package/dist/tools/discovery.js +204 -0
  41. package/dist/tools/discovery.js.map +1 -0
  42. package/dist/tools/execute-script.d.ts +9 -0
  43. package/dist/tools/execute-script.d.ts.map +1 -0
  44. package/dist/tools/execute-script.js +106 -0
  45. package/dist/tools/execute-script.js.map +1 -0
  46. package/dist/tools/find-atf-tests.d.ts +9 -0
  47. package/dist/tools/find-atf-tests.d.ts.map +1 -0
  48. package/dist/tools/find-atf-tests.js +152 -0
  49. package/dist/tools/find-atf-tests.js.map +1 -0
  50. package/dist/tools/health.d.ts +9 -0
  51. package/dist/tools/health.d.ts.map +1 -0
  52. package/dist/tools/health.js +137 -0
  53. package/dist/tools/health.js.map +1 -0
  54. package/dist/tools/lookup-app.d.ts +11 -0
  55. package/dist/tools/lookup-app.d.ts.map +1 -0
  56. package/dist/tools/lookup-app.js +242 -0
  57. package/dist/tools/lookup-app.js.map +1 -0
  58. package/dist/tools/lookup-columns.d.ts +10 -0
  59. package/dist/tools/lookup-columns.d.ts.map +1 -0
  60. package/dist/tools/lookup-columns.js +180 -0
  61. package/dist/tools/lookup-columns.js.map +1 -0
  62. package/dist/tools/lookup-table.d.ts +10 -0
  63. package/dist/tools/lookup-table.d.ts.map +1 -0
  64. package/dist/tools/lookup-table.js +150 -0
  65. package/dist/tools/lookup-table.js.map +1 -0
  66. package/dist/tools/query-batch.d.ts +16 -0
  67. package/dist/tools/query-batch.d.ts.map +1 -0
  68. package/dist/tools/query-batch.js +197 -0
  69. package/dist/tools/query-batch.js.map +1 -0
  70. package/dist/tools/query-syslog.d.ts +9 -0
  71. package/dist/tools/query-syslog.d.ts.map +1 -0
  72. package/dist/tools/query-syslog.js +127 -0
  73. package/dist/tools/query-syslog.js.map +1 -0
  74. package/dist/tools/query-table.d.ts +8 -0
  75. package/dist/tools/query-table.d.ts.map +1 -0
  76. package/dist/tools/query-table.js +137 -0
  77. package/dist/tools/query-table.js.map +1 -0
  78. package/dist/tools/schema.d.ts +24 -0
  79. package/dist/tools/schema.d.ts.map +1 -0
  80. package/dist/tools/schema.js +321 -0
  81. package/dist/tools/schema.js.map +1 -0
  82. package/dist/tools/scope.d.ts +25 -0
  83. package/dist/tools/scope.d.ts.map +1 -0
  84. package/dist/tools/scope.js +226 -0
  85. package/dist/tools/scope.js.map +1 -0
  86. package/dist/tools/scriptsync.d.ts +14 -0
  87. package/dist/tools/scriptsync.d.ts.map +1 -0
  88. package/dist/tools/scriptsync.js +167 -0
  89. package/dist/tools/scriptsync.js.map +1 -0
  90. package/dist/tools/task.d.ts +39 -0
  91. package/dist/tools/task.d.ts.map +1 -0
  92. package/dist/tools/task.js +390 -0
  93. package/dist/tools/task.js.map +1 -0
  94. package/dist/tools/updateset.d.ts +46 -0
  95. package/dist/tools/updateset.d.ts.map +1 -0
  96. package/dist/tools/updateset.js +475 -0
  97. package/dist/tools/updateset.js.map +1 -0
  98. package/dist/tools/workflow.d.ts +9 -0
  99. package/dist/tools/workflow.d.ts.map +1 -0
  100. package/dist/tools/workflow.js +164 -0
  101. package/dist/tools/workflow.js.map +1 -0
  102. package/package.json +68 -0
package/README.md ADDED
@@ -0,0 +1,393 @@
1
+ # now-sdk-ext-mcp
2
+
3
+ An MCP (Model Context Protocol) server that enables AI assistants to interact directly with ServiceNow instances — executing background scripts, querying data, running ATF tests, tailing logs, and more.
4
+
5
+ Built on [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk) and [`@sonisoft/now-sdk-ext-core`](https://git.sonisoft.io).
6
+
7
+ ## Quick Start
8
+
9
+ ### Prerequisites
10
+
11
+ - **Node.js** >= 18
12
+ - **ServiceNow CLI credentials** configured via `snc configure`
13
+
14
+ ### Install and Build
15
+
16
+ ```bash
17
+ git clone <repo-url>
18
+ cd now-sdk-ext-mcp
19
+ npm install
20
+ npm run build
21
+ ```
22
+
23
+ ### Configure Credentials
24
+
25
+ This server uses the same credential store as the ServiceNow CLI (`snc`). If you haven't already, configure your instance credentials:
26
+
27
+ ```bash
28
+ now-sdk auth --add <instance_alias>
29
+ ```
30
+
31
+ This stores credentials locally so the MCP server can authenticate without prompting.
32
+
33
+ ### Run the Server
34
+
35
+ ```bash
36
+ node dist/index.js
37
+ ```
38
+
39
+ The server communicates over **stdio** (standard input/output) using the MCP JSON-RPC protocol. It is not meant to be run interactively — it's designed to be launched by an MCP client (Claude Desktop, VS Code, Cursor, etc.).
40
+
41
+ ## Connecting to an MCP Client
42
+
43
+ ### Claude Desktop
44
+
45
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "servicenow": {
51
+ "command": "node",
52
+ "args": ["/absolute/path/to/now-sdk-ext-mcp/dist/index.js"]
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ To set a default instance (so you don't have to specify it every time):
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "servicenow": {
64
+ "command": "node",
65
+ "args": ["/absolute/path/to/now-sdk-ext-mcp/dist/index.js"],
66
+ "env": {
67
+ "SN_AUTH_ALIAS": "dev224436"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ ### VS Code / Cursor
75
+
76
+ Add to your `.vscode/mcp.json` or Cursor MCP settings:
77
+
78
+ ```json
79
+ {
80
+ "servers": {
81
+ "servicenow": {
82
+ "command": "node",
83
+ "args": ["/absolute/path/to/now-sdk-ext-mcp/dist/index.js"],
84
+ "env": {
85
+ "SN_AUTH_ALIAS": "dev224436"
86
+ }
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ ### Claude Code
93
+
94
+ Add to your `.claude/settings.json` or project-level `.mcp.json`:
95
+
96
+ ```json
97
+ {
98
+ "mcpServers": {
99
+ "servicenow": {
100
+ "command": "node",
101
+ "args": ["/absolute/path/to/now-sdk-ext-mcp/dist/index.js"],
102
+ "env": {
103
+ "SN_AUTH_ALIAS": "dev224436"
104
+ }
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ ### Opencode
111
+
112
+ Add to your `.config/opencode/opencode.json` or project-level `opencode.jsonc`
113
+
114
+ ```json
115
+ {
116
+ "mcp": {
117
+ "servicenow": {
118
+ "type": "local",
119
+ "command": ["node", "/absolute/path/to/now-sdk-ext-mcp/dist/index.js"],
120
+ "enabled": true,
121
+ "environment": {
122
+ "SN_AUTH_ALIAS": "dev224436"
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ ## How It Works
129
+
130
+ Once connected, you can talk to your AI assistant naturally:
131
+
132
+ > "Find all CMDB CI records in the computer class on my dev224436 instance"
133
+
134
+ > "Run a script on dev224436 that counts all active incidents by priority"
135
+
136
+ > "Query the sys_user table for users with the admin role on prod"
137
+
138
+ The AI will:
139
+ 1. Write the appropriate ServiceNow server-side JavaScript
140
+ 2. Call the `execute_script` tool with the instance alias and script
141
+ 3. Return the results in a readable format
142
+
143
+ The `instance` parameter can be passed explicitly per-request or defaulted via the `SN_AUTH_ALIAS` environment variable, so if you only work with one instance you can set-and-forget.
144
+
145
+ ## Available Tools
146
+
147
+ See **[TOOLS.md](TOOLS.md)** for the full list of available tools with parameters and examples.
148
+
149
+ ## Environment Variables
150
+
151
+ | Variable | Default | Description |
152
+ |----------|---------|-------------|
153
+ | `SN_AUTH_ALIAS` | _(none)_ | Default ServiceNow auth alias. Used when a tool call doesn't specify an `instance` parameter. |
154
+
155
+ ## Development
156
+
157
+ ### Project Structure
158
+
159
+ ```
160
+ src/
161
+ ├── index.ts # Server entry point — registers tools, starts stdio transport
162
+ ├── tools/ # MCP tool implementations (one file per tool)
163
+ │ └── execute-script.ts # execute_script tool
164
+ └── common/
165
+ └── connection.ts # ServiceNow connection manager (credential resolution + caching)
166
+
167
+ test/
168
+ ├── __mocks__/ # Manual mocks for external dependencies
169
+ ├── helpers/ # Shared test utilities and factories
170
+ ├── unit/ # Unit tests (mocked external deps)
171
+ │ ├── common/
172
+ │ └── tools/
173
+ └── integration/ # Integration tests (full MCP protocol, no real SN calls)
174
+ ```
175
+
176
+ ### Scripts
177
+
178
+ | Command | Description |
179
+ |---------|-------------|
180
+ | `npm run build` | Clean and compile TypeScript to `dist/` |
181
+ | `npm run dev` | Build and run the server |
182
+ | `npm test` | Run unit tests |
183
+ | `npm run test:unit` | Run unit tests with coverage and junit reporting |
184
+ | `npm run test:integration` | Run MCP protocol integration tests |
185
+ | `npm run test:all` | Run all tests |
186
+ | `npm run lint` | Type-check with `tsc --noEmit` |
187
+
188
+ ### Adding a New Tool
189
+
190
+ 1. Create a new file in `src/tools/` (e.g., `src/tools/query-table.ts`).
191
+ 2. Export a registration function:
192
+
193
+ ```typescript
194
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
195
+ import { z } from "zod";
196
+ import { getServiceNowInstance } from "../common/connection.js";
197
+
198
+ export function registerQueryTableTool(server: McpServer): void {
199
+ server.registerTool(
200
+ "query_table",
201
+ {
202
+ title: "Query Table",
203
+ description: "Query records from a ServiceNow table.",
204
+ inputSchema: {
205
+ instance: z.string().optional().describe("ServiceNow instance auth alias"),
206
+ table: z.string().describe("Table name to query"),
207
+ // ... more params
208
+ },
209
+ },
210
+ async ({ instance, table }) => {
211
+ const snInstance = await getServiceNowInstance(instance);
212
+ // ... use core library to query
213
+ return {
214
+ content: [{ type: "text" as const, text: "results here" }],
215
+ };
216
+ }
217
+ );
218
+ }
219
+ ```
220
+
221
+ 3. Register it in `src/index.ts`:
222
+
223
+ ```typescript
224
+ import { registerQueryTableTool } from "./tools/query-table.js";
225
+
226
+ registerQueryTableTool(server);
227
+ ```
228
+
229
+ 4. Add tests in `test/unit/tools/` following the existing pattern.
230
+ 5. Document the tool in [`TOOLS.md`](TOOLS.md).
231
+
232
+ ### Testing Approach
233
+
234
+ Tests use the MCP SDK's `InMemoryTransport` to create linked client+server pairs entirely in-process. This means tests go through the full MCP protocol stack (JSON-RPC serialization, schema validation, handler dispatch) without spawning processes or touching the network.
235
+
236
+ - **Unit tests** (`test/unit/`): Mock external dependencies (`@sonisoft/now-sdk-ext-core`, `@servicenow/sdk-cli`) using `jest.unstable_mockModule()` for ESM compatibility. Test tool behavior through the MCP client.
237
+ - **Integration tests** (`test/integration/`): Verify the MCP protocol lifecycle (handshake, tool listing, sequential calls) without mocking.
238
+
239
+ ### Sibling Projects
240
+
241
+ This MCP server wraps the same core library used by the CLI:
242
+
243
+ - **Core library**: [`@sonisoft/now-sdk-ext-core`](../now-sdk-ext-core) — all ServiceNow communication (auth, HTTP, WebSocket, script execution, ATF, syslog)
244
+ - **CLI**: [`@sonisoft/now-sdk-ext-cli`](../now-sdk-ext-cli) — the `nex` CLI that wraps the core library with oclif
245
+
246
+ When adding new MCP tools, reference the corresponding CLI command in `now-sdk-ext-cli/src/commands/` for the expected behavior and data flow.
247
+
248
+ ## Contributing
249
+
250
+ ### Testing
251
+
252
+ There are three layers of testing for this project:
253
+
254
+ #### 1. Automated Tests (Jest)
255
+
256
+ Unit and integration tests run entirely in-process using the MCP SDK's `InMemoryTransport` — no server process, no network, no credentials needed.
257
+
258
+ ```bash
259
+ npm test # Unit tests (default, fast)
260
+ npm run test:unit # Unit tests with coverage + junit
261
+ npm run test:integration # MCP protocol integration tests
262
+ npm run test:all # Everything
263
+ ```
264
+
265
+ Unit tests mock all external dependencies (`@sonisoft/now-sdk-ext-core`, `@servicenow/sdk-cli`) so they are fast and deterministic. Integration tests verify the MCP protocol lifecycle (handshake, tool listing, tool calls, error responses) without hitting real ServiceNow instances.
266
+
267
+ Always run `npm test` before committing.
268
+
269
+ #### 2. MCP Inspector (Interactive Testing)
270
+
271
+ The official [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is a web UI that acts as an MCP client, letting you interactively browse tools, invoke them with custom inputs, and see results — without connecting to Claude or any AI client.
272
+
273
+ ```bash
274
+ # Build first
275
+ npm run build
276
+
277
+ # Launch the inspector (opens a browser UI at http://localhost:6274)
278
+ npx @modelcontextprotocol/inspector node dist/index.js
279
+
280
+ # Pass env vars to the server (e.g., default instance alias)
281
+ npx @modelcontextprotocol/inspector -e SN_AUTH_ALIAS=dev224436 node dist/index.js
282
+ ```
283
+
284
+ In the inspector UI you can:
285
+ - Browse registered tools and their input schemas in the **Tools** tab
286
+ - Fill in parameters and invoke tools
287
+ - See the JSON-RPC request/response and tool output
288
+ - View server stderr logs in the **Notifications** pane
289
+
290
+ The inspector also has a headless CLI mode for scripting:
291
+
292
+ ```bash
293
+ # List all tools
294
+ npx @modelcontextprotocol/inspector --cli node dist/index.js --method tools/list
295
+
296
+ # Call a specific tool
297
+ npx @modelcontextprotocol/inspector --cli node dist/index.js \
298
+ --method tools/call --tool-name execute_script \
299
+ --tool-arg instance=dev224436 \
300
+ --tool-arg script='gs.print("hello")' \
301
+ --tool-arg scope=global
302
+ ```
303
+
304
+ #### 3. Testing with Claude Code
305
+
306
+ To test the server end-to-end with Claude Code as the MCP client:
307
+
308
+ **Add the server:**
309
+
310
+ ```bash
311
+ # From the now-sdk-ext-mcp project root (after building):
312
+ claude mcp add --transport stdio --env SN_AUTH_ALIAS=dev224436 servicenow \
313
+ -- node /absolute/path/to/now-sdk-ext-mcp/dist/index.js
314
+ ```
315
+
316
+ Or create a `.mcp.json` at your project root (this is shareable via version control):
317
+
318
+ ```json
319
+ {
320
+ "mcpServers": {
321
+ "servicenow": {
322
+ "command": "node",
323
+ "args": ["/absolute/path/to/now-sdk-ext-mcp/dist/index.js"],
324
+ "env": {
325
+ "SN_AUTH_ALIAS": "dev224436"
326
+ }
327
+ }
328
+ }
329
+ }
330
+ ```
331
+
332
+ **Verify the connection:**
333
+
334
+ Inside a Claude Code session, run `/mcp` to see all connected servers and their status. The `servicenow` server should show as connected.
335
+
336
+ **Test it:**
337
+
338
+ Ask Claude something like:
339
+
340
+ > "Run a script on dev224436 that prints the current user's name using gs.print(gs.getUserName())"
341
+
342
+ Claude should call the `execute_script` tool and return the result.
343
+
344
+ **Manage servers:**
345
+
346
+ ```bash
347
+ claude mcp list # List all configured servers
348
+ claude mcp get servicenow # Show details for the servicenow server
349
+ claude mcp remove servicenow # Remove it
350
+ ```
351
+
352
+ #### Manual stdin Testing
353
+
354
+ Since the server communicates via JSON-RPC over stdio, you can pipe messages directly for quick smoke tests:
355
+
356
+ ```bash
357
+ # List tools (single-message shortcut — works for basic inspection)
358
+ echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
359
+ | node dist/index.js 2>/dev/null \
360
+ | jq '.result.tools[].name'
361
+ ```
362
+
363
+ For a full protocol exchange (initialize handshake + tool call):
364
+
365
+ ```bash
366
+ printf '%s\n%s\n%s\n' \
367
+ '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":0}' \
368
+ '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
369
+ '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
370
+ | node dist/index.js 2>/dev/null \
371
+ | jq
372
+ ```
373
+
374
+ ### Debugging
375
+
376
+ Since stdout is reserved for JSON-RPC, **never use `console.log()` in server code** — it corrupts the protocol stream. Use these approaches instead:
377
+
378
+ - **`console.error()`** — writes to stderr, which is safe and visible in the MCP Inspector's Notifications pane and in Claude Desktop's log files (`~/Library/Logs/Claude/mcp*.log`).
379
+ - **MCP Inspector** — run the server under the inspector to see all JSON-RPC messages and stderr output in real time.
380
+ - **File logging** — for persistent debug logs, the core library's `Logger` class writes to `logs/` with Winston. Set the log level via the tool's logic as needed.
381
+
382
+ ### Code Conventions
383
+
384
+ - ES Modules (`"type": "module"` in package.json)
385
+ - TypeScript strict mode
386
+ - Target ES2022, module Node16
387
+ - Match the patterns and style of the sibling `now-sdk-ext-core` and `now-sdk-ext-cli` projects
388
+ - Every tool that talks to ServiceNow should accept an optional `instance` parameter
389
+ - Test every tool through the MCP client (not by calling handler functions directly) so the full protocol stack is exercised
390
+
391
+ ## License
392
+
393
+ MIT
@@ -0,0 +1,42 @@
1
+ import { ServiceNowInstance } from "@sonisoft/now-sdk-ext-core";
2
+ /**
3
+ * Returns a ServiceNowInstance using stored credentials from the ServiceNow
4
+ * CLI credential store (snc configure).
5
+ *
6
+ * Resolution order: explicit authAlias parameter → SN_AUTH_ALIAS env var.
7
+ * Throws if neither is available.
8
+ *
9
+ * Instances are cached per alias with a 30-minute TTL so that repeated
10
+ * calls reuse the same session, but stale sessions are automatically
11
+ * refreshed. This allows the AI to work with multiple instances in a
12
+ * single conversation (e.g., dev224436 and prod).
13
+ */
14
+ export declare function getServiceNowInstance(authAlias?: string): Promise<ServiceNowInstance>;
15
+ /**
16
+ * Evicts a cached instance so the next call to getServiceNowInstance()
17
+ * creates a fresh connection.
18
+ */
19
+ export declare function clearInstance(authAlias?: string): void;
20
+ /**
21
+ * Returns true if an HTTP response indicates a stale or dead session.
22
+ */
23
+ export declare function isRetryableResponse(response: {
24
+ status?: number;
25
+ statusText?: string;
26
+ } | null | undefined): boolean;
27
+ /**
28
+ * Executes an operation against a ServiceNow instance with automatic retry.
29
+ *
30
+ * On the first attempt, uses the (possibly cached) instance. If the operation
31
+ * throws a retryable error, the cached instance is evicted and the operation
32
+ * is retried once with a fresh connection.
33
+ *
34
+ * Usage:
35
+ * ```ts
36
+ * return withConnectionRetry(instance, async (snInstance) => {
37
+ * // ... use snInstance ...
38
+ * });
39
+ * ```
40
+ */
41
+ export declare function withConnectionRetry<T>(authAlias: string | undefined, operation: (instance: ServiceNowInstance) => Promise<T>): Promise<T>;
42
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/common/connection.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAEnB,MAAM,4BAA4B,CAAC;AA4BpC;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAmC7B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAKtD;AAgBD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GACpE,OAAO,CAIT;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,SAAS,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC,CAAC,CAAC,GACtD,OAAO,CAAC,CAAC,CAAC,CAeZ"}
@@ -0,0 +1,119 @@
1
+ import { getCredentials } from "@servicenow/sdk-cli/dist/auth/index.js";
2
+ import { ServiceNowInstance, } from "@sonisoft/now-sdk-ext-core";
3
+ /** Cache TTL — 30 minutes. ServiceNow sessions typically expire after idle time. */
4
+ const CACHE_TTL_MS = 30 * 60 * 1000;
5
+ const instanceCache = new Map();
6
+ /**
7
+ * Resolves the auth alias from the explicit parameter or the SN_AUTH_ALIAS env var.
8
+ * Throws if neither is available.
9
+ */
10
+ function resolveAlias(authAlias) {
11
+ const resolved = authAlias || process.env.SN_AUTH_ALIAS;
12
+ if (!resolved) {
13
+ throw new Error("No instance specified. Either pass an instance alias " +
14
+ '(e.g., "on my dev224436 instance") or set the SN_AUTH_ALIAS environment variable.');
15
+ }
16
+ return resolved;
17
+ }
18
+ /**
19
+ * Returns a ServiceNowInstance using stored credentials from the ServiceNow
20
+ * CLI credential store (snc configure).
21
+ *
22
+ * Resolution order: explicit authAlias parameter → SN_AUTH_ALIAS env var.
23
+ * Throws if neither is available.
24
+ *
25
+ * Instances are cached per alias with a 30-minute TTL so that repeated
26
+ * calls reuse the same session, but stale sessions are automatically
27
+ * refreshed. This allows the AI to work with multiple instances in a
28
+ * single conversation (e.g., dev224436 and prod).
29
+ */
30
+ export async function getServiceNowInstance(authAlias) {
31
+ const resolvedAlias = resolveAlias(authAlias);
32
+ const cached = instanceCache.get(resolvedAlias);
33
+ if (cached) {
34
+ const age = Date.now() - cached.createdAt;
35
+ if (age < CACHE_TTL_MS) {
36
+ return cached.instance;
37
+ }
38
+ // TTL expired — evict and create fresh
39
+ console.error(`[connection] Cache TTL expired for "${resolvedAlias}", refreshing session`);
40
+ instanceCache.delete(resolvedAlias);
41
+ }
42
+ const credential = await getCredentials(resolvedAlias);
43
+ if (!credential) {
44
+ throw new Error(`No credentials found for auth alias "${resolvedAlias}". ` +
45
+ `Run "snc configure --auth ${resolvedAlias}" to set up credentials.`);
46
+ }
47
+ const snSettings = {
48
+ alias: resolvedAlias,
49
+ credential,
50
+ };
51
+ const instance = new ServiceNowInstance(snSettings);
52
+ instanceCache.set(resolvedAlias, {
53
+ instance,
54
+ createdAt: Date.now(),
55
+ });
56
+ return instance;
57
+ }
58
+ /**
59
+ * Evicts a cached instance so the next call to getServiceNowInstance()
60
+ * creates a fresh connection.
61
+ */
62
+ export function clearInstance(authAlias) {
63
+ const resolved = authAlias || process.env.SN_AUTH_ALIAS;
64
+ if (resolved) {
65
+ instanceCache.delete(resolved);
66
+ }
67
+ }
68
+ /** Patterns that indicate a connection/session problem worth retrying. */
69
+ const RETRYABLE_PATTERNS = /ECONNREFUSED|ECONNRESET|ETIMEDOUT|EPIPE|socket hang up|fetch failed|No response|Body not XML/i;
70
+ /**
71
+ * Returns true if the error looks like a transient connection or stale-session
72
+ * problem that could be fixed by creating a fresh ServiceNowInstance.
73
+ */
74
+ function isRetryableError(error) {
75
+ if (!error)
76
+ return false;
77
+ const msg = error instanceof Error ? error.message : String(error);
78
+ return RETRYABLE_PATTERNS.test(msg);
79
+ }
80
+ /**
81
+ * Returns true if an HTTP response indicates a stale or dead session.
82
+ */
83
+ export function isRetryableResponse(response) {
84
+ if (!response || response.status == null)
85
+ return true; // no response at all
86
+ if (response.status === 401)
87
+ return true; // session expired
88
+ return false;
89
+ }
90
+ /**
91
+ * Executes an operation against a ServiceNow instance with automatic retry.
92
+ *
93
+ * On the first attempt, uses the (possibly cached) instance. If the operation
94
+ * throws a retryable error, the cached instance is evicted and the operation
95
+ * is retried once with a fresh connection.
96
+ *
97
+ * Usage:
98
+ * ```ts
99
+ * return withConnectionRetry(instance, async (snInstance) => {
100
+ * // ... use snInstance ...
101
+ * });
102
+ * ```
103
+ */
104
+ export async function withConnectionRetry(authAlias, operation) {
105
+ const snInstance = await getServiceNowInstance(authAlias);
106
+ try {
107
+ return await operation(snInstance);
108
+ }
109
+ catch (error) {
110
+ if (isRetryableError(error)) {
111
+ console.error(`[connection] Retryable error detected, refreshing session and retrying: ${error}`);
112
+ clearInstance(authAlias);
113
+ const freshInstance = await getServiceNowInstance(authAlias);
114
+ return await operation(freshInstance);
115
+ }
116
+ throw error;
117
+ }
118
+ }
119
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/common/connection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { registerExecuteScriptTool } from "./tools/execute-script.js";
5
+ import { registerRunAtfTestTool, registerRunAtfTestSuiteTool, } from "./tools/atf.js";
6
+ import { registerQueryTableTool } from "./tools/query-table.js";
7
+ import { registerFindAtfTestsTool } from "./tools/find-atf-tests.js";
8
+ import { registerQuerySyslogTool } from "./tools/query-syslog.js";
9
+ import { registerLookupAppTool } from "./tools/lookup-app.js";
10
+ import { registerLookupTableTool } from "./tools/lookup-table.js";
11
+ import { registerLookupColumnsTool } from "./tools/lookup-columns.js";
12
+ import { registerCodeSearchTool, registerListCodeSearchGroupsTool, registerListCodeSearchTablesTool, registerAddCodeSearchTableTool, } from "./tools/codesearch.js";
13
+ import { registerDiscoverTableSchemaTool, registerExplainFieldTool, registerValidateCatalogTool, } from "./tools/schema.js";
14
+ import { registerGetCurrentScopeTool, registerSetCurrentScopeTool, registerListScopedAppsTool, } from "./tools/scope.js";
15
+ import { registerGetCurrentUpdateSetTool, registerListUpdateSetsTool, registerCreateUpdateSetTool, registerSetCurrentUpdateSetTool, registerInspectUpdateSetTool, registerCloneUpdateSetTool, registerMoveUpdateSetRecordsTool, } from "./tools/updateset.js";
16
+ import { registerAddTaskCommentTool, registerAssignTaskTool, registerResolveIncidentTool, registerCloseIncidentTool, registerApproveChangeTool, registerFindTaskTool, } from "./tools/task.js";
17
+ import { registerBatchCreateRecordsTool, registerBatchUpdateRecordsTool, } from "./tools/batch.js";
18
+ import { registerListAttachmentsTool, registerGetAttachmentInfoTool, registerUploadAttachmentTool, } from "./tools/attachment.js";
19
+ import { registerGetAppDetailsTool, registerValidateAppInstallTool, registerSearchStoreAppsTool, registerListCompanyAppsTool, registerInstallStoreAppTool, registerUpdateStoreAppTool, registerInstallFromAppRepoTool, registerPublishToAppRepoTool, } from "./tools/app-manager.js";
20
+ import { registerCreateWorkflowTool } from "./tools/workflow.js";
21
+ import { registerPullScriptTool, registerPushScriptTool, } from "./tools/scriptsync.js";
22
+ import { registerCountRecordsTool, registerAggregateQueryTool, registerAggregateGroupedTool, } from "./tools/aggregate.js";
23
+ import { registerCheckInstanceHealthTool } from "./tools/health.js";
24
+ import { registerGetCmdbRelationshipsTool, registerTraverseCmdbGraphTool, } from "./tools/cmdb.js";
25
+ import { registerListInstanceTablesTool, registerListPluginsTool, } from "./tools/discovery.js";
26
+ import { registerQueryUpdateRecordsTool, registerQueryDeleteRecordsTool, } from "./tools/query-batch.js";
27
+ const server = new McpServer({
28
+ name: "now-sdk-ext-mcp",
29
+ version: "1.0.0-alpha.0",
30
+ });
31
+ // Register tools
32
+ registerExecuteScriptTool(server);
33
+ registerRunAtfTestTool(server);
34
+ registerRunAtfTestSuiteTool(server);
35
+ registerQueryTableTool(server);
36
+ registerFindAtfTestsTool(server);
37
+ registerQuerySyslogTool(server);
38
+ registerLookupAppTool(server);
39
+ registerLookupTableTool(server);
40
+ registerLookupColumnsTool(server);
41
+ registerCodeSearchTool(server);
42
+ registerListCodeSearchGroupsTool(server);
43
+ registerListCodeSearchTablesTool(server);
44
+ registerAddCodeSearchTableTool(server);
45
+ registerDiscoverTableSchemaTool(server);
46
+ registerExplainFieldTool(server);
47
+ registerValidateCatalogTool(server);
48
+ registerGetCurrentScopeTool(server);
49
+ registerSetCurrentScopeTool(server);
50
+ registerListScopedAppsTool(server);
51
+ registerGetCurrentUpdateSetTool(server);
52
+ registerListUpdateSetsTool(server);
53
+ registerCreateUpdateSetTool(server);
54
+ registerSetCurrentUpdateSetTool(server);
55
+ registerInspectUpdateSetTool(server);
56
+ registerAddTaskCommentTool(server);
57
+ registerAssignTaskTool(server);
58
+ registerResolveIncidentTool(server);
59
+ registerCloseIncidentTool(server);
60
+ registerApproveChangeTool(server);
61
+ registerFindTaskTool(server);
62
+ registerBatchCreateRecordsTool(server);
63
+ registerBatchUpdateRecordsTool(server);
64
+ registerListAttachmentsTool(server);
65
+ registerGetAttachmentInfoTool(server);
66
+ registerGetAppDetailsTool(server);
67
+ registerValidateAppInstallTool(server);
68
+ registerSearchStoreAppsTool(server);
69
+ registerListCompanyAppsTool(server);
70
+ registerInstallStoreAppTool(server);
71
+ registerUpdateStoreAppTool(server);
72
+ registerInstallFromAppRepoTool(server);
73
+ registerPublishToAppRepoTool(server);
74
+ registerCreateWorkflowTool(server);
75
+ registerPullScriptTool(server);
76
+ registerPushScriptTool(server);
77
+ registerCountRecordsTool(server);
78
+ registerAggregateQueryTool(server);
79
+ registerAggregateGroupedTool(server);
80
+ registerCheckInstanceHealthTool(server);
81
+ registerGetCmdbRelationshipsTool(server);
82
+ registerTraverseCmdbGraphTool(server);
83
+ registerListInstanceTablesTool(server);
84
+ registerListPluginsTool(server);
85
+ registerQueryUpdateRecordsTool(server);
86
+ registerQueryDeleteRecordsTool(server);
87
+ registerCloneUpdateSetTool(server);
88
+ registerMoveUpdateSetRecordsTool(server);
89
+ registerUploadAttachmentTool(server);
90
+ // Prevent the process from crashing silently on unexpected errors.
91
+ // Log to stderr (stdout is reserved for JSON-RPC).
92
+ process.on("uncaughtException", (error) => {
93
+ console.error("[now-sdk-ext-mcp] Uncaught exception:", error);
94
+ });
95
+ process.on("unhandledRejection", (reason) => {
96
+ console.error("[now-sdk-ext-mcp] Unhandled rejection:", reason);
97
+ });
98
+ // Start the server on stdio transport
99
+ async function main() {
100
+ const transport = new StdioServerTransport();
101
+ await server.connect(transport);
102
+ console.error("now-sdk-ext-mcp server running on stdio");
103
+ }
104
+ main().catch((error) => {
105
+ console.error("Fatal error:", error);
106
+ process.exit(1);
107
+ });
108
+ //# sourceMappingURL=index.js.map