Kai Ase Siren
Skip to content

Guardfile controls on the proxy

The sibling nodes an mcp-upstream guardfile states, and what each one does to a passthrough surface. The REST half is guardfile-controls.md. The parse and the credential are upstream.md.

Every node here is checked offline, against the allowlist the file declares. The allowlist is the whole surface a proxy serves - an absent upstream tool already fails the snapshot - so a control naming a tool nobody serves is a lint error rather than a boot that looks configured.

icon. The same serverInfo.icons projection spec mode does, so a connector tile shows the brand mark instead of a placeholder. Prefer data: URIs, since a gated deploy sits behind oauth2-proxy where a hosted icon URL 401s for the connecting client.

server-info. On by default here as well, so mcp_beaver_info answers on every mcp-beaver deployment and its absence carries one meaning rather than two. It reports the server's own name, mode, and inventory, reaches no upstream, and is spared the rate limiter because it doubles as the liveness probe the protocol's retired ping left without one. server-info name="..." renames it, server-info disabled removes it, and a name that collides with an allowlisted tool is a startup error. The flag form takes the same default, having no file to state one in.

confirm. confirm "<tool>" message="..." gates one proxied tool behind the Multi Round-Trip Request: the first call returns an elicitation and the upstream is reached only on a retry carrying an explicit accept. Opt-in per tool, because a proxy runs headless deployments where a blanket prompt would wedge every write. Naming a withhold stub or the info tool is accepted and wraps nothing: neither reaches an upstream, so there is nothing to confirm.

pin. The proxy half of the node spec mode states as query. See upstream-pins.md.

rate-limit. The per-server outbound bucket, applied to proxied tools alone. It sits inside the confirmation gate, so a call awaiting a human holds no upstream slot and a declined one spends none, and inside the argument pins, so a call contradicting one charges no budget. Everything else about it - the store redis durable form, the shared bucket key, waiting rather than rejecting - reads the same as it does beside wrap.

What is not projected, and why

Absence here is a statement rather than a backlog. resource, prompt and app are content a REST guardfile serves beside its grants, and a passthrough proxy mints none of it. cache, extract, reject-empty, reject-empty-argument and set all shape a request or a response opcore assembles from a grant. A proxy forwards the upstream's own contract verbatim instead, which is the property the drift check exists to hold. Stating one beside mcp-upstream fails closed, and the refusal names the set that works.