MatplotLibAPI 3.3.0__py3-none-any.whl → 4.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. MatplotLibAPI/__init__.py +4 -86
  2. MatplotLibAPI/accessor.py +288 -191
  3. MatplotLibAPI/area.py +235 -0
  4. MatplotLibAPI/bar.py +193 -0
  5. MatplotLibAPI/base_plot.py +88 -0
  6. MatplotLibAPI/box_violin.py +186 -0
  7. MatplotLibAPI/bubble.py +569 -0
  8. MatplotLibAPI/{Composite.py → composite.py} +70 -83
  9. MatplotLibAPI/heatmap.py +246 -0
  10. MatplotLibAPI/histogram.py +172 -0
  11. MatplotLibAPI/mcp/__init__.py +17 -0
  12. MatplotLibAPI/mcp/metadata.py +90 -0
  13. MatplotLibAPI/mcp/renderers.py +45 -0
  14. MatplotLibAPI/mcp_server.py +626 -0
  15. MatplotLibAPI/network/__init__.py +28 -0
  16. MatplotLibAPI/network/constants.py +22 -0
  17. MatplotLibAPI/{Network.py → network/core.py} +347 -809
  18. MatplotLibAPI/network/plot.py +597 -0
  19. MatplotLibAPI/network/scaling.py +56 -0
  20. MatplotLibAPI/pie.py +155 -0
  21. MatplotLibAPI/pivot.py +282 -0
  22. MatplotLibAPI/sankey.py +99 -0
  23. MatplotLibAPI/{StyleTemplate.py → style_template.py} +8 -4
  24. MatplotLibAPI/sunburst.py +139 -0
  25. MatplotLibAPI/{Table.py → table.py} +109 -93
  26. MatplotLibAPI/{Timeserie.py → timeserie.py} +99 -42
  27. MatplotLibAPI/{Treemap.py → treemap.py} +43 -55
  28. MatplotLibAPI/typing.py +12 -0
  29. MatplotLibAPI/{_visualization_utils.py → utils.py} +30 -13
  30. MatplotLibAPI/waffle.py +174 -0
  31. MatplotLibAPI/{Wordcloud.py → word_cloud.py} +188 -88
  32. {matplotlibapi-3.3.0.dist-info → matplotlibapi-4.0.1.dist-info}/METADATA +98 -9
  33. matplotlibapi-4.0.1.dist-info/RECORD +36 -0
  34. {matplotlibapi-3.3.0.dist-info → matplotlibapi-4.0.1.dist-info}/WHEEL +1 -1
  35. matplotlibapi-4.0.1.dist-info/entry_points.txt +2 -0
  36. MatplotLibAPI/Area.py +0 -80
  37. MatplotLibAPI/Bar.py +0 -83
  38. MatplotLibAPI/BoxViolin.py +0 -75
  39. MatplotLibAPI/Bubble.py +0 -460
  40. MatplotLibAPI/Heatmap.py +0 -121
  41. MatplotLibAPI/Histogram.py +0 -73
  42. MatplotLibAPI/Pie.py +0 -70
  43. MatplotLibAPI/Pivot.py +0 -134
  44. MatplotLibAPI/Sankey.py +0 -46
  45. MatplotLibAPI/Sunburst.py +0 -89
  46. MatplotLibAPI/Waffle.py +0 -86
  47. MatplotLibAPI/_typing.py +0 -17
  48. matplotlibapi-3.3.0.dist-info/RECORD +0 -26
  49. {matplotlibapi-3.3.0.dist-info → matplotlibapi-4.0.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,626 @@
1
+ """MCP server helpers for exposing MatplotLibAPI plotting tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from io import BytesIO
6
+ from pathlib import Path
7
+ from typing import Any, Dict, List, Optional
8
+
9
+ import matplotlib.pyplot as plt
10
+ import pandas as pd
11
+ from matplotlib.figure import Figure
12
+
13
+ from .style_template import BUBBLE_STYLE_TEMPLATE, StyleTemplate
14
+
15
+ from .bubble import Bubble
16
+ from .network import fplot_network
17
+ from .mcp.metadata import (
18
+ DEDICATED_PLOT_TOOLS,
19
+ PLOT_MODULE_PARAMETER_HINTS,
20
+ SHARED_INPUT_CONTRACT,
21
+ )
22
+ from .mcp.renderers import (
23
+ MATPLOTLIB_RENDERERS,
24
+ PLOTLY_RENDERERS,
25
+ SUPPORTED_PLOT_MODULES,
26
+ )
27
+
28
+ TableRecords = List[Dict[str, Any]]
29
+
30
+
31
+ def _load_dataframe(
32
+ csv_path: Optional[str] = None,
33
+ table: Optional[TableRecords] = None,
34
+ ) -> pd.DataFrame:
35
+ """Load plotting data from either a CSV file or table records."""
36
+ if csv_path is None and table is None:
37
+ raise ValueError("Provide either `csv_path` or `table`.")
38
+
39
+ if table is not None:
40
+ return pd.DataFrame(table)
41
+
42
+ data_path = Path(str(csv_path)).expanduser().resolve()
43
+ return pd.read_csv(data_path)
44
+
45
+
46
+ def _figure_to_png_bytes(fig: Figure) -> bytes:
47
+ """Serialize a Matplotlib figure to PNG bytes and close it."""
48
+ buffer = BytesIO()
49
+ fig.savefig(buffer, format="png", dpi=300, bbox_inches="tight")
50
+ plt.close(fig)
51
+ return buffer.getvalue()
52
+
53
+
54
+ def _build_bubble_chart_figure(
55
+ label: str,
56
+ x: str,
57
+ y: str,
58
+ z: str,
59
+ csv_path: Optional[str] = None,
60
+ table: Optional[TableRecords] = None,
61
+ title: Optional[str] = None,
62
+ max_values: int = 50,
63
+ center_to_mean: bool = False,
64
+ sort_by: Optional[str] = None,
65
+ ascending: bool = False,
66
+ style: StyleTemplate = BUBBLE_STYLE_TEMPLATE,
67
+ hline: bool = False,
68
+ vline: bool = False,
69
+ ) -> Figure:
70
+ """Create a bubble chart figure from tabular input."""
71
+ pd_df = _load_dataframe(csv_path=csv_path, table=table)
72
+ fig = Bubble(
73
+ pd_df=pd_df,
74
+ label=label,
75
+ x=x,
76
+ y=y,
77
+ z=z,
78
+ max_values=max_values,
79
+ center_to_mean=center_to_mean,
80
+ sort_by=sort_by,
81
+ ascending=ascending,
82
+ ).fplot(
83
+ title=title,
84
+ hline=hline,
85
+ vline=vline,
86
+ style=style,
87
+ )
88
+ return fig
89
+
90
+
91
+ def _build_network_chart_figure(
92
+ csv_path: Optional[str] = None,
93
+ table: Optional[TableRecords] = None,
94
+ edge_source_col: str = "source",
95
+ edge_target_col: str = "target",
96
+ edge_weight_col: str = "weight",
97
+ title: Optional[str] = None,
98
+ ) -> Figure:
99
+ """Create a network chart figure from tabular input."""
100
+ pd_df = _load_dataframe(csv_path=csv_path, table=table)
101
+ return fplot_network(
102
+ pd_df=pd_df,
103
+ edge_source_col=edge_source_col,
104
+ edge_target_col=edge_target_col,
105
+ edge_weight_col=edge_weight_col,
106
+ title=title,
107
+ )
108
+
109
+
110
+ def render_bubble_chart(
111
+ output_path: str,
112
+ label: str,
113
+ x: str,
114
+ y: str,
115
+ z: str,
116
+ csv_path: Optional[str] = None,
117
+ table: Optional[TableRecords] = None,
118
+ title: Optional[str] = None,
119
+ max_values: int = 50,
120
+ center_to_mean: bool = False,
121
+ sort_by: Optional[str] = None,
122
+ ascending: bool = False,
123
+ hline: bool = False,
124
+ vline: bool = False,
125
+ ) -> str:
126
+ """Render a bubble chart from table data and write it to disk.
127
+
128
+ Parameters
129
+ ----------
130
+ output_path : str
131
+ Path where the generated chart image is saved.
132
+ label : str
133
+ Column name used for bubble labels.
134
+ x : str
135
+ Column name for the x-axis values.
136
+ y : str
137
+ Column name for the y-axis values.
138
+ z : str
139
+ Column name used for bubble size.
140
+ csv_path : str, optional
141
+ Path to a CSV file containing plotting data. The default is None.
142
+ table : list[dict[str, Any]], optional
143
+ In-memory row records with column names as keys. The default is None.
144
+ title : str, optional
145
+ Chart title. The default is None.
146
+ max_values : int, optional
147
+ Maximum number of rows to include in the plot. The default is 50.
148
+ center_to_mean : bool, optional
149
+ Whether to center x-axis values around their mean. The default is False.
150
+ sort_by : str, optional
151
+ Column used to sort before selecting rows. The default is None.
152
+ ascending : bool, optional
153
+ Sort order used with ``sort_by``. The default is False.
154
+ hline : bool, optional
155
+ Whether to draw a horizontal mean line for y values. The default is False.
156
+ vline : bool, optional
157
+ Whether to draw a vertical mean line for x values. The default is False.
158
+
159
+ Returns
160
+ -------
161
+ str
162
+ The resolved output path for the generated image.
163
+ """
164
+ out_path = Path(output_path).expanduser().resolve()
165
+ if out_path.suffix.lower() != ".png":
166
+ out_path = out_path.with_suffix(".png")
167
+ out_path.parent.mkdir(parents=True, exist_ok=True)
168
+
169
+ out_path.write_bytes(
170
+ render_bubble_chart_octet(
171
+ label=label,
172
+ x=x,
173
+ y=y,
174
+ z=z,
175
+ csv_path=csv_path,
176
+ table=table,
177
+ title=title,
178
+ max_values=max_values,
179
+ center_to_mean=center_to_mean,
180
+ sort_by=sort_by,
181
+ ascending=ascending,
182
+ hline=hline,
183
+ vline=vline,
184
+ )
185
+ )
186
+ return str(out_path)
187
+
188
+
189
+ def render_bubble_chart_octet(
190
+ label: str,
191
+ x: str,
192
+ y: str,
193
+ z: str,
194
+ csv_path: Optional[str] = None,
195
+ table: Optional[TableRecords] = None,
196
+ title: Optional[str] = None,
197
+ max_values: int = 50,
198
+ center_to_mean: bool = False,
199
+ sort_by: Optional[str] = None,
200
+ ascending: bool = False,
201
+ hline: bool = False,
202
+ vline: bool = False,
203
+ ) -> bytes:
204
+ """Render a bubble chart and return PNG bytes as an octet payload.
205
+
206
+ Parameters
207
+ ----------
208
+ label : str
209
+ Column name used for bubble labels.
210
+ x : str
211
+ Column name for the x-axis values.
212
+ y : str
213
+ Column name for the y-axis values.
214
+ z : str
215
+ Column name used for bubble size.
216
+ csv_path : str, optional
217
+ Path to a CSV file containing plotting data. The default is None.
218
+ table : list[dict[str, Any]], optional
219
+ In-memory row records with column names as keys. The default is None.
220
+ title : str, optional
221
+ Chart title. The default is None.
222
+ max_values : int, optional
223
+ Maximum number of rows to include in the plot. The default is 50.
224
+ center_to_mean : bool, optional
225
+ Whether to center x-axis values around their mean. The default is False.
226
+ sort_by : str, optional
227
+ Column used to sort before selecting rows. The default is None.
228
+ ascending : bool, optional
229
+ Sort order used with ``sort_by``. The default is False.
230
+ hline : bool, optional
231
+ Whether to draw a horizontal mean line for y values. The default is False.
232
+ vline : bool, optional
233
+ Whether to draw a vertical mean line for x values. The default is False.
234
+
235
+ Returns
236
+ -------
237
+ bytes
238
+ PNG payload bytes suitable for ``application/octet-stream`` responses.
239
+ """
240
+ fig = _build_bubble_chart_figure(
241
+ label=label,
242
+ x=x,
243
+ y=y,
244
+ z=z,
245
+ csv_path=csv_path,
246
+ table=table,
247
+ title=title,
248
+ max_values=max_values,
249
+ center_to_mean=center_to_mean,
250
+ sort_by=sort_by,
251
+ ascending=ascending,
252
+ hline=hline,
253
+ vline=vline,
254
+ )
255
+ return _figure_to_png_bytes(fig)
256
+
257
+
258
+ def render_network_chart_octet(
259
+ csv_path: Optional[str] = None,
260
+ table: Optional[TableRecords] = None,
261
+ edge_source_col: str = "source",
262
+ edge_target_col: str = "target",
263
+ edge_weight_col: str = "weight",
264
+ title: Optional[str] = None,
265
+ ) -> bytes:
266
+ """Render a network chart and return PNG bytes as an octet payload.
267
+
268
+ Parameters
269
+ ----------
270
+ csv_path : str, optional
271
+ Path to a CSV file containing edge data. The default is None.
272
+ table : list[dict[str, Any]], optional
273
+ In-memory row records with edge columns as keys. The default is None.
274
+ edge_source_col : str, optional
275
+ Column name for source nodes. The default is ``"source"``.
276
+ edge_target_col : str, optional
277
+ Column name for target nodes. The default is ``"target"``.
278
+ edge_weight_col : str, optional
279
+ Column name for edge weights. The default is ``"weight"``.
280
+ title : str, optional
281
+ Chart title. The default is None.
282
+
283
+ Returns
284
+ -------
285
+ bytes
286
+ PNG payload bytes suitable for ``application/octet-stream`` responses.
287
+ """
288
+ fig = _build_network_chart_figure(
289
+ csv_path=csv_path,
290
+ table=table,
291
+ edge_source_col=edge_source_col,
292
+ edge_target_col=edge_target_col,
293
+ edge_weight_col=edge_weight_col,
294
+ title=title,
295
+ )
296
+ return _figure_to_png_bytes(fig)
297
+
298
+
299
+ def get_plot_module_metadata() -> Dict[str, Any]:
300
+ """Return MCP metadata for module discoverability and exploration.
301
+
302
+ Returns
303
+ -------
304
+ dict[str, Any]
305
+ Discoverability metadata containing supported modules, shared input
306
+ contract, and parameter hints per module.
307
+ """
308
+ return {
309
+ "supported_plot_modules": SUPPORTED_PLOT_MODULES,
310
+ "shared_input_contract": SHARED_INPUT_CONTRACT,
311
+ "parameter_hints": PLOT_MODULE_PARAMETER_HINTS,
312
+ "dedicated_tools": DEDICATED_PLOT_TOOLS,
313
+ }
314
+
315
+
316
+ def render_plot_module_octet(
317
+ plot_module: str,
318
+ params: Dict[str, Any],
319
+ csv_path: Optional[str] = None,
320
+ table: Optional[TableRecords] = None,
321
+ ) -> bytes:
322
+ """Render a supported plot module and return PNG bytes.
323
+
324
+ Parameters
325
+ ----------
326
+ plot_module : str
327
+ Plot module key.
328
+ params : dict[str, Any]
329
+ Module-specific plotting parameters excluding the DataFrame argument.
330
+ csv_path : str, optional
331
+ Path to a CSV file containing source data. The default is None.
332
+ table : list[dict[str, Any]], optional
333
+ In-memory row records with source columns as keys. The default is None.
334
+
335
+ Returns
336
+ -------
337
+ bytes
338
+ PNG payload bytes suitable for ``application/octet-stream`` responses.
339
+
340
+ Raises
341
+ ------
342
+ ValueError
343
+ If ``plot_module`` is not supported.
344
+ """
345
+ if plot_module == "bubble":
346
+ return render_bubble_chart_octet(csv_path=csv_path, table=table, **params)
347
+ if plot_module == "network":
348
+ return render_network_chart_octet(csv_path=csv_path, table=table, **params)
349
+
350
+ pd_df = _load_dataframe(csv_path=csv_path, table=table)
351
+
352
+ if plot_module in MATPLOTLIB_RENDERERS:
353
+ fig = MATPLOTLIB_RENDERERS[plot_module](pd_df=pd_df, **params)
354
+ return _figure_to_png_bytes(fig)
355
+
356
+ if plot_module in PLOTLY_RENDERERS:
357
+ fig = PLOTLY_RENDERERS[plot_module](pd_df=pd_df, **params)
358
+ return bytes(fig.to_image(format="png"))
359
+
360
+ raise ValueError(
361
+ f"Unsupported plot_module '{plot_module}'. Supported: {SUPPORTED_PLOT_MODULES}"
362
+ )
363
+
364
+
365
+ def create_mcp_server() -> Any:
366
+ """Create an MCP server exposing MatplotLibAPI plotting tools.
367
+
368
+ Returns
369
+ -------
370
+ FastMCP
371
+ A configured FastMCP server instance.
372
+
373
+ Raises
374
+ ------
375
+ ImportError
376
+ If the optional ``mcp`` package is not installed.
377
+ """
378
+ try:
379
+ from mcp.server.fastmcp import FastMCP # pyright: ignore[reportMissingImports]
380
+ except ImportError as exc: # pragma: no cover
381
+ raise ImportError(
382
+ "Install MatplotLibAPI with MCP support: `pip install MatplotLibAPI[mcp]`."
383
+ ) from exc
384
+
385
+ mcp = FastMCP("MatplotLibAPI")
386
+
387
+ @mcp.tool()
388
+ def plot_bubble(
389
+ label: str,
390
+ x: str,
391
+ y: str,
392
+ z: str,
393
+ csv_path: Optional[str] = None,
394
+ table: Optional[TableRecords] = None,
395
+ title: Optional[str] = None,
396
+ max_values: int = 50,
397
+ center_to_mean: bool = False,
398
+ sort_by: Optional[str] = None,
399
+ ascending: bool = False,
400
+ hline: bool = False,
401
+ vline: bool = False,
402
+ ) -> bytes:
403
+ """Generate a bubble chart and return PNG octets."""
404
+ return render_bubble_chart_octet(
405
+ label=label,
406
+ x=x,
407
+ y=y,
408
+ z=z,
409
+ csv_path=csv_path,
410
+ table=table,
411
+ title=title,
412
+ max_values=max_values,
413
+ center_to_mean=center_to_mean,
414
+ sort_by=sort_by,
415
+ ascending=ascending,
416
+ hline=hline,
417
+ vline=vline,
418
+ )
419
+
420
+ @mcp.tool()
421
+ def plot_network(
422
+ csv_path: Optional[str] = None,
423
+ table: Optional[TableRecords] = None,
424
+ edge_source_col: str = "source",
425
+ edge_target_col: str = "target",
426
+ edge_weight_col: str = "weight",
427
+ title: Optional[str] = None,
428
+ ) -> bytes:
429
+ """Generate a network chart and return PNG octets."""
430
+ return render_network_chart_octet(
431
+ csv_path=csv_path,
432
+ table=table,
433
+ edge_source_col=edge_source_col,
434
+ edge_target_col=edge_target_col,
435
+ edge_weight_col=edge_weight_col,
436
+ title=title,
437
+ )
438
+
439
+ @mcp.tool()
440
+ def plot_module(
441
+ plot_module: str,
442
+ params: Dict[str, Any],
443
+ csv_path: Optional[str] = None,
444
+ table: Optional[TableRecords] = None,
445
+ ) -> bytes:
446
+ """Generate a chart for any supported plot module and return octets."""
447
+ return render_plot_module_octet(
448
+ plot_module=plot_module,
449
+ params=params,
450
+ csv_path=csv_path,
451
+ table=table,
452
+ )
453
+
454
+ def _render_module(
455
+ module_name: str,
456
+ params: Dict[str, Any],
457
+ csv_path: Optional[str] = None,
458
+ table: Optional[TableRecords] = None,
459
+ ) -> bytes:
460
+ """Render a module-specific chart as PNG octets."""
461
+ return render_plot_module_octet(
462
+ plot_module=module_name,
463
+ params=params,
464
+ csv_path=csv_path,
465
+ table=table,
466
+ )
467
+
468
+ @mcp.tool()
469
+ def plot_bar(
470
+ params: Dict[str, Any],
471
+ csv_path: Optional[str] = None,
472
+ table: Optional[TableRecords] = None,
473
+ ) -> bytes:
474
+ """Generate a bar chart and return PNG octets."""
475
+ return _render_module("bar", params=params, csv_path=csv_path, table=table)
476
+
477
+ @mcp.tool()
478
+ def plot_histogram(
479
+ params: Dict[str, Any],
480
+ csv_path: Optional[str] = None,
481
+ table: Optional[TableRecords] = None,
482
+ ) -> bytes:
483
+ """Generate a histogram chart and return PNG octets."""
484
+ return _render_module(
485
+ "histogram", params=params, csv_path=csv_path, table=table
486
+ )
487
+
488
+ @mcp.tool()
489
+ def plot_box_violin(
490
+ params: Dict[str, Any],
491
+ csv_path: Optional[str] = None,
492
+ table: Optional[TableRecords] = None,
493
+ ) -> bytes:
494
+ """Generate a box/violin chart and return PNG octets."""
495
+ return _render_module(
496
+ "box_violin", params=params, csv_path=csv_path, table=table
497
+ )
498
+
499
+ @mcp.tool()
500
+ def plot_heatmap(
501
+ params: Dict[str, Any],
502
+ csv_path: Optional[str] = None,
503
+ table: Optional[TableRecords] = None,
504
+ ) -> bytes:
505
+ """Generate a heatmap chart and return PNG octets."""
506
+ return _render_module("heatmap", params=params, csv_path=csv_path, table=table)
507
+
508
+ @mcp.tool()
509
+ def plot_correlation_matrix(
510
+ params: Dict[str, Any],
511
+ csv_path: Optional[str] = None,
512
+ table: Optional[TableRecords] = None,
513
+ ) -> bytes:
514
+ """Generate a correlation matrix chart and return PNG octets."""
515
+ return _render_module(
516
+ "correlation_matrix", params=params, csv_path=csv_path, table=table
517
+ )
518
+
519
+ @mcp.tool()
520
+ def plot_area(
521
+ params: Dict[str, Any],
522
+ csv_path: Optional[str] = None,
523
+ table: Optional[TableRecords] = None,
524
+ ) -> bytes:
525
+ """Generate an area chart and return PNG octets."""
526
+ return _render_module("area", params=params, csv_path=csv_path, table=table)
527
+
528
+ @mcp.tool()
529
+ def plot_pie(
530
+ params: Dict[str, Any],
531
+ csv_path: Optional[str] = None,
532
+ table: Optional[TableRecords] = None,
533
+ ) -> bytes:
534
+ """Generate a pie chart and return PNG octets."""
535
+ return _render_module("pie", params=params, csv_path=csv_path, table=table)
536
+
537
+ @mcp.tool()
538
+ def plot_waffle(
539
+ params: Dict[str, Any],
540
+ csv_path: Optional[str] = None,
541
+ table: Optional[TableRecords] = None,
542
+ ) -> bytes:
543
+ """Generate a waffle chart and return PNG octets."""
544
+ return _render_module("waffle", params=params, csv_path=csv_path, table=table)
545
+
546
+ @mcp.tool()
547
+ def plot_sankey(
548
+ params: Dict[str, Any],
549
+ csv_path: Optional[str] = None,
550
+ table: Optional[TableRecords] = None,
551
+ ) -> bytes:
552
+ """Generate a sankey chart and return PNG octets."""
553
+ return _render_module("sankey", params=params, csv_path=csv_path, table=table)
554
+
555
+ @mcp.tool()
556
+ def plot_table(
557
+ params: Dict[str, Any],
558
+ csv_path: Optional[str] = None,
559
+ table: Optional[TableRecords] = None,
560
+ ) -> bytes:
561
+ """Generate a table chart and return PNG octets."""
562
+ return _render_module("table", params=params, csv_path=csv_path, table=table)
563
+
564
+ @mcp.tool()
565
+ def plot_timeserie(
566
+ params: Dict[str, Any],
567
+ csv_path: Optional[str] = None,
568
+ table: Optional[TableRecords] = None,
569
+ ) -> bytes:
570
+ """Generate a timeserie chart and return PNG octets."""
571
+ return _render_module(
572
+ "timeserie", params=params, csv_path=csv_path, table=table
573
+ )
574
+
575
+ @mcp.tool()
576
+ def plot_wordcloud(
577
+ params: Dict[str, Any],
578
+ csv_path: Optional[str] = None,
579
+ table: Optional[TableRecords] = None,
580
+ ) -> bytes:
581
+ """Generate a wordcloud chart and return PNG octets."""
582
+ return _render_module(
583
+ "wordcloud", params=params, csv_path=csv_path, table=table
584
+ )
585
+
586
+ @mcp.tool()
587
+ def plot_treemap(
588
+ params: Dict[str, Any],
589
+ csv_path: Optional[str] = None,
590
+ table: Optional[TableRecords] = None,
591
+ ) -> bytes:
592
+ """Generate a treemap chart and return PNG octets."""
593
+ return _render_module("treemap", params=params, csv_path=csv_path, table=table)
594
+
595
+ @mcp.tool()
596
+ def plot_sunburst(
597
+ params: Dict[str, Any],
598
+ csv_path: Optional[str] = None,
599
+ table: Optional[TableRecords] = None,
600
+ ) -> bytes:
601
+ """Generate a sunburst chart and return PNG octets."""
602
+ return _render_module("sunburst", params=params, csv_path=csv_path, table=table)
603
+
604
+ @mcp.tool()
605
+ def describe_plot_modules() -> Dict[str, Any]:
606
+ """Describe MCP plot-module capabilities for tool exploration.
607
+
608
+ Returns
609
+ -------
610
+ dict[str, Any]
611
+ Metadata including supported module names, shared input contract,
612
+ and parameter-hint dictionaries.
613
+ """
614
+ return get_plot_module_metadata()
615
+
616
+ return mcp
617
+
618
+
619
+ def main() -> None:
620
+ """Run the MCP server over stdio transport."""
621
+ server = create_mcp_server()
622
+ server.run(transport="stdio")
623
+
624
+
625
+ if __name__ == "__main__": # pragma: no cover
626
+ main()
@@ -0,0 +1,28 @@
1
+ """Network plotting tools for graph-based visualizations.
2
+
3
+ This package preserves the public ``MatplotLibAPI.network`` API while
4
+ organizing implementation details in submodules.
5
+ """
6
+
7
+ from .constants import _DEFAULT, _WEIGHT_PERCENTILES
8
+ from .core import NETWORK_STYLE_TEMPLATE, NetworkGraph
9
+ from .plot import (
10
+ aplot_network,
11
+ aplot_network_node,
12
+ aplot_network_components,
13
+ fplot_network,
14
+ fplot_network_node,
15
+ fplot_network_components,
16
+ )
17
+ from .scaling import _scale_weights, _softmax
18
+
19
+ __all__ = [
20
+ "aplot_network",
21
+ "aplot_network_node",
22
+ "aplot_network_components",
23
+ "fplot_network",
24
+ "fplot_network_node",
25
+ "fplot_network_components",
26
+ "NETWORK_STYLE_TEMPLATE",
27
+ "NetworkGraph",
28
+ ]
@@ -0,0 +1,22 @@
1
+ """Constants used by network plotting helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+
7
+ _DEFAULT = {
8
+ "MAX_EDGES": 100,
9
+ "MAX_NODES": 30,
10
+ "MIN_NODE_SIZE": 100,
11
+ "MAX_NODE_SIZE": 2000,
12
+ "MAX_EDGE_WIDTH": 10,
13
+ "GRAPH_SCALE": 2,
14
+ "MAX_FONT_SIZE": 20,
15
+ "MIN_FONT_SIZE": 8,
16
+ "SPRING_LAYOUT_K": 1.0,
17
+ "SPRING_LAYOUT_SEED": 42,
18
+ }
19
+
20
+ _WEIGHT_PERCENTILES = np.arange(10, 100, 10)
21
+
22
+ __all__ = ["_DEFAULT", "_WEIGHT_PERCENTILES"]