cfg_compose_flags
Resolves WITH_* flags to identify which modules load and globals set. Use to determine configuration requirements before building a full config.
Instructions
Resolve a set of WITH_* flags into modules and globals.
Does NOT produce a config — it answers the question "if I enable
these flags, what modules get loaded and which globals get set?".
Pairs with :func:cfg_build_from_flags for the full render.
Parameters
flags:
List of WITH_* flag names (case-insensitive).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| flags | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- MCP tool handler for cfg_compose_flags: resolves WITH_* feature flags into their constituent modules and global overrides using FlagComposer.resolve(), returns the composition dict.
@mcp.tool() @require_permission("config.read") async def cfg_compose_flags( ctx: Context, flags: list[str], ) -> dict[str, Any]: """Resolve a set of ``WITH_*`` flags into modules and globals. Does NOT produce a config — it answers the question "if I enable these flags, what modules get loaded and which globals get set?". Pairs with :func:`cfg_build_from_flags` for the full render. Parameters ---------- flags: List of ``WITH_*`` flag names (case-insensitive). """ try: composition = _composer.resolve(flags) return composition.to_dict() except Exception as exc: # noqa: BLE001 return {"error": str(exc)} - FlagComposition dataclass defines the output schema returned by cfg_compose_flags: the resolved flags, modules, globals, unknown_flags, and implied_by fields.
class FlagComposition: flags: list[str] = field(default_factory=list) modules: list[str] = field(default_factory=list) globals: dict[str, str] = field(default_factory=dict) unknown_flags: list[str] = field(default_factory=list) implied_by: dict[str, list[str]] = field(default_factory=dict) def to_dict(self) -> dict: return { "flags": self.flags, "modules": self.modules, "globals": self.globals, "unknown_flags": self.unknown_flags, "implied_by": self.implied_by, } - src/opensips_mcp/tools/cfg_tools.py:1341-1341 (registration)Registration via @mcp.tool() decorator in cfg_tools.py, imported in server.py at line 168.
@mcp.tool() - FlagComposer.resolve() is the core helper that the cfg_compose_flags handler calls. It canonicalizes flags, unfolds transitive implications, resolves modules and globals, and topologically sorts the module list.
class FlagComposer: """Resolve a set of ``WITH_*`` flags into modules and global overrides.""" def resolve(self, flags: list[str]) -> FlagComposition: # Canonicalize case — accept both ``WITH_AUTH`` and ``with_auth``. raw = [f.strip().upper() for f in flags if f.strip()] unknown = [f for f in raw if f not in FLAGS] known = [f for f in raw if f in FLAGS] resolved: set[str] = set(known) implied_by: dict[str, list[str]] = {} # Transitive implication closure. changed = True while changed: changed = False for flag in list(resolved): for implied in _FLAG_IMPLIES.get(flag, set()): if implied not in resolved: resolved.add(implied) implied_by.setdefault(implied, []).append(flag) changed = True modules: set[str] = set() globals_: dict[str, str] = {} for flag in resolved: modules.update(_FLAG_MODULES.get(flag, set())) globals_.update(_FLAG_GLOBALS.get(flag, {})) # Resolve transitive module dependencies from the catalog. from opensips_mcp.cfg.module_catalog import get_module deps_added = True while deps_added: deps_added = False for m in list(modules): info = get_module(m) if not info: continue for dep in info.depends_on: if dep not in modules: modules.add(dep) deps_added = True # Stable ordering: core first, then dependencies, then the rest. ordered = self._topologically_order(modules) return FlagComposition( flags=sorted(resolved), modules=ordered, globals=globals_, unknown_flags=sorted(unknown), implied_by=implied_by, ) - Supporting data structures (FLAGS vocabulary, _FLAG_MODULES, _FLAG_GLOBALS, _FLAG_IMPLIES) used by FlagComposer.resolve() to map flags to modules/globals/implications.
FLAGS: dict[str, str] = { # DB "WITH_MYSQL": "MySQL/MariaDB database backend (db_mysql).", "WITH_POSTGRES": "PostgreSQL database backend (db_postgres).", "WITH_SQLITE": "SQLite database backend (db_sqlite).", # Auth "WITH_AUTH": "SIP digest authentication against DB subscribers.", "WITH_AUTH_JWT": "JWT-based authentication (bearer token).", "WITH_USRLOCDB": "Persist usrloc to DB (write-back) instead of memory only.", # NAT / media "WITH_NAT": "Basic NAT detection + Contact/Record-Route fixup.", "WITH_NAT_TRAVERSAL": "Keepalive pings + media relay for symmetric-NAT clients.", "WITH_RTPENGINE": "Media relay via RTPEngine control protocol.", "WITH_RTPPROXY": "Media relay via legacy RTPProxy.", # Transport "WITH_TLS": "TLS transport (requires tls_mgm + cert/key).", "WITH_WEBSOCKET": "WS + WSS transports for SIP over WebSocket.", "WITH_WEBRTC": "Enables WS/WSS + TLS + rtpengine DTLS/SRTP for browser clients.", # Routing "WITH_DISPATCHER": "Dispatcher module with OPTIONS probing.", "WITH_DROUTING": "Dynamic routing / LCR tables.", "WITH_LOAD_BALANCER": "Weighted load-balancer with resource-aware selection.", "WITH_CARRIERROUTE": "Carrier routing tables (legacy LCR).", # Call-control "WITH_DIALOG": "Dialog tracking (INVITE state).", "WITH_ACCDB": "Accounting to database (start-of-call / missed).", "WITH_CDRDB": "CDR generation at call end (requires dialog).", "WITH_FRAUD": "Fraud detection module (per-user CPS / destination anomalies).", # Security "WITH_ANTIFLOOD": "pike + ratelimit anti-flood stack.", "WITH_PIKE": "pike per-IP packet rate limiter only.", "WITH_RATELIMIT": "global / per-pipe rate limiter only.", "WITH_TOPOLOGY_HIDING": "Strip Via/Record-Route topology on egress.", "WITH_PERMISSIONS": "IP/URI allow-deny rules.", # Presence "WITH_PRESENCE": "Presence framework (PUBLISH/SUBSCRIBE handling).", "WITH_PUBLISH": "PUA helper for outgoing PUBLISH.", "WITH_DIALOGINFO": "dialog-info event (BLF).", # Cluster / observability "WITH_CLUSTER": "clusterer module for active-active / replication.", "WITH_TRACER": "SIP/HEP tracing (Homer / flat file).", "WITH_HOMER": "HEP-3 trace to an external Homer collector.", "WITH_PROMETHEUS": "/metrics endpoint via httpd.", "WITH_STATUS_REPORT": "Per-module health / readiness endpoints.", # MI "WITH_MI_FIFO": "Management Interface over FIFO (local).", "WITH_MI_HTTP": "Management Interface over HTTP (JSON-RPC v2).", # Debug "WITH_DEBUG": "Verbose logging (log_level=4, debug_mode=yes).", } # Flag → required modules to load. Load-order is determined separately via # the module_catalog dependency graph. _FLAG_MODULES: dict[str, set[str]] = { "WITH_MYSQL": {"db_mysql"}, "WITH_POSTGRES": {"db_postgres"}, "WITH_SQLITE": {"db_sqlite"}, "WITH_AUTH": {"auth", "auth_db"}, "WITH_AUTH_JWT": {"auth", "auth_jwt"}, "WITH_USRLOCDB": {"usrloc"}, "WITH_NAT": {"nathelper", "sipmsgops"}, "WITH_NAT_TRAVERSAL": {"nathelper", "sipmsgops"}, "WITH_RTPENGINE": {"rtpengine"}, "WITH_RTPPROXY": {"rtpproxy"}, "WITH_TLS": {"proto_tls", "tls_mgm"}, "WITH_WEBSOCKET": {"proto_ws", "proto_wss", "tls_mgm"}, "WITH_WEBRTC": {"proto_ws", "proto_wss", "tls_mgm", "rtpengine", "nathelper"}, "WITH_DISPATCHER": {"dispatcher"}, "WITH_DROUTING": {"drouting"}, "WITH_LOAD_BALANCER": {"load_balancer", "dialog"}, "WITH_CARRIERROUTE": {"carrierroute"}, "WITH_DIALOG": {"dialog"}, "WITH_ACCDB": {"acc"}, "WITH_CDRDB": {"acc", "dialog"}, "WITH_FRAUD": {"fraud_detection"}, "WITH_ANTIFLOOD": {"pike", "ratelimit"}, "WITH_PIKE": {"pike"}, "WITH_RATELIMIT": {"ratelimit"}, "WITH_TOPOLOGY_HIDING": {"topology_hiding"}, "WITH_PERMISSIONS": {"permissions"}, "WITH_PRESENCE": {"presence", "presence_xml"}, "WITH_PUBLISH": {"pua"}, "WITH_DIALOGINFO": {"presence_dialoginfo", "pua_dialoginfo"}, "WITH_CLUSTER": {"clusterer", "proto_bin"}, "WITH_TRACER": {"tracer"}, "WITH_HOMER": {"tracer", "proto_hep"}, "WITH_PROMETHEUS": {"prometheus", "httpd"}, "WITH_STATUS_REPORT": {"status_report"}, "WITH_MI_FIFO": {"mi_fifo"}, "WITH_MI_HTTP": {"mi_http", "httpd"}, "WITH_DEBUG": set(), # flips globals only } # Flags that turn on specific global-parameter defaults. _FLAG_GLOBALS: dict[str, dict[str, str]] = { "WITH_DEBUG": {"log_level": "4", "debug_mode": "yes"}, } # Flags that imply other flags. _FLAG_IMPLIES: dict[str, set[str]] = { "WITH_WEBRTC": {"WITH_TLS", "WITH_WEBSOCKET"}, "WITH_CDRDB": {"WITH_DIALOG", "WITH_ACCDB"}, "WITH_ANTIFLOOD": {"WITH_PIKE", "WITH_RATELIMIT"}, "WITH_NAT_TRAVERSAL": {"WITH_NAT"}, } @dataclass class FlagComposition: flags: list[str] = field(default_factory=list) modules: list[str] = field(default_factory=list) globals: dict[str, str] = field(default_factory=dict) unknown_flags: list[str] = field(default_factory=list) implied_by: dict[str, list[str]] = field(default_factory=dict)