fix: rename the strict exit code, cover the config path, refresh the docs

Copilot review round 3.

- Rename the `--strict` failure from "invalid" to "unportable"
  (`EXIT_CODES.SCHEMA_UNPORTABLE`, envelope `schema_unportable`, and the
  message text). The module argues at length that these schemas ARE
  valid JSON Schema and are merely refused by some clients, so a
  machine-readable code of `schema_invalid` told automated callers the
  opposite. Done now, while nothing depends on it.

- Add a fixture-path integration case. Every other test built a
  `ServerConfig` by hand, so `resolveConfig`'s `rawToolSchemas`
  forwarding was uncovered and `unportable-schemas-http.json` was loaded
  by nothing — the shipped fixture could have lost its overrides with
  the suite still green. It now goes through loadConfig/resolveConfig
  and asserts the exact per-tool verdict the README claims.

- Refresh the CLI README's sample output, which still showed the
  pre-round-2 wording and fixture, and so dropped the contract warning
  that rewording existed to add. Captured from a real run rather than
  written by hand, plus a paragraph making the suggestion pattern
  explicit.

- Drop the self-referential "[Try it](#unportable-tool-schemas)" link in
  the root README, which pointed back at its own section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UL9retfvAXRgvi6EWk4SY4
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-25 02:17:58 -04:00
commit a3d864ed6e

View file

@ -1,7 +1,9 @@
import { describe, it, expect, afterEach } from "vitest";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { InspectorClient } from "@inspector/core/mcp/inspectorClient.js";
import { createTransportNode } from "@inspector/core/mcp/node/transport.js";
import { lintTools } from "@inspector/core/json/schemaLint.js";
import { countFindings, lintTools } from "@inspector/core/json/schemaLint.js";
import {
createTestServerHttp,
type TestServerHttp,
@ -9,6 +11,8 @@ import {
createEchoTool,
createGetWeatherTool,
createGetTempTool,
loadConfig,
resolveConfig,
} from "@modelcontextprotocol/inspector-test-server";
import type { ServerConfig } from "@modelcontextprotocol/inspector-test-server";
@ -211,6 +215,49 @@ describe("rawToolSchemas override (#1005)", () => {
);
});
it("serves the shape the showcase config declares", async () => {
// Covers the JSON → ConfigFile → ServerConfig plumbing, not just the
// in-process option: a config file is how the manual repro and the
// screenshots in #1005 are produced, and `resolveConfig` forwarding
// `rawToolSchemas` is a line nothing else exercises. Without this, the
// shipped fixture could silently lose its overrides while every other
// test in this file stayed green.
const configPath = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"../../../../../../test-servers/configs/unportable-schemas-http.json",
);
const resolved = resolveConfig(loadConfig(configPath));
expect(Object.keys(resolved.rawToolSchemas ?? {}).sort()).toEqual([
"add",
"echo",
"get_temp",
]);
// Let the harness pick the port instead of the config's fixed one, so this
// test can't collide with a manually-running showcase server.
const started = await start({
tools: resolved.tools,
rawToolSchemas: resolved.rawToolSchemas,
});
const connected = await connect(started.url);
const { tools } = await connected.listAllTools();
// The exact verdict the README and the PR screenshots claim: one error on
// get_temp, warnings on echo and add, get_weather clean.
const results = lintTools(tools);
expect(
results.map((r) => ({
tool: r.toolName,
rules: r.findings.map((f) => f.rule).sort(),
})),
).toEqual([
{ tool: "get_temp", rules: ["boolean-schema"] },
{ tool: "echo", rules: ["type-union", "untyped-schema"] },
{ tool: "add", rules: ["remote-ref"] },
]);
expect(countFindings(results)).toEqual({ errors: 1, warnings: 3 });
});
it("keeps a structured-content tool callable under an outputSchema override", async () => {
// The shape the showcase config actually ships: the bare `true` rides a
// tool that returns structured content, and the override stays permissive