Utilities API
dcc_mcp_core.utils — filesystem, constants, type wrappers, logging.
Type Wrappers (RPyC)
Type-safe wrappers for transmitting Python values over RPyC connections.
from dcc_mcp_core import wrap_value, unwrap_value, unwrap_parameters
from dcc_mcp_core import BooleanWrapper, IntWrapper, FloatWrapper, StringWrapper
# Wrap native values
wrapped = wrap_value(True) # BooleanWrapper(True)
wrapped = wrap_value(42) # IntWrapper(42)
wrapped = wrap_value(3.14) # FloatWrapper(3.14)
wrapped = wrap_value("hello") # StringWrapper("hello")
# Unwrap back to native
original = unwrap_value(wrapped) # True, 42, 3.14, or "hello"
# Unwrap all values in a dict
params = {"visible": BooleanWrapper(True), "count": IntWrapper(5)}
native = unwrap_parameters(params) # {"visible": True, "count": 5}Wrapper Classes
| Class | Python Type | Special Methods |
|---|---|---|
BooleanWrapper(value) | bool | __bool__, __eq__, __hash__ |
IntWrapper(value) | int (i64) | __int__, __index__, __eq__, __hash__ |
FloatWrapper(value) | float (f64) | __float__, __eq__ (relative tolerance), not hashable |
StringWrapper(value) | str | __str__, __eq__, __hash__ |
WARNING
FloatWrapper is intentionally not hashable (__hash__ raises TypeError) because f64 does not implement Eq/Hash (NaN ≠ NaN). Do not use it as a dict key or in sets.
Filesystem
Platform-specific directory resolution using the dirs crate.
from dcc_mcp_core import (
get_platform_dir, get_config_dir, get_data_dir, get_log_dir,
get_tools_dir, get_skills_dir, get_skill_paths_from_env,
get_app_skill_paths_from_env,
)
config = get_config_dir() # ~/.config/dcc-mcp (Linux)
data = get_data_dir() # ~/.local/share/dcc-mcp (Linux)
log = get_log_dir() # ~/.local/share/dcc-mcp/log (Linux)
tools = get_tools_dir("maya") # {data}/actions/maya/
skills = get_skills_dir("maya") # {data}/skills/maya/
skills = get_skills_dir() # {data}/skills/ (global)
# Generic platform dir (config, data, cache, log, documents)
dir = get_platform_dir("cache")
# From global environment variable
paths = get_skill_paths_from_env() # splits DCC_MCP_SKILL_PATHS
# From per-app environment variable
paths = get_app_skill_paths_from_env("maya") # reads DCC_MCP_MAYA_SKILL_PATHS
paths = get_app_skill_paths_from_env("blender") # reads DCC_MCP_BLENDER_SKILL_PATHSTIP
All get_*_dir() functions create the directory if it doesn't exist (except get_skills_dir which only returns the path).
get_app_skill_paths_from_env
def get_app_skill_paths_from_env(app_name: str) -> list[str]: ...Return skill paths from the DCC_MCP_{APP_NAME}_SKILL_PATHS environment variable.
| Parameter | Type | Description |
|---|---|---|
app_name | str | DCC application name, e.g. "maya", "blender". Case-insensitive — automatically uppercased to form the env var key. |
Returns: list[str] — directory paths from the env var, or [] if not set.
import os
os.environ["DCC_MCP_MAYA_SKILL_PATHS"] = "/studio/maya-skills;/home/user/skills"
from dcc_mcp_core import get_app_skill_paths_from_env
paths = get_app_skill_paths_from_env("maya")
# ["/studio/maya-skills", "/home/user/skills"]
# Case-insensitive: "Maya", "MAYA", "maya" all read DCC_MCP_MAYA_SKILL_PATHS
paths = get_app_skill_paths_from_env("Maya") # same resultSkill Functions
Top-level functions for scanning, loading, and resolving skill dependencies.
parse_skill_md()
from dcc_mcp_core import parse_skill_md
metadata = parse_skill_md("/path/to/skills/maya-geometry")
if metadata:
print(metadata.name, metadata.version) # "maya-geometry", "1.0.0"scan_skill_paths()
Scan configured directories for skill packages:
from dcc_mcp_core import scan_skill_paths
# Scan all configured paths (env vars + platform dirs)
paths = scan_skill_paths()
# Scope to a specific DCC
paths = scan_skill_paths(dcc_name="maya")
# Add extra directories
paths = scan_skill_paths(extra_paths=["/my/extra/skills"], dcc_name="blender")scan_and_load()
Full pipeline: scan directories, parse SKILL.md files, and topologically sort by dependencies. Raises ValueError on missing dependencies or cycles.
from dcc_mcp_core import scan_and_load
skills, skipped = scan_and_load(dcc_name="maya")
for skill in skills:
print(f"{skill.name} v{skill.version}")
print(f"Skipped {len(skipped)} directories")scan_and_load_lenient()
Like scan_and_load() but silently skips skills with missing dependencies (only fails on cycles):
from dcc_mcp_core import scan_and_load_lenient
skills, skipped = scan_and_load_lenient(dcc_name="maya")
# skills with unresolvable deps are in skipped, not raised as errorsresolve_dependencies()
Topologically sort a list of SkillMetadata objects by their depends field:
from dcc_mcp_core import resolve_dependencies
ordered = resolve_dependencies(skills) # skills sorted so deps come firstRaises ValueError if a dependency is missing or a cycle is detected.
validate_dependencies()
Validate that all declared dependencies exist in the provided skill list:
from dcc_mcp_core import validate_dependencies
errors = validate_dependencies(skills)
for err in errors:
print(f"Missing dep: {err}")Returns a list of error messages. Empty list means all deps are satisfied.
expand_transitive_dependencies()
Get all transitive dependencies of a skill (recursively):
from dcc_mcp_core import expand_transitive_dependencies
all_deps = expand_transitive_dependencies(skills, "maya-complex-tool")
print(all_deps) # ["base-utils", "maya-core", "maya-geometry"]Raises ValueError on missing deps or cycles.
Constants
Available as module-level attributes:
| Constant | Value | Description |
|---|---|---|
APP_NAME | "dcc-mcp" | App name for platform dirs |
APP_AUTHOR | "dcc-mcp" | App author |
DEFAULT_DCC | "python" | Default DCC name |
DEFAULT_LOG_LEVEL | "DEBUG" | Default log level |
ENV_LOG_LEVEL | "MCP_LOG_LEVEL" | Env var for log level |
ENV_SKILL_PATHS | "DCC_MCP_SKILL_PATHS" | Env var for global skill paths |
ENV_APP_SKILL_PATHS | "DCC_MCP_{APP}_SKILL_PATHS" | Template for per-app skill paths env var |
SKILL_METADATA_FILE | "SKILL.md" | Skill metadata filename |
SKILL_SCRIPTS_DIR | "scripts" | Scripts subdirectory |
Environment Variables
| Variable | Description |
|---|---|
DCC_MCP_SKILL_PATHS | Global skill search paths (; on Windows, : on Unix) |
DCC_MCP_{APP}_SKILL_PATHS | Per-app skill paths, e.g. DCC_MCP_MAYA_SKILL_PATHS for Maya |
MCP_LOG_LEVEL | Log level override (DEBUG, INFO, WARN, ERROR) |
DCC_MCP_LOG_DIR | Directory for rolling log files (falls back to platform log dir) |
DCC_MCP_LOG_FILE_PREFIX | Log file stem (default: dcc-mcp) |
DCC_MCP_LOG_MAX_SIZE | Max bytes per file before size-triggered rotation (default: 10 MiB) |
DCC_MCP_LOG_MAX_FILES | Retention cap for rolled files (default: 7) |
DCC_MCP_LOG_ROTATION | Rotation policy: size, daily, or both (default: both) |
Search Path Priority
When create_skill_server("maya") is called, skill directories are resolved in this order:
extra_pathsargument (highest priority)- Per-app env var:
DCC_MCP_MAYA_SKILL_PATHS - Global env var:
DCC_MCP_SKILL_PATHS - Platform data dir:
~/.local/share/dcc-mcp/skills/maya/
File Logging
Rolling file logging for durable, multi-process debugging. Console output (stderr) is unaffected.
from dcc_mcp_core import FileLoggingConfig, init_file_logging, shutdown_file_logging
# Quick start — use environment variables or defaults
init_file_logging() # reads DCC_MCP_LOG_* env vars
# Explicit configuration
init_file_logging(FileLoggingConfig(
directory="./logs",
file_name_prefix="maya-mcp",
max_size_bytes=5 * 1024 * 1024, # 5 MiB
max_files=10,
rotation="both", # size + daily
))
# Swap config at runtime (idempotent)
init_file_logging(FileLoggingConfig.from_env())
# Uninstall file logging; console remains active
shutdown_file_logging()FileLoggingConfig
| Parameter | Type | Default | Description |
|---|---|---|---|
directory | str | None | platform log dir | Target directory for log files |
file_name_prefix | str | "dcc-mcp" | File-name stem (<prefix>.<YYYYMMDD>.log) |
max_size_bytes | int | 10485760 (10 MiB) | Max bytes per file before size rotation |
max_files | int | 7 | Retention cap — keep this many rolled files (current excluded) |
rotation | str | "both" | "size", "daily", or "both" |
include_console | bool | True | Reserved for future use; console always stays active today |
Methods
| Method | Returns | Description |
|---|---|---|
FileLoggingConfig.from_env() | FileLoggingConfig | Build a config pre-populated from DCC_MCP_LOG_* env vars |
Functions
| Function | Returns | Description |
|---|---|---|
init_file_logging(config=None) | str | Install (or swap) the rolling-file layer. Returns resolved log directory. |
shutdown_file_logging() | None | Uninstall the file-logging layer. |
flush_logs() | None | Flush buffered log events to disk immediately. tracing_appender batches writes on a background thread — call this after an error, before opening the log viewer, or from a periodic timer to make entries visible before process exit. No-op when file logging is not installed. (issue #402) |
Multi-instance isolation:
DccServerBaseautomatically includes the process PID in the log file prefix —dcc-mcp-<dcc_name>.<pid>.<YYYYMMDD>.log— so multiple instances of the same DCC application on the same machine write to distinct, non-interleaved files.