houdini
Allows interaction with a local Houdini session, providing tools to inspect the current scene, query node status, and execute VEX/Python in the Houdini main thread to create and modify nodes and parameters.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@houdinicreate a box of size 2 in the current scene"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
dsh-houdini-bridge
把本机正在跑的 Houdini 接进 dsh:侧栏底部一个入口(用的是 houdini.exe 自己的图标),
点开就能看到连没连上、哪个 hip 开着、选中了什么、哪些节点在报错,并用一个「自动部署」按钮
把整条链路装好;输入框里 @Houdini 一下,就等于把"接下来这些话是对这个 Houdini 说的、
请用 MCP 工具真的去做"写进了每一轮上下文。
dsh Web GUI
├─ 侧栏面板(部署 + 状态)┐
├─ 输入框 @ 引用 ─────────┼── HTTP ──┐
│ │ │
└─ mcp-client 插件 ── MCP · stdio ────┤
│
dsh-houdini-bridge 宿主半侧 / houdini-mcp.mjs
│ HTTP · 127.0.0.1:8765
│
houdini_link.py (跑在 Houdini 进程里,零依赖)
│ hdefereval
hou 模块 —— 只能在主线程碰为什么绕三层:hou 只能在 Houdini 进程内部 import,外面拿不到。为什么要有 hdefereval 那一跳:
hou 不是线程安全的,HTTP 请求跑在子线程,直接调 hou.* 会让 Houdini 崩。
组成
文件 | 位置 | 作用 |
| dsh 宿主进程 | 三条只读路由 + 一个部署动作路由(写 Houdini 目录与 profile patch) |
| 浏览器 | 侧栏底部的入口 + 部署面板,以及输入框 |
| 宿主 / CLI 共用 | 钩子与 patch 文本的生成与识别(纯字符串,可单测) |
| 拷进 Houdini 的 | 监听回环端口,把请求送回主线程执行 |
| 任意位置,dsh 会 spawn 它 | MCP 协议 ⇄ HTTP |
| 随包 | 从 |
| 开发用 | 从 exe 的 RT_ICON 资源抽图标 → PNG(见下面「图标」一节) |
| 开发用 | 把 PNG 内联进 |
profile 的 |
| 让 dsh 连上上面那个 MCP server |
面板和 MCP 是分开的两件事,故意如此:面板走插件自己的路由,MCP 走
@deepseek-ai/dsh-mcp-client 那一行。serverName 同一处挂两次会撞,所以插件里不挂 MCP。
好处是:MCP 那一行配错了,面板照样告诉你 Houdini 通不通。
Related MCP server: fxhoudinimcp
装
dsh plugin --profile web add github:JUSTDOITzhw/dsh-houdini-bridge正在改这个插件本身时用 add link:<这个目录> —— 源码目录直接进 profile,
改完客户端半侧约 1 秒自动生效,不用重装。
装完重启 dsh(新增插件必须重启,改已装插件的客户端代码才不用)。重启后打开 侧栏底部 → Houdini → 自动部署,下面三件事一次做完:
做什么 | 写到哪 | 为什么 |
装服务文件 |
| 让 |
开启动自启 |
| Houdini 一启动就自动起服务,不用每次手敲 |
挂 MCP | 装了本插件的 profile 的 | 聊天里出现 |
<Houdini 用户目录> 是问出来的,不是猜出来的
按 ~/Documents/houdiniX.Y 猜,在「文档」被重定向的机器上是死路 —— 本机就是这样:文档其实在
D:\文档,于是脚本被装进 C:\Users\<用户>\Documents\houdini20.5 这个 Houdini 从来不读的
空壳目录:面板显示"装好了",打开 Houdini 却永远连不上(心跳日志一条都没有)。现在按可信度
往下问四级(实现在 lib/discover.mjs):
配置里写死的
houdiniDirs—— 硬指定,连问都不问;环境变量
HOUDINI_USER_PREF_DIR;注册表找到安装目录 → 用它自带的
hython问$HOUDINI_USER_PREF_DIR—— 权威来源: 连版本与内置 Python 版本一起问出来,于是houdiniX.Y、pythonX.Ylibs、scripts/python全都不用再查表(手抄的对照表迟早会在某个新版本上过期);都问不到:Windows「已知文件夹」里的文档目录(重定向也算进去) →
~/Documents/houdiniX.Y→~/houdiniX.Y,以及它们下面同主版本的houdini*目录。
探到权威答案就只装那一个目录,探不到才退回"存在的候选都装一份"。安装目录从
HKLM\SOFTWARE\Side Effects Software 来,拿不到就扫 Program Files / /opt/hfs* / PATH。
面板与 install.mjs 共用同一份逻辑,不会出现"命令行装对了、面板装错了"。
排障第一眼看面板「部署」卡片里的来源(hython / env / documents / fallback)——
它直接说明路径是怎么定下来的。想让它别去起 hython(CI、需要可复现的测试)就设
DSH_HOUDINI_NO_DISCOVER=1。
⚠️ 钩子必须落在
pythonX.Ylibs/里,不是用户目录根上的pythonrc.py。Houdini 只在pythonX.Ylibs/pythonrc.py里找启动脚本(X.Y= 它内置的 Python,20.5 是python3.11libs); 放根上那份根本不会被执行。实测方式:把探针分别放到根上和python3.11libs/里跑hython与houdinifx—— 根上那份毫无动静,libs 里那份立刻生效。 所以插件按"用户目录里已有的python*.libs→ 问到的内置 Python 版本 → Houdini 版本查表 → 兜底全写"四级决定落点;0.2 早期误写在根上的那份会在下次部署时改名留档 (pythonrc.py.bak-<时间>,只在那个文件整个就是我们的钩子时才动它)。⚠️ 钩子插在文件最前面(只让开 shebang 与编码声明)。
pythonrc.py里任何一行抛错, 它后面的代码就全部不执行 —— 追加在末尾等于把自己吊在别人那行绳子上。这条不是理论: 0.3.0 就栽过一次,文件头上有一行写文件的探针,探针的目录被删之后整份pythonrc.py从第一行崩起,钩子一次都没跑到,而面板还显示"启动自启已开"。
钩子到底跑没跑:心跳
每执行一次 start_if_gui() 就往系统临时目录的 dsh-houdini-bridge-autostart.log 追加一行:
2026-09-17T16:36:49 pid=12728 gui=False result=skipped-not-gui version=0.3.1
2026-09-17T16:39:28 pid=22020 gui=True result=started version=0.3.1result 有四种:started(起了)/ already(本来就在跑)/ skipped-not-gui(命令行会话,
按设计跳过)/ failed(起失败)。面板「部署」卡片底部会把最近一条翻成人话,例如
「自启钩子上次执行:16:39 · 已起服务」。
为什么值得单独记一份:文件里有没有钩子只说明写进去了,心跳才说明执行到了。 两种失效在现场长得一模一样 —— 装好了、Houdini 开着、服务却不通 —— 只有这份日志能把 "没跑到"和"跑了没起来"分开。
每一步都是幂等的(内容没变就不写盘),每次改写前都会留一份带时间戳的 .bak-<时间>。
面板上还有三个单项按钮(只装服务文件 / 开关启动自启 / 挂或摘 MCP),想只做一半也行。
dsh 的 patch 文件是热加载的(profile 里
patchReload: live),所以写完通常不用重启 dsh,MCP 工具会自己出现。万一没出现,重启一次 dsh。
手动装(不想要面板代劳时)
node install.mjs --auto-start # 装服务文件 + 写自启钩子(--dry-run 只看不写)
node install.mjs --patch # 只打印要贴进 profile 的那段 YAML然后在 Houdini 的 Python Shell 里。下面这行不依赖任何路径配置,最稳:
exec(open(r"<这个目录>\houdini\houdini_link.py", encoding="utf-8").read())
start()装的位置正好落在 Houdini 的搜索路径里,就可以短一点:import dsh_houdini_link as L; L.start()
Houdini 的用户目录不一定是
~/Documents/houdiniX.Y—— 设了HOUDINI_USER_PREF_DIR就以那个为准。本机实测~/houdini20.5与~/Documents/houdini20.5同时存在, 所以部署时存在的houdini<主版本>.*目录会都装一份(装一份不生效也不碍事,漏装才麻烦)。
面板
侧栏底部(在「设置」上面,和 GitHub 面板相邻):
◦ Houdini 未部署 ← 点了才展开┌ Houdini 联动 v0.4.0 ×
│ 连接
│ 已连接 · 20.5.370 · hero_shot_010.hip(未保存)· 帧 42 · 选中 2
│ 127.0.0.1:8765 · 主线程 hdefereval · token 关 · pid 4242 [重新检测]
│ 部署
│ 已部署(1 个 Houdini 目录 · MCP 已挂)
│ 装到哪:问的是装好的 Houdini 自己 · Houdini 20.5 · 内置 Python 3.11
│ houdini20.5 模块 0.4.0 · 启动自启已开
│ D:\文档\houdini20.5 → python3.11libs/pythonrc.py
│ dsh profile:web MCP 已挂载
│ 自启钩子上次执行:16:39 · 已起服务
│ [自动部署] [只装服务文件] [关掉启动自启] [摘掉 MCP]
│ 怎么用
│ 自动部署做三件事:……
└那一行的图标是 houdini.exe 自己那一枚(橙色方块 + 螺旋),不是手画的近似图形;
详见下面「图标」。
点有三种颜色:绿=连上了,灰=装好了但 Houdini 没开,橙=还没部署,红=宿主路由本身出错。
每 8 秒问一次,只为读几个标量;标签页在后台时不问。
Houdini 没开时是灰点 + 一句「在 Houdini 里跑 start() 之后,127.0.0.1:8765 就会亮」, 不弹窗、不变红抢注意力。
报错那块要遍历节点树,比概览贵一个数量级,所以是打开面板时才拉。
面板只读。改场景是 MCP 那几个工具的活;唯一会写盘的是「部署」那一区。
输入框里 @Houdini
打 @,菜单里就有一组 Houdini(order: 1,排在文件/会话之后、其它插件之前,
不用滚就能看到);打完 @houdini,别的组因为筛不到会自动消失,只剩这两行里属于我们的那条:
Houdini
[图标] Houdini 20.5.445 · hero_shot_010.hip ›回车把它插成草稿里的一个 chip(@ 完接着说要做什么就行);按 Tab 或点右边的 › 进下一层,
是一排常用起点:
Houdini · 常用
[图标] 查看场景 结构、选中与报错
[图标] 搭节点 创建并连好线
[图标] 调参数 改参数并说明改动
[图标] 修报错 定位并修掉节点报错
[图标] 写脚本 用 hou 脚本完成这次操作这个 chip 给模型的不是"一个名字",而是一段说明:它带上 Houdini 连没连上、版本、当前 hip、
端点地址,以及"请用 mcp__houdini__* 真的去动它,不要只给步骤或示例代码,动手前后各读一次
场景"—— 所以不必每句话都重新交代一遍。下钻里挑了某一条时,还会多一句"用户选定的做法:……"。
没连上时说的是另一套:明说服务没在跑、这些工具会失败,别假装看到了场景。
发出去之后,面板里看到的是什么(0.3.2)
那段说明不该钉在某一条消息上:用户中途把 Houdini 关了,旧消息里那句"已连上"就成了谎话; 而且面板会把整段 XML 原样显示,看着就是一坨字符。0.3.2 把两件事拆开:
谁 | 放在哪 | 什么时候求值 |
那行引用 | 消息里( | 发送那一刻 |
上面那段说明 | 宿主的系统提示词段 | 每一步都重新求值 |
于是面板里只剩一行引用。引用是 dsh 自己投影出来的(消息里任何 @词 都会变成带
data-ref-chip 与 title 的节点),配一条 CSS 就能把官方那枚 file 图标换成 Houdini 自己的:
[data-ref-chip][title="@Houdini"] [class*="_refIcon_"]{display:none}
[data-ref-chip][title="@Houdini"]::before{content:"";width:1em;height:1em;margin-right:4px;vertical-align:-.125em;background:url(<图标>) center/contain no-repeat}输入框里那个 chip 同理 —— 它的 @ 是个字符节点(.yAWgPa_marker),title 是我们给的
label,所以四条选择器一起把"字符 / 官方图标"换成真图标,别家引用(@/some/dir/)纹丝不动。
为什么只能这么换:ReferenceChip 是 appearance === undefined ? "@" : <ReferenceIcon kind={appearance}/>,
而 ReferenceIcon 是个没有 default 的 switch(只认 session/file/folder)—— 给它自定义 kind
只会什么都不画,插件侧也没有消息渲染扩展点(conversation.chat.node 是 keyed 槽,覆盖 user
等于把 markdown/图片/fork 全自己重写一遍)。CSS 是唯一贴得上去的地方。
⚠️ 宿主半侧不热替换:改完 index.js 要重启 dsh,/state 里才会出现 prompt: true。
在那之前 serialize 自动退回长版(@Houdini + 那段 XML)—— 指令一句不少,只是面板里还带着字符。
那一行的状态 15 秒才重新问一次宿主:菜单每敲一个字都会开合,不值得每次都打一趟。
既不是这一组、也不是
houdini的前缀时,这一组整体不出现 —— 免得用户@找文件时被插队。下钻后筛不到时给一行说明(而不是空白列表),回车回到上一层。
打
@houdini/后接着打中文也会当筛选词(@houdini修→ 只剩「修报错」); 长别名直接吞词是特意的,否则余下的字会把本组筛空,而"所有组都空"会让菜单在手指底下整个关掉。
配置(profile 的 patch 行里加 config:,全部可选):
- id: houdini-bridge
name: dsh-houdini-bridge
config:
host: 127.0.0.1
port: 8765
token: '' # Houdini 侧开了 token 校验时填同一个
timeoutMs: 2500 # ping 的超时
sceneTimeoutMs: 15000
errorTimeoutMs: 30000
houdiniDirs: [] # 部署目标;留空 = 自己扫(HOUDINI_USER_PREF_DIR 优先)
houdiniVersion: '20.5'
home: '' # dsh home;留空 = $DSH_HOME,再退回 ~/.dsh工具
dsh 里会多出四个工具,名字前缀 mcp__houdini__:
工具 | 干什么 |
| Houdini 连着吗、当前文件、帧、选中数量 |
| hip 路径、未保存标记、帧与范围、选中节点(含非默认参数)、各上下文节点 |
| 所有带错误/警告的节点及消息 |
| 在 Houdini 主线程里跑一段 Python |
exec 是主力:hou 已导入,print() 的输出和 RESULT 变量的值都会带回来。
# 模型写这种代码就能干活
n = hou.node('/obj').createNode('geo', 'hero')
box = n.createNode('box')
box.parm('size').set(2)
RESULT = n.path()端点
Houdini 侧只提供四个:
方法 | 路径 | 说明 |
GET |
| 存活、版本、文件、帧、选中数、服务自己那一版的版本号 |
GET |
| 场景概览 |
GET |
| 报错与警告 |
POST |
|
|
dsh 宿主侧四个(只允许本机同源访问):
方法 | 路径 | 说明 |
GET |
| 上面的 |
GET |
| 上面的 |
GET |
| 上面的 |
POST |
|
|
action 认这六个值:setup.all、setup.install、setup.autostart.on、setup.autostart.off、
setup.mcp.on、setup.mcp.off。
手动验:
curl -s http://127.0.0.1:8765/ping
curl -s http://127.0.0.1:3080/api/houdini-bridge/state
curl -s -X POST http://127.0.0.1:3080/api/houdini-bridge/action -H "content-type: application/json" -d "{\"action\":\"setup.all\"}"
curl -s -X POST http://127.0.0.1:8765/exec -H "content-type: application/json" -d "{\"code\":\"RESULT = hou.frame()\"}"配置
插件(profile 的 cordis.patch.yml 里按 row id 覆盖,patch 替换整行 config,字段要写全):
houdiniDirs:硬指定要装进哪些用户目录(留空 = 按上面那四级去问/去推)。houdiniVersion:留空 = 以装好的 Houdini 自己报的为准;填了(如"20.5")就按它组织houdiniX.Y目录名与pythonX.Ylibs。host/port/token/timeoutMs/sceneTimeoutMs/errorTimeoutMs、home、scriptPath见index.js的DEFAULTS。
环境变量
DSH_HOUDINI_NO_DISCOVER=1会让发现层完全不起子进程(不读注册表、不跑hython),退回到"环境变量 + 老兜底"的老行为 —— CI 与需要可复现的测试用得上。
Houdini 侧:start(port=8765, host="127.0.0.1", token=None)
token:设了就要求请求带X-DSH-Token;MCP 侧用环境变量DSH_HOUDINI_TOKEN对齐。DSH_HOUDINI_URL:MCP 侧连的地址,默认http://127.0.0.1:8765。DSH_HOUDINI_TIMEOUT_MS:单次调用超时,默认 120000。
Houdini 侧另外还有 start_if_gui():和 start() 一样,但非图形界面会话(hython / hbatch)
里静默跳过。pythonrc.py 在命令行 Houdini 里同样会被执行,用 start() 会让每个批处理进程
都去抢 8765,所以自动启动钩子调的是它。它每次被调用都写一行心跳(见上面「钩子到底跑没跑」)。
pythonrc.py 跑得很早(官方文档说"UI 就绪之前"),所以特意实测过 hou.isUIAvailable() 在那一刻
的取值:真开一次 GUI Houdini,5 秒内 8765 就答上了,说明在图形会话里它那时已经是 true,
钩子不会被误判成批处理而跳过。
单个返回值(stdout 或 RESULT)超过 20 万字符会被换成一句提示,避免把 JSON 撑爆。
排错
连不上 —— Houdini 里没跑 start(),或者用的是命令行 hython(下面这条)。
开了 Houdini 但还是没自启 —— 按面板「部署」卡片里的几行往下查,从便宜到贵:
装到哪个目录了,凭什么:卡片里那行列的用户目录,以及来源一行 ——
hython= 问装好的 Houdini 问出来的(最可信);env= 环境变量指过去的;documents= 从「已知文件夹」的文档目录推的;fallback= 没问到,目录是猜的, 第一个要怀疑的就是它。想固定住就设houdiniDirs,想看清 CLI 判断过程就跑node install.mjs --dry-run。落点对不对:那行列的是
<用户目录>/python3.11libs/pythonrc.py还是<用户目录>/pythonrc.py。后者是 0.2 早期的错位置,Houdini 不读;点一次「自动部署」就会 挪到对的地方并把旧的改名留档。钩子在不在文件最前面:行尾若写着「但排在文件后段」,说明它排在别人代码后面 —— 别人抛一次错它就没了。重新部署会把它挪到最前。
心跳怎么说(最有信息量的一条):卡片底部那行「自启钩子上次执行:……」
「还没被执行过」 ⇒ 这份
pythonrc.py根本没被读到:查落点,或那个 Houdini 是在 部署之前就开着的(钩子只在下一次启动时才生效,重开一次)。「命令行会话,按设计跳过」 ⇒ 那次是 hython / hbatch 起的,属正常;开个图形界面的就有了。
「起失败」 ⇒ 钩子跑到了、
start()抛了,原因在 Houdini 控制台的[dsh-houdini] 自动启动失败:那一行。时间戳比当前 Houdini 的启动时间早 ⇒ 这次启动没走到钩子(同上第 1、2 条)。
心跳文件在系统临时目录(Windows:%TEMP%\dsh-houdini-bridge-autostart.log),只留最近 50 行。
正在开着的 Houdini不会补跑钩子 —— 钩子只在启动那一刻执行一次。不想关掉当前场景的话,
在它的 Python Shell 里跑 import dsh_houdini_link; dsh_houdini_link.start() 即可。
RuntimeError: 这个 Houdini 会话里既没有 hdefereval…… —— 服务必须在图形界面的
Houdini 里跑。hython / 命令行模式两者都没有,起不来。
自动部署报"没有找到装了本插件的 profile" —— 插件在 package.json 的 bundles 里,
但 profile 目录名不是 web 且 manifest 里找不到 dsh-houdini-bridge。检查
~/.dsh/profiles/*/package.json。
卡住不动 —— 两个可能:
Houdini 主线程忙(渲染、求解),请求排在队列里等。等到超时就返回"调用超时"。
你在 Houdini 的 Python Shell 里同步发了个 HTTP 请求给自己。主线程在等响应,
hdefereval就没机会执行 → 死锁。要测就用外部工具(curl)或另开线程。
@ 里没有 Houdini —— 三种可能,从便宜的查起:
外壳的
inputTriggers没就绪。这个服务的注册走ctx.get+ctx.inject两条路, 都没成的话只有@这一块没有,侧栏入口照常在。看控制台有没有[dsh-houdini-bridge] @ 来源没挂上。你在打别的词。这一组只在"像是在打 houdini"时出现(是它的前缀), 否则整体不出现 —— 这是有意的,免得你
@找文件时被插一行。新装的插件没重启。客户端 bundle 热替换只对已装插件生效; 首次安装要重启 dsh(模块清单在启动时快照)。
改了 houdini_link.py 要重启 —— 模块已经在 Houdini 里 import 过了,重新
L.stop() + importlib.reload(L) + L.start(),或者重开 Houdini(有自启钩子的话自动就起)。
开发
node test/smoke.mjs # MCP 侧,17 项
node test/plugin.mjs # 插件两侧(宿主路由 + 部署 + 浏览器纯函数 + @ 来源),101 项
hython.exe test/houdini_side_check.py # Houdini 侧,25 项(要真 hou)
node test/fake-houdini.mjs # 起个假 Houdini,不用开真家伙也能看面板smoke.mjs 起一个假的 Houdini HTTP 服务,把 MCP server 完整喂一遍协议。
plugin.mjs 把宿主的四个路由(含回环守卫、token、超时、坏响应)打一遍,把部署动作真的
写进一个临时目录再逐项断言(幂等、备份、不动别人的条目),再用 loader 桩把客户端 bundle
物化出来断言纯函数、@ 来源的候选与序列化、以及样式约束。
houdini_side_check.py 用真的 hou 验读写、异常处理与长度保护;主线程调度那一段
只在图形界面里成立,所以在命令行模式下它顺带验证"报错说得清楚",以及
start_if_gui() 在 hython 里确实静默跳过。
图标
侧栏入口和 @ 菜单里用的是 houdini.exe 自己的图标,不是手画的:
node tools/extract-icon.mjs "D:/Houdini/bin/houdini.exe" assets/houdini-icon.png
node tools/inject-icon.mjs # 把 PNG 内联进 lib/client.js(幂等)extract-icon.mjs 自己解析 PE 资源:DOS 头 → .rsrc 节 → 资源目录三级 →
RT_GROUP_ICON 挑最大那一档 → 从 RT_ICON 读 DIB(BITMAPINFOHEADER 的
高度字段是两倍行数,下面还跟着一层 AND 掩码)→ 自己编 PNG(zlib + CRC32)。
没有走 System.Drawing:本机的安全策略把 Add-Type(连 -AssemblyName)拦掉了。
另外这台机器的 houdini*.exe 图标全是 DIB,所以"扫 PNG 签名"那个取巧办法一块都扫不到。
内联而不是走宿主路由,是因为 @ 菜单是浮层,外链图片会让它先闪一下空图;
代价是 lib/client.js 大约大 8KB。换图标就重跑上面两条。
This server cannot be deployed
Maintenance
Related MCP Connectors
Your org's AI agents, tasks, runs, search, and brain files as MCP tools and resources.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to control Unreal E…
Use AI models for chat, image, and video generation from Claude Code and other MCP hosts.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables natural language control of SideFX Houdini for tasks including node management, parameter editing, and geometry inspection. It leverages RPYC to execute Python scripts and manage scene data through an MCP-compatible interface.MIT
- AlicenseCqualityAmaintenanceComprehensive MCP server for SideFX Houdini, offering 168 tools across 19 categories to enable natural-language control of scene building, simulation, rendering, and more via AI assistants.2061,511 PyPI238MIT
- FlicenseNot gradedqualityBmaintenanceEnables AI assistants to control Houdini by executing Python code, querying scene information, and creating nodes via the Model Context Protocol.2-
- AlicenseCqualityBmaintenanceConnects SideFX Houdini to Claude via the Model Context Protocol, enabling control of Houdini scenes, nodes, rendering, and more through 166 MCP tools.100442 PyPI4MIT