Give an MCP server its secret
Your catalog has an mcp entry whose server needs a token, and you want
that token supplied on this machine without ever committing it. agent-rigger keeps the value out of
the catalog entirely: the entry carries a
${VAR} reference, and you decide at
install time which environment variable resolves it. This guide covers that mapping and shows where
the value does and does not land. For a first install end to end, see
getting started; for the entry’s full schema, see
catalog schema; for every install flag, see the
install reference.
Before you start
Section titled “Before you start”- A catalog is configured (
rigger catalog lslists it). - That catalog declares an mcp entry with at least one secret. The reference and example catalogs ship none today, so this is an entry your team’s catalog defines. The rest of this guide uses a GitHub MCP server as the worked example.
The catalog holds a reference, never the value
Section titled “The catalog holds a reference, never the value”An mcp entry declares its server config inline, under config. Every value in the secret-bearing
sub-objects (env for Claude Code’s stdio servers, environment and headers for opencode) must
be an exact ${VAR_NAME} reference. A GitHub server entry looks like this:
{ "kind": "artifact", "id": "mcp:github", "nature": "mcp", "targets": ["claude"], "scopes": ["user"], "config": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}" } }, "secrets": [ { "ref": "GITHUB_PERSONAL_ACCESS_TOKEN", "prompt": "GitHub personal access token", "required": true, "help": "https://github.com/settings/tokens" } ]}Write a literal there instead of a reference and the catalog is rejected when it parses, before any install work begins:
mcp entry "mcp:github" has a non-ref value at config.env.GITHUB_PERSONAL_ACCESS_TOKEN — use a "${VAR_NAME}" reference instead of a literal valueThe match is exact: "Bearer ${TOKEN}" is rejected, since the check requires a literal
${VAR_NAME} form. The full rule, the three checked sub-objects, and the secrets declaration
fields are in catalog schema.
Map the reference to a variable at install
Section titled “Map the reference to a variable at install”Each declared secret has a ref (the name inside ${…}) and a required flag. Point a reference at
the variable that holds it with
--secret-env=<ref>=<VAR>:
export MY_GH_PAT=ghp_your_tokenrigger install acme/mcp:github --secret-env=GITHUB_PERSONAL_ACCESS_TOKEN=MY_GH_PAT --yesThe flag is repeatable, once per reference, and the last value wins for a given ref.
Whether you can skip the flag depends on the secret and the session. In an interactive session,
install always asks which variable holds each declared secret, even one already exported under a
matching name: there is no flag-free silent path. In a non-interactive session, a secret that is not
required defaults to a variable of its own name, but a secret marked required, like the GitHub
token above, has no default. Exporting a correctly named variable does not change that: without the
flag, install still exits 2 (see “When install cannot resolve the secret”, below). Pass the flag
even when <VAR> matches <ref>:
export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_your_tokenrigger install acme/mcp:github --secret-env=GITHUB_PERSONAL_ACCESS_TOKEN=GITHUB_PERSONAL_ACCESS_TOKEN --yesWhere the value goes, and where it never goes
Section titled “Where the value goes, and where it never goes”--secret-env maps a name to a name. The secret value itself is read only from the environment
variable, and only when the MCP server is spawned. It goes nowhere else:
- Not into the catalog. The entry carries
${VAR}references and thesecretsdeclarations (ref,prompt,required,example,help): reference names and advisory text only, no value. - Not into the manifest. agent-rigger records the reference-to-
variable mapping (names only) so a later
updatere-renders without asking you again. The value is not part of it. - Not into the files agent-rigger writes. For Claude Code, install
delegates the server to
claude mcp add-json. The config it passes still carries a${VAR}reference, never the literal token, but agent-rigger rewrites the name inside${…}first: it becomes whichever variable--secret-envmapped the catalog’srefto, or theref’s own name when no mapping was given. That rewritten name, not the catalog’sref, is what Claude Code stores in its own config and expands when it later launches the server: keep it exported in the environment that starts the server, or the server breaks without warning.
When install cannot resolve the secret
Section titled “When install cannot resolve the secret”A malformed --secret-env value is caught before any catalog is fetched, and install exits 2:
[error] Invalid --secret-env value: "notvalid". Expected "<ref>=<VAR>" (e.g. --secret-env=GITHUB_TOKEN=MY_PAT).A secret marked required that stays unresolved fails closed; it does not fall back to a silent
guess. On a non-interactive session with no matching
variable and no override, install exits 2 and names the fix:
[error] missing required secret "GITHUB_PERSONAL_ACCESS_TOKEN" (GitHub personal access token) — pass --secret-env=GITHUB_PERSONAL_ACCESS_TOKEN=<VAR_NAME> or export GITHUB_PERSONAL_ACCESS_TOKEN directlyThe same presence check runs again just before the config is rendered, so a required secret whose
variable is unset at that point stops the install before anything is written:
[error] mcp entry "acme/mcp:github" is missing required secret "GITHUB_PERSONAL_ACCESS_TOKEN" (GitHub personal access token) — env var "GITHUB_PERSONAL_ACCESS_TOKEN" is not set. Export it (export GITHUB_PERSONAL_ACCESS_TOKEN=<value>) or re-run with --secret-env=GITHUB_PERSONAL_ACCESS_TOKEN=<OTHER_VAR>.A later update reuses the recorded mapping and does not re-ask, but it
runs the same presence check: if the variable is gone from your environment, the re-render fails
closed the same way.