Skills 技能包系统
Skills 系统允许你将任何脚本(Python、MEL、MaxScript、BAT、Shell 等)零代码注册为 MCP 可发现的工具,直接复用 OpenClaw Skills / Anthropic Skills 生态格式。
快速上手
1. 创建 Skill 目录
maya-geometry/
├── SKILL.md
├── tools.yaml
└── scripts/
├── create_sphere.py
├── batch_rename.mel
└── export_fbx.bat2. 编写 SKILL.md 和 tools.yaml
SKILL.md:
---
name: maya-geometry
description: "Maya 几何体创建和修改工具。用于创建、检查或导出 Maya 多边形网格。"
metadata:
dcc-mcp:
dcc: maya
version: "1.0.0"
tags: ["geometry", "create"]
search-hint: "polygon modeling, sphere, bevel, extrude, mesh editing"
search-aliases: [primitive ball, mesh globe]
tools: tools.yaml
---
# Maya Geometry Skill
使用这些工具在 Maya 中创建和修改几何体。tools.yaml:
tools:
- name: create_sphere
description: "根据给定半径创建多边形球体"
search_aliases: [primitive ball, mesh globe]
source_file: scripts/create_sphere.py
read_only: false
- name: export_fbx
description: "将选中对象导出为 FBX"
source_file: scripts/export_fbx.batmetadata.dcc-mcp.search-hint 提供不加载完整工具 schema 也能用于 search_skills 的关键词。metadata.dcc-mcp.search-aliases 和 tools.yaml 中的 per-tool search_aliases 用来声明有界的搜索同义词、本地化词或常见 用户说法;它们只改善 gateway/per-DCC 搜索召回,不改变 tool name、summary、 tag 或实际调用参数。
3. 设置环境变量
# Linux/macOS
export DCC_MCP_SKILL_PATHS="/path/to/my-skills"
# Windows
set DCC_MCP_SKILL_PATHS=C:\path\to\my-skills
# 多个路径(使用平台路径分隔符)
export DCC_MCP_SKILL_PATHS="/path/skills1:/path/skills2"4. 发现并加载
推荐使用 SkillCatalog 进行完整的渐进式加载,也可使用低级扫描函数进行一次性操作:
from dcc_mcp_core import SkillCatalog, ToolRegistry, ToolDispatcher
# 创建目录(基于 ToolRegistry)
registry = ToolRegistry()
catalog = SkillCatalog(registry)
# 可选:附加调度器以启用自动处理器注册
dispatcher = ToolDispatcher(registry)
catalog.with_dispatcher(dispatcher)
# 发现 DCC_MCP_SKILL_PATHS 中的所有 Skill
catalog.discover(dcc_name="maya")
# 列出可用 Skill
for skill in catalog.list_skills():
print(f" {skill.name} v{skill.version}: {skill.description} (已加载={skill.loaded})")
# 加载 Skill — 附加调度器后工具自动注册
actions = catalog.load_skill("maya-geometry")
print(f"已注册工具: {actions}")Skill 目录(推荐 API)
SkillCatalog 管理完整生命周期:发现 → 渐进式加载 → 卸载。
from dcc_mcp_core import SkillCatalog, ToolRegistry, ToolDispatcher
registry = ToolRegistry()
catalog = SkillCatalog(registry)
dispatcher = ToolDispatcher(registry)
catalog.with_dispatcher(dispatcher)
# 发现
catalog.discover(extra_paths=["/my/skills"], dcc_name="maya")
# 搜索
results = catalog.search_skills(query="geometry", tags=["create"], dcc="maya")
for s in results:
print(f"{s.name}: {s.tool_count} 个工具 {s.tool_names}")
# 加载/卸载
actions = catalog.load_skill("maya-geometry") # 返回 List[str]
catalog.is_loaded("maya-geometry") # True
removed = catalog.unload_skill("maya-geometry") # 返回 int
# 状态查询
catalog.loaded_count() # int
len(catalog) # 目录中的 Skill 总数
catalog.list_skills() # 所有 Skill(SkillSummary 列表)
catalog.list_skills("loaded") # 仅已加载的
catalog.list_skills("unloaded") # 仅未加载的
# 详细信息
info = catalog.get_skill_info("maya-geometry") # dict 或 NoneSkillSummary 字段
search_skills() 和 list_skills() 返回 SkillSummary 对象:
| 字段 | 类型 | 说明 |
|---|---|---|
name | str | Skill 名称 |
description | str | 简短描述 |
tags | list[str] | Skill 标签 |
dcc | str | 目标 DCC(如 "maya") |
version | str | Skill 版本 |
tool_count | int | 声明的工具数量 |
tool_names | list[str] | 声明的工具名称列表 |
loaded | bool | 当前是否已加载 |
ToolDeclaration
ToolDeclaration 描述 Skill 中的单个工具;新 skill 必须在 metadata.dcc-mcp.tools 指向的同级 tools.yaml 中声明工具,旧的 SKILL.md 顶层 tools: frontmatter 会被严格 loader 拒绝:
tools:
- name: create_sphere
description: "创建多边形球体"
input_schema: '{"type":"object","properties":{"radius":{"type":"number"}}}'
search_aliases: [primitive ball, mesh globe]
read_only: false
destructive: false
idempotent: false
defer-loading: true
source_file: scripts/create_sphere.pyfrom dcc_mcp_core import ToolDeclaration
decl = ToolDeclaration(
name="create_sphere",
description="创建多边形球体",
input_schema='{"type":"object","properties":{"radius":{"type":"number"}}}',
read_only=False,
destructive=False,
idempotent=False,
defer_loading=True,
source_file="scripts/create_sphere.py",
)| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name | str | 必填 | 工具名称(在 Skill 内唯一) |
description | str | "" | 人类可读的描述 |
input_schema | str(JSON) | None | 输入参数的 JSON Schema |
output_schema | str(JSON) | None | 输出的 JSON Schema |
search_aliases | list[str] | [] | 有界的搜索同义词;会和 schema 字段名一起参与发现索引,但不是工具参数,也不能替代简洁描述。 |
read_only | bool | False | 仅读取数据(无副作用) |
destructive | bool | False | 可能导致破坏性修改 |
idempotent | bool | False | 相同参数始终产生相同结果 |
defer_loading | bool | False | 接受 defer-loading / defer_loading,用于标记发现阶段的声明 |
source_file | str | "" | 脚本的显式路径(相对于 Skill 目录) |
tools/list 返回的未加载 skill stub 还会显式带上 annotations.deferredHint = true。调用 load_skill(...) 后,stub 会被真实工具替换,且这些工具返回 deferredHint = false。
从 Python 类型派生 input_schema / output_schema(#242)
手写 JSON Schema 易出错 —— 代理生成的 action 会漂移、缓存 schema 会和运行时代码分叉、schema 文本在 review 时难读。dcc_mcp_core.schema 只用标准库(无 pydantic、无 jsonschema、无 attrs)从 Python 类型注解派生这两份 schema。
写一个带类型标注的 handler 即可:
from dataclasses import dataclass, field
from typing import Tuple
from dcc_mcp_core import tool_spec_from_callable
from dcc_mcp_core._tool_registration import register_tools
@dataclass
class ExportInput:
scene_path: str = field(metadata={"description": "Scene file to export."})
format: str = "fbx"
frame_range: Tuple[int, int] = (1, 100)
@dataclass
class ExportResult:
path: str
size_bytes: int
took_ms: int
def export_scene(args: ExportInput) -> ExportResult:
"""Export a scene to an interchange format."""
...
spec = tool_spec_from_callable(export_scene)
register_tools(server, [spec], dcc_name="maya")tool_spec_from_callable 能识别两种签名风格:
- 单个 dataclass / TypedDict 参数 —— 整个参数就是
inputSchema(上例即为此类)。 - 多个原子类型参数 —— 每个参数成为
object型inputSchema的一个 property。
若有返回类型标注,会作为 outputSchema,MCP 2025-06-18 的客户端即可用它校验 structuredContent。未类型化的 handler 抛 TypeError,不会静默回退成宽松的 {"type": "object"}(修掉 #588 同代的陷阱)。
支持的类型(全标准库):bool、int、float、str、bytes、None、list[X]、tuple[X, ...]、定长 tuple[A, B, ...]、dict[str, V]、Optional[X] / X | None、Union[A, B]、Literal[...]、Enum、datetime.datetime、datetime.date、pathlib.Path、uuid.UUID、@dataclass、TypedDict。Python 3.7 中请用 typing.List、typing.Dict、typing.Tuple、typing.Optional、typing.Union 来书写容器与联合类型;Literal 与 TypedDict 需要由 skill 作者环境提供 typing_extensions。核心包自身仍保持零运行时依赖。不支持的类型会抛 TypeError 并附一条明确的"逃生通道":显式传 input_schema=... dict 或用 pydantic 的 MyModel.model_json_schema()。
为什么不用 pydantic?
我们刻意保持 0 依赖。仅为此特性引入 pydantic 会拉进 3MB wheel 再加 pydantic-core,对只想写几个 dataclass handler 的作者成本过高。对于已经在用 pydantic 的调用方,派生出的 shape 与 pydantic 的约定一致(title、$defs、$ref、anyOf、required),因此换成 MyModel.model_json_schema() 是即插即用的。
pydantic-core(Rust)也被评估过,结论:对我们没帮助 —— pydantic 官方架构文档确认 JSON Schema 生成完全在 Python 层做(pydantic.json_schema.GenerateJsonSchema),Rust crate 只做校验和序列化,不看 Python 类型注解。
完整示例见 examples/skills/typed-schema-demo/;完整类型映射表以可执行形式见 tests/test_schema.py。
脚本查找优先级
加载 Skill 时,目录按以下优先级解析每个 ToolDeclaration 对应的脚本:
ToolDeclaration.source_file— 显式路径优先scripts/中文件名(去扩展名)与工具名匹配的脚本- 如果 Skill 只有一个脚本,则该脚本服务所有工具
- 无脚本可匹配 — 工具在注册表中可见但无法执行
低级技能函数
如需简单的一次性扫描(不使用渐进式加载):
import os
from dcc_mcp_core import (
SkillScanner,
SkillWatcher,
SkillMetadata,
parse_skill_md,
scan_skill_paths,
scan_and_load,
scan_and_load_lenient,
)
os.environ["DCC_MCP_SKILL_PATHS"] = "/path/to/skills"
# 一次性扫描 + 加载 + 依赖排序 → 返回 (skills, skipped_dirs)
skills, skipped = scan_and_load(extra_paths=["/my/skills"], dcc_name="maya")
skills_lenient, skipped = scan_and_load_lenient(dcc_name="maya") # 保留软依赖缺失的 Skill
# 扫描目录中的 SKILL.md 文件
scanner = SkillScanner()
skill_dirs = scanner.scan(extra_paths=["/my/skills"], dcc_name="maya")
# 解析单个 Skill 目录
metadata = parse_skill_md("/path/to/maya-geometry")
# 获取原始 Skill 目录路径列表
paths = scan_skill_paths(extra_paths=["/my/skills"], dcc_name="maya")使用 SkillWatcher 实现热重载
SkillWatcher 监控文件系统,当 SKILL.md 文件更改时自动重新加载技能:
from dcc_mcp_core import SkillWatcher
watcher = SkillWatcher(debounce_ms=300)
watcher.watch("/path/to/skills")
# 获取当前技能(快照)
skills = watcher.skills() # List[SkillMetadata]
count = watcher.skill_count() # int
# 手动重载
watcher.reload()
# 停止监控
watcher.unwatch("/path/to/skills")
# 查看已监控的路径
paths = watcher.watched_paths() # List[str]依赖管理
Skill 可通过 SKILL.md 中的 metadata.dcc-mcp.depends 或同级 metadata/depends.md 声明对其他 Skill 的依赖:
---
name: maya-animation
metadata:
dcc-mcp:
depends: ["maya-geometry"]
---from dcc_mcp_core import (
resolve_dependencies,
validate_dependencies,
expand_transitive_dependencies,
)
skills, _ = scan_and_load()
# 拓扑排序(每个 Skill 在其依赖项之后出现)
ordered = resolve_dependencies(skills)
# 验证 — 返回错误消息列表
errors = validate_dependencies(skills)
# 指定 Skill 的所有传递依赖
deps = expand_transitive_dependencies(skills, "maya-animation")
# ["maya-geometry"]Catalog 发现阶段会把这些声明当作软依赖处理。若组合 Skill 依赖 maya-dev,但扫描时 maya-dev 还没有被发现,组合 Skill 仍会出现在 search_skills() / list_skills() 中,并带有 status: "pending_deps" 和 missing_dependencies。调用 load_skill("maya-animation") 时会先自动加载已经 发现的依赖;如果依赖仍缺失,则返回包含缺失 Skill 名称和修复建议的错误。 需要在打包或 CI 中强制校验依赖时,继续使用 scan_and_load() / resolve_dependencies() 的 fail-fast 行为。
SkillMetadata 字段
从 agentskills.io SKILL.md frontmatter 以及 metadata.dcc-mcp.* 指向的同级文件解析。严格 loader 会拒绝顶层 dcc-mcp 扩展键;扩展数据应放在命名空间和同级文件中。
| 字段 | 类型 | 说明 |
|---|---|---|
name | str | 唯一 Skill 名称 |
description | str | 简短描述 |
tools | list[ToolDeclaration] | 从 metadata.dcc-mcp.tools 指向的同级文件解析的声明工具 |
dcc | str | 目标 DCC 应用(默认:"python") |
tags | list[str] | 分类标签 |
scripts | list[str] | 发现的脚本文件路径 |
skill_path | str | Skill 目录的绝对路径 |
version | str | Skill 版本(默认:"1.0.0") |
depends | list[str] | 依赖的 Skill 名称 |
metadata_files | list[str] | metadata/ 目录中的 .md 文件路径 |
license | str | 许可证标识 |
compatibility | str | 兼容性描述 |
allowed_tools | list[str] | 允许的工具名称 |
groups | list[SkillGroup] | 工具分组(渐进式暴露) |
跨重启的 Skill 持久化
DccServerBase.enable_skill_load_persistence(policy) 将目录的 after-load / after-unload / after-group-change 钩子连接到 LoadedStateStore,以 ~/.dcc-mcp/<dcc>/loaded.json 作为数据源,gateway admin SQLite 的 skill_loaded_state / skill_active_groups 表作为最佳努力镜像(用于 admin UI 可见性)。
server = DccServerBase(opts)
server.start()
server.enable_skill_load_persistence(policy="skip_on_drift")启动时,store 会重放持久化的快照,使之前加载的 Skill 和激活的分组得以恢复,无需 AI 客户端重新发现和重新激活。
重放策略
| 策略 | 行为 |
|---|---|
"skip_on_drift"(默认) | 如果磁盘版本与持久化版本不同则跳过 |
"require_exact_version" | 如果检测到任何版本不匹配则失败 |
"ignore_version" | 无论版本漂移如何都重新加载 |
持久化契约
- 数据源:每个 DCC 的 JSON 文件
~/.dcc-mcp/<dcc>/loaded.json - Gateway admin SQLite 是最佳努力镜像,用于仪表盘可见性
- 所有写入使用原子替换(
write → fsync → rename) - 钩子不得抛出异常;缺失文件或 schema 不匹配视为空状态
语义 Skill 搜索
dcc-mcp-core 提供词法 + 向量融合索引,使词形变体和变形查询(如 rendering vs render)仍能召回正确的 skill。
标准融合设置
from dcc_mcp_core import LexicalSkillIndex, RrfFusionIndex, VectorSkillIndex
fused = (
RrfFusionIndex()
.register("lex", LexicalSkillIndex())
.register("vec", VectorSkillIndex())
)
fused.index(documents)
hits = fused.search("如何创建多边形球体", k=8)组件
| 类 | 用途 | 依赖 |
|---|---|---|
LexicalSkillIndex | BM25 风格关键词匹配 | 零依赖(纯 Python) |
VectorSkillIndex | 密集嵌入相似度搜索 | 默认零依赖(HashedEmbedder + InMemoryVectorStore) |
RrfFusionIndex | 互惠排名融合合并器(Cormack et al. 2009) | 零依赖 |
HashedEmbedder | 确定性特征哈希嵌入器(dim=256 时约 5µs/文档) | 零依赖 |
OnnxEmbedder | 高质量神经嵌入(三层后端) | 可选:dcc-mcp-core-semantic 或 fastembed |
嵌入器后端
OnnxEmbedder 使用三层回退:
dcc_mcp_core_semantic.native.NativeEmbedder— Rust 原生(最快)fastembed.TextEmbedding— 纯 Python fastembed- 均未安装 →
EmbedderError
from dcc_mcp_core import OnnxEmbedder, VectorSkillIndex
emb = OnnxEmbedder(model_name="BAAI/bge-base-en-v1.5")
idx = VectorSkillIndex(embedder=emb)模型覆盖环境变量:
| 变量 | 用途 |
|---|---|
DCC_MCP_EMBED_MODEL | 覆盖默认 ONNX 模型名称 |
DCC_MCP_EMBED_MODEL_DIR | 覆盖本地模型缓存目录 |
生命周期钩子
类型化的生命周期钩子框架让适配器和策略代码无需修补 DccServerBase 内部即可订阅发现、工具调用和会话事件。
钩子事件
from dcc_mcp_core.lifecycle_hooks import HookEvent
# 可用事件:
HookEvent.SESSION_START # 会话开始
HookEvent.BEFORE_SEARCH # 搜索前(策略 — 可否决)
HookEvent.AFTER_SEARCH # 搜索后
HookEvent.BEFORE_SKILL_LOAD # Skill 加载前(策略 — 可否决)
HookEvent.AFTER_SKILL_LOAD # Skill 加载后
HookEvent.BEFORE_TOOL_CALL # 工具调用前(策略 — 可否决)
HookEvent.AFTER_TOOL_CALL # 工具调用后
HookEvent.SESSION_END # 会话结束策略事件(BEFORE_SKILL_LOAD、BEFORE_TOOL_CALL、BEFORE_SEARCH)可通过抛出 HookDeny 来否决操作:
from dcc_mcp_core.lifecycle_hooks import HookDeny
def my_policy(ctx):
if is_blocked(ctx.payload.get("tool_name")):
raise HookDeny("工具被策略阻止", hint="请使用 dcc_diagnostics__screenshot")
hooks.on(HookEvent.BEFORE_TOOL_CALL, my_policy)注册
from dcc_mcp_core.lifecycle_hooks import LifecycleHooks, HookEvent
hooks = LifecycleHooks()
server.register_lifecycle_hooks(hooks)
@hooks.on(HookEvent.AFTER_TOOL_CALL)
def log_call(ctx):
print(f"工具 {ctx.payload.get('tool_name')} 在 {ctx.dcc_name} 上被调用")分发辅助方法
DccServerBase 提供便捷方法供适配器代码触发事件:
server.dispatch_session_start(session_id="abc-123")
server.dispatch_before_tool_call("create_sphere", session_id="abc-123")
server.dispatch_after_tool_call("create_sphere", payload={"ok": True})
server.dispatch_session_end(session_id="abc-123")安全分发
处理器按注册顺序分发。对于非策略事件,处理器异常记录为 WARNING 并被吞掉。对于策略事件,任何处理器抛出的 HookDeny 会传播给调用者;其他异常被记录并视为"无决策"。
Agent 记忆
DCC 适配器的三层记忆模型:有界的、会话感知的、注重隐私的。契约是"仅存储摘要事实,永不存储原始提示"。
记忆层
| 层 | 范围 | 持久化 | 默认上限 |
|---|---|---|---|
EPHEMERAL | 会话级环形缓冲区 | 永不持久化 | 每会话 256 条 |
WORKING | 任务级,基于 TTL | 默认不持久化 | 每会话 1024 条,6 小时 TTL |
LONGTERM | 持久化模式 | 需要显式存储后端 | 总计 4096 条 |
快速开始
from dcc_mcp_core.agent_memory import InMemoryMemoryStore, MemoryRecorder
from dcc_mcp_core.lifecycle_hooks import LifecycleHooks
hooks = LifecycleHooks()
store = InMemoryMemoryStore()
recorder = MemoryRecorder(store).install(hooks)
server.register_lifecycle_hooks(hooks)MemoryRecorder.install(hooks) 为 SESSION_START、BEFORE_SEARCH、AFTER_SKILL_LOAD、BEFORE_TOOL_CALL、AFTER_TOOL_CALL 和 SESSION_END 注册处理器。记忆摘要自动注入到 HookContext.payload 中,包含 memory_summary、memory_prefer_tools 和 memory_avoid_tools。
会话压缩
在 SESSION_END 时,working 条目被提升为 LONGTERM 的 "pattern:*" 摘要,然后 ephemeral 和 working 条目被遗忘。这保持了记忆占用有界,同时跨会话保留学习到的模式。
隐私开关
recorder.set_enabled(False) # 禁用捕获/注入,无需取消注册钩子查询记忆
from dcc_mcp_core.agent_memory import MemoryQuery, MemoryLayer
# 查询特定 DCC 的长期模式
results = store.query(MemoryQuery(layer=MemoryLayer.LONGTERM, dcc_name="maya", limit=10))
# 查询会话的所有层
results = store.query(MemoryQuery(session_id="abc-123"))
# 忘记会话的所有条目
forgotten = store.forget(session_id="abc-123")环境变量
| 变量 | 说明 |
|---|---|
DCC_MCP_{APP}_SKILL_PATHS | 应用专属 Skill 路径,如 DCC_MCP_MAYA_SKILL_PATHS(Windows 用 ;,Unix 用 :) |
DCC_MCP_SKILL_PATHS | 全局兜底 Skill 路径(应用专属变量未设置时使用) |
应用专属路径优先级更高
对于 app_name="maya",DCC_MCP_MAYA_SKILL_PATHS 优先检查,DCC_MCP_SKILL_PATHS 作为全局兜底。
一键 Skills-First 启动:create_skill_server
使用 create_skill_server(v0.12.12+)可以一键完成所有配置,将 ToolRegistry、ToolDispatcher、SkillCatalog 和 McpHttpServer 组合在一起:
import os
from dcc_mcp_core import create_skill_server, McpHttpConfig
# 设置应用专属 Skill 路径
os.environ["DCC_MCP_MAYA_SKILL_PATHS"] = "/studio/maya-skills"
# 一键:发现 Skills + 启动 MCP HTTP 服务器
server = create_skill_server("maya", McpHttpConfig(port=8765))
handle = server.start()
print(f"Maya MCP 服务器地址:{handle.mcp_url()}")
# AI 客户端连接到 http://127.0.0.1:8765/mcpcreate_skill_server 自动完成:
- 创建
ToolRegistry和ToolDispatcher - 创建连接到 dispatcher 的
SkillCatalog - 从
DCC_MCP_MAYA_SKILL_PATHS和DCC_MCP_SKILL_PATHS发现 Skills - 返回已配置好的
McpHttpServer
def create_skill_server(
app_name: str,
config: McpHttpConfig | None = None,
extra_paths: list[str] | None = None,
dcc_name: str | None = None,
) -> McpHttpServer: ...| 参数 | 类型 | 说明 |
|---|---|---|
app_name | str | DCC 应用名("maya"、"blender" 等)— 用于推导环境变量名和服务器名 |
config | McpHttpConfig | None | HTTP 配置;默认端口 8765 |
extra_paths | list[str] | None | 额外扫描的 Skill 目录 |
dcc_name | str | None | 覆盖 Skill 扫描的 DCC 过滤条件(默认与 app_name 相同) |
支持的脚本类型
| 扩展名 | 类型 | 执行方式 |
|---|---|---|
.py | Python | python 解释器 |
.sh, .bash | Shell | bash |
.bat, .cmd | Batch | cmd /C |
.mel | MEL(Maya) | python 包装器 |
.ms | MaxScript(3ds Max) | python 包装器 |
.lua, .hscript | Lua / Houdini | python 包装器 |
Skills-First 架构
推荐使用 create_skill_server 作为 v0.12.12+ 的首选入口。它将 SkillCatalog 自动脚本执行与 MCP HTTP 服务集成,Agent 无需任何手动处理器注册即可通过 tools/call 调用工具。
脚本执行
所有脚本作为子进程运行。输入参数通过 stdin 以 JSON 格式传入。脚本应将 JSON 结果写入 stdout,成功时退出码为 0。
按需 Skill 发现(MCP HTTP)
使用 MCP HTTP 服务器(McpHttpServer 或 create_skill_server)时,tools/list 返回三层响应:
三层 tools/list 响应
5 个核心发现工具(始终存在):
list_skills— 列出所有 Skill,可按状态筛选get_skill_info— 获取指定 Skill 的完整元数据load_skill— 加载 Skill,将其工具注册到 ToolRegistryunload_skill— 卸载 Skill,移除其工具search_skills— 跨 name、description、search_hint、tool_names 的关键词搜索
已加载 Skill 工具 — 来自
ToolRegistry的完整input_schema未加载 Skill Stub —
__skill__<name>条目,仅含一行描述(无完整 schema)
工作流
1. AI 调用 tools/list → 看到核心工具 + 已加载工具 + __skill__ stub
2. AI 调用 search_skills(query="geometry") → 找到匹配的 Skill
3. AI 调用 load_skill(skill_name="maya-geometry") → 工具已注册
4. AI 再次调用 tools/list → maya-geometry 工具现在有完整 schema
5. AI 调用 maya_geometry__create_sphere → Skill 脚本执行Skill Stub 行为
调用未加载的 Skill stub(__skill__<name>)时返回带提示的错误:
{
"error": "Skill 'maya-geometry' is not loaded. Call load_skill(skill_name=\"maya-geometry\") to register its tools."
}search_skills MCP 工具
{
"name": "search_skills",
"description": "搜索匹配关键词的 Skill",
"inputSchema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "搜索关键词"},
"dcc": {"type": "string", "description": "按 DCC 类型筛选"}
},
"required": ["query"]
}
}搜索范围:name、description、search_hint、search_aliases、tags、 dcc,以及同级 tools.yaml 中的工具 name、description 和 per-tool search_aliases。search_hint 字段(来自 SKILL.md search-hint:)无需加载 完整 schema 即可改善关键词匹配。
create_skill_server() 启动时只调用 discover() — Skills 不会自动加载。这保持了初始工具列表的精简,让 Agent 按需加载。
MCP 核心发现工具
通过 MCP HTTP 服务器,tools/list 始终返回 6 个核心发现工具:
| 工具 | 说明 |
|---|---|
list_skills | 列出技能及加载状态 |
get_skill_info | 获取技能的完整元数据 |
load_skill | 加载技能 — 将其工具注册到 ToolRegistry |
unload_skill | 卸载技能 — 从 ToolRegistry 移除其工具 |
search_skills | 按关键词搜索技能(紧凑一行摘要) |
三层 tools/list 响应
tools/list 返回三层内容:
- 核心发现工具(5 个,始终完整)—
list_skills、get_skill_info、load_skill、unload_skill、search_skills - 已加载技能工具 — 来自 ToolRegistry,带完整
input_schema - 未加载技能存根 —
__skill__<name>格式名称 + 一句话描述,无完整 schema
存根调用行为
当调用 __skill__<name> 时,返回错误提示:
Call load_skill(skill_name="<name>") to register its tools推荐工作流
1. tools/list → 看到核心工具 + 已加载工具 + 存根
2. search_skills(query="modeling") → 找到相关技能
3. load_skill(skill_name="maya-geometry") → 加载技能
4. tools/list → 现在看到 maya-geometry 的完整工具
5. tools/call → 直接使用工具