Skip to main content

Configuration

MCP Compose is configured using a TOML file, typically named mcp_compose.toml. This page provides a comprehensive reference for all configuration options.

Configuration File Location

MCP Compose searches for configuration in the following order:

  1. Path specified via --config CLI argument
  2. mcp_compose.toml in the current directory
  3. mcp_compose.toml in parent directories (walking up to root)

Variable Substitution

Keep sensitive data like API keys out of your configuration files by referencing environment variables instead. MCP Compose automatically replaces variable placeholders with their actual values when the configuration is loaded.

Environment Variables

Use ${VAR_NAME} or $VAR_NAME syntax to reference environment variables:

[[servers.proxied.stdio]]
name = "my-server"
command = ["python", "server.py"]
env = { API_KEY = "${MY_API_KEY}" }

Special Variables

VariableDescription
${MCP_COMPOSE_CONFIG_DIR}Absolute path to the directory containing the config file

The MCP_COMPOSE_CONFIG_DIR variable is particularly useful for portable configurations with relative paths:

[[servers.proxied.stdio]]
name = "calculator"
command = ["python", "server.py"]
working_dir = "${MCP_COMPOSE_CONFIG_DIR}"

Composer Section

This is the main section where you give your unified MCP server a name and configure how it behaves when multiple downstream servers expose tools with the same name. Think of it as the identity and personality of your composed server.

[composer]
name = "my-unified-server" # Server name (required)
conflict_resolution = "prefix" # Tool name conflict strategy
log_level = "INFO" # Logging level
port = 8080 # Default port for HTTP transports

Conflict Resolution Strategies

StrategyDescriptionExample Result
prefixPrefix tool name with server namecalculator_add
suffixSuffix tool name with server nameadd_calculator
errorFail on conflictError raised
overrideLast server winsadd
ignoreSkip conflicting toolsTool skipped
customUse custom templateSee tool_manager section

Log Levels

LevelDescription
DEBUGVerbose debugging information
INFOGeneral operational messages
WARNINGWarning messages
ERRORError messages only

Transport Section

Choose how MCP clients (like Claude Desktop or VS Code) connect to your composed server. STDIO is the simplest option where the client launches MCP Compose as a subprocess, while Streamable HTTP runs as a standalone server that multiple clients can connect to over the network.

[transport]
# STDIO transport - for subprocess communication
stdio_enabled = true

# Streamable HTTP transport (recommended)
streamable_http_enabled = true
streamable_http_path = "/mcp"
streamable_http_cors_enabled = true

# SSE transport (deprecated - use streamable_http instead)
sse_enabled = false
sse_path = "/sse"
sse_cors_enabled = true
OptionTypeDefaultDescription
stdio_enabledbooltrueEnable STDIO transport
streamable_http_enabledboolfalseEnable Streamable HTTP transport
streamable_http_pathstring"/mcp"HTTP endpoint path
streamable_http_cors_enabledbooltrueEnable CORS for HTTP
sse_enabledboolfalseEnable SSE transport (deprecated)
sse_pathstring"/sse"SSE endpoint path
sse_cors_enabledbooltrueEnable CORS for SSE

Authentication Section

Protect your MCP server from unauthorized access by requiring clients to prove their identity. Choose from simple API keys for internal use, JWT tokens for stateless authentication, or OAuth2 for integration with existing identity providers like GitHub or Auth0.

[authentication]
enabled = false
providers = ["api_key"]
default_provider = "api_key"

API Key Authentication

[authentication.api_key]
header_name = "X-API-Key"
keys = ["${MCP_API_KEY_1}", "${MCP_API_KEY_2}"]

Basic Authentication

[authentication.basic]
username = "${MCP_USERNAME}"
password = "${MCP_PASSWORD}"

JWT Authentication

[authentication.jwt]
secret = "${JWT_SECRET}"
algorithm = "HS256"
issuer = "mcp-compose"
audience = "mcp-clients"

OAuth2 Authentication

[authentication.oauth2]
provider = "generic" # github, auth0, okta, or generic
authorization_endpoint = "https://id.example.com/oauth2/authorize"
token_endpoint = "https://id.example.com/oauth2/token"
userinfo_endpoint = "https://id.example.com/oauth2/userinfo"
scopes = ["openid", "profile"]
client_id = "${OAUTH_CLIENT_ID}"
client_secret = "${OAUTH_CLIENT_SECRET}"
discovery_url = "${OAUTH_DISCOVERY_URL}" # Optional OIDC discovery

mTLS Authentication

[authentication.mtls]
ca_cert = "/path/to/ca.crt"
client_cert = "/path/to/client.crt"
client_key = "/path/to/client.key"

Authorization Section

Control what authenticated users are allowed to do. Define roles with specific permissions so that administrators can manage servers while regular users can only execute tools. Rate limiting prevents any single user from overwhelming the system.

[authorization]
enabled = false
model = "rbac"

Role-Based Access Control (RBAC)

[[authorization.roles]]
name = "admin"
permissions = ["*"]

[[authorization.roles]]
name = "developer"
permissions = ["tools:*", "servers:read", "logs:read"]

[[authorization.roles]]
name = "user"
permissions = ["tools:execute", "tools:list"]

Rate Limiting

[authorization.rate_limiting]
enabled = false
default_limit = 100
per_role_limits = { admin = 1000, developer = 500, user = 100 }

Servers Section

This is where you define the MCP servers that MCP Compose will unify. You can embed Python-based servers directly, launch local servers as subprocesses (STDIO), or connect to remote servers over HTTP. Each downstream server's tools become available through the single composed endpoint.

Embedded Servers

Embedded servers are Python packages that implement MCP servers, loaded directly into the MCP Compose process.

[servers.embedded]

[[servers.embedded.servers]]
name = "jupyter-mcp-server"
package = "jupyter_mcp_server"
enabled = true
tool_mappings = { "create" = "jupyter_create", "run" = "jupyter_run" }
OptionTypeRequiredDescription
namestringYesServer identifier
packagestringYesPython package name
enabledboolNoEnable/disable server (default: true)
tool_mappingstableNoCustom tool name mappings

STDIO Proxied Servers

STDIO servers run as subprocesses, communicating via stdin/stdout.

[[servers.proxied.stdio]]
name = "weather-server"
command = ["uvx", "mcp-server-weather"]
working_dir = "${MCP_COMPOSE_CONFIG_DIR}"
env = { WEATHER_API_KEY = "${WEATHER_API_KEY}" }

# Lifecycle management
restart_policy = "on-failure" # never, on-failure, always
max_restarts = 3
restart_delay = 5

# Health checks
health_check_enabled = true
health_check_interval = 30
health_check_timeout = 5
health_check_method = "tool"
health_check_tool = "health"

# Logging
log_stdout = true
log_stderr = true
log_file = "/var/log/mcp-compose/weather-server.log"
OptionTypeRequiredDefaultDescription
namestringYes-Server identifier
commandarrayYes-Command and arguments
working_dirstringNo-Working directory for subprocess
envtableNo{}Environment variables
restart_policystringNo"never"Restart behavior
max_restartsintNo3Maximum restart attempts
restart_delayintNo5Seconds between restarts
health_check_enabledboolNofalseEnable health checking
health_check_intervalintNo30Seconds between checks
health_check_timeoutintNo5Health check timeout
health_check_methodstringNo"tool"Check method
health_check_toolstringNo"health"Tool to call for health
log_stdoutboolNotrueLog server stdout
log_stderrboolNotrueLog server stderr
log_filestringNo-File path for logs

Resource Limits

[[servers.proxied.stdio]]
name = "resource-limited-server"
command = ["python", "server.py"]

[servers.proxied.stdio.resource_limits]
max_memory_mb = 512
max_cpu_percent = 50

Streamable HTTP Proxied Servers

Connect to remote MCP servers over HTTP with streaming support.

[[servers.proxied.http]]
name = "remote-api-server"
url = "https://api.example.com/mcp"
protocol = "lines" # chunked, lines, poll
auth_token = "${REMOTE_SERVER_TOKEN}"
auth_type = "bearer" # bearer, basic
timeout = 30
retry_interval = 5
keep_alive = true
reconnect_on_failure = true
max_reconnect_attempts = 10
poll_interval = 2 # Only for poll protocol
mode = "proxy" # proxy or translator

# Health checks
health_check_enabled = true
health_check_interval = 60
health_check_endpoint = "/health"
OptionTypeRequiredDefaultDescription
namestringYes-Server identifier
urlstringYes-Server URL
protocolstringNo"lines"HTTP streaming protocol
auth_tokenstringNo-Authentication token
auth_typestringNo"bearer"Token type
timeoutintNo30Request timeout in seconds
retry_intervalintNo5Retry interval in seconds
keep_aliveboolNotrueMaintain persistent connection
reconnect_on_failureboolNotrueAuto-reconnect on failure
max_reconnect_attemptsintNo10Maximum reconnection attempts
poll_intervalintNo2Polling interval (poll protocol only)
modestringNo"proxy"Operating mode

SSE Proxied Servers (Deprecated)

warning

SSE transport is deprecated. Use Streamable HTTP ([[servers.proxied.http]]) instead.

[[servers.proxied.sse]]
name = "legacy-server"
url = "http://localhost:8080/sse"
auth_token = "${AUTH_TOKEN}"
timeout = 30
retry_interval = 5
reconnect_on_failure = true
max_reconnect_attempts = 10

Tool Manager Section

Fine-tune how tools from different servers are named and organized. Create user-friendly aliases, apply different naming strategies to specific tools, or enable versioning when you need to support multiple versions of the same tool simultaneously.

[tool_manager]
conflict_resolution = "prefix"

Tool-Specific Overrides

Apply different conflict resolution strategies to specific tools:

[[tool_manager.tool_overrides]]
tool_pattern = "notebook_*"
resolution = "prefix"

[[tool_manager.tool_overrides]]
tool_pattern = "search_*"
resolution = "suffix"

Custom Naming Template

Define a custom template for tool naming:

[tool_manager.custom_template]
template = "{server_name}_{tool_name}"

Available template variables:

  • {server_name} - The server's name
  • {tool_name} - The original tool name

Tool Aliases

Create aliases for tools:

[tool_manager.aliases]
jupyter_create = "create_notebook"
fs_read = "read_file"

Tool Versioning

Enable multiple versions of the same tool:

[tool_manager.versioning]
enabled = false
allow_multiple_versions = false
version_suffix_format = "_v{version}"

REST API Section

Expose a REST API for programmatic management of your MCP Compose instance. Use it to start/stop servers, invoke tools, or integrate with external systems. The built-in OpenAPI documentation makes it easy to explore available endpoints.

[api]
enabled = true
path_prefix = "/api/v1"
host = "0.0.0.0"
port = 8080
cors_enabled = true
cors_origins = ["http://localhost:3000"]
cors_methods = ["GET", "POST", "PUT", "DELETE"]
docs_enabled = true
docs_path = "/docs"
openapi_path = "/openapi.json"
OptionTypeDefaultDescription
enabledbooltrueEnable REST API
path_prefixstring"/api/v1"API path prefix
hoststring"0.0.0.0"Bind address
portint8080Listen port
cors_enabledbooltrueEnable CORS
cors_originsarray["*"]Allowed origins
cors_methodsarray["GET", "POST", ...]Allowed HTTP methods
docs_enabledbooltrueEnable API documentation
docs_pathstring"/docs"Documentation path
openapi_pathstring"/openapi.json"OpenAPI spec path

Web UI Section

Enable a browser-based dashboard for managing your MCP Compose instance without command-line tools. Test tools interactively, view logs in real-time, monitor metrics, and even edit configuration—all from a visual interface.

[ui]
enabled = true
framework = "react"
mode = "embedded" # embedded or separate
path = "/ui"
port = 9456 # Used when mode is 'separate'
static_dir = "/var/www/mcp-compose/ui"
features = [
"server_management",
"tool_testing",
"logs_viewing",
"metrics_dashboard",
"configuration_editor"
]
OptionTypeDefaultDescription
enabledbooltrueEnable web UI
frameworkstring"react"UI framework
modestring"embedded"Deployment mode
pathstring"/ui"UI path
portint9456Separate mode port
static_dirstring-Static files directory
featuresarrayallEnabled UI features

Available Features

FeatureDescription
server_managementStart, stop, restart servers
tool_testingInteractive tool testing
logs_viewingView server logs
metrics_dashboardMetrics and monitoring
configuration_editorEdit configuration

Monitoring Section

Gain visibility into how your MCP servers are performing. Collect metrics for dashboards and alerts, configure structured logging for troubleshooting, and enable distributed tracing to follow requests across multiple servers.

[monitoring]
enabled = true

Metrics

[monitoring.metrics]
enabled = true
provider = "prometheus"
endpoint = "/metrics"
collection_interval = 15
collect = [
"tool_invocation_count",
"tool_invocation_duration",
"tool_error_rate",
"server_health_status",
"process_cpu_usage",
"process_memory_usage",
"request_rate",
"response_time"
]

Logging

[monitoring.logging]
level = "INFO"
format = "json" # json or text
output = "stdout" # stdout or file
log_file = "/var/log/mcp-compose/composer.log"
rotation = "daily" # daily, size, or none
max_size_mb = 100
max_files = 7
aggregate_managed_logs = true

Distributed Tracing

[monitoring.tracing]
enabled = false
provider = "opentelemetry"
endpoint = "http://localhost:4317"
sample_rate = 1.0

Health Endpoints

[monitoring.health]
endpoint = "/health"
detailed_endpoint = "/health/detailed"

Complete Example

Here's a complete configuration example combining multiple features:

# ============================================================================
# Composer Settings
# ============================================================================
[composer]
name = "production-composer"
conflict_resolution = "prefix"
log_level = "INFO"
port = 8080

# ============================================================================
# Transport Configuration
# ============================================================================
[transport]
stdio_enabled = false
streamable_http_enabled = true
streamable_http_path = "/mcp"
streamable_http_cors_enabled = true

# ============================================================================
# Authentication
# ============================================================================
[authentication]
enabled = true
providers = ["jwt"]
default_provider = "jwt"

[authentication.jwt]
secret = "${JWT_SECRET}"
algorithm = "HS256"
issuer = "mcp-compose"

# ============================================================================
# Servers
# ============================================================================
[[servers.proxied.stdio]]
name = "filesystem"
command = ["python", "-m", "mcp_server_filesystem", "./data"]
working_dir = "${MCP_COMPOSE_CONFIG_DIR}"
restart_policy = "on-failure"
max_restarts = 3

[[servers.proxied.stdio]]
name = "database"
command = ["python", "db_server.py"]
working_dir = "${MCP_COMPOSE_CONFIG_DIR}"
env = { DATABASE_URL = "${DATABASE_URL}" }
health_check_enabled = true
health_check_interval = 30

[[servers.proxied.http]]
name = "remote-api"
url = "https://api.example.com/mcp"
auth_token = "${API_TOKEN}"
timeout = 60
reconnect_on_failure = true

# ============================================================================
# Monitoring
# ============================================================================
[monitoring]
enabled = true

[monitoring.metrics]
enabled = true
provider = "prometheus"
endpoint = "/metrics"

[monitoring.logging]
level = "INFO"
format = "json"
output = "stdout"

[monitoring.health]
endpoint = "/health"

Validating Configuration

Validate your configuration file without starting the server:

mcp-compose validate --config mcp_compose.toml

Best Practices

  1. Use environment variables for sensitive data like API keys, tokens, and passwords
  2. Use ${MCP_COMPOSE_CONFIG_DIR} for portable relative paths in working_dir
  3. Set appropriate restart policies for production deployments (on-failure recommended)
  4. Enable health checks for critical servers to detect failures early
  5. Use prefix conflict resolution to avoid tool name collisions
  6. Enable monitoring in production for observability
  7. Use Streamable HTTP instead of SSE for new deployments
  8. Validate configuration before deploying changes