@slbdn/mcp-tester 1.5.4 → 1.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.
@@ -0,0 +1,138 @@
1
+ # Browser Support
2
+
3
+ `mcp-tester` can run inside browser and edge runtimes to test MCP servers over
4
+ network transports. This is useful for:
5
+
6
+ - Component/unit tests that run in a real browser (Vitest browser mode, Karma,
7
+ Playwright component testing, Web Test Runner).
8
+ - In-browser debugging or smoke checks of a remote MCP server.
9
+ - Edge runtimes (Cloudflare Workers, Deno Deploy, Vercel Edge) that expose
10
+ `fetch` and `WebSocket`.
11
+
12
+ ## How it works
13
+
14
+ The package declares a `browser` field in `package.json`:
15
+
16
+ ```json
17
+ "browser": {
18
+ "./dist/index.js": "./dist/browser.js",
19
+ "./dist/utils/stdio-transport.js": false
20
+ }
21
+ ```
22
+
23
+ When a bundler targets the browser it resolves the package entry to
24
+ `dist/browser.js` and replaces the Node-only stdio transport with an empty
25
+ module. You import the package exactly as you would on Node:
26
+
27
+ ```typescript
28
+ import { MCPClient, assert } from '@slbdn/mcp-tester';
29
+
30
+ const client = new MCPClient();
31
+ await client.start({ transport: 'http', url: 'https://api.example.com/mcp' });
32
+
33
+ const result = await client.callTool({ name: 'echo', arguments: { message: 'hi' } });
34
+ assert.toolTextContains(result, 'hi');
35
+
36
+ await client.stop();
37
+ ```
38
+
39
+ Webpack, Vite, Rollup, esbuild, Parcel, and Rspack all honor the `browser`
40
+ field. The repository's CI runs `npm run test:browser`, which bundles the
41
+ package with esbuild using `platform: 'browser'` and fails if any Node built-in
42
+ (`node:*`) or the stdio transport (`cross-spawn`) leaks into the bundle.
43
+
44
+ ## Transports
45
+
46
+ | Transport | Browser | Notes |
47
+ |-----------|:-------:|-------|
48
+ | `http` (Streamable HTTP) | ✅ | Uses `fetch` + `ReadableStream`. Recommended for remote servers. |
49
+ | `sse` (legacy) | ✅ | Uses `fetch` + `EventSource` semantics. |
50
+ | `websocket` | ✅ | Uses the global `WebSocket` API. |
51
+ | `stdio` | ❌ | Spawns a local process; Node.js only. |
52
+
53
+ ### Streamable HTTP
54
+
55
+ ```typescript
56
+ await client.start({
57
+ transport: 'http',
58
+ url: 'https://api.example.com/mcp',
59
+ headers: { Authorization: 'Bearer token' },
60
+ });
61
+ ```
62
+
63
+ ### WebSocket
64
+
65
+ ```typescript
66
+ await client.start({
67
+ transport: 'websocket',
68
+ url: 'wss://api.example.com/mcp',
69
+ });
70
+ ```
71
+
72
+ The client connects using the `mcp` WebSocket subprotocol, matching the MCP
73
+ SDK's `WebSocketClientTransport`.
74
+
75
+ > **Node.js note:** the WebSocket transport needs a global `WebSocket`.
76
+ > Node.js 22+ provides one. On older Node versions use `--experimental-websocket`
77
+ > or a polyfill such as [`ws`](https://www.npmjs.com/package/ws) installed on
78
+ > `globalThis`.
79
+
80
+ ## What is included
81
+
82
+ The browser entry point (`dist/browser.js`) exports the browser-safe subset:
83
+
84
+ - `MCPClient` and all client types (`ServerConfig`, `StreamableHttpServerConfig`,
85
+ `SseServerConfig`, `WebSocketServerConfig`, `MCPClientOptions`, `HealthStatus`, …)
86
+ - Custom matchers (`toHaveTool`, `toReturnText`, `setupVitestMatchers`, …)
87
+ - The `assert` module
88
+ - Error classes (`MCPClientError`, `MCPTimeoutError`, …)
89
+ - Logging/masking utilities (`startTimer`, `prettyPrint`, `maskSecrets`, …)
90
+ - The deterministic edge-case generator (`suggestEdgeCases`, `generateToolArgs`, …)
91
+
92
+ ## What is Node-only
93
+
94
+ These are intentionally excluded and tree-shaken from browser bundles:
95
+
96
+ - **stdio transport** — spawns a child process via `cross-spawn`.
97
+ - **`generateTests` / `generateTypes`** — read/write files and read the package
98
+ version from disk.
99
+ - **AI provider** (`OpenAICompatProvider`, `createProviderFromEnv`) — uses
100
+ `node:crypto`, `node:fs`, and `node:path`.
101
+
102
+ If you need test generation in a browser workflow, generate the files in a Node
103
+ step (CLI or script) and run the generated tests in the browser.
104
+
105
+ ## Limitations
106
+
107
+ - **No stdio.** Browsers cannot spawn processes. Use `http`, `sse`, or
108
+ `websocket`.
109
+ - **CORS applies.** The MCP server must send appropriate
110
+ `Access-Control-Allow-Origin` headers (and allow the `mcp-session-id` header
111
+ for Streamable HTTP). This is a browser/network constraint, not a limitation
112
+ of this library.
113
+ - **File-based caching is unavailable.** The AI provider (Node-only) writes a
114
+ cache to disk; in-browser code paths do not touch the filesystem.
115
+ - **`process`-based features degrade gracefully.** Health checks for stdio PIDs
116
+ return `null`, and logging falls back to a non-TTY format.
117
+
118
+ ## Testing from Node
119
+
120
+ You can exercise the browser-safe entry point from Node without a browser. The
121
+ repository's `test:browser` script is a good template; a focused version is:
122
+
123
+ ```js
124
+ import { build } from 'esbuild';
125
+
126
+ await build({
127
+ entryPoints: ['src/entry.ts'],
128
+ bundle: true,
129
+ platform: 'browser',
130
+ format: 'esm',
131
+ outfile: 'out.js',
132
+ });
133
+ ```
134
+
135
+ If the bundle succeeds, no Node built-ins were pulled in.
136
+
137
+ See also: [Starter Templates](./starter-templates.md) and
138
+ [Testing](./testing.md).
package/docs/cicd.md CHANGED
@@ -39,6 +39,9 @@ jobs:
39
39
  - name: Build TypeScript
40
40
  run: npm run build
41
41
 
42
+ - name: Check browser bundle
43
+ run: npm run test:browser
44
+
42
45
  - name: Run tests
43
46
  run: npm test
44
47
 
package/docs/cli.md CHANGED
@@ -10,7 +10,7 @@ npx @slbdn/mcp-tester test node ./server.js
10
10
 
11
11
  ## Transports
12
12
 
13
- The CLI supports three transport types:
13
+ The CLI supports four transport types:
14
14
 
15
15
  ### Stdio (default)
16
16
 
@@ -43,6 +43,19 @@ mcp-tester test --transport http --url https://api.example.com/mcp --headers '{"
43
43
  mcp-tester test --transport sse --url http://localhost:3000/sse
44
44
  ```
45
45
 
46
+ ### WebSocket
47
+
48
+ ```bash
49
+ mcp-tester test --transport websocket --url ws://localhost:3000
50
+ ```
51
+
52
+ A `ws://` or `wss://` URL passed as the first argument is auto-detected as the
53
+ WebSocket transport:
54
+
55
+ ```bash
56
+ mcp-tester test wss://api.example.com/mcp
57
+ ```
58
+
46
59
  ## Commands
47
60
 
48
61
  ### `test` — Test Server Connection
@@ -6,8 +6,8 @@
6
6
  |----------------|--------|-------|
7
7
  | 18.x | ⚠️ Not Supported | ESM limitations — upgrade recommended |
8
8
  | 20.x (LTS) | ✅ Fully Supported | **Recommended** (minimum required version) |
9
- | 21.x | ✅ Fully Supported | Latest stable |
10
- | 22.x+ | ✅ Compatible | Tested, works |
9
+ | 21.x | ⚠️ Not Supported | End-of-life; dropped from the test matrix in 1.5.2 |
10
+ | 22.x+ | ✅ Fully Supported | Latest LTS, tested |
11
11
 
12
12
  ## Requirements
13
13
 
@@ -36,6 +36,20 @@ Targets TypeScript 5.3+ with ES2022 output.
36
36
  - ✅ Linux (Ubuntu, Debian, CentOS, Alpine)
37
37
  - ✅ Windows 10/11 (via WSL or native Node.js)
38
38
 
39
+ ## Browser and Edge Runtimes
40
+
41
+ Modern browsers and edge runtimes (Cloudflare Workers, Deno Deploy, Vercel
42
+ Edge) are supported for the network transports:
43
+
44
+ | Runtime | stdio | http | sse | websocket |
45
+ |---------|:-----:|:----:|:---:|:---------:|
46
+ | Node.js ≥ 20 | ✅ | ✅ | ✅ | ✅ (global `WebSocket` on ≥ 22) |
47
+ | Browsers | ❌ | ✅ | ✅ | ✅ |
48
+ | Edge runtimes | ❌ | ✅ | ✅ | ✅ |
49
+
50
+ Bundlers select the browser entry point automatically via the `browser` field.
51
+ See [Browser Support](./browser.md) for details.
52
+
39
53
  ## CI/CD Version Specification
40
54
 
41
55
  **GitHub Actions:**
package/docs/testing.md CHANGED
@@ -8,6 +8,7 @@ Guide to writing and running tests with MCP Tester.
8
8
  npm test # Run all tests
9
9
  npm run test:watch # Watch mode (auto-rerun on changes)
10
10
  npm run test:coverage # Run with coverage report
11
+ npm run test:browser # Bundle-check the browser build (esbuild)
11
12
  npm run lint # Lint code
12
13
  ```
13
14
 
@@ -20,6 +21,8 @@ src/__tests__/
20
21
  ├── advanced.test.ts # Sampling, elicitation
21
22
  ├── real-server.test.ts # Integration tests (stdio transport)
22
23
  ├── everything-server.test.ts # Full integration (server-everything)
24
+ ├── http-transport.test.ts # Streamable HTTP / SSE transport
25
+ ├── websocket-transport.test.ts # WebSocket transport (in-process double)
23
26
  ├── cli.test.ts # CLI tool tests
24
27
  └── fixtures/
25
28
  └── mock-server.ts # In-memory mock MCP server
@@ -114,8 +114,8 @@ Coverage thresholds: **80%** for branches, functions, lines, and statements.
114
114
 
115
115
  ## Known Limitations
116
116
 
117
- - Supports **stdio**, **Streamable HTTP**, and **SSE** transports
118
- - Designed for **Node.js** servers (other runtimes may need adjustments)
117
+ - Supports **stdio**, **Streamable HTTP**, **SSE**, and **WebSocket** transports
118
+ - Node.js by default; the HTTP/SSE/WebSocket transports also run in browsers and edge runtimes (see [Browser Support](./browser.md))
119
119
  - Mock server has configurable delays, failures, stateful tools, and validation
120
120
 
121
121
  ## Vitest
@@ -137,3 +137,24 @@ If you get TypeScript errors with custom matchers, make sure your `tsconfig.json
137
137
  "include": ["node_modules/@slbdn/mcp-tester/vitest.d.ts"]
138
138
  }
139
139
  ```
140
+
141
+ ## Browser and Edge Runtimes
142
+
143
+ **"Module not found: node:child_process" / "cross-spawn" in a browser build**
144
+ Your bundler is not honouring the `browser` field. Ensure it targets `browser`
145
+ (Webpack `target: 'web'`, Vite `build.target` not `ssr`, esbuild
146
+ `platform: 'browser'`). The repo's `npm run test:browser` script is a minimal
147
+ reference. Do not import `@slbdn/mcp-tester/dist/...` deep paths — import the
148
+ package root so the browser entry point is selected.
149
+
150
+ **"WebSocket is not defined" (Node.js)**
151
+ The WebSocket transport needs a global `WebSocket`. Node.js 22+ provides one;
152
+ on older versions enable it with `--experimental-websocket` or set a polyfill
153
+ such as `ws` on `globalThis`.
154
+
155
+ **CORS errors against a remote MCP server**
156
+ The server must allow your origin and, for Streamable HTTP, the
157
+ `mcp-session-id` header. This is a browser/network constraint — configure the
158
+ server, not the client.
159
+
160
+ See [Browser Support](./browser.md) for the full matrix.
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@slbdn/mcp-tester",
3
- "version": "1.5.4",
3
+ "version": "1.6.0",
4
4
  "description": "Minimal MCP client implementation for CI/CD testing of MCP servers",
5
5
  "main": "dist/index.js",
6
+ "browser": {
7
+ "./dist/index.js": "./dist/browser.js",
8
+ "./dist/utils/stdio-transport.js": false
9
+ },
6
10
  "types": "dist/index.d.ts",
7
11
  "type": "module",
8
12
  "sideEffects": false,
@@ -24,6 +28,7 @@
24
28
  "test:watch": "jest --watch",
25
29
  "test:coverage": "jest --coverage",
26
30
  "test:package": "node scripts/smoke-test-package.mjs",
31
+ "test:browser": "node scripts/check-browser-bundle.mjs",
27
32
  "benchmark": "tsx src/__tests__/benchmarks.ts",
28
33
  "lint": "eslint src --ext .ts",
29
34
  "format": "prettier --write \"src/**/*.ts\"",
@@ -77,6 +82,7 @@
77
82
  "@types/node": "^20.19.41",
78
83
  "@typescript-eslint/eslint-plugin": "^8.60.0",
79
84
  "@typescript-eslint/parser": "^8.60.0",
85
+ "esbuild": "^0.28.2",
80
86
  "eslint": "^8.56.0",
81
87
  "fast-check": "^4.8.0",
82
88
  "husky": "^9.1.7",