How the ALLOW_MODIFY_OPERATIONS Flag Controls Resource Modification Requests in the MCP OpenStack Server

The ALLOW_MODIFY_OPERATIONS environment variable acts as a runtime safety switch that prevents accidental creation, update, or deletion of OpenStack resources by conditionally unregistering modify tools from the MCP framework when set to false (default).

In the call518/mcp-openstack-ops repository, this flag provides a critical guardrail for production environments. When disabled, write-capable tools like set_instance or set_network are never exposed to the MCP client, ensuring that read-only operations remain available while destructive actions are physically blocked. The implementation spans three coordinated mechanisms in the core server bootstrap file.

Core Protection Mechanisms

The safety system relies on three distinct components working in concert within src/mcp_openstack_ops/mcp_main.py:

_is_modify_operation_allowed() Boolean Helper

This simple utility reads the environment variable and returns True only when the value is "true" (case-insensitive). The implementation parses os.environ.get("ALLOW_MODIFY_OPERATIONS", "false"), converts it to lowercase, and compares against "true" (lines 220-226). All other values—including the default "false"—result in a False return, triggering the protective behavior.

conditional_tool Decorator

Every write tool (e.g., set_instance, set_network, set_image) uses this decorator to determine MCP registration eligibility. If _is_modify_operation_allowed() returns True, the decorator wraps the function with mcp.tool(), making it a live callable. Otherwise, it returns the original function without registration, effectively rendering the tool invisible to the MCP client (lines 55-63). This import-time decision happens during register_all_tools() in src/mcp_openstack_ops/tools/__init__.py.

_check_modify_operation_permission() Runtime Guard

As a secondary defense, each protected tool invokes this function before executing OpenStack API calls. When the flag is disabled, it returns a multi-line markdown error block explaining that modify operations are blocked and instructing the user to set ALLOW_MODIFY_OPERATIONS=true in their .env file (lines 227-253). This prevents direct Python imports from bypassing the decorator-based protection.

How the Protection Flow Works

  1. Server startup imports all tools via register_all_tools(), triggering the @conditional_tool decorator on every modify-capable function.

  2. Registration decision: If ALLOW_MODIFY_OPERATIONS is not explicitly "true", the decorator returns the raw function without mcp.tool() wrapping, excluding it from the MCP tool registry.

  3. Client invocation: Chat commands targeting unregistered tools fail immediately because the MCP framework has no knowledge of the function's existence.

  4. Direct import fallback: If a developer imports and calls a protected function directly (e.g., from mcp_openstack_ops.tools.set_instance import set_instance), the internal call to _check_modify_operation_permission() returns a permission error before any OpenStack connection occurs.

Thus, read-only tools (e.g., get_instance_details, get_quota) remain universally available, while modify tools (prefixed with set_…) become active only after explicit opt-in.

Configuration Examples

Disabling Modify Operations (Default)

When the environment variable is unset or explicitly false, the server operates in read-only mode:


# .env or shell export

export ALLOW_MODIFY_OPERATIONS=false

Attempting to invoke a protected tool returns the guard message:

from mcp_openstack_ops.tools.set_instance import set_instance

result = await set_instance(instance_names="demo-vm", action="start")
print(result)

Output:


❌ **MODIFY OPERATION BLOCKED**

This operation can modify or delete OpenStack resources and has been disabled for safety.

To enable modify operations, set the following in your .env file:

ALLOW_MODIFY_OPERATIONS=true

Enabling Modify Operations

Setting the flag to "true" registers all tools and permits OpenStack mutations:

export ALLOW_MODIFY_OPERATIONS=true

Now the same Python call executes against the OpenStack API and returns a JSON success payload processed by handle_operation_result().

Implementing Protection in New Tools

When adding a new resource-modifying tool, apply the safety pattern used in src/mcp_openstack_ops/tools/set_instance.py:


# src/mcp_openstack_ops/tools/set_custom_resource.py

from ..mcp_main import conditional_tool, _check_modify_operation_permission

@conditional_tool
async def set_custom_resource(name: str, action: str) -> str:
    # Runtime guard for direct imports

    permission_msg = _check_modify_operation_permission()
    if permission_msg:
        return permission_msg

    # Proceed with actual OpenStack API call

    return "✅ custom resource modified"

If ALLOW_MODIFY_OPERATIONS is disabled, the @conditional_tool decorator prevents MCP registration, and _check_modify_operation_permission() ensures any direct invocation returns the standard permission error.

Summary

  • The ALLOW_MODIFY_OPERATIONS environment variable defaults to "false", enforcing read-only mode unless explicitly enabled.
  • The conditional_tool decorator in mcp_main.py (lines 55-63) controls tool registration at import time, physically excluding modify tools from the MCP framework when the flag is disabled.
  • _check_modify_operation_permission() (lines 227-253) provides runtime protection against direct Python imports, returning a formatted error message instead of executing OpenStack calls.
  • Tools prefixed with set_ (such as set_instance) are protected, while get_ tools remain always available.

Frequently Asked Questions

How do I check if modify operations are enabled programmatically?

Import the boolean helper from the main module and inspect its return value:

from mcp_openstack_ops.mcp_main import _is_modify_operation_allowed

if _is_modify_operation_allowed():
    print("Modify operations enabled")

This function performs a case-insensitive check against the environment variable, returning True only for the exact string "true".

Why can I still import the function when ALLOW_MODIFY_OPERATIONS is false?

The conditional_tool decorator only controls MCP framework registration, not Python module imports. The function object remains importable, but the _check_modify_operation_permission() guard inside the function body returns an error message before any OpenStack API call executes. This dual-layer protection prevents accidental usage while maintaining code availability for testing.

Does this flag affect read-only operations like listing instances?

No. Tools such as get_instance_details, get_quota, and other read-only functions do not use the @conditional_tool decorator. They are always registered with the MCP framework regardless of the ALLOW_MODIFY_OPERATIONS value, ensuring continuous observability even in heavily restricted environments.

What happens if ALLOW_MODIFY_OPERATIONS is set to "TRUE" or "True"?

The _is_modify_operation_allowed() function converts the environment variable to lowercase before comparison, so "TRUE", "True", and "true" are all valid values that enable modify operations. Any other value—including unset variables, "yes", or "1"—results in the default protective behavior.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →