Skip to content

Configure MCP clients from the MCP gateway in external mode - #529

Merged
pufit merged 2 commits into
mainfrom
alex/mcp-gateway-catalog
Oct 20, 2026
Merged

pufit merged 2 commits into
mainfrom
alex/mcp-gateway-catalog

Conversation

@alex-clickhouse

Copy link
Copy Markdown
Collaborator

What

In external mode (NERVE_AUTH_MODE=external), an MCP gateway decides which
MCP servers the agent can use and holds the upstream credentials. Nerve:

  • reads GET <mcp_gateway_url>/catalog when it starts, before each new
    session, and on an MCP reload. The request has no credential and does
    not use the proxy environment variables; it takes at most 5 seconds.
  • reads revision and servers, and id, url, revision of each
    server, and ignores other members. IDs must be valid and in ascending
    order; each url resolves against the catalog URL to the same scheme,
    host and port, without user information. A catalog that is not valid
    is refused as a whole, and Nerve keeps the last catalog (none after
    startup: Nerve's own server only). One read runs at a time; a session
    that waits for a read in progress uses its result.
  • uses only the catalog servers, not mcp_servers, and loads no Claude
    Code plugin. Each server is an HTTP MCP server at its gateway URL,
    without headers. Claude gets --strict-mcp-config. Codex gets
    default_tools_approval_mode = "approve" for each gateway server.
  • gives new sessions a changed catalog. A running session keeps its
    servers.

New setting: mcp_gateway_url (restart-only). Local mode does not change.

Why

Agents on hosted machines get their organization's MCP servers through
the gateway, and the machine holds no upstream credential.

Against the old PR (#498)

The gateway's catalog is smaller now. Not ported: schema version 2
(generation, digest, desired revisions, tool policy, inventory, health),
background retry, diagnostics and UI for the catalog, read-only MCP
server API, nerve doctor lines, refusals of MCP reloads and of MCP keys
in propose_config_change, Claude allowed_tools, and the Codex feature,
config-file, collision, and Ultracode handling. A client policy in the
machine image (a separate change) refuses the other MCP paths. Running
sessions follow catalog changes in a later PR.

Verification

  • tests/test_mcp_gateway.py: document checks, URL resolution, failures
    keep the last catalog, timeout, no proxy (real loopback server), other
    members ignored, a waiting call uses the read in progress.
  • tests/test_external_mode_mcp.py: Claude and Codex configuration, no
    catalog, a server named nerve, new sessions only, outage then recovery,
    initialize without plugins, reload, local mode, the setting.
  • Full backend suite: 6078 passed, 38 skipped (6116 collected).

🤖 Generated with Claude Code

alex-clickhouse and others added 2 commits October 20, 2026 07:22
The MCP gateway serves GET /catalog to the agent's machine: an opaque
revision and, for each MCP server, id, displayName, description, url and
an opaque revision. url is a URL reference that the client resolves
against the URL of the catalog request.

McpGatewayCatalog reads this document and keeps the last one that it
read. The request has no credential and does not use the proxy
environment variables, and it takes at most 5 seconds. Members that
Nerve does not use are ignored. A document without a valid revision or
server list, a server without a valid id, url or revision, servers out
of ID order, or a server URL with another scheme, host or port, or with
user information, is refused as a whole. A failed read keeps the last
catalog, and each new problem gives one warning. A read that starts
while another read is in progress waits for it and returns its result.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
In external mode an MCP gateway decides which MCP servers the agent can
use, and it holds the upstream credentials. The new setting
mcp_gateway_url names the gateway; the first boot of a hosted machine
writes it.

In external mode Nerve reads the catalog when it starts, before each new
session, and on an MCP reload. The servers of the last catalog read
replace the configured mcp_servers, and Nerve loads no Claude Code
plugin. Each server goes to the clients as an HTTP MCP server at its
gateway URL, without headers:

- Claude: the gateway servers and Nerve's own server, with
  --strict-mcp-config.
- Codex: mcp_servers.<id>.url and default_tools_approval_mode "approve",
  because the organization approved the tools at the gateway and Codex
  refuses an MCP tool that it would have to ask about under approval
  policy never.

A running session keeps the servers that it started with. Without a catalog, sessions start with Nerve's own
server only. A catalog server named "nerve" is not used. Local mode does
not change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@alex-clickhouse
alex-clickhouse force-pushed the alex/mcp-gateway-catalog branch from 24a27be to 22d1f5a Compare October 20, 2026 08:10
@alex-clickhouse
alex-clickhouse added this pull request to stack #531 October 20, 2026 12:22
@alex-clickhouse
alex-clickhouse marked this pull request as ready for review October 20, 2026 12:22
@pufit
pufit merged commit 1f9741c into main Oct 20, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants