An MCP server for cellpy. It lets an agent — or a chat window — load battery cells, collect them into frames, render figures, export data, look up how any cellpy call works, and set up a batch project. Without writing any Python.
pip install cellpy-mcp
cellpy mcp install # register it with your chat clientThen restart the client and ask it to plot something.
- People building on cellpy — a GUI, a script that automates cell handling.
The tools are the useful API, and
describe_apigives real signatures from the version you have installed. - People who just want to ask. Open a chat window, point it at a file, ask for capacity versus cycle. The prompts below are pickable starting points, so you do not have to know what to ask for.
- People who would rather not use a terminal.
new_projectiscellpy newwithout the command line.
Nothing is hosted. Under stdio your chat client starts the server itself, on your machine, reading your files — there is no service to sign up for and nothing leaves the machine except what you paste into the chat.
Cells and figures:
| Tool | What it gives you |
|---|---|
list_instruments |
loaders, and whether each can actually run on this machine |
load_cell |
a handle, cycle count, mass, summary column names |
list_cells |
what is loaded |
describe_plot_families |
the 20 summary families, marked available or missing-columns |
collect |
a handle, row count, columns, is_grouped, direction counts |
preview_collection |
a few rows, capped at 20 |
render |
writes a figure; returns trace types and points plotted |
export_collection |
writes csv/parquet/json; returns rows and bytes |
The cellpy API — "how does this call work, and what are its arguments":
| Tool | What it gives you |
|---|---|
search_api |
calls matching a name or a docstring line |
describe_api |
signature, argument types and defaults, docstring, undocumented_parameters, optionally source |
Batch templating:
| Tool | What it gives you |
|---|---|
list_templates |
registered and local templates, and which is the default |
new_project |
a project from a template; returns the notebooks it made |
Prompts: analyse_cell, start_batch_project, explain_call.
cellpy mcp install writes the block for you. Pass --client for anything
other than Claude Desktop:
cellpy mcp install # Claude Desktop (default)
cellpy mcp install --client cursor
cellpy mcp install --client vscode
cellpy mcp install --list-clients # where each one keeps its configAdd --dry-run to see the target first. Restart the client afterwards — none
of them re-read the file while running.
| Client | File it writes | Key |
|---|---|---|
| Claude Desktop | %APPDATA%/Claude/claude_desktop_config.json · ~/Library/Application Support/Claude/… · ~/.config/Claude/… |
mcpServers |
| Cursor | ~/.cursor/mcp.json — global; a project's .cursor/mcp.json wins over it |
mcpServers |
| VS Code | %APPDATA%/Code/User/mcp.json · ~/Library/Application Support/Code/User/mcp.json · ~/.config/Code/User/mcp.json |
servers |
VS Code names the key servers, not mcpServers. If you edit that file by
hand, this is the mistake to avoid: the wrong key parses, saves, and does
nothing at all.
Claude Code is not registered by editing a file — its servers live in
~/.claude.json alongside your sign-in session and per-project trust
decisions, or in a project-scoped .mcp.json. It has a command that handles
scopes properly, so use that:
claude mcp add cellpy --env CELLPY_MCP_ROOT=/path/to/cells -- python -m cellpy_mcpcellpy mcp install --list-clients prints that line filled in with the
interpreter and the roots for your machine.
Any client that speaks stdio can run this; it is an ordinary MCP server. The block is the same everywhere except the top-level key:
{
"mcpServers": {
"cellpy": {
"command": "/path/to/python",
"args": ["-m", "cellpy_mcp"],
"env": { "CELLPY_MCP_ROOT": "/path/to/cells" }
}
}
}Use the full path to the interpreter that has cellpy-mcp installed, not a
bare python: a desktop client activates no virtualenv and inherits no shell
PATH, which is the most common reason a server shows up as failed.
cellpy mcp status says which clients it can see cellpy registered with.
Everything is confined to a set of roots, and both reads and writes are checked — an unchecked write would make the read check decoration.
By default the roots are the directories cellpy already knows about
(rawdatadir, cellpydatadir, outdatadir, notebookdir), because you told
cellpy where your data is when you set it up. Override with CELLPY_MCP_ROOT,
which takes several directories separated the way PATH separates them:
CELLPY_MCP_ROOT=/data/cells:/data/out cellpy mcp serveRoots that are not plain local directories are dropped: rawdatadir is often
scp://host/…, and containment here is pathlib-based and cannot express
"inside that remote share". If cellpy has no configured paths at all, the
single root is ~/cellpy_mcp — never your whole filesystem.
Handles, not data. Only preview_collection returns rows. A tool result
goes into a model's context window, and one collected summary is ~29 kB of CSV
while a raw figure can be several MB.
Results carry the traps. render returns trace_types alongside
points_plotted and rows_collected, so an agent that asked for a density film
can see it got histogram2d rather than lines, and one that forgot
direction="both" can see it plotted 891 of 2328 rows. describe_api returns
undocumented_parameters for the same reason: cellpy documents about half its
arguments, and a model should know when the package never said.
It follows the docstring's own cross-references. CellpyCell.get_cap takes
23 arguments, documents none, and points at
cellpy.readers.capacity_curves.get_cap — which documents 22 of 24. Following
that takes argument coverage across the documented API from 51% to 72%. Only a
docs site resolves those markers; nobody reading a docstring does.
One client per process. State is process-wide, deliberately: the MCP SDK does not give a tool a stable session identity, and under stdio each client spawns its own process anyway. Do not put this behind a shared HTTP endpoint as written.
load_cellblocks, with no progress and no cancellation, so a slow load can look like a hang to a client. This is the gap most worth closing.- No quota and no eviction: an agent can fill the sandbox with figures, and cells stay in memory until the process exits.
new_projecthas to create the project directory itself, becausecellpy newprompts even withno_input=True(cellpy#990). It also downloads a cookiecutter from GitHub on first use.
cellpy mcp arrived in cellpy 2.1.3.post3, which this package requires. The
same things are reachable without it:
python -m cellpy_mcp serve
python -m cellpy_mcp install --dry-run
python -m cellpy_mcp statusThe design, the measurements behind it, and a two-round prototype log are in cellpy#840.
MIT licensed.