Skip to content

Utilities API

dcc_mcp_core.utils — filesystem, constants, type wrappers, logging.

Type Wrappers (RPyC)

Type-safe wrappers for transmitting Python values over RPyC connections.

python
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

ClassPython TypeSpecial 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.

python
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_PATHS

TIP

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

python
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.

ParameterTypeDescription
app_namestrDCC 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.

python
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 result

Skill Functions

Top-level functions for scanning, loading, and resolving skill dependencies.

parse_skill_md()

python
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:

python
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.

python
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):

python
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 errors

resolve_dependencies()

Topologically sort a list of SkillMetadata objects by their depends field:

python
from dcc_mcp_core import resolve_dependencies

ordered = resolve_dependencies(skills)  # skills sorted so deps come first

Raises ValueError if a dependency is missing or a cycle is detected.

validate_dependencies()

Validate that all declared dependencies exist in the provided skill list:

python
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):

python
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:

ConstantValueDescription
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

VariableDescription
DCC_MCP_SKILL_PATHSGlobal skill search paths (; on Windows, : on Unix)
DCC_MCP_{APP}_SKILL_PATHSPer-app skill paths, e.g. DCC_MCP_MAYA_SKILL_PATHS for Maya
MCP_LOG_LEVELLog level override (DEBUG, INFO, WARN, ERROR)
DCC_MCP_LOG_DIRDirectory for rolling log files (falls back to platform log dir)
DCC_MCP_LOG_FILE_PREFIXLog file stem (default: dcc-mcp)
DCC_MCP_LOG_MAX_SIZEMax bytes per file before size-triggered rotation (default: 10 MiB)
DCC_MCP_LOG_MAX_FILESRetention cap for rolled files (default: 7)
DCC_MCP_LOG_ROTATIONRotation policy: size, daily, or both (default: both)

Search Path Priority

When create_skill_server("maya") is called, skill directories are resolved in this order:

  1. extra_paths argument (highest priority)
  2. Per-app env var: DCC_MCP_MAYA_SKILL_PATHS
  3. Global env var: DCC_MCP_SKILL_PATHS
  4. Platform data dir: ~/.local/share/dcc-mcp/skills/maya/

File Logging

Rolling file logging for durable, multi-process debugging. Console output (stderr) is unaffected.

python
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

ParameterTypeDefaultDescription
directorystr | Noneplatform log dirTarget directory for log files
file_name_prefixstr"dcc-mcp"File-name stem (<prefix>.<YYYYMMDD>.log)
max_size_bytesint10485760 (10 MiB)Max bytes per file before size rotation
max_filesint7Retention cap — keep this many rolled files (current excluded)
rotationstr"both""size", "daily", or "both"
include_consoleboolTrueReserved for future use; console always stays active today

Methods

MethodReturnsDescription
FileLoggingConfig.from_env()FileLoggingConfigBuild a config pre-populated from DCC_MCP_LOG_* env vars

Functions

FunctionReturnsDescription
init_file_logging(config=None)strInstall (or swap) the rolling-file layer. Returns resolved log directory.
shutdown_file_logging()NoneUninstall the file-logging layer.
flush_logs()NoneFlush 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: DccServerBase automatically 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.

Released under the MIT License.