Summary
Make setting up an MCP server easy from the CLI, and stop requiring an environment
variable for the credential-store key. Today both are manual and error-prone: adding
a server means hand-editing the operator settings.yaml mcp.servers[] block, and
the local OAuth credential store can only take its encryption key from a MECATL_*
env var that must be present in every shell/process that logs in or reads the credential.
Motivation
Writing the setup guide for connecting mecatui to an OAuth MCP server (the Stacklok
staging connector-gateway) surfaced two friction points:
-
No CLI to manage servers. mecated mcp only supports login
(cmd/mecated/command.go). Adding a server means hand-writing a ~20-line nested
mcp.servers[] OAuth/DCR block with several non-obvious, easy-to-get-wrong fields:
the server name must be [A-Za-z0-9_]+ (no hyphens), url must include the
resource path (e.g. /gw/mcp), issuer must be the exact origin,
client: {mode: dcr, dcr: {}}, plus credentials and network blocks. A typo in
any of these fails discovery with a generic, detail-suppressed error (the specific
cause is dropped in internal/app/mcplogin.go's loginDiagnostic).
-
The credential-store key must come from an env var. The local OAuth credential
store (MCPLocalCredentialProfile, internal/adapter/permconfig/schema.go) accepts
only key_env: MECATL_*. So the operator has to generate a 32-byte key, store it,
and export MECATL_MCP_CREDENTIAL_KEY in every shell that runs mecated mcp login
and every process that later reads the credential (e.g. mecatui). Miss it in the
runtime shell and the credential silently can't be decrypted → the server drops to
"login required" with no obvious cause. This is exactly the "harder to set up in a
durable way" concern raised in review.
Proposal
1. CLI management of MCP servers
Add mecated mcp subcommands to add/list/remove/inspect servers without hand-editing YAML:
mecated mcp add <name> --url <url> --auth oauth — sensible OAuth/DCR defaults;
discover issuer/resource from the server's RFC 9728 protected-resource metadata
where possible (the same discoverDCRMetadata path login already runs), prompting
only for what can't be discovered, and validating up front so it can surface the
specific failure (issuer mismatch, DCR unsupported, missing scope) instead of the
current generic "authorization unavailable".
mecated mcp list / mecated mcp status — configured servers plus connection and
credential state.
mecated mcp remove <name>.
These read/write the same operator settings.yaml mcp.servers[] schema, so config
stays declarative and reviewable — the CLI is a safe editor over it, not a second
source of truth. mecated mcp login <name> already exists and composes with this.
2. Don't require an env variable for the credential-store key
Give the MCP local credential store the same key-custody choice the OIDC provider
credential store already has. NativeCredentialKey
(internal/adapter/permconfig/providers.go) already supports source: keyring | environment,
defaulting to the OS keyring. MCPLocalCredentialProfile should adopt the same union so
the default is keyring custody (macOS Keychain / Secret Service / etc.) with no env
var, and key_env becomes one opt-in option (for headless/CI) rather than the only
mechanism.
Result: mecated mcp login <name> and the runtime both get the key from the keyring
automatically — nothing to export, nothing to re-export per shell, and it survives
across sessions and processes durably.
Acceptance sketch
mecated mcp add|list|remove manage mcp.servers[] entries in operator
settings.yaml; round-trip preserves unknown top-level keys.
- The MCP local credential store defaults to keyring-backed key custody; no
MECATL_*
env var is required to log in or to read the credential at runtime. key_env remains
supported for headless deployments.
- Docs updated to the CLI-first, no-env-var flow
(user-docs/features/mcp-oauth-and-credentials.md, and the eng setup guide).
References
- Current CLI surface:
cmd/mecated/command.go (only mcp login).
- Env-only key today:
internal/adapter/permconfig/schema.go (MCPLocalCredentialProfile.KeyEnv).
- Existing keyring pattern to reuse:
internal/adapter/permconfig/providers.go (NativeCredentialKey, source: keyring).
- Generic-error masking to improve alongside
add: internal/app/mcplogin.go (loginDiagnostic).
Summary
Make setting up an MCP server easy from the CLI, and stop requiring an environment
variable for the credential-store key. Today both are manual and error-prone: adding
a server means hand-editing the operator
settings.yamlmcp.servers[]block, andthe local OAuth credential store can only take its encryption key from a
MECATL_*env var that must be present in every shell/process that logs in or reads the credential.
Motivation
Writing the setup guide for connecting mecatui to an OAuth MCP server (the Stacklok
staging connector-gateway) surfaced two friction points:
No CLI to manage servers.
mecated mcponly supportslogin(
cmd/mecated/command.go). Adding a server means hand-writing a ~20-line nestedmcp.servers[]OAuth/DCR block with several non-obvious, easy-to-get-wrong fields:the server
namemust be[A-Za-z0-9_]+(no hyphens),urlmust include theresource path (e.g.
/gw/mcp),issuermust be the exact origin,client: {mode: dcr, dcr: {}}, pluscredentialsandnetworkblocks. A typo inany of these fails discovery with a generic, detail-suppressed error (the specific
cause is dropped in
internal/app/mcplogin.go'sloginDiagnostic).The credential-store key must come from an env var. The local OAuth credential
store (
MCPLocalCredentialProfile,internal/adapter/permconfig/schema.go) acceptsonly
key_env: MECATL_*. So the operator has to generate a 32-byte key, store it,and export
MECATL_MCP_CREDENTIAL_KEYin every shell that runsmecated mcp loginand every process that later reads the credential (e.g. mecatui). Miss it in the
runtime shell and the credential silently can't be decrypted → the server drops to
"login required" with no obvious cause. This is exactly the "harder to set up in a
durable way" concern raised in review.
Proposal
1. CLI management of MCP servers
Add
mecated mcpsubcommands to add/list/remove/inspect servers without hand-editing YAML:mecated mcp add <name> --url <url> --auth oauth— sensible OAuth/DCR defaults;discover
issuer/resource from the server's RFC 9728 protected-resource metadatawhere possible (the same
discoverDCRMetadatapathloginalready runs), promptingonly for what can't be discovered, and validating up front so it can surface the
specific failure (issuer mismatch, DCR unsupported, missing scope) instead of the
current generic "authorization unavailable".
mecated mcp list/mecated mcp status— configured servers plus connection andcredential state.
mecated mcp remove <name>.These read/write the same operator
settings.yamlmcp.servers[]schema, so configstays declarative and reviewable — the CLI is a safe editor over it, not a second
source of truth.
mecated mcp login <name>already exists and composes with this.2. Don't require an env variable for the credential-store key
Give the MCP local credential store the same key-custody choice the OIDC provider
credential store already has.
NativeCredentialKey(
internal/adapter/permconfig/providers.go) already supportssource: keyring | environment,defaulting to the OS keyring.
MCPLocalCredentialProfileshould adopt the same union sothe default is keyring custody (macOS Keychain / Secret Service / etc.) with no env
var, and
key_envbecomes one opt-in option (for headless/CI) rather than the onlymechanism.
Result:
mecated mcp login <name>and the runtime both get the key from the keyringautomatically — nothing to export, nothing to re-export per shell, and it survives
across sessions and processes durably.
Acceptance sketch
mecated mcp add|list|removemanagemcp.servers[]entries in operatorsettings.yaml; round-trip preserves unknown top-level keys.MECATL_*env var is required to log in or to read the credential at runtime.
key_envremainssupported for headless deployments.
(
user-docs/features/mcp-oauth-and-credentials.md, and the eng setup guide).References
cmd/mecated/command.go(onlymcp login).internal/adapter/permconfig/schema.go(MCPLocalCredentialProfile.KeyEnv).internal/adapter/permconfig/providers.go(NativeCredentialKey,source: keyring).add:internal/app/mcplogin.go(loginDiagnostic).