@sonisoft/now-sdk-ext-mcp 4.4.0 → 4.6.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.
- package/README.md +9 -1
- package/dist/common/annotations.d.ts +8 -2
- package/dist/common/annotations.d.ts.map +1 -1
- package/dist/common/annotations.js +32 -15
- package/dist/common/annotations.js.map +1 -1
- package/dist/common/connection.d.ts.map +1 -1
- package/dist/common/connection.js +6 -2
- package/dist/common/connection.js.map +1 -1
- package/dist/common/logging.d.ts +48 -0
- package/dist/common/logging.d.ts.map +1 -0
- package/dist/common/logging.js +65 -0
- package/dist/common/logging.js.map +1 -0
- package/dist/common/progress.d.ts.map +1 -1
- package/dist/common/progress.js +5 -3
- package/dist/common/progress.js.map +1 -1
- package/dist/common/tool-packages.d.ts +39 -0
- package/dist/common/tool-packages.d.ts.map +1 -0
- package/dist/common/tool-packages.js +132 -0
- package/dist/common/tool-packages.js.map +1 -0
- package/dist/config/tool-packages.d.ts +37 -0
- package/dist/config/tool-packages.d.ts.map +1 -0
- package/dist/config/tool-packages.js +197 -0
- package/dist/config/tool-packages.js.map +1 -0
- package/dist/index.js +82 -119
- package/dist/index.js.map +1 -1
- package/dist/tools/registry.d.ts +24 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +164 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/tool-packages.d.ts +13 -0
- package/dist/tools/tool-packages.d.ts.map +1 -0
- package/dist/tools/tool-packages.js +50 -0
- package/dist/tools/tool-packages.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -398,7 +398,15 @@ Since stdout is reserved for JSON-RPC, **never use `console.log()` in server cod
|
|
|
398
398
|
|
|
399
399
|
- **`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`).
|
|
400
400
|
- **MCP Inspector** — run the server under the inspector to see all JSON-RPC messages and stderr output in real time.
|
|
401
|
-
- **File logging** —
|
|
401
|
+
- **File logging** — off by default. This server's working directory is chosen by whoever launched it, so it writes no files unless asked. Configure with environment variables:
|
|
402
|
+
|
|
403
|
+
| Variable | Effect |
|
|
404
|
+
| --- | --- |
|
|
405
|
+
| `NEX_LOG_FILE` | `1`/`true` writes a log file to `$XDG_STATE_HOME/now-sdk-ext/logs` (`~/.local/state/...`) |
|
|
406
|
+
| `NEX_LOG_DIR` | Write log files to this directory instead. Implies `NEX_LOG_FILE` |
|
|
407
|
+
| `NEX_LOG_LEVEL` | `error`, `warn`, `info` (default), `http`, `verbose`, `debug`, `silly` |
|
|
408
|
+
|
|
409
|
+
Diagnostics always go to **stderr**, never stdout — stdout carries JSON-RPC. Credential material is stripped from both metadata and message text before anything is written.
|
|
402
410
|
|
|
403
411
|
### Code Conventions
|
|
404
412
|
|
|
@@ -27,8 +27,14 @@
|
|
|
27
27
|
* - `idempotentHint` — repeating the call with identical arguments leaves the
|
|
28
28
|
* same end state. True for "set this field to X"; false for "append a comment"
|
|
29
29
|
* or "create a record", which accumulate.
|
|
30
|
-
* - `openWorldHint` —
|
|
31
|
-
*
|
|
30
|
+
* - `openWorldHint` — TRUE FOR EVERY TOOL HERE, and set explicitly rather than
|
|
31
|
+
* left to the spec's default. All of them talk to a live ServiceNow instance,
|
|
32
|
+
* which is exactly what the spec means by an open world. It was tempting to use
|
|
33
|
+
* it to single out arbitrary code execution, but that would have been wrong in
|
|
34
|
+
* both directions: the spec default is already `true`, so the marking would
|
|
35
|
+
* have been a no-op, and leaving it off the other 80-odd would have implied
|
|
36
|
+
* they were closed-world when they are not. MCP has no hint for "runs
|
|
37
|
+
* caller-supplied logic" — the tool description carries that.
|
|
32
38
|
*/
|
|
33
39
|
export interface ToolAnnotations {
|
|
34
40
|
readOnlyHint?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/common/annotations.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/common/annotations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,MAAM,WAAW,eAAe;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AA2BD,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAwJ5D,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAchE"}
|
|
@@ -27,27 +27,39 @@
|
|
|
27
27
|
* - `idempotentHint` — repeating the call with identical arguments leaves the
|
|
28
28
|
* same end state. True for "set this field to X"; false for "append a comment"
|
|
29
29
|
* or "create a record", which accumulate.
|
|
30
|
-
* - `openWorldHint` —
|
|
31
|
-
*
|
|
30
|
+
* - `openWorldHint` — TRUE FOR EVERY TOOL HERE, and set explicitly rather than
|
|
31
|
+
* left to the spec's default. All of them talk to a live ServiceNow instance,
|
|
32
|
+
* which is exactly what the spec means by an open world. It was tempting to use
|
|
33
|
+
* it to single out arbitrary code execution, but that would have been wrong in
|
|
34
|
+
* both directions: the spec default is already `true`, so the marking would
|
|
35
|
+
* have been a no-op, and leaving it off the other 80-odd would have implied
|
|
36
|
+
* they were closed-world when they are not. MCP has no hint for "runs
|
|
37
|
+
* caller-supplied logic" — the tool description carries that.
|
|
32
38
|
*/
|
|
33
39
|
/** Cannot modify anything. */
|
|
34
|
-
const READ = { readOnlyHint: true };
|
|
40
|
+
const READ = { readOnlyHint: true, openWorldHint: true };
|
|
35
41
|
/** Adds something new; nothing pre-existing is lost. Repeating accumulates. */
|
|
36
|
-
const CREATE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false };
|
|
42
|
+
const CREATE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
|
|
37
43
|
/** Sets state to a given value. Nothing is destroyed; repeating is a no-op. */
|
|
38
|
-
const SET = { readOnlyHint: false, destructiveHint: false, idempotentHint: true };
|
|
44
|
+
const SET = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true };
|
|
39
45
|
/** Overwrites or removes data that already existed. Repeating settles. */
|
|
40
|
-
const OVERWRITE = { readOnlyHint: false, destructiveHint: true, idempotentHint: true };
|
|
46
|
+
const OVERWRITE = { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true };
|
|
41
47
|
/** Overwrites or removes, and repeating does NOT settle. */
|
|
42
|
-
const OVERWRITE_ONCE = { readOnlyHint: false, destructiveHint: true, idempotentHint: false };
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const OVERWRITE_ONCE = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true };
|
|
49
|
+
/**
|
|
50
|
+
* Runs caller-supplied logic — a script or a flow someone else authored.
|
|
51
|
+
*
|
|
52
|
+
* Structurally identical to OVERWRITE_ONCE, because MCP has no hint that means
|
|
53
|
+
* "unbounded effect". Kept as a separate name purely so this table records which
|
|
54
|
+
* tools are in that category; the warning that actually reaches the model is in
|
|
55
|
+
* the tool description.
|
|
56
|
+
*/
|
|
57
|
+
const ARBITRARY = OVERWRITE_ONCE;
|
|
50
58
|
export const TOOL_ANNOTATIONS = {
|
|
59
|
+
// ---- meta: reports this server's own configuration, touches no instance.
|
|
60
|
+
// Registered outside TOOL_REGISTRY because it is always available regardless
|
|
61
|
+
// of the active package.
|
|
62
|
+
list_tool_packages: READ,
|
|
51
63
|
// ---- aggregate: server-side counts, no writes
|
|
52
64
|
count_records: READ,
|
|
53
65
|
aggregate_query: READ,
|
|
@@ -183,7 +195,12 @@ export const TOOL_ANNOTATIONS = {
|
|
|
183
195
|
* Failing at registration makes that impossible.
|
|
184
196
|
*/
|
|
185
197
|
export function annotationsFor(toolName) {
|
|
186
|
-
|
|
198
|
+
// hasOwnProperty, not bracket access: TOOL_ANNOTATIONS["constructor"] would
|
|
199
|
+
// otherwise return the Object constructor — truthy — and this would hand back
|
|
200
|
+
// a function instead of throwing.
|
|
201
|
+
const annotations = Object.prototype.hasOwnProperty.call(TOOL_ANNOTATIONS, toolName)
|
|
202
|
+
? TOOL_ANNOTATIONS[toolName]
|
|
203
|
+
: undefined;
|
|
187
204
|
if (!annotations) {
|
|
188
205
|
throw new Error(`No annotations defined for tool "${toolName}". Add it to TOOL_ANNOTATIONS ` +
|
|
189
206
|
`in src/common/annotations.ts — every tool must declare whether it is read-only.`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"annotations.js","sourceRoot":"","sources":["../../src/common/annotations.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"annotations.js","sourceRoot":"","sources":["../../src/common/annotations.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/common/connection.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAEnB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/common/connection.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAEnB,MAAM,4BAA4B,CAAC;AA+BpC;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAgD7B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAKtD;AAED,0EAA0E;AAC1E,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,6BAA6B,CAAC;AAEjE;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAeD;;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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { getCredentials } from "@servicenow/sdk-cli/dist/auth/index.js";
|
|
2
2
|
import { ServiceNowInstance, } from "@sonisoft/now-sdk-ext-core";
|
|
3
|
+
import { getLogger } from "./logging.js";
|
|
4
|
+
const log = getLogger("connection");
|
|
3
5
|
/** Cache TTL — 30 minutes. ServiceNow sessions typically expire after idle time. */
|
|
4
6
|
const CACHE_TTL_MS = 30 * 60 * 1000;
|
|
5
7
|
const instanceCache = new Map();
|
|
@@ -36,7 +38,7 @@ export async function getServiceNowInstance(authAlias) {
|
|
|
36
38
|
return cached.instance;
|
|
37
39
|
}
|
|
38
40
|
// TTL expired — evict and create fresh
|
|
39
|
-
|
|
41
|
+
log.debug("Cache TTL expired, refreshing session", { alias: resolvedAlias });
|
|
40
42
|
instanceCache.delete(resolvedAlias);
|
|
41
43
|
}
|
|
42
44
|
const credential = await getCredentials(resolvedAlias);
|
|
@@ -134,7 +136,9 @@ export async function withConnectionRetry(authAlias, operation) {
|
|
|
134
136
|
}
|
|
135
137
|
catch (error) {
|
|
136
138
|
if (isRetryableError(error)) {
|
|
137
|
-
|
|
139
|
+
// The error went into the message before, unredacted — an auth or HTTP failure
|
|
140
|
+
// here carries a live session. As metadata it goes through redaction.
|
|
141
|
+
log.warn("Retryable error, refreshing session and retrying", { error });
|
|
138
142
|
clearInstance(authAlias);
|
|
139
143
|
const freshInstance = await getServiceNowInstance(authAlias);
|
|
140
144
|
return await operation(freshInstance);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/common/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EACL,kBAAkB,GAEnB,MAAM,4BAA4B,CAAC;AAQpC,oFAAoF;AACpF,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEpC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAsB,CAAC;AAEpD;;;GAGG;AACH,SAAS,YAAY,CAAC,SAAkB;IACtC,MAAM,QAAQ,GAAG,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IACxD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,uDAAuD;YACrD,oFAAoF,CACvF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,SAAkB;IAElB,MAAM,aAAa,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAChD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;QAC1C,IAAI,GAAG,GAAG,YAAY,EAAE,CAAC;YACvB,OAAO,MAAM,CAAC,QAAQ,CAAC;QACzB,CAAC;QACD,uCAAuC;QACvC,
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/common/connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging setup for the MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Two things make this different from the CLI.
|
|
5
|
+
*
|
|
6
|
+
* First, there are no flags — an MCP server is launched by a client with an argv it
|
|
7
|
+
* does not control — so configuration is environment only: NEX_LOG_FILE, NEX_LOG_DIR,
|
|
8
|
+
* NEX_LOG_LEVEL, read by core itself.
|
|
9
|
+
*
|
|
10
|
+
* Second, this process is the reason file logging had to become opt-in at all. The
|
|
11
|
+
* server's working directory is chosen by whoever launched it, and core used to create
|
|
12
|
+
* ./logs/ there on import, unconditionally, without this server ever asking to log
|
|
13
|
+
* anything (NEX-3).
|
|
14
|
+
*
|
|
15
|
+
* stdout is the JSON-RPC transport. Everything here goes to stderr, and nothing in this
|
|
16
|
+
* process may ever write to fd 1.
|
|
17
|
+
*/
|
|
18
|
+
import { Logger } from "@sonisoft/now-sdk-ext-core";
|
|
19
|
+
/**
|
|
20
|
+
* Configures process-wide logging. Call once, before anything constructs a manager.
|
|
21
|
+
*
|
|
22
|
+
* Explicit rather than relying on core reading the environment on its own: the
|
|
23
|
+
* stderr-only guarantee is this server's to make, so it should be stated here where it
|
|
24
|
+
* can be read and tested, not inherited by accident.
|
|
25
|
+
*/
|
|
26
|
+
export declare function initLogging(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Returns a named logger.
|
|
29
|
+
*
|
|
30
|
+
* Safe to call before `initLogging()`, and several modules do: ES modules evaluate
|
|
31
|
+
* every import before the importing module's own top-level statements, so the
|
|
32
|
+
* `const log = getLogger(...)` in connection.ts, progress.ts and tool-packages.ts all
|
|
33
|
+
* run BEFORE the `initLogging()` call in index.ts, even though it sits above them in
|
|
34
|
+
* source order.
|
|
35
|
+
*
|
|
36
|
+
* That is fine because nothing is decided at construction time. `Logger` only stores a
|
|
37
|
+
* label; core builds the underlying winston logger on the first WRITE, resolves config
|
|
38
|
+
* lazily at that point, and rebuilds when configuration changes. So a logger built
|
|
39
|
+
* during import picks up whatever `initLogging()` sets, provided nothing logs during
|
|
40
|
+
* module evaluation — and nothing does.
|
|
41
|
+
*
|
|
42
|
+
* This is not left to inspection: the "still reports startup breadcrumbs on stderr"
|
|
43
|
+
* case in test/unit/common/logging.test.ts only passes if `consoleLevel: info` from
|
|
44
|
+
* `initLogging()` reached a logger that was constructed before it ran. Core's default
|
|
45
|
+
* is `warn`, which would suppress those lines.
|
|
46
|
+
*/
|
|
47
|
+
export declare function getLogger(name: string): Logger;
|
|
48
|
+
//# sourceMappingURL=logging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../src/common/logging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,MAAM,EAAoB,MAAM,4BAA4B,CAAC;AAItE;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAelC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging setup for the MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Two things make this different from the CLI.
|
|
5
|
+
*
|
|
6
|
+
* First, there are no flags — an MCP server is launched by a client with an argv it
|
|
7
|
+
* does not control — so configuration is environment only: NEX_LOG_FILE, NEX_LOG_DIR,
|
|
8
|
+
* NEX_LOG_LEVEL, read by core itself.
|
|
9
|
+
*
|
|
10
|
+
* Second, this process is the reason file logging had to become opt-in at all. The
|
|
11
|
+
* server's working directory is chosen by whoever launched it, and core used to create
|
|
12
|
+
* ./logs/ there on import, unconditionally, without this server ever asking to log
|
|
13
|
+
* anything (NEX-3).
|
|
14
|
+
*
|
|
15
|
+
* stdout is the JSON-RPC transport. Everything here goes to stderr, and nothing in this
|
|
16
|
+
* process may ever write to fd 1.
|
|
17
|
+
*/
|
|
18
|
+
import { Logger, configureLogging } from "@sonisoft/now-sdk-ext-core";
|
|
19
|
+
let configured = false;
|
|
20
|
+
/**
|
|
21
|
+
* Configures process-wide logging. Call once, before anything constructs a manager.
|
|
22
|
+
*
|
|
23
|
+
* Explicit rather than relying on core reading the environment on its own: the
|
|
24
|
+
* stderr-only guarantee is this server's to make, so it should be stated here where it
|
|
25
|
+
* can be read and tested, not inherited by accident.
|
|
26
|
+
*/
|
|
27
|
+
export function initLogging() {
|
|
28
|
+
if (configured) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
configured = true;
|
|
32
|
+
configureLogging({
|
|
33
|
+
// stderr IS this server's log channel — an operator reading `docker logs` or a
|
|
34
|
+
// client's server pane sees only this. `warn` (core's default, right for a CLI
|
|
35
|
+
// where the terminal is the user's) would silence the startup breadcrumbs that
|
|
36
|
+
// say which tool package loaded and whether credentials resolved.
|
|
37
|
+
consoleLevel: process.env.NEX_LOG_LEVEL?.trim() || "info",
|
|
38
|
+
// File logging stays off unless NEX_LOG_FILE/NEX_LOG_DIR asks for it. Core
|
|
39
|
+
// resolves those; naming them here would override an operator's NEX_LOG_FILE=0.
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns a named logger.
|
|
44
|
+
*
|
|
45
|
+
* Safe to call before `initLogging()`, and several modules do: ES modules evaluate
|
|
46
|
+
* every import before the importing module's own top-level statements, so the
|
|
47
|
+
* `const log = getLogger(...)` in connection.ts, progress.ts and tool-packages.ts all
|
|
48
|
+
* run BEFORE the `initLogging()` call in index.ts, even though it sits above them in
|
|
49
|
+
* source order.
|
|
50
|
+
*
|
|
51
|
+
* That is fine because nothing is decided at construction time. `Logger` only stores a
|
|
52
|
+
* label; core builds the underlying winston logger on the first WRITE, resolves config
|
|
53
|
+
* lazily at that point, and rebuilds when configuration changes. So a logger built
|
|
54
|
+
* during import picks up whatever `initLogging()` sets, provided nothing logs during
|
|
55
|
+
* module evaluation — and nothing does.
|
|
56
|
+
*
|
|
57
|
+
* This is not left to inspection: the "still reports startup breadcrumbs on stderr"
|
|
58
|
+
* case in test/unit/common/logging.test.ts only passes if `consoleLevel: info` from
|
|
59
|
+
* `initLogging()` reached a logger that was constructed before it ran. Core's default
|
|
60
|
+
* is `warn`, which would suppress those lines.
|
|
61
|
+
*/
|
|
62
|
+
export function getLogger(name) {
|
|
63
|
+
return new Logger(name);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=logging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../../src/common/logging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,IAAI,UAAU,GAAG,KAAK,CAAC;AAEvB;;;;;;GAMG;AACH,MAAM,UAAU,WAAW;IACvB,IAAI,UAAU,EAAE,CAAC;QACb,OAAO;IACX,CAAC;IACD,UAAU,GAAG,IAAI,CAAC;IAElB,gBAAgB,CAAC;QACb,+EAA+E;QAC/E,+EAA+E;QAC/E,+EAA+E;QAC/E,kEAAkE;QAClE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,MAAM;QACzD,2EAA2E;QAC3E,gFAAgF;KACnF,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IAClC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/common/progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AACxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/common/progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AACxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAM5F,MAAM,MAAM,SAAS,GAAG,mBAAmB,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;AAE/E,iFAAiF;AACjF,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEzD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,gBAAgB,GAAG,SAAS,CA8B3F"}
|
package/dist/common/progress.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* left undefined. Inventing a percentage from a message count would be a lie that
|
|
17
17
|
* gets less accurate the longer an operation runs.
|
|
18
18
|
*/
|
|
19
|
+
import { getLogger } from "./logging.js";
|
|
20
|
+
const log = getLogger("progress");
|
|
19
21
|
/**
|
|
20
22
|
* Returns an `onProgress` callback to hand to core, or `undefined` when the client
|
|
21
23
|
* did not request progress.
|
|
@@ -48,9 +50,9 @@ export function progressReporter(extra) {
|
|
|
48
50
|
message,
|
|
49
51
|
},
|
|
50
52
|
}).catch((error) => {
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
53
|
+
// stderr, never stdout: stdout is the JSON-RPC transport. Via the logger
|
|
54
|
+
// so the error is redacted rather than stringified into the message.
|
|
55
|
+
log.debug("Failed to send progress notification", { error });
|
|
54
56
|
});
|
|
55
57
|
};
|
|
56
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/common/progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/common/progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;AAOlC;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA4B;IACzD,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC;IAC1C,2EAA2E;IAC3E,oEAAoE;IACpE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QACvC,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,4EAA4E;IAC5E,uEAAuE;IACvE,uBAAuB;IACvB,MAAM,IAAI,GAAG,KAAK,CAAC;IACnB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,OAAO,CAAC,OAAe,EAAE,EAAE;QACvB,IAAI,IAAI,CAAC,CAAC;QACV,4EAA4E;QAC5E,yEAAyE;QACzE,KAAK,IAAI,CAAC,gBAAgB,CAAC;YACvB,MAAM,EAAE,wBAAwB;YAChC,MAAM,EAAE;gBACJ,aAAa,EAAE,KAAK;gBACpB,QAAQ,EAAE,IAAI;gBACd,OAAO;aACV;SACJ,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACxB,yEAAyE;YACzE,qEAAqE;YACrE,GAAG,CAAC,KAAK,CAAC,sCAAsC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves MCP_TOOL_PACKAGE into the set of tools to register.
|
|
3
|
+
*
|
|
4
|
+
* Every failure mode here degrades toward MORE tools, never fewer. A typo in the
|
|
5
|
+
* env var that silently exposed a smaller surface would look like the server was
|
|
6
|
+
* broken — tools simply missing, with no error to search for. Falling back to
|
|
7
|
+
* `full` and saying so on stderr is the recoverable direction.
|
|
8
|
+
*/
|
|
9
|
+
export interface ResolvedPackage {
|
|
10
|
+
/** Package names that were actually applied. */
|
|
11
|
+
names: string[];
|
|
12
|
+
/** Tool names to register, already filtered to ones that exist. */
|
|
13
|
+
tools: string[];
|
|
14
|
+
/** Requested names that matched no package. */
|
|
15
|
+
unknownPackages: string[];
|
|
16
|
+
/** Tools named by a package that this server does not have. */
|
|
17
|
+
unknownTools: string[];
|
|
18
|
+
/** True when the requested selection could not be used and `full` was substituted. */
|
|
19
|
+
fellBack: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolves a selection into a concrete tool set.
|
|
23
|
+
*
|
|
24
|
+
* `selection` is the raw env value: one package name, or several comma-separated,
|
|
25
|
+
* in which case the result is their UNION. Union rather than intersection because
|
|
26
|
+
* combining packages is how someone expresses "I do service desk work AND change
|
|
27
|
+
* approvals" — an intersection of two role packages is almost always empty.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveToolPackage(selection: string | undefined): ResolvedPackage;
|
|
30
|
+
/**
|
|
31
|
+
* Reports the resolution on stderr.
|
|
32
|
+
*
|
|
33
|
+
* stderr, never stdout — stdout is the JSON-RPC transport, and a stray byte
|
|
34
|
+
* there breaks the client's parser. Goes through the logger so it obeys
|
|
35
|
+
* NEX_LOG_LEVEL and reaches the log file when one is configured; these are the
|
|
36
|
+
* breadcrumbs that explain why a client sees the tools it sees.
|
|
37
|
+
*/
|
|
38
|
+
export declare function reportResolution(resolved: ResolvedPackage): void;
|
|
39
|
+
//# sourceMappingURL=tool-packages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-packages.d.ts","sourceRoot":"","sources":["../../src/common/tool-packages.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH,MAAM,WAAW,eAAe;IAC5B,gDAAgD;IAChD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,mEAAmE;IACnE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,+CAA+C;IAC/C,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,+DAA+D;IAC/D,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,sFAAsF;IACtF,QAAQ,EAAE,OAAO,CAAC;CACrB;AAgBD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,eAAe,CAuEjF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAuBhE"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves MCP_TOOL_PACKAGE into the set of tools to register.
|
|
3
|
+
*
|
|
4
|
+
* Every failure mode here degrades toward MORE tools, never fewer. A typo in the
|
|
5
|
+
* env var that silently exposed a smaller surface would look like the server was
|
|
6
|
+
* broken — tools simply missing, with no error to search for. Falling back to
|
|
7
|
+
* `full` and saying so on stderr is the recoverable direction.
|
|
8
|
+
*/
|
|
9
|
+
import { TOOL_PACKAGES, DEFAULT_PACKAGE } from "../config/tool-packages.js";
|
|
10
|
+
import { TOOL_ANNOTATIONS } from "./annotations.js";
|
|
11
|
+
import { allToolNames } from "../tools/registry.js";
|
|
12
|
+
import { getLogger } from "./logging.js";
|
|
13
|
+
const log = getLogger("tool-packages");
|
|
14
|
+
/** Sentinel in a package's `tools` meaning "everything". */
|
|
15
|
+
const ALL = "*";
|
|
16
|
+
/** Sentinel meaning "every tool annotated readOnlyHint: true". */
|
|
17
|
+
const READONLY = "@readonly";
|
|
18
|
+
/**
|
|
19
|
+
* Tools annotated as read-only. Derived, so it cannot drift from the annotations.
|
|
20
|
+
*
|
|
21
|
+
* Filtered to the registry deliberately. TOOL_ANNOTATIONS covers one tool the
|
|
22
|
+
* registry does not — `list_tool_packages`, which is registered unconditionally —
|
|
23
|
+
* and returning it here would put a name in the resolved set that has no
|
|
24
|
+
* registrar, crashing startup on `TOOL_REGISTRY[name](server)`.
|
|
25
|
+
*/
|
|
26
|
+
function readonlyTools(known) {
|
|
27
|
+
return Object.entries(TOOL_ANNOTATIONS)
|
|
28
|
+
.filter(([name, a]) => a.readOnlyHint === true && known.has(name))
|
|
29
|
+
.map(([name]) => name);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a selection into a concrete tool set.
|
|
33
|
+
*
|
|
34
|
+
* `selection` is the raw env value: one package name, or several comma-separated,
|
|
35
|
+
* in which case the result is their UNION. Union rather than intersection because
|
|
36
|
+
* combining packages is how someone expresses "I do service desk work AND change
|
|
37
|
+
* approvals" — an intersection of two role packages is almost always empty.
|
|
38
|
+
*/
|
|
39
|
+
export function resolveToolPackage(selection) {
|
|
40
|
+
const known = new Set(allToolNames());
|
|
41
|
+
const requested = (selection ?? "")
|
|
42
|
+
.split(",")
|
|
43
|
+
.map((s) => s.trim())
|
|
44
|
+
.filter((s) => s.length > 0);
|
|
45
|
+
if (requested.length === 0) {
|
|
46
|
+
return {
|
|
47
|
+
names: [DEFAULT_PACKAGE],
|
|
48
|
+
tools: allToolNames(),
|
|
49
|
+
unknownPackages: [],
|
|
50
|
+
unknownTools: [],
|
|
51
|
+
fellBack: false,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const names = [];
|
|
55
|
+
const unknownPackages = [];
|
|
56
|
+
const selected = new Set();
|
|
57
|
+
const namedButMissing = new Set();
|
|
58
|
+
for (const name of requested) {
|
|
59
|
+
// hasOwnProperty, not bracket access. TOOL_PACKAGES is an object literal,
|
|
60
|
+
// so TOOL_PACKAGES["constructor"] returns the Object constructor — truthy,
|
|
61
|
+
// with no `.tools` — and `for (const t of pkg.tools)` then throws at module
|
|
62
|
+
// load, before the uncaughtException handler exists. MCP_TOOL_PACKAGE=
|
|
63
|
+
// constructor would crash the server rather than falling back, which is the
|
|
64
|
+
// exact opposite of this file's stated invariant.
|
|
65
|
+
const pkg = Object.prototype.hasOwnProperty.call(TOOL_PACKAGES, name)
|
|
66
|
+
? TOOL_PACKAGES[name]
|
|
67
|
+
: undefined;
|
|
68
|
+
if (!pkg) {
|
|
69
|
+
unknownPackages.push(name);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
names.push(name);
|
|
73
|
+
for (const tool of pkg.tools) {
|
|
74
|
+
if (tool === ALL) {
|
|
75
|
+
allToolNames().forEach((t) => selected.add(t));
|
|
76
|
+
}
|
|
77
|
+
else if (tool === READONLY) {
|
|
78
|
+
readonlyTools(known).forEach((t) => selected.add(t));
|
|
79
|
+
}
|
|
80
|
+
else if (known.has(tool)) {
|
|
81
|
+
selected.add(tool);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Expected for packages naming tools from open tickets; still
|
|
85
|
+
// reported, because the same path catches a genuine typo.
|
|
86
|
+
namedButMissing.add(tool);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Nothing usable was requested. Fall back rather than register an empty
|
|
91
|
+
// server: a server with no tools is indistinguishable from a broken one.
|
|
92
|
+
if (names.length === 0) {
|
|
93
|
+
return {
|
|
94
|
+
names: [DEFAULT_PACKAGE],
|
|
95
|
+
tools: allToolNames(),
|
|
96
|
+
unknownPackages,
|
|
97
|
+
unknownTools: [],
|
|
98
|
+
fellBack: true,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
names,
|
|
103
|
+
tools: [...selected].sort(),
|
|
104
|
+
unknownPackages,
|
|
105
|
+
unknownTools: [...namedButMissing].sort(),
|
|
106
|
+
fellBack: false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Reports the resolution on stderr.
|
|
111
|
+
*
|
|
112
|
+
* stderr, never stdout — stdout is the JSON-RPC transport, and a stray byte
|
|
113
|
+
* there breaks the client's parser. Goes through the logger so it obeys
|
|
114
|
+
* NEX_LOG_LEVEL and reaches the log file when one is configured; these are the
|
|
115
|
+
* breadcrumbs that explain why a client sees the tools it sees.
|
|
116
|
+
*/
|
|
117
|
+
export function reportResolution(resolved) {
|
|
118
|
+
const { names, tools, unknownPackages, unknownTools, fellBack } = resolved;
|
|
119
|
+
if (unknownPackages.length > 0) {
|
|
120
|
+
const available = Object.keys(TOOL_PACKAGES).sort().join(", ");
|
|
121
|
+
log.warn(`Unknown tool package(s): ${unknownPackages.join(", ")}. ` +
|
|
122
|
+
`Available: ${available}.` +
|
|
123
|
+
(fellBack ? ` Falling back to "${DEFAULT_PACKAGE}".` : ""));
|
|
124
|
+
}
|
|
125
|
+
if (unknownTools.length > 0) {
|
|
126
|
+
log.warn(`${unknownTools.length} tool(s) named by a package do not exist ` +
|
|
127
|
+
`on this server and were skipped: ${unknownTools.join(", ")}. ` +
|
|
128
|
+
`This is expected for tools from work that has not landed yet.`);
|
|
129
|
+
}
|
|
130
|
+
log.info(`Active tool package(s): ${names.join(", ")} — ${tools.length} of ${allToolNames().length} tools registered`);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=tool-packages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-packages.js","sourceRoot":"","sources":["../../src/common/tool-packages.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,GAAG,GAAG,SAAS,CAAC,eAAe,CAAC,CAAC;AAEvC,4DAA4D;AAC5D,MAAM,GAAG,GAAG,GAAG,CAAC;AAEhB,kEAAkE;AAClE,MAAM,QAAQ,GAAG,WAAW,CAAC;AAe7B;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,KAAkB;IACrC,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC;SAClC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;SACjE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAA6B;IAC5D,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC;SAC9B,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEjC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACH,KAAK,EAAE,CAAC,eAAe,CAAC;YACxB,KAAK,EAAE,YAAY,EAAE;YACrB,eAAe,EAAE,EAAE;YACnB,YAAY,EAAE,EAAE;YAChB,QAAQ,EAAE,KAAK;SAClB,CAAC;IACN,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;IAE1C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC3B,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,uEAAuE;QACvE,4EAA4E;QAC5E,kDAAkD;QAClD,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC;YACjE,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC;YACrB,CAAC,CAAC,SAAS,CAAC;QAChB,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3B,SAAS;QACb,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBACf,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACnD,CAAC;iBAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3B,aAAa,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,CAAC;iBAAM,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACJ,8DAA8D;gBAC9D,0DAA0D;gBAC1D,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;QACL,CAAC;IACL,CAAC;IAED,wEAAwE;IACxE,yEAAyE;IACzE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;YACH,KAAK,EAAE,CAAC,eAAe,CAAC;YACxB,KAAK,EAAE,YAAY,EAAE;YACrB,eAAe;YACf,YAAY,EAAE,EAAE;YAChB,QAAQ,EAAE,IAAI;SACjB,CAAC;IACN,CAAC;IAED,OAAO;QACH,KAAK;QACL,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,EAAE;QAC3B,eAAe;QACf,YAAY,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,IAAI,EAAE;QACzC,QAAQ,EAAE,KAAK;KAClB,CAAC;AACN,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAyB;IACtD,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC;IAE3E,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,GAAG,CAAC,IAAI,CACJ,4BAA4B,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YACtD,cAAc,SAAS,GAAG;YAC1B,CAAC,QAAQ,CAAC,CAAC,CAAC,qBAAqB,eAAe,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CACjE,CAAC;IACN,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,GAAG,CAAC,IAAI,CACJ,GAAG,YAAY,CAAC,MAAM,2CAA2C;YAC7D,oCAAoC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAC/D,+DAA+D,CACtE,CAAC;IACN,CAAC;IAED,GAAG,CAAC,IAAI,CACJ,2BAA2B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,MAAM,OAAO,YAAY,EAAE,CAAC,MAAM,mBAAmB,CAC/G,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool packages — named subsets of the tool surface.
|
|
3
|
+
*
|
|
4
|
+
* All 86 tools are registered by default, and their descriptions are long by
|
|
5
|
+
* design (`execute_script`'s runs to about thirteen lines, because it has to
|
|
6
|
+
* teach scoped-vs-global name qualification). That is a fixed cost paid on every
|
|
7
|
+
* session, and model tool-selection accuracy degrades as the option count grows.
|
|
8
|
+
* A user doing service-desk work does not need Flow Designer, ATF, or app
|
|
9
|
+
* publishing in front of them.
|
|
10
|
+
*
|
|
11
|
+
* Selected with MCP_TOOL_PACKAGE. Unset means "full", so the default behaviour
|
|
12
|
+
* is unchanged.
|
|
13
|
+
*
|
|
14
|
+
* ---
|
|
15
|
+
*
|
|
16
|
+
* ON NAMING TOOLS THAT DO NOT EXIST YET
|
|
17
|
+
*
|
|
18
|
+
* Packages may reference tools this server does not have. That is deliberate,
|
|
19
|
+
* not sloppiness: NEX-38 (user and group tools), NEX-39 (incident and change
|
|
20
|
+
* tools) and NEX-46 (workflow execution) are all open, and their tools belong in
|
|
21
|
+
* these packages the day they land. Naming them now means the package is already
|
|
22
|
+
* right rather than something someone has to remember to revisit.
|
|
23
|
+
*
|
|
24
|
+
* Unknown names are reported once at startup and skipped. They are a warning
|
|
25
|
+
* rather than an error precisely because forward references are expected — but
|
|
26
|
+
* they are still surfaced, because the same warning catches a genuine typo.
|
|
27
|
+
*/
|
|
28
|
+
export interface ToolPackage {
|
|
29
|
+
/** Shown by list_tool_packages, so write it for whoever picks a package. */
|
|
30
|
+
description: string;
|
|
31
|
+
/** Tool names, or ["*"] for everything. May name tools that do not exist yet. */
|
|
32
|
+
tools: string[];
|
|
33
|
+
}
|
|
34
|
+
export declare const TOOL_PACKAGES: Record<string, ToolPackage>;
|
|
35
|
+
/** The package used when MCP_TOOL_PACKAGE is unset or unusable. */
|
|
36
|
+
export declare const DEFAULT_PACKAGE = "full";
|
|
37
|
+
//# sourceMappingURL=tool-packages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-packages.d.ts","sourceRoot":"","sources":["../../src/config/tool-packages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,WAAW,WAAW;IACxB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,KAAK,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAkLrD,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,eAAe,SAAS,CAAC"}
|