Skip to content

MCP Proxy Configuration

An organization that already runs its own MCP proxy — a policy engine, a DLP gateway, a vendor proxy — can put it in front of any MCP in the AlienGiraffe Connector catalog. The proxy is configured per catalog MCP, as part of the normal “add an MCP to the catalog” flow: the admin writes the catalog entry so that the proxy is the entrypoint to that MCP. There is no separate, org-wide proxy feature and no proxy switch to turn on.

Lander then enforces it. On the device, the AI client only ever talks to Lander; Lander only ever talks to the entrypoint the catalog entry declares; and the entry declares the proxy. The hop after Lander is the one the admin chose:

AI client → Lander → your MCP proxy → upstream MCP server

Four things follow from this model, and the rest of the page explains each:

  1. Configuration is per catalog MCP. The proxy run configuration (command and args, or URL and headers) lives on the MCP’s catalog entry. Different MCPs can use different proxies, or none.
  2. Lander enforces it. Tool calls to a catalog MCP are always carried through the configured entrypoint. There is no fallback to a direct connection or to Lander’s native routing; if the entrypoint cannot be reached the call fails closed.
  3. Catalog purview. Lander can keep a device to the catalog: in enforce mode it removes MCPs the Connector has blocked or no longer declares, and it reports any MCP added by hand as an unmanaged change.
  4. Authentication and authorization are inherited. Whatever authN, authZ, RBAC, or permissioning the proxy (and the MCP server behind it) applies is respected as-is. Lander does not replace, add to, or bypass it.

Lander has no concept of a “proxy MCP” as a separate kind of thing. A catalog entry is a catalog entry: it declares an entrypoint (a command to spawn or a URL to connect to), and Lander installs, mirrors, audits, and governs it the same way whether that entrypoint is the vendor’s MCP server or your gateway in front of it. The proxy is simply the MCP, as far as the device is concerned; the upstream server behind it is your proxy’s business.

flowchart LR
  subgraph direct["Catalog entry without a proxy"]
    direction LR
    A1["AI client"] --> L1["Lander<br/>(mirror · audit · tool policy)"] --> M1["MCP server"]
  end
  subgraph proxied["Catalog entry whose install spec names your proxy"]
    direction LR
    A2["AI client"] --> L2["Lander<br/>(mirror · audit · tool policy)"] --> P2["Your MCP proxy<br/>(treated as the MCP)"] -.->|"opened by your proxy"| M2["Upstream MCP server"]
  end

Everything on the Lander side of the entrypoint is identical in both rows, which is why the proxy gets the full observability surface for free:

Lander behavior With your proxy as the entrypoint
Identity The MCP is known by the catalog entry’s slug. Events, logs, and dashboards carry that name, so you can tell which entry (and therefore which proxy configuration) produced a call.
Install The agent’s config is rewritten to point at Lander (lander serve-stdio --proxy <slug> or /stream/proxy/<slug>), with the original entrypoint stored as the upstream. Removing the entry restores the agent’s original config. See lossless restore.
Mirroring and audit Lander records the message it hands to the entrypoint and the message it gets back — the same access.log / query.log records and mcp.tool_call events as any other MCP.
Tool policy allowed_tools / disallowed_tools are applied by Lander before the call is forwarded, and disabled tools are filtered out of tools/list responses.
Lifecycle Approval, auto-install, block, and group access come from the same catalog flags as for any MCP.

Because observability happens around the entrypoint rather than inside it, you get a complete record of what your proxy was asked and what it answered without the proxy having to emit anything. What happens beyond the proxy — its connection to the upstream server — is outside what Lander can observe (see Security considerations).

Lander proxies in two transports — a per-session stdio subprocess (lander serve-stdio --proxy <name>) and the daemon’s HTTP-stream reverse proxy (/stream/proxy/<name>), as described in MCP mirroring. For a Connector-managed MCP, the thing Lander forwards to is whatever the catalog entry’s install spec says: a command to spawn or a url to connect to. When that spec points at your proxy, the proxy is what Lander spawns or connects to, and the proxy opens the connection to the real MCP server. Lander records the message it hands to the proxy and the message it gets back.

sequenceDiagram
  participant Client as AI client
  participant Lander
  participant Proxy as Your MCP proxy<br/>(catalog entry command / url)
  participant MCP as Upstream MCP server
  participant Connector
  Client->>Lander: JSON-RPC tools/call (stdin or /stream/proxy/<name>)
  Note over Lander: attribute (LANDER_AGENT_ID / X-Lander-Agent)<br/>check allowed_tools / disallowed_tools<br/>audit: message sent
  Lander->>Proxy: tools/call
  Note over Proxy: proxy's own authN / authZ / policy
  Proxy->>MCP: tools/call
  MCP-->>Proxy: result
  Proxy-->>Lander: result
  Note over Lander: audit: message received<br/>emit event
  Lander-->>Client: JSON-RPC result
  Lander->>Connector: POST /v1/devices/events (mcp.tool_call)

The failure path is short. If the step Lander->>Proxy cannot complete — the command does not start, the URL does not answer, or the proxy rejects the session — Lander returns a JSON-RPC error to the client, logs the failure, and reports an event with result = error. It does not open a direct connection to the upstream MCP server, because it has no configuration for one: the only entrypoint it knows is the proxy. See Failure modes.

Every catalog entry that can be installed on devices carries an install spec. It is the same handful of fields whether the entry points straight at an MCP server or at a proxy in front of one; routing through a proxy is a matter of what you put in them. The fields fall into four groups.

Field Type Description
install_transport stdio | http | sse How Lander reaches the entrypoint. Empty means a display-only entry with no install spec.
install_command string stdio only, required. Executable Lander spawns per MCP session — your proxy binary. Resolved on the device’s PATH or given as an absolute path.
install_args string[] stdio only. Arguments for the command. For a proxy, this is where you tell it which upstream to launch or route to.
install_url string http / sse only, required. The URL Lander connects to — your proxy’s endpoint for this MCP.

The Connector validates the pairing on save: stdio requires install_command; http and sse require install_url; install_headers is rejected on stdio because a stdio process has no HTTP layer; and install fields without a transport are rejected.

2. Credentials: what Lander presents to the entrypoint

Section titled “2. Credentials: what Lander presents to the entrypoint”
Field Type Description
install_env list Environment for the spawned process (stdio). Each item is a name plus either a plain value or a secret.
install_headers list Static HTTP headers sent on every request (http / sse). Each item is a name plus a plain value or a secret, with an optional value_prefix such as Bearer that is prepended to the resolved value.

A value and a secret reference are mutually exclusive on one item. value_prefix keeps the stored secret a pure credential: rotating a token cannot silently drop the Bearer prefix and leave the proxy returning an opaque 401. Secret handling is covered in Secrets, and what these credentials mean for authentication in Authentication and authorization are inherited.

3. Lander-side governance: what Lander enforces before the call

Section titled “3. Lander-side governance: what Lander enforces before the call”
Field Type Description
proxied boolean Whether Lander installs the entry routed through its own mirror on the device, so the agent talks to Lander (lander serve-stdio --proxy <slug> or /stream/proxy/<slug>). Set it for every entry you want observed and governed. The three fields below are delivered to devices only when it is set.
allowed_tools / disallowed_tools string[] Tool allow/deny policy Lander applies before a call is forwarded. Independent of, and in addition to, any policy your proxy applies; it can only narrow what the proxy would permit.
capture_payloads boolean Whether the redacted, truncated arguments and result are attached to events. Set it to false to keep payloads out of the Connector.
Field Type Description
requires_approval boolean Users must request the MCP and an admin approves it before it installs.
auto_install boolean Installed on every eligible device without a request.
blocked boolean Disallowed: hidden from the catalog, never auto-installed, removed from devices in enforce mode. See Catalog purview.
access_groups string[] Groups whose members may see and request the entry.
available boolean Hide an entry from users without deleting it.

The install spec is the same for every device and every user who installs that entry. If you need different proxies, or a proxy for some MCPs and not others, express it in the catalog: one entry per routing. Per-tenant or per-user routing inside your proxy is the proxy’s business; Lander passes the calling agent’s identity (LANDER_AGENT_ID for stdio, X-Lander-Agent for HTTP-stream) so the proxy can make that decision if it wants to.

Entries reach the Connector’s catalog from two sources, and both produce the same install spec in the same desired-state document:

  • The admin catalog (database). Created and edited under Admin → MCP Catalog. Editable in the dashboard and through the admin API; this is the source for install_env, including secret values.
  • A catalog config file (mcp.toml). Loaded at Connector start from ./mcp.toml, or the path given by A10E_MCP_CONFIG_PATH or the --mcp-config flag. Entries from the file appear read-only (“from mcp.toml”) in the admin catalog. Each [[mcp_server]] must have an [mcp_server.install] block or the Connector refuses to start, and [[mcp_server.install.headers]] is how install_headers are declared, with secret_ref pointing at a secrets backend. If a database entry has the same slug or alias, the database entry wins and the file entry is reported as shadowed.

A file-sourced entry that routes through your proxy looks like this:

[[mcp_server]]
slug = "atlassian"
name = "Atlassian"
transport = "http"
auth_type = "none" # the proxy, not the vendor, authenticates the device
proxied = true
disallowed_tools = ["deleteJiraIssue"]
[mcp_server.install]
transport = "http"
url = "https://mcp-proxy.corp.example.com/mcp/atlassian"
[[mcp_server.install.headers]]
name = "X-Tenant"
value = "acme"
[[mcp_server.install.headers]]
name = "Authorization"
value_prefix = "Bearer "
secret_ref = { backend = "file", reference = "/etc/connector/secrets/proxy.json", key = "token" }

Example: stdio proxy in front of a stdio MCP

Section titled “Example: stdio proxy in front of a stdio MCP”

The admin adds github to the catalog, but instead of naming the GitHub MCP server as the command, names the organization’s gateway binary and tells it to launch GitHub as its upstream. Lander spawns the gateway per session; the gateway spawns and governs the real server.

{
"slug": "github",
"name": "GitHub",
"install_transport": "stdio",
"install_command": "/usr/local/bin/mcp-gateway",
"install_args": [
"--policy", "default",
"--upstream-name", "github",
"--", "npx", "-y", "@modelcontextprotocol/server-github"
],
"install_env": [
{ "name": "GATEWAY_TENANT", "value": "acme" },
{ "name": "GATEWAY_API_KEY", "value": "…", "secret": true },
{ "name": "GITHUB_PERSONAL_ACCESS_TOKEN", "value": "…", "secret": true }
],
"proxied": true,
"disallowed_tools": ["delete_repo", "transfer_repo"],
"requires_approval": true
}

Example: URL proxy in front of a remote MCP

Section titled “Example: URL proxy in front of a remote MCP”

The admin adds a remote MCP, but the URL is the organization’s DLP proxy endpoint for it rather than the vendor’s. Lander connects to the proxy; the proxy connects onward. The effective install spec, as delivered to devices, is:

{
"slug": "atlassian",
"install_transport": "http",
"install_url": "https://mcp-proxy.corp.example.com/mcp/atlassian",
"install_headers": [
{ "name": "X-Tenant", "value": "acme" },
{ "name": "Authorization", "value_prefix": "Bearer ", "secret": true }
],
"proxied": true,
"auto_install": true
}

The mcp.toml form above declares the same entry from a file.

Adding a proxied MCP is the ordinary catalog flow; the only difference is what goes in the install spec.

  1. In the Connector dashboard, open Admin → MCP Catalog (/admin/mcp-catalog) and choose Add entry (or edit an existing one). Writing the catalog requires the org super admin role.
  2. Fill in the display fields (slug, name, description, category) as for any entry.
  3. Set the install spec so the proxy is the entrypoint: for stdio, install_command is your proxy and install_args name the upstream; for http / sse, install_url is your proxy’s endpoint for this MCP. Add install_env as the proxy needs and mark credentials Secret. For http / sse entrypoints that need headers, declare install_headers in the entry’s mcp.toml source.
  4. Turn Proxied on so Lander installs the entry routed through its proxy and observes it.
  5. Set the tool policy (allowed_tools / disallowed_tools) and lifecycle flags (requires_approval, auto_install).
  6. Save. The Connector validates the spec (for example, install_headers requires an http or sse transport), stores it, writes an administrative audit entry, and bumps the desired-state generation so devices pick up the change on their next poll.

From there, users request and admins approve it exactly as in Install an approved MCP. Changing the proxy for an MCP later is an edit to the same entry; devices converge to the new spec on their next poll.

Credentials for the proxy follow the Connector’s write-only secret handling:

  • On write through the admin catalog, an install_env value is submitted as { "name", "value", "secret": true }. The value goes to the Connector’s secrets registry (the preferred writable backend) and only a reference is stored on the catalog row. Submitting a secret with an empty value keeps the existing reference, which is how an entry is edited without re-entering its credentials.
  • In an mcp.toml entry, a secret is a secret_ref — backend, reference, and key — pointing at a value in a secrets backend. Do not inline real credentials with value.
  • On read — the admin API and the dashboard — a secret comes back as { "name", "has_secret": true }. The value is never echoed back. To rotate it, re-enter it.
  • At delivery, the Connector resolves secrets server-side when it builds the device’s desired-state document (prepending any value_prefix), so Lander receives runtime values, never a reference.
  • On the device, Lander holds the resolved values for the proxy process or request headers; they are not reported in inventory and not included in events.
  • Client-specific exposure. Agents with native HTTP MCP support (Claude Code, Gemini CLI, Codex) receive header values in their config file. Claude Desktop reaches HTTP MCPs through mcp-remote, which receives the value as a command-line argument and so makes it visible to ps on that device. Prefer a proxy credential that is scoped, short-lived, and revocable.

Lander learns about catalog MCPs through the desired-state call it already makes — GET /v1/devices/desired-state. The document lists, per agent on the device, the MCP entries that should be installed, each with the install spec resolved from its catalog entry. Lander polls it in the lander serve convergence loop; the response is versioned by generation with ETag / 304 Not Modified, and Lander keeps a last-known-good copy. Saving a catalog entry bumps the generation.

{
"generation": 42,
"issued_at": "2026-10-06T14:00:00Z",
"modes": { "mcp": "enforce", "skill": "enforce" },
"blocked": { "mcp": ["legacy-filesystem"], "skills": [] },
"agents": {
"claude-code": {
"mcp": [
{
"name": "github",
"transport": "stdio",
"command": "/usr/local/bin/mcp-gateway",
"args": ["--policy", "default", "--upstream-name", "github", "--", "npx", "-y", "@modelcontextprotocol/server-github"],
"env": { "GATEWAY_TENANT": "acme", "GATEWAY_API_KEY": "<resolved at delivery>", "GITHUB_PERSONAL_ACCESS_TOKEN": "<resolved at delivery>" },
"proxied": true,
"disallowed_tools": ["delete_repo", "transfer_repo"],
"source": "user"
}
],
"skills": []
}
}
}

The request is authenticated like every other /v1/devices/* call: TLS to the Connector plus the device certificate and a signature in X-Device-Certificate, X-Device-Timestamp, and X-Device-Signature. The Connector verifies the device before it resolves secrets into the response. See Security & audit.

The device cannot override the configuration locally. There is no flag, environment variable, managed preference, or ~/.lander.json field that re-points a catalog MCP at a different entrypoint. The only source is the catalog entry in the Connector.

For each desired entry with proxied: true, Lander:

  1. Writes the agent’s config so the agent talks to Lander, not to the entrypoint — lander serve-stdio --proxy <name> for stdio, or /stream/proxy/<name> for HTTP-stream — exactly as for any other mirrored MCP.
  2. Stores the entry’s command / args / env or url / headers as the upstream Lander itself forwards to. When the catalog entry names your proxy, this is your proxy.
  3. Applies the entry’s allowed_tools / disallowed_tools before forwarding each call.
  4. Reports the outcome in its converge report (POST /v1/devices/converge-report) as a result with kind: "mcp", action: "install", and status ok or failed with an error string.

Because the agent only knows about Lander, and Lander only knows about the entrypoint the catalog declares, every tool call to a catalog MCP goes through the configured proxy. There is nothing on the device that could route around it.

When a poll returns a new generation whose entry for an MCP differs from what is installed, Lander converges: it reinstalls the entry with the new spec and reports the result. New MCP sessions use the new entrypoint immediately; a stdio proxy process already serving a session runs until that session ends.

If the Connector is unreachable and Lander is working from its last-known-good document, it keeps the last-known-good configuration and does not remove or re-point anything. Losing contact with the Connector never turns a proxied MCP into a direct one.

Lander fails closed. Whenever the configured entrypoint cannot carry a call, the call does not go through — not to the proxy and not directly to the upstream MCP server. There is no native or direct fallback, because none is configured.

Situation What Lander does
http / sse entrypoint does not answer, refuses the connection, or times out Returns a JSON-RPC error to the client; emits an event with result = error and the transport error in error_message.
stdio command is missing, not executable, or exits before accepting the first message Same: JSON-RPC error to the client, result = error event with the spawn error.
The proxy rejects the session or call (its own authN / authZ) Lander forwards the proxy’s JSON-RPC error to the client unchanged and records the exchange; the proxy’s decision stands.

What the AI client sees. A JSON-RPC error for the request it made, which the agent surfaces the way it surfaces any failed tool call. The MCP’s tool list may still be visible; every call on it fails until the entrypoint is healthy.

What the user sees. A line in the daemon log (~/.lander/logs/service.err) and in the per-MCP access.log naming the MCP and the error.

What the Connector sees. One observability event per failed call, in the existing event shape, with result = error and error_message carrying the transport or spawn error:

{
"event_time": "2026-10-06T14:02:11Z",
"agent_type": "claude-code",
"mcp_name": "github",
"tool": "list_issues",
"event_type": "mcp.tool_call",
"result": "error",
"error_message": "dial tcp 10.0.4.12:443: connection refused"
}

The Error tile and the result filter in Observability → Events (/admin/observability/events) show how much traffic an outage blocked, on which devices, for which MCPs.

Catalog purview: keeping devices to the catalog

Section titled “Catalog purview: keeping devices to the catalog”

Two settings decide how far Lander goes in keeping a device to the catalog. Both are set in the Connector and delivered in the desired-state document.

  • Convergence mode (modes.mcp, set fleet-wide in the Connector at /v1/admin/fleet/converge-mode with a per-device override at /v1/admin/devices/{id}/converge-mode). In report-only, Lander computes the actions needed to match the catalog and reports them with status: "planned" but does not execute them. In enforce, Lander executes them: it installs declared entries, removes any MCP the Connector lists under blocked, and removes Lander-managed MCPs the Connector no longer declares. A user-initiated install or removal that already passed the Connector’s authorization converges in either mode.
  • Blocked entries (blocked on the catalog entry). A blocked MCP is hidden from the user catalog, never auto-installed, and removed from devices that have it. Blocking is how an admin forbids a specific MCP outright.

An MCP that someone adds by hand, outside the catalog — editing ~/.claude.json, say — is not something Lander installed and not something the catalog declares. Lander detects the edit through file-watching and reports it as an unmanaged change to the audit log, and the device’s compliance view (/v1/admin/devices/{id}/compliance) lists it under unmanaged until it is removed or brought into the catalog. If the hand-added MCP matches a blocked entry, enforce mode removes it. Together these let an organization require that every MCP on a device be a catalog MCP — and therefore one whose routing, including any proxy, the admin chose. See Audit & compliance.

Authentication and authorization are inherited

Section titled “Authentication and authorization are inherited”

Lander adds no authentication or authorization layer of its own between the agent and the MCP. Routing through Lander does not replace the proxy’s login, re-issue its tokens, or widen what a caller may do. The guarantee has four parts:

  • Credentials pass through untouched. What the admin put in install_env or install_headers is handed to the proxy process or sent on each request, and Lander does nothing else with it. On the HTTP-stream path Lander is a reverse proxy: request headers reach the entrypoint as sent, and the entrypoint’s response — status, headers, and body — comes back to the client as sent.
  • The proxy’s decision stands. A call the proxy rejects is rejected: Lander returns the proxy’s error to the client and records it. It does not retry around the proxy, re-authenticate on the agent’s behalf, or re-route to the upstream.
  • Lander can only narrow. A call the proxy allows is allowed as far as the proxy is concerned. Lander’s only independent gate is the catalog entry’s allowed_tools / disallowed_tools policy, which runs before the call reaches the proxy and can only narrow, never widen, what the proxy would permit.
  • Identity is offered, not imposed. The calling agent’s identity is available to the proxy (LANDER_AGENT_ID or X-Lander-Agent) if its authorization wants it, but Lander does not require the proxy to use it.

Three separate credentials can be involved on a proxied call. Lander touches only the first, and only to deliver it.

Layer Who holds it What Lander does
Device → proxy The credential the proxy requires from this device or tenant: a bearer token, API key, or tenant header, set in install_env / install_headers. Resolved by the Connector at delivery, held by Lander, and presented to the proxy on every session or request.
User → proxy A per-user credential the proxy demands, such as an OAuth login the proxy runs itself. Nothing. The proxy’s challenge and the client’s answer travel through Lander as ordinary traffic; Lander does not terminate, mint, or cache the user’s token.
Proxy → upstream Whatever the proxy uses to reach the real MCP server. Nothing. This hop belongs entirely to the proxy; Lander neither sees nor holds that credential.

The common case. The proxy needs a credential that identifies the organization or device, so the admin puts it in the catalog entry and the Connector delivers it. Lander never sees the secret in a reference form and the agent never needs it.

sequenceDiagram
  participant Admin as Admin (Connector dashboard)
  participant Connector
  participant Secrets as Secrets backend
  participant Lander
  participant Client as AI client
  participant Proxy as Your MCP proxy
  participant MCP as Upstream MCP server
  Admin->>Connector: Save catalog entry (install spec + secret credential)
  Connector->>Secrets: Store credential, keep only a reference
  Lander->>Connector: GET /v1/devices/desired-state (device-signed)
  Connector->>Secrets: Resolve reference (+ value_prefix)
  Connector-->>Lander: Entry with runtime credential
  Client->>Lander: tools/call (no proxy credential needed)
  Lander->>Proxy: tools/call + credential (env or Authorization header)
  Note over Proxy: authenticates the device / tenant<br/>applies its own authZ and policy
  alt proxy accepts
    Proxy->>MCP: tools/call (proxy's own upstream credential)
    MCP-->>Proxy: result
    Proxy-->>Lander: result
    Lander-->>Client: result
  else proxy rejects
    Proxy-->>Lander: JSON-RPC / HTTP error
    Lander-->>Client: error, unchanged
  end

Flow: the proxy authenticates the user itself

Section titled “Flow: the proxy authenticates the user itself”

When the proxy runs its own interactive login (for example an OAuth authorization the user completes), Lander is a transparent hop. The challenge reaches the client exactly as the proxy issued it, the user completes the proxy’s flow, and the client then presents what it obtained on later calls. Lander adds nothing to the exchange and does not hold the user’s token.

sequenceDiagram
  participant User
  participant Client as AI client
  participant Lander
  participant Proxy as Your MCP proxy
  participant IdP as Proxy's identity provider
  participant MCP as Upstream MCP server
  Client->>Lander: tools/call
  Lander->>Proxy: tools/call (as sent)
  Proxy-->>Lander: 401 + authorization challenge
  Lander-->>Client: 401 + challenge (relayed unchanged)
  Note over Lander: records the exchange; no retry,<br/>no fallback to the upstream
  Client->>User: Open proxy's sign-in
  User->>IdP: Authenticate and consent
  IdP-->>Client: Token for the proxy
  Client->>Lander: tools/call + token
  Lander->>Proxy: tools/call + token (as sent)
  Proxy->>MCP: tools/call (proxy's upstream credential)
  MCP-->>Proxy: result
  Proxy-->>Lander: result
  Lander-->>Client: result

How the client completes a challenge depends on the agent and on whether it reaches the MCP through its native HTTP support or through mcp-remote; that is client behavior, not something Lander changes. Test the sign-in once from each agent you support before rolling an OAuth-protected proxy out to a fleet.

The proxy’s RBAC says this user may not call this tool. Lander applied its own tool policy first, forwarded the call, and returns the proxy’s refusal as the answer. Both sides are recorded, so the denial is visible in the Connector without the proxy having to report it.

sequenceDiagram
  participant Client as AI client
  participant Lander
  participant Proxy as Your MCP proxy
  participant Connector
  Client->>Lander: tools/call delete_project
  Note over Lander: allowed_tools / disallowed_tools: pass<br/>audit: message sent to proxy
  Lander->>Proxy: tools/call delete_project
  Note over Proxy: RBAC: caller may not call this tool
  Proxy-->>Lander: JSON-RPC error (forbidden)
  Note over Lander: audit: message received (the proxy's error)
  Lander-->>Client: JSON-RPC error, unchanged
  Lander->>Connector: POST /v1/devices/events (mcp.tool_call, result = error)

A call that Lander’s own policy denies never reaches the proxy; it is rejected locally with a denied result, and the proxy sees nothing.

Concern Without Lander in the path With Lander in the path
Who authenticates the caller The proxy The proxy
Token and credential format the proxy accepts Whatever the proxy defines The same; value_prefix and headers are how the admin supplies it
Who decides which tools a caller may use The proxy’s RBAC The proxy’s RBAC, optionally narrowed by disallowed_tools
What the upstream sees What the proxy sends The same; Lander is not on that hop
Where authentication failures surface To the client To the client, unchanged, and as an event in the Connector

Because Lander treats the proxy as a regular MCP, nothing about the event model changes. Every event Lander reports carries mcp_name, the agent, the device, and the tool, exactly as for an MCP without a proxy, so the catalog entry — and with it the proxy configuration in effect — is identifiable from the MCP name. The dashboards under Observability and MCP Servers aggregate proxied and unproxied MCPs alike.

Payload capture is unchanged: unless the catalog entry opts out with capture_payloads: false, metadata.arguments and metadata.result hold the redacted, truncated payloads Lander sent to and received from the proxy. The local query.log carries the same two sides in full, subject to the redaction controls.

Security considerations and trust boundary

Section titled “Security considerations and trust boundary”

Putting a proxy in the path moves part of the trust boundary. Be explicit about what changes:

  • The proxy sees and can alter MCP traffic. Everything Lander forwards — tool names, arguments, results, and any upstream credentials carried in install_env or install_headers — is visible to the proxy, and the proxy can modify, delay, or drop it before it reaches the upstream server or on the way back.
  • Lander audits both sides. For every call it records the message it sent to the proxy and the message it received back. For stdio, that is what Lander wrote to the proxy’s stdin and read from its stdout; for http / sse, the request body it sent and the response body it received. Those two records are what Alien Giraffe can vouch for.
  • What Alien Giraffe vouches for: that the configuration in effect is the catalog entry the organization’s super admin wrote (it comes only from the Connector over the device-authenticated channel, and every change is in the administrative audit log); that the events and logs reflect what Lander observed on both sides of the proxy; and that no device-local setting can re-point a catalog MCP.
  • What Alien Giraffe cannot vouch for: what the proxy does on its connection to the upstream server; the proxy’s own logging, retention, or onward disclosure of the traffic it sees; or that the response the upstream server produced reached Lander unmodified. Those are properties of the proxy and are the proxy operator’s responsibility.
  • Secrets. Credentials in install_env / install_headers are write-only in the Connector and resolved only into the device’s desired-state response. Upstream credentials now transit the proxy; treat the proxy as a secret-handling system in your threat model.
  • Fail closed is a security property. An outage of the proxy blocks that MCP’s traffic rather than bypassing the control the organization chose. If a bypass is ever acceptable, the admin expresses it by editing the catalog entry to point at the upstream directly — a change that is itself audited and that reaches devices on the next poll.
Symptom Likely cause What to check / fix
Every call to one MCP fails with a connection error and the Error tile climbs across many devices The install_url proxy is down or unreachable from endpoints Reach the URL from an affected device; check the proxy’s health and firewall rules for the endpoint network. Filter events on result = error and the MCP name to list affected devices.
Spawn errors on a subset of devices install_command is not installed, not executable, or at a different path on those devices Ship the proxy binary with your MDM to the configured path, or use a PATH-resolvable name. The converge report for the device shows the install error.
Authentication errors right after rotating a credential Devices still hold the old resolved value, or the proxy rejects the new one Confirm the proxy accepts the new credential directly. Saving the entry bumps the generation, so devices refresh on their next poll.
Calls to an MCP produce no events The catalog entry has proxied off, so the agent talks to the entrypoint directly Turn Proxied on for the entry; Lander reinstalls it routed through its proxy on the next poll.
A hand-added MCP keeps working outside the proxy It is not a catalog MCP, so no catalog routing applies It appears as an unmanaged change in the audit log and under unmanaged in the device’s compliance view. Add a blocked entry for it and run the fleet in enforce mode, or bring it into the catalog with the right install spec.
Dashboard shows has_secret but you need to see the value Secrets are write-only by design Re-enter the value; the Connector never returns it.
A device seems to have reverted to direct routing after a Connector outage It did not — a last-known-good document keeps the installed configuration If events are missing, the device may never have completed a poll with the current generation. Check registration and connectivity.

Admin endpoints are organization-scoped and live under /v1/admin/... (the dashboard calls them as /api/admin/...). Reading requires an org admin; writing requires the org super admin role.

Method Path Result
GET /v1/admin/mcp-catalog The organization’s catalog entries including each install spec. Secret values in install_env / install_headers are returned as { "name", "has_secret": true }.
POST / PATCH /v1/admin/mcp-catalog (create) and /v1/admin/mcp-catalog/{id} (update) Create or update an entry, including install_transport, install_command, install_args, install_url, install_env, proxied, tool policy, and lifecycle flags. install_headers entries are declared in the mcp.toml catalog source. Secret values are submitted as { "name", "value", "secret": true }. On success bumps the desired-state generation and writes an administrative audit entry. Org super admin only.
GET /v1/devices/desired-state Device-authenticated. Per-agent MCP entries with the install spec resolved from the catalog, proxied, tool policy, and source; plus modes and blocked. ETag is the generation; If-None-Match yields 304.
POST /v1/devices/converge-report Device-authenticated. Lander reports one result per action with kind: "mcp", action install or remove, status ok, failed, or planned, and error when the install failed.
GET /v1/admin/devices/{id}/compliance Desired versus observed MCPs for a device, including the unmanaged list of MCPs present on the device that no catalog entry declares.
POST /v1/devices/events Device-authenticated. Unchanged route; a failed hop to the proxy is reported with result = error and error_message.

Related reading: MCP mirroring for the on-device proxy, Install an approved MCP for the request and approval flow, Observability events for the event model, and Security & audit for device identity and redaction.