> ## Documentation Index
> Fetch the complete documentation index at: https://help.the-meridian.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Meridian MCP

Meridian MCP connects the AI coding client you already work in (Cursor, Claude Code, or Claude Desktop) to your Meridian organizations and apps. Instead of guessing, the agent reads your app's real Plan Builder catalog, deploy plane, runtime logs, secret metadata, Shopify API alerts, Grow config (emails, automations, segments, discounts), and developer databases before it writes a line of code.

So the agent reads your actual feature and event keys before writing `isEnabled()` / `track()` calls, reads the real error off a failed deployment before proposing a fix, and can trigger a deploy or roll one back without you leaving the editor.

| | |
| - | - |
| **Server URL** | `https://mcp.the-meridian.ai/mcp` |
| **Transport** | Streamable HTTP |
| **Authentication** | OAuth 2.1 authorization code + PKCE, with dynamic client registration |
| **Authorization** | Your existing organization role and permissions, re-checked on every call |
| **Server version** | `1.9.0` |
| **Where to set it up** | The **Meridian MCP** page (`/mcp`) in the dashboard |

Meridian MCP never grants access you don't already have in the dashboard. Every tool call resolves the signed-in user and runs the same [permission](/customer-permissions) checks the web app runs, on the same catalog: a tool that mirrors a dashboard action needs the permission that action needs, and a role's app scope applies to every app-scoped tool.

This server accesses the production Meridian platform only, including the staging and development environments of its apps. Apps created on the separate Meridian staging platform are not available through this server. See [Environments](/environments#app-environments-and-platform-staging).

## Connect an AI client

There is **no API key and no token to paste anywhere**. The dashboard hands out a URL; your client obtains its own credentials over OAuth, and you approve the connection once in a browser.

1. Add the **server URL** to your MCP client (see below).
2. The client discovers Meridian's OAuth endpoints and registers itself. Nothing has to be pre-created in the dashboard.
3. The client opens Meridian's consent screen in a browser, which names the client and what it's asking for. Sign in if you aren't already.
4. On **Approve**, the client exchanges the authorization code for its tokens. On **Deny**, nothing is granted.
5. The client appears under **Connected AI clients** on the Meridian MCP page, with when it connected and when it was last used.

Every client reads the same config:

```json theme={null}
{
  "mcpServers": {
    "meridian": {
      "url": "https://mcp.the-meridian.ai/mcp"
    }
  }
}
```

| Client | How to add it |
| - | - |
| **Cursor** | Settings → Tools & MCP → New MCP Server, or add the snippet to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project). Cursor shows a Connect button that opens the consent screen. |
| **Claude Code** | Run `claude mcp add --transport http meridian https://mcp.the-meridian.ai/mcp`, then run `/mcp` to authenticate. |
| **Claude Desktop** | Settings → Connectors → Add custom connector, paste the server URL, then approve in the browser. |

### Revoke a client

**Connected AI clients** lists every client you've approved. Revoking one invalidates its access token and refresh token immediately. The client stops working and has to connect and be approved again. Revoked entries stay in the list, marked as revoked, rather than disappearing. Connections are per developer, not per organization.

## How tools are scoped

The server keeps no state between calls. There's no "current app" or "current organization", so every app-scoped tool takes explicit identifiers:

| Argument | Required | What it is |
| - | - | - |
| `app_id` | On app-scoped tools | The app's public identifier, as returned by `apps_list`. |
| `organization_id` | Optional | An organization uuid or slug. When supplied, `app_id` is resolved within it and may be a slug too. |

The opening move for an agent is **`whoami`, then `apps_list`**. The identifiers every other tool needs come from those two calls. Output always uses the same public identifiers the platform API exposes; internal numeric ids never appear.

## Tool catalog

Forty-nine tools. Each is annotated so a well-behaved client knows what it can run unattended: **read-only** (no side effects), **idempotent** (safe to repeat), **destructive** (needs confirmation), or **write** (creates or updates Meridian config, draft-only for emails and automations). The catalog leans towards facts your repository can't tell you: the variables injected into the deployed runtime, the live database schema, where webhooks are routed, and the Grow config an agent needs when wiring lifecycle flows.

### Orientation

| Tool | Annotation | Purpose |
| - | - | - |
| `whoami` | read-only | The calling user plus every organization they belong to, with role and key permissions. No arguments. |
| `apps_list` | read-only | Apps visible to the user, each with the `app_id` every other tool needs. |
| `app_get` | read-only | One app's detail: Shopify connection state, environments, plan feature entitlements. The `environments` field is omitted without **Hosting: View** on the app, and the entitlements without **Plan builder: View**. |
| `docs_search` | read-only | Ranked passages from Meridian's public product docs (the same knowledge base [Answers](/introduction-7) uses). Returns path, title, URL, heading, anchor, snippet, and score. No `app_id`. |
| `docs_get` | read-only | One public docs page in full by relative path (for example `mcp-server.mdx`). Same tree as `meridian://docs/{path}`. |

### Plan Builder

The highest-value surface: what an agent reads before writing SDK integration code.

| Tool | Annotation | Purpose |
| - | - | - |
| `plans_list` | read-only | The app's plans (handle, status, monthly/yearly price, trial days) and the feature and event terms each grants. |
| `features_list` | read-only | The feature catalog: each feature's `key` (what `isEnabled("<key>")` gates on), type (`boolean`/`number`), and per-plan value. |
| `events_list` | read-only | The metered-usage event catalog: each event's `key` (what `track({ eventKey })` reports against), unit, and per-plan terms. |
| `events_create` | write | Adds a metered-usage event to the catalog: `name`, `key` (lowercase snake\_case, unique per app across events and usage views, immutable), optional `unit`, `description`, and default metering terms. Per-plan terms stay in the dashboard. |
| `entitlements_explain` | read-only | What one shop is entitled to right now, resolved through the same path the SDK reads. A debugging aid for a gate behaving unexpectedly. |

### Deploy

| Tool | Annotation | Purpose |
| - | - | - |
| `environments_list` | read-only | Environments with type, region, base URL, scaling config, database status, and `config_pending_deploy` (true when settings changed that only reach the app with the next deploy) with `config_changed_at`. |
| `managed_variables_list` | read-only | The variables Meridian injects at deploy time (`SHOPIFY_API_KEY`, the `MERIDIAN_*` and managed `DB_*` set). **Names and metadata only, never values.** |
| `github_refs_list` | read-only | Branches and recent commits of the connected GitHub repository. |
| `deployments_list` | read-only | Recent deployments with status, current step, build step (built, reused image, or none for a rollback), timing, git ref/sha, and who triggered them. |
| `deployment_get` | read-only | One deployment's detail plus its event stream and build/runtime log links. |
| `logs_tail` | read-only | Recent runtime logs for one environment. Options include `since`/`until`, `deployment_id`, `min_severity` (a floor, so `WARNING` also matches errors), and `contains`. |
| `secrets_list` | read-only | Secret and environment-variable **metadata**: key, scope, creator, timestamps. Never a value. |
| `previews_list` | read-only | Pull-request preview deployments: PR number, head sha, URL, status, expiry. |
| `resource_usage_get` | read-only | Current CPU/memory/network usage against limits, plus hosting meters for the billing period. A periodic sample. |
| `resource_usage_history` | read-only | Time-range CPU/memory/network/throughput/latency history for one environment. |
| `deployment_create` | idempotent | Trigger a deployment of a `git_sha`. V1 deploys to production only. |
| `deployment_rollback` | destructive | Shift live traffic back to a previously succeeded deployment's revision. No rebuild. |

Call `managed_variables_list` before writing any code that reads `process.env`. Those variables are in neither the repository nor `secrets_list`, so an agent reading only your config concludes they're unset. Use `github_refs_list` to feed `deployment_create` a real `git_sha`: a sha that hasn't been pushed fails late during the build.

### Webhooks

| Tool | Annotation | Purpose |
| - | - | - |
| `webhook_routes_list` | read-only | Which Shopify topics are routed, the `forward_path` each is delivered to, whether Shopify accepted the subscription, and the destination itself: `forward_host` plus the `forward_host_source` it came from. |
| `webhook_deliveries_list` | read-only | Recent inbound deliveries: topic, shop, status, attempts, the response code your handler returned, and any error. |

Together these separate the two failures that look identical from inside your code: no delivery row means Shopify never sent it (check the route); a 4xx/5xx `response_code` means it arrived and your handler rejected it. Neither tool returns the webhook body or headers.

In `webhook_routes_list`, each route's `registration` counts the stores where Shopify accepted the subscription (`registered_shops`) and where it did not (`failed_shops`, with `last_error`). A store Meridian could not reach at all, because its token was revoked or can't be renewed, or Shopify refused the request, counts as failed on every topic it hasn't registered, and its `last_error` starts with `Could not reach` followed by the store. A store that uninstalled your app drops out of these counts the next time you save the topic set.

`forward_host` is the origin Meridian really posts to, and `forward_host_source` says which destination won: `external` (the verified external domain you selected as the forward host), `environment` (your app's Meridian deployment), or `platform`. A `platform` source is Meridian's own receiver, the last resort when an app has neither: forwarded events stop there and never reach your app, and `forward_url` then gives the single URL they go to. Both are null when nothing is routed at all.

`reconcile_available` says whether Meridian can reach the stores that already installed your app, which it can only do when the app is deployed on Meridian. When it is `false`, Meridian can't register topics on those stores itself: the SDK registers your topic set on each store when that store authenticates your app (see [Re-registering webhooks](/sdk-app#re-registering-webhooks)). Managed topics are in that set from install on. A route you added reaches a store only at that store's first sign-in after you added it, so each such route carries `sign_in`: `installed_shops`, and `awaiting_shops`, the installed stores that haven't signed in since. A sign-in within five minutes of adding the route still counts as awaiting, because the SDK can serve the topic set it cached before. `sign_in` is null on managed topics, and on every route when `reconcile_available` is `true`.

### Shopify API alerts

| Tool | Annotation | Purpose |
| - | - | - |
| `shopify_alerts_list` | read-only | Open API-deprecation alerts: the changelog entry, severity, migration deadline, matched code references, and whether a fix suggestion exists. |
| `shopify_alert_get` | read-only | One alert plus its stored fix suggestion, if generated. |

Both need the **Shopify API alerts: View** permission on the app.

### Databases

| Tool | Annotation | Purpose |
| - | - | - |
| `dev_database_get` | read-only | Your own developer database for the app: status, storage usage, lifecycle timestamps, recent migration runs. Metadata only. |
| `dev_database_migrate` | idempotent | **Record** a migration run that already happened locally. It does not execute one. |
| `dev_database_reset` | destructive | Drop and recreate your developer database schema. Requires `confirm: true`. |
| `database_schema_get` | read-only | The table, column and index structure of an environment's managed database. **Structure only, never rows.** |

Developer database tools only ever reach the caller's own database, never a teammate's, even for an organization owner. Read `database_schema_get` before writing a migration or query against a deployed environment: the live schema is whatever the last applied migration produced, and your repository may not agree with it.

### CRM

| Tool | Annotation | Purpose |
| - | - | - |
| `custom_fields_list` | read-only | The app's [custom field](/custom-fields) definitions: handle, type, and the options a select field allows. Optional `shop_domain` also returns that store's values. |
| `custom_field_values_set` | idempotent | Set custom field values on a store, keyed by handle. A null value clears one, at most 20 per call. Writes through the same single writer the dashboard uses, so the [custom field changed](/custom-field-changed) automation trigger fires exactly as it would otherwise. |
| `crm_segments_list` | read-only | [Segment](/segments) definitions and conditions (not membership dumps). |
| `crm_segment_get` | read-only | One segment's definition and conditions. |

### Emails

| Tool | Annotation | Purpose |
| - | - | - |
| `emails_list` | read-only | Email templates for the app: name, status, subject, timestamps. Never HTML or design. Newest first, 20 per call by default (`limit` up to 100). While `next_cursor` is not null, call again with it as `cursor` and the same `status` and `search` to read the next page. |
| `email_get` | read-only | One template's metadata. Never the body. |
| `emails_variables_list` | read-only | The merge-tag catalog available when authoring subjects and bodies. |
| `email_create` | write | Create an **inactive** draft (name, optional subject and preview text). Status is always forced inactive; design/HTML cannot be set through MCP. |
| `email_update` | write | Update an email's name, subject, or preview text. Cannot activate or edit the body. An active email must keep a subject, so clearing it is refused until the email is deactivated. Like a save in the dashboard, it also updates the [Send email](/send-email) steps of every automation that sends the email, so their next send uses the change. |

Edit the body and activate in the dashboard. MCP never live-sends.

### Automations

| Tool | Annotation | Purpose |
| - | - | - |
| `automations_list` | read-only | Automations with status, node count, and timestamps. |
| `automation_get` | read-only | One automation's config and graph. Graphs strip `emailHtml`; node ids are remapped to public `node_id` values. |
| `automation_runs_list` | read-only | Recent runs for an automation. |
| `automations_metrics_get` | read-only | Aggregate run metrics. |
| `automations_catalog_get` | read-only | Builder catalogs: triggers, conditions, actions the graph can use. |
| `automation_create` | write | Create an **inactive** draft (name, optional description of up to 2,000 characters). |
| `automation_update` | write | Update name, description (up to 2,000 characters), and/or draft graph. Graph updates only while inactive; no `emailHtml`. |
| `automation_test_run` | write | Dry-run an automation against a store (or synthetic store) and return the trace. Nothing is sent, metered, or persisted. |

### Discounts

| Tool | Annotation | Purpose |
| - | - | - |
| `discounts_list` | read-only | Plan Builder discount codes: name, type, value, status, duration. |
| `discount_get` | read-only | One discount's detail. |

Discounts are read-only through MCP: no create or reprice.

## Resources and prompts

Two resources give the agent retrievable context: `meridian://docs/{path}` (Meridian's public product documentation, the same pages at help.the-meridian.ai; empty path returns an index of `.md`/`.mdx` paths). Prefer `docs_search` mid-task and `meridian://apps/{app}/config` (an app's whole configuration as one JSON document).

Two prompts arrive pre-filled with live data:

* **`integrate_sdk`**: a guided walkthrough for wiring `@the-meridian/sdk` into a Shopify embedded app, pre-filled with that app's real feature and event keys, types, and per-plan values. Optional `surface` narrows it to `provider`, `pricing`, `usage`, or `server`.
* **`debug_failed_deploy`**: a guided investigation of a failed deployment, pre-filled with its real status, error, and recent build events, followed by an ordered set of follow-up calls.

## What Meridian MCP never returns

**No tool ever returns a secret value**, not for a secret and not for a plain environment variable. An agent can confirm a variable exists and is spelled correctly, which is what almost every "missing env var" investigation actually needs.

* `secrets_list` and `managed_variables_list` return metadata only, never values and never the Secret Manager reference.
* `dev_database_get` returns no password, connection string, or proxy token. Connect with the Meridian CLI (`meridian dev`).
* `emails_list` / `email_get` return metadata only, never HTML or design.
* `webhook_deliveries_list` returns no body and no request headers.
* Internal identifiers and internal-team-only flags never appear.

Some things are intentionally absent, because doing them through an agent is a worse idea than doing them in the dashboard: reading rows from your databases, writing secrets, pricing changes (including creating or repricing discounts), live email sends, activating automations, and account administration (billing, team, roles, auth settings). Store membership exports and report runs stay dashboard-only; CRM **definitions** (custom fields, segments) and Grow config are the exception.

## Mutations

Eleven tools write; the other thirty-eight only read. Ten of them change something, and every successful change is recorded in the organization's [activity log](/activity-log) with the tool that made it. The eleventh, `automation_test_run`, is a dry trace that changes nothing and leaves no entry.

* **`events_create`** adds one metered-usage event to the catalog. It does not touch per-plan terms.
* **`deployment_create`** is idempotent on `(environment_id, git_sha)`: repeating it while that deployment is in flight returns the same one. A *different* sha while a deployment is active is refused: one active deployment per environment. V1 targets production only.
* **`deployment_rollback`** shifts live traffic (annotated destructive). Refused if the target didn't succeed or the environment has another deployment in progress.
* **`dev_database_migrate`** records a run; it does not execute one. `meridian migrate` runs the migration locally and records it automatically.
* **`dev_database_reset`** destroys every table and row in your developer database schema. Requires `confirm: true`.
* **`custom_field_values_set`** writes [custom field](/custom-fields) values on a store and fires the automation trigger a dashboard edit would.
* **`email_create`** writes an inactive metadata draft. **`email_update`** changes an email's metadata and, as a dashboard save does, the snapshot that every automation sending the email sends from. Neither activates, edits the body, or sends.
* **`automation_create` / `automation_update`** write inactive drafts; graph updates only while inactive; no `emailHtml`.
* **`automation_test_run`** is a dry trace: no mail, no metering, no persisted run.

## Plan requirements

Meridian MCP is gated on the `mcp` plan feature, and the deploy-related tools additionally on the `deploy` feature. Every tier (**Lite, Pro, and Enterprise**) includes both, so no plan change is needed. Lite meters MCP calls (1,000 requests a month); Pro and Enterprise don't meter them.

| Refusal | What it means |
| - | - |
| *This app's Meridian plan does not include the Meridian MCP feature.* | The app's plan lacks `mcp`. Nothing works for that app. |
| *This app's Meridian plan does not include the Deploy feature.* | The app's plan lacks `deploy`. The deploy-plane and webhook tools refuse; Plan Builder, Grow, Shopify alert, and developer database tools keep working. |
| *App not found, or you do not have access to it.* | The `app_id` is wrong, belongs to an organization you can't reach, or your role lacks the permission the tool needs (for example `hosting.promote` for `deployment_create` to production, or `shopify_alerts.view` for the alert tools). The two cases read the same on purpose, so a tool can't be used to probe for apps you can't see. Re-run `apps_list`, then ask an admin to grant the missing permission through a [role](/roles). |

## Versioning

The server reports its version on connect and follows semver. New tools, new resources, and new optional fields on an existing tool's output bump the **minor** version. A breaking change to an existing tool's contract, meaning a removed or renamed field or changed semantics, bumps the **major** version.

| Version | What changed |
| - | - |
| `1.9.0` | Added `events_create`, which adds a metered-usage event to an app's catalog. |
| `1.8.0` | `environments_list` gains `config_pending_deploy` and `config_changed_at`. |
| `1.7.0` | Added `docs_search` and `docs_get` over this documentation site. |
| `1.6.0` | Added sanitised `error_message` on failed previews and developer databases, and `database_error_code` / `database_error_message` on `environments_list` when provisioning failed. |
| `1.5.0` | Renamed the product to Meridian MCP (feature key `developer_mcp` became `mcp`), and added Grow tools for emails, automations, CRM segments, and discounts (reads, inactive draft writes, and `automation_test_run`). |
| `1.4.0` | Added `custom_fields_list` and `custom_field_values_set`. |
| `1.3.0` | Added the optional `deployment_id`, `min_severity`, `contains`, and `until` arguments to `logs_tail`, changing no existing field's semantics. |
| `1.2.0` | Added the read-only `resource_usage_get` and `resource_usage_history`. |
| `1.1.0` | Added six read-only tools: `managed_variables_list`, `github_refs_list`, `previews_list`, `webhook_routes_list`, `webhook_deliveries_list`, and `database_schema_get`. |

## Troubleshooting

| Symptom | Cause and fix |
| - | - |
| The client asks to authenticate again, or every call fails as unauthorized | The connection was revoked (or the token invalidated). Reconnect from the client and approve again. |
| `events_create` says the key is already used by a usage view | Event keys and usage view keys share one namespace per app. Pick another key, or use the existing view. |
| The agent invents feature or event keys that don't exist | It skipped the catalog. Point it at `features_list` / `events_list`, or run the `integrate_sdk` prompt. |
| The agent invents platform behaviour that doesn't match Meridian | It skipped the docs. Point it at `docs_search` (then `docs_get` for the full page), and use live catalog tools for real feature/event keys. |
| `meridian://docs/` paths from an older client no longer resolve | Paths now match the public Mintlify tree (for example `mcp-server.mdx`), not the old internal codex layout. Re-read the empty-path index. |
| Calls start failing after a burst of activity | Tool calls are rate limited per user (120 per minute). Wait and retry. |
| The client can't complete registration | Dynamic client registration is rate limited per IP. Wait before retrying rather than re-adding the server repeatedly. |
| The agent expects `dev_database_migrate` to run the migration | It doesn't. Run `meridian migrate` locally. |
| The agent expects `email_create` or `automation_create` to go live | Creates are always inactive. Activate in the dashboard after editing the body/graph there. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.