Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ Label: "MCP"
import hou
import houdinimcp

_PORT = 9876

if hasattr(hou.session, "houdinimcp_server") and hou.session.houdinimcp_server:
houdinimcp.stop_server()
hou.ui.displayMessage("Houdini MCP Server stopped")
else:
houdinimcp.start_server()
hou.ui.displayMessage("Houdini MCP Server started on localhost:9876")
houdinimcp.start_server(_PORT)
hou.ui.displayMessage(f"Houdini MCP Server started on localhost:{_PORT}")

```

Expand Down Expand Up @@ -128,12 +130,17 @@ Add an entry:
"args": [
"run",
"python",
"C:/Users/<YourUserName>/Documents/houdini19.5/scripts/python/houdinimcp/houdini_mcp_server.py"
"C:/Users/<YourUserName>/Documents/houdini19.5/scripts/python/houdinimcp/houdini_mcp_server.py",
"--port",
"9876"
]
}
}
}
```
Note: The port number must match between the shelf tool and Claude Desktop configuration.
If you change it in one place, make sure to update it in the other.

if uv run was successful and claude failed to load mcp, make sure claude is using the same python version, use:
```cmd
python -c "import sys; print(sys.executable)"
Expand All @@ -152,6 +159,7 @@ you will need a Rapid API key to log in. Create an account at: [RapidAPI](https:
Subscribe to OPUS API at: [OPUS API Subscribe](https://rapidapi.com/genel-gi78OM1rB/api/opus5/pricing)
Get your Rapid API key at [OPUS API](https://rapidapi.com/genel-gi78OM1rB/api/opus5)
add the key to urls.env
### 4 Acknowledgement

### 6 Acknowledgement

Houdini-MCP was built following [blender-mcp](https://github.com/ahujasid/blender-mcp). We thank them for the contribution.
22 changes: 14 additions & 8 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import hou
from .server import HoudiniMCPServer

def start_server():
def start_server(port=9876):
if not hasattr(hou.session, "houdinimcp_server") or hou.session.houdinimcp_server is None:
hou.session.houdinimcp_server = HoudiniMCPServer()
hou.session.houdinimcp_server = HoudiniMCPServer(port=port)
hou.session.houdinimcp_server.start()
else:
print("Houdini MCP Server is already running.")
Expand All @@ -15,13 +15,19 @@ def stop_server():
else:
print("Houdini MCP Server is not running.")

# Optionally auto-start
def initialize_plugin():
# Optional auto-start
# Note: This requires Houdini GUI mode (won't work in hython/hbatch)
#
# Usage:
# If you want the MCP server to start automatically when Houdini launches,
# add the following to your pythonrc.py:
#
# import houdinimcp
# houdinimcp.initialize_plugin(port=9876) # You can change the port number
#
def initialize_plugin(port=9876):
# Set up default session toggles if desired
if not hasattr(hou.session, "houdinimcp_use_assetlib"):
hou.session.houdinimcp_use_assetlib = False
# Auto-start server if you want:
start_server()

# If you want the plugin to auto-load on import:
initialize_plugin()
start_server(port)
32 changes: 23 additions & 9 deletions houdini_mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

This is the "bridge" or "driver" script that Claude will run via `uv run`.
It uses the MCP library (fastmcp) to communicate with Claude over stdio,
and relays each command to the local Houdini plugin on port 9876.
and relays each command to the local Houdini plugin on a configurable port.
"""
import sys
import os
import site
import argparse

# Get the directory where the script is located
script_dir = os.path.dirname(os.path.abspath(__file__))
Expand Down Expand Up @@ -442,7 +443,7 @@ def send_command(self, cmd_type: str, params: Dict[str, Any] = None) -> Dict[str
"""
if not self.connect():
# Instead of raising, return an error dict consistent with API errors
error_msg = "Could not connect to Houdini on port 9876."
error_msg = f"Could not connect to Houdini on port {self.port}."
logger.error(error_msg)
# Return structure similar to API failures
return {"status": "error", "message": error_msg, "origin": "mcp_server_connection"}
Expand Down Expand Up @@ -513,19 +514,20 @@ def send_command(self, cmd_type: str, params: Dict[str, Any] = None) -> Dict[str

# A global Houdini connection object
_houdini_connection: HoudiniConnection = None
_houdini_port: int = 9876 # Default port

def get_houdini_connection() -> HoudiniConnection:
"""Get or create a persistent HoudiniConnection object."""
global _houdini_connection
global _houdini_connection, _houdini_port
if _houdini_connection is None:
logger.info("Creating new HoudiniConnection.")
_houdini_connection = HoudiniConnection(host="localhost", port=9876)
logger.info(f"Creating new HoudiniConnection on port {_houdini_port}.")
_houdini_connection = HoudiniConnection(host="localhost", port=_houdini_port)

# Always try to connect, returns True if already connected or successful now
if not _houdini_connection.connect():
# Connection failed, reset _houdini_connection to allow retry next time?
_houdini_connection = None
raise ConnectionError("Could not connect to Houdini on localhost:9876. Is the plugin running?")
raise ConnectionError(f"Could not connect to Houdini on localhost:{_houdini_port}. Is the plugin running?")

return _houdini_connection

Expand Down Expand Up @@ -889,11 +891,23 @@ def get_opus_job_result(batch_job_id: str) -> dict:

def main():
"""Run the MCP server on stdio."""
global _houdini_port

# Parse command line arguments
parser = argparse.ArgumentParser(description='Houdini MCP Server Bridge')
parser.add_argument('--port', type=int, default=9876,
help='Port to connect to Houdini (default: 9876)')
args = parser.parse_args()

# Set the global port variable
_houdini_port = args.port
logger.info(f"Configured to connect to Houdini on port {_houdini_port}")

# Check necessary RapidAPI variables are set before running
if not RAPIDAPI_HOST_URL or not RAPIDAPI_HOST or not RAPIDAPI_KEY:
logger.critical("RAPIDAPI_HOST_URL, RAPIDAPI_HOST, and RAPIDAPI_KEY environment variables are not set. Please configure urls.env.")
logger.critical("Server will not start.")
sys.exit(1) # Exit if critical configuration is missing
logger.critical("RAPIDAPI_HOST_URL, RAPIDAPI_HOST, and RAPIDAPI_KEY environment variables are not set. Please configure urls.env.")
logger.critical("Server will not start.")
sys.exit(1) # Exit if critical configuration is missing

logger.info(f"Using RapidAPI Host URL: {RAPIDAPI_HOST_URL}")
logger.info(f"Using RapidAPI Host Header: {RAPIDAPI_HOST}")
Expand Down