Skip to content

DCC API Executor — 面向巨型 API 的代码编排模式 ​

源码:python/dcc_mcp_core/dcc_api_executor.py · Issue #411

English

把 "Cloudflare 模式" 引入 DCC:只暴露两个工具——dcc_search 和 dcc_execute——二者合计约 500 token,即可覆盖整个 DCC Python API,而不必为 1500+ 个命令各注册一个 MCP 工具。

为什么 DCC 尤其需要这种模式

DCC大致 API 规模
Maya2000+ MEL / Python 命令
Houdini1500+ hou 方法
Blender800+ bpy 操作符
3ds Max1000+ pymxs 命令

逐个注册会把 tools/list 撑爆 Agent 上下文。代码编排把工具面恒定在:2 个工具,任意 DCC,任意规模。

参考:Anthropic《Building agents that reach production systems with MCP》(2026-04-22)——"当工具面很大时要按代码编排设计。Cloudflare 的 MCP server 是参考范例:两个工具(search/execute)在大约 1K token 内覆盖 2500 个端点。"

导入 ​

python
from dcc_mcp_core import (
    DccApiCatalog,
    DccApiExecutor,
    register_dcc_api_executor,
)

DccApiCatalog(dcc_name, commands=None, catalog_text=None) ​

可搜索的 DCC 命令目录,用于 dcc_search。

来源

  1. commands — 显式列表:[{"name": "polyCube", "signature": "...", "description": "..."}]
  2. catalog_text — 纯文本目录,一行一条:name - description。空行与 # 注释自动忽略。
  3. add_command(name, *, signature="", description="") — 运行期追加。

搜索 search(query: str, *, limit: int = 10) -> list[dict]

  • 按空白/标点切词
  • 丢弃停用词(the、a、an、in、for、of)
  • 按 name + description + signature 命中计数打分
  • Top-limit,分数同则按名称字典序
  • 零分项不返回

len(catalog) 返回命令条数。

DccApiExecutor(dcc_name, catalog=None, dispatcher=None) ​

2 工具包装器。

参数类型默认说明
dcc_namestr—DCC 标识
catalogDccApiCatalog | None空目录dcc_search 使用
dispatcherToolDispatcher | NoneNone提供时 dcc_execute 脚本内可用 dispatch(name, args)
persistent_namespaceboolFalse在多次 dcc_execute 调用间共享变量(见下)
script_execution_contextScriptExecutionContext | None兼容上下文持有共享命名空间

持久化命名空间(issue #2300) ​

默认情况下每次 dcc_execute 都从空命名空间开始,导入、缓存场景查询等开销会重复计算。 通过构造参数 persistent_namespace=True,或在调用参数中传入 "persistent_namespace": true 开启:

python
executor = DccApiExecutor("maya", persistent_namespace=True)
executor.execute_params({"code": "scene = load_scene_cache()\nreturn len(scene)"})
executor.execute_params({"code": "return scene['mesh_count']"})   # 复用 scene

仅变量命名空间持久:EvalContext 每次仍重建 dispatch、json 与受限的 __builtins__,沙箱约束不变;dispatch 等保留名不会写回。嵌套 def/class 内部的绑定保持局部。使用 clear_script_namespace() 清空共享状态。

执行成功时返回携带的变量:

json
{"context": {"persistent_namespace": {"enabled": true, "variables": ["scene"]}}}

.search(query, *, limit=10) -> dict ​

处理 dcc_search 调用:

json
{
  "success": true,
  "message": "Found 3 command(s) matching 'create sphere'.",
  "results": [
    {"name": "polySphere", "signature": "...", "description": "..."}
  ]
}

无匹配时返回 "results": [] 并附带建议使用 search_skills 的 "hint"。

.execute(code, *, timeout_secs=30) -> dict ​

处理 dcc_execute 调用。脚本在 EvalContext 中以 sandbox=True 执行;构造时传入 ToolDispatcher 后脚本可使用 dispatch()。

可能返回:

  • {"success": True, "output": <脚本 return 值>, "message": "..."}
  • {"success": False, "error": "...", "message": "Script timed out ..."}
  • {"success": False, "error": "...", "message": "Script failed ..."}

output 为脚本顶层 return 返回的值(最后一行表达式不会隐式返回)。

.execute_params({file_path, params, ...}) -> dict ​

需要反复调整参数时,只物化一次带类型的脚本,然后以结构化参数调用入口:

python
script = materialize_script(
    "def main(scale: float, copies: int = 1):\n"
    "    return {'value': scale * copies}\n",
    dcc_type="maya",
    instance_id="maya-local",
    session_id="task-42",
    reuse=True,
    reuse_key="scale-assets",
)
executor.execute_params({"file_path": script.file_path, "params": {"scale": 2.5, "copies": 4}})

物化结果会公开通过静态分析得到的 parameters_schema。execute_params 会从已验证的脚本文本重新推导该契约:旧 sidecar 缺少该字段时保持兼容,已存 schema 与脚本文本冲突时则关闭失败。参数在执行前完成校验,并在与无参数调用 相同的沙箱、dispatcher、owner thread 和 timeout 边界中传给 main(**params)。 入口注解仅允许保持 JSON 运行时语义的类型,固定 tuple 的每个位置及长度约束 都会执行。同一文件使用不同参数不会重写文件,并返回 context.materialized_script.reused=true。可选的 sha256 用于校验内容完整性。

register_dcc_api_executor(server, executor, *, search_tool_name="dcc_search", execute_tool_name="dcc_execute") -> None ​

在 server.start() 之前在 McpHttpServer 上注册这两个工具。工具名可覆盖,用于多 DCC gateway 消歧(maya_search、blender_search)。

端到端示例 ​

python
from dcc_mcp_core import (
    ToolRegistry, ToolDispatcher,
    McpHttpServer, McpHttpConfig,
    DccApiCatalog, DccApiExecutor, register_dcc_api_executor,
)

registry = ToolRegistry()
dispatcher = ToolDispatcher(registry)

catalog = DccApiCatalog(
    "maya",
    catalog_text="""
polyCube - Create a cube polygon mesh
polySphere - Create a sphere polygon mesh
select - Select nodes in the scene
render - Render the current frame
""",
)

executor = DccApiExecutor("maya", catalog=catalog, dispatcher=dispatcher)

server = McpHttpServer(registry, McpHttpConfig(port=8765))
register_dcc_api_executor(server, executor)
handle = server.start()
# tools/list 现在恰好包含 2 个条目:dcc_search、dcc_execute

Agent 典型使用流 ​

text
User  : "创建 5 个球体排成一行"
Agent : dcc_search({"query": "create sphere"})         → 命中 polySphere
Agent : dcc_execute({"code": "..."} )
        for i in range(5):
            dispatch("polySphere", {"position": [i, 0, 0]})
        return {"created": 5}
Server: { "output": {"created": 5}, "success": true }

只有最终脚本返回值进入模型——5 次中间 dispatch 不消耗 Token。

当前状态 ​

Python 助手已可用。Rust 级 dcc_search / dcc_execute 内建 MCP 工具跟踪于 issue #411。之前通过 register_dcc_api_executor(server, executor) 注册——Python 处理器行为完全一致。

参见 ​

Released under the MIT License.