Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,20 @@ v2/main/
│ │ # Shared by BOTH form builders — web SchemaForm
│ │ # and TUI schemaToForm — since each dispatches on
│ │ # a single `type` string and would otherwise miss
│ │ # a nullable field entirely — #1928/#2015)
│ │ # a nullable field entirely — #1928/#2015;
│ │ # schemaLint.ts: tool-schema PORTABILITY lint —
│ │ # constructs that are legal JSON Schema and are
│ │ # refused or mishandled by real MCP clients (a bare
│ │ # `true` in `properties`, an array-form `type`, a
│ │ # remote `$ref`, a schema carrying no validation
│ │ # keyword, a non-object root). Deliberately NOT a
│ │ # validator: a census of 617 public servers found 0
│ │ # that fail the SDK's own parse, so a conformance
│ │ # check reports nothing on real servers. ONE verdict
│ │ # for all three clients — the CLI's `--strict` report
│ │ # (exit 6), the TUI's tool detail pane, the web Tools
│ │ # tab — so they cannot disagree about what is
│ │ # portable; the report TEXT is shared too — #1005)
│ ├── logging/ # Silent pino logger singleton
│ ├── mcp/ # InspectorClient runtime + state stores
│ │ # (uriTemplate.ts: RFC 6570 parse/classify/expand.
Expand Down
47 changes: 45 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ inspector/
│ │ # plus per-server secret storage — the keychain/file/memory SecretStore
│ │ # implementations, the selection policy, and the descriptor the banner and UI report
│ ├── client/ # Install-level client config (`client.json`): browser-safe parse/validate + Node load/save, remote backend, secrets
│ ├── json/ # JSON + parameter/argument conversion utilities, and the nullable-union
│ │ # schema collapse shared by the web and TUI form builders
│ ├── json/ # JSON + parameter/argument conversion utilities, the nullable-union
│ │ # schema collapse shared by the web and TUI form builders, and the
│ │ # tool-schema portability lint all three clients report from
│ ├── logging/ # Silent pino logger singleton
│ ├── mcp/ # InspectorClient runtime, state stores, transports, config import,
│ │ # and the RFC 6570 URI-template helpers the web form and TUI expand through
Expand Down Expand Up @@ -152,6 +153,7 @@ Each config below is a ready-made server for exercising one feature by hand. Loa
| `structured-output-http.json` | Tools tab: a result's `structuredContent` section | [#1908](https://github.com/modelcontextprotocol/inspector/issues/1908) |
| `duplicate-tool-names-http.json` | A `tools/list` that repeats a tool name | [#1957](https://github.com/modelcontextprotocol/inspector/issues/1957) |
| `nullable-fields-http.json` | Tools tab: nullable (`anyOf` + `null`) arguments | [#1928](https://github.com/modelcontextprotocol/inspector/issues/1928) |
| `unportable-schemas-http.json` **(legacy era)** | Tool schemas a real client rejects, flagged in all three clients | [#1005](https://github.com/modelcontextprotocol/inspector/issues/1005) |
| `rfc6570-templates-http.json` | Resources tab: RFC 6570 resource-template expansion | [#1919](https://github.com/modelcontextprotocol/inspector/issues/1919) |
| `advertised-extensions-http.json` | Tool registration gated on advertised extensions | [#1739](https://github.com/modelcontextprotocol/inspector/issues/1739) |
| `oauth-custom-resource-metadata-http.json` **(legacy era)** | OAuth discovery driven by the challenge's `resource_metadata` | [#2071](https://github.com/modelcontextprotocol/inspector/issues/2071) |
Expand Down Expand Up @@ -286,6 +288,47 @@ Open the Tools tab and select `record_shipment`: `direction` must render as a **

The **TUI** had the same gap and is worth checking against the same server (`--tui`, then test `record_shipment`): `direction` is a select, `quantity` an integer field, `express` a boolean. Both clients now share one collapse step — `normalizeNullableUnion` in [`core/json/nullableUnion.ts`](./core/json/nullableUnion.ts) — precisely so they cannot drift on which schemas they can render.

#### Unportable tool schemas

`unportable-schemas-http.json` serves three tools, two of whose advertised
schemas carry constructs that are legal JSON Schema and are refused or
mishandled by real MCP clients: `echo` declares `outputSchema.properties.data`
as a bare `true` (what Go's `jsonschema` package emits for `interface{}` —
the case reported in [#1005](https://github.com/modelcontextprotocol/inspector/issues/1005)),
an array-form `"type": ["null","boolean"]`, and an `opts` property that
constrains nothing; `add` points a property at a remote `$ref`. `get_temp` is
left clean, so a flagged tool sits next to an unflagged one. Plain
streamable-HTTP — connect with the **default (legacy)** protocol era.

The Inspector is where a server author looks first, so a construct that will
fail downstream is named here rather than passed through silently. All three
clients report the same verdict from
[`core/json/schemaLint.ts`](./core/json/schemaLint.ts), each with the room it
has:

```bash
mcp-inspector --cli http://127.0.0.1:6603/mcp --method tools/list --strict # exits 6
```

- **CLI** — `--strict` prints the full report (path, issue, suggested fix) on
stderr and exits `6` on an error-severity finding; without it, one summary
line. See [Schema portability](./clients/cli/README.md#schema-portability---strict).
- **TUI** — the tools list marks `echo` with a red `!` and `add` with a yellow
`?`; the detail pane lists each finding under **Schema Portability**.
- **Web** — the Tools sidebar row carries the same flag as a hover-labelled
icon, and selecting the tool shows a **Schema portability** section above the
argument form.

This is deliberately **not** a JSON Schema validator. A census of 617 public
servers (14,804 tool schemas) reported on that issue found **0** that fail the
SDK's own `ListToolsResultSchema.safeParse`, so a conformance check would
report nothing on essentially every real server. What bites is the narrower
subset each consumer accepts, and each rule here is a construct known to be
refused or degraded by a shipping client. The schemas are supplied through the
test server's `rawToolSchemas` override, because the Zod-built presets cannot
express any of them — which is the same reason a real server hits this only
when its schemas come from another generator.

#### RFC 6570 resource templates

`rfc6570-templates-http.json` serves two resource templates straight out of [#1919](https://github.com/modelcontextprotocol/inspector/issues/1919) — `events_by_topic` (`foobar://events/{topic}`) and `events_by_query` (`foobar://events{?topic}`) — each echoing the URI it was matched against, plus a plain `foobar://events` resource (see below). Plain streamable-HTTP; connect with the **default (legacy)** protocol era.
Expand Down
46 changes: 46 additions & 0 deletions clients/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Options that specify the MCP server (catalog/config file, ad-hoc command/URL, en
| `--tool-metadata <key=value>` | Tool-specific `_meta` entries for `tools/call`. Same JSON-parsed value handling as `--metadata`. |
| `--connect-timeout <ms>` | Connection timeout in ms. Defaults to `15000` for ad-hoc `--server-url`/target runs (so a black-holed host fails fast) and to the file-level timeout for `--catalog`/`--config` runs. `0` disables the timeout. |
| `--app-info` | Probe a tool's MCP App UI metadata without invoking it. With `--method tools/call --tool-name <name>`: prints one JSON line (`hasApp`, `resourceUri`, `csp`, `permissions`, `domain`, …) and exits `0` if the tool has an app or `2` (`no_app`) if not. With `--method tools/list`: emits NDJSON — one app-info line per tool over a single connection. |
| `--strict` | With `--method tools/list`: report tool-schema portability problems in full (path, issue, suggested fix) on stderr, and exit `6` if any is error-severity. Without it, a one-line count is printed instead. See [Schema portability](#schema-portability---strict). |
| `--format <text\|json>` | Output format. `text` (default) pretty-prints the result. `json` emits a single JSON object on stdout (`{ "result": … }`, plus `{ "appInfo": … }` as a sibling key for App tools) with no banners, so the whole output pipes cleanly into `jq`. |
| `--relogin` | Delete stored OAuth for this server URL from the shared store before connect; interactive login still only runs if the server requires auth. Requires an HTTP/SSE URL (rejected for stdio). Conflicts with `--stored-auth-only` / `--use-stored-auth` / `--wait-for-auth` / catalog short-circuits. |
| `--stored-auth-only` | **CI / non-interactive safe:** never start interactive OAuth / step-up (and never auto-open a browser); use the shared store if present, otherwise fail immediately with `auth_required`. Prefer this over a bare pipe/CI run that would otherwise attempt interactive login. |
Expand Down Expand Up @@ -152,6 +153,50 @@ mcp-inspector --cli <server> --method tools/call --tool-name my_app_tool --forma

A `tools/call` that returns `isError:true` still prints its payload but exits `5` (`tool_is_error`) so `&&` chains don't proceed on a failed call.

#### Schema portability (`--strict`)

A tool schema can be perfectly legal JSON Schema and still be refused by the
client the server is meant to run against. `--strict` (with `--method
tools/list`) names those constructs — path, what is wrong, and a concrete fix —
on **stderr**, and exits `6` if any is error-severity:

```bash
mcp-inspector --cli <server> --method tools/list --strict
```

```text
Error: tool "info"
Path: outputSchema.properties.data
Issue: Bare `true` used where a schema object is expected.
Suggestion: Spell the "anything" schema out — `{"type": "object", "additionalProperties": true}` …
Comment thread
Copilot marked this conversation as resolved.
Outdated

1 error, 3 warnings across 2 tools.
```

Severity decides the exit code, not the report: **errors** are constructs a
shipping MCP client refuses outright (a bare `true` in a `properties` map, a
non-object schema root), **warnings** are ones handled unevenly (an array-form
`type`, a remote `$ref`, a schema carrying no validation keyword at all). Only
errors fail the run — a `--strict` that failed on warnings would be unusable as
a CI gate against servers that are in fact fine.

The report goes to stderr, so the result on stdout stays parseable. Under
`--format json` the findings are folded into the same envelope instead
(`{"result":…,"schemaFindings":[…]}`), so a caller reads one document rather
than correlating two.

**Without `--strict` nothing changes except one line.** A `tools/list` whose
schemas have findings prints a single stderr summary — `Schema portability: 1
error, 3 warnings across 2 tools. Re-run with --strict for details.` — and
still exits `0`. A clean list prints nothing at all.

This is deliberately not a JSON Schema validator. A census of 617 public
servers found **zero** that fail the SDK's own parser, so a conformance check
would report nothing on essentially every real server; what bites is the
narrower subset each consumer accepts, which is what these rules encode. The
same verdict drives the TUI's tool detail pane and the web Tools tab — all
three read `core/json/schemaLint`.

### CLI-specific (OAuth for HTTP servers)

The CLI runs the same loopback callback server as the TUI (`http://127.0.0.1:6276/oauth/callback` by default).
Expand Down Expand Up @@ -250,6 +295,7 @@ prose from stderr:
| `3` | Server requires authentication (401/403, `WWW-Authenticate`, OAuth). |
| `4` | Server unreachable (DNS, connection refused, timeout, `fetch failed`). |
| `5` | Tool error (`tools/call` returned `isError:true`, or the tool was not found). |
| `6` | `--strict` found an error-severity tool-schema portability problem. |

On any non-zero exit the CLI also writes a single JSON line to **stderr** — the
`ErrorEnvelope`:
Expand Down
Loading