@novorb/mcp-core 2.1.1 → 3.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.
- package/README.md +82 -161
- package/bin/mcp-core-jest.mjs +112 -0
- package/dist/auth.d.ts +57 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +74 -0
- package/dist/auth.js.map +1 -0
- package/dist/bootstrap.d.ts +129 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +206 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/client-input.d.ts +72 -0
- package/dist/client-input.d.ts.map +1 -0
- package/dist/client-input.js +109 -0
- package/dist/client-input.js.map +1 -0
- package/dist/credential-checks.d.ts +80 -0
- package/dist/credential-checks.d.ts.map +1 -0
- package/dist/credential-checks.js +189 -0
- package/dist/credential-checks.js.map +1 -0
- package/dist/credential-health.d.ts +12 -0
- package/dist/credential-health.d.ts.map +1 -0
- package/dist/credential-health.js +12 -0
- package/dist/credential-health.js.map +1 -0
- package/dist/credential-report.d.ts +46 -0
- package/dist/credential-report.d.ts.map +1 -0
- package/dist/credential-report.js +135 -0
- package/dist/credential-report.js.map +1 -0
- package/dist/credential-status-tool.d.ts +20 -0
- package/dist/credential-status-tool.d.ts.map +1 -0
- package/dist/credential-status-tool.js +33 -0
- package/dist/credential-status-tool.js.map +1 -0
- package/dist/credentials.d.ts +288 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +652 -0
- package/dist/credentials.js.map +1 -0
- package/dist/express-peers.d.ts +29 -0
- package/dist/express-peers.d.ts.map +1 -0
- package/dist/express-peers.js +36 -0
- package/dist/express-peers.js.map +1 -0
- package/dist/express.d.ts +74 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +164 -0
- package/dist/express.js.map +1 -0
- package/dist/http-client.d.ts +278 -43
- package/dist/http-client.d.ts.map +1 -1
- package/dist/http-client.js +620 -182
- package/dist/http-client.js.map +1 -1
- package/dist/index.d.ts +27 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -10
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +28 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +37 -0
- package/dist/limits.js.map +1 -0
- package/dist/list-pagination.d.ts +41 -0
- package/dist/list-pagination.d.ts.map +1 -0
- package/dist/list-pagination.js +104 -0
- package/dist/list-pagination.js.map +1 -0
- package/dist/net.d.ts +112 -0
- package/dist/net.d.ts.map +1 -0
- package/dist/net.js +490 -0
- package/dist/net.js.map +1 -0
- package/dist/notifications.d.ts +78 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +173 -0
- package/dist/notifications.js.map +1 -0
- package/dist/pagination.d.ts +236 -5
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +393 -6
- package/dist/pagination.js.map +1 -1
- package/dist/prompts.d.ts +70 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +109 -0
- package/dist/prompts.js.map +1 -0
- package/dist/protocol-errors.d.ts +15 -0
- package/dist/protocol-errors.d.ts.map +1 -0
- package/dist/protocol-errors.js +28 -0
- package/dist/protocol-errors.js.map +1 -0
- package/dist/protocol-logging.d.ts +37 -0
- package/dist/protocol-logging.d.ts.map +1 -0
- package/dist/protocol-logging.js +77 -0
- package/dist/protocol-logging.js.map +1 -0
- package/dist/rate-limiter.d.ts +36 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +79 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/request-state.d.ts +74 -0
- package/dist/request-state.d.ts.map +1 -0
- package/dist/request-state.js +190 -0
- package/dist/request-state.js.map +1 -0
- package/dist/resources.d.ts +181 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +302 -0
- package/dist/resources.js.map +1 -0
- package/dist/result-limits.d.ts +80 -0
- package/dist/result-limits.d.ts.map +1 -0
- package/dist/result-limits.js +248 -0
- package/dist/result-limits.js.map +1 -0
- package/dist/retry.d.ts +79 -23
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +170 -57
- package/dist/retry.js.map +1 -1
- package/dist/sdk/client-stdio.d.ts +6 -0
- package/dist/sdk/client-stdio.d.ts.map +1 -0
- package/dist/sdk/client-stdio.js +6 -0
- package/dist/sdk/client-stdio.js.map +1 -0
- package/dist/sdk/client.d.ts +6 -0
- package/dist/sdk/client.d.ts.map +1 -0
- package/dist/sdk/client.js +6 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/express.d.ts +3 -0
- package/dist/sdk/express.d.ts.map +1 -0
- package/dist/sdk/express.js +11 -0
- package/dist/sdk/express.js.map +1 -0
- package/dist/sdk/node.d.ts +6 -0
- package/dist/sdk/node.d.ts.map +1 -0
- package/dist/sdk/node.js +6 -0
- package/dist/sdk/node.js.map +1 -0
- package/dist/sdk/server-stdio.d.ts +6 -0
- package/dist/sdk/server-stdio.d.ts.map +1 -0
- package/dist/sdk/server-stdio.js +6 -0
- package/dist/sdk/server-stdio.js.map +1 -0
- package/dist/sdk/server.d.ts +6 -0
- package/dist/sdk/server.d.ts.map +1 -0
- package/dist/sdk/server.js +6 -0
- package/dist/sdk/server.js.map +1 -0
- package/dist/server.d.ts +88 -36
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +98 -44
- package/dist/server.js.map +1 -1
- package/dist/task-request.d.ts +20 -0
- package/dist/task-request.d.ts.map +1 -0
- package/dist/task-request.js +43 -0
- package/dist/task-request.js.map +1 -0
- package/dist/task-store.d.ts +230 -0
- package/dist/task-store.d.ts.map +1 -0
- package/dist/task-store.js +459 -0
- package/dist/task-store.js.map +1 -0
- package/dist/task-tools.d.ts +115 -0
- package/dist/task-tools.d.ts.map +1 -0
- package/dist/task-tools.js +397 -0
- package/dist/task-tools.js.map +1 -0
- package/dist/testing/client.d.ts +25 -0
- package/dist/testing/client.d.ts.map +1 -0
- package/dist/testing/client.js +11 -0
- package/dist/testing/client.js.map +1 -0
- package/dist/testing/in-memory.d.ts +26 -0
- package/dist/testing/in-memory.d.ts.map +1 -0
- package/dist/testing/in-memory.js +41 -0
- package/dist/testing/in-memory.js.map +1 -0
- package/dist/testing/index.d.ts +20 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +20 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/isolated-dirs.d.ts +27 -0
- package/dist/testing/isolated-dirs.d.ts.map +1 -0
- package/dist/testing/isolated-dirs.js +67 -0
- package/dist/testing/isolated-dirs.js.map +1 -0
- package/dist/testing/logs.d.ts +61 -0
- package/dist/testing/logs.d.ts.map +1 -0
- package/dist/testing/logs.js +113 -0
- package/dist/testing/logs.js.map +1 -0
- package/dist/testing/raw-http.d.ts +25 -0
- package/dist/testing/raw-http.d.ts.map +1 -0
- package/dist/testing/raw-http.js +41 -0
- package/dist/testing/raw-http.js.map +1 -0
- package/dist/testing/raw-peer.d.ts +43 -0
- package/dist/testing/raw-peer.d.ts.map +1 -0
- package/dist/testing/raw-peer.js +101 -0
- package/dist/testing/raw-peer.js.map +1 -0
- package/dist/testing/recording-server.d.ts +38 -0
- package/dist/testing/recording-server.d.ts.map +1 -0
- package/dist/testing/recording-server.js +38 -0
- package/dist/testing/recording-server.js.map +1 -0
- package/dist/testing/server-process.d.ts +76 -0
- package/dist/testing/server-process.d.ts.map +1 -0
- package/dist/testing/server-process.js +241 -0
- package/dist/testing/server-process.js.map +1 -0
- package/dist/testing/tools.d.ts +53 -0
- package/dist/testing/tools.d.ts.map +1 -0
- package/dist/testing/tools.js +98 -0
- package/dist/testing/tools.js.map +1 -0
- package/dist/testing/unit.d.ts +52 -0
- package/dist/testing/unit.d.ts.map +1 -0
- package/dist/testing/unit.js +95 -0
- package/dist/testing/unit.js.map +1 -0
- package/dist/tools.d.ts +205 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +370 -0
- package/dist/tools.js.map +1 -0
- package/dist/transport.d.ts +144 -53
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +305 -122
- package/dist/transport.js.map +1 -1
- package/dist/url-path.d.ts +49 -0
- package/dist/url-path.d.ts.map +1 -0
- package/dist/url-path.js +165 -0
- package/dist/url-path.js.map +1 -0
- package/dist/utils/dataStore.d.ts +143 -0
- package/dist/utils/dataStore.d.ts.map +1 -0
- package/dist/utils/dataStore.js +429 -0
- package/dist/utils/dataStore.js.map +1 -0
- package/dist/utils/envLoader.d.ts +129 -0
- package/dist/utils/envLoader.d.ts.map +1 -0
- package/dist/utils/envLoader.js +399 -0
- package/dist/utils/envLoader.js.map +1 -0
- package/dist/utils/logger.d.ts +181 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +664 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/redact.d.ts +50 -0
- package/dist/utils/redact.d.ts.map +1 -0
- package/dist/utils/redact.js +318 -0
- package/dist/utils/redact.js.map +1 -0
- package/docs/api-reference.md +29 -0
- package/docs/building-a-server.md +63 -0
- package/docs/credentials.md +131 -0
- package/docs/data-store.md +37 -0
- package/docs/env.md +38 -0
- package/docs/features.md +28 -0
- package/docs/http-client.md +116 -0
- package/docs/logger.md +100 -0
- package/docs/migrating-to-v3.md +45 -0
- package/docs/outbound-urls.md +104 -0
- package/docs/pagination.md +45 -0
- package/docs/protocol-features.md +62 -0
- package/docs/resources-prompts-tasks.md +95 -0
- package/docs/sdk.md +33 -0
- package/docs/server.md +68 -0
- package/docs/testing-harness.md +139 -0
- package/docs/testing.md +151 -0
- package/docs/tools.md +88 -0
- package/docs/transport.md +85 -0
- package/examples/testing/tools.integration.test.example.ts +312 -0
- package/examples/testing/tools.unit.test.example.ts +149 -0
- package/package.json +140 -18
- package/dist/logger.d.ts +0 -80
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -236
- package/dist/logger.js.map +0 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core/testing - per-run temp directories so a server under test writes nothing into the
|
|
3
|
+
* developer's tree or profile.
|
|
4
|
+
*/
|
|
5
|
+
import { mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
function safeSegment(name) {
|
|
9
|
+
const safe = name.replace(/[^A-Za-z0-9._-]/g, '-').replace(/^\.+/, '');
|
|
10
|
+
if (!safe)
|
|
11
|
+
throw new Error(`createIsolatedDirs: invalid directory name "${name}"`);
|
|
12
|
+
return safe;
|
|
13
|
+
}
|
|
14
|
+
/** Create a fresh temp root under the OS temp dir with cwd, home, logs, state and tmp inside it. */
|
|
15
|
+
export function createIsolatedDirs(prefix = 'mcp-it') {
|
|
16
|
+
const root = mkdtempSync(join(tmpdir(), `${safeSegment(prefix)}-`));
|
|
17
|
+
const make = (name) => {
|
|
18
|
+
const path = join(root, safeSegment(name));
|
|
19
|
+
mkdirSync(path, { recursive: true });
|
|
20
|
+
return path;
|
|
21
|
+
};
|
|
22
|
+
const cwd = make('cwd');
|
|
23
|
+
const home = make('home');
|
|
24
|
+
const logs = make('logs');
|
|
25
|
+
const state = make('state');
|
|
26
|
+
const tmp = make('tmp');
|
|
27
|
+
const appData = join(home, 'AppData', 'Roaming');
|
|
28
|
+
const localAppData = join(home, 'AppData', 'Local');
|
|
29
|
+
mkdirSync(appData, { recursive: true });
|
|
30
|
+
mkdirSync(localAppData, { recursive: true });
|
|
31
|
+
let removed = false;
|
|
32
|
+
return {
|
|
33
|
+
root,
|
|
34
|
+
cwd,
|
|
35
|
+
home,
|
|
36
|
+
logs,
|
|
37
|
+
state,
|
|
38
|
+
tmp,
|
|
39
|
+
dir: make,
|
|
40
|
+
env: () => ({
|
|
41
|
+
HOME: home,
|
|
42
|
+
USERPROFILE: home,
|
|
43
|
+
APPDATA: appData,
|
|
44
|
+
LOCALAPPDATA: localAppData,
|
|
45
|
+
XDG_CONFIG_HOME: join(home, '.config'),
|
|
46
|
+
XDG_CACHE_HOME: join(home, '.cache'),
|
|
47
|
+
XDG_DATA_HOME: join(home, '.local', 'share'),
|
|
48
|
+
XDG_STATE_HOME: join(home, '.local', 'state'),
|
|
49
|
+
TMPDIR: tmp,
|
|
50
|
+
TEMP: tmp,
|
|
51
|
+
TMP: tmp,
|
|
52
|
+
LOG_PATH: logs,
|
|
53
|
+
}),
|
|
54
|
+
cleanup: () => {
|
|
55
|
+
if (removed)
|
|
56
|
+
return;
|
|
57
|
+
try {
|
|
58
|
+
rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
|
59
|
+
removed = true;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// A file still held open (Windows) must not fail the suite; the OS temp dir is reaped later.
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=isolated-dirs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isolated-dirs.js","sourceRoot":"","sources":["../../src/testing/isolated-dirs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core/testing - read the logger's three files and scan text for leaked values.
|
|
3
|
+
*/
|
|
4
|
+
export interface LogFiles {
|
|
5
|
+
/** `<serverName>.log` (or the `mainFile` override): every entry at or above the level. */
|
|
6
|
+
main: string;
|
|
7
|
+
/** `exceptions.log`: ERROR entries only. */
|
|
8
|
+
exceptions: string;
|
|
9
|
+
/** `debug.log`: DEBUG entries only. */
|
|
10
|
+
debug: string;
|
|
11
|
+
/** Whether each file exists (a switched-off sink writes no file). */
|
|
12
|
+
exists: {
|
|
13
|
+
main: boolean;
|
|
14
|
+
exceptions: boolean;
|
|
15
|
+
debug: boolean;
|
|
16
|
+
};
|
|
17
|
+
/** Absolute path of each file. */
|
|
18
|
+
paths: {
|
|
19
|
+
main: string;
|
|
20
|
+
exceptions: string;
|
|
21
|
+
debug: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Same rule as the logger: the main file is `<name>.log` with the name sanitised to `[A-Za-z0-9._-]`. */
|
|
25
|
+
export declare function logFileName(serverName: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Read `<logDir>/<serverName>.log`, `exceptions.log` and `debug.log`. An absent file reads as ''.
|
|
28
|
+
* `mainFile` mirrors LOG_FILE: a bare name inside `logDir`, or an absolute path used as-is.
|
|
29
|
+
*/
|
|
30
|
+
export declare function readLogFiles(logDir: string, serverName: string, options?: {
|
|
31
|
+
mainFile?: string;
|
|
32
|
+
}): LogFiles;
|
|
33
|
+
/**
|
|
34
|
+
* The level of every non-empty line of a log file, in order: `DEBUG` / `INFO` / `WARN` / `ERROR`, or
|
|
35
|
+
* `UNKNOWN` for a line in neither format. Reads the text format (`<ISO> | INFO | ...`) and the JSON
|
|
36
|
+
* format (`{"level":"INFO",...}`).
|
|
37
|
+
*/
|
|
38
|
+
export declare function logLevels(text: string): string[];
|
|
39
|
+
/** Needles shorter than this are ignored by `findLeaks` (too likely to match by chance). */
|
|
40
|
+
export declare const MIN_LEAK_NEEDLE_LENGTH = 6;
|
|
41
|
+
export interface Leak {
|
|
42
|
+
/** Index of the needle in the `needles` array. */
|
|
43
|
+
index: number;
|
|
44
|
+
/** A masked preview: the first two characters, then `***`, then the length. Never the full value. */
|
|
45
|
+
preview: string;
|
|
46
|
+
/** Indexes of the texts the needle appears in. */
|
|
47
|
+
foundIn: number[];
|
|
48
|
+
}
|
|
49
|
+
/** Mask a value for a report: `ab***(len=12)`. */
|
|
50
|
+
export declare function maskValue(value: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Return every needle that appears verbatim in any text, as `{ index, preview, foundIn }` (never the
|
|
53
|
+
* value itself, so a failing assertion cannot print the secret). Needles that are empty or shorter than
|
|
54
|
+
* `minLength` (default {@link MIN_LEAK_NEEDLE_LENGTH}) are skipped, as are duplicates.
|
|
55
|
+
*/
|
|
56
|
+
export declare function findLeaks(texts: string[], needles: string[], options?: {
|
|
57
|
+
minLength?: number;
|
|
58
|
+
}): Leak[];
|
|
59
|
+
/** Collect every string value (recursively) from tool arguments, for use as `findLeaks` needles. */
|
|
60
|
+
export declare function collectStrings(value: unknown, out?: string[]): string[];
|
|
61
|
+
//# sourceMappingURL=logs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/testing/logs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,QAAQ;IACvB,0FAA0F;IAC1F,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/D,kCAAkC;IAClC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D;AAED,0GAA0G;AAC1G,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAItD;AAOD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,QAAQ,CAkB9G;AAID;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAsBhD;AAED,4FAA4F;AAC5F,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,MAAM,WAAW,IAAI;IACnB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,qGAAqG;IACrG,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,kDAAkD;AAClD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,IAAI,EAAE,CAc1G;AAED,oGAAoG;AACpG,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CAK3E"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core/testing - read the logger's three files and scan text for leaked values.
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { isAbsolute, join } from 'node:path';
|
|
6
|
+
/** Same rule as the logger: the main file is `<name>.log` with the name sanitised to `[A-Za-z0-9._-]`. */
|
|
7
|
+
export function logFileName(serverName) {
|
|
8
|
+
const safe = serverName.replace(/[^A-Za-z0-9._-]/g, '-').replace(/^\.+/, '');
|
|
9
|
+
if (!safe)
|
|
10
|
+
throw new Error(`logFileName: "${serverName}" sanitises to an empty name`);
|
|
11
|
+
return `${safe}.log`;
|
|
12
|
+
}
|
|
13
|
+
function readOrEmpty(path) {
|
|
14
|
+
if (!existsSync(path))
|
|
15
|
+
return { text: '', exists: false };
|
|
16
|
+
return { text: readFileSync(path, 'utf8'), exists: true };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Read `<logDir>/<serverName>.log`, `exceptions.log` and `debug.log`. An absent file reads as ''.
|
|
20
|
+
* `mainFile` mirrors LOG_FILE: a bare name inside `logDir`, or an absolute path used as-is.
|
|
21
|
+
*/
|
|
22
|
+
export function readLogFiles(logDir, serverName, options = {}) {
|
|
23
|
+
const mainPath = options.mainFile !== undefined
|
|
24
|
+
? isAbsolute(options.mainFile)
|
|
25
|
+
? options.mainFile
|
|
26
|
+
: join(logDir, options.mainFile)
|
|
27
|
+
: join(logDir, logFileName(serverName));
|
|
28
|
+
const paths = { main: mainPath, exceptions: join(logDir, 'exceptions.log'), debug: join(logDir, 'debug.log') };
|
|
29
|
+
const main = readOrEmpty(paths.main);
|
|
30
|
+
const exceptions = readOrEmpty(paths.exceptions);
|
|
31
|
+
const debug = readOrEmpty(paths.debug);
|
|
32
|
+
return {
|
|
33
|
+
main: main.text,
|
|
34
|
+
exceptions: exceptions.text,
|
|
35
|
+
debug: debug.text,
|
|
36
|
+
exists: { main: main.exists, exceptions: exceptions.exists, debug: debug.exists },
|
|
37
|
+
paths,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const TEXT_LEVEL = /^\S+ \| (DEBUG|INFO|WARN|ERROR)\s* \|/;
|
|
41
|
+
/**
|
|
42
|
+
* The level of every non-empty line of a log file, in order: `DEBUG` / `INFO` / `WARN` / `ERROR`, or
|
|
43
|
+
* `UNKNOWN` for a line in neither format. Reads the text format (`<ISO> | INFO | ...`) and the JSON
|
|
44
|
+
* format (`{"level":"INFO",...}`).
|
|
45
|
+
*/
|
|
46
|
+
export function logLevels(text) {
|
|
47
|
+
const levels = [];
|
|
48
|
+
for (const line of text.split(/\r?\n/)) {
|
|
49
|
+
if (line.trim() === '')
|
|
50
|
+
continue;
|
|
51
|
+
const m = TEXT_LEVEL.exec(line);
|
|
52
|
+
if (m?.[1]) {
|
|
53
|
+
levels.push(m[1]);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
let level = 'UNKNOWN';
|
|
57
|
+
if (line.startsWith('{')) {
|
|
58
|
+
try {
|
|
59
|
+
const parsed = JSON.parse(line);
|
|
60
|
+
const value = typeof parsed === 'object' && parsed !== null ? parsed.level : undefined;
|
|
61
|
+
if (typeof value === 'string')
|
|
62
|
+
level = value;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// not JSON: UNKNOWN
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
levels.push(level);
|
|
69
|
+
}
|
|
70
|
+
return levels;
|
|
71
|
+
}
|
|
72
|
+
/** Needles shorter than this are ignored by `findLeaks` (too likely to match by chance). */
|
|
73
|
+
export const MIN_LEAK_NEEDLE_LENGTH = 6;
|
|
74
|
+
/** Mask a value for a report: `ab***(len=12)`. */
|
|
75
|
+
export function maskValue(value) {
|
|
76
|
+
return `${value.slice(0, 2)}***(len=${value.length})`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Return every needle that appears verbatim in any text, as `{ index, preview, foundIn }` (never the
|
|
80
|
+
* value itself, so a failing assertion cannot print the secret). Needles that are empty or shorter than
|
|
81
|
+
* `minLength` (default {@link MIN_LEAK_NEEDLE_LENGTH}) are skipped, as are duplicates.
|
|
82
|
+
*/
|
|
83
|
+
export function findLeaks(texts, needles, options = {}) {
|
|
84
|
+
const minLength = Math.max(1, options.minLength ?? MIN_LEAK_NEEDLE_LENGTH);
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
const leaks = [];
|
|
87
|
+
needles.forEach((needle, index) => {
|
|
88
|
+
if (needle.length < minLength || seen.has(needle))
|
|
89
|
+
return;
|
|
90
|
+
seen.add(needle);
|
|
91
|
+
const foundIn = [];
|
|
92
|
+
texts.forEach((text, t) => {
|
|
93
|
+
if (text.includes(needle))
|
|
94
|
+
foundIn.push(t);
|
|
95
|
+
});
|
|
96
|
+
if (foundIn.length > 0)
|
|
97
|
+
leaks.push({ index, preview: maskValue(needle), foundIn });
|
|
98
|
+
});
|
|
99
|
+
return leaks;
|
|
100
|
+
}
|
|
101
|
+
/** Collect every string value (recursively) from tool arguments, for use as `findLeaks` needles. */
|
|
102
|
+
export function collectStrings(value, out = []) {
|
|
103
|
+
if (typeof value === 'string')
|
|
104
|
+
out.push(value);
|
|
105
|
+
else if (Array.isArray(value))
|
|
106
|
+
for (const v of value)
|
|
107
|
+
collectStrings(v, out);
|
|
108
|
+
else if (typeof value === 'object' && value !== null)
|
|
109
|
+
for (const v of Object.values(value))
|
|
110
|
+
collectStrings(v, out);
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/testing/logs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core/testing - a minimal fetch wrapper for adverse HTTP checks (401, 404, 413, 415, ...).
|
|
3
|
+
*/
|
|
4
|
+
export interface RawHttpOptions {
|
|
5
|
+
/** HTTP method (default `POST`). */
|
|
6
|
+
method?: string;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
/** Request body (use `'x'.repeat(n)` for an oversized body). */
|
|
9
|
+
body?: string;
|
|
10
|
+
/** Abort after this many milliseconds (default 10000). */
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface RawHttpResponse {
|
|
14
|
+
status: number;
|
|
15
|
+
/** Response headers, names lower-cased. */
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
text: string;
|
|
18
|
+
}
|
|
19
|
+
/** Send one request and return status, headers and body text. Network errors throw. */
|
|
20
|
+
export declare function rawHttp(url: string, options?: RawHttpOptions): Promise<RawHttpResponse>;
|
|
21
|
+
/** A JSON-RPC `initialize` request body (2025-06-18), for raw requests that must reach the MCP handler. */
|
|
22
|
+
export declare function initializeRequestBody(id?: number): string;
|
|
23
|
+
/** Headers a Streamable HTTP POST needs (`content-type` + `accept`), plus an optional Bearer key. */
|
|
24
|
+
export declare function mcpPostHeaders(apiKey?: string): Record<string, string>;
|
|
25
|
+
//# sourceMappingURL=raw-http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw-http.d.ts","sourceRoot":"","sources":["../../src/testing/raw-http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,uFAAuF;AACvF,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,eAAe,CAAC,CAYjG;AAED,2GAA2G;AAC3G,wBAAgB,qBAAqB,CAAC,EAAE,SAAI,GAAG,MAAM,CAWpD;AAED,qGAAqG;AACrG,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOtE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core/testing - a minimal fetch wrapper for adverse HTTP checks (401, 404, 413, 415, ...).
|
|
3
|
+
*/
|
|
4
|
+
/** Send one request and return status, headers and body text. Network errors throw. */
|
|
5
|
+
export async function rawHttp(url, options = {}) {
|
|
6
|
+
const res = await fetch(url, {
|
|
7
|
+
method: options.method ?? 'POST',
|
|
8
|
+
headers: options.headers,
|
|
9
|
+
body: options.body,
|
|
10
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 10_000),
|
|
11
|
+
});
|
|
12
|
+
const headers = {};
|
|
13
|
+
res.headers.forEach((value, key) => {
|
|
14
|
+
headers[key.toLowerCase()] = value;
|
|
15
|
+
});
|
|
16
|
+
return { status: res.status, headers, text: await res.text() };
|
|
17
|
+
}
|
|
18
|
+
/** A JSON-RPC `initialize` request body (2025-06-18), for raw requests that must reach the MCP handler. */
|
|
19
|
+
export function initializeRequestBody(id = 1) {
|
|
20
|
+
return JSON.stringify({
|
|
21
|
+
jsonrpc: '2.0',
|
|
22
|
+
id,
|
|
23
|
+
method: 'initialize',
|
|
24
|
+
params: {
|
|
25
|
+
protocolVersion: '2025-06-18',
|
|
26
|
+
capabilities: {},
|
|
27
|
+
clientInfo: { name: 'mcp-core-testing-raw', version: '1.0.0' },
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** Headers a Streamable HTTP POST needs (`content-type` + `accept`), plus an optional Bearer key. */
|
|
32
|
+
export function mcpPostHeaders(apiKey) {
|
|
33
|
+
const headers = {
|
|
34
|
+
'content-type': 'application/json',
|
|
35
|
+
accept: 'application/json, text/event-stream',
|
|
36
|
+
};
|
|
37
|
+
if (apiKey !== undefined)
|
|
38
|
+
headers.authorization = `Bearer ${apiKey}`;
|
|
39
|
+
return headers;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=raw-http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw-http.js","sourceRoot":"","sources":["../../src/testing/raw-http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAmBH,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,UAA0B,EAAE;IACrE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,MAAM;QAChC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;KACzD,CAAC,CAAC;IACH,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACjC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;IACrC,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;AACjE,CAAC;AAED,2GAA2G;AAC3G,MAAM,UAAU,qBAAqB,CAAC,EAAE,GAAG,CAAC;IAC1C,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,OAAO,EAAE,KAAK;QACd,EAAE;QACF,MAAM,EAAE,YAAY;QACpB,MAAM,EAAE;YACN,eAAe,EAAE,YAAY;YAC7B,YAAY,EAAE,EAAE;YAChB,UAAU,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE;SAC/D;KACF,CAAC,CAAC;AACL,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,MAAM,EAAE,qCAAqC;KAC9C,CAAC;IACF,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,aAAa,GAAG,UAAU,MAAM,EAAE,CAAC;IACrE,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core/testing - a raw JSON-RPC peer, for wire shapes the SDK client does not accept.
|
|
3
|
+
*
|
|
4
|
+
* The SDK 2.3.1 `Client` refuses a 2026-07-28 `tools/call` answered with `resultType: "task"`
|
|
5
|
+
* (typescript-sdk#2637; the requester side lives in `@modelcontextprotocol/ext-tasks`), so task tools
|
|
6
|
+
* are exercised on the modern era with plain JSON-RPC. `rawPeer` drives any SDK client transport
|
|
7
|
+
* (in memory, stdio); `rawHttpPeer` POSTs to a Streamable HTTP endpoint with the SEP-2243 headers.
|
|
8
|
+
* `modernMeta` builds the per-request 2026-07-28 envelope.
|
|
9
|
+
*/
|
|
10
|
+
import { type Transport } from '@modelcontextprotocol/client';
|
|
11
|
+
export declare const MODERN_PROTOCOL_VERSION = "2026-07-28";
|
|
12
|
+
/** The `_meta` envelope of a 2026-07-28 request. */
|
|
13
|
+
export declare function modernMeta(capabilities?: Record<string, unknown>): Record<string, unknown>;
|
|
14
|
+
/** A JSON-RPC response as received. */
|
|
15
|
+
export interface RawResponse {
|
|
16
|
+
result?: Record<string, unknown>;
|
|
17
|
+
error?: {
|
|
18
|
+
code: number;
|
|
19
|
+
message: string;
|
|
20
|
+
data?: unknown;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface RawPeer {
|
|
24
|
+
/** Send a request and resolve with its response (notifications before it are collected). */
|
|
25
|
+
request(method: string, params?: Record<string, unknown>, options?: {
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
}): Promise<RawResponse>;
|
|
28
|
+
/** Send a notification. */
|
|
29
|
+
notify(method: string, params?: Record<string, unknown>): Promise<void>;
|
|
30
|
+
/** Every notification received so far. */
|
|
31
|
+
notifications: Array<{
|
|
32
|
+
method: string;
|
|
33
|
+
params?: Record<string, unknown>;
|
|
34
|
+
}>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/** Drive an SDK client transport (started here) with raw JSON-RPC. */
|
|
38
|
+
export declare function rawPeer(transport: Transport): Promise<RawPeer>;
|
|
39
|
+
/** The last JSON-RPC message of a JSON or SSE body. */
|
|
40
|
+
export declare function parseRpcBody(contentType: string | null, text: string): RawResponse;
|
|
41
|
+
/** POST one 2026-07-28 request per call to `url` (an MCP endpoint), with an optional Bearer key. */
|
|
42
|
+
export declare function rawHttpPeer(url: string, apiKey?: string): Pick<RawPeer, 'request'>;
|
|
43
|
+
//# sourceMappingURL=raw-peer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw-peer.d.ts","sourceRoot":"","sources":["../../src/testing/raw-peer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,8BAA8B,CAAC;AAEtC,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,oDAAoD;AACpD,wBAAgB,UAAU,CAAC,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAM9F;AAED,uCAAuC;AACvC,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3D;AAED,MAAM,WAAW,OAAO;IACtB,4FAA4F;IAC5F,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAClH,2BAA2B;IAC3B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,0CAA0C;IAC1C,aAAa,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC3E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,sEAAsE;AACtE,wBAAsB,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAqCpE;AAeD,uDAAuD;AACvD,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAUlF;AAED,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAelF"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core/testing - a raw JSON-RPC peer, for wire shapes the SDK client does not accept.
|
|
3
|
+
*
|
|
4
|
+
* The SDK 2.3.1 `Client` refuses a 2026-07-28 `tools/call` answered with `resultType: "task"`
|
|
5
|
+
* (typescript-sdk#2637; the requester side lives in `@modelcontextprotocol/ext-tasks`), so task tools
|
|
6
|
+
* are exercised on the modern era with plain JSON-RPC. `rawPeer` drives any SDK client transport
|
|
7
|
+
* (in memory, stdio); `rawHttpPeer` POSTs to a Streamable HTTP endpoint with the SEP-2243 headers.
|
|
8
|
+
* `modernMeta` builds the per-request 2026-07-28 envelope.
|
|
9
|
+
*/
|
|
10
|
+
import { CLIENT_CAPABILITIES_META_KEY, CLIENT_INFO_META_KEY, PROTOCOL_VERSION_META_KEY, } from '@modelcontextprotocol/client';
|
|
11
|
+
export const MODERN_PROTOCOL_VERSION = '2026-07-28';
|
|
12
|
+
/** The `_meta` envelope of a 2026-07-28 request. */
|
|
13
|
+
export function modernMeta(capabilities = {}) {
|
|
14
|
+
return {
|
|
15
|
+
[PROTOCOL_VERSION_META_KEY]: MODERN_PROTOCOL_VERSION,
|
|
16
|
+
[CLIENT_INFO_META_KEY]: { name: 'mcp-core-raw-peer', version: '1.0.0' },
|
|
17
|
+
[CLIENT_CAPABILITIES_META_KEY]: capabilities,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Drive an SDK client transport (started here) with raw JSON-RPC. */
|
|
21
|
+
export async function rawPeer(transport) {
|
|
22
|
+
let nextId = 1;
|
|
23
|
+
const pending = new Map();
|
|
24
|
+
const notifications = [];
|
|
25
|
+
transport.onmessage = (message) => {
|
|
26
|
+
const m = message;
|
|
27
|
+
if (m.method !== undefined && m.id === undefined) {
|
|
28
|
+
notifications.push({ method: m.method, ...(m.params ? { params: m.params } : {}) });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (m.id !== undefined && m.method === undefined) {
|
|
32
|
+
const resolve = pending.get(m.id);
|
|
33
|
+
pending.delete(m.id);
|
|
34
|
+
resolve?.({ ...(m.result ? { result: m.result } : {}), ...(m.error ? { error: m.error } : {}) });
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
await transport.start();
|
|
38
|
+
return {
|
|
39
|
+
notifications,
|
|
40
|
+
request: (method, params, options) => {
|
|
41
|
+
const id = nextId++;
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const timer = setTimeout(() => {
|
|
44
|
+
pending.delete(id);
|
|
45
|
+
reject(new Error(`raw peer: no response to ${method} within ${options?.timeoutMs ?? 10_000} ms`));
|
|
46
|
+
}, options?.timeoutMs ?? 10_000);
|
|
47
|
+
pending.set(id, (response) => {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
resolve(response);
|
|
50
|
+
});
|
|
51
|
+
transport.send({ jsonrpc: '2.0', id, method, ...(params ? { params } : {}) }).catch(reject);
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
notify: (method, params) => transport.send({ jsonrpc: '2.0', method, ...(params ? { params } : {}) }),
|
|
55
|
+
close: () => transport.close(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Headers SEP-2243 requires on a 2026-07-28 POST (`Mcp-Name` for tools/call and tasks/*). */
|
|
59
|
+
function modernHeaders(method, params) {
|
|
60
|
+
const headers = {
|
|
61
|
+
'content-type': 'application/json',
|
|
62
|
+
accept: 'application/json, text/event-stream',
|
|
63
|
+
'mcp-protocol-version': MODERN_PROTOCOL_VERSION,
|
|
64
|
+
'mcp-method': method,
|
|
65
|
+
};
|
|
66
|
+
const name = method === 'tools/call' ? params?.name : method.startsWith('tasks/') ? params?.taskId : undefined;
|
|
67
|
+
if (typeof name === 'string')
|
|
68
|
+
headers['mcp-name'] = name;
|
|
69
|
+
return headers;
|
|
70
|
+
}
|
|
71
|
+
/** The last JSON-RPC message of a JSON or SSE body. */
|
|
72
|
+
export function parseRpcBody(contentType, text) {
|
|
73
|
+
const payload = contentType?.includes('text/event-stream')
|
|
74
|
+
? text
|
|
75
|
+
.split('\n')
|
|
76
|
+
.filter((line) => line.startsWith('data:'))
|
|
77
|
+
.map((line) => line.slice(5).trim())
|
|
78
|
+
.filter((line) => line.includes('"id"'))
|
|
79
|
+
.at(-1)
|
|
80
|
+
: text;
|
|
81
|
+
return JSON.parse(payload ?? '{}');
|
|
82
|
+
}
|
|
83
|
+
/** POST one 2026-07-28 request per call to `url` (an MCP endpoint), with an optional Bearer key. */
|
|
84
|
+
export function rawHttpPeer(url, apiKey) {
|
|
85
|
+
let nextId = 1;
|
|
86
|
+
return {
|
|
87
|
+
async request(method, params, options) {
|
|
88
|
+
const headers = modernHeaders(method, params);
|
|
89
|
+
if (apiKey !== undefined)
|
|
90
|
+
headers.authorization = `Bearer ${apiKey}`;
|
|
91
|
+
const res = await fetch(url, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers,
|
|
94
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: nextId++, method, ...(params ? { params } : {}) }),
|
|
95
|
+
signal: AbortSignal.timeout(options?.timeoutMs ?? 10_000),
|
|
96
|
+
});
|
|
97
|
+
return parseRpcBody(res.headers.get('content-type'), await res.text());
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=raw-peer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw-peer.js","sourceRoot":"","sources":["../../src/testing/raw-peer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core/testing - a minimal stand-in for McpServer that records registered tools and
|
|
3
|
+
* prompts, so unit tests can invoke their handlers directly without the MCP protocol.
|
|
4
|
+
*
|
|
5
|
+
* Behaviour (unchanged from the per-server copies it replaces): `registerTool` / `registerPrompt` store
|
|
6
|
+
* `{ name, config, handler }` under the name (a later registration with the same name replaces the
|
|
7
|
+
* earlier one) and return a handle whose `remove()` deletes it. Nothing is validated and nothing else
|
|
8
|
+
* of McpServer is implemented.
|
|
9
|
+
*/
|
|
10
|
+
import type { ClientCapabilities, GetPromptResult } from '@modelcontextprotocol/server';
|
|
11
|
+
import type { McpCallToolResult, McpServer } from '../server.js';
|
|
12
|
+
/** Handler signature as recorded; call it with the tool arguments (and optionally a request extra). */
|
|
13
|
+
export type RecordedToolHandler = (args: Record<string, unknown>, extra?: unknown) => Promise<McpCallToolResult>;
|
|
14
|
+
export type RecordedPromptHandler = (args: Record<string, unknown>, extra?: unknown) => GetPromptResult | Promise<GetPromptResult>;
|
|
15
|
+
/** The tool/prompt config as passed to register*: description, schemas, annotations, ... */
|
|
16
|
+
export type RecordedConfig = Record<string, unknown>;
|
|
17
|
+
export interface CapturedTool {
|
|
18
|
+
name: string;
|
|
19
|
+
config: RecordedConfig;
|
|
20
|
+
handler: RecordedToolHandler;
|
|
21
|
+
}
|
|
22
|
+
export interface CapturedPrompt {
|
|
23
|
+
name: string;
|
|
24
|
+
config: RecordedConfig;
|
|
25
|
+
handler: RecordedPromptHandler;
|
|
26
|
+
}
|
|
27
|
+
export interface RecordingServer {
|
|
28
|
+
tools: Map<string, CapturedTool>;
|
|
29
|
+
prompts: Map<string, CapturedPrompt>;
|
|
30
|
+
/** Cast to McpServer for passing into register* functions. */
|
|
31
|
+
asMcpServer(): McpServer;
|
|
32
|
+
}
|
|
33
|
+
export interface RecordingServerOptions {
|
|
34
|
+
/** What `server.getClientCapabilities()` answers (the `ToolContext` client input helpers read it). */
|
|
35
|
+
clientCapabilities?: ClientCapabilities;
|
|
36
|
+
}
|
|
37
|
+
export declare function makeRecordingServer(options?: RecordingServerOptions): RecordingServer;
|
|
38
|
+
//# sourceMappingURL=recording-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recording-server.d.ts","sourceRoot":"","sources":["../../src/testing/recording-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEjE,uGAAuG;AACvG,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AACjH,MAAM,MAAM,qBAAqB,GAAG,CAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,CAAC,EAAE,OAAO,KACZ,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAEhD,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,qBAAqB,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACrC,8DAA8D;IAC9D,WAAW,IAAI,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,sGAAsG;IACtG,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,sBAA2B,GAAG,eAAe,CA6BzF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core/testing - a minimal stand-in for McpServer that records registered tools and
|
|
3
|
+
* prompts, so unit tests can invoke their handlers directly without the MCP protocol.
|
|
4
|
+
*
|
|
5
|
+
* Behaviour (unchanged from the per-server copies it replaces): `registerTool` / `registerPrompt` store
|
|
6
|
+
* `{ name, config, handler }` under the name (a later registration with the same name replaces the
|
|
7
|
+
* earlier one) and return a handle whose `remove()` deletes it. Nothing is validated and nothing else
|
|
8
|
+
* of McpServer is implemented.
|
|
9
|
+
*/
|
|
10
|
+
export function makeRecordingServer(options = {}) {
|
|
11
|
+
const tools = new Map();
|
|
12
|
+
const prompts = new Map();
|
|
13
|
+
const server = {
|
|
14
|
+
server: { getClientCapabilities: () => options.clientCapabilities },
|
|
15
|
+
registerTool(name, config, handler) {
|
|
16
|
+
tools.set(name, { name, config, handler });
|
|
17
|
+
return {
|
|
18
|
+
remove() {
|
|
19
|
+
tools.delete(name);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
registerPrompt(name, config, handler) {
|
|
24
|
+
prompts.set(name, { name, config, handler });
|
|
25
|
+
return {
|
|
26
|
+
remove() {
|
|
27
|
+
prompts.delete(name);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
tools,
|
|
34
|
+
prompts,
|
|
35
|
+
asMcpServer: () => server,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=recording-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recording-server.js","sourceRoot":"","sources":["../../src/testing/recording-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuCH,MAAM,UAAU,mBAAmB,CAAC,UAAkC,EAAE;IACtE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAElD,MAAM,MAAM,GAAG;QACb,MAAM,EAAE,EAAE,qBAAqB,EAAE,GAAmC,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE;QACnG,YAAY,CAAC,IAAY,EAAE,MAAsB,EAAE,OAA4B;YAC7E,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;YAC3C,OAAO;gBACL,MAAM;oBACJ,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACrB,CAAC;aACF,CAAC;QACJ,CAAC;QACD,cAAc,CAAC,IAAY,EAAE,MAAsB,EAAE,OAA8B;YACjF,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;YAC7C,OAAO;gBACL,MAAM;oBACJ,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvB,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC;IAEF,OAAO;QACL,KAAK;QACL,OAAO;QACP,WAAW,EAAE,GAAG,EAAE,CAAC,MAA8B;KAClD,CAAC;AACJ,CAAC"}
|