The MCP-SWAIG Gateway bridges Model Context Protocol (MCP) servers with SignalWire AI Gateway (SWAIG) functions, allowing SignalWire AI agents to seamlessly interact with MCP-based tools. This gateway acts as a translation layer and session manager between the two protocols.
mcp_gateway/
)Translates between SWAIG and MCP protocols
MCP Gateway Skill (signalwire_agents/skills/mcp_gateway/
)
Manages session lifecycle using call_id
Test MCP Server (mcp_gateway/test/todo_mcp.py
)
SignalWire Agent Gateway Service MCP Server
| | |
|---(1) Add Skill--------------->| |
|<--(2) Query Tools--------------| |
| |---(3) List Tools-------->|
| |<--(4) Tool List----------|
|---(5) Call SWAIG Function----->| |
| |---(6) Spawn Session----->|
| |---(7) Call MCP Tool----->|
| |<--(8) MCP Response-------|
|<--(9) SWAIG Response-----------| |
| | |
|---(10) Hangup Hook------------>| |
| |---(11) Close Session---->|
The gateway uses a custom envelope format for routing and session management:
{
"session_id": "call_xyz123", // From SWAIG call_id
"service": "todo", // MCP service name
"tool": "add_todo", // Tool name
"arguments": { // Tool arguments
"text": "Buy milk"
},
"timeout": 300, // Session timeout in seconds
"metadata": { // Optional metadata
"agent_id": "agent_123",
"timestamp": "2024-01-20T10:30:00Z"
}
}
mcp_gateway/
├── config.json # Gateway configuration
├── gateway_service.py # Main HTTP/HTTPS server
├── mcp_manager.py # MCP server lifecycle management
├── session_manager.py # Session handling and timeouts
├── requirements.txt # Python dependencies
├── Dockerfile # Docker container definition
├── docker-compose.yml # Docker compose configuration
├── mcp-docker.sh # Docker management helper script
├── README.md # Gateway documentation
├── certs/ # SSL certificates (optional)
│ └── .gitignore # Ignore actual certificates
├── test/
│ ├── todo_mcp.py # Test MCP server
│ ├── test_gateway.sh # Curl test scripts
│ └── test_agent.py # Test SignalWire agent
└── examples/
├── config.example.json
└── generate_cert.sh # Generate self-signed certificate
signalwire_agents/skills/mcp_gateway/
├── __init__.py
├── skill.py # MCP gateway skill
└── README.md # Skill documentation
config.json
) 🔗 ↑ TOCThe configuration supports environment variable substitution using ${VAR_NAME|default}
syntax:
{
"server": {
"host": "${MCP_HOST|0.0.0.0}",
"port": "${MCP_PORT|8080}",
"auth_user": "${MCP_AUTH_USER|admin}",
"auth_password": "${MCP_AUTH_PASSWORD|changeme}",
"auth_token": "${MCP_AUTH_TOKEN|optional-bearer-token}"
},
"services": {
"todo": {
"command": ["python3", "./test/todo_mcp.py"],
"description": "Simple todo list for testing",
"enabled": true,
"sandbox": {
"enabled": true,
"resource_limits": true,
"restricted_env": true
}
},
"shell-mpc": {
"command": ["python3", "/path/to/shell_mpc.py"],
"description": "Shell PTY access",
"enabled": false,
"sandbox": {
"enabled": false,
"note": "Shell access needs full filesystem"
}
},
"calculator": {
"command": ["node", "/path/to/calculator.js"],
"description": "Math calculations",
"enabled": true,
"sandbox": {
"enabled": true,
"resource_limits": true,
"restricted_env": false,
"note": "Needs NODE_PATH but can have resource limits"
}
}
},
"session": {
"default_timeout": 300,
"max_sessions_per_service": 100,
"cleanup_interval": 60,
"sandbox_dir": "./sandbox"
},
"rate_limiting": {
"default_limits": ["200 per day", "50 per hour"],
"tools_limit": "30 per minute",
"call_limit": "10 per minute",
"session_delete_limit": "20 per minute",
"storage_uri": "memory://"
},
"logging": {
"level": "INFO",
"file": "gateway.log"
}
}
The gateway supports environment variable substitution in config.json using the format ${VAR_NAME|default_value}
.
Example usage:
Method 1: Using .env file (recommended)
# Copy the example
cp .env.example .env
# Edit with your values
vim .env
# Run - Docker Compose automatically reads .env
./mcp-docker.sh start
# Or for non-Docker
source .env
python3 gateway_service.py
Method 2: Export environment variables
# Set environment variables
export MCP_PORT=9000
export MCP_AUTH_PASSWORD=mysecret
# Run the gateway
python3 gateway_service.py
Method 3: Inline variables
# Set variables for just this command
MCP_PORT=9000 MCP_AUTH_PASSWORD=mysecret ./mcp-docker.sh start
Supported variables:
MCP_HOST
: Server bind address (default: 0.0.0.0)MCP_PORT
: Server port (default: 8080)MCP_AUTH_USER
: Basic auth username (default: admin)MCP_AUTH_PASSWORD
: Basic auth password (default: changeme)MCP_AUTH_TOKEN
: Bearer token for API access (default: empty)MCP_SESSION_TIMEOUT
: Session timeout in seconds (default: 300)MCP_MAX_SESSIONS
: Max sessions per service (default: 100)MCP_CLEANUP_INTERVAL
: Session cleanup interval in seconds (default: 60)MCP_LOG_LEVEL
: Logging level (default: INFO)MCP_LOG_FILE
: Log file path (default: gateway.log)Each service can have its own sandbox configuration:
Option | Default | Description |
---|---|---|
enabled |
true |
Enable/disable sandboxing completely |
resource_limits |
true |
Apply CPU, memory, process limits |
restricted_env |
true |
Use minimal environment variables |
working_dir |
Current dir | Working directory for the process |
allowed_paths |
N/A | Future: Path access restrictions |
"sandbox": {
"enabled": true,
"resource_limits": true,
"restricted_env": true
}
"sandbox": {
"enabled": true,
"resource_limits": true,
"restricted_env": false
}
"sandbox": {
"enabled": false
}
agent.add_skill("mcp_gateway", {
"gateway_url": "https://localhost:8080",
"auth_user": "admin",
"auth_password": "changeme",
"services": [
{
"name": "todo",
"tools": ["add_todo", "list_todos"] # Specific tools only
},
{
"name": "calculator",
"tools": "*" # All tools
}
],
"session_timeout": 300, # Override default timeout
"tool_prefix": "mcp_", # Prefix for SWAIG function names
"retry_attempts": 3, # Gateway connection retries
"request_timeout": 30, # Individual request timeout
"verify_ssl": True # SSL certificate verification
})
Health check endpoint
curl http://localhost:8080/health
List available MCP services
curl -u admin:changeme http://localhost:8080/services
Get tools for a specific service
curl -u admin:changeme http://localhost:8080/services/todo/tools
Call a tool on a service
Using Basic Auth:
curl -u admin:changeme -X POST http://localhost:8080/services/todo/call \
-H "Content-Type: application/json" \
-d '{
"tool": "add_todo",
"arguments": {"text": "Test item"},
"session_id": "test-123",
"timeout": 300
}'
Using Bearer Token:
curl -X POST http://localhost:8080/services/todo/call \
-H "Authorization: Bearer your-token-here" \
-H "Content-Type: application/json" \
-d '{
"tool": "add_todo",
"arguments": {"text": "Test item"},
"session_id": "test-123"
}'
List active sessions
curl -u admin:changeme http://localhost:8080/sessions
Close a specific session
curl -u admin:changeme -X DELETE http://localhost:8080/sessions/test-123
Fully configurable through the rate_limiting
section in config.json:
"rate_limiting": {
"default_limits": ["200 per day", "50 per hour"],
"tools_limit": "30 per minute",
"call_limit": "10 per minute",
"session_delete_limit": "20 per minute",
"storage_uri": "memory://"
}
default_limits
: Global rate limits per IP addresstools_limit
: Rate limit for /services/*/tools
endpointscall_limit
: Rate limit for /services/*/call
endpointssession_delete_limit
: Rate limit for session deletionstorage_uri
: Storage backend for rate limit counters (memory:// or redis://)Configurable per MCP service with three security levels:
File size: 10MB max
Medium Security
For services needing PATH, NODE_PATH, etc.
No Sandbox
# Start the gateway
cd mcp_gateway
python3 gateway_service.py
# Test with curl
./test/test_gateway.sh
# Test the agent with MCP skill
swaig-test test/test_agent.py --list-tools
# IMPORTANT: --call-id must come BEFORE --exec for session persistence
swaig-test test/test_agent.py --call-id test-session --exec mcp_todo_add_todo --text "Buy milk"
swaig-test test/test_agent.py --call-id test-session --exec mcp_todo_list_todos
# WRONG: This won't work - --call-id after --exec is treated as function argument
swaig-test test/test_agent.py --exec mcp_todo_add_todo --text "Buy milk" --call-id test-session
# Generate SWML document
swaig-test test/test_agent.py --dump-swml
# test/test_agent.py
from signalwire_agents import AgentBase
class TestMCPAgent(AgentBase):
def __init__(self):
super().__init__(name="MCP Test Agent")
self.add_skill("mcp_gateway", {
"gateway_url": "http://localhost:8080",
"auth_user": "admin",
"auth_password": "changeme",
"services": [{"name": "todo"}]
})
if __name__ == "__main__":
agent = TestMCPAgent()
agent.run()
cd mcp_gateway
python3 gateway_service.py
The Docker setup supports three configuration scenarios:
To pre-configure the image at build time:
# Edit your config.json
cp sample_config.json config.json
vim config.json
# Build with config included
./mcp-docker.sh build # Will include config.json in image
Port Configuration: The Docker setup automatically reads the port from your config.json file. If your config specifies port 8100, Docker will expose the service on port 8100.
The mcp-docker.sh script automatically detects the port from config.json. You can also override it using an environment variable:
# Override port at runtime (must match what's in config.json)
MCP_PORT=8100 ./mcp-docker.sh start
Note: The port in the MCP_PORT environment variable should match the port configured in your config.json file, as the container internally listens on the configured port.
The easiest way to manage the Docker deployment is using the provided helper script:
cd mcp_gateway
# Show available commands
./mcp-docker.sh help
# Build the Docker image
./mcp-docker.sh build
# Start in foreground (Ctrl+C to stop)
./mcp-docker.sh start
# Start in background
./mcp-docker.sh start -d
# View logs
./mcp-docker.sh logs
./mcp-docker.sh logs -f # Follow logs
# Check status
./mcp-docker.sh status
# Restart the container
./mcp-docker.sh restart
# Stop the container
./mcp-docker.sh stop
# Open shell in running container
./mcp-docker.sh shell
# Clean up (remove container and volumes)
./mcp-docker.sh clean
cd mcp_gateway
docker build -t mcp-gateway .
docker run -p 8080:8080 -v $(pwd)/config.json:/app/config.json mcp-gateway
cd mcp_gateway
docker-compose up
docker-compose up -d # Run in background
docker-compose logs -f # Follow logs
docker-compose down # Stop and remove
# Generate or place certificates
mkdir -p certs
# Place server.pem in certs/
# Run with HTTPS
python3 gateway_service.py
Ensure working directory is correct for sandboxed processes
Authentication Failures
For Bearer tokens, ensure "Bearer " prefix is included
Session Timeouts
Monitor for stuck MCP processes
SSL Certificate Errors
verify_ssl: false
Ensure cert path is correct
Gateway Shutdown Hangs
Check for zombie MCP processes: ps aux | grep mcp
Session Persistence Issues
ps aux | grep todo_mcp
Enable debug logging:
{
"logging": {
"level": "DEBUG",
"file": "gateway.log"
}
}