Skip to main content
Glama
Fervoyush
by Fervoyush

create_plot

Create customizable statistical visualizations using the grammar of graphics. Specify data sources, aesthetic mappings, geometries, scales, themes, and facets to generate publication-quality plots.

Instructions

Create a plotnine visualization from data.

This tool allows you to create highly customizable plots using the grammar of graphics. You can specify data sources (file, URL, or inline), aesthetic mappings, geometries, scales, themes, facets, labels, and coordinate systems.

NEW: Multi-layer plots! Use 'geoms' array to combine multiple geometries in one plot.

Example usage:

  • Simple scatter plot: provide data_source, aes (x, y), and geom (type: "point")

  • Multi-layer plot: use geoms array with multiple geometries (e.g., point + smooth)

  • Line plot with custom theme: add theme config with base and customizations

  • Faceted plot: include facet config to split by categorical variables

  • Multiple scales: provide list of scale configs for x, y, color, etc.

All parameters support extensive customization through nested objects.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
data_sourceYesData source configuration (file, URL, or inline data)
aesYesAesthetic mappings (column names from data)
geomNoSingle geometry specification (use 'geoms' for multi-layer plots)
geomsNoMultiple geometry specifications for layered plots (e.g., scatter + smooth, boxplot + jitter)
scalesNoScale configurations for axes and aesthetics
themeNoTheme configuration
facetsNoFaceting configuration
labelsNoPlot labels
coordsNoCoordinate system configuration
statsNoStatistical transformation configurations
transformsNoData transformations to apply before plotting (filter, group_summarize, sort, select, rename, mutate, drop_na, fill_na, sample, unique, rolling, pivot)
outputNoOutput configuration

Implementation Reference

  • The primary handler function for the 'create_plot' tool. Parses and validates input arguments using Pydantic schemas, loads data from various sources, applies optional transforms, builds the plot using the plot_builder module, saves the output file, and returns a detailed success or error message.
    async def create_plot_handler(arguments: dict[str, Any]) -> list[TextContent]: """Handle create_plot tool calls.""" try: # Parse and validate arguments data_source = DataSource(**arguments["data_source"]) aes_config = Aesthetics(**arguments["aes"]) # Handle geom vs geoms (backward compatibility) geom_config = None geom_configs = None if "geom" in arguments and arguments["geom"]: geom_config = GeomConfig(**arguments["geom"]) if "geoms" in arguments and arguments["geoms"]: geom_configs = [GeomConfig(**g) for g in arguments["geoms"]] if not geom_config and not geom_configs: return [ TextContent( type="text", text="Error: Either 'geom' or 'geoms' must be provided.\n\nUse 'geom' for single layer or 'geoms' for multi-layer plots.", ) ] # Optional configurations scales = None if "scales" in arguments and arguments["scales"]: scales = [ScaleConfig(**s) for s in arguments["scales"]] theme_config = None if "theme" in arguments and arguments["theme"]: theme_config = ThemeConfig(**arguments["theme"]) facet_config = None if "facets" in arguments and arguments["facets"]: facet_config = FacetConfig(**arguments["facets"]) labels_config = None if "labels" in arguments and arguments["labels"]: labels_config = LabelsConfig(**arguments["labels"]) coord_config = None if "coords" in arguments and arguments["coords"]: coord_config = CoordConfig(**arguments["coords"]) stats = None if "stats" in arguments and arguments["stats"]: stats = [StatConfig(**s) for s in arguments["stats"]] output_config = OutputConfig(**(arguments.get("output", {}))) # Load data try: data = load_data(data_source) except DataLoadError as e: return [ TextContent( type="text", text=f"Data loading error: {str(e)}\n\nPlease check:\n- File path or URL is correct\n- File format is supported\n- Data is properly formatted", ) ] # Apply data transformations if provided if "transforms" in arguments and arguments["transforms"]: try: data = apply_transforms(data, arguments["transforms"]) except ValueError as e: return [ TextContent( type="text", text=f"Data transformation error: {str(e)}\n\nPlease check your transformation configurations.", ) ] # Build plot try: plot = build_plot( data=data, aes_config=aes_config, geom_config=geom_config, geom_configs=geom_configs, scales=scales, theme_config=theme_config, facet_config=facet_config, labels_config=labels_config, coord_config=coord_config, stats=stats, ) except PlotBuildError as e: return [ TextContent( type="text", text=f"Plot building error: {str(e)}\n\nPlease check:\n- Column names exist in data\n- Aesthetic mappings are valid\n- Geom type is supported\n- Scale/theme configurations are correct", ) ] # Save plot try: result = save_plot(plot, output_config) except PlotBuildError as e: return [ TextContent( type="text", text=f"Plot saving error: {str(e)}\n\nPlease check:\n- Output directory is writable\n- Filename is valid\n- Output format is supported", ) ] # Return success message with details success_message = f"""Plot created successfully! Output file: {result['path']} Format: {result['format']} Dimensions: {result['width']} x {result['height']} inches DPI: {result['dpi']} Data summary: - Rows: {len(data)} - Columns: {', '.join(data.columns.tolist())} """ return [TextContent(type="text", text=success_message)] except Exception as e: return [ TextContent( type="text", text=f"Unexpected error: {str(e)}\n\nPlease check your input parameters and try again.", ) ]
  • Tool registration block defining the 'create_plot' tool, including its name, detailed description, and comprehensive JSON inputSchema specifying all parameters like data_source, aes, geom/geoms, scales, theme, facets, labels, output, etc.
    Tool( name="create_plot", description="""Create a plotnine visualization from data. This tool allows you to create highly customizable plots using the grammar of graphics. You can specify data sources (file, URL, or inline), aesthetic mappings, geometries, scales, themes, facets, labels, and coordinate systems. NEW: Multi-layer plots! Use 'geoms' array to combine multiple geometries in one plot. Example usage: - Simple scatter plot: provide data_source, aes (x, y), and geom (type: "point") - Multi-layer plot: use geoms array with multiple geometries (e.g., point + smooth) - Line plot with custom theme: add theme config with base and customizations - Faceted plot: include facet config to split by categorical variables - Multiple scales: provide list of scale configs for x, y, color, etc. All parameters support extensive customization through nested objects.""", inputSchema={ "type": "object", "properties": { "data_source": { "type": "object", "description": "Data source configuration (file, URL, or inline data)", "properties": { "type": { "type": "string", "enum": ["file", "url", "inline"], "description": "Source type", }, "path": { "type": "string", "description": "File path or URL (for file/url types)", }, "data": { "type": "array", "items": {"type": "object"}, "description": "Inline data as array of objects (for inline type)", }, "format": { "type": "string", "enum": ["csv", "json", "parquet", "excel"], "description": "Data format (auto-detected if not specified)", }, }, "required": ["type"], }, "aes": { "type": "object", "description": "Aesthetic mappings (column names from data)", "properties": { "x": {"type": "string", "description": "X-axis variable"}, "y": {"type": "string", "description": "Y-axis variable"}, "color": {"type": "string", "description": "Color variable"}, "fill": {"type": "string", "description": "Fill variable"}, "size": {"type": "string", "description": "Size variable"}, "alpha": {"type": "string", "description": "Alpha (transparency) variable"}, "shape": {"type": "string", "description": "Shape variable"}, "linetype": {"type": "string", "description": "Linetype variable"}, "group": {"type": "string", "description": "Grouping variable"}, }, }, "geom": { "type": "object", "description": "Single geometry specification (use 'geoms' for multi-layer plots)", "properties": { "type": { "type": "string", "description": "Geometry type: point, line, bar, histogram, boxplot, violin, area, density, smooth, jitter, tile, text, errorbar, hline, vline, abline, path, polygon, ribbon, col", }, "params": { "type": "object", "description": "Additional geom parameters (e.g., size, alpha, color, fill, etc.)", }, }, "required": ["type"], }, "geoms": { "type": "array", "description": "Multiple geometry specifications for layered plots (e.g., scatter + smooth, boxplot + jitter)", "items": { "type": "object", "properties": { "type": { "type": "string", "description": "Geometry type: point, line, bar, histogram, boxplot, violin, area, density, smooth, jitter, tile, text, errorbar, hline, vline, abline, path, polygon, ribbon, col", }, "params": { "type": "object", "description": "Additional geom parameters (e.g., size, alpha, color, fill, etc.)", }, }, "required": ["type"], }, }, "scales": { "type": "array", "description": "Scale configurations for axes and aesthetics", "items": { "type": "object", "properties": { "aesthetic": { "type": "string", "description": "Which aesthetic: x, y, color, fill, size, etc.", }, "type": { "type": "string", "description": "Scale type: continuous, discrete, log10, sqrt, datetime, gradient, brewer, etc.", }, "params": { "type": "object", "description": "Scale parameters (limits, breaks, labels, etc.)", }, }, "required": ["aesthetic", "type"], }, }, "theme": { "type": "object", "description": "Theme configuration", "properties": { "base": { "type": "string", "description": "Base theme: gray, bw, minimal, classic, dark, light, void", "default": "gray", }, "customizations": { "type": "object", "description": "Theme customizations (figure_size, legend_position, text properties, etc.)", }, }, }, "facets": { "type": "object", "description": "Faceting configuration", "properties": { "type": { "type": "string", "enum": ["wrap", "grid"], "description": "Facet type", }, "facets": { "type": "string", "description": "Faceting formula for facet_wrap (e.g., '~ variable')", }, "rows": { "type": "string", "description": "Row variable for facet_grid", }, "cols": { "type": "string", "description": "Column variable for facet_grid", }, "params": { "type": "object", "description": "Additional facet parameters (ncol, scales, etc.)", }, }, }, "labels": { "type": "object", "description": "Plot labels", "properties": { "title": {"type": "string", "description": "Plot title"}, "x": {"type": "string", "description": "X-axis label"}, "y": {"type": "string", "description": "Y-axis label"}, "caption": {"type": "string", "description": "Plot caption"}, "subtitle": {"type": "string", "description": "Plot subtitle"}, }, }, "coords": { "type": "object", "description": "Coordinate system configuration", "properties": { "type": { "type": "string", "description": "Coordinate type: cartesian, flip, fixed, trans", }, "params": { "type": "object", "description": "Coordinate parameters", }, }, }, "stats": { "type": "array", "description": "Statistical transformation configurations", "items": { "type": "object", "properties": { "type": { "type": "string", "description": "Stat type: smooth, bin, density, summary", }, "params": { "type": "object", "description": "Stat parameters", }, }, "required": ["type"], }, }, "transforms": { "type": "array", "description": "Data transformations to apply before plotting (filter, group_summarize, sort, select, rename, mutate, drop_na, fill_na, sample, unique, rolling, pivot)", "items": { "type": "object", "properties": { "type": { "type": "string", "description": "Transform type", }, }, "required": ["type"], }, }, "output": { "type": "object", "description": "Output configuration", "properties": { "format": { "type": "string", "enum": ["png", "pdf", "svg"], "default": "png", }, "filename": { "type": "string", "description": "Output filename (auto-generated if not provided)", }, "width": {"type": "number", "default": 8, "description": "Width in inches"}, "height": {"type": "number", "default": 6, "description": "Height in inches"}, "dpi": {"type": "integer", "default": 300, "description": "DPI for raster formats"}, "directory": { "type": "string", "default": "./output", "description": "Output directory", }, }, }, }, "required": ["data_source", "aes"], }, ),
  • Pydantic BaseModel definitions for input validation used in the create_plot_handler, including DataSource, Aesthetics, GeomConfig, ScaleConfig, ThemeConfig, FacetConfig, LabelsConfig, CoordConfig, StatConfig, and OutputConfig.
    """ Pydantic schemas for validating plotnine MCP tool parameters. """ from typing import Any, Literal, Optional from pydantic import BaseModel, Field class DataSource(BaseModel): """Data source configuration.""" type: Literal["file", "url", "inline"] = Field( description="Type of data source: file path, URL, or inline JSON data" ) path: Optional[str] = Field( None, description="File path or URL (required for file/url types)" ) data: Optional[list[dict[str, Any]]] = Field( None, description="Inline data as list of dictionaries (required for inline type)" ) format: Optional[Literal["csv", "json", "parquet", "excel"]] = Field( "csv", description="Data format (auto-detected if not specified)" ) class Aesthetics(BaseModel): """Aesthetic mappings for the plot.""" x: Optional[str] = Field(None, description="Column name for x-axis") y: Optional[str] = Field(None, description="Column name for y-axis") color: Optional[str] = Field(None, description="Column name for color aesthetic") fill: Optional[str] = Field(None, description="Column name for fill aesthetic") size: Optional[str] = Field(None, description="Column name for size aesthetic") alpha: Optional[str] = Field(None, description="Column name for alpha aesthetic") shape: Optional[str] = Field(None, description="Column name for shape aesthetic") linetype: Optional[str] = Field(None, description="Column name for linetype aesthetic") group: Optional[str] = Field(None, description="Column name for grouping") class GeomConfig(BaseModel): """Geometry configuration.""" type: str = Field( description="Geometry type: point, line, bar, histogram, boxplot, violin, smooth, etc." ) params: dict[str, Any] = Field( default_factory=dict, description="Additional parameters for the geom" ) class ScaleConfig(BaseModel): """Scale configuration.""" aesthetic: str = Field(description="Which aesthetic this scale applies to (x, y, color, etc.)") type: str = Field( description="Scale type: continuous, discrete, log10, sqrt, date, datetime, etc." ) params: dict[str, Any] = Field( default_factory=dict, description="Scale parameters (limits, breaks, labels, etc.)" ) class ThemeConfig(BaseModel): """Theme configuration.""" base: str = Field( "gray", description="Base theme: gray, bw, minimal, classic, dark, light, void, etc." ) customizations: dict[str, Any] = Field( default_factory=dict, description="Theme customizations (figure_size, legend_position, text sizes, etc.)" ) class FacetConfig(BaseModel): """Facet configuration.""" type: Literal["wrap", "grid"] = Field("wrap", description="Facet type: wrap or grid") facets: Optional[str] = Field(None, description="Faceting formula (e.g., '~ variable' or 'var1 ~ var2')") cols: Optional[str] = Field(None, description="Column variable for facet_grid") rows: Optional[str] = Field(None, description="Row variable for facet_grid") params: dict[str, Any] = Field( default_factory=dict, description="Additional facet parameters (ncol, scales, etc.)" ) class LabelsConfig(BaseModel): """Labels configuration.""" title: Optional[str] = Field(None, description="Plot title") x: Optional[str] = Field(None, description="X-axis label") y: Optional[str] = Field(None, description="Y-axis label") caption: Optional[str] = Field(None, description="Plot caption") subtitle: Optional[str] = Field(None, description="Plot subtitle") class CoordConfig(BaseModel): """Coordinate system configuration.""" type: str = Field( "cartesian", description="Coordinate system: cartesian, flip, fixed, trans, etc." ) params: dict[str, Any] = Field( default_factory=dict, description="Coordinate system parameters" ) class StatConfig(BaseModel): """Statistical transformation configuration.""" type: str = Field(description="Stat type: smooth, bin, density, etc.") params: dict[str, Any] = Field( default_factory=dict, description="Stat parameters" ) class OutputConfig(BaseModel): """Output configuration.""" format: Literal["png", "pdf", "svg"] = Field("png", description="Output format") filename: Optional[str] = Field(None, description="Output filename (auto-generated if not provided)") width: float = Field(8, description="Figure width in inches") height: float = Field(6, description="Figure height in inches") dpi: int = Field(300, description="DPI for raster formats") directory: str = Field("./output", description="Output directory")
  • Core helper function build_plot that assembles the plotnine ggplot object: validates columns, builds aesthetics, adds geometry layers, stats, scales, facets, labels, coords, and theme.
    def build_plot( data: pd.DataFrame, aes_config: Aesthetics, geom_config: Optional[GeomConfig] = None, geom_configs: Optional[list[GeomConfig]] = None, scales: Optional[list[ScaleConfig]] = None, theme_config: Optional[ThemeConfig] = None, facet_config: Optional[FacetConfig] = None, labels_config: Optional[LabelsConfig] = None, coord_config: Optional[CoordConfig] = None, stats: Optional[list[StatConfig]] = None, ) -> ggplot: """ Build a plotnine plot from configuration. Args: data: DataFrame to plot aes_config: Aesthetic mappings geom_config: Single geometry configuration (deprecated, use geom_configs) geom_configs: List of geometry configurations for multi-layer plots scales: Scale configurations theme_config: Theme configuration facet_config: Facet configuration labels_config: Labels configuration coord_config: Coordinate system configuration stats: Statistical transformation configurations Returns: ggplot object Raises: PlotBuildError: If plot cannot be built """ try: # Handle backward compatibility: convert single geom to list if geom_config and not geom_configs: geom_configs = [geom_config] elif not geom_configs: raise PlotBuildError("Either geom_config or geom_configs must be provided") # Validate column names exist in data available_columns = data.columns.tolist() columns_to_check = [] # Collect all column references from aesthetics if aes_config.x: columns_to_check.append(aes_config.x) if aes_config.y: columns_to_check.append(aes_config.y) if aes_config.color: columns_to_check.append(aes_config.color) if aes_config.fill: columns_to_check.append(aes_config.fill) if aes_config.size: columns_to_check.append(aes_config.size) if aes_config.alpha: columns_to_check.append(aes_config.alpha) if aes_config.shape: columns_to_check.append(aes_config.shape) if aes_config.linetype: columns_to_check.append(aes_config.linetype) if aes_config.group: columns_to_check.append(aes_config.group) # Check if facet columns exist if facet_config: if facet_config.facets: # Extract column names from formula (e.g., "~ var" or "row ~ col") facet_formula = facet_config.facets.replace("~", "").strip() if facet_formula and facet_formula != ".": facet_cols = [c.strip() for c in facet_formula.split("+")] columns_to_check.extend(facet_cols) if facet_config.rows: columns_to_check.append(facet_config.rows) if facet_config.cols: columns_to_check.append(facet_config.cols) # Validate all columns for col in columns_to_check: if col and col != "." and col not in available_columns: raise PlotBuildError(format_column_error(col, available_columns)) # Build aesthetics aes_obj = _build_aesthetics(aes_config) # Start with base plot plot = ggplot(data, aes_obj) # Add geometries (multiple layers) for geom_cfg in geom_configs: plot = plot + _build_geom(geom_cfg) # Add statistical transformations if any if stats: for stat_config in stats: plot = plot + _build_stat(stat_config) # Add scales if scales: for scale_config in scales: plot = plot + _build_scale(scale_config) # Add facets if facet_config: plot = plot + _build_facet(facet_config) # Add labels if labels_config: plot = plot + _build_labels(labels_config) # Add coordinate system if coord_config: plot = plot + _build_coord(coord_config) # Apply theme if theme_config: plot = plot + _build_theme(theme_config) return plot except Exception as e: raise PlotBuildError(f"Failed to build plot: {str(e)}") from e
  • Helper function save_plot that saves the ggplot to file according to OutputConfig (format, size, dpi, directory), generating filename if needed and creating directories.
    def save_plot( plot: ggplot, output_config: OutputConfig ) -> dict[str, str]: """ Save plot to file. Args: plot: ggplot object output_config: Output configuration Returns: Dict with 'path' key containing the saved file path Raises: PlotBuildError: If plot cannot be saved """ try: # Create output directory if it doesn't exist output_dir = Path(output_config.directory).expanduser().resolve() output_dir.mkdir(parents=True, exist_ok=True) # Generate filename if not provided if output_config.filename: filename = output_config.filename else: filename = f"plot_{uuid.uuid4().hex[:8]}.{output_config.format}" # Full output path output_path = output_dir / filename # Save plot plot.save( filename=str(output_path), width=output_config.width, height=output_config.height, dpi=output_config.dpi, verbose=False, ) return { "path": str(output_path), "format": output_config.format, "width": output_config.width, "height": output_config.height, "dpi": output_config.dpi, } except Exception as e: raise PlotBuildError(f"Failed to save plot: {str(e)}") from e

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/Fervoyush/plotnine-mcp'

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