Skip to main content
Glama
server.py12.6 kB
""" Maya MCP Server - FastMCP implementation for Smithery Entry point for the Maya Model Context Protocol server using FastMCP """ try: from fastmcp import FastMCP except ImportError: try: from mcp.server.fastmcp import FastMCP except ImportError: from mcp import FastMCP from pydantic import BaseModel, Field from typing import List, Optional import logging import sys import os # Add parent directory to path for imports current_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(current_dir) if parent_dir not in sys.path: sys.path.insert(0, parent_dir) try: from maya_bridge import MayaBridge from models import ObjectType except ImportError as e: logging.error(f"Import error: {e}") # Fallback for basic functionality class MayaBridge: def __init__(self, **kwargs): pass def execute_command(self, cmd): return type('Response', (), {'success': False, 'error': 'Maya not available'})() class ObjectType: pass # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Create FastMCP server instance mcp = FastMCP("Maya MCP Server") # Global Maya bridge instance maya_bridge = None def get_maya_bridge() -> MayaBridge: """Get or create Maya bridge""" global maya_bridge if maya_bridge is None: maya_bridge = MayaBridge( host="localhost", port=7022, timeout=30, debug=False ) return maya_bridge @mcp.tool() def maya_create( object_type: str, name: Optional[str] = None ) -> str: """Create Maya objects (cubes, spheres, etc.) Args: object_type: Type of object to create (polyCube, polySphere, etc.) name: Optional name for the object """ try: bridge = get_maya_bridge() # Build Maya command if name: command = f'import maya.cmds as cmds; result = cmds.{object_type}(name="{name}"); print(f"Created: {{result}}")' else: command = f'import maya.cmds as cmds; result = cmds.{object_type}(); print(f"Created: {{result}}")' response = bridge.execute_command(command) if response.success: return f"Successfully created {object_type}" + (f" named '{name}'" if name else "") else: return f"Failed to create {object_type}: {response.error}" except Exception as e: logger.error(f"Error in maya_create: {e}") return f"Error creating object: {str(e)}" @mcp.tool() def maya_select(objects: List[str]) -> str: """Select objects by name Args: objects: List of object names to select """ try: bridge = get_maya_bridge() if not objects: return "Error: No objects specified for selection" # Build selection command objects_str = '", "'.join(objects) command = f'import maya.cmds as cmds; cmds.select(["{objects_str}"]); print(f"Selected: {objects}")' response = bridge.execute_command(command) if response.success: return f"Successfully selected {len(objects)} object(s): {', '.join(objects)}" else: return f"Failed to select objects: {response.error}" except Exception as e: logger.error(f"Error in maya_select: {e}") return f"Error selecting objects: {str(e)}" @mcp.tool() def maya_execute(command: str) -> str: """Execute Python commands in Maya Args: command: Python command to execute in Maya """ try: bridge = get_maya_bridge() if not command.strip(): return "Error: Empty command provided" response = bridge.execute_command(command) if response.success: return f"Command executed successfully. Output: {response.result}" else: return f"Command failed: {response.error}" except Exception as e: logger.error(f"Error in maya_execute: {e}") return f"Error executing command: {str(e)}" @mcp.tool() def maya_get_selection() -> str: """Get currently selected objects""" try: bridge = get_maya_bridge() command = 'import maya.cmds as cmds; selected = cmds.ls(selection=True); print(f"Selected objects: {selected}")' response = bridge.execute_command(command) if response.success: return f"Current selection: {response.result}" else: return f"Failed to get selection: {response.error}" except Exception as e: logger.error(f"Error in maya_get_selection: {e}") return f"Error getting selection: {str(e)}" @mcp.tool() def maya_get_scene_info( include_transforms: bool = True, include_attributes: bool = False ) -> str: """Get detailed scene information Args: include_transforms: Include transform information include_attributes: Include attribute information """ try: bridge = get_maya_bridge() command = f''' import maya.cmds as cmds import json scene_info = {{ "scene_name": cmds.file(query=True, sceneName=True) or "untitled", "objects": cmds.ls(transforms=True) if {include_transforms} else [], "total_objects": len(cmds.ls()), "selected_objects": cmds.ls(selection=True) }} if {include_attributes}: scene_info["attributes"] = {{}} for obj in scene_info.get("objects", [])[:5]: # Limit to first 5 objects try: scene_info["attributes"][obj] = cmds.listAttr(obj, keyable=True) or [] except: pass print(json.dumps(scene_info, indent=2)) ''' response = bridge.execute_command(command) if response.success: return f"Scene information: {response.result}" else: return f"Failed to get scene info: {response.error}" except Exception as e: logger.error(f"Error in maya_get_scene_info: {e}") return f"Error getting scene info: {str(e)}" @mcp.tool() def maya_transform( objects: List[str], translate: Optional[List[float]] = None, rotate: Optional[List[float]] = None, scale: Optional[List[float]] = None ) -> str: """Transform objects (move, rotate, scale) Args: objects: List of object names to transform translate: Translation values [x, y, z] rotate: Rotation values [x, y, z] in degrees scale: Scale values [x, y, z] """ try: bridge = get_maya_bridge() if not objects: return "Error: No objects specified for transformation" commands = [] commands.append('import maya.cmds as cmds') for obj in objects: if translate: commands.append(f'cmds.move({translate[0]}, {translate[1]}, {translate[2]}, "{obj}", absolute=True)') if rotate: commands.append(f'cmds.rotate({rotate[0]}, {rotate[1]}, {rotate[2]}, "{obj}", absolute=True)') if scale: commands.append(f'cmds.scale({scale[0]}, {scale[1]}, {scale[2]}, "{obj}", absolute=True)') commands.append(f'print(f"Transformed objects: {objects}")') command = '; '.join(commands) response = bridge.execute_command(command) if response.success: return f"Successfully transformed {len(objects)} object(s): {', '.join(objects)}" else: return f"Failed to transform objects: {response.error}" except Exception as e: logger.error(f"Error in maya_transform: {e}") return f"Error transforming objects: {str(e)}" @mcp.tool() def maya_delete(objects: List[str]) -> str: """Delete objects from scene Args: objects: List of object names to delete """ try: bridge = get_maya_bridge() if not objects: return "Error: No objects specified for deletion" objects_str = '", "'.join(objects) command = f'import maya.cmds as cmds; cmds.delete(["{objects_str}"]); print(f"Deleted: {objects}")' response = bridge.execute_command(command) if response.success: return f"Successfully deleted {len(objects)} object(s): {', '.join(objects)}" else: return f"Failed to delete objects: {response.error}" except Exception as e: logger.error(f"Error in maya_delete: {e}") return f"Error deleting objects: {str(e)}" @mcp.tool() def maya_get_object_info( object_name: str, include_attributes: bool = True ) -> str: """Get detailed information about specific objects Args: object_name: Name of the object to inspect include_attributes: Include attribute information """ try: bridge = get_maya_bridge() command = f''' import maya.cmds as cmds import json try: if not cmds.objExists("{object_name}"): print("Object does not exist") else: info = {{ "name": "{object_name}", "type": cmds.objectType("{object_name}"), "transform": {{ "translate": cmds.getAttr("{object_name}.translate")[0] if cmds.attributeQuery("translate", node="{object_name}", exists=True) else None, "rotate": cmds.getAttr("{object_name}.rotate")[0] if cmds.attributeQuery("rotate", node="{object_name}", exists=True) else None, "scale": cmds.getAttr("{object_name}.scale")[0] if cmds.attributeQuery("scale", node="{object_name}", exists=True) else None }} }} if {include_attributes}: info["attributes"] = cmds.listAttr("{object_name}", keyable=True) or [] print(json.dumps(info, indent=2)) except Exception as e: print(f"Error getting object info: {{e}}") ''' response = bridge.execute_command(command) if response.success: return f"Object information: {response.result}" else: return f"Failed to get object info: {response.error}" except Exception as e: logger.error(f"Error in maya_get_object_info: {e}") return f"Error getting object info: {str(e)}" @mcp.tool() def maya_list_objects( object_type: str = "transform", pattern: Optional[str] = None ) -> str: """List objects in scene by type Args: object_type: Type of objects to list (transform, mesh, camera, etc.) pattern: Optional name pattern to filter objects """ try: bridge = get_maya_bridge() if pattern: command = f'import maya.cmds as cmds; objects = cmds.ls("{pattern}", type="{object_type}"); print(f"Objects: {{objects}}")' else: command = f'import maya.cmds as cmds; objects = cmds.ls(type="{object_type}"); print(f"Objects: {{objects}}")' response = bridge.execute_command(command) if response.success: return f"Objects of type '{object_type}': {response.result}" else: return f"Failed to list objects: {response.error}" except Exception as e: logger.error(f"Error in maya_list_objects: {e}") return f"Error listing objects: {str(e)}" @mcp.tool() def maya_get_console_output() -> str: """Get console output from Maya Script Editor""" try: bridge = get_maya_bridge() command = ''' import maya.cmds as cmds try: # Get recent console output console_output = "Maya MCP Server is running and connected" print(f"Console status: {console_output}") except Exception as e: print(f"Console error: {e}") ''' response = bridge.execute_command(command) if response.success: return f"Console output: {response.result}" else: return f"Failed to get console output: {response.error}" except Exception as e: logger.error(f"Error in maya_get_console_output: {e}") return f"Error getting console output: {str(e)}" # Entry point for Smithery def main(): """Main entry point for Smithery deployment""" return mcp if __name__ == "__main__": # For local testing import asyncio asyncio.run(mcp.run())

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jeffreytsai1004/maya-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server