User Configuration
Many MCP servers need configuration like API keys. The user_config field in your manifest lets you declare what users need to provide.
Declaring Configuration
Section titled “Declaring Configuration”In your manifest.json:
{ "name": "@yourorg/my-api-server", "version": "1.0.0", "user_config": { "api_key": { "type": "string", "title": "API Key", "description": "Your API key from example.com/dashboard", "sensitive": true, "required": true }, "base_url": { "type": "string", "title": "API Base URL", "description": "Override the default API endpoint", "default": "https://api.example.com" } }, "server": { "type": "python", "mcp_config": { "command": "python", "args": ["-m", "my_server"], "env": { "API_KEY": "${user_config.api_key}", "API_BASE_URL": "${user_config.base_url}" } } }}Configuration Fields
Section titled “Configuration Fields”| Field | Type | Description |
|---|---|---|
type | string | string, number, boolean |
title | string | Human-readable label |
description | string | Help text for users |
sensitive | boolean | Mask in output (default: false) |
required | boolean | Must be provided (default: false) |
default | any | Default value if not provided |
How Values Are Resolved
Section titled “How Values Are Resolved”The mpak SDK’s gatherUserConfig walks these tiers for each user_config field. First non-empty value wins:
- Caller override —
userConfigpassed toprepareServer({ userConfig })by a host runtime (SDK 0.3.0+). - Stored config — values set via
mpak config set, read from~/.mpak/config.json. - Env alias — the manifest’s
mcp_config.envdeclaration, read in reverse. If you mapped"API_KEY": "${user_config.api_key}", a hostAPI_KEYenv var satisfiesapi_key(SDK 0.4.0+). Only whole-value substitutions participate; composite templates like"prefix-${user_config.a}"don’t. - Manifest default — the field’s
default.
If a required field is still unresolved, the SDK throws MpakConfigError. Each missing field carries its envAliases list so callers can render a specific export VAR=... hint (SDK 0.5.0+).
User Workflow
Section titled “User Workflow”Option 1: mpak config (CLI use)
Section titled “Option 1: mpak config (CLI use)”# Store configurationmpak config set @yourorg/my-api-server api_key=sk-xxx
# Run uses stored config automaticallympak bundle run @yourorg/my-api-serverOption 2: Claude Desktop (environment variables)
Section titled “Option 2: Claude Desktop (environment variables)”{ "mcpServers": { "my-api": { "command": "mpak", "args": ["bundle", "run", "@yourorg/my-api-server"], "env": { "API_KEY": "sk-xxx" } } }}Key Name Mapping
Section titled “Key Name Mapping”The flow from user config to environment variable:
manifest.user_config.api_key → mpak config set ... api_key=xxx → env API_KEY ^^^^^^^ ^^^^^^^ ^^^^^^^ user_config key config key env varUsers provide the user_config key name. Your manifest maps it to an environment variable.
Best Practices
Section titled “Best Practices”Document Required Config
Section titled “Document Required Config”In your README, list what users need to configure:
## Configuration
This server requires an API key from [example.com](https://example.com/dashboard).
### Claude Desktop\`\`\`json{ "mcpServers": { "my-api": { "command": "mpak", "args": ["bundle", "run", "@yourorg/my-api-server"], "env": { "API_KEY": "your-api-key" } } }}\`\`\`
### CLI\`\`\`bashmpak config set @yourorg/my-api-server api_key=your-api-keympak bundle run @yourorg/my-api-server\`\`\`Mark Sensitive Values
Section titled “Mark Sensitive Values”Always set sensitive: true for API keys, passwords, and tokens:
{ "user_config": { "api_key": { "type": "string", "sensitive": true } }}This masks the value in mpak config get output.
Use standard env var names
Section titled “Use standard env var names”Your mcp_config.env name is also the host env alias (tier 3 above). Pick the name the upstream service’s own SDK uses so users who already have it exported get zero-config startup:
{ "mcp_config": { "env": { "ANTHROPIC_API_KEY": "${user_config.anthropic_api_key}" } }}A user with ANTHROPIC_API_KEY already in their shell — which Anthropic’s own SDK reads — runs your bundle with no extra configuration. Renaming to MYBUNDLE_ANTHROPIC_KEY would force them to re-export the same secret for no benefit.
When the upstream has no de-facto standard, use the name the service itself calls it:
| Upstream | Env var |
|---|---|
| Anthropic | ANTHROPIC_API_KEY |
| OpenAI | OPENAI_API_KEY |
| GitHub | GITHUB_TOKEN |
| Google Gemini | GOOGLE_GENERATIVE_AI_API_KEY |
| Stripe | STRIPE_API_KEY |
| AWS | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY |
| Postgres | DATABASE_URL, or PGHOST/PGUSER/etc. |
For multiple keys into the same service, keep the service prefix and disambiguate with a suffix: SLACK_BOT_TOKEN and SLACK_USER_TOKEN, not MYBUNDLE_SLACK_BOT.
Provide Sensible Defaults
Section titled “Provide Sensible Defaults”For optional configuration, provide defaults:
{ "user_config": { "timeout": { "type": "number", "title": "Request Timeout", "description": "Timeout in seconds", "default": 30 } }}