Give an MCP server a secret
This page is for an MCP server that agents start in the sandbox, as a local
process listed in a harness profile's mcpServers. For a remote server
reached over HTTP, name it on an integration instead, so its calls get
approvals, hooks and the run's journal: see
Call a remote MCP server.
The example uses a placeholder server, acme-mcp, that reads its key from
ACME_API_KEY and calls api.acme.example; put your own server, variable and
host in their place. You need:
- Terraform set up with the Spotlight provider, using a token that has the
configureandsecretsscopes. See Manage Spotlight with Terraform. - A host to take the runs. Runs that use secrets go only to hosts, never to runners a push pool provisions. See Install a host.
- The API's host, the header or query parameter it reads the key from, and
any prefix, such as
Bearer.
Keep the key as a secret bound to the API's host
variable "acme_api_key" {
type = string
sensitive = true
}
data "spotlight_workspace" "acme" {
slug = "acme"
}
resource "spotlight_secret" "acme_api_key" {
workspace_id = data.spotlight_workspace.acme.id
name = "ACME_API_KEY"
bindings = [
{ host = "api.acme.example", carrier = "header", name = "authorization", prefix = "Bearer " },
]
value_wo = var.acme_api_key
value_wo_version = 1
}If the API reads the key from a query parameter, as the Google Maps APIs read
key, bind it there instead:
bindings = [
{ host = "maps.googleapis.com", carrier = "query", name = "key" },
]Name the secret in the server's environment
In a harness profile, set the environment variable the server reads to
secret.<NAME>:
data "spotlight_team" "core" {
workspace_id = data.spotlight_workspace.acme.id
key = "core"
}
resource "spotlight_harness_profile" "acme" {
team_id = data.spotlight_team.core.id
key = "acme"
mcp_servers = [
{
name = "acme"
command = "npx"
args = ["-y", "acme-mcp"]
env = { ACME_API_KEY = "secret.${spotlight_secret.acme_api_key.name}" }
},
]
}The secret must exist before the profile names it, and it cannot be deleted while the profile names it.
Run a station with the profile
Set the station's profile to the profile's key:
station {
key = "implementation"
name = "Implementation"
harness = "claude-code"
profile = spotlight_harness_profile.acme.key
passes_to = ["code-review"]
}Run terraform apply. On the next run at that station, the server's calls to
api.acme.example succeed. Inside the sandbox, ACME_API_KEY holds only the
stand-in spotlight-egress:ACME_API_KEY. The server must send it in the
authorization header as the whole value, or right after the binding's exact
prefix (Bearer spotlight-egress:ACME_API_KEY), and egress puts the real key
in its place on the way out. For a secret bound to a query parameter, the
server must send the stand-in as that parameter's whole value, or right after
the prefix, as in ?key=spotlight-egress:MAPS_KEY for a secret named
MAPS_KEY. A server that encodes the key, as HTTP Basic does, or sends it
somewhere else, gets nothing. Other requests to api.acme.example from the
sandbox go out without the key.
If the host sets SPOTLIGHT_EGRESS_ALLOW, the list must admit the API's host,
or egress refuses the server's requests to it. A server that pins certificates
cannot reach a host egress adds a secret for.
Related
spotlight_harness_profile: every MCP server field.- Run secrets, on how egress adds a secret to a request.
spotlight_secret- The sandbox is the security boundary