Skip to main content
Glama
MR901

mcp-plots

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
NameRequiredDescriptionDefault
chart_typeYes
dataNo
field_mapNo
config_overridesNo
optionsNo
output_formatNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • 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
        )
  • 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)}
  • 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)
  • 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__
                }
            )
  • 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
            }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It describes key behaviors like special modes ('help' and 'suggest'), return types (MCP-compatible content), and optional parameters, but lacks details on error handling, performance limits, or authentication needs. This provides moderate transparency but leaves gaps for a tool with 6 parameters.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with sections for general purpose, special modes, and parameters, making it easy to scan. It is appropriately sized with no redundant sentences, though it could be slightly more front-loaded by emphasizing the primary use case before special modes.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (6 parameters, nested objects) and the presence of an output schema (which reduces the need to explain return values), the description is mostly complete. It covers key usage scenarios and parameter semantics but lacks details on behavioral aspects like error conditions or rate limits, which are important for a rendering tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 0%, so the description must compensate. It adds significant meaning by explaining each parameter's purpose, such as 'chart_type' options ('line', 'bar', 'pie', etc.), 'data' as 'list of objects (rows)', and 'field_map' keys. However, it does not fully detail all aspects like the structure of 'config_overrides' or 'options', preventing a perfect score.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('Render a chart from tabular data') and distinguishes it from the only sibling tool 'configure_preferences' by focusing on chart generation rather than configuration. It also mentions the return type ('MCP-compatible content'), making the purpose explicit and differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear usage contexts by detailing special modes ('help' and 'suggest') and when data is required (e.g., 'requires data' for 'suggest' mode). However, it does not explicitly state when to use this tool versus the sibling 'configure_preferences' or other alternatives, which limits the score to 4.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/MR901/mcp-plots'

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