Skip to main content
Glama
OpenSIPS

OpenSIPS MCP Server

Official
by OpenSIPS

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

TableJSON Schema
NameRequiredDescriptionDefault
flagsYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

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,
            }
  • 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)
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It clearly explains the tool's behavior: it resolves flags into modules/globals and does not produce a config. However, it does not explicitly state side effects, safety, or error conditions, though the nature of the operation implies read-only analysis.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very concise, using a clear structure with a main sentence and a 'Parameters' section. Every sentence adds value without redundancy. It is front-loaded with the essential purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one parameter, output schema exists), the description covers purpose, usage, parameter semantics, and relationship to a sibling tool. It is complete enough for an agent to select and invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds meaning beyond the input schema: it specifies that flags are 'WITH_* flag names (case-insensitive)'. The schema only defines an array of strings without such context. This addition is valuable for agent understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool resolves WITH_* flags into modules and globals, specifies what it does NOT do ('Does NOT produce a config'), and distinguishes from sibling 'cfg_build_from_flags'. This provides a specific verb and resource with clear delineation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells when to use this tool ('if I enable these flags, what modules get loaded and which globals get set?') and pairs it with an alternative ('Pairs with cfg_build_from_flags for the full render'). Also notes flags are case-insensitive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/OpenSIPS/opensips-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server