# dcc-mcp-core

> Foundational library for the DCC Model Context Protocol (MCP) ecosystem. Rust-powered core (via PyO3) providing action registry, structured results, event bus, skills/script registration, and MCP protocol type definitions for Digital Content Creation applications (Maya, Blender, Houdini, etc.).

## Quick Start

```python
import dcc_mcp_core

# Action registry
reg = dcc_mcp_core.ActionRegistry()
reg.register(name="create_sphere", description="Create a sphere", dcc="maya")
meta = reg.get_action("create_sphere")

# Structured results
result = dcc_mcp_core.success_result("Created sphere", count=1)
error = dcc_mcp_core.error_result("Failed", "File not found")

# Event bus
bus = dcc_mcp_core.EventBus()
bus.subscribe("evt", lambda **kw: print(kw))
bus.publish("evt", x=1)

# Skill scanning
scanner = dcc_mcp_core.SkillScanner()
dirs = scanner.scan(extra_paths=["/path/to/skills"], dcc_name="maya")
meta = dcc_mcp_core.parse_skill_md(dirs[0])
```

## Installation

- PyPI: `pip install dcc-mcp-core`
- Build from source: `maturin develop --features python-bindings,ext-module`
- Python: >=3.9 (CI tests 3.9–3.13)
- Build: maturin (Rust + PyO3)
- License: MIT

## Architecture

Rust workspace with PyO3 bindings. All logic in Rust sub-crates; Python gets a single `dcc_mcp_core._core` extension module.

```
crates/
├── dcc-mcp-models/      # ActionResultModel, SkillMetadata
├── dcc-mcp-actions/     # ActionRegistry, EventBus
├── dcc-mcp-skills/      # SkillScanner, parse_skill_md
├── dcc-mcp-protocols/   # MCP type definitions (Tool, Resource, Prompt)
└── dcc-mcp-utils/       # Filesystem, constants, type wrappers, logging
```

## Core Concepts

- **ActionRegistry**: Thread-safe registry for action metadata (name, description, dcc, tags, schemas)
- **ActionResultModel**: Structured result (success, message, prompt, error, context dict)
- **EventBus**: Thread-safe publish/subscribe system with per-event subscriber lists
- **SkillScanner**: Discovers SKILL.md files in directories with mtime-based caching
- **SkillMetadata**: Parsed from SKILL.md YAML frontmatter (name, dcc, tags, scripts, depends)
- **MCP Protocol Types**: ToolDefinition, ResourceDefinition, PromptDefinition, ToolAnnotations

## Key APIs

### Top-level exports (`dcc_mcp_core`)
- `ActionRegistry` — Thread-safe registry; `.register(name, ...)`, `.get_action(name)`, `.list_actions()`
- `ActionResultModel` — Structured result: success, message, prompt, error, context
- `success_result(message, prompt=None, **context) -> ActionResultModel`
- `error_result(message, error, prompt=None, possible_solutions=None, **context) -> ActionResultModel`
- `from_exception(error_message, message=None, ...) -> ActionResultModel`
- `validate_action_result(result) -> ActionResultModel`
- `EventBus` — `.subscribe(event, callback)`, `.unsubscribe(event, id)`, `.publish(event, **kwargs)`
- `SkillScanner` — `.scan(extra_paths=None, dcc_name=None, force_refresh=False) -> List[str]`
- `parse_skill_md(skill_dir) -> Optional[SkillMetadata]`
- `scan_skill_paths(extra_paths=None, dcc_name=None) -> List[str]`

### MCP Protocol Types (`dcc_mcp_core`)
- `ToolDefinition(name, description, input_schema, output_schema=None, annotations=None)`
- `ToolAnnotations(title=None, read_only_hint=None, destructive_hint=None, ...)`
- `ResourceDefinition(uri, name, description, mime_type="text/plain")`
- `ResourceTemplateDefinition(uri_template, name, description, mime_type="text/plain")`
- `PromptDefinition(name, description, arguments=None)`
- `PromptArgument(name, description, required=False)`

### Utility Functions (`dcc_mcp_core`)
- `get_config_dir()`, `get_data_dir()`, `get_log_dir()`, `get_platform_dir(dir_type)`
- `get_actions_dir(dcc_name)`, `get_skills_dir(dcc_name=None)`
- `get_skill_paths_from_env() -> List[str]`
- `wrap_value(value)` / `unwrap_value(value)` — RPyC type safety wrappers
- `unwrap_parameters(params_dict)` — Unwrap all wrapper values in a dict

### Constants (`dcc_mcp_core`)
- `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"`

## Skills System

Register scripts as MCP tools with zero Python code via SKILL.md.

### Directory Structure
```
maya-geometry/
├── SKILL.md          # YAML frontmatter + description
├── scripts/
│   ├── create_sphere.py
│   └── batch_rename.py
└── metadata/         # Optional: help.md, install.md, depends.md
```

### Supported Script Types
| Extension | Type |
|-----------|------|
| `.py` | Python |
| `.mel` | MEL (Maya) |
| `.ms` | MaxScript |
| `.bat`, `.cmd` | Batch |
| `.sh`, `.bash` | Shell |
| `.ps1` | PowerShell |
| `.js`, `.jsx` | JavaScript |

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

## Documentation

- [README (English)](https://github.com/loonghao/dcc-mcp-core/blob/main/README.md)
- [README (中文)](https://github.com/loonghao/dcc-mcp-core/blob/main/README_zh.md)
- [Contributing Guide](https://github.com/loonghao/dcc-mcp-core/blob/main/CONTRIBUTING.md)
- [Changelog](https://github.com/loonghao/dcc-mcp-core/blob/main/CHANGELOG.md)
- [Full API Reference](https://github.com/loonghao/dcc-mcp-core/blob/main/llms-full.txt)

## Development

- Tool manager: [vx](https://github.com/loonghao/vx)
- Build: maturin (Rust + PyO3), `vx just dev` or `vx just install`
- Test: `vx just test-rust` (Rust), `vx just test` (Python)
- Lint: `vx just lint` (clippy + ruff + isort), fix: `vx just lint-fix`
- Pre-flight: `vx just preflight`
- Release: [Release Please](https://github.com/googleapis/release-please) with Conventional Commits

## Related Projects

- [dcc-mcp-rpyc](https://github.com/loonghao/dcc-mcp-rpyc) — RPyC bridge for remote DCC operations
- [dcc-mcp-maya](https://github.com/loonghao/dcc-mcp-maya) — Maya MCP server implementation
