Skill: loonghao/dcc-mcp-skills-creator (version 0.17.47)

--- name: dcc-mcp-skills-creator description: >- Infrastructure skill - create, validate, scaffold, and review DCC-MCP skills for the dcc-mcp-core ecosystem. Use when authoring SKILL.md, tools.yaml, scripts, groups, prompts, or skill taxonomy. Not for creating a full DCC-MCP adapter repository - use dcc-mcp-creator. license: MIT-0 compatibility: "Python 3.7+, dcc-mcp-core 0.17+" allowed-tools: Bash Read Write Edit metadata: dcc-mcp: dcc: python version: "0.17.47" # x-release-please-version layer: infrastructure search-hint: "create dcc mcp skill, validate skill, scaffold skill, SKILL.md, tools.yaml, scripts, groups, prompts, skill taxonomy" tools: tools.yaml skill-reference-docs: - "references/*.md" openclaw: homepage: https://github.com/loonghao/dcc-mcp-core/blob/main/skills/dcc-mcp-skills-creator/SKILL.md --- # DCC-MCP Skills Creator A first-class meta-skill for creating, validating, and reviewing DCC-MCP skill packages. It bundles scaffold/validation tools together with agent-facing authoring guidance for `SKILL.md`, `tools.yaml`, scripts, groups, prompts, and progressive-loading taxonomy. Use `dcc-mcp-creator` when the task is to create a full adapter repository for a host such as Nuke, Blender, 3ds Max, Unreal, ZBrush, Houdini, or Maya. Use this skill when the task is to create or improve the skill packages loaded by those adapters. ## Installation This skill ships with dcc-mcp-core. Add it to your skill path: ```bash # Linux/macOS export DCC_MCP_SKILL_PATHS="${DCC_MCP_SKILL_PATHS}:$(python -c 'import dcc_mcp_core; print(dcc_mcp_core.__file__)')/../skills" # Windows set DCC_MCP_SKILL_PATHS=%DCC_MCP_SKILL_PATHS%;C:\path\to\dcc-mcp-core\skills ``` Or reference it directly when starting your MCP server: ```python from dcc_mcp_core import create_skill_server, McpHttpConfig server = create_skill_server( "maya", McpHttpConfig(port=8765), extra_paths=["/path/to/dcc-mcp-core/skills"], ) ``` ## Quick Start ### Create a new skill ```python # Call the loaded MCP tool: # dcc_mcp_skills_creator__create_skill( # name="maya-rigging", # parent_dir="/path/to/skills/dir", # dcc="maya", # tool_name="create_locator", # affinity="main", # ) ``` ### Validate an existing skill ```python from dcc_mcp_core import validate_skill report = validate_skill("/path/to/my-skill") if report.has_errors: for issue in report.issues: print(f"[{issue.severity}] {issue.category}: {issue.message}") else: print("Skill is valid!") ``` ### Get a SKILL.md template ```python # Call the loaded MCP tool: # dcc_mcp_skills_creator__skill_template() ``` ## Skill Directory Structure ``` my-skill/ |-- SKILL.md # Required: metadata frontmatter + instructions |-- tools.yaml # Required when metadata.dcc-mcp.tools points here |-- scripts/ # Optional: tool implementation scripts | `-- create_locator.py `-- references/ # Optional: recipes, examples, and long-form docs |-- RECIPES.md `-- NOTES.md ``` ## Current Tool Contract Generated `tools.yaml` entries follow the modern contract: - Local tool names are snake_case and client-safe. Do not use dotted names. - Loaded tools are published as `<skill-name>__<tool_name>` when namespacing is needed. - `input_schema` and `output_schema` are declared explicitly. - `execution` is `sync` or `async`; use `async` for deferred/long-running work. - `affinity` is explicit. Use `main` for host API or scene mutation work and `any` for pure work. - `enforce_thread_affinity: true` is emitted so adapter dispatch stays honest. - `annotations` use MCP hints: read-only, destructive, idempotent, open-world, and deferred. ## Authoring Workflow 1. Decide whether the skill is infrastructure, domain, thin-harness, or example. 2. Give the skill a kebab-case name and each local tool a snake_case name. 3. Keep host API calls inside scripts, with lazy imports so discovery works without the host running. 4. Import dependency-light runtime helpers from `dcc_mcp_core.skills_helper` first: JSON/YAML codecs, bounded HTTP helpers, safe file/path helpers, validation, cancellation checks, and result helpers. 5. Declare `execution`, `affinity`, `timeout_hint_secs`, schemas, annotations, and failure recovery chains in `tools.yaml`. 6. Put long examples, recipes, and host-specific notes under `references/`. 7. Validate with `validate_skill_dir` or `dcc_mcp_core.validate_skill()` before loading it in an adapter. 8. If the desired behavior requires parsing core internals or adapter-private YAML at runtime, stop and request a core API instead. Read [AUTHORING_WORKFLOW.md](references/AUTHORING_WORKFLOW.md) and [DCC_TOOL_CONTRACTS.md](references/DCC_TOOL_CONTRACTS.md) before changing a production skill package. ## Validation Rules The validator checks: - **SKILL.md** exists and is readable - **YAML frontmatter** is well-formed - **Required fields**: `name`, `description` - **Name format**: kebab-case, <=64 chars, matches directory name - **Field lengths**: description <=1024, compatibility <=500 - **Tool declarations**: non-empty names, no duplicates, snake_case client-safe format - **Script files**: `source_file` references exist in `scripts/` - **Sidecar files**: `metadata.dcc-mcp.tools/groups/prompts` references exist - **Dependencies**: `metadata.dcc-mcp.depends` consistency - **Spec compliance**: non-standard top-level keys are frontmatter errors; dcc-mcp-core extensions must live under `metadata.dcc-mcp.*` and point to sibling files - **Skill helper adoption**: `validate_skill_dir` emits `skill-helper-adoption` warnings when scripts import avoidable dependencies covered by `dcc_mcp_core.skills_helper`, such as `requests`, `httpx`, PyYAML, or local JSON/HTTP/file/path helper modules

path: references/AUTHORING_WORKFLOW.md
content: # DCC-MCP Skill Authoring Workflow

Use this workflow when creating or modernizing a skill package that will be
loaded by a DCC-MCP adapter.

## 1. Pick The Right Scope

- Use `infrastructure` for reusable primitives shared across hosts.
- Use `domain` for host or workflow-specific operations, such as `nuke-comp` or `maya-geometry`.
- Use `thin-harness` for a deliberately small raw scripting fallback with recipes.
- Use `example` for authoring references that should not be loaded in production.

If the task is to create the adapter repository itself, switch to
`dcc-mcp-creator`.

## 2. Shape Discovery First

Agents find skills from `name`, `description`, and `metadata.dcc-mcp.search-hint`.
Keep those fields concrete:

- Say what the skill does.
- Say when to use it.
- Say when not to use it, and name the better skill when one exists.

The `metadata:` configuration block belongs in `SKILL.md` frontmatter. Put
DCC-MCP extension pointers such as `tools`, `prompts`, `recipes`, `workflows`,
and `depends` under `metadata.dcc-mcp.*`. Use `references/` for long-form docs,
recipes, examples, and notes that agents should load only when needed.

## 3. Keep Runtime Scripts Host-Safe

Scripts should lazy-import host APIs inside the callable function. This keeps
catalog discovery, validation, and server startup available without a running
host process.

Import shared helper APIs from `dcc_mcp_core.skills_helper` before adding small
dependencies or local utility modules. That namespace is the preferred path for
JSON/YAML codecs, bounded HTTP requests, safe file/path helpers, validation,
result envelopes, argument normalization, and cancellation checks. Keep
`requests`, PyYAML, custom HTTP/file helpers, or SDK-specific libraries only
when they provide behavior `skills_helper` intentionally does not cover, such
as sessions, streaming, multipart upload, custom retry/auth flows, or rich
domain file formats.

Use host-thread affinity only where needed:

- `affinity: main` for host API calls and scene mutations.
- `affinity: any` for pure filesystem, math, parsing, or metadata work.

## 4. Validate Before Loading

Run the creator validation tool or `dcc_mcp_core.validate_skill()` before adding
the skill to an adapter's default path. Treat validation warnings as design
feedback, not only syntax feedback.

`validate_skill_dir` adds `skill-helper-adoption` warnings for scripts that
import avoidable dependencies covered by `skills_helper`. New generated and
reference skills should ship without those warnings; legacy production skills
can migrate one helper category at a time while their existing tests stay green.


path: references/DCC_TOOL_CONTRACTS.md
content: # DCC-MCP Tool Contracts

Use this checklist for every `tools.yaml` entry.

## Required Shape

- `name`: local snake_case tool name, never dotted.
- `description`: concise action description shown to agents.
- `source_file`: script path relative to the skill directory.
- `input_schema`: JSON Schema for parameters.
- `output_schema`: JSON Schema for returned data when practical.
- `execution`: `sync` for quick calls, `async` for long-running work.
- `affinity`: `main` for host API calls, `any` for pure work.
- `timeout_hint_secs`: realistic upper bound for dispatch and UX.
- `annotations`: MCP safety hints.

## Recovery Chains

Domain tools should include `next-tools.on-failure` entries that point to
diagnostic or observation tools, such as screenshots, audit logs, or scene
snapshots. Infrastructure tools can omit failure chains when they are already
the recovery target.

## Core Boundary

Keep configuration in `SKILL.md` frontmatter under `metadata.dcc-mcp.*`, and
keep large payloads in sibling files such as `tools.yaml`, `prompts/*.yaml`,
`workflows/*.yaml`, or `references/*.md`.

Do not parse `SKILL.md`, `tools.yaml`, `groups.yaml`, prompts, or workflows from
adapter runtime code when core exposes a catalog or typed skill object API. If a
needed transform or hook is missing, create a core RFC and keep the adapter shim
narrow until the core API exists.


path: scripts/create_skill.py
content: """Scaffold a new DCC skill directory using the current DCC-MCP contract."""

from __future__ import annotations

from pathlib import Path
import re
import sys

_KEBAB_CASE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
_DCC_NAME = re.compile(r"^[A-Za-z0-9_-]{1,64}$")
_TOOL_NAME = re.compile(r"^[a-z0-9]+(?:_[a-z0-9]+)*$")
_AFFINITIES = {"any", "main"}
_EXECUTION_MODES = {"sync", "async"}


def _validate_skill_name(name: str) -> None:
    if not _KEBAB_CASE.fullmatch(name):
        raise ValueError("Skill name must be kebab-case, e.g. 'maya-rigging-tools'")


def _validate_dcc_name(dcc: str) -> None:
    if not _DCC_NAME.fullmatch(dcc):
        raise ValueError("DCC name must be client-safe: letters, digits, '_' or '-', max 64 chars")


def _validate_tool_name(tool_name: str) -> None:
    if not _TOOL_NAME.fullmatch(tool_name):
        raise ValueError(
            "Tool name must be snake_case and client-safe, e.g. 'create_locator'; dotted names are not supported"
        )


def _validate_choice(value: str, allowed: set, label: str) -> None:
    if value not in allowed:
        expected = ", ".join(sorted(allowed))
        raise ValueError(f"{label} must be one of: {expected}")


def _stage_line(stage: str) -> str:
    return f"    stage: {stage}\n" if stage else ""


def create_skill(
    name: str,
    parent_dir: str,
    dcc: str = "python",
    *,
    tool_name: str = "example_tool",
    layer: str = "thin-harness",
    stage: str = "",
    affinity: str = "any",
    execution: str = "sync",
) -> str:
    """Create a new skill directory with the modern standard structure.

    Args:
        name: Skill name in kebab-case.
        parent_dir: Directory where the skill folder will be created.
        dcc: Target DCC (e.g. "maya", "blender", "houdini", "python").
        tool_name: First generated tool name. Must be snake_case, never dotted.
        layer: Skill taxonomy layer, usually thin-harness, infrastructure, domain, or example.
        stage: Optional progressive-loading stage such as scene, authoring, or pipeline.
        affinity: Tool thread affinity. Use "main" for host API / scene work.
        execution: "sync" or "async".

    Returns:
        Absolute path to the created skill directory.

    """
    dcc = dcc.strip()
    tool_name = tool_name.strip()
    layer = layer.strip() or "thin-harness"
    stage = stage.strip()
    affinity = affinity.strip().lower() or "any"
    execution = execution.strip().lower() or "sync"

    _validate_skill_name(name)
    _validate_dcc_name(dcc)
    _validate_tool_name(tool_name)
    _validate_choice(affinity, _AFFINITIES, "affinity")
    _validate_choice(execution, _EXECUTION_MODES, "execution")

    skill_dir = Path(parent_dir).expanduser() / name
    if skill_dir.exists():
        raise FileExistsError(f"Skill directory already exists: {skill_dir}")

    skill_dir.mkdir(parents=True)
    (skill_dir / "scripts").mkdir()
    (skill_dir / "references").mkdir()

    title = name.replace("-", " ").title()
    script_file = f"scripts/{tool_name}.py"

    skill_md = skill_dir / "SKILL.md"
    skill_md.write_text(
        f"""---
name: {name}
description: >-
  DCC skill - TODO: describe the user intent this skill serves. Use when an
  agent needs TODO. Not for TODO.
license: MIT
compatibility: "Python 3.7+; dcc-mcp-core 0.17+"
allowed-tools: Bash Read Write Edit
metadata:
  dcc-mcp:
    dcc: {dcc}
    version: "0.1.0"
    layer: {layer}
{_stage_line(stage)}    tags: ["generated", "{dcc}"]
    search-hint: "TODO: add search keywords for this skill"
    tools: tools.yaml
---

# {title}

Keep instructions focused on when an agent should use this skill, what each
tool returns, and what to inspect after success or failure.

Tool names must stay client-safe (`^[A-Za-z0-9_-]{{1,64}}$`). Use local
snake_case tool names in `tools.yaml`; dcc-mcp-core publishes them as
`{name}__tool_name` after the skill is loaded.

Runtime scripts should import dependency-light helpers from
`dcc_mcp_core.skills_helper` first. It provides JSON/YAML, bounded HTTP, safe
file/path helpers, validation, cancellation checks, and result helpers without
adding small Python dependencies to DCC hosts.
""",
        encoding="utf-8",
    )

    tools_yaml = skill_dir / "tools.yaml"
    tools_yaml.write_text(
        f"""tools:
  - name: {tool_name}
    description: "Example read-only scaffold. Replace with one concrete DCC intent and describe side effects."
    source_file: {script_file}
    input_schema:
      type: object
      properties:
        label:
          type: string
          description: "Optional label echoed by the scaffold implementation."
        dry_run:
          type: boolean
          description: "Keep true until this scaffold performs real host work."
          default: true
      additionalProperties: false
    output_schema:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        context:
          type: object
    execution: {execution}
    affinity: {affinity}
    enforce_thread_affinity: true
    timeout_hint_secs: 30
    annotations:
      read_only_hint: true
      destructive_hint: false
      idempotent_hint: true
      open_world_hint: false
      deferred_hint: false
    next-tools:
      on-success: []
      on-failure: []
""",
        encoding="utf-8",
    )

    example_script = skill_dir / script_file
    example_script.write_text(
        '"""Example DCC-MCP skill tool implementation."""\n'
        "from __future__ import annotations\n\n"
        "from dcc_mcp_core.skills_helper import run_main, skill_entry, skill_success\n\n\n"
        "@skill_entry\n"
        'def main(label: str = "example", dry_run: bool = True, **params):\n'
        '    """Replace this scaffold with a concrete DCC operation."""\n'
        "    return skill_success(\n"
        '        "Example tool completed",\n'
        '        prompt="Replace this scaffold with a concrete DCC operation.",\n'
        "        label=label,\n"
        "        dry_run=dry_run,\n"
        "        extra_params=params,\n"
        "    )\n\n\n"
        'if __name__ == "__main__":\n'
        "    run_main(main)\n",
        encoding="utf-8",
    )

    return str(skill_dir.resolve())


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python create_skill.py <name> <parent_dir> [dcc]")
        sys.exit(1)
    path = create_skill(sys.argv[1], sys.argv[2], sys.argv[3] if len(sys.argv) > 3 else "python")
    print(f"Created skill at: {path}")


path: scripts/skill_template.py
content: """Return a SKILL.md template using the current dcc-mcp-core layout."""

from __future__ import annotations

SKILL_TEMPLATE = """---
name: my-skill
description: >-
  DCC skill - Describe the durable user intent this skill serves. Use when an
  AI agent needs that intent. Not for unrelated diagnostics or raw code eval.
license: MIT
compatibility: "Python 3.7+; dcc-mcp-core 0.17+"
allowed-tools: Bash Read Write Edit
metadata:
  dcc-mcp:
    dcc: maya
    version: "1.0.0"
    layer: thin-harness
    stage: authoring
    tags: ["modeling", "animation", "example"]
    search-hint: "create object, inspect scene, export asset"
    tools: tools.yaml
---

# My Skill

Write concise instructions for the AI agent:

- when to load this skill;
- what each tool changes or reads;
- what to verify after success;
- what diagnostic tool to call after failure.

Tool names must be client-safe (`^[A-Za-z0-9_-]{1,64}$`). In `tools.yaml`, use
local snake_case names such as `create_locator`; dcc-mcp-core publishes loaded
tools as `<skill-name>__<tool_name>`.

Runtime scripts should import dependency-light helpers from
`dcc_mcp_core.skills_helper` first. It provides JSON/YAML, bounded HTTP, safe
file/path helpers, validation, cancellation checks, and result helpers while
keeping generated skills free of small Python dependencies such as `requests`
or PyYAML for covered cases.
"""


def skill_template() -> str:
    """Return a current SKILL.md template.

    Tool declarations live in the sibling ``tools.yaml`` referenced by
    ``metadata.dcc-mcp.tools``. Use the creator scaffold for a full
    SKILL.md + tools.yaml + scripts/ example with schemas, annotations, and
    thread-affinity metadata.
    """
    return SKILL_TEMPLATE


if __name__ == "__main__":
    print(skill_template())


path: scripts/validate_skill_dir.py
content: """Validate a skill directory using dcc_mcp_core.validate_skill."""

from __future__ import annotations

import ast
from pathlib import Path
import sys

_AVOIDABLE_IMPORTS = {
    "requests": "use http_request/http_get_json/http_post_json from dcc_mcp_core.skills_helper for bounded REST calls",
    "httpx": "use http_request/http_get_json/http_post_json from dcc_mcp_core.skills_helper for bounded REST calls",
    "yaml": "use yaml_loads/yaml_dumps or load_yaml_file/dump_yaml_file from dcc_mcp_core.skills_helper",
    "ruamel": "use yaml_loads/yaml_dumps or load_yaml_file/dump_yaml_file from dcc_mcp_core.skills_helper",
}
_LOCAL_HELPER_MODULES = {
    "file_utils",
    "http_utils",
    "json_utils",
    "path_utils",
    "yaml_utils",
}


def validate_skill_dir(skill_dir: str) -> dict:
    """Validate a skill directory and return a structured report.

    Args:
        skill_dir: Path to the skill directory.

    Returns:
        Dict with 'skill_dir', 'is_clean', 'has_errors', and 'issues' list.

    """
    from dcc_mcp_core import validate_skill

    report = validate_skill(skill_dir)
    issues = [{"severity": i.severity, "category": i.category, "message": i.message} for i in report.issues]
    issues.extend(_skill_helper_adoption_warnings(skill_dir))
    has_errors = any(issue["severity"] == "error" for issue in issues)
    has_warnings = any(issue["severity"] == "warning" for issue in issues)
    return {
        "skill_dir": report.skill_dir,
        "is_clean": not has_errors and not has_warnings,
        "has_errors": has_errors,
        "has_warnings": has_warnings,
        "issues": issues,
    }


def _skill_helper_adoption_warnings(skill_dir: str) -> list[dict]:
    """Warn when skill scripts import dependencies covered by skills_helper."""
    scripts_dir = Path(skill_dir) / "scripts"
    if not scripts_dir.is_dir():
        return []

    warnings: list[dict] = []
    seen: set[tuple[str, str]] = set()
    for script in sorted(scripts_dir.rglob("*.py")):
        try:
            tree = ast.parse(script.read_text(encoding="utf-8"), filename=str(script))
        except (OSError, SyntaxError):
            continue
        rel = script.relative_to(skill_dir).as_posix()
        for node in ast.walk(tree):
            for module, hint in _iter_import_hints(node):
                key = (rel, module)
                if key in seen:
                    continue
                seen.add(key)
                warnings.append(
                    {
                        "severity": "warning",
                        "category": "skill-helper-adoption",
                        "message": f"{rel} imports {module!r}; {hint}.",
                    }
                )
    return warnings


def _iter_import_hints(node: ast.AST):
    if isinstance(node, ast.Import):
        for alias in node.names:
            module = alias.name.split(".", 1)[0]
            hint = _AVOIDABLE_IMPORTS.get(module)
            if hint:
                yield module, hint
            elif module in _LOCAL_HELPER_MODULES:
                yield module, "review whether dcc_mcp_core.skills_helper already covers this local helper"
    elif isinstance(node, ast.ImportFrom):
        module = node.module or ""
        root = module.split(".", 1)[0]
        basename = module.rsplit(".", 1)[-1]
        hint = _AVOIDABLE_IMPORTS.get(root)
        if hint:
            yield root, hint
        elif node.level and basename in _LOCAL_HELPER_MODULES:
            yield basename, "review whether dcc_mcp_core.skills_helper already covers this local helper"
        elif root in _LOCAL_HELPER_MODULES:
            yield root, "review whether dcc_mcp_core.skills_helper already covers this local helper"


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python validate_skill_dir.py <skill_dir>")
        sys.exit(1)
    result = validate_skill_dir(sys.argv[1])
    print(f"Skill: {result['skill_dir']}")
    print(f"Clean: {result['is_clean']}")
    print(f"Errors: {result['has_errors']}")
    print(f"Warnings: {result['has_warnings']}")
    for issue in result["issues"]:
        print(f"  [{issue['severity']}] {issue['category']}: {issue['message']}")


path: tools.yaml
content: tools:
  - name: create_skill
    description: "Scaffold a current DCC-MCP skill package with client-safe tool names, schemas, annotations, and scripts."
    source_file: scripts/create_skill.py
    input_schema:
      type: object
      required: [name, parent_dir]
      properties:
        name:
          type: string
          pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$"
          description: "Skill directory and package name in kebab-case."
        parent_dir:
          type: string
          description: "Directory that will receive the new skill folder."
        dcc:
          type: string
          pattern: "^[A-Za-z0-9_-]{1,64}$"
          default: python
          description: "Target host, such as maya, blender, houdini, or python."
        tool_name:
          type: string
          pattern: "^[a-z0-9]+(?:_[a-z0-9]+)*$"
          default: example_tool
          description: "First local tool name. Must be snake_case, never dotted."
        layer:
          type: string
          enum: [thin-harness, infrastructure, domain, example]
          default: thin-harness
          description: "Skill taxonomy layer."
        stage:
          type: string
          description: "Optional progressive-loading stage, for example scene or authoring."
        affinity:
          type: string
          enum: [any, main]
          default: any
          description: "Use main for host API or scene mutation work."
        execution:
          type: string
          enum: [sync, async]
          default: sync
          description: "Use async for long-running work surfaced as deferred MCP execution."
      additionalProperties: false
    output_schema:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        context:
          type: object
    execution: sync
    affinity: any
    enforce_thread_affinity: true
    timeout_hint_secs: 30
    annotations:
      read_only_hint: false
      destructive_hint: false
      idempotent_hint: false
      open_world_hint: true
      deferred_hint: false
    next-tools:
      on-success: [validate_skill_dir]
      on-failure: []

  - name: validate_skill_dir
    description: "Validate a skill directory against the current dcc-mcp-core skill specification."
    source_file: scripts/validate_skill_dir.py
    input_schema:
      type: object
      required: [skill_dir]
      properties:
        skill_dir:
          type: string
          description: "Path to the skill directory containing SKILL.md."
      additionalProperties: false
    output_schema:
      type: object
      properties:
        skill_dir:
          type: string
        is_clean:
          type: boolean
        has_errors:
          type: boolean
        has_warnings:
          type: boolean
        issues:
          type: array
    execution: sync
    affinity: any
    enforce_thread_affinity: true
    timeout_hint_secs: 10
    annotations:
      read_only_hint: true
      destructive_hint: false
      idempotent_hint: true
      open_world_hint: true
      deferred_hint: false

  - name: skill_template
    description: "Return a current SKILL.md template that references sibling tools.yaml declarations."
    source_file: scripts/skill_template.py
    input_schema:
      type: object
      properties: {}
      additionalProperties: false
    output_schema:
      type: string
    execution: sync
    affinity: any
    enforce_thread_affinity: true
    timeout_hint_secs: 5
    annotations:
      read_only_hint: true
      destructive_hint: false
      idempotent_hint: true
      open_world_hint: false
      deferred_hint: false


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)