Spaces:
Sleeping
Sleeping
| """MCP server that generates charts and returns static image data.""" | |
| from __future__ import annotations | |
| import base64 | |
| import inspect | |
| import os | |
| from typing import Any, Literal | |
| from mcp.server.fastmcp import FastMCP, Image | |
| from src.charting import render_chart_png_base64 | |
| DEFAULT_HOST = "0.0.0.0" | |
| DEFAULT_PORT = 7860 | |
| DEFAULT_MCP_PATH = "/mcp" | |
| def _read_port() -> int: | |
| raw_port = os.getenv("PORT", str(DEFAULT_PORT)).strip() | |
| try: | |
| return int(raw_port) | |
| except ValueError: | |
| return DEFAULT_PORT | |
| def _fastmcp_ctor_kwargs_from_env() -> dict[str, Any]: | |
| ctor_params = inspect.signature(FastMCP).parameters | |
| kwargs: dict[str, Any] = {} | |
| host = os.getenv("HOST", DEFAULT_HOST).strip() or DEFAULT_HOST | |
| port = _read_port() | |
| path = os.getenv("MCP_PATH", DEFAULT_MCP_PATH).strip() or DEFAULT_MCP_PATH | |
| if "host" in ctor_params: | |
| kwargs["host"] = host | |
| if "port" in ctor_params: | |
| kwargs["port"] = port | |
| if "streamable_http_path" in ctor_params: | |
| kwargs["streamable_http_path"] = path | |
| return kwargs | |
| def _create_mcp_server() -> FastMCP: | |
| server = FastMCP("mcp-charts", **_fastmcp_ctor_kwargs_from_env()) | |
| _register_tools(server) | |
| return server | |
| def generate_chart( | |
| chart_type: Literal["line", "bar", "scatter", "pie", "grouped_bar", "stacked_bar"], | |
| x_values: list[float | str], | |
| y_values: list[float], | |
| y_series: list[list[float]] | None = None, | |
| series_labels: list[str] | None = None, | |
| pie_labels: list[str] | None = None, | |
| title: str = "Generated Chart", | |
| x_label: str = "X", | |
| y_label: str = "Y", | |
| color: str = "#1f77b4", | |
| ) -> Image: | |
| """Return a typed MCP image payload so clients can render directly.""" | |
| image_base64 = render_chart_png_base64( | |
| chart_type=chart_type, | |
| x_values=x_values, | |
| y_values=y_values, | |
| y_series=y_series, | |
| series_labels=series_labels, | |
| pie_labels=pie_labels, | |
| title=title, | |
| x_label=x_label, | |
| y_label=y_label, | |
| color=color, | |
| ) | |
| return Image(data=base64.b64decode(image_base64), format="png") | |
| def _register_tools(server: FastMCP) -> None: | |
| server.tool( | |
| name="generate_chart", | |
| description=( | |
| "Generate a chart image (line, bar, scatter, pie, grouped_bar, stacked_bar) and return a typed PNG image." | |
| ), | |
| )(generate_chart) | |
| mcp = _create_mcp_server() | |
| def main() -> None: | |
| run_params = inspect.signature(mcp.run).parameters | |
| transport = os.getenv("MCP_TRANSPORT", "streamable-http").strip().lower() | |
| if transport == "stdio": | |
| kwargs = {"transport": "stdio"} if "transport" in run_params else {} | |
| try: | |
| mcp.run(**kwargs) | |
| except TypeError: | |
| mcp.run() | |
| return | |
| kwargs = {"transport": "streamable-http"} if "transport" in run_params else {} | |
| try: | |
| mcp.run(**kwargs) | |
| except TypeError: | |
| mcp.run() | |
| if __name__ == "__main__": | |
| main() | |