Configuration
Config file structure
Section titled “Config file structure”MCP Front uses a single JSON config file.
{ "version": "v0.0.1-DEV_EDITION_EXPECT_CHANGES", "proxy": { "baseURL": "https://mcp.company.com", "addr": ":8080", "auth": { ... } }, "mcpServers": { ... }}The version string signals the config format may change in future releases.
CLI flags
Section titled “CLI flags”mcp-front -config config.json # Run with config file (required)mcp-front -config-init config.json # Generate a default config filemcp-front -config config.json -validate # Validate config and exitmcp-front -version # Print versionmcp-front -help # Print usageThe -validate flag checks your config for errors without starting the server. It validates structure, field types, and references without requiring environment variables to be set.
Environment variable references
Section titled “Environment variable references”Any string value in the config can reference environment variables using {"$env": "VAR_NAME"}. This keeps secrets out of your config files. Values are resolved at config load time.
{ "jwtSecret": { "$env": "JWT_SECRET" }, "url": { "$env": "DATABASE_MCP_URL" }}This syntax prevents accidental shell expansion when configs pass through scripts or CI/CD pipelines.
User token references
Section titled “User token references”For MCP servers that need per-user tokens injected at request time, use the {"$userToken": "...{{token}}..."} syntax. The {{token}} placeholder is replaced with the authenticated user’s service token when a request is made.
{ "env": { "API_KEY": { "$userToken": "{{token}}" }, "AUTH_HEADER": { "$userToken": "Bearer {{token}}" } }}This works in env, args, url, and headers fields on MCP servers. See Service Authentication for how users provide these tokens.
Proxy configuration
Section titled “Proxy configuration”proxy.baseURL
Section titled “proxy.baseURL”Your public URL. Required. Must be HTTPS in production.
proxy.addr
Section titled “proxy.addr”Listen address, typically ":8080" for port 8080 on all interfaces.
proxy.name
Section titled “proxy.name”Used as the MCP server implementation name. Optional.
proxy.sessions
Section titled “proxy.sessions”Session management configuration. All fields are optional.
{ "proxy": { "sessions": { "timeout": "24h", "cleanupInterval": "15m", "maxPerUser": 5 } }}timeout controls how long sessions last (default: "5m"). cleanupInterval is how often expired sessions are garbage collected (default: "1m"). maxPerUser limits concurrent sessions per user (default: 10, set to 0 for unlimited).
Authentication
Section titled “Authentication”Configure OAuth under proxy.auth.
{ "auth": { "kind": "oauth", "issuer": "https://mcp.company.com", "idp": { "provider": "google", "clientId": { "$env": "GOOGLE_CLIENT_ID" }, "clientSecret": { "$env": "GOOGLE_CLIENT_SECRET" }, "redirectUri": "https://mcp.company.com/oauth/callback" }, "allowedDomains": ["company.com"], "allowedOrigins": ["https://claude.ai"], "allowedRedirectUriHosts": ["https://claude.ai"], "tokenTtl": "4h", "storage": "memory", "jwtSecret": { "$env": "JWT_SECRET" }, "encryptionKey": { "$env": "ENCRYPTION_KEY" } }}auth.kind
Section titled “auth.kind”Must be "oauth". This is the only supported authentication kind.
auth.issuer
Section titled “auth.issuer”The OAuth issuer URI. Should match your baseURL.
auth.idp
Section titled “auth.idp”Identity provider configuration. MCP Front supports Google, Azure AD, GitHub, and generic OIDC providers. See Identity Providers for setup details.
auth.allowedDomains
Section titled “auth.allowedDomains”Restricts access to users with email addresses from these domains. At least one of allowedDomains or idp.allowedOrgs is required.
auth.allowedOrigins
Section titled “auth.allowedOrigins”CORS origin whitelist. When empty, all origins are allowed. Set to ["https://claude.ai"] to restrict to Claude, or add additional origins for other MCP clients.
auth.allowedRedirectUriHosts
Section titled “auth.allowedRedirectUriHosts”Allowlist of permitted redirect-URI origins for OAuth client registration and authorization. Each entry is an origin in the form scheme://host[:port] (for example "https://claude.ai" or "http://127.0.0.1" for native MCP clients per RFC 8252). A registered redirect URI passes when its scheme and hostname match an entry, case-insensitively per RFC 3986. An entry without a port matches any port for that host; an entry with an explicit port matches only that port.
This gates Dynamic Client Registration: an MCP client trying to register a redirect_uris value whose host is not on this list is rejected with 400 Bad Request at /register. The same check is re-applied at /authorize to catch clients registered before the allowlist was tightened. Structural checks always apply regardless of this list — a redirect URI must be an absolute URI, must have a host, must not be opaque (which catches the common form of javascript: and data: URIs), and must not contain a fragment.
In production (i.e. when MCP_FRONT_ENV is not development), one of allowedRedirectUriHosts or allowAnyRedirectUriHost is required at startup. The server fails to boot if both are absent.
auth.allowAnyRedirectUriHost
Section titled “auth.allowAnyRedirectUriHost”Set to true to explicitly disable the host allowlist. Structural checks still apply. Cannot be combined with a non-empty allowedRedirectUriHosts. Intended for local development and trusted-tenant deployments where the operator has other controls in place; production deployments should prefer the explicit allowlist.
auth.tokenTtl
Section titled “auth.tokenTtl”Access token lifetime as a Go duration string. Default: "1h". Examples: "4h", "30m", "24h".
auth.refreshTokenTtl
Section titled “auth.refreshTokenTtl”Refresh token lifetime. Default: "720h" (30 days).
auth.refreshTokenScopes
Section titled “auth.refreshTokenScopes”When set, refresh tokens are only issued if the authorization request includes at least one of these scopes. Default: empty (always issue refresh tokens).
auth.storage
Section titled “auth.storage”"memory" (default) for development — data lost on restart. "firestore" for production with persistent storage.
auth.jwtSecret
Section titled “auth.jwtSecret”Secret for signing JWT tokens. Must be at least 32 bytes. Must use {"$env": "VAR"} syntax.
auth.encryptionKey
Section titled “auth.encryptionKey”Secret for encrypting sensitive data at rest (AES-256-GCM). Must be exactly 32 bytes. Required for Firestore storage, optional for memory storage. Must use {"$env": "VAR"} syntax.
Firestore configuration
Section titled “Firestore configuration”When storage is "firestore":
{ "auth": { "storage": "firestore", "gcpProject": { "$env": "GOOGLE_CLOUD_PROJECT" }, "firestoreDatabase": "(default)", "firestoreCollection": "mcp_front_data" }}gcpProject is your GCP project ID (required for Firestore). firestoreDatabase defaults to "(default)". firestoreCollection defaults to "mcp_front_data".
auth.dangerouslyAcceptIssuerAudience
Section titled “auth.dangerouslyAcceptIssuerAudience”When true, allows tokens with just the base issuer as audience to access any service. This is a workaround for MCP clients that don’t implement RFC 8707 resource indicators, but it defeats per-service token isolation. Default: false. Only enable if you understand the security implications.
MCP server configuration
Section titled “MCP server configuration”Each server needs at least a transportType. See Server Types for transport-specific documentation.
{ "mcpServers": { "postgres": { "transportType": "stdio", "command": "docker", "args": ["run", "--rm", "-i", "my-postgres-mcp"], "env": { "DATABASE_URL": { "$env": "DATABASE_URL" } } }, "linear": { "transportType": "sse", "url": "http://linear-mcp:3000/sse" } }}Server names
Section titled “Server names”Server names must start with an alphanumeric character and contain only alphanumeric characters, underscores, and hyphens. A server named "postgres" is accessible at /postgres/sse.
serviceAuths
Section titled “serviceAuths”Per-server authentication validated on incoming requests before proxying. Useful for development or non-OAuth MCP clients.
Bearer tokens:
{ "serviceAuths": [ { "type": "bearer", "name": "dev", "tokens": ["dev-token-123", "dev-token-456"] } ]}The name field is the identity used in logs, session keys, and per-user
limits. Required for bearer; optional for basic (defaults to username).
Names must be unique within a server’s serviceAuths across types.
Basic authentication:
{ "serviceAuths": [ { "type": "basic", "username": "admin", "password": { "$env": "ADMIN_PASSWORD" } } ]}Tokens for one server don’t work for another — each server’s serviceAuths are independent.
requiresUserToken and userAuthentication
Section titled “requiresUserToken and userAuthentication”When a backend service needs individual user tokens (Notion API keys, Linear OAuth tokens), set requiresUserToken: true and configure userAuthentication. See Service Authentication for details.
options.toolFilter
Section titled “options.toolFilter”Filter which tools are exposed to clients.
{ "options": { "toolFilter": { "mode": "allow", "list": ["safe_tool_1", "safe_tool_2"] } }}mode is "allow" (only expose listed tools) or "block" (hide listed tools).
Aggregate servers
Section titled “Aggregate servers”Set type to "aggregate" to combine tools from multiple backends into one endpoint. See Server Types for details.
{ "all": { "type": "aggregate", "servers": ["postgres", "linear"], "discovery": { "timeout": "10s", "cacheTtl": "5m", "maxConnsPerUser": 10 } }}Runtime environment variables
Section titled “Runtime environment variables”Optional variables that control runtime behavior:
MCP_FRONT_ENV=development # Relaxes OAuth validation for local dev (allows HTTP, makes allowedRedirectUriHosts optional)LOG_LEVEL=debug # Options: trace, debug, info, warn, errorLOG_FORMAT=json # Options: json (structured) or text (human-readable)Complete examples
Section titled “Complete examples”Production with OAuth and Firestore
Section titled “Production with OAuth and Firestore”{ "version": "v0.0.1-DEV_EDITION_EXPECT_CHANGES", "proxy": { "name": "Production Proxy", "baseURL": "https://mcp.company.com", "addr": ":8080", "auth": { "kind": "oauth", "issuer": "https://mcp.company.com", "idp": { "provider": "google", "clientId": { "$env": "GOOGLE_CLIENT_ID" }, "clientSecret": { "$env": "GOOGLE_CLIENT_SECRET" }, "redirectUri": "https://mcp.company.com/oauth/callback" }, "allowedDomains": ["company.com"], "allowedOrigins": ["https://claude.ai"], "allowedRedirectUriHosts": ["https://claude.ai"], "tokenTtl": "4h", "storage": "firestore", "gcpProject": { "$env": "GOOGLE_CLOUD_PROJECT" }, "firestoreDatabase": "(default)", "firestoreCollection": "mcp_front_data", "jwtSecret": { "$env": "JWT_SECRET" }, "encryptionKey": { "$env": "ENCRYPTION_KEY" } } }, "mcpServers": { "database": { "transportType": "sse", "url": { "$env": "DATABASE_MCP_URL" } }, "notion": { "transportType": "stdio", "command": "notion-mcp", "requiresUserToken": true, "userAuthentication": { "type": "manual", "displayName": "Notion Integration Token", "instructions": "Create a new internal integration and copy the secret.", "helpUrl": "https://www.notion.so/my-integrations", "validation": "^secret_[a-zA-Z0-9]{43}$" }, "env": { "NOTION_TOKEN": { "$userToken": "{{token}}" } } } }}Aggregate with multiple backends
Section titled “Aggregate with multiple backends”This example adds an aggregate server to the production config. OAuth configuration is omitted for brevity — see the production example above.
{ "mcpServers": { "postgres": { "transportType": "sse", "url": "http://postgres-mcp:3000/sse" }, "linear": { "transportType": "stdio", "command": "linear-mcp", "args": ["serve"] }, "all": { "type": "aggregate", "discovery": { "cacheTtl": "5m" } } }}Claude connects to /all/sse and sees namespaced tools like postgres.query and linear.create_issue.