Skip to content

Sandbox Guide

Script execution sandbox with API whitelist, audit logging, and input validation.

Overview

Enterprise users (game studios, VFX facilities) have strong security requirements that vanilla Python-based DCC MCP integrations cannot satisfy. The sandbox provides:

  • API whitelist / deny list — restrict which DCC actions an Agent may invoke
  • Audit log — tamper-evident, structured record of every action invocation
  • Input validation — schema-based validation of Agent-supplied parameters
  • Read-only mode — Agent can query but not mutate the scene
  • Path allowlist — restrict file-system access to project directories

Quick Start

python
from dcc_mcp_core import SandboxPolicy, SandboxContext
import json

# Create a restrictive policy
policy = SandboxPolicy()
policy.allow_actions(["get_scene_info", "list_objects"])
policy.deny_actions(["delete_all"])
policy.set_timeout_ms(5000)
policy.set_max_actions(100)
policy.set_read_only(False)

# Create sandbox context
ctx = SandboxContext(policy)
ctx.set_actor("ai-agent")

# Execute an allowed action (returns JSON string)
result_json = ctx.execute_json("get_scene_info", "{}")
print(f"Result: {result_json}")

# Try a forbidden action (raises RuntimeError)
try:
    ctx.execute_json("delete_all", "{}")
except RuntimeError as e:
    print(f"Denied: {e}")

SandboxPolicy

Security policy configuration.

Constructor

python
from dcc_mcp_core import SandboxPolicy

policy = SandboxPolicy()

Methods

MethodDescription
allow_actions(actions)Restrict execution to only the listed actions
deny_actions(actions)Deny these actions even if in the whitelist
allow_paths(paths)Allow file-system access inside these directory paths
set_timeout_ms(ms)Set the execution timeout in milliseconds
set_max_actions(count)Set the maximum number of actions allowed per session
set_read_only(read_only)Enable (True) or disable read-only mode
is_read_onlyProperty: True if the policy is in read-only mode

Example

python
policy = SandboxPolicy()
policy.allow_actions(["get_scene_info", "list_objects", "get_selection"])
policy.deny_actions(["delete_*", "format_*"])
policy.allow_paths(["/project/assets", "/project/scenes"])
policy.set_timeout_ms(10000)
policy.set_max_actions(100)
policy.set_read_only(False)

SandboxContext

Main sandbox execution context for a single session.

Constructor

python
from dcc_mcp_core import SandboxPolicy, SandboxContext

policy = SandboxPolicy()
policy.allow_actions(["echo"])
ctx = SandboxContext(policy)

Methods

MethodReturnsDescription
set_actor(actor)NoneSet the caller identity for audit entries
execute_json(action, params_json)strExecute an action; returns result as JSON string
is_allowed(action)boolCheck if action is permitted by the policy
is_path_allowed(path)boolCheck if path is within an allowed directory
action_countintNumber of actions successfully executed (property)
audit_logAuditLogThe audit log for this context (property)

execute_json

Runs the full sandbox pipeline (policy check, validation). Returns result as a JSON string:

python
ctx = SandboxContext(policy)
ctx.set_actor("my-agent")

# Execute with JSON parameters
result_json = ctx.execute_json("echo", '{"x": 1}')
# Returns: '{"success": true, ...}'

# Execute with empty params
result_json = ctx.execute_json("echo", "{}")

# Denied action raises RuntimeError
try:
    ctx.execute_json("forbidden_action", "{}")
except RuntimeError as e:
    print(f"Sandbox error: {e}")

Audit Logging

Accessing the Audit Log

python
ctx = SandboxContext(policy)
ctx.set_actor("agent-1")

# Execute some actions
ctx.execute_json("get_scene_info", "{}")
ctx.execute_json("list_objects", "{}")

# Access audit log
log = ctx.audit_log

print(f"Total entries: {len(log)}")
print(f"Successes: {len(log.successes())}")
print(f"Denials: {len(log.denials())}")

Iterating Entries

python
log = ctx.audit_log

for entry in log.entries():
    print(f"Time: {entry.timestamp_ms}")
    print(f"Actor: {entry.actor}")
    print(f"Action: {entry.action}")
    print(f"Params: {entry.params_json}")
    print(f"Duration: {entry.duration_ms}ms")
    print(f"Outcome: {entry.outcome}")  # "success", "denied", "error", "timeout"
    if entry.outcome_detail:
        print(f"Detail: {entry.outcome_detail}")

Filtering

python
# All entries for a specific action
for entry in log.entries_for_action("get_scene_info"):
    print(entry.outcome)

# Only successes
for entry in log.successes():
    print(f"{entry.action}: {entry.outcome}")

# Only denials (security-relevant)
for entry in log.denials():
    print(f"SUSPICIOUS: {entry.actor} tried {entry.action}")

# Serialize all to JSON
json_str = log.to_json()

AuditEntry

Properties of each entry:

PropertyTypeDescription
timestamp_msintUnix timestamp in milliseconds
actorstr | NoneCaller identity set via set_actor()
actionstrAction name invoked
params_jsonstrParameters as a JSON string
duration_msintExecution duration in milliseconds
outcomestr"success", "denied", "error", or "timeout"
outcome_detailstr | NoneDenial reason or error message

Input Validation

Use InputValidator to validate action parameters before execution.

Creating a Validator

python
from dcc_mcp_core import InputValidator

v = InputValidator()

# Register a required string field with max length
v.require_string("name", max_length=50)

# Register a required numeric field with range
v.require_number("count", min_value=0, max_value=1000)

# Block injection patterns
v.forbid_substrings("script", ["__import__", "exec(", "eval(", "subprocess"])

Validating Input

python
# Safe input
ok, error = v.validate('{"name": "sphere", "count": 5}')
assert ok, error  # (True, None)

# String too long
ok, error = v.validate('{"name": "x" * 100, "count": 5}')
assert not ok  # (False, "...")

# Injection attempt blocked
ok, error = v.validate('{"script": "__import__('os').system('ls')"}')
assert not ok  # (False, "...")

validate() Return Value

Returns (True, None) on success, (False, error_message) on failure.

python
v = InputValidator()
v.require_string("name")

ok, err = v.validate('{"name": "ok"}')
assert ok and err is None

ok, err = v.validate('{"name": 123}')  # wrong type
assert not ok and err is not None

Execution Modes

Interactive (Default)

python
policy = SandboxPolicy()
policy.allow_actions(["get_scene_info"])
policy.set_timeout_ms(5000)

ctx = SandboxContext(policy)
result = ctx.execute_json("get_scene_info", "{}")

Read-Only Mode

Prevents any write operations:

python
policy = SandboxPolicy()
policy.allow_actions(["get_*", "list_*", "query_*"])
policy.set_read_only(True)

ctx = SandboxContext(policy)
# All mutations will be denied

Error Handling

execute_json() raises RuntimeError on denial, validation failure, or timeout:

python
try:
    ctx.execute_json("forbidden_action", "{}")
except RuntimeError as e:
    print(f"Sandbox error: {e}")
    # e.g., "Action 'forbidden_action' is not allowed by policy"

Best Practices

1. Start Restrictive

python
# Start with minimal permissions
policy = SandboxPolicy()
policy.allow_actions([])  # Nothing allowed initially

# Add specific actions as needed
policy.allow_actions(["get_scene_info"])

2. Separate Read and Write Contexts

python
# Query-only context
read_policy = SandboxPolicy()
read_policy.allow_actions(["get_*", "list_*", "query_*"])
read_policy.set_read_only(True)
read_ctx = SandboxContext(read_policy)
read_ctx.set_actor("query-agent")

# Mutation context
write_policy = SandboxPolicy()
write_policy.allow_actions(["get_*", "list_*", "create_*", "set_*"])
write_ctx = SandboxContext(write_policy)
write_ctx.set_actor("mutation-agent")

3. Monitor and Alert

python
log = ctx.audit_log
denials = [e for e in log.denials() if "delete" in e.action]

if len(denials) > 5:
    alert_security_team(denials)

4. Set Appropriate Timeouts

python
# Short for quick queries
policy.set_timeout_ms(1000)

# Longer for export/bake operations
policy.set_timeout_ms(300000)

Integration Examples

Maya Integration

python
from dcc_mcp_core import SandboxPolicy, SandboxContext
import json

# Create Maya-specific policy
policy = SandboxPolicy()
policy.allow_actions(["get_scene_info", "list_objects", "query_attributes"])
policy.deny_actions(["delete_*", "set_*", "create_*"])
policy.set_read_only(True)

maya_sandbox = SandboxContext(policy)
maya_sandbox.set_actor("maya-agent")

# Wrap Maya commands
def safe_maya_action(action, params):
    result_json = maya_sandbox.execute_json(action, json.dumps(params))
    return json.loads(result_json)

scene_info = safe_maya_action("get_scene_info", {})

Blender Integration

python
import bpy
from dcc_mcp_core import SandboxPolicy, SandboxContext

policy = SandboxPolicy()
policy.allow_actions(["get_scene", "list_objects", "query_data"])
policy.set_read_only(True)

blender_sandbox = SandboxContext(policy)

Released under the MIT License.