# dcc-mcp-core — Full API Reference

> This document provides comprehensive API documentation for AI agents and downstream projects
> that depend on dcc-mcp-core. For a concise overview, see [llms.txt](llms.txt).

---

## Architecture

dcc-mcp-core is a **Rust workspace** with Python bindings via PyO3. All logic lives in Rust
sub-crates; the root crate re-exports everything into a single `dcc_mcp_core._core` Python
extension module. The Python package `dcc_mcp_core` re-exports all public APIs from `_core`.

```
crates/
├── dcc-mcp-models/       # ActionResultModel, SkillMetadata
├── dcc-mcp-actions/      # ActionRegistry, EventBus
├── dcc-mcp-skills/       # SkillScanner, parse_skill_md, loader
├── dcc-mcp-protocols/    # MCP type definitions
└── dcc-mcp-utils/        # Filesystem, constants, type wrappers, logging, JSON conversion
```

---

## Models (`dcc_mcp_core`)

### ActionResultModel

Structured result for all action executions. Immutable snapshot — use `with_error()`/`with_context()` to derive.

**Constructor:**
```python
ActionResultModel(success=True, message="", prompt=None, error=None, context=None)
```

**Properties (read-only except `message`):**
- `success: bool` — Whether the execution was successful
- `message: str` — Human-readable result description (settable)
- `prompt: Optional[str]` — Suggestion for AI about next steps
- `error: Optional[str]` — Error message when success is False
- `context: dict` — Additional context data (returns a new dict each access)

**Methods:**
- `with_error(error: str) -> ActionResultModel` — Copy with error info (sets success=False)
- `with_context(**kwargs) -> ActionResultModel` — Copy with updated context
- `to_dict() -> dict` — Full dict representation

**Factory functions (preferred for creation):**
```python
from dcc_mcp_core import success_result, error_result, from_exception, validate_action_result

result = success_result("Created 5 spheres", prompt="Use modify_spheres next", count=5)
error = error_result("Failed", "File not found", prompt="Check path", possible_solutions=["fix A"])
exc_result = from_exception("ValueError: bad", message="Import failed", include_traceback=True)
validated = validate_action_result({"success": True, "message": "ok"})  # dict → ActionResultModel
validated = validate_action_result("hello")  # wraps non-dict as success with context["value"]
```

### SkillMetadata

Metadata parsed from SKILL.md frontmatter. All fields are get/set.

**Fields:**
- `name: str` — Unique identifier
- `description: str` — Human-readable description (default: "")
- `tools: List[str]` — Required tool permissions (default: [])
- `dcc: str` — Target DCC application (default: "python")
- `tags: List[str]` — Classification tags (default: [])
- `scripts: List[str]` — Discovered script file paths (populated by loader)
- `skill_path: str` — Absolute path to skill directory (populated by loader)
- `version: str` — Skill version (default: "1.0.0")
- `depends: List[str]` — Names of other skills this skill requires (default: [])
- `metadata_files: List[str]` — Files discovered under metadata/ directory (default: [])

---

## Actions (`dcc_mcp_core`)

### ActionRegistry

Thread-safe action registry using DashMap. Each instance is independent (no singleton).

```python
reg = dcc_mcp_core.ActionRegistry()

# Register
reg.register(
    name="create_sphere",
    description="Create a sphere",
    category="geometry",
    tags=["geo", "create"],
    dcc="maya",
    version="1.0.0",
    input_schema='{"type": "object", "properties": {"radius": {"type": "number"}}}',
    output_schema='{"type": "object"}',
    source_file="/path/to/action.py",
)

# Query
meta = reg.get_action("create_sphere")                    # -> dict or None
meta = reg.get_action("create_sphere", dcc_name="maya")   # DCC-scoped lookup
names = reg.list_actions_for_dcc("maya")                   # -> List[str]
all_actions = reg.list_actions()                           # -> List[dict]
all_actions = reg.list_actions(dcc_name="maya")            # -> List[dict]
dccs = reg.get_all_dccs()                                  # -> List[str]

# Manage
reg.reset()    # clear all
len(reg)       # count
repr(reg)      # "ActionRegistry(actions=N)"
```

### EventBus

Thread-safe publish/subscribe event system.

```python
bus = dcc_mcp_core.EventBus()

# Subscribe (returns subscriber ID for unsubscription)
sub_id = bus.subscribe("my_event", lambda **kw: print(kw))

# Publish with keyword arguments
bus.publish("my_event", x=1, y="hello")

# Unsubscribe
removed = bus.unsubscribe("my_event", sub_id)  # -> bool

# Info
repr(bus)  # "EventBus(subscriptions=N)"
```

---

## Skills System (`dcc_mcp_core`)

### SkillScanner

Discovers SKILL.md files in directories with mtime-based caching.

```python
scanner = dcc_mcp_core.SkillScanner()
dirs = scanner.scan(
    extra_paths=["/my/skills"],
    dcc_name="maya",       # also checks platform-specific skills dir
    force_refresh=False,   # True to ignore cache
)
# dirs: List[str] — absolute paths to skill directories

scanner.discovered_skills  # -> List[str] (last scan result)
scanner.clear_cache()      # reset mtime cache
```

### parse_skill_md

```python
meta = dcc_mcp_core.parse_skill_md("/path/to/skill-dir")  # -> SkillMetadata or None
```

Parses YAML frontmatter from SKILL.md, enumerates scripts/ directory,
discovers metadata/ directory files, and merges depends from metadata/depends.md.

### scan_skill_paths

```python
dirs = dcc_mcp_core.scan_skill_paths(extra_paths=["/my/skills"], dcc_name="maya")
```

Convenience: creates a fresh SkillScanner, scans, and returns results.

### SKILL.md Format
```yaml
---
name: maya-geometry
description: "Maya geometry tools"
tools: ["Bash", "Read"]
tags: ["maya", "geometry"]
dcc: maya
version: "1.0.0"
depends: ["other-skill"]
---
# Description body (markdown)
```

### Supported Script Extensions
`.py`, `.mel`, `.ms`, `.bat`, `.cmd`, `.sh`, `.bash`, `.ps1`, `.jsx`, `.js`

### Environment Variable
```bash
export DCC_MCP_SKILL_PATHS="/path/to/skills1:/path/to/skills2"
```

---

## MCP Protocol Types (`dcc_mcp_core`)

### ToolDefinition

```python
td = dcc_mcp_core.ToolDefinition(
    name="create_sphere",
    description="Creates a sphere",
    input_schema='{"type": "object", "properties": {"radius": {"type": "number"}}}',
    output_schema=None,          # Optional
    annotations=None,            # Optional ToolAnnotations
)
# All fields are get/set
```

### ToolAnnotations

```python
ann = dcc_mcp_core.ToolAnnotations(
    title="Create Sphere",
    read_only_hint=False,
    destructive_hint=False,
    idempotent_hint=True,
    open_world_hint=False,
)
# All fields are Optional, get/set
```

### ResourceDefinition

```python
rd = dcc_mcp_core.ResourceDefinition(
    uri="file:///test.txt",
    name="test",
    description="A test resource",
    mime_type="text/plain",   # default
)
```

### ResourceTemplateDefinition

```python
rtd = dcc_mcp_core.ResourceTemplateDefinition(
    uri_template="file:///{path}",
    name="template",
    description="A template",
    mime_type="text/plain",   # default
)
```

### PromptArgument

```python
pa = dcc_mcp_core.PromptArgument(
    name="object_name",
    description="Name of the object",
    required=True,    # default: False
)
```

### PromptDefinition

```python
pd = dcc_mcp_core.PromptDefinition(
    name="review_model",
    description="Review a 3D model",
    arguments=[pa],   # Optional List[PromptArgument]
)
```

---

## Utilities (`dcc_mcp_core`)

### Filesystem Functions

```python
dcc_mcp_core.get_config_dir()             # -> str (platform-specific config dir)
dcc_mcp_core.get_data_dir()               # -> str
dcc_mcp_core.get_log_dir()                # -> str (includes /log subdirectory)
dcc_mcp_core.get_platform_dir("config")   # -> str (accepts: config, data, cache, log, state, documents)
dcc_mcp_core.get_actions_dir("maya")      # -> str (.../data/actions/maya/)
dcc_mcp_core.get_skills_dir("maya")       # -> str (.../data/skills/maya/)
dcc_mcp_core.get_skills_dir()             # -> str (.../data/skills/)
dcc_mcp_core.get_skill_paths_from_env()   # -> List[str] (from DCC_MCP_SKILL_PATHS)
```

### Type Wrappers (RPyC Safety)

```python
# Wrap native values for safe RPyC transport
w = dcc_mcp_core.wrap_value(True)       # -> BooleanWrapper(True)
w = dcc_mcp_core.wrap_value(42)         # -> IntWrapper(42)
w = dcc_mcp_core.wrap_value(3.14)       # -> FloatWrapper(3.14)
w = dcc_mcp_core.wrap_value("hello")    # -> StringWrapper("hello")
w = dcc_mcp_core.wrap_value([1, 2])     # -> [1, 2] (unsupported types pass through)

# Unwrap back to native
v = dcc_mcp_core.unwrap_value(w)        # -> True / 42 / 3.14 / "hello"

# Unwrap all values in a dict
result = dcc_mcp_core.unwrap_parameters({"key": BooleanWrapper(True)})
# -> {"key": True}
```

**Wrapper classes:** `BooleanWrapper`, `IntWrapper`, `FloatWrapper`, `StringWrapper`
- Each has `.value` property, `__repr__`, `__eq__`
- `BooleanWrapper` supports `__bool__`
- `IntWrapper` supports `__int__`, `__index__`
- `FloatWrapper` supports `__float__`
- `StringWrapper` supports `__str__`

---

## Constants (`dcc_mcp_core`)

| Constant | Value |
|----------|-------|
| `APP_NAME` | `"dcc-mcp"` |
| `APP_AUTHOR` | `"dcc-mcp"` |
| `DEFAULT_DCC` | `"python"` |
| `DEFAULT_LOG_LEVEL` | `"DEBUG"` |
| `SKILL_METADATA_FILE` | `"SKILL.md"` |
| `SKILL_SCRIPTS_DIR` | `"scripts"` |
| `ENV_SKILL_PATHS` | `"DCC_MCP_SKILL_PATHS"` |
| `ENV_LOG_LEVEL` | `"MCP_LOG_LEVEL"` |

---

## Environment Variables

| Variable | Description |
|----------|-------------|
| `DCC_MCP_SKILL_PATHS` | Skill search paths (platform path separator) |
| `MCP_LOG_LEVEL` | Override log level (default: DEBUG) |

---

## Development

- Tool manager: [vx](https://github.com/loonghao/vx)
- Build: `maturin develop --features python-bindings,ext-module`
- Rust tests: `cargo test --workspace` (or `vx just test-rust`)
- Python tests: `vx just test` (requires build first)
- Lint: `vx just lint` (clippy + fmt-check + ruff + isort)
- Pre-flight: `vx just preflight` (check + clippy + fmt-check + test-rust)
- Release: [Release Please](https://github.com/googleapis/release-please) with Conventional Commits
