Secret Sources
Secrets are configured under secrets and resolved at execution time.
Supported source types
| Type | Example | Runtime behavior |
|---|---|---|
env | {"type":"env","value":"HELPDESK_TOKEN"} | Reads os.Getenv(value) |
file | {"type":"file","value":"/run/secrets/helpdesk-token"} | Reads the entire file contents |
osKeychain | {"type":"osKeychain","value":"helpdesk/token"} | Uses the platform keychain helper |
exec | {"type":"exec","command":["sh","-lc","printf token"]} | Runs a command and uses stdout |
oauth2 | see below | Acquires, caches, and reuses OAuth access tokens |
env
{
"secrets": {
"bearerAuth": {
"type": "env",
"value": "HELPDESK_TOKEN"
}
}
}
Nuance: if the env var is unset, the runtime gets an empty string. That usually means auth is skipped or malformed, not that config loading fails.
file
{
"secrets": {
"bearerAuth": {
"type": "file",
"value": "/run/secrets/helpdesk-token"
}
}
}
Nuance: the runtime reads the file verbatim. Trailing newlines are preserved.
osKeychain
{
"secrets": {
"bearerAuth": {
"type": "osKeychain",
"value": "helpdesk/token"
}
}
}
Supported reference formats:
service/accountservice:account
Current helpers:
- macOS:
security find-generic-password -s <service> -a <account> -w - Linux:
secret-tool lookup service <service> account <account>
The default keychain resolver trims surrounding whitespace from command output.
exec
exec secrets are disabled unless:
{
"policy": {
"allowExecSecrets": true
}
}
Example:
{
"policy": {
"allowExecSecrets": true
},
"secrets": {
"bearerAuth": {
"type": "exec",
"command": ["sh", "-lc", "printf token-from-exec"]
}
}
}
Important nuances:
- if
commandis omitted andvalueis set,valueis treated as the executable path, not as a shell command line - stdout is used as-is; trailing newlines are not trimmed
- there is no stdin wiring or timeout in the current implementation
- command failures propagate as secret-resolution errors, which usually causes auth to be skipped for that scheme
Operational guidance
- prefer
envorosKeychainfor local developer workflows - prefer
filefor container/secret-volume setups - use
execonly when you need dynamic retrieval and accept the extra risk/latency
oauth2
oauth2 secrets are used for OpenAPI oauth2 and openIdConnect security schemes.
Supported runtime modes:
clientCredentialsauthorizationCode
Example:
{
"secrets": {
"pets.petstore_oauth": {
"type": "oauth2",
"mode": "clientCredentials",
"clientId": {
"type": "env",
"value": "PETSTORE_CLIENT_ID"
},
"clientSecret": {
"type": "osKeychain",
"value": "oas-cli/petstore-secret"
},
"scopes": ["pets.read"],
"tokenStorage": "instance"
}
}
}
Important runtime behavior:
- service-specific lookup wins before shared lookup, so
pets.petstore_oauthoverridespetstore_oauth openIdConnectuses OIDC discovery and then follows the configured mode- tokens are cached per runtime instance under the instance state directory
tokenStorage: "memory"keeps tokens in-process onlyauthorizationCodeuses a loopback callback on127.0.0.1implicitandpasswordflows are not supported
MCP headerSecrets
MCP transport.headerSecrets references top-level static secrets and injects them into remote MCP transport headers.
Example:
{
"sources": {
"remoteDocs": {
"type": "mcp",
"transport": {
"type": "streamable-http",
"url": "https://mcp.example.com/mcp",
"headerSecrets": {
"X-API-Key": "mcp.apiKey"
}
}
}
},
"secrets": {
"mcp.apiKey": {
"type": "env",
"value": "MCP_API_KEY"
}
}
}
MCP transport oauth
Remote MCP HTTP transports can also authenticate the transport itself with a source-local oauth block.
Important runtime behavior:
- only
clientCredentialsis allowed for MCP transport OAuth tokenURLcan be configured directly, or discovered through the configured OIDC issuer- transport tokens are cached per runtime instance
- if the provider returns a refresh token, later
streamable-httpandsserequests refresh the token before expiry - concurrent MCP clients that share the same instance state reuse the same refreshed transport token instead of racing duplicate refresh requests
- if refresh fails, or the refreshed token expires again, the transport fails closed and requires reauthorization instead of silently retrying forever