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
-
Server startup imports all tools via
register_all_tools(), triggering the@conditional_tooldecorator on every modify-capable function. -
Registration decision: If
ALLOW_MODIFY_OPERATIONSis not explicitly"true", the decorator returns the raw function withoutmcp.tool()wrapping, excluding it from the MCP tool registry. -
Client invocation: Chat commands targeting unregistered tools fail immediately because the MCP framework has no knowledge of the function's existence.
-
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_OPERATIONSenvironment variable defaults to"false", enforcing read-only mode unless explicitly enabled. - The
conditional_tooldecorator inmcp_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 asset_instance) are protected, whileget_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →