render_chart
Create charts from tabular data using various visualization types like line, bar, and pie charts, with options for data analysis suggestions and format customization.
Instructions
Render a chart from tabular data and return MCP-compatible content.
Special modes:
- chart_type="help": Returns available chart types, themes, and field suggestions
- chart_type="suggest": Analyzes your data and suggests field mappings (requires data)
Parameters:
- chart_type: chart type ("line", "bar", "pie", etc.) or "help"/"suggest"
- data: list of objects (rows) - optional for help mode
- field_map: keys like x_field, y_field, category_field, value_field, group_field, size_field
- config_overrides: subset of ChartConfig as dict (width, height, title, theme, dpi, etc.)
- options: generator-specific options (e.g., smooth, stack)
- output_format: MCP_IMAGE (PNG), MCP_TEXT (SVG), or MERMAID
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chart_type | Yes | ||
| data | No | ||
| field_map | No | ||
| config_overrides | No | ||
| options | No | ||
| output_format | No |
Implementation Reference
- src/capabilities/tools.py:271-302 (registration)Registration of the 'render_chart' MCP tool with parameter schema and delegation to implementation.@mcp_server.tool() def render_chart( chart_type: str, data: List[Dict[str, Any]] = None, field_map: Dict[str, str] = None, config_overrides: Dict[str, Any] = None, options: Dict[str, Any] = None, output_format: str = None ) -> Dict[str, Any]: """ Render a chart from tabular data and return MCP-compatible content. Special modes: - chart_type="help": Returns available chart types, themes, and field suggestions - chart_type="suggest": Analyzes your data and suggests field mappings (requires data) Parameters: - chart_type: chart type ("line", "bar", "pie", etc.) or "help"/"suggest" - data: list of objects (rows) - optional for help mode - field_map: keys like x_field, y_field, category_field, value_field, group_field, size_field - config_overrides: subset of ChartConfig as dict (width, height, title, theme, dpi, etc.) - options: generator-specific options (e.g., smooth, stack) - output_format: MCP_IMAGE (PNG), MCP_TEXT (SVG), or MERMAID """ return _render_chart_impl( chart_type=chart_type, data=data, field_map=field_map, config_overrides=config_overrides, options=options, output_format=output_format )
- src/capabilities/tools.py:141-185 (handler)Primary handler implementation that converts tool arguments to ChartRequest, invokes the chart rendering service, and formats the MCP response.def _render_chart_impl( chart_type: str, data: List[Dict[str, Any]] = None, field_map: Dict[str, str] = None, config_overrides: Dict[str, Any] = None, options: Dict[str, Any] = None, output_format: str = None ) -> Dict[str, Any]: """ Render a chart from tabular data and return MCP-compatible content. Special modes: - chart_type="help": Returns available chart types, themes, and field suggestions - chart_type="suggest": Analyzes your data and suggests field mappings (requires data) Parameters: - chart_type: chart type ("line", "bar", "pie", etc.) or "help"/"suggest" - data: list of objects (rows) - optional for help mode - field_map: keys like x_field, y_field, category_field, value_field, group_field, size_field - config_overrides: subset of ChartConfig as dict (width, height, title, theme, dpi, etc.) - options: generator-specific options (e.g., smooth, stack) - output_format: MCP_IMAGE (PNG), MCP_TEXT (SVG), or MERMAID """ try: # Create chart request from parameters request = ChartRequest.from_tool_params( chart_type=chart_type, data=data, field_map=field_map, config_overrides=config_overrides, options=options, output_format=output_format ) # Use chart service chart_service = _get_chart_service() response = chart_service.render_chart(request) # Convert to MCP format return response.to_mcp_format() except Exception as e: logger.error(f"render_chart failed: {e}") return {"status": "error", "error": str(e)}
- src/domain/models.py:143-217 (schema)Dataclass defining the input schema for chart requests, including factory method from_tool_params that maps MCP tool parameters, validation, and field mapping.class ChartRequest: """ Complete chart generation request with validation. Encapsulates all information needed to generate a chart, including data, configuration, and output preferences. """ chart_type: str data: List[Dict[str, Any]] field_mapping: FieldMapping = field(default_factory=FieldMapping) config_overrides: Dict[str, Any] = field(default_factory=dict) options: Dict[str, Any] = field(default_factory=dict) output_format: Optional[str] = None mode: ChartRequestMode = ChartRequestMode.RENDER def __post_init__(self): """Validate request after initialization""" # Only validate for render mode, not for help/suggest if self.mode == ChartRequestMode.RENDER: self.validate() @classmethod def from_tool_params( cls, chart_type: str, data: Optional[List[Dict[str, Any]]] = None, field_map: Optional[Dict[str, str]] = None, config_overrides: Optional[Dict[str, Any]] = None, options: Optional[Dict[str, Any]] = None, output_format: Optional[str] = None ) -> 'ChartRequest': """Create request from MCP tool parameters""" # Determine mode based on chart_type if chart_type == "help": mode = ChartRequestMode.HELP elif chart_type == "suggest": mode = ChartRequestMode.SUGGEST else: mode = ChartRequestMode.RENDER return cls( chart_type=chart_type, data=data or [], field_mapping=FieldMapping.from_dict(field_map or {}), config_overrides=config_overrides or {}, options=options or {}, output_format=output_format, mode=mode ) def validate(self) -> None: """Validate request for chart generation""" if self.mode == ChartRequestMode.RENDER: if not self.data: raise ValueError(ChartConstants.ErrorMessages.EMPTY_DATA) if not isinstance(self.data, list): raise ValueError(ChartConstants.ErrorMessages.INVALID_DATA_TYPE) if len(self.data) > ChartConstants.ConfigDefaults.MAX_DATA_POINTS: raise ValueError(f"Data exceeds maximum allowed points: {ChartConstants.ConfigDefaults.MAX_DATA_POINTS}") elif self.mode == ChartRequestMode.SUGGEST: if not self.data: raise ValueError("Data is required for field suggestions") def get_field_kwargs(self) -> Dict[str, Any]: """Get field mappings as kwargs for ChartGenerator""" return self.field_mapping.get_non_none_fields() def is_special_mode(self) -> bool: """Check if this is a special mode request (help/suggest)""" return self.mode in (ChartRequestMode.HELP, ChartRequestMode.SUGGEST)
- src/services/chart_service.py:51-120 (helper)Core service method implementing the chart rendering logic: handles special modes, validation, config building, chart generation via factory, and response normalization.def render_chart(self, request: ChartRequest) -> ChartResponse: """ Render chart with full error handling and configuration management. Args: request: Complete chart rendering request Returns: ChartResponse: Standardized response with content or error """ try: # Handle special modes if request.mode == ChartRequestMode.HELP: return self._generate_help_response() if request.mode == ChartRequestMode.SUGGEST: return self._generate_field_suggestions(request.data) # Validate request for regular chart rendering request.validate() # Get user preferences user_prefs = self._config_service.get_user_preferences() # Build chart configuration config = self._build_chart_config(request, user_prefs) # Build chart data chart_data = self._build_chart_data(request) # Generate chart using factory result = self._chart_factory.generate_chart( request.chart_type, chart_data=chart_data, config=config, **request.options ) # Normalize response content = self._normalize_chart_result(result) return ChartResponse.success_response( content=content, metadata={ "chart_type": request.chart_type, "data_points": len(request.data), "output_format": config.output_format.value, "generation_time": datetime.now().isoformat() } ) except Exception as e: # Use centralized error handling error_response = ErrorHandler.handle_chart_error(e, getattr(request, 'chart_type', None)) # Log the error with appropriate level if isinstance(e, ChartGenerationError): self._logger.warning(f"Chart generation error: {e}") else: self._logger.error(f"Unexpected chart rendering error: {e}") return ChartResponse.error_response( error=error_response["error"], metadata={ "chart_type": getattr(request, 'chart_type', 'unknown'), "error_code": error_response.get("error_code", "UNKNOWN_ERROR"), "error_type": type(e).__name__ } )
- src/domain/models.py:219-275 (schema)Output response schema with MCP formatting method used by the handler.@dataclass class ChartResponse: """ Standardized chart generation response. Provides consistent response format for all chart generation operations, including success and error cases. """ success: bool content: Optional[List[Dict[str, Any]]] = None error: Optional[str] = None metadata: Dict[str, Any] = field(default_factory=dict) @classmethod def success_response( cls, content: List[Dict[str, Any]], metadata: Optional[Dict[str, Any]] = None ) -> 'ChartResponse': """Create successful response""" return cls( success=True, content=content, metadata=metadata or {} ) @classmethod def error_response(cls, error: str, metadata: Optional[Dict[str, Any]] = None) -> 'ChartResponse': """Create error response""" return cls( success=False, error=error, metadata=metadata or {} ) def to_mcp_format(self) -> Dict[str, Any]: """Convert to MCP tool response format""" if self.success: result = {"status": "success"} if self.content: result["content"] = self.content return result else: return { "status": "error", "error": self.error or "Unknown error" } def to_dict(self) -> Dict[str, Any]: """Convert to dictionary for serialization""" return { "success": self.success, "content": self.content, "error": self.error, "metadata": self.metadata }