更新 maya_mcp_plugin.py
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Maya MCP Plugin
|
Maya MCP Plugin
|
||||||
This plugin provides integration with the Model Context Protocol for Maya.
|
This plugin provides integration with the Model Context Protocol for Maya.
|
||||||
@@ -16,6 +19,8 @@ import os
|
|||||||
import maya.api.OpenMaya as om
|
import maya.api.OpenMaya as om
|
||||||
import maya.cmds as cmds
|
import maya.cmds as cmds
|
||||||
import traceback
|
import traceback
|
||||||
|
import time
|
||||||
|
import inspect
|
||||||
|
|
||||||
# Plugin information
|
# Plugin information
|
||||||
def maya_useNewAPI():
|
def maya_useNewAPI():
|
||||||
@@ -24,6 +29,320 @@ def maya_useNewAPI():
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Global variables
|
||||||
|
_mcp_menu = None
|
||||||
|
|
||||||
|
def create_menu():
|
||||||
|
"""
|
||||||
|
Create the MCP menu in Maya
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Menu name if created successfully, None otherwise
|
||||||
|
"""
|
||||||
|
global _mcp_menu
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Delete existing menu if it exists
|
||||||
|
if _mcp_menu and cmds.menu(_mcp_menu, exists=True):
|
||||||
|
cmds.deleteUI(_mcp_menu)
|
||||||
|
|
||||||
|
# Get main window
|
||||||
|
main_window = "MayaWindow"
|
||||||
|
|
||||||
|
# Create menu
|
||||||
|
_mcp_menu = cmds.menu("MCPMenu", label="MCP", parent=main_window)
|
||||||
|
|
||||||
|
# Add menu items
|
||||||
|
cmds.menuItem(label="Start Server", command=lambda x: start_server_cmd())
|
||||||
|
cmds.menuItem(label="Stop Server", command=lambda x: stop_server_cmd())
|
||||||
|
cmds.menuItem(label="Restart Server", command=lambda x: restart_server_cmd())
|
||||||
|
cmds.menuItem(divider=True)
|
||||||
|
cmds.menuItem(label="Configure Port", command=lambda x: configure_port_cmd())
|
||||||
|
cmds.menuItem(divider=True)
|
||||||
|
cmds.menuItem(label="About", command=lambda x: about_cmd())
|
||||||
|
|
||||||
|
om.MGlobal.displayInfo("MCP menu created successfully")
|
||||||
|
return _mcp_menu
|
||||||
|
except Exception as e:
|
||||||
|
om.MGlobal.displayError(f"Error creating MCP menu: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def start_server_cmd(*args):
|
||||||
|
"""Command to start the MCP server"""
|
||||||
|
try:
|
||||||
|
import server
|
||||||
|
|
||||||
|
if server.is_server_running():
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="MCP Server",
|
||||||
|
message="MCP server is already running",
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
port = server.start_server()
|
||||||
|
|
||||||
|
if port:
|
||||||
|
cmds.inViewMessage(
|
||||||
|
message=f"MCP server started on port {port}",
|
||||||
|
position="topCenter",
|
||||||
|
fade=True,
|
||||||
|
fadeOutTime=5.0,
|
||||||
|
backColor=(0.2, 0.6, 0.2),
|
||||||
|
fontSize=24
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="MCP Server Error",
|
||||||
|
message="Failed to start MCP server",
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Error starting MCP server: {e}"
|
||||||
|
om.MGlobal.displayError(error_msg)
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="MCP Server Error",
|
||||||
|
message=error_msg,
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
|
||||||
|
def stop_server_cmd(*args):
|
||||||
|
"""Command to stop the MCP server"""
|
||||||
|
try:
|
||||||
|
import server
|
||||||
|
|
||||||
|
if not server.is_server_running():
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="MCP Server",
|
||||||
|
message="MCP server is not running",
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
success = server.stop_server()
|
||||||
|
|
||||||
|
if success:
|
||||||
|
cmds.inViewMessage(
|
||||||
|
message="MCP server stopped",
|
||||||
|
position="topCenter",
|
||||||
|
fade=True,
|
||||||
|
fadeOutTime=5.0,
|
||||||
|
backColor=(0.6, 0.2, 0.2),
|
||||||
|
fontSize=24
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="MCP Server Error",
|
||||||
|
message="Failed to stop MCP server",
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Error stopping MCP server: {e}"
|
||||||
|
om.MGlobal.displayError(error_msg)
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="MCP Server Error",
|
||||||
|
message=error_msg,
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
|
||||||
|
def restart_server_cmd(*args):
|
||||||
|
"""Command to restart the MCP server"""
|
||||||
|
try:
|
||||||
|
import server
|
||||||
|
|
||||||
|
# Stop the server
|
||||||
|
om.MGlobal.displayInfo("Stopping MCP server...")
|
||||||
|
server.stop_server()
|
||||||
|
|
||||||
|
# Wait for a moment to ensure the server is fully stopped
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Start the server
|
||||||
|
om.MGlobal.displayInfo("Starting MCP server...")
|
||||||
|
port = server.start_server()
|
||||||
|
|
||||||
|
if port:
|
||||||
|
cmds.inViewMessage(
|
||||||
|
message=f"MCP server restarted on port {port}",
|
||||||
|
position="topCenter",
|
||||||
|
fade=True,
|
||||||
|
fadeOutTime=5.0,
|
||||||
|
backColor=(0.2, 0.6, 0.2),
|
||||||
|
fontSize=24
|
||||||
|
)
|
||||||
|
om.MGlobal.displayInfo(f"MCP server successfully restarted on port {port}")
|
||||||
|
else:
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="MCP Server Error",
|
||||||
|
message="Failed to restart MCP server",
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
om.MGlobal.displayError("Failed to restart MCP server")
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Error restarting MCP server: {e}"
|
||||||
|
om.MGlobal.displayError(error_msg)
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="MCP Server Error",
|
||||||
|
message=error_msg,
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
|
||||||
|
def configure_port_cmd(*args):
|
||||||
|
"""Command to configure server port"""
|
||||||
|
try:
|
||||||
|
# Import port_config to get current port
|
||||||
|
import port_config
|
||||||
|
current_port = port_config.SERVER_PORT
|
||||||
|
|
||||||
|
# Show dialog to input new port
|
||||||
|
result = cmds.promptDialog(
|
||||||
|
title='Configure MCP Port',
|
||||||
|
message='Enter new port number:',
|
||||||
|
text=str(current_port),
|
||||||
|
button=['OK', 'Cancel'],
|
||||||
|
defaultButton='OK',
|
||||||
|
cancelButton='Cancel',
|
||||||
|
dismissString='Cancel'
|
||||||
|
)
|
||||||
|
|
||||||
|
if result == 'OK':
|
||||||
|
# Get the new port number
|
||||||
|
new_port_str = cmds.promptDialog(query=True, text=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_port = int(new_port_str)
|
||||||
|
|
||||||
|
# Validate port number
|
||||||
|
if new_port < 1024 or new_port > 65535:
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="Invalid Port",
|
||||||
|
message="Port number must be between 1024 and 65535",
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get the plugin path using a more reliable method
|
||||||
|
import sys
|
||||||
|
plugin_path = None
|
||||||
|
for path in sys.path:
|
||||||
|
if path.endswith('MayaMCP') and os.path.exists(path):
|
||||||
|
plugin_path = path
|
||||||
|
break
|
||||||
|
|
||||||
|
if not plugin_path:
|
||||||
|
# Fallback to a hardcoded path if needed
|
||||||
|
plugin_path = "D:/Dev/Tools/MayaMCP"
|
||||||
|
if not os.path.exists(plugin_path):
|
||||||
|
raise Exception(f"Could not find plugin path: {plugin_path}")
|
||||||
|
|
||||||
|
om.MGlobal.displayInfo(f"Using plugin path: {plugin_path}")
|
||||||
|
|
||||||
|
# Update port_config.py file
|
||||||
|
port_config_path = os.path.join(plugin_path, "port_config.py")
|
||||||
|
om.MGlobal.displayInfo(f"Port config path: {port_config_path}")
|
||||||
|
|
||||||
|
# Read the current content
|
||||||
|
with open(port_config_path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# Replace the port number
|
||||||
|
import re
|
||||||
|
new_content = re.sub(
|
||||||
|
r'SERVER_PORT\s*=\s*\d+',
|
||||||
|
f'SERVER_PORT = {new_port}',
|
||||||
|
content
|
||||||
|
)
|
||||||
|
|
||||||
|
# Write the updated content
|
||||||
|
with open(port_config_path, 'w') as f:
|
||||||
|
f.write(new_content)
|
||||||
|
|
||||||
|
# Reload port_config module to apply changes
|
||||||
|
import importlib
|
||||||
|
if 'port_config' in sys.modules:
|
||||||
|
importlib.reload(sys.modules['port_config'])
|
||||||
|
om.MGlobal.displayInfo("Reloaded port_config module")
|
||||||
|
# Update global variable
|
||||||
|
import port_config
|
||||||
|
port_config.SERVER_PORT = new_port
|
||||||
|
om.MGlobal.displayInfo(f"Updated SERVER_PORT to {port_config.SERVER_PORT}")
|
||||||
|
|
||||||
|
# Ask if user wants to restart the server
|
||||||
|
result = cmds.confirmDialog(
|
||||||
|
title="Port Updated",
|
||||||
|
message=f"Port has been updated to {new_port}.\n\nDo you want to restart the server now?",
|
||||||
|
button=["Yes", "No"],
|
||||||
|
defaultButton="Yes",
|
||||||
|
cancelButton="No",
|
||||||
|
dismissString="No"
|
||||||
|
)
|
||||||
|
|
||||||
|
if result == "Yes":
|
||||||
|
# Stop current server
|
||||||
|
import server
|
||||||
|
om.MGlobal.displayInfo("Stopping current server...")
|
||||||
|
server.stop_server()
|
||||||
|
|
||||||
|
# Wait for server to fully stop
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Reload server module
|
||||||
|
if 'server' in sys.modules:
|
||||||
|
importlib.reload(sys.modules['server'])
|
||||||
|
om.MGlobal.displayInfo("Reloaded server module")
|
||||||
|
|
||||||
|
# Reload http_handler module
|
||||||
|
if 'http_handler' in sys.modules:
|
||||||
|
importlib.reload(sys.modules['http_handler'])
|
||||||
|
om.MGlobal.displayInfo("Reloaded http_handler module")
|
||||||
|
|
||||||
|
# Start server on new port
|
||||||
|
om.MGlobal.displayInfo(f"Starting server on new port {new_port}...")
|
||||||
|
port = server.start_server()
|
||||||
|
|
||||||
|
if port == new_port:
|
||||||
|
cmds.inViewMessage(
|
||||||
|
message=f"MCP server restarted on port {port}",
|
||||||
|
position="topCenter",
|
||||||
|
fade=True,
|
||||||
|
fadeOutTime=5.0,
|
||||||
|
backColor=(0.2, 0.6, 0.2),
|
||||||
|
fontSize=24
|
||||||
|
)
|
||||||
|
om.MGlobal.displayInfo(f"Server successfully restarted on port {port}")
|
||||||
|
else:
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="Port Mismatch",
|
||||||
|
message=f"Server started on port {port}, but requested port was {new_port}",
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
om.MGlobal.displayInfo(f"MCP port updated to {new_port}. Please restart the server manually.")
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="Invalid Input",
|
||||||
|
message="Please enter a valid port number",
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Error configuring port: {e}"
|
||||||
|
om.MGlobal.displayError(error_msg)
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="Configuration Error",
|
||||||
|
message=error_msg,
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
|
||||||
|
def about_cmd(*args):
|
||||||
|
"""Command to show about dialog"""
|
||||||
|
cmds.confirmDialog(
|
||||||
|
title="About MCP",
|
||||||
|
message="Maya MCP (Model Context Protocol)\nVersion 1.0.0\n\nAuthor: Jeffrey Tsai",
|
||||||
|
button="OK"
|
||||||
|
)
|
||||||
|
|
||||||
# Plugin initialization
|
# Plugin initialization
|
||||||
def initializePlugin(plugin):
|
def initializePlugin(plugin):
|
||||||
"""
|
"""
|
||||||
@@ -32,18 +351,31 @@ def initializePlugin(plugin):
|
|||||||
Args:
|
Args:
|
||||||
plugin: MObject used to register commands
|
plugin: MObject used to register commands
|
||||||
"""
|
"""
|
||||||
vendor = "Virtuos Games"
|
vendor = "Jeffrey Tsai"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
||||||
plugin_fn = om.MFnPlugin(plugin, vendor, version)
|
plugin_fn = om.MFnPlugin(plugin, vendor, version)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Add the plugin directory to the Python path
|
# Get more reliable plugin path
|
||||||
# Use plugin_fn.loadPath() instead of __file__ to get the plugin path
|
|
||||||
plugin_path = os.path.dirname(plugin_fn.loadPath())
|
plugin_path = os.path.dirname(plugin_fn.loadPath())
|
||||||
om.MGlobal.displayInfo(f"Plugin path: {plugin_path}")
|
om.MGlobal.displayInfo(f"Plugin path: {plugin_path}")
|
||||||
|
|
||||||
# Make sure the path is added to sys.path
|
# Get MayaMCP directory path - fix the path issue
|
||||||
|
mcp_dir = os.path.join(plugin_path, "MayaMCP")
|
||||||
|
mcp_dir = mcp_dir.replace('\\', '/') # Ensure forward slashes
|
||||||
|
om.MGlobal.displayInfo(f"MayaMCP directory: {mcp_dir}")
|
||||||
|
|
||||||
|
if os.path.exists(mcp_dir):
|
||||||
|
om.MGlobal.displayInfo(f"MayaMCP directory found: {mcp_dir}")
|
||||||
|
# Add MayaMCP directory to sys.path
|
||||||
|
if mcp_dir not in sys.path:
|
||||||
|
sys.path.append(mcp_dir)
|
||||||
|
om.MGlobal.displayInfo(f"Added MayaMCP directory to sys.path: {mcp_dir}")
|
||||||
|
else:
|
||||||
|
om.MGlobal.displayInfo(f"MayaMCP directory not found at: {mcp_dir}")
|
||||||
|
|
||||||
|
# Ensure plugin path is also added to sys.path
|
||||||
if plugin_path not in sys.path:
|
if plugin_path not in sys.path:
|
||||||
sys.path.append(plugin_path)
|
sys.path.append(plugin_path)
|
||||||
om.MGlobal.displayInfo(f"Added to sys.path: {plugin_path}")
|
om.MGlobal.displayInfo(f"Added to sys.path: {plugin_path}")
|
||||||
@@ -51,26 +383,71 @@ def initializePlugin(plugin):
|
|||||||
# Print current sys.path for debugging
|
# Print current sys.path for debugging
|
||||||
om.MGlobal.displayInfo(f"Current sys.path: {sys.path}")
|
om.MGlobal.displayInfo(f"Current sys.path: {sys.path}")
|
||||||
|
|
||||||
# Check if loader.py exists in the plugin directory
|
# Force reload modules if they are already loaded
|
||||||
loader_path = os.path.join(plugin_path, "loader.py")
|
# This ensures we're using the latest version of the code
|
||||||
if os.path.exists(loader_path):
|
for module_name in ["server", "http_handler", "maya_mcp"]:
|
||||||
om.MGlobal.displayInfo(f"loader.py found at: {loader_path}")
|
if module_name in sys.modules:
|
||||||
else:
|
om.MGlobal.displayInfo(f"Reloading module: {module_name}")
|
||||||
om.MGlobal.displayError(f"loader.py not found at: {loader_path}")
|
|
||||||
|
|
||||||
# Import and start MCP server
|
|
||||||
try:
|
try:
|
||||||
# Try to import directly from the plugin path
|
# Python 3.4+
|
||||||
sys.path.insert(0, plugin_path) # Add to the beginning of sys.path
|
import importlib
|
||||||
|
importlib.reload(sys.modules[module_name])
|
||||||
|
except Exception as e:
|
||||||
|
om.MGlobal.displayWarning(f"Failed to reload module {module_name}: {e}")
|
||||||
|
|
||||||
# Try to import the module
|
# Directly import required modules, not dependent on loader.py
|
||||||
om.MGlobal.displayInfo("Attempting to import loader module...")
|
try:
|
||||||
from loader import auto_load
|
# Try to import server module
|
||||||
om.MGlobal.displayInfo("Successfully imported loader module")
|
om.MGlobal.displayInfo("Attempting to import server module...")
|
||||||
|
|
||||||
# Start the server
|
# Add current directory to sys.path
|
||||||
auto_load()
|
# Cannot use __file__ in Maya plugin, use plugin_path instead
|
||||||
om.MGlobal.displayInfo("MCP server loaded successfully")
|
current_dir = plugin_path
|
||||||
|
if "MayaMCP" in current_dir:
|
||||||
|
current_dir = current_dir # Already in MayaMCP directory
|
||||||
|
else:
|
||||||
|
# Try to find MayaMCP directory
|
||||||
|
mcp_dir = os.path.join(current_dir, "MayaMCP")
|
||||||
|
if os.path.exists(mcp_dir):
|
||||||
|
current_dir = mcp_dir
|
||||||
|
|
||||||
|
current_dir = current_dir.replace('\\', '/') # Ensure forward slashes
|
||||||
|
om.MGlobal.displayInfo(f"Using directory: {current_dir}")
|
||||||
|
|
||||||
|
if current_dir not in sys.path:
|
||||||
|
sys.path.insert(0, current_dir)
|
||||||
|
om.MGlobal.displayInfo(f"Added directory to sys.path: {current_dir}")
|
||||||
|
|
||||||
|
# Import server module
|
||||||
|
import server
|
||||||
|
om.MGlobal.displayInfo("Successfully imported server module")
|
||||||
|
|
||||||
|
# Force create menu
|
||||||
|
om.MGlobal.displayInfo("Forcing menu creation...")
|
||||||
|
menu_result = create_menu()
|
||||||
|
if menu_result:
|
||||||
|
om.MGlobal.displayInfo("Menu created successfully")
|
||||||
|
else:
|
||||||
|
om.MGlobal.displayWarning("Menu creation may have failed")
|
||||||
|
|
||||||
|
# Try alternative method to create menu
|
||||||
|
try:
|
||||||
|
# Use a deferred command to create the menu
|
||||||
|
# This gives Maya time to initialize the UI
|
||||||
|
cmds.evalDeferred("import maya_mcp_plugin; maya_mcp_plugin.create_menu()")
|
||||||
|
om.MGlobal.displayInfo("Scheduled deferred menu creation")
|
||||||
|
except Exception as e:
|
||||||
|
om.MGlobal.displayError(f"Failed to schedule deferred menu creation: {e}")
|
||||||
|
|
||||||
|
# Start server
|
||||||
|
port = server.start_server()
|
||||||
|
if port:
|
||||||
|
message = "=" * 50
|
||||||
|
message += "\nMCP SERVER STARTED SUCCESSFULLY ON PORT {}\n".format(port)
|
||||||
|
message += "=" * 50
|
||||||
|
om.MGlobal.displayInfo(message)
|
||||||
|
else:
|
||||||
|
om.MGlobal.displayError("Failed to start MCP server")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
om.MGlobal.displayError(f"Failed to load MCP server: {e}")
|
om.MGlobal.displayError(f"Failed to load MCP server: {e}")
|
||||||
om.MGlobal.displayError(f"Traceback: {traceback.format_exc()}")
|
om.MGlobal.displayError(f"Traceback: {traceback.format_exc()}")
|
||||||
|
Reference in New Issue
Block a user