Configuration
5 min read
MCP Registry is configured through a YAML file specified at startup with the -config flag. Every value can be overridden with environment variables.
./mcp-registry -config /path/to/config.yaml
Configuration Precedence
- Environment variables (highest priority)
- YAML config file
- Defaults (lowest priority)
Environment Variable Overrides
Any configuration key can be overridden with an environment variable using the pattern:
MCP_REGISTRY_{SECTION}_{KEY}
All uppercase, with underscores separating nested keys.
| Config Key | Environment Variable |
|---|---|
server.listen | MCP_REGISTRY_SERVER_LISTEN |
db.dsn | MCP_REGISTRY_DB_DSN |
storage.type | MCP_REGISTRY_STORAGE_TYPE |
storage.s3.bucket | MCP_REGISTRY_STORAGE_S3_BUCKET |
auth.mode | MCP_REGISTRY_AUTH_MODE |
auth.oss.issuer_secret | MCP_REGISTRY_AUTH_OSS_ISSUER_SECRET |
auth.enterprise.jwks_url | MCP_REGISTRY_AUTH_ENTERPRISE_JWKS_URL |
public.read_catalog | MCP_REGISTRY_PUBLIC_READ_CATALOG |
presign.enabled | MCP_REGISTRY_PRESIGN_ENABLED |
logging.json | MCP_REGISTRY_LOGGING_JSON |
Environment variables take precedence over config file values, making them ideal for secrets and environment-specific settings.
Complete Configuration Reference
Server
server:
listen: ":8080"
| Key | Type | Default | Description |
|---|---|---|---|
server.listen | string | ":8080" | TCP address for the HTTP server. Use ":PORT" for all interfaces or "HOST:PORT" for a specific interface. |
Database
db:
dsn: "postgres://user:pass@localhost:5432/mcp_registry?sslmode=disable"
| Key | Type | Default | Required | Description |
|---|---|---|---|---|
db.dsn | string | – | Yes | Database connection string. Supports SQLite (sqlite:/path/to/file.db) and PostgreSQL (postgres://user:pass@host:port/dbname). |
SQLite – Suitable for development and small, single-instance deployments. The database file is created automatically. Does not support concurrent writes well.
db:
dsn: "sqlite:/tmp/mcp-registry.db"
PostgreSQL – Recommended for production. Supports concurrent access, transactions, and horizontal scaling.
db:
dsn: "postgres://mcp_user:[email protected]:5432/mcp_registry?sslmode=require"
Common PostgreSQL parameters: sslmode (disable, require, verify-full), connect_timeout, application_name.
Storage
storage:
type: "s3"
fs:
root: "/data/artifacts"
s3:
bucket: "mcp-artifacts"
region: "us-east-1"
endpoint: ""
| Key | Type | Default | Required | Description |
|---|---|---|---|---|
storage.type | string | – | Yes | Storage backend: "fs" (filesystem) or "s3" (S3-compatible). |
storage.fs.root | string | – | When type=fs | Root directory for local artifact storage. Created if it does not exist. |
storage.s3.bucket | string | – | When type=s3 | S3 bucket name. Must already exist. |
storage.s3.region | string | – | When type=s3 | AWS region for the bucket. |
storage.s3.endpoint | string | "" | No | Custom S3 endpoint for MinIO or other compatible services. Empty uses AWS default. |
Authentication
auth:
mode: "oss"
oss:
issuer: "mcp-registry-oss"
audience: "mcp-registry"
issuer_secret: "your-32-byte-or-longer-secret-here"
enable_basic: false
enterprise:
jwks_url: "https://idp.example.com/.well-known/jwks.json"
issuer: "https://idp.example.com/"
audience: "mcp-registry"
| Key | Type | Default | Required | Description |
|---|---|---|---|---|
auth.mode | string | – | Yes | Authentication mode: "oss" or "enterprise". |
auth.oss.issuer | string | "mcp-registry-oss" | No | Issuer identifier for self-issued JWTs (iss claim). |
auth.oss.audience | string | "mcp-registry" | No | Audience identifier for JWTs (aud claim). |
auth.oss.issuer_secret | string | – | In OSS mode | Secret for signing JWTs (HS256). Must be >= 32 bytes. |
auth.oss.enable_basic | bool | false | No | Enable HTTP Basic Auth for API requests. |
auth.enterprise.jwks_url | string | – | In Enterprise mode | URL to the JWKS endpoint for JWT signature verification. |
auth.enterprise.issuer | string | – | In Enterprise mode | Expected iss claim in externally-issued JWTs. |
auth.enterprise.audience | string | "mcp-registry" | No | Expected aud claim in externally-issued JWTs. |
Public Access
public:
read_catalog: true
download_artifacts: false
| Key | Type | Default | Description |
|---|---|---|---|
public.read_catalog | bool | false | Allow unauthenticated GET /v1/catalog requests. Returns only public packages. |
public.download_artifacts | bool | false | Allow unauthenticated download of public package artifacts. Private packages still require auth. |
Repository Policy
repo_policy:
allow_domains:
- "github.com"
- "gitlab.com"
deny_patterns:
- "/malware-"
- "/blocked-org/"
allow_orgs:
- "acme"
- "trusted-vendor"
| Key | Type | Default | Description |
|---|---|---|---|
repo_policy.allow_domains | []string | [] (all allowed) | Whitelist of allowed repository domains. Empty list allows all domains. |
repo_policy.deny_patterns | []string | [] | URL path patterns to block (substring match). |
repo_policy.allow_orgs | []string | [] (all allowed) | Whitelist of allowed repository owners. Empty list allows all orgs. |
Policy evaluation order:
- If
allow_domainsis non-empty, therepo_urlhost must match - The
repo_urlpath must not contain anydeny_patterns - If
allow_orgsis non-empty, the first path segment ofrepo_urlmust match
Versions that fail policy evaluation are set to quarantined status (not rejected).
Presigned URLs
presign:
enabled: true
ttl_seconds: 300
| Key | Type | Default | Description |
|---|---|---|---|
presign.enabled | bool | true | Enable presigned URLs for direct S3 uploads and downloads. Only effective with S3 storage. |
presign.ttl_seconds | int | 300 | Presigned URL lifetime in seconds. |
Logging
logging:
json: true
| Key | Type | Default | Description |
|---|---|---|---|
logging.json | bool | false | Output logs in JSON format. Recommended for production (log aggregation systems). When false, uses human-readable format. |
Example Configurations
Development (SQLite + Filesystem)
Minimal configuration for local development with no external dependencies.
server:
listen: ":8080"
db:
dsn: "sqlite:/tmp/mcp-registry.db"
storage:
type: "fs"
fs:
root: "/tmp/mcp-registry-storage"
auth:
mode: "oss"
oss:
issuer_secret: "super-secret-key-for-development-only-32chars!"
public:
read_catalog: true
download_artifacts: true
presign:
enabled: false
logging:
json: false
Production OSS (PostgreSQL + S3)
For small teams managing their own registry without an external IdP.
server:
listen: ":8080"
db:
dsn: "postgres://mcp_user:${DB_PASSWORD}@db.internal:5432/mcp_registry?sslmode=require"
storage:
type: "s3"
s3:
bucket: "company-mcp-artifacts"
region: "us-east-1"
auth:
mode: "oss"
oss:
issuer: "mcp-registry"
audience: "mcp-registry"
issuer_secret: "${MCP_REGISTRY_AUTH_OSS_ISSUER_SECRET}"
enable_basic: false
public:
read_catalog: false
download_artifacts: false
repo_policy:
allow_domains:
- "github.com"
deny_patterns: []
allow_orgs:
- "my-company"
presign:
enabled: true
ttl_seconds: 300
logging:
json: true
Production Enterprise (PostgreSQL + S3 + External IdP)
For organizations with existing identity infrastructure.
server:
listen: ":8080"
db:
dsn: "postgres://mcp_user:${DB_PASSWORD}@db.internal:5432/mcp_registry?sslmode=require"
storage:
type: "s3"
s3:
bucket: "company-mcp-artifacts"
region: "us-east-1"
auth:
mode: "enterprise"
enterprise:
jwks_url: "https://auth.company.com/.well-known/jwks.json"
issuer: "https://auth.company.com/"
audience: "mcp-registry"
public:
read_catalog: false
download_artifacts: false
repo_policy:
allow_domains:
- "github.com"
allow_orgs:
- "company-org"
presign:
enabled: true
ttl_seconds: 300
logging:
json: true
Docker Compose Stack (PostgreSQL + MinIO)
Configuration matching the MCP Hub Platform docker-compose.yml.
server:
listen: ":8080"
db:
dsn: "postgres://mcphub:mcphub@postgres:5432/mcp_registry?sslmode=disable"
storage:
type: "s3"
s3:
bucket: "mcp"
region: "us-east-1"
endpoint: "http://minio:9000"
auth:
mode: "oss"
oss:
issuer: "mcp-registry-oss"
audience: "mcp-registry"
issuer_secret: "${MCP_REGISTRY_AUTH_OSS_ISSUER_SECRET}"
public:
read_catalog: true
download_artifacts: false
repo_policy:
allow_domains:
- "github.com"
deny_patterns:
- "/malware-"
allow_orgs: []
presign:
enabled: true
ttl_seconds: 300
logging:
json: true
Startup Validation
On startup, the registry validates the configuration:
- Database connection – Attempts to connect and run auto-migrations
- Storage access – Verifies the storage backend is accessible (bucket exists for S3, directory is writable for filesystem)
- Auth configuration – Validates the issuer secret length (>= 32 bytes) in OSS mode, or verifies the JWKS URL is reachable in enterprise mode
Invalid configuration causes the server to exit immediately with a descriptive error message. Check the startup logs for details.
Gotchas
issuer_secretmust be >= 32 characters – The server refuses to start with a shorter secret. Useopenssl rand -base64 32to generate one.- SQLite does not support concurrent writes – Use PostgreSQL for production or any setup with multiple concurrent requests.
- Presigned URLs only work with S3 storage – The filesystem backend always proxies artifact bytes through the registry.
- Enterprise mode has no login endpoint –
POST /v1/auth/loginreturns501 Not Implemented. Tokens must come from the external IdP. MCP_REGISTRY_PUBLIC_READ_CATALOG=trueallows unauthenticated browsing – Disable this in enterprise deployments where catalog visibility is sensitive.- Repo policy violations quarantine, not reject – Versions failing policy checks are created with
quarantinedstatus rather than being rejected outright.