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

  1. app_lifespan() is called when the MCP server starts.
  2. Settings is loaded from environment variables (with OPENSIPS_MCP_ prefix).
  3. An httpx.AsyncClient and MIClient are created for MI communication.
  4. A SQLAlchemy async engine and session factory are created for database access.
  5. All resources are bundled into AppContext and yielded.
  6. 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 which command is used to verify connectivity.
  • Shared HTTP client: A single httpx.AsyncClient is 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 -- Base declarative 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 a session_factory and manages its own session lifecycle.

Supported Backends

BackendDriverInstall Extra
SQLiteaiosqlite(included)
MySQLasyncmypip install -e ".[mysql]"
PostgreSQLasyncpgpip 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:

  1. Scenario rendering (render(scenario, params)) -- Renders a complete config from a named scenario template.
  2. 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.