Robust error handling is essential for building reliable applications with ShotGrid MCP Server. This page covers best practices and patterns for handling errors at different levels of your application.
For MCP tools, it’s often better to raise exceptions rather than returning error objects. The MCP protocol will automatically convert exceptions to appropriate error responses:
Copy
@server.tool()def update_shot_status(shot_id: int, status: str) -> dict: """Update a shot's status with proper error raising.""" # Validate the status valid_statuses = ["wtg", "rdy", "ip", "cmpt", "fin"] if status not in valid_statuses: raise ValueError( f"Invalid status: {status}. Must be one of: {', '.join(valid_statuses)}" ) # Check if the shot exists shot = server.connection.find_one("Shot", [["id", "is", shot_id]]) if not shot: raise ValueError(f"Shot with ID {shot_id} not found") # Update the shot updated_shot = server.connection.update("Shot", shot_id, {"sg_status_list": status}) return updated_shot
For more structured error handling, define custom error classes:
Copy
# Define custom error classesclass ShotGridError(Exception): """Base class for ShotGrid-related errors.""" passclass EntityNotFoundError(ShotGridError): """Raised when an entity is not found.""" def __init__(self, entity_type, entity_id): self.entity_type = entity_type self.entity_id = entity_id message = f"{entity_type} with ID {entity_id} not found" super().__init__(message)class ValidationError(ShotGridError): """Raised when validation fails.""" passclass PermissionError(ShotGridError): """Raised when permission is denied.""" pass# Use custom error classes in tools@server.tool()def update_entity(entity_type: str, entity_id: int, data: dict) -> dict: """Update an entity with custom error handling.""" # Check if the entity exists entity = server.connection.find_one(entity_type, [["id", "is", entity_id]]) if not entity: raise EntityNotFoundError(entity_type, entity_id) # Validate the data if not data: raise ValidationError("No data provided for update") # Update the entity try: updated_entity = server.connection.update(entity_type, entity_id, data) return updated_entity except Exception as e: if "Permission denied" in str(e): raise PermissionError(f"Permission denied to update {entity_type} {entity_id}") else: # Re-raise other exceptions raise
The Connection Pool in ShotGrid MCP Server already handles many connection-related errors, but you can add additional error handling:
Copy
@server.tool()async def safe_find_one(entity_type: str, entity_id: int) -> dict: """Find an entity with connection pool error handling.""" try: # Get a connection from the pool async with server.connection_pool.connection() as sg: entity = sg.find_one(entity_type, [["id", "is", entity_id]]) if not entity: return { "success": False, "error": "Not Found", "message": f"{entity_type} with ID {entity_id} not found" } return { "success": True, "entity": entity } except Exception as e: # The connection pool will handle connection errors, # but we still need to handle other exceptions return { "success": False, "error": "Error", "message": str(e) }
Validate entity types and fields against the schema:
Copy
@server.tool()def validate_entity_field(entity_type: str, field_name: str) -> dict: """Validate if a field exists for an entity type.""" schema = server.schema_loader.get_schema() # Check if the entity type exists if entity_type not in schema: return { "valid": False, "error": "Invalid Entity Type", "message": f"Entity type '{entity_type}' does not exist in the schema" } # Check if the field exists if field_name not in schema[entity_type]: return { "valid": False, "error": "Invalid Field", "message": f"Field '{field_name}' does not exist for entity type '{entity_type}'" } # Get field information field_info = schema[entity_type][field_name] return { "valid": True, "field_type": field_info.get("data_type", {}).get("value"), "editable": field_info.get("editable", {}).get("value", False) }