mcp-charts / src /server.py
mfallahian's picture
fix: image payload type
f53313f
Raw
History Blame Contribute Delete
2.99 kB
"""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()