Metadata-Version: 2.5
Name: meryn-mcp
Version: 0.1.0
Summary: MCP server for the meryn API — read your MMM results and launch runs from Claude
Project-URL: Homepage, https://meryn.ai
Project-URL: Documentation, https://meryn.ai/docs/api/mcp/
Project-URL: API reference, https://meryn.ai/api/v1/docs/
Author-email: meryn <hi@meryn.ai>
License-Expression: MIT
Keywords: marketing-mix-modeling,mcp,meridian,mmm,pymc-marketing,robyn
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.10
Requires-Dist: mcp<3,>=2.0
Description-Content-Type: text/markdown

# meryn MCP

Connect Claude to your [meryn](https://meryn.ai) account: read your MMM results and launch
runs without leaving the conversation.

The server runs **on your machine**, talks to the meryn API over HTTPS, and holds nothing —
your key lives in your own config.

## Install

```bash
claude mcp add meryn -e MERYN_API_KEY=mk_… -- uvx meryn-mcp
```

Or, in any MCP client's config file:

```json
{
  "mcpServers": {
    "meryn": {
      "command": "uvx",
      "args": ["meryn-mcp"],
      "env": {
        "MERYN_API_KEY": "mk_...",
        "MERYN_BASE_URL": "https://meryn.ai"
      }
    }
  }
}
```

`MERYN_BASE_URL` is optional; it defaults to `https://meryn.ai`.

**The key** is the one you create in your team's settings → **API keys**
([how](https://meryn.ai/docs/api/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 the plan.** Professional and Enterprise include it; Free does
not. Without it the first tool call returns
`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 the numbers

The API returns **raw** values with a `units` map beside them, and that is on purpose: an
assistant doing arithmetic on `"12.3%"` gets it wrong two steps later, in a budget
recommendation. Four rules that matter:

- `fraction` is 0–1 and `percent` is 0–100 — both appear in the same response, so read the
  unit instead of guessing from the field name;
- a `null` means *not measured*, never zero — an organic channel's ROI is `null` because it
  has no spend, and averaging it as zero drags the average down;
- anything listed in `not_applicable` **does not exist** for that engine (Robyn does no
  MCMC, so it has no r-hat). That is not missing data;
- on the readiness verdict, `pending` and `not_evaluated` are **not** green: the first means
  it hasn't been computed yet, the second that it ran and could not measure anything.

⚠️ **And on charts, watch `national_only`.** When it is set, the rows are **national**, not
the market you asked for — either because that chart cannot be cut by market (Meridian
estimates one decay per channel, shared across markets) or because the model was trained
before we started saving it per market. Reporting them as that market's is the mistake the
field exists to prevent.

## Not hosted anywhere

There is no remote MCP endpoint to point at, and that is deliberate for now: stdio on your
own machine means the key never leaves it and there is no third party in the path. A hosted
version with OAuth is something we will build when someone needs it.

## Links

- [Using the API](https://meryn.ai/docs/api/using-the-api/) — authentication, pagination,
  retries, and the same four rules in more detail
- [API reference](https://meryn.ai/api/v1/docs/) — generated from the API itself

---

<!-- Lo de abajo es para quien trabaja en el repo, no para quien instala el paquete; va
     acá porque este README ES la página de PyPI y partirlo en dos archivos deja uno de
     los dos sin leer. -->

### Para el repo

| Archivo | Qué es |
|---|---|
| `meryn_mcp/__init__.py` | el cliente HTTP y el catálogo de tools. **Sin dependencias**, y es lo que prueba la suite (`apps/api/tests/test_mcp.py`) |
| `meryn_mcp/servidor.py` | el cableado del SDK de MCP. Lo mínimo que no se puede probar sin el paquete `mcp` |

⚠️ **Es un paquete y no dos módulos sueltos**, y el motivo aparece recién al publicar: el
wheel dejaba un top-level llamado `servidor` en el `site-packages` de cualquiera que lo
instalara. Adentro del repo daba igual; distribuido, no.

Publicar: `uv build && uv publish` desde `clients/mcp/`. ⚠️ Subir la versión en
`pyproject.toml` antes — PyPI no deja re-subir una versión ya publicada.
