The MCP server connects an assistant like Claude to your meryn account: it can read your models' results and launch runs without you leaving the conversation.
It runs on your machine, talks to the API over HTTPS, and holds nothing — your key lives in your own config and the server has no state of its own.
Install
It is published on PyPI as meryn-mcp, so there is
nothing to clone. In Claude Code, one line:
claude mcp add meryn -e MERYN_API_KEY=mk_... -- uvx meryn-mcp
In any other MCP client, the mcpServers block of its config file:
{
"mcpServers": {
"meryn": {
"command": "uvx",
"args": ["meryn-mcp"],
"env": {
"MERYN_API_KEY": "mk_...",
"MERYN_BASE_URL": "https://meryn.ai"
}
}
}
}
uvx comes with uv and runs the server without installing
anything permanently. If you would rather install it, pip install meryn-mcp gives you a
meryn-mcp command to use as the command instead.
MERYN_BASE_URL is optional — it defaults to https://meryn.ai.
The key is the one you create in API keys. It is shown once, so copy
it then. Give it read unless you want the assistant to be able to launch runs.
⚠️ API access is part of your plan. Professional and Enterprise include it; Free does
not. Without it the first tool call comes back with
403: This team plan does not include API access.
What it can do
| Read | projects, datasets, mappings and periods; models with their metrics and readiness verdict; the progress, logs and run history; every chart dataset and the finer-grained result tables; EDA runs; the budget allocator's split by channel and by market, and its efficiency |
| Launch | create a model on an existing period, run it, cancel it; create and run a budget optimisation |
What it deliberately doesn't do: upload and map a CSV. That step stays in the web app, where a person confirms how the file was interpreted — which column is the date, what each column means, what got normalised.
Three things worth knowing
The knobs aren't written into the server. list_engine_params fetches them live from
the API, with each one's type, default, bounds and help text. They're the same rows that
draw the modals in the app, so they can't go stale — and it's the first place to look
before building a config.
Anything that costs compute asks first. run_model and run_allocator start
containers: tens of minutes of compute and one of your plan's concurrent-run slots. The
server refuses them without an explicit confirm, so an assistant can't fire one in
passing. That's a guard in the code, not a hint in the description.
Retries don't duplicate runs. Every POST carries an Idempotency-Key derived from the
operation and its arguments, so if the assistant retries because it didn't see the
response, the API returns what it already created instead of launching a second run.
How to read what it returns
The same four rules as the rest of the API — they're in Using the API and they matter more here, because an assistant will do arithmetic on whatever it gets:
fractionis 0–1 andpercentis 0–100; read theunitsmap rather than guessing;- a
nullis not measured, never a zero — an organic channel's ROI isnullbecause it has no spend, and averaging it as zero drags the average down; - what's in
not_applicabledoesn't exist for that engine, so it isn't missing data; - on the readiness verdict,
pendingandnot_evaluatedare not green.
⚠️ And on charts, national_only has to be read. When it is set, the rows are
national, not the market that was asked for — either because that chart cannot be cut
by market at all, or because the model was trained before we started saving it per market.
Reporting them as that market's numbers is the mistake the field exists to prevent.
Not hosted anywhere
There's no remote MCP endpoint to point at, and that's deliberate for now: stdio on your own machine means the key never leaves it and there's no third party in the path. A hosted version with OAuth is a thing we'll build when someone needs it.
Takeaway: give the assistant a read key unless you want it launching runs, and tell
it to check list_engine_params before it builds a config.