Skip to content

Sandbox API

dcc_mcp_core (sandbox module)

Script execution sandbox with API allowlist, path controls, 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 module provides:

  • API allowlist — restrict which DCC actions an Agent may invoke
  • Path allowlist — restrict filesystem access to safe directories
  • Audit log — structured record of every action invocation (AuditEntry / AuditLog)
  • Input validation — field-level rules with injection guards (InputValidator)
  • Read-only mode — Agent can query but not mutate the scene

SandboxPolicy

Security policy configuration for a sandbox session.

Constructor

python
from dcc_mcp_core import SandboxPolicy

policy = SandboxPolicy()

Methods

MethodReturnsDescription
allow_actions(actions)NoneRestrict execution to only these actions (replaces previous allowlist)
deny_actions(actions)NoneDeny these actions even if on the allowlist
allow_paths(paths)NoneAllow filesystem access inside these directory paths
set_timeout_ms(ms)NoneSet execution timeout in milliseconds
set_max_actions(count)NoneSet max number of actions allowed per session
set_read_only(read_only)NoneEnable/disable read-only mode

Properties

PropertyTypeDescription
is_read_onlyboolWhether policy is in read-only mode

Example

python
policy = SandboxPolicy()
policy.allow_actions(["get_scene_info", "list_objects", "get_object_info"])
policy.deny_actions(["delete_scene", "delete_object"])
policy.allow_paths(["/studio/assets", "/tmp/renders"])
policy.set_timeout_ms(5000)
policy.set_max_actions(100)
policy.set_read_only(False)

print(policy.is_read_only)  # False

Use allowlists, not denylists

Start with no actions allowed, then explicitly permit safe ones. This is more secure than listing everything you want to block.

SandboxContext

Main sandbox execution context. Bundles a SandboxPolicy with an AuditLog and an action counter.

Constructor

python
from dcc_mcp_core import SandboxPolicy, SandboxContext

policy = SandboxPolicy()
policy.allow_actions(["get_scene_info", "list_objects"])

ctx = SandboxContext(policy)

Methods

MethodReturnsDescription
set_actor(actor)NoneSet caller identity for audit entries
execute_json(action, params_json)strExecute action with JSON params, returns JSON result
is_allowed(action)boolCheck if action is permitted by current policy
is_path_allowed(path)boolCheck if path is within an allowed directory

Properties

PropertyTypeDescription
action_countintNumber of actions successfully executed
audit_logAuditLogThe audit log for this context

Execution

python
ctx.set_actor("claude-agent")

# Execute with JSON params — returns JSON string
result = ctx.execute_json("get_scene_info", "{}")
print(result)  # '{"name": "my_scene", "object_count": 42, ...}'

# Check permissions without executing
if ctx.is_allowed("delete_object"):
    ctx.execute_json("delete_object", '{"name": "pSphere1"}')

Errors raise RuntimeError

execute_json() raises RuntimeError if the action is denied, validation fails, or a sandbox error occurs.

Audit Log Access

python
log = ctx.audit_log
print(len(log))  # number of recorded entries

for entry in log.entries():
    print(f"{entry.actor}: {entry.action}{entry.outcome} ({entry.duration_ms}ms)")

# Filtered views
denied = log.denials()
succeeded = log.successes()

# Serialize to JSON
json_str = log.to_json()

AuditEntry

A single audit record for one action invocation. All attributes are read-only properties.

Properties

PropertyTypeDescription
timestamp_msintUnix timestamp in milliseconds
actorstr | NoneCaller identity, or None
actionstrAction name
params_jsonstrParameters as JSON string
duration_msintExecution duration in milliseconds
outcomestr"success", "denied", "error", or "timeout"
outcome_detailstr | NoneDenial reason or error message, or None
python
for entry in ctx.audit_log.entries():
    print(f"[{entry.timestamp_ms}] {entry.actor}: {entry.action}")
    print(f"  params: {entry.params_json}")
    print(f"  outcome: {entry.outcome} ({entry.duration_ms}ms)")
    if entry.outcome_detail:
        print(f"  detail: {entry.outcome_detail}")

AuditLog

Read-only view of the sandbox audit log.

Methods

MethodReturnsDescription
entries()list[AuditEntry]All recorded entries
successes()list[AuditEntry]Entries with outcome "success"
denials()list[AuditEntry]Entries with outcome "denied"
entries_for_action(action)list[AuditEntry]Entries for a specific action
to_json()strAll entries serialized as a JSON array
__len__()intNumber of entries
python
log = ctx.audit_log

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

# All scene_info queries
scene_queries = log.entries_for_action("get_scene_info")

# Export for logging system
import json
entries_json = log.to_json()

InputValidator

Field-level input validator with injection guards. Use to validate parameters before passing to SandboxContext.execute_json().

Constructor

python
from dcc_mcp_core import InputValidator

validator = InputValidator()

Methods

MethodReturnsDescription
require_string(field, max_length=None, min_length=None)NoneRegister a required string field with optional length bounds
require_number(field, min_value=None, max_value=None)NoneRegister a required numeric field with optional range
forbid_substrings(field, substrings)NoneInjection guard: field must not contain any listed substring
validate(params_json)tuple[bool, str | None]Validate JSON params; returns (True, None) or (False, error)

Example

python
from dcc_mcp_core import InputValidator

validator = InputValidator()

# Field constraints
validator.require_string("name", min_length=1, max_length=64)
validator.require_number("radius", min_value=0.01, max_value=1000.0)

# Injection guards
validator.forbid_substrings("script", ["__import__", "exec(", "eval(", "os.system"])

# Valid input
ok, error = validator.validate('{"name": "sphere1", "radius": 2.0}')
print(ok, error)  # True, None

# Injection attempt blocked
ok, error = validator.validate('{"script": "__import__(os)"}')
print(ok, error)  # False, "field 'script' contains forbidden substring '__import__'"

# Runtime error on invalid JSON
try:
    validator.validate("not json")
except RuntimeError as e:
    print(f"Invalid JSON: {e}")

validate() vs ToolValidator

InputValidator is for sandbox field-level rules (length, range, injection guards). ToolValidator (from the actions module) validates against a JSON Schema. Use InputValidator inside a sandbox; use ToolValidator at the tool dispatch layer.

Best Practices

  1. Always use allowlists — Start with no actions allowed, then explicitly permit safe ones
  2. Set timeouts — Prevent runaway scripts from hanging the DCC
  3. Limit action countsset_max_actions() prevents runaway agent loops
  4. Enable audit logging — Always inspect ctx.audit_log after a session
  5. Use read-only mode — Enable when only querying data to prevent accidental mutations
  6. Add injection guards — Use InputValidator.forbid_substrings() for any script/code parameters
  7. Validate paths — Use allow_paths() + ctx.is_path_allowed() to prevent path traversal

Full Example

python
from dcc_mcp_core import SandboxPolicy, SandboxContext, InputValidator

# Build policy
policy = SandboxPolicy()
policy.allow_actions(["get_scene_info", "list_objects", "run_script"])
policy.allow_paths(["/studio/assets"])
policy.set_timeout_ms(10000)
policy.set_max_actions(50)

# Build validator for run_script
validator = InputValidator()
validator.require_string("script", max_length=10000)
validator.forbid_substrings("script", [
    "__import__", "exec(", "eval(", "os.system", "subprocess",
])

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

# Execute a safe read query
result = ctx.execute_json("get_scene_info", "{}")
print(result)

# Attempt a potentially dangerous action (blocked by policy)
try:
    ctx.execute_json("delete_scene", "{}")
except RuntimeError as e:
    print(f"Blocked: {e}")

# Review audit log
for entry in ctx.audit_log.entries():
    print(f"{entry.action}: {entry.outcome}")

Released under the MIT License.