Publication-quality plotting for data scientists, engineers, and AI agents
A lightweight, agent-friendly plotting DSL with 85% fewer tokens than matplotlib. Create stunning visualizations from CSV, TXT, or DAT files in seconds.
Gnuplot-style syntax. 85% fewer tokens than matplotlib. Perfect for AI agents.
CSV, TXT, DAT, Excel, JSON. Automatic format detection.
Lines, markers, error bars, scatter, heatmaps, contours, distributions, and more.
Scientific notation, custom ticks, date formatting, log scales, rotations.
Flexible multi-panel layouts with custom grid definitions.
Plot mathematical expressions with NumPy. Parameter control included.
Save/replay configurations as JSON. Perfect for research.
Use as a library. Full programmatic control over plots.
pip3 install multifunctionplotter
Then run anywhere:
mfp --help
pip3 install multifunctionplotter && mfp-mcp
Claude Code:
claude mcp add mfp -- mfp-mcp
opencode — find the path first:
which mfp-mcp
Then open ~/.config/opencode/opencode.json and add:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"mfp": {
"type": "local",
"command": [""]
}
}
}
mfp data.csv using 1:2 with lines
mfp data.csv using 1:2 with lines title "Stock Prices" xlabel "Date" ylabel "Close"
mfp data.csv using 1:2 with lines --save output.pdf
mfp data.csv using 1:2 with lines --figsize 10:5
mfp data.csv using 1:2 with lines, data.csv using 1:3 with dashed
mfp --help
The MFP CLI command and Python tools work slightly differently:
plot.json for replaymfp_multi_plot, mfp_plot) handle complex plots more reliably| Token | Format | Description | Example |
|---|---|---|---|
using / u |
X:Y | Column indices (1-based for text, 0-based for CSV) | using 1:2 |
with / w |
style | Plot style (see Styles section) | with lines |
title |
"text" | Plot title | title "Stock Prices" |
xlabel |
"text" | X-axis label | xlabel "Date" |
ylabel |
"text" | Y-axis label | ylabel "Price" |
legend / lg |
label | Legend entry (no spaces) | legend "Series A" |
linewidth / lw |
int | Line width in points (default: 2) | linewidth 3 |
linecolor / lc |
color | Color: tab:red, #3a7ab3, steelblue, etc. | linecolor tab:blue |
xrange |
min:max | Force x-axis limits | xrange 0:100 |
yrange |
min:max | Force y-axis limits | yrange 0:1000 |
bin |
int | Histogram bin count (default: auto) | bin 30 |
func: |
"expr" | Math expression (requires xrange) | func: "f(x) = np.sin(x)" |
| Flag | Description | Example |
|---|---|---|
--xlog |
Set x-axis to logarithmic scale | --xlog |
--ylog |
Set y-axis to logarithmic scale | --ylog |
--save |
Save figure to file (format from extension) | --save output.pdf |
--subplot |
Create subplot mosaic layout | --subplot AB-CD |
--figsize |
Set figure size (width:height) | --figsize 10:5 |
--help |
Display help documentation | --help |
--list-styles |
List all available plot styles | --list-styles |
| Token | Format | Description |
|---|---|---|
yerr |
<col> | 1-based column index holding ±σ values |
capsize |
int | Cap width in points (default: 4) |
mfp data.csv using 1:2 with errorbars yerr 3 capsize 5
Same tokens as errorbars; capsize is ignored.
mfp data.csv using 1:2 with errorshade yerr 3
| Token | Format | Description |
|---|---|---|
cmap |
<col> | 1-based column index for colour values (required) |
colormap |
name | Matplotlib colormap (default: viridis) |
cbar_label |
"text" | Colorbar label (optional) |
mfp results.csv using 1:2 with scatter cmap 3 colormap plasma cbar_label "Temp (K)"
using, xrange, and yrange tokens are ignored.
| Style | Description | Example |
|---|---|---|
heatmap |
2D heatmap (imshow) — best for raster data | with heatmap |
contour |
Contour lines only | with contour levels 15 |
contourf |
Filled contours | with contourf levels 20 |
colormap <name> — Colormap (default: viridis)levels <int> — Number of contour levels (default: 10)cbar_label "text" — Colorbar label| Style | Alias | Description | Example |
|---|---|---|---|
lines | l | Solid line | with lines |
dashed | Dashed line | with dashed | |
dotted | Dotted line | with dotted | |
points | p | Circle markers only | with points |
linespoints | lp | Line + circle markers | with linespoints |
stars | Star markers | with stars | |
d | Diamond markers | with d |
| Style | Description | Example |
|---|---|---|
hist | Histogram (seaborn histplot) | with hist bin 30 |
kde | Kernel Density Estimation | with kde |
box | Box-and-whisker plot | with box |
violin | Violin plot | with violin |
| Style | Description | Requirements |
|---|---|---|
errorbars / eb | Discrete error bars | Requires yerr <col> |
errorshade / es | Shaded ±σ band | Requires yerr <col> |
scatter | Scatter with colormap | Requires cmap <col> |
heatmap | 2D heatmap | 2D matrix file |
contour | Contour lines | 2D matrix file |
contourf | Filled contours | 2D matrix file |
mfp data.csv using 1:2 with lines sci_notation both
Options: x, y, or both
mfp angles.csv using 1:2 with points xticks "0,90,180,270"
mfp data.csv using 1:2 with lines xtick_rotation 45 ytick_rotation 90
mfp timeseries.csv using 1:2 with lines date_format "%Y-%m-%d"
Format codes: %Y (year), %m (month), %d (day), %H (hour), %M (min), %S (sec)
mfp func: "f(x) = np.sin(x)" xrange 0:10
mfp func: "f(x,a=2,b=0.5) = a*np.exp(-b*x)" xrange 0:10
np.sin(x), np.cos(x), np.tan(x) — Trigonometricnp.exp(x), np.log(x), np.sqrt(x) — Exponential & Logarithmicnp.where(condition, true_val, false_val) — Conditionalnp.linspace(start, stop, num) — Linear spacingmfp --subplot AB-CD data1.csv using 1:2 with lines, data2.csv using 1:2 with hist, data3.csv using 1:2 with kde, data4.csv using 1:2 with scatter cmap 3
mfp --subplot AA-BC top_spans_two, bottom_left, bottom_right
- to separate rowsmfp "cmd1 xlabel 'X' ylabel 'Y', cmd2 xlabel 'A' ylabel 'B'"
hist style requires both x and y columns:
# CORRECTmfp data.csv using 5:0 with hist bin 25# WILL ERROR - missing y columnmfp data.csv using 5 with hist bin 25
mfp spectrum.csv using 1:2 with lines --xlog
mfp spectrum.csv using 1:2 with lines --ylog
mfp spectrum.csv using 1:2 with lines --xlog --ylog
mfp data.csv using 1:2 with lines lc tab:blue legend "Series A"
mfp data.csv using 1:2 with lines lc green, data.csv using 1:3 with dashed lc red
mfp data.csv using 1:2 with errorbars yerr 3
mfp data.csv using 1:2 with errorshade yerr 3 lc steelblue, data.csv using 1:2 with lines lc steelblue
mfp results.csv using 1:2 with scatter cmap 3 colormap plasma
mfp matrix.dat with heatmap colormap viridis cbar_label "Intensity"
mfp matrix.dat with contourf levels 20 colormap RdBu
mfp spectrum.csv using 1:2 with lines --xlog --ylog
mfp samples.csv using 0:1 with hist bin 30 lc tab:orange
mfp func: "f(x,a=1) = a*np.sin(x)" xrange 0:10 lc tab:green
mfp --subplot AB-CD d1.csv using 1:2 with lines, d2.csv using 1:2 with hist, d3.csv using 1:2 with kde, d4.csv using 1:2 with scatter cmap 3
mfp data.csv using 1:2 with lines title "Results" xlabel "Time" ylabel "Value" sci_notation y --save figure.pdf
mfp plot.json
MFP saves last plot config to plot.json
Interactive CLI tool for exploring, cleaning, and transforming tabular data without writing code.
mfp DM
Launches interactive data manipulation REPL.
show | Print current DataFrame |
head [N] | First N rows (default 10) |
tail [N] | Last N rows (default 10) |
properties / props | Column dtypes, NaN counts, stats |
counts <col> | Frequency of unique values |
filter <expr> | Pandas query expression |
slice start:end | Keep rows [start, end) |
sort <col> asc|desc | Sort by column |
rename old:new,... | Rename columns |
cast <col> type | Change dtype (int/float/str) |
addcol <name> expr | Add column from expression |
modify <col> old new | Replace values |
delete <col> | Drop column or row |
dedup [col1,col2] | Remove duplicate rows |
fillna <col> value | Fill NaN cells |
dropna <col> | Drop rows with NaN |
load <file> | Load new file |
save <file> | Save to file |
append <file> | Append rows from file |
merge <file> col | Merge on column |
generate expr | Generate data from expression |
undo | Revert last operation |
redo | Re-apply undone operation |
help command to see all available actionsundo / redo to navigate through changes (up to 20 steps)price > 100, name == "test"df.eval() expressions: addcol total price * quantity