Architecture
April 28, 2026 ยท View on GitHub
This document describes the internal architecture of the OpenSIPS MCP Server.
Layered Design
The server is organized into five distinct layers. Each layer depends only on layers below it.
+---------------------------------------------------------+
| MCP Layer (FastMCP) |
| - Transport handling (stdio, SSE, streamable-http) |
| - Tool/Resource/Prompt registration and dispatch |
+---------------------------------------------------------+
| Service Layer (Tools, Resources, Prompts) |
| - 94 tools grouped by domain |
| - 20 resources exposing system state |
| - 18 prompts for guided workflows |
+---------------------------------------------------------+
| Infrastructure Layer |
| - MI Client (JSON-RPC over HTTP) |
| - Database (SQLAlchemy async, multi-backend) |
| - Config Engine (Jinja2 templates) |
+---------------------------------------------------------+
| Version Layer |
| - Base version strategy |
| - v3.4, v3.6, v4.0 implementations |
| - Version registry |
+---------------------------------------------------------+
| Security Layer |
| - RBAC (Role-Based Access Control) |
| - Input validation and sanitization |
| - Audit logging |
+---------------------------------------------------------+
AppContext and Lifespan Management
The AppContext dataclass holds all shared application state. It is created during the FastMCP lifespan and made available to every tool, resource, and prompt through the MCP context object.
@dataclass
class AppContext:
mi_client: MIClient # HTTP client for MI commands
db_session_factory: async_sessionmaker # Database session factory
version_strategy: str # Active OpenSIPS version
settings: Settings # All configuration
http_client: httpx.AsyncClient # Shared HTTP client
Lifespan Flow
app_lifespan()is called when the MCP server starts.Settingsis loaded from environment variables (withOPENSIPS_MCP_prefix).- An
httpx.AsyncClientandMIClientare created for MI communication. - A SQLAlchemy async engine and session factory are created for database access.
- All resources are bundled into
AppContextand yielded. - On shutdown, the HTTP client and database engine are disposed.
MI Client Design
The MIClient class (src/opensips_mcp/mi/client.py) communicates with OpenSIPS via JSON-RPC 2.0 over HTTP.
Key features
- Retry with backoff: Transient failures (connection errors, timeouts, 5xx) are retried up to 3 times with exponential backoff (0.5s, 1s, 2s).
- Error classification: Responses are parsed into specific exception types (
MIConnectionError,MITimeoutError,MICommandNotFoundError). - Health check: A lightweight
whichcommand is used to verify connectivity. - Shared HTTP client: A single
httpx.AsyncClientis reused across all MI calls for connection pooling.
Error Hierarchy
MIError (base)
+-- MIConnectionError (network/transport failures)
+-- MITimeoutError (request exceeded timeout)
+-- MICommandNotFoundError (unknown MI command)
Database Layer Design
The database layer uses SQLAlchemy 2.0 async with support for multiple backends.
Components
db/engine.py--Basedeclarative class and engine factory. Non-SQLite backends get connection pooling (pool_size=5, max_overflow=10).db/models/-- SQLAlchemy ORM models for each OpenSIPS table (subscriber, dispatcher, dr_rules, dr_gateways, dr_carriers, dr_groups, address, call_center, load_balancer, dialplan, domain, location, dialog, acc, clusterer).db/crud/-- Async CRUD functions for each domain. Every function accepts asession_factoryand manages its own session lifecycle.
Supported Backends
| Backend | Driver | Install Extra |
|---|---|---|
| SQLite | aiosqlite | (included) |
| MySQL | asyncmy | pip install -e ".[mysql]" |
| PostgreSQL | asyncpg | pip install -e ".[postgres]" |
Config Engine Design
The configuration engine (src/opensips_mcp/cfg/) generates complete OpenSIPS configuration files from Jinja2 templates.
Template Structure
cfg/templates/
base.cfg.j2 # Base template for custom configs
globals.cfg.j2 # Global parameters block
modules.cfg.j2 # Module loading block
routes.cfg.j2 # Route block skeleton
scenarios/ # 8 complete scenario templates
load_balancer.cfg.j2
class4_sbc.cfg.j2
registrar_class5.cfg.j2
webrtc_gateway.cfg.j2
residential_pbx.cfg.j2
call_center.cfg.j2
b2bua.cfg.j2
sbc_with_rtpengine.cfg.j2
partials/ # 13 reusable template fragments
accounting.cfg.j2
anti_flood.cfg.j2
authentication.cfg.j2
dialog_setup.cfg.j2
dispatcher_logic.cfg.j2
drouting_logic.cfg.j2
load_balancer_logic.cfg.j2
media_relay.cfg.j2
nat_traversal.cfg.j2
presence.cfg.j2
registrar_logic.cfg.j2
tls.cfg.j2
websocket.cfg.j2
ConfigBuilder
The ConfigBuilder class provides two rendering modes:
- Scenario rendering (
render(scenario, params)) -- Renders a complete config from a named scenario template. - Custom rendering (
render_custom(features, params)) -- Renders from the base template with selected feature partials.
Each scenario defines required_params and optional_params in the SCENARIOS registry.
Security Model
See security.md for full details.
RBAC
Two roles are supported:
- readonly -- Can read MI output, statistics, health, database state, config, resources, and docs.
- admin -- All readonly permissions plus MI execution, database writes, config generation/reload, and process management.
Tool Protection
Every tool is decorated with @require_permission("scope"). The permission check reads the active role from AppContext.settings.role and verifies it against the PERMISSIONS map.
Input Validation
The security/validators.py module provides:
- SIP URI validation (regex-based)
- IP address validation (IPv4/IPv6)
- OpenSIPS identifier validation (alphanumeric + underscore, 1-64 chars)
- MI parameter sanitization (strips control characters and semicolons)
- SQL identifier whitelisting (only known OpenSIPS table/column names)
- Port number range validation
- Domain name validation (RFC 1035/1123)
Audit Logging
The security/audit.py module provides structured JSON logging for sensitive operations. The @audited(operation) decorator automatically logs tool calls with masked credentials.