* refactor(launcher): self-contained validate; split smokes into top-level step (#1504) Previously `validate:launcher` built cli/tui/web and ran the cross-client smokes, so the launcher's "validation" depended on every other client being built — and the launcher had no lint/format/coverage of its own. - clients/launcher: add a web-style `validate` (format:check && lint && build && test:coverage) with a Node-only eslint flat config, prettier, and a per-file coverage gate (parse-launcher-argv.ts covered; src/index.ts excluded as binary bootstrap). 100% on the gated file. - Root `validate:launcher` now just delegates: `cd clients/launcher && npm run validate` — no dependency on the other clients. - New top-level `smoke` step (smoke:launcher + smoke:cli + smoke:tui + smoke:web) with NO build commands; `validate` runs it last. Each client's own validation builds its bundle first, so nothing builds twice (cli/tui via test:coverage, launcher via its validate; smoke:web builds dist on demand). - clients/tui test:coverage now builds the TUI bundle (mirroring cli) so the build-free smoke has it available. - CI: replace the standalone cli/tui/launcher build step with a "Validate launcher" step. Docs updated (AGENTS.md, launcher README). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(cli,tui,root): every client self-validates; smoke separate from validate Extends the launcher self-validate work to the CLI and TUI, and makes the top-level `validate` nothing but the per-client validations — smokes are now a separate top-level target. - clients/cli, clients/tui: add a web-style `validate` with a Node flat eslint config + prettier. CLI lint/format pass as-is. TUI registers eslint-plugin-react-hooks for the classic rules only (rules-of-hooks + exhaustive-deps, which the source already references); the stricter react-hooks@7 rules are not enforced on the interim component surface (#1501). - Avoid double builds: - CLI's e2e spawns the built binary, so its `test:coverage` builds first; `validate` folds that in (`format:check && lint && test:coverage`, no separate `build`). - TUI tests don't spawn a bundle, so `validate` keeps an explicit `build` (for the smoke) and `test:coverage` is pure. - Root: `validate` = validate:web && validate:cli && validate:tui && validate:launcher (each delegates to the client's own validate). `smoke` is no longer part of `validate` — it's a separate top-level target run after a build/validate, with no build commands of its own. - CI: per-client "Validate X" steps + a single "Run cross-client smokes" (npm run smoke). Docs updated (AGENTS.md, cli/tui READMEs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(root): drop redundant test aggregator scripts Now that every client self-validates and the top-level `validate` chains the per-client validations, the root `test` / `test:web` / `test:cli` / `test:tui` / `test:coverage` aggregators are redundant — run `npm run validate` (all) or `cd clients/<name> && npm run validate` (one). `test:storybook` stays (it's the one check not part of validate). CI was already using each client's own scripts, so it's unaffected. Docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(web): silence happy-dom infra noise in unit runs Passing unit runs printed dozens of alarming-but-expected lines that made green runs look broken — all from happy-dom performing real I/O with no server behind it: - `<iframe src="/sandbox.html">` (MCP Apps sandbox) made happy-dom fetch and later unload that URL, emitting ~40 `DOMException [NetworkError/ AbortError]` + `AsyncTaskManager destroyed` lines. Disable child-frame navigation in the unit project (the tests only assert on the iframe element/attributes, not its document), removing the noise with no coverage loss. - App-level renders read `GET /api/config` via useSandboxUrl / useServerListWritable, 404-ing ~76x. Add a benign default `fetch` (empty 200) in the unit setup; tests that exercise fetch still override it. Negative tests that assert on errors already suppress their own console output (e.g. jsonUtils/schemaUtils); CLI, TUI, and the web integration suite were already clean. Unit suite is now noise-free; 2424 web tests pass and the coverage gate is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(scripts): make validate fast (test), split coverage into its own target Each client's `validate` now runs `test` instead of `test:coverage`, so the everyday pre-push check skips v8 instrumentation (and, for web, the slower integration project). The per-file coverage gate moves to a dedicated top-level `coverage` script: coverage = coverage:web && coverage:cli && coverage:tui && coverage:launcher coverage:<name> = cd clients/<name> && npm run test:coverage `validate` still builds each client's bundle (cli via its test `pretest`, the others via an explicit `build`), so `npm run validate && npm run smoke` works unchanged. CI now runs `validate` (fast checks + builds), then `coverage` (the gate), then `smoke` — collapsing the old per-client steps. Docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: drop the coverage gate from CI; keep web integration as its own step CI no longer runs `npm run coverage` — the per-file coverage gate is now a local/manual check (`npm run coverage` before pushing). To avoid silently dropping the web integration suite (it only ran via test:coverage, and `validate` runs the unit project only), CI gains a standalone `clients/web` `test:integration` step that exercises the real stdio/HTTP/OAuth/storage paths without coverage instrumentation. CI is now: validate (fast) → web integration → smoke → storybook. Docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(smoke): give smoke:launcher its own script like the others smoke:launcher was the odd one out — an inline chain of three `node clients/launcher/build/index.js ... --help` commands while smoke:cli / smoke:tui / smoke:web each run a `scripts/smoke-*.mjs`. Extract it to `scripts/smoke-launcher.mjs`, which runs the same `--help` / `--cli --help` / `--tui --help` dispatch checks but now also asserts each prints that mode's usage banner (proving the launcher resolved the right client build) and verifies the required builds exist up front with a clear error. Prints a `smoke:launcher OK — ...` summary in the same style as the rest. Docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: reorder package.json scripts for readability Move frequently-run scripts toward the top and rarely-run-by-hand ones (pretest hooks, lint/format internals, etc.) toward the end, consistently across the root and client manifests. Pure reordering — no script names or values changed, and no other manifest content is touched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: drop references to removed root inspector / test:storybook scripts The reordering dropped the root `inspector` and `test:storybook` aggregators (intentional — `inspector` is redundant with `web`, and storybook is run from clients/web in CI). Update AGENTS.md so it no longer points at `npm run inspector`, and run `test:storybook` from `clients/web/`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: address PR #1505 review (format glob gap, fetch-baseline comment) - TUI format/format:check now also cover the client-root source files (index.ts, tui.tsx, dev.ts) that live outside src/ — `eslint .` already lints them, so they were linted but not format-checked. (cli/launcher have no source outside src/; their root .ts are single-quoted configs intentionally left out of prettier, matching web.) - Reword the setup.ts default-fetch comment: it overstated auto-restore (the unit project sets no restoreMocks/unstubGlobals), and now also flags that the permissive 200 default means a test asserting on a fetch FAILURE must install its own rejecting stub. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: remove redundant root install:clients script `install:clients` ran the same `node scripts/install-clients.mjs` as `postinstall`, which fires automatically on `npm install` — so the manual alias was redundant. Drop it and the doc references (AGENTS.md, spec). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ci): skip smoke:tui in CI (Ink needs a real TTY) This PR made CI run the full `npm run smoke`, which surfaced that smoke:tui fails in headless CI: the Ink TUI throws "Raw mode is not supported" on mount (no TTY) and exits before rendering its first frame. The old CI never ran smoke:tui, so this was latent. smoke-tui.mjs now self-skips when `process.env.CI` is set — the boot/ render check is inherently local-only (a TUI needs a terminal). The TUI is still built and unit-tested in CI; `npm run smoke` stays the single command everywhere (full locally, tui-skipped in CI). Verified `CI=true npm run smoke` is green (launcher/cli OK, tui SKIPPED, web OK). Docs + CI comment updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
388ea5a7bd
commit
5efe9ddc7b
20 changed files with 5290 additions and 250 deletions
|
|
@ -110,13 +110,21 @@ While the Web Client provides a rich visual interface, the CLI is designed for:
|
|||
|
||||
## Development
|
||||
|
||||
Run the test suite (and coverage gate) from `clients/cli/`:
|
||||
Like the other clients, the CLI self-validates from its own folder:
|
||||
|
||||
```bash
|
||||
npm test # build test-servers + binary, then run all tests
|
||||
npm run test:coverage # same, under the per-file coverage gate
|
||||
npm run validate # format:check && lint && test:coverage
|
||||
npm test # build test-servers + binary, then run all tests
|
||||
npm run test:coverage # build + tests under the per-file coverage gate
|
||||
```
|
||||
|
||||
The CLI's `test:coverage` **builds the binary first** (its out-of-process
|
||||
`e2e.test.ts` spawns it, so it must run against a fresh build). `validate`
|
||||
therefore folds the build into `test:coverage` rather than repeating it — it is
|
||||
`format:check && lint && test:coverage`, with no separate `build` step (the
|
||||
other clients, whose tests don't spawn their bundle, keep an explicit `build`).
|
||||
The repo-root `validate:cli` just delegates here.
|
||||
|
||||
Tests run the CLI **in-process** (importing `runCli()`) so `src/` is measured
|
||||
under coverage, with a thin out-of-process spawn layer for the real binary. See
|
||||
[`__tests__/README.md`](./__tests__/README.md) for details.
|
||||
|
|
|
|||
19
clients/cli/eslint.config.js
Normal file
19
clients/cli/eslint.config.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
|
||||
// The CLI is plain Node TypeScript (no React/browser), so this mirrors the web
|
||||
// client's flat config minus the React/Storybook plugins.
|
||||
export default defineConfig([
|
||||
globalIgnores(['build', 'coverage']),
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
extends: [js.configs.recommended, tseslint.configs.recommended],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
globals: globals.node,
|
||||
},
|
||||
},
|
||||
]);
|
||||
1314
clients/cli/package-lock.json
generated
1314
clients/cli/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -17,7 +17,7 @@
|
|||
],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"pretest": "npm run test-servers:build && npm run build",
|
||||
"validate": "npm run format:check && npm run lint && npm run test",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:coverage": "npm run test-servers:build && npm run build && vitest run --coverage",
|
||||
|
|
@ -25,7 +25,11 @@
|
|||
"test:cli": "vitest run cli.test.ts",
|
||||
"test:cli-tools": "vitest run tools.test.ts",
|
||||
"test:cli-headers": "vitest run headers.test.ts",
|
||||
"test:cli-metadata": "vitest run metadata.test.ts"
|
||||
"test:cli-metadata": "vitest run metadata.test.ts",
|
||||
"pretest": "npm run test-servers:build && npm run build",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write src __tests__",
|
||||
"format:check": "prettier --check src __tests__"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
|
|
@ -37,10 +41,15 @@
|
|||
"zustand": "^5.0.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"@types/node": "^24.12.4",
|
||||
"@vitest/coverage-v8": "^4.1.0",
|
||||
"eslint": "^9.39.4",
|
||||
"globals": "^17.4.0",
|
||||
"prettier": "^3.8.1",
|
||||
"tsup": "^8.5.0",
|
||||
"typescript": "~5.9.3",
|
||||
"typescript-eslint": "^8.56.1",
|
||||
"vitest": "^4.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,15 +56,16 @@ dependencies are missing — the launcher exits with an actionable error telling
|
|||
you to run `npm run build` (from `clients/web`) or relaunch with `--dev` to use
|
||||
the Vite dev server. `--dev` never needs `dist/`; it runs Vite directly.
|
||||
|
||||
CI and `npm run validate` (via `validate:launcher`) exercise this prod path
|
||||
end-to-end with `npm run smoke:web` (`scripts/smoke-web.mjs`): it starts
|
||||
CI and `npm run validate` (via the top-level `smoke` step) exercise this prod
|
||||
path end-to-end with `npm run smoke:web` (`scripts/smoke-web.mjs`): it starts
|
||||
`mcp-inspector --web` against the built `dist/` and asserts `GET /` returns the
|
||||
SPA (HTTP 200) with the injected `__INSPECTOR_API_TOKEN__`.
|
||||
|
||||
### CLI and TUI smokes
|
||||
|
||||
`validate:launcher` also runs end-to-end smokes for the other two modes through
|
||||
the built launcher artifact (beyond the `--help` checks in `smoke:launcher`):
|
||||
The top-level `smoke` step also runs end-to-end smokes for the other two modes
|
||||
through the built launcher artifact (beyond the `--help` checks in
|
||||
`smoke:launcher`):
|
||||
|
||||
- `npm run smoke:cli` (`scripts/smoke-cli.mjs`) — runs `mcp-inspector --cli`
|
||||
against the bundled stdio test server via a temp `--catalog` and asserts
|
||||
|
|
@ -78,6 +79,21 @@ the built launcher artifact (beyond the `--help` checks in `smoke:launcher`):
|
|||
|
||||
Both build `test-servers/build` on demand if it is missing.
|
||||
|
||||
## Development
|
||||
|
||||
Like the web client, the launcher self-validates from its own folder:
|
||||
|
||||
```bash
|
||||
npm run validate # format:check && lint && build && test:coverage
|
||||
```
|
||||
|
||||
This has **no** dependency on the other clients being built — it only checks the
|
||||
launcher's own source. `eslint.config.js` is a Node-only flat config (the web
|
||||
client's React/Storybook plugins stripped out), and the per-file coverage gate
|
||||
covers `parse-launcher-argv.ts` (the pure arg-parsing logic); `src/index.ts` is
|
||||
excluded as binary bootstrap and is instead exercised by the smokes above. The
|
||||
repo-root `validate:launcher` simply delegates here (`cd clients/launcher && npm run validate`).
|
||||
|
||||
## Publishing
|
||||
|
||||
The root `@modelcontextprotocol/inspector` package ships as one fat tarball: `npm run build` at the repo root builds all clients, then `prepack` runs before `npm publish`. Runtime dependencies are declared on the root `package.json`; client builds bundle `@inspector/core` and externalize npm packages resolved from the root install.
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ describe("parseLauncherArgv", () => {
|
|||
});
|
||||
|
||||
it("does not treat a trailing mode-like token as launcher mode", () => {
|
||||
expect(parseLauncherArgv([...EXEC, "node", "./server.js", "--cli"])).toEqual(
|
||||
{
|
||||
mode: "web",
|
||||
forwardedArgv: [...EXEC, "node", "./server.js", "--cli"],
|
||||
hasPrefixModeFlag: false,
|
||||
},
|
||||
);
|
||||
expect(
|
||||
parseLauncherArgv([...EXEC, "node", "./server.js", "--cli"]),
|
||||
).toEqual({
|
||||
mode: "web",
|
||||
forwardedArgv: [...EXEC, "node", "./server.js", "--cli"],
|
||||
hasPrefixModeFlag: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects multiple mode flags in the launcher prefix", () => {
|
||||
|
|
|
|||
19
clients/launcher/eslint.config.js
Normal file
19
clients/launcher/eslint.config.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
|
||||
// The launcher is plain Node TypeScript (no React/browser), so this mirrors
|
||||
// the web client's flat config minus the React/Storybook plugins.
|
||||
export default defineConfig([
|
||||
globalIgnores(['build', 'coverage']),
|
||||
{
|
||||
files: ['**/*.{ts,mts}'],
|
||||
extends: [js.configs.recommended, tseslint.configs.recommended],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
globals: globals.node,
|
||||
},
|
||||
},
|
||||
]);
|
||||
1772
clients/launcher/package-lock.json
generated
1772
clients/launcher/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -15,14 +15,25 @@
|
|||
"scripts": {
|
||||
"build": "tsc",
|
||||
"postbuild": "node scripts/make-executable.js",
|
||||
"test": "vitest run"
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write src __tests__",
|
||||
"format:check": "prettier --check src __tests__",
|
||||
"test": "vitest run",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"validate": "npm run format:check && npm run lint && npm run build && npm run test"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "^13.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"@types/node": "^24.12.4",
|
||||
"@vitest/coverage-v8": "^4.1.0",
|
||||
"eslint": "^9.39.4",
|
||||
"globals": "^17.4.0",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "~5.9.3",
|
||||
"typescript-eslint": "^8.56.1",
|
||||
"vitest": "^4.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ try {
|
|||
|
||||
const { mode, forwardedArgv, hasPrefixModeFlag } = parsedArgv;
|
||||
|
||||
const helpOnly =
|
||||
process.argv.includes("-h") || process.argv.includes("--help");
|
||||
const helpOnly = process.argv.includes("-h") || process.argv.includes("--help");
|
||||
|
||||
if (helpOnly && !hasPrefixModeFlag) {
|
||||
program.outputHelp();
|
||||
|
|
|
|||
30
clients/launcher/vitest.config.ts
Normal file
30
clients/launcher/vitest.config.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: false,
|
||||
environment: 'node',
|
||||
include: ['__tests__/**/*.test.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'html', 'json-summary'],
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: [
|
||||
// Binary bootstrap: shebang + commander wiring that dynamically imports
|
||||
// each client's build/index.js and dispatches via process.exit. It only
|
||||
// runs when launched as the real binary and is covered end-to-end by the
|
||||
// launcher smokes (smoke:launcher / smoke:cli / smoke:tui / smoke:web).
|
||||
// Mirrors the cli/web `index.ts` exclusion. The pure arg-parsing logic
|
||||
// lives in parse-launcher-argv.ts, which is unit-tested and gated below.
|
||||
'src/index.ts',
|
||||
],
|
||||
thresholds: {
|
||||
perFile: true,
|
||||
lines: 90,
|
||||
statements: 85,
|
||||
functions: 80,
|
||||
branches: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -61,13 +61,19 @@ The TUI provides terminal-native tabs and panes for interacting with your MCP se
|
|||
|
||||
## Development
|
||||
|
||||
Run the test suite from `clients/tui/`:
|
||||
Like the other clients, the TUI self-validates from its own folder:
|
||||
|
||||
```bash
|
||||
npm run validate # format:check && lint && build && test:coverage
|
||||
npm test # run all tests
|
||||
npm run test:coverage # run tests under the per-file coverage gate
|
||||
```
|
||||
|
||||
The repo-root `validate:tui` just delegates here. `eslint.config.js` registers
|
||||
`react-hooks` for the classic rules only (rules-of-hooks + exhaustive-deps); the
|
||||
stricter react-hooks@7 rules are not enforced on the interim component surface
|
||||
(#1501).
|
||||
|
||||
Tests live in `__tests__/`. The coverage gate currently covers the TUI's
|
||||
non-React logic (server resolution, logger, tab metadata, and the `utils/`
|
||||
form/URL helpers); the Ink components and `App.tsx` are an interim exclusion in
|
||||
|
|
|
|||
33
clients/tui/eslint.config.js
Normal file
33
clients/tui/eslint.config.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
|
||||
// The TUI is Node + React (Ink) TypeScript. This mirrors the web client's flat
|
||||
// config minus the browser-only plugins (react-refresh / Storybook).
|
||||
//
|
||||
// react-hooks is registered for the two classic rules the Ink components were
|
||||
// written against (rules-of-hooks + exhaustive-deps, which the source already
|
||||
// references in inline disable directives). We deliberately do NOT pull in the
|
||||
// full react-hooks@7 "recommended" set: its newer error-level rules (e.g.
|
||||
// set-state-in-effect) flag long-standing, intentional patterns in the
|
||||
// component/hook surface that is itself the interim-excluded one tracked in
|
||||
// #1501. Enforcing them would require refactoring code out of scope here.
|
||||
export default defineConfig([
|
||||
globalIgnores(['build', 'coverage']),
|
||||
{
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
plugins: { 'react-hooks': reactHooks },
|
||||
extends: [js.configs.recommended, tseslint.configs.recommended],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
globals: globals.node,
|
||||
},
|
||||
rules: {
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': 'warn',
|
||||
},
|
||||
},
|
||||
]);
|
||||
1772
clients/tui/package-lock.json
generated
1772
clients/tui/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -17,10 +17,14 @@
|
|||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "vite-node --config vitest.config.ts dev.ts",
|
||||
"build": "tsup",
|
||||
"validate": "npm run format:check && npm run lint && npm run build && npm run test",
|
||||
"test": "vitest run",
|
||||
"test:coverage": "vitest run --coverage"
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write src __tests__ index.ts tui.tsx dev.ts",
|
||||
"format:check": "prettier --check src __tests__ index.ts tui.tsx dev.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
|
|
@ -40,12 +44,18 @@
|
|||
"ink-select-input": "^6.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"@types/node": "^24.12.4",
|
||||
"@types/react": "^19.2.14",
|
||||
"@vitest/coverage-v8": "^4.1.0",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"globals": "^17.4.0",
|
||||
"prettier": "^3.8.1",
|
||||
"tsup": "^8.5.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "~5.9.3",
|
||||
"typescript-eslint": "^8.56.1",
|
||||
"vite": "^8.0.16",
|
||||
"vite-node": "^6.0.0",
|
||||
"vitest": "^4.1.0"
|
||||
|
|
|
|||
|
|
@ -149,7 +149,10 @@ function App({
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
getTuiLogger().info({ serverNames: Object.keys(mcpServers) }, "TUI started");
|
||||
getTuiLogger().info(
|
||||
{ serverNames: Object.keys(mcpServers) },
|
||||
"TUI started",
|
||||
);
|
||||
}, [mcpServers]);
|
||||
|
||||
const [selectedServer, setSelectedServer] = useState<string | null>(null);
|
||||
|
|
@ -711,7 +714,9 @@ function App({
|
|||
oauthInProgressRef.current = true;
|
||||
setOauthStatus("authenticating");
|
||||
setOauthMessage(null);
|
||||
getTuiLogger().info("OAuth authentication started (Guided Auth advance step)");
|
||||
getTuiLogger().info(
|
||||
"OAuth authentication started (Guided Auth advance step)",
|
||||
);
|
||||
try {
|
||||
await selectedInspectorClient.proceedOAuthStep();
|
||||
const state = selectedInspectorClient.getOAuthState();
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ import type {
|
|||
MCPServerConfig,
|
||||
InspectorClient,
|
||||
} from "@inspector/core/mcp/index.js";
|
||||
import type {
|
||||
AuthGuidedState,
|
||||
OAuthStep,
|
||||
} from "@inspector/core/auth/index.js";
|
||||
import type { AuthGuidedState, OAuthStep } from "@inspector/core/auth/index.js";
|
||||
|
||||
const STEP_LABELS: Record<OAuthStep, string> = {
|
||||
metadata_discovery: "Metadata Discovery",
|
||||
|
|
|
|||
|
|
@ -15,21 +15,21 @@
|
|||
"build": "tsc -b && vite build && npm run build:runner",
|
||||
"build:client": "vite build",
|
||||
"build:runner": "tsup --config tsup.runner.config.ts",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write src",
|
||||
"format:check": "prettier --check src",
|
||||
"validate": "npm run format:check && npm run lint && npm run build && npm run test:coverage",
|
||||
"preview": "vite preview",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build",
|
||||
"build:storybook": "storybook build",
|
||||
"validate": "npm run format:check && npm run lint && npm run build && npm run test",
|
||||
"test": "vitest run --project=unit",
|
||||
"test:watch": "vitest --project=unit",
|
||||
"test:coverage": "npm run test-servers:build && vitest run --project=unit --project=integration --coverage",
|
||||
"test:storybook": "vitest run --project=storybook",
|
||||
"test:storybook:watch": "vitest --project=storybook",
|
||||
"test-servers:build": "tsc -p ../../test-servers --noCheck",
|
||||
"test:integration": "npm run test-servers:build && vitest run --project=integration",
|
||||
"test:integration:watch": "npm run test-servers:build && vitest --project=integration"
|
||||
"test:integration:watch": "npm run test-servers:build && vitest --project=integration",
|
||||
"test-servers:build": "tsc -p ../../test-servers --noCheck",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write src",
|
||||
"format:check": "prettier --check src",
|
||||
"preview": "vite preview",
|
||||
"storybook": "storybook dev -p 6006"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
|
|
|
|||
|
|
@ -33,6 +33,30 @@ Object.defineProperty(window, "localStorage", {
|
|||
value: new MemoryStorage(),
|
||||
});
|
||||
|
||||
// Benign default `fetch`. Several components hit the backend on mount — e.g.
|
||||
// the app reads `GET /api/config` via `useSandboxUrl` / `useServerListWritable`.
|
||||
// Under happy-dom (no server) those real requests 404 and log alarming
|
||||
// `GET .../api/config 404 (Not Found)` lines that make a green run look broken.
|
||||
// Returning an empty 200 keeps such *incidental* calls quiet. Tests that care
|
||||
// about fetch install their own per-test spy/stub (e.g. `vi.spyOn(globalThis,
|
||||
// "fetch")`), which Vitest only auto-reverts to this baseline if the test
|
||||
// registers it through Vitest's mock APIs — this project doesn't set
|
||||
// `restoreMocks` globally, so a test that mutates `globalThis.fetch` directly
|
||||
// must restore it itself. Note the tradeoff: this default is permissive, so a
|
||||
// test meaning to assert on a fetch FAILURE must set up its own rejecting/
|
||||
// erroring stub rather than relying on the absence of a server.
|
||||
Object.defineProperty(globalThis, "fetch", {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () =>
|
||||
Promise.resolve(
|
||||
new Response("{}", {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
window.localStorage.clear();
|
||||
|
|
|
|||
|
|
@ -162,6 +162,19 @@ export default defineConfig({
|
|||
test: {
|
||||
name: 'unit',
|
||||
environment: 'happy-dom',
|
||||
// Don't let happy-dom actually navigate child frames. Components like
|
||||
// the MCP Apps sandbox render an <iframe src="/sandbox.html">; with
|
||||
// navigation enabled happy-dom fetches that URL (and unloads it on
|
||||
// teardown), which fails under the test server and floods the run with
|
||||
// alarming-but-expected `DOMException [NetworkError/AbortError]` and
|
||||
// `AsyncTaskManager destroyed` output. The component tests only assert
|
||||
// on the iframe element/attributes, not its loaded document, so
|
||||
// disabling frame navigation removes the noise without losing coverage.
|
||||
environmentOptions: {
|
||||
happyDOM: {
|
||||
settings: { navigation: { disableChildFrameNavigation: true } },
|
||||
},
|
||||
},
|
||||
// Root the unit project at the repo root so vitest's coverage
|
||||
// transformer (which only processes files inside a project root)
|
||||
// can reach core/ modules. Without this, untested core/ files fall
|
||||
|
|
|
|||
Loading…
Reference in a new issue