chore: close quality-gate coverage gaps (#1689)#1766
Conversation
Three pre-existing gaps in the repo's quality gates, all closed: 1. cli & tui `src` are now typechecked. Each gains a `typecheck` script (`tsc --noEmit`) folded into `validate`; their tsconfigs mirror web's effective options (DOM lib, bundler resolution, no noUncheckedIndexedAccess) so imported `core/` validates the same way web validates it. Fixes the real errors this surfaced: cli's closure-narrowed `destroy()` in a finally, and tui's InfoTab reading `headers` off the transport config (they live on the per-server settings) + a too-narrow schemaToForm parameter type. 2. `core/` now has a format + lint gate. New root `validate:core` (`format:check:core` + `lint:core` via a root eslint.config.js) runs first in the root `validate`; 22 pre-existing unformatted core files reformatted. Root gains prettier/eslint devDependencies for this. 3. `core/json` and `core/client` are now in web's coverage `include` and gated ≥90. Added remote.test.ts and node-persistence.test.ts and extended the json/client tests to cover the newly-measured lines. Gates verified locally: validate:core, cli/tui validate (typecheck + tests), and coverage:web (4459 tests, 0 threshold failures) / coverage:cli / coverage:tui all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
|
@claude review |
|
Claude finished @cliffhall's task in 5m 13s —— View job Review: chore: close quality-gate coverage gaps (#1689)
Verdict: solid, and the three real bug/type fixes are correct. I verified the whole Findings (2 worth acting on, rest are nits / follow-ups)1. No root
|
- Add a root `format` script chaining format:core + each client's format, so the documented "npm run format" works from the root; note it in README + AGENTS (review #1). - Soften the cli/tui tsconfig comments: they mirror web's module/lib *resolution* only, not its extra strictness flags — so cli/tui src is checked slightly more loosely than web src (review #2). - Guard schemaToForm against a non-object property value (malformed server schema `properties: { foo: null }`) instead of throwing on `.title`; add a test (review #3). - Add optional `secretStore` injection to loadRunnerClientConfig and use an InMemorySecretStore + vi.stubEnv in its tests so the result no longer depends on the developer's real OS keychain / env (review #4, #5). - Justify remote.test.ts's `as unknown as Response` double cast inline per the AGENTS rule (review #5). - Widen the core lint/format globs and the root eslint `files` to `{ts,tsx}` so a future core/**/*.tsx can't silently escape (review #6). - Update README's quality-gate table for validate:core + cli/tui typecheck (review #7). Also merges origin/v2/main into the branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
|
Thanks for the thorough review. Addressed in a58288a (also merged Fixed:
Deferred (out of #1689's scope, noted for a follow-up): the remaining #6 gaps — prettier-checking #8 pr-screenshots — those two commits are @cliffhall's own (deliberately removing the screenshots), not part of the #1689 work; flagging your note about the Focused coverage after the changes: all core/json + core/client files still ≥90 (runner.ts branch 94.33%, remote/config 100%), cli/tui typecheck clean. |
|
@claude review |
|
Claude finished @cliffhall's task in 5m 36s —— View job Review: chore: close quality-gate coverage gaps (#1689) — round 2
Verdict: all seven review items are genuinely addressed, and I found one new real issue — the CI run 30124149500 is still in progress, and this checkout has no New finding: reformat broke a coverage annotation (worth fixing)
|
| # | Item | Status |
|---|---|---|
| 1 | Root format script |
✅ chains format:core + all four clients; every client does have a format script, and the cd clients/web && … && cd ../cli && … relative chain resolves correctly. |
| 2 | tsconfig comments softened | ✅ both now say "module/lib resolution options" and explicitly state cli/tui src is checked more loosely than web's. |
| 3 | schemaToForm guard |
✅ typeof prop === "object" && prop !== null + a test asserting properties: { bad: null, worse: 42 } degrades to two string fields instead of throwing. |
| 4 | secretStore injection |
✅ options?.secretStore ?? new KeyringSecretStore(). Worth noting the test that omits the store is fully deterministic anyway: its client.json is absent, so readClientConfigStore returns {} before ever calling secretStore.get(), and KeyringSecretStore has no constructor body — no real keychain touch on any platform. |
| 5 | Test hygiene | ✅ vi.stubEnv + vi.unstubAllEnvs(); the as unknown as Response justification is accurate (Response has ~20 required members, so as Response really is a TS2352). |
| 6 | {ts,tsx} globs |
✅ in lint:core / format:core / format:check:core and the root eslint files. (@typescript-eslint/parser enables JSX by extension, so a future core/*.tsx will at least parse — it just won't get react-hooks rules, which matches the config's own comment.) |
| 7 | README quality-gate table | ✅ mentions validate:core and the cli/tui typecheck. |
Nits / follow-ups (non-blocking)
a. AGENTS.md now contradicts package.json on two points. The new bullet still says format:check:core (prettier --check "core/**/*.ts") + lint:core (eslint "core/**/*.ts") — fix #6 widened both to {ts,tsx}. And the cli/tui bullet still claims the tsconfigs "mirror clients/web/tsconfig.app.json's effective options", which is the exact wording fix #2 softened in the code comments. Two one-line edits.
Fix this →
b. Prettier version skew across the repo. The root adds prettier: ^3.9.6 (lock resolves 3.9.6) while the clients' own locks pin 3.8.1 (web) / 3.8.4 (cli, tui, launcher). Nothing breaks — only root prettier ever formats core/, and only client prettier ever formats client dirs — but the repo now has two formatter versions producing two styles, visible in the reformat itself: core/ unions collapsed to the hugged form (export type ConnectionStatus =\n "disconnected" | "connecting" | …) while every client union is in the leading-| form. Pinning the root to the clients' range (or bumping all five together) keeps one style for the whole tree.
c. destroyManagedState silences the error without restoring the check. The helper is a clean, cast-free fix for the finally-block narrowing (clients/cli/src/cli.ts:130), and the doc comment explains it well. Be aware it isn't stronger than the cast it replaces: because every argument is narrowed to null at the call site, the { destroy(): void } parameter type never actually validates anything. If you want real checking, collect the states as they're created inside the closure (const teardowns: Array<{ destroy(): void }> = []; teardowns.push((managedToolsState = new ManagedToolsState(…)))) and iterate in finally. Fine to leave as-is for this PR.
d. InfoTab's header block is now duplicated verbatim across the sse and streamable-http branches (clients/tui/src/components/InfoTab.tsx:119-146). Since headerPairs is hoisted, both branches render byte-identical JSX — a small HeaderLine subcomponent (or hoisting the block above the ternary) would remove the copy. Pre-existing duplication, just now more obviously copy-pasted.
e. moduleResolution: "bundler" for cli/tui is right for tsup-bundled output but no longer models Node's own resolution (it accepts imports Node ESM would reject). Correct trade-off given the goal was to validate core/ the way web does — worth knowing that the typecheck gate won't catch a Node-resolution-only mistake.
f. Still open from round 1, deferred by agreement: prettier-checking clients/web/server, root scripts/ / test-servers/src / vitest.shared.mts / eslint.config.js, and typechecking clients/{cli,tui}/__tests__. Filing that tracking issue would be good — those are the same class of gap this PR closes.
g. pr-screenshots/ (out of scope, @cliffhall's own commits). Re-confirming what I flagged last round, now verified against the tree: .github/ on this branch contains only workflows/, so the 318-line README is not relocated anywhere in this diff. Nothing in the repo links to pr-screenshots/ any more (only .gitignore mentions it), so no broken in-repo links — but merged PR bodies that hotlink raw.githubusercontent.com/.../v2/main/pr-screenshots/… will 404. The .gitignore entry is also still unanchored (pr-screenshots vs /pr-screenshots/), so it ignores nested dirs of that name too.
Things I checked and found correct
core/diff is formatting-only. I diffed every one of the 22 changedcore/files at whitespace-insensitive level and read the full-wdiff end to end: every change is prettier reflow (line joins/splits, trailing-comma removal on collapse, parenthesized cast reflow, one missing trailing newline incore/mcp/node/authChallengeFetch.ts). No identifier, operator, or control-flow change anywhere. The two non-formattingcore/edits are the intentional ones:runner.ts'ssecretStoreoption and theSecretStoretype import.InfoTabfix remains correct.InspectorServerSettings.headersis{ key: string; value: string }[](core/mcp/types.ts:611) with noenabledflag, so rendering every pair is right;MCPServerConfigstill has noheaders, confirming the display block was previously dead. Tests now build settings through the realheadersToServerSettingshelper instead of a lying double cast — good change.App.tsxthreadsselectedServerEntry?.settings, which is the same entry the config already comes from.- tsconfig mechanics.
allowImportingTsExtensionsis paired withnoEmit(required); removingoutDir/rootDirfrom tui is safe since tsup sets its own;pathsin the child configs resolve relative to the child config file (nobaseUrl), so../../core/*and./node_modules/@types/reactboth land correctly;core/is checked transitively through cli/tui imports even thoughincludeis onlysrc. - New tests are well-targeted.
node-persistence.test.tscovers all fourmigrateClientPlaintextSecretbranches (empty keychain / existing value wins /KeychainUnavailableErrorbail leaving plaintext on disk / non-keychain rethrow) plus both delete paths;remote.test.tscovers 404-vs-500, header presence/absence, non-object JSON, and theglobalThis.fetchfallback; theconfig.test.ts/jsonUtils.test.tsadditions hit exactly the newly-measured lines (toRecord,getClientConfigFilePath, the three CIMD validator messages,convertParameterValue's empty-string short-circuit and JSON-parse fallback). - Coverage
includewidening is a strict superset of the oldcore/json/xMcpHeader.tsentry, and matches the{ts,tsx}pattern used by its siblings.
- Fix a v8 ignore annotation the core reformat broke: prettier split the
statement under `/* v8 ignore next */` in inspectorClient.ts across two
lines, so the `?? MODERN_PROTOCOL_VERSION` branch was no longer ignored.
Changed to `v8 ignore next 2` (review: new finding).
- Pin root prettier to ^3.8.1 (installed 3.8.4) to match the clients, and
reformat core/ so the whole tree shares one style (leading-`|` unions)
instead of the 3.9.x hugged form (review nit b).
- Dedup InfoTab's sse/streamable-http header block into a single hoisted
`headersBlock` (review nit d).
- Fix AGENTS.md doc drift: `{ts,tsx}` core globs and the softened cli/tui
tsconfig wording now match the code (review nit a).
Deferred gap-closing items (review nit f) tracked in #1767.
Gate re-verified: web coverage 4459 tests / 0 threshold failures,
validate:core + cli/tui typecheck clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
|
Round 2 addressed in f572b0f: New finding (fixed): the Nit a (fixed): AGENTS.md no longer drifts from the code — the Nit b (fixed): pinned root prettier to Nit d (fixed): hoisted the duplicated sse/streamable-http header JSX into a single Nits c, e: left as-is per your "fine to leave" — noted. (c: Nit f (tracked): filed #1767 for the remaining gap-closing items (prettier-check #8 pr-screenshots: still @cliffhall's own commits — relaying your verified notes (the README isn't relocated into |
|
@claude review |
|
Claude finished @cliffhall's task in 4m 41s —— View job Review: chore: close quality-gate coverage gaps (#1689) — round 3
Verdict: ready to merge from my side. All four round-2 items are correctly addressed, the Verification of the round-2 fixes
Semantic drift re-check. Worth calling out: the new gate paid for itself immediately — Nits (non-blocking)1. cli/tui 2. Duplicate import in 3. Prettier lock skew is narrowed, not eliminated. Declared ranges now agree ( 4. 5. Placement note, no change requested. Things I checked and found correct
|
All non-blocking (round-3 verdict was "ready to merge"):
- Make the inspectorClient.ts v8 annotation reflow-proof: switched from
`v8 ignore next 2` to a `v8 ignore start`/`stop` bracket around the
single statement (repo already uses this form), so neither a split nor
a rejoin by a future prettier can mis-count the ignored branch.
- Widen the cli/tui tsconfig `exclude` test glob to `**/*.test.{ts,tsx}`
so a future in-src `.test.tsx` isn't treated differently from `.test.ts`.
- Fold the duplicate `secrets.js` import in config.test.ts into one block.
- Converge the prettier lock: bump clients/web to 3.8.4 (matching root +
cli/tui/launcher) while keeping every declared range at ^3.8.1, so the
whole tree resolves one prettier version.
Gate re-verified: web coverage 4459 tests / 0 threshold failures,
validate:core + cli/tui typecheck clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
|
Thanks — round 3's nits addressed in e15515d (all four):
#5 (placement note) — left as-is per your "no change requested"; the FS-touching unit tests match the existing Gate re-verified after the changes: web coverage 4459 tests / 0 threshold failures, |
|
@claude review |
|
Claude finished @cliffhall's task in 2m 19s —— View job Review: chore: close quality-gate coverage gaps (#1689) — round 4
Verdict: three of the four round-3 fixes are correct; the fourth doesn't work, and that one is my fault — my round-3 nit #1 gave bad advice. TypeScript's Finding:
|
exclude entry |
foo.test.ts in the program? |
|---|---|
"**/*.test.{ts,tsx}" |
yes — TS2322 reported |
"**/*.test.ts", "**/*.test.tsx" |
no — not listed, no error |
(Run with the global tsc on this runner, 7.0.2, not the repo's pinned ~5.9.3 — the brace behavior is identical in both, since it's the absence of a feature rather than a version difference.)
The repo already has the correct precedent: clients/web/tsconfig.app.json:62-70 spells out both extensions separately (src/**/*.test.tsx, src/**/*.test.ts, ../../core/**/*.test.ts, ../../core/**/*.test.tsx), and clients/tui/vitest.config.ts does the same in its coverage exclude. Note vitest/tinyglobby does support braces — that asymmetry is exactly what makes this an easy trap, and it's why the {ts,tsx} widening was right for lint:core/format:core (prettier + eslint globs) but wrong here.
Practical impact today: none. include is src/**/* (cli) and index.ts / tui.tsx / src/**/* (tui), and there are no *.test.ts(x) files under either src — all tests live in __tests__/, which include never reaches. So the gate result you reported (cli/tui typecheck clean) is unaffected either way. The problem is that the guard is now inert: the first in-src test file would be pulled into typecheck unexpectedly, which is the exact scenario the entry exists to prevent.
Fix:
"exclude": ["node_modules", "**/*.test.ts", "**/*.test.tsx", "build"]Verification of the round-3 fixes
| # | Item | Status |
|---|---|---|
| 1 | tsconfig exclude glob widened |
❌ See above — the brace form doesn't work in tsconfig. My advice, my error; the fix is two entries. |
| 2 | Duplicate secrets.js import folded |
✅ config.test.ts now has exactly one from "@inspector/core/client/secrets.js" (line 15), with hasClientPlaintextSecret in the existing block. I also scanned all five touched/new test files for any other duplicate module specifier — none. |
| 3 | Prettier lock converged | ✅ All five locks now resolve 3.8.4 (root, web, cli, tui, launcher) while every declared range stays ^3.8.1. The skew I flagged in round 2/3 is gone. |
| 4 | v8 ignore start/stop bracket |
✅ Correct and tight: core/mcp/inspectorClient.ts:1830 opens, :1835 closes, wrapping only the const protocolVersion = … ?? MODERN_PROTOCOL_VERSION; statement. Counted the pairs in the file — exactly one start and one stop, so there's no unbalanced start silently ignoring the file tail. The other 10 annotations in the file are all next N and untouched. |
Semantic-drift re-check (cumulative, not just e15515d9). Re-read the full git diff -w --ignore-blank-lines origin/v2/main...HEAD -- 'core/**' end to end. Every hunk is a prettier reflow — constructor-param joins, import-list collapse/expand, parenthesized-cast reflow, union splits, ternary joins. The only non-formatting core/ changes remain the three intentional ones in core/client/runner.ts (the secretStore?: SecretStore option, the type SecretStore import, options?.secretStore ?? new KeyringSecretStore()) plus the v8 comment rewrite above. No identifier, operator, or control-flow change anywhere.
Still standing from earlier rounds (all settled, no action)
- Nits c (
destroyManagedStatedoesn't restore real type checking) and e (moduleResolution: bundlerwon't catch Node-resolution-only mistakes) — accepted as-is by agreement. - Round-3 nit 5 (FS-touching tests in the unit project) — matches the
config.test.tsprecedent; no change requested. - Remaining gap-closers tracked in Close remaining quality-gate gaps: web/server + root-file format, cli/tui __tests__ typecheck #1767 (prettier-check
clients/web/server, rootscripts//test-servers/src/vitest.shared.mts/eslint.config.js, typecheck cli/tui__tests__). Worth noting the brace-glob trap above is a good argument for including "audit theexcludeglobs when__tests__joinstypecheck" in that issue's scope. pr-screenshots/removal remains @cliffhall's own two commits (1a798daa,69a57193), outside Close quality-gate coverage gaps: typecheck cli/tui, format+lint core/, coverage core/json+core/client #1689's scope. Re-verified against the current tree: the 318-line README is still not present anywhere under.github/, and the.gitignoreentry is still unanchored (pr-screenshotsrather than/pr-screenshots/). Both are for them to confirm as intended.
I could not run the gate locally (no node_modules in this checkout), so all findings are from reading the tree plus the isolated tsc experiment above. Once the one-line exclude change lands, this looks good to merge from my side.
· branch v2/quality-gate-gaps-1689
TypeScript's tsconfig include/exclude globs don't support brace expansion (microsoft/TypeScript#5583), so the round-3 `**/*.test.{ts,tsx}` matched nothing and silently dropped the previous `.test.ts` exclusion. Split it into two entries (`**/*.test.ts` + `**/*.test.tsx`), the form clients/web/tsconfig.app.json already uses. Verified empirically: a broken src/*.test.ts is now excluded from typecheck while a broken src/*.ts is still caught. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
|
Good catch — fixed in ea856b5. You're right that tsconfig Verified empirically: a broken Everything else (the other three round-3 fixes, semantic-drift, the |
|
@claude review |
|
Claude finished @cliffhall's task in 2m 0s —— View job Review: chore: close quality-gate coverage gaps (#1689) — round 5
Verdict: no findings — LGTM to merge. The round-4 Empirical verification of
|
exclude entry |
Result |
|---|---|
"**/*.test.ts", "**/*.test.tsx" (this PR, ea856b5d) |
✅ exit 0 — both test files excluded |
"**/*.test.{ts,tsx}" (previous, e15515d9) |
❌ exit 1 — both test files compiled and errored, i.e. the entry matched nothing |
control: add a non-test src/ctrl.ts with the same error |
✅ exit 1 — TS2322 reported, so the program isn't accidentally empty |
That last row is the check that matters most: it proves the exclusion is narrow rather than swallowing the whole include. Matches your own probe result (src/__probe.test.ts excluded, src/__probe.ts caught), and matches clients/web/tsconfig.app.json:62-70's existing separate-entry form. Both clients/cli/tsconfig.json:24 and clients/tui/tsconfig.json:29 carry the two entries.
Final re-audit (cumulative, whole diff)
core/ is formatting-only. Re-ran git diff -w --ignore-blank-lines origin/v2/main...HEAD -- 'core/**' and read every hunk: constructor-param joins, import collapse/expand, union splits, parenthesized-cast reflow, ternary joins, one EOF newline. No identifier, operator, or control-flow change. The only non-formatting core/ edits remain the four intentional ones — core/client/runner.ts's secretStore?: SecretStore field, the type SecretStore import, options?.secretStore ?? new KeyringSecretStore(), and the v8 ignore comment rewrite in inspectorClient.ts.
Worth noting the one reflow that looks semantic but isn't: core/client/runner.ts:98 collapsed the oauthFromCli ternary condition onto a single line (cliOverrides?.clientId || cliOverrides?.clientSecret || clientMetadataUrl ? { … }). || binds tighter than ?:, so precedence is unchanged — it just reads more densely.
v8 ignore balance. core/mcp/inspectorClient.ts has exactly one start (1830) and one stop (1835), bracketing only the const protocolVersion = … ?? MODERN_PROTOCOL_VERSION; statement — no unbalanced start silently ignoring the file tail. The other 10 sites are all next N and untouched by the diff. Re-grepped the other touched core/ files: runner-oauth-callback.ts (6 sites, all above/below the single line-34 join, unaffected) and authChallengeFetch.ts (1 site, only change is the EOF newline); everything else has zero annotations.
Docs now match the code. AGENTS.md's validate:core bullet shows the {ts,tsx} globs and the "module/lib resolution options" wording; README's quality-gate table names validate:core and the cli/tui typecheck; the tsconfig comments say the same thing. No drift left between the three.
Root tooling. format chain (format:core → web → cli → tui → launcher) resolves correctly — the relative cd ../cli runs in the shell already inside clients/web, and no client's own format cds. Root eslint.config.js scopes files to core/**/*.{ts,tsx}, so client eslint . runs (which resolve their own config from cwd) are unaffected. Root devDeps don't reach the tarball (files allowlist unchanged).
Real code fixes re-read and unchanged from my round-1/2/3 verification: destroyManagedState (cast-free, well-documented), the InfoTab headers fix with the hoisted headersBlock, and schemaToForm's typeof prop === "object" && prop !== null guard with its test.
Still standing, all settled — no action
- Round-2 nits c (
destroyManagedStatedoesn't restore real type checking — its parameter validates nothing, since every call-site argument is narrowed tonull) and e (moduleResolution: bundlerwon't catch Node-resolution-only mistakes): accepted as-is by agreement. - Round-3 nit 5 (FS-touching tests in the unit project rather than
src/test/integration/): matches theconfig.test.tsprecedent; no change requested. - Remaining gap-closers tracked in Close remaining quality-gate gaps: web/server + root-file format, cli/tui __tests__ typecheck #1767 — prettier-check
clients/web/server, rootscripts//test-servers/src/vitest.shared.mts/eslint.config.js, typecheck cli/tui__tests__. The brace-glob trap is a good thing to fold into that issue's scope, since bringing__tests__undertypecheckmeans touching these sameexcludeglobs. pr-screenshots/removal remains @cliffhall's own two commits (1a798daa,69a57193), outside Close quality-gate coverage gaps: typecheck cli/tui, format+lint core/, coverage core/json+core/client #1689's scope. Re-verified against the current tree:.github/still contains onlyworkflows/, so the 318-line README is not relocated anywhere; and.gitignore:23is still the unanchoredpr-screenshotsrather than/pr-screenshots/. Both for @cliffhall to confirm as intended.
Caveat on scope of verification: this checkout has no node_modules (root or any client), so I could not run the repo's own gate — findings are from reading the tree plus the isolated tsc probe above (global tsc 7.0.2; the brace behavior is the absence of a feature, identical under the repo's pinned ~5.9.3). I'm relying on your reported gate results (web coverage 4459 tests / 0 threshold failures, validate:core + cli/tui typecheck clean). CI run 30126143641 is still in progress at time of writing — worth a glance before merging.
· branch v2/quality-gate-gaps-1689
|
Thanks for the five thorough rounds — appreciate the empirical |
Closes #1689
Closes the three pre-existing quality-gate gaps found while auditing the SDK v2 migration (#1688): cli/tui
srcwas never typechecked,core/had no format/lint gate, andcore/json/core/clientweren't in the coverageinclude.1. cli & tui
srcare now typecheckedtypecheckscript (tsc --noEmit -p tsconfig.json) folded into itsvalidate.tsconfig.jsonnow mirrorsclients/web/tsconfig.app.json's effective options — DOM lib,moduleResolution: bundler, and nonoUncheckedIndexedAccess(web's app config doesn't extendtsconfig.base, so re-enabling it would flagcore/issues web itself never gates). This validates the importedcore/sources the same way web does, collapsing ~130 tsconfig-noise errors down to the genuinely-real ones.cli.ts: themanaged*Statelocals are assigned inside therunMethodclosure, so the outerfinallystill narrowed them tonulland?.destroy()typed asnever. Routed teardown through a small typeddestroyManagedState()helper (no cast).InfoTab.tsx: was reading.headersoff the transportMCPServerConfig, but headers live on the per-serverInspectorServerSettings(pair-array). ThreadedserverSettingsin and read headers from there (a latent display bug — the block never rendered before).schemaToForm.ts: widened theJsonSchemaObject.propertiesvalue type tounknownso the SDK's broadly-typedTool["inputSchema"]is assignable (each value was already narrowed at the point of use).2.
core/format + lint gatevalidate:core=format:check:core(prettier --check "core/**/*.ts") +lint:core(eslint "core/**/*.ts"via a new rooteslint.config.js), run first in the rootvalidate.core/files reformatted (formatting-only)._-prefix as the intentionally-unused marker.3.
core/json+core/clientcoverageincludetocore/json/**andcore/client/**.remote.test.ts(fetch-mocked load/save) andnode-persistence.test.ts(keychain migration, unavailable-keychain bail, delete paths); extended the json/client tests for the newly-measured lines (toRecord,getClientConfigFilePath,loadRunnerClientConfig, CIMD validators, …).Verification
All gates green locally:
validate:core,cli/tuivalidate(typecheck + tests: 212 / 279)coverage:web— 4459 tests, 0 threshold failures (core/client 97.6%, core/json 100%),coverage:cli,coverage:tuiall exit 0🤖 Generated with Claude Code