From 01572c31b9ed7bfc7a88036c80ff5bc314f5be27 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Sun, 23 Aug 2026 13:20:17 -0400 Subject: [PATCH] docs: note that the CLI does not yet apply catalog metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The field table said this payload is "merged into every outgoing request", which is true of web and the TUI but not the CLI — it never passes the persisted value to its client, so the setting is inert for `--cli` runs (#2093). Scope the claim and point at the per-invocation `--metadata` flag as the CLI workaround, so a CLI user isn't told a setting works when it doesn't. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh Signed-off-by: cliffhall --- docs/mcp-server-configuration.md | 34 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/mcp-server-configuration.md b/docs/mcp-server-configuration.md index c6bbd29b..67f98b12 100644 --- a/docs/mcp-server-configuration.md +++ b/docs/mcp-server-configuration.md @@ -163,19 +163,19 @@ The file is the familiar MCP client-config shape — an `mcpServers` object keye These have no analog in the broader `mcp.json` ecosystem. Each is **omitted on write when it equals its default**, so a round-trip through the Inspector keeps the file diff minimal. -| Field | Default | Meaning | -| -------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------- | -| `protocolEra` | `"legacy"` | `"legacy"` \| `"auto"` \| `"modern"` — which protocol era to negotiate, orthogonal to the transport | -| `modernLogLevel` | `"debug"` | Per-request log level stamped on modern connections, or `"off"`. Legacy connections ignore it | -| `roots` | — | Roots advertised via the `roots` client capability; each is `{ uri, name? }` | -| `metadata` | — | Default `_meta` payload merged into every outgoing request — a JSON object, values may be any JSON | -| `connectionTimeout` / `requestTimeout` | — | Timeouts in ms | -| `taskTtl` | `60000` | TTL in ms for tasks created via "Run as task" (`DEFAULT_TASK_TTL_MS`) | -| `autoRefreshOnListChanged` | `false` | Refresh lists automatically on `*/list_changed` instead of only flagging the indicator | -| `paginatedLists` | `false` | Fetch tools/resources/prompts one page at a time instead of auto-aggregating | -| `advertisedExtensions` | — | Per-extension overrides for what the Inspector declares in `capabilities.extensions` | -| `maxFetchRequests` | `1000` | Network-log retention for this server (`DEFAULT_MAX_FETCH_REQUESTS`); `0` means unlimited | -| `oauth` | — | `{ clientId, clientSecret, scopes, authorizationParams, authorizationUrl, tokenUrl, enterpriseManaged, onInsufficientScope }` | +| Field | Default | Meaning | +| -------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `protocolEra` | `"legacy"` | `"legacy"` \| `"auto"` \| `"modern"` — which protocol era to negotiate, orthogonal to the transport | +| `modernLogLevel` | `"debug"` | Per-request log level stamped on modern connections, or `"off"`. Legacy connections ignore it | +| `roots` | — | Roots advertised via the `roots` client capability; each is `{ uri, name? }` | +| `metadata` | — | Default `_meta` payload merged into every outgoing request — a JSON object, values may be any JSON. Web and TUI only today; see the CLI note below | +| `connectionTimeout` / `requestTimeout` | — | Timeouts in ms | +| `taskTtl` | `60000` | TTL in ms for tasks created via "Run as task" (`DEFAULT_TASK_TTL_MS`) | +| `autoRefreshOnListChanged` | `false` | Refresh lists automatically on `*/list_changed` instead of only flagging the indicator | +| `paginatedLists` | `false` | Fetch tools/resources/prompts one page at a time instead of auto-aggregating | +| `advertisedExtensions` | — | Per-extension overrides for what the Inspector declares in `capabilities.extensions` | +| `maxFetchRequests` | `1000` | Network-log retention for this server (`DEFAULT_MAX_FETCH_REQUESTS`); `0` means unlimited | +| `oauth` | — | `{ clientId, clientSecret, scopes, authorizationParams, authorizationUrl, tokenUrl, enterpriseManaged, onInsufficientScope }` | `metadata` is a JSON **object**, and its values may be any JSON — an object, an array, a number, a boolean, `null` — not only a string. Nothing in the MCP spec restricts `_meta` value types, and the SDK models the field as a passthrough object, so the Inspector does not narrow it either ([#1910](https://github.com/modelcontextprotocol/inspector/issues/1910)). Edit it in Server Settings → Request Metadata, which is a JSON editor rather than the key/value rows `headers` and `env` use; text that is not a JSON object is flagged inline and not applied. @@ -187,17 +187,19 @@ These have no analog in the broader `mcp.json` ecosystem. Each is **omitted on w } ``` +> **Not yet applied by the CLI.** The web client and the TUI both send this payload on every request. The CLI does not — it never passes the persisted value to its client, so a `metadata` entry is currently inert for `--cli` runs ([#2093](https://github.com/modelcontextprotocol/inspector/issues/2093)). Use the per-invocation `--metadata` flag there in the meantime. + > **Reading an older file.** Before #1910 this field was a `[{ "key": …, "value": … }]` pair array. That shape is still **read**, so an existing `mcp.json` keeps working unchanged; it is never written back, so the field is rewritten as an object the next time the Inspector saves the entry. `oauth.authorizationParams` is a string→string record of extra query parameters merged into the OAuth **authorization request** URL only — never the token request. Use it for provider-specific hints the core specs don't standardize (Keycloak's `kc_idp_hint`, OIDC's `login_hint` / `prompt` / `acr_values`, Auth0's `audience`). The protocol-critical parameters — `client_id`, `code_challenge`, `code_challenge_method`, `redirect_uri`, `resource`, `response_type`, `scope`, `state` — are **reserved**: the web form rejects them inline, and any that reach the merge anyway are dropped with a warning rather than overriding what the flow set (overriding them breaks PKCE, the CSRF state binding, or RFC 8707). Edit them in Server Settings → Authorization ("Additional authorization parameters"), beside Scopes. -`oauth.authorizationUrl` and `oauth.tokenUrl` override the `authorization_endpoint` and `token_endpoint` that authorization-server metadata discovery resolved. The Inspector deliberately has no such fields by default — it resolves both from the AS's metadata document, exactly as a real MCP host does — but a server under development often advertises its *production* authorization server while you want to hit staging. Set either (they are independent) to an absolute `http(s)` URL and it replaces what the metadata returned, for both the authorization request and the token request; leave blank to use discovery. A malformed value is flagged inline in the form and dropped with a warning at connect time rather than failing the connection. Edit them in Server Settings → Authorization ("Authorization URL override" / "Token URL override"). +`oauth.authorizationUrl` and `oauth.tokenUrl` override the `authorization_endpoint` and `token_endpoint` that authorization-server metadata discovery resolved. The Inspector deliberately has no such fields by default — it resolves both from the AS's metadata document, exactly as a real MCP host does — but a server under development often advertises its _production_ authorization server while you want to hit staging. Set either (they are independent) to an absolute `http(s)` URL and it replaces what the metadata returned, for both the authorization request and the token request; leave blank to use discovery. A malformed value is flagged inline in the form and dropped with a warning at connect time rather than failing the connection. Edit them in Server Settings → Authorization ("Authorization URL override" / "Token URL override"). -They redirect the **endpoints**, not the authorization server's identity: `issuer` is left exactly as discovery returned it, because that is what RFC 9207 / SEP-2352 validate the callback's `iss` against to defend against an authorization-server mix-up. So they fit alternate endpoints of the *same* logical issuer — a staging deployment fronting the same issuer, a local proxy. Point one at an authorization server advertising a **different** issuer and the callback is rejected as an issuer mismatch before the code is redeemed; that is the mix-up defense working, and it is not disabled to make the override succeed. +They redirect the **endpoints**, not the authorization server's identity: `issuer` is left exactly as discovery returned it, because that is what RFC 9207 / SEP-2352 validate the callback's `iss` against to defend against an authorization-server mix-up. So they fit alternate endpoints of the _same_ logical issuer — a staging deployment fronting the same issuer, a local proxy. Point one at an authorization server advertising a **different** issuer and the callback is rejected as an issuer mismatch before the code is redeemed; that is the mix-up defense working, and it is not disabled to make the override succeed. They are **not applied under enterprise-managed authorization** (`oauth.enterpriseManaged`), for the same reason `oauth.authorizationParams` isn't: that flow authorizes against the enterprise IdP — a different authorization server, whose OIDC discovery would otherwise be rewritten too, pointing the IdP login (or the IdP code exchange) at the resource server's authorization server. -> Because the override is applied to the *discovered metadata document*, a server whose authorization server publishes no metadata at all is unaffected — there the SDK falls back to `/authorize` and `/token` on the AS origin, and there is nothing to override. +> Because the override is applied to the _discovered metadata document_, a server whose authorization server publishes no metadata at all is unaffected — there the SDK falls back to `/authorize` and `/token` on the AS origin, and there is nothing to override. A catalog carrying these fields: