Edit matplotlib figures with drag-and-drop — and ask Claude Code to do the rest.
PlotCanvas is two things in one:
- A JupyterLab extension — capture any matplotlib figure as an interactive SVG, then drag the legend, title, or axis labels and watch the notebook cell code update automatically.
- A Claude Code MCP server — exposes your notebook's figures as tools Claude can read, render, and rewrite. No API key required beyond your existing Claude Code subscription.
You say in Claude Code:
"Move the legend to the bottom right and increase the title font size"
Claude calls:
find_plot_cells("analysis.ipynb") → finds cell 3
read_notebook_cell("analysis.ipynb", 3) → reads the code
render_figure(code) → sees the current figure as SVG
write_notebook_cell("analysis.ipynb", 3, patched_code)
You re-run the cell in JupyterLab → done.
For visual edits, drag directly in the JupyterLab panel — no conversation needed.
- Python >= 3.9
mcp >= 1.0.0matplotlib >= 3.7- Claude Code (for natural language edits)
- JupyterLab >= 4.0 (for the drag-and-drop panel)
pip install plotcanvas-mcpclaude mcp add plotcanvas python /path/to/mcp_server.pyOr with the full Python path on Windows:
claude mcp add plotcanvas "C:\Users\you\AppData\Local\Programs\Python\Python311\python.exe" "C:\path\to\plotcanvas\mcp_server.py"pip install -e ".[dev]"
jlpm install && jlpm run build
jupyter labextension develop . --overwrite
jupyter server extension enable plotcanvas
jupyter labClaude Code gets access to four tools:
| Tool | Description |
|---|---|
render_figure(code) |
Execute matplotlib code, return figure as SVG for Claude to inspect |
find_plot_cells(notebook_path) |
Find all cells containing matplotlib figures |
read_notebook_cell(notebook_path, cell_index) |
Read a cell's source code |
write_notebook_cell(notebook_path, cell_index, new_code) |
Write patched code back and clear outputs |
"In analysis.ipynb, move the legend to the lower left"
"Change the line colors to match the Nature journal palette"
"Add error bars to the bar chart in cell 4"
"Make all axis labels bold and increase font size to 12pt"
"Add a second y-axis for the impedance data"
- Run a cell containing a matplotlib figure
- Open PlotCanvas from the right sidebar (or right-click a code cell → Open PlotCanvas)
- Click Capture Figure — the figure renders as an interactive SVG
- Drag the legend, title, or axis labels — the cell code patches itself via AST rewriting and re-runs
Supported drag targets: legend, title, xlabel, ylabel.
plotcanvas/
├── mcp_server.py MCP server — 4 tools for Claude Code
├── src/ TypeScript/React (JupyterLab frontend)
│ ├── index.ts Extension entry point + command registration
│ ├── panel.tsx Panel widget — kernel communication + SVG display
│ └── components/
│ ├── Canvas.tsx SVG drag-and-drop (mouse events → position delta)
│ └── NLInput.tsx Claude Code hint banner
├── plotcanvas/ Python server extension (JupyterLab backend)
│ ├── handlers.py /plotcanvas/patch endpoint (drag → AST patch)
│ └── patcher.py SVG position delta → matplotlib AST rewrite
└── test_mcp.py Unit tests for all 4 MCP tools (16/16 passing)
No Anthropic API key required. The MCP server is pure Python — it reads files, runs subprocesses, and writes JSON. Claude Code is the intelligence; PlotCanvas is the matplotlib-specific tooling.
git clone https://github.com/rolston-lab-asu/plotcanvas-mcp
cd plotcanvas-mcp
pip install -e ".[dev]"
# Run tests
python test_mcp.py
# Watch mode for frontend
jlpm install
jlpm run watch- Undo/redo stack
- Multi-element drag selection
- Annotation drawing (add text boxes, arrows)
- Export to PNG/PDF at custom DPI
- Seaborn and Plotly figure support
- VS Code extension port
Issues and PRs welcome. Please open an issue first for large changes.
MIT