Actions API
dcc_mcp_core — ActionRegistry, EventBus, ActionDispatcher, ActionValidator, SemVer, VersionConstraint, VersionedRegistry.
ActionRegistry
Thread-safe action registry backed by DashMap. Each registry instance is independent.
Constructor
from dcc_mcp_core import ActionRegistry
registry = ActionRegistry()Methods
| Method | Returns | Description |
|---|---|---|
register(name, description="", category="", tags=[], dcc="python", version="1.0.0", input_schema=None, output_schema=None, source_file=None) | — | Register an action |
get_action(name, dcc_name=None) | dict? | Get action metadata as dict |
list_actions(dcc_name=None) | List[dict] | List all actions as metadata dicts |
list_actions_for_dcc(dcc_name) | List[str] | List action names for a DCC |
get_all_dccs() | List[str] | List all registered DCC names |
search_actions(category=None, tags=[], dcc_name=None) | List[dict] | Search with AND-ed filters |
get_categories(dcc_name=None) | List[str] | Sorted unique categories |
get_tags(dcc_name=None) | List[str] | Sorted unique tags |
count_actions(category=None, tags=[], dcc_name=None) | int | Count matching actions |
reset() | — | Clear all registered actions |
Dunder Methods
| Method | Description |
|---|---|
__len__ | Number of registered actions |
__contains__(name) | Check if action name is registered (scoped to "python" dcc) |
__repr__ | ActionRegistry(actions=N) |
Action Metadata Dict
When retrieved via get_action(), list_actions(), or search_actions(), each action is a dict:
{
"name": "create_sphere",
"description": "Creates a sphere",
"category": "geometry",
"tags": ["geometry"],
"dcc": "maya",
"version": "1.0.0",
"input_schema": {"type": "object", "properties": {}},
"output_schema": {"type": "object", "properties": {}},
"source_file": "/path/to/source.py" # or null
}Example
reg = ActionRegistry()
reg.register(
"create_sphere",
description="Create a polygon sphere",
category="geometry",
tags=["geo", "create"],
dcc="maya",
input_schema='{"type": "object", "properties": {"radius": {"type": "number"}}}',
)
# Get it back
meta = reg.get_action("create_sphere", dcc_name="maya")
print(meta["version"]) # "1.0.0"
# Search
results = reg.search_actions(category="geometry", tags=["create"])ActionValidator
Validates JSON-encoded action parameters against a JSON Schema. Created from a schema string or from an ActionRegistry action.
Static Factory Methods
from dcc_mcp_core import ActionValidator
# From a JSON Schema string
validator = ActionValidator.from_schema_json(
'{"type": "object", "required": ["radius"], '
'"properties": {"radius": {"type": "number", "minimum": 0.0}}}'
)
# From an ActionRegistry action
from dcc_mcp_core import ActionRegistry
reg = ActionRegistry()
reg.register("create_sphere", input_schema='{"type": "object", "properties": {"radius": {"type": "number"}}}')
validator = ActionValidator.from_action_registry(reg, "create_sphere")Validating Input
# Valid input — returns (True, [])
ok, errors = validator.validate('{"radius": 1.0}')
print(ok) # True
print(errors) # []
# Invalid input — returns (False, [error1, ...])
ok, errors = validator.validate('{"radius": -1.0}')
print(ok) # False
print(errors) # ["radius must be >= 0"]
# Missing required field
ok, errors = validator.validate("{}")
print(ok) # False
print(errors) # ["radius is required"]Error Handling
try:
validator.validate('not json at all')
except ValueError as e:
print(f"Invalid JSON: {e}")TIP
validate() accepts a JSON string ('{"radius": 1.0}'), not a Python dict. This matches the wire-format used by the MCP protocol.
ActionDispatcher
Routes action calls to registered Python callables with automatic validation.
Constructor
from dcc_mcp_core import ActionRegistry, ActionDispatcher
reg = ActionRegistry()
dispatcher = ActionDispatcher(reg)Registering Handlers
def handle_create_sphere(params):
# params is a dict deserialised from the JSON input
return {"created": True, "radius": params.get("radius", 1.0)}
dispatcher.register_handler("create_sphere", handle_create_sphere)Dispatching Actions
import json
result = dispatcher.dispatch("create_sphere", json.dumps({"radius": 2.0}))
# result = {"action": "create_sphere", "output": {"created": True, "radius": 2.0}, "validation_skipped": False}
print(result["output"]["created"]) # TrueHandler Function Signature
def handler(params: dict) -> Any:
"""Receive validated JSON params as a Python dict."""
passOther Methods
| Method | Returns | Description |
|---|---|---|
register_handler(action_name, handler) | — | Register a Python callable |
remove_handler(action_name) | bool | Remove handler, return True if existed |
has_handler(action_name) | bool | Check if handler is registered |
handler_count() | int | Number of registered handlers |
handler_names() | List[str] | Alphabetically sorted handler names |
skip_empty_schema_validation | bool | Property: skip validation when schema is {} |
SemVer
Semantic versioning with major.minor.patch components. Pre-release labels (-alpha, -beta) are stripped and ignored for all comparisons.
Constructor
from dcc_mcp_core import SemVer
v = SemVer(1, 2, 3)
print(str(v)) # "1.2.3"Parsing
from dcc_mcp_core import SemVer
v = SemVer.parse("1.2.3")
print(v.major) # 1
print(v.minor) # 2
print(v.patch) # 3
# Leading "v" is accepted
v2 = SemVer.parse("v2.0")
print(v2.major) # 2Version Comparison
v1 = SemVer.parse("1.2.3")
v2 = SemVer.parse("1.2.4")
v3 = SemVer.parse("2.0.0")
print(v1 < v2) # True
print(v2 > v1) # True
print(v3 > v1) # True
print(v1 == SemVer.parse("1.2.3")) # TrueVersion Sorting
versions = [SemVer.parse("2.0.0"), SemVer.parse("1.0.0"), SemVer.parse("1.2.3")]
sorted_versions = sorted(versions)
print([str(v) for v in sorted_versions]) # ["1.0.0", "1.2.3", "2.0.0"]Error Handling
from dcc_mcp_core import VersionParseError
try:
v = SemVer.parse("invalid")
except VersionParseError as e:
print(f"Invalid version: {e}")TIP
SemVer only has three numeric components (major, minor, patch). Pre-release labels and build metadata are stripped and ignored.
VersionConstraint
Version requirement specification for matching against registered action versions.
Creating Constraints
from dcc_mcp_core import VersionConstraint
# Various constraint types
constraint1 = VersionConstraint.parse(">=1.0.0,<2.0.0")
constraint2 = VersionConstraint.parse("^1.2.3") # Compatible with 1.x.x
constraint3 = VersionConstraint.parse("~1.2.3") # Patch compatible (1.2.x)
constraint4 = VersionConstraint.parse("1.2.3") # Exact version
constraint5 = VersionConstraint.parse("*") # Any versionChecking Constraints
from dcc_mcp_core import SemVer, VersionConstraint
v = SemVer.parse("1.5.0")
constraint = VersionConstraint.parse(">=1.0.0,<2.0.0")
print(constraint.matches(v)) # TrueSupported Constraint Formats
| Format | Example | Description |
|---|---|---|
| Exact | 1.2.3 | Must match exactly |
| Greater than | >1.2.3 | Must be strictly greater |
| Range | >=1.0.0,<2.0.0 | Within range |
| Caret | ^1.2.3 | Same major (1.x.x) |
| Tilde | ~1.2.3 | Same major.minor (1.2.x) |
| Wildcard | * | Any version |
VersionedRegistry
Multi-version action registry. Allows multiple versions of the same (action_name, dcc_name) pair to coexist. Provides resolution of the best-matching version given a constraint.
Constructor
from dcc_mcp_core import VersionedRegistry
registry = VersionedRegistry()Registering Versions
registry.register_versioned(
"create_sphere",
dcc="maya",
version="1.0.0",
description="Create a sphere",
category="geometry",
tags=["geo", "create"],
)
registry.register_versioned(
"create_sphere",
dcc="maya",
version="2.0.0",
description="Create a sphere with segments",
category="geometry",
tags=["geo", "create"],
)
registry.register_versioned(
"create_sphere",
dcc="blender",
version="1.0.0",
description="Blender sphere creation",
)Resolving Versions
# Get all registered versions for (name, dcc)
versions = registry.versions("create_sphere", "maya")
print(versions) # ["1.0.0", "2.0.0"]
# Get the latest version string
latest = registry.latest_version("create_sphere", "maya")
print(latest) # "2.0.0"
# Resolve best match for a constraint — returns metadata dict or None
result = registry.resolve("create_sphere", "maya", "^1.0.0")
if result:
print(result["version"]) # "2.0.0"
print(result["category"]) # "geometry"
# Resolve all versions matching a constraint
all_matches = registry.resolve_all("create_sphere", "maya", ">=1.0.0,<3.0.0")
for m in all_matches:
print(m["version"]) # ["1.0.0", "2.0.0"]Registry Introspection
# All registered (name, dcc) keys
keys = registry.keys()
print(keys) # [("create_sphere", "maya"), ("create_sphere", "blender")]
# Total number of versioned entries
print(registry.total_entries()) # 3
# Remove versions by constraint
removed = registry.remove("create_sphere", "maya", "^1.0.0")
print(removed) # 2 (removed 1.0.0 and 2.0.0)Methods
| Method | Returns | Description |
|---|---|---|
register_versioned(name, dcc, version, description, category, tags) | — | Register an action version |
versions(name, dcc) | List[str] | All versions sorted ascending |
latest_version(name, dcc) | str? | Highest version string or None |
resolve(name, dcc, constraint) | dict? | Best match metadata dict or None |
resolve_all(name, dcc, constraint) | List[dict] | All matching metadata dicts |
keys() | List[tuple] | All (name, dcc) pairs |
total_entries() | int | Total entry count across all |
remove(name, dcc, constraint) | int | Remove count (by constraint) |
TIP
resolve() and resolve_all() use VersionConstraint.parse() internally — pass a constraint string like "^1.0.0" or ">=1.0.0,<2.0.0".