This guide explains the unified configuration system available in SignalWire AI Agents SDK.
All SignalWire services (SWML-based agents, Search, MCP Gateway) now support optional JSON configuration files with environment variable substitution. SWML (SignalWire Markup Language) is the JSON document format that defines agent behavior during calls. Services continue to work without any configuration file, maintaining full backward compatibility.
# Works exactly as before - no config needed
agent = MyAgent()
agent.run()# Automatically detects config.json if present
agent = MyAgent()
# Or specify a config file
agent = MyAgent(config_file="production_config.json")Services look for configuration files in this order:
- Service-specific:
{service_name}_config.json(e.g.,search_config.json) - Generic:
config.json - Hidden:
.swml/config.json - User home:
~/.swml/config.json - System:
/etc/swml/config.json
{
"service": {
"name": "my-service",
"host": "${HOST|0.0.0.0}",
"port": "${PORT|3000}"
},
"security": {
"ssl_enabled": "${SSL_ENABLED|false}",
"ssl_cert_path": "${SSL_CERT|/etc/ssl/cert.pem}",
"ssl_key_path": "${SSL_KEY|/etc/ssl/key.pem}",
"auth": {
"basic": {
"enabled": true,
"user": "${AUTH_USER|signalwire}",
"password": "${AUTH_PASSWORD}"
},
"bearer": {
"enabled": "${BEARER_ENABLED|false}",
"token": "${BEARER_TOKEN}"
}
},
"allowed_hosts": ["${PRIMARY_HOST}", "${SECONDARY_HOST|localhost}"],
"cors_origins": "${CORS_ORIGINS|*}",
"rate_limit": "${RATE_LIMIT|60}"
}
}The configuration system supports ${VAR|default} syntax:
${VAR}- Use environment variable VAR (error if not set)${VAR|default}- Use VAR or "default" if not set${VAR|}- Use VAR or empty string if not set
{
"database": {
"host": "${DB_HOST|localhost}",
"port": "${DB_PORT|5432}",
"password": "${DB_PASSWORD}"
}
}Configuration values are applied in this order (highest to lowest):
- Constructor parameters - Explicitly passed to service
- Config file values - From JSON configuration
- Environment variables - Direct env vars (backward compatibility)
- Defaults - Hard-coded defaults
{
"service": {
"name": "my-agent",
"route": "/agent",
"port": "${AGENT_PORT|3000}"
},
"security": {
"ssl_enabled": "${SSL_ENABLED|false}",
"auth": {
"basic": {
"user": "${AGENT_USER|agent}",
"password": "${AGENT_PASSWORD}"
}
}
}
}{
"service": {
"port": "${SEARCH_PORT|8001}",
"indexes": {
"docs": "${DOCS_INDEX|./docs.swsearch}",
"api": "${API_INDEX|./api.swsearch}"
}
},
"security": {
"ssl_enabled": "${SEARCH_SSL|false}",
"auth": {
"basic": {
"enabled": true,
"user": "${SEARCH_USER|search}",
"password": "${SEARCH_PASSWORD}"
}
}
}
}{
"server": {
"host": "${MCP_HOST|0.0.0.0}",
"port": "${MCP_PORT|8080}",
"auth_user": "${MCP_USER|admin}",
"auth_password": "${MCP_PASSWORD}",
"auth_token": "${MCP_TOKEN}"
},
"services": {
"example": {
"command": ["python", "${SERVICE_PATH|./service.py}"],
"enabled": "${SERVICE_ENABLED|true}"
}
},
"session": {
"default_timeout": "${SESSION_TIMEOUT|300}",
"max_sessions_per_service": "${MAX_SESSIONS|100}"
}
}All services share the same security configuration options:
{
"security": {
"ssl_enabled": true,
"ssl_cert_path": "/etc/ssl/cert.pem",
"ssl_key_path": "/etc/ssl/key.pem",
"domain": "api.example.com",
"allowed_hosts": ["api.example.com", "app.example.com"],
"cors_origins": ["https://app.example.com"],
"max_request_size": 5242880,
"rate_limit": 30,
"request_timeout": 60,
"use_hsts": true,
"hsts_max_age": 31536000
}
}Before:
export SWML_SSL_ENABLED=true
export SWML_SSL_CERT_PATH=/etc/ssl/cert.pem
python my_agent.pyAfter (Option 1 - Keep using env vars):
# Still works exactly the same
export SWML_SSL_ENABLED=true
export SWML_SSL_CERT_PATH=/etc/ssl/cert.pem
python my_agent.pyAfter (Option 2 - Use config file):
// config.json
{
"security": {
"ssl_enabled": true,
"ssl_cert_path": "/etc/ssl/cert.pem"
}
}After (Option 3 - Mix config and env vars):
// config.json
{
"security": {
"ssl_enabled": true,
"ssl_cert_path": "${SSL_CERT|/etc/ssl/cert.pem}"
}
}-
Keep secrets in environment variables
{ "security": { "auth": { "basic": { "user": "admin", "password": "${ADMIN_PASSWORD}" } } } } -
Use defaults for development
{ "service": { "port": "${PORT|3000}", "host": "${HOST|localhost}" } } -
Environment-specific configs
dev_config.json- Development settingsprod_config.json- Production settings- Use
${ENV}to switch between them
-
Version control
- Commit config files WITHOUT secrets
- Use
.gitignorefor local overrides - Document required environment variables
from signalwire_agents.core.config_loader import ConfigLoader
# Load config
loader = ConfigLoader(["my_config.json"])
if loader.has_config():
config = loader.get_config()
# Get specific value with substitution
port = loader.get("service.port", default=3000)
# Get entire section
security = loader.get_section("security")# SWML Service
from signalwire_agents import AgentBase
class MyAgent(AgentBase):
def __init__(self):
# Auto-detects config.json if present
super().__init__(name="my-agent", config_file="agent_config.json")
# Search Service
from signalwire_agents.search import SearchService
service = SearchService(config_file="search_config.json")
# MCP Gateway
from mcp_gateway.gateway_service import MCPGateway
gateway = MCPGateway(config_path="mcp_config.json")-
Check file exists and is valid JSON:
python -m json.tool config.json
-
Enable debug logging:
import logging logging.basicConfig(level=logging.DEBUG)
-
Check for syntax errors in variable substitution
-
Ensure correct syntax:
${VAR}or${VAR|default} -
Check environment variable is exported:
echo $MY_VAR
-
Remember config file values override env vars
- Config file auth settings override env vars
- Check which auth method is enabled
- Verify credentials match