> ## Documentation Index
> Fetch the complete documentation index at: https://metamanager.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP server

> Connect your AI agent to MetaManager so it can check pages, read audits and verify its own fixes.

MetaManager runs a remote [MCP](https://modelcontextprotocol.io) server, so an
agent you already use can check pages, read your audits, and confirm that a fix
actually worked.

That last part is the point. An agent can check a page, read exactly what is
wrong, edit your templates, then **check again and see the score move** — a loop
with a verifiable end, rather than a suggestion you have to evaluate yourself.

```text theme={null}
https://mcp.metamanager.dev/mcp
```

## Connecting

Every client needs the same two facts: the address above, and which door you are
coming through.

**OAuth** — the client opens MetaManager in your browser, you tick the
permissions it may have, and the client stores and refreshes the credential
itself. Nothing is written into a config file.

**An API key** — a bearer token you paste, for clients that do not speak OAuth
yet and for scripts of your own.

Prefer OAuth where the client supports it. The credential never lands in a file,
it carries only the permissions you ticked, and revoking it is one click.

<Tabs>
  <Tab title="Claude Code">
    Add the server:

    ```bash theme={null}
    claude mcp add --transport http metamanager https://mcp.metamanager.dev/mcp -s user
    ```

    Then run `/mcp` inside Claude Code, pick MetaManager, and approve in the
    browser window that opens. From a shell instead:

    ```bash theme={null}
    claude mcp login metamanager
    ```

    **With an API key**, one command does both steps:

    ```bash theme={null}
    claude mcp add --transport http metamanager https://mcp.metamanager.dev/mcp \
      --header "Authorization: Bearer mm_live_..." \
      -s user
    ```

    <Warning>
      With a key, do not use `-s project`. That scope writes `.mcp.json`, which
      is meant to be committed and shared — your key would go into version
      control with it. `-s user` keeps the key in your own configuration and
      makes MetaManager available in every project.
    </Warning>
  </Tab>

  <Tab title="Codex">
    Add the server to `~/.codex/config.toml`:

    ```toml theme={null}
    [mcp_servers.metamanager]
    url = "https://mcp.metamanager.dev/mcp"
    ```

    Then sign in:

    ```bash theme={null}
    codex mcp login metamanager
    ```

    Codex treats `auth = "oauth"` as the default for a server given a `url`, so
    there is nothing else to set.

    **With an API key**, Codex holds the *name* of an environment variable
    rather than the key itself:

    ```toml theme={null}
    [mcp_servers.metamanager]
    url = "https://mcp.metamanager.dev/mcp"
    bearer_token_env_var = "METAMANAGER_API_KEY"
    ```

    ```bash theme={null}
    export METAMANAGER_API_KEY=mm_live_...
    ```

    <Note>
      `bearer_token_env_var` takes a variable name, not a token. Pasting the key
      there sends the literal string `mm_live_...` as your bearer token and every
      call comes back unauthenticated.
    </Note>
  </Tab>

  <Tab title="Cursor">
    Add the server to `~/.cursor/mcp.json` for every project, or
    `.cursor/mcp.json` for one:

    ```json theme={null}
    {
      "mcpServers": {
        "metamanager": {
          "url": "https://mcp.metamanager.dev/mcp"
        }
      }
    }
    ```

    With no `headers`, Cursor discovers that the server wants OAuth and prompts
    you to sign in.

    **With an API key**, reference an environment variable rather than pasting
    the key:

    ```json theme={null}
    {
      "mcpServers": {
        "metamanager": {
          "url": "https://mcp.metamanager.dev/mcp",
          "headers": {
            "Authorization": "Bearer ${env:METAMANAGER_API_KEY}"
          }
        }
      }
    }
    ```

    <Warning>
      `.cursor/mcp.json` sits in the project and is usually committed. Use the
      `${env:VAR}` form there, or put the key in `~/.cursor/mcp.json` instead.
    </Warning>
  </Tab>

  <Tab title="VS Code">
    Add the server to `.vscode/mcp.json` for one workspace, or run
    **MCP: Open User Configuration** from the command palette for all of them:

    ```json theme={null}
    {
      "servers": {
        "metamanager": {
          "type": "http",
          "url": "https://mcp.metamanager.dev/mcp"
        }
      }
    }
    ```

    VS Code prompts you to authenticate the first time a tool is used.

    <Note>
      The top-level key here is `servers`, not `mcpServers`, and `type` is
      required. A file copied from another client's docs will be ignored
      silently.
    </Note>
  </Tab>

  <Tab title="Zed">
    **Settings → AI → MCP Servers → Add Server → Add Remote Server**, or in
    `settings.json`:

    ```json theme={null}
    {
      "context_servers": {
        "metamanager": {
          "url": "https://mcp.metamanager.dev/mcp"
        }
      }
    }
    ```

    When a remote server has no `Authorization` header configured, Zed runs the
    OAuth flow and prompts you to sign in — so leaving `headers` out is how you
    ask for OAuth.

    **With an API key**, add the header:

    ```json theme={null}
    {
      "context_servers": {
        "metamanager": {
          "url": "https://mcp.metamanager.dev/mcp",
          "headers": { "Authorization": "Bearer mm_live_..." }
        }
      }
    }
    ```
  </Tab>

  <Tab title="Gemini CLI">
    Add the server to `~/.gemini/settings.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "metamanager": {
          "httpUrl": "https://mcp.metamanager.dev/mcp"
        }
      }
    }
    ```

    Then authenticate:

    ```text theme={null}
    /mcp auth metamanager
    ```

    **With an API key**, add a header instead:

    ```json theme={null}
    {
      "mcpServers": {
        "metamanager": {
          "httpUrl": "https://mcp.metamanager.dev/mcp",
          "headers": { "Authorization": "Bearer mm_live_..." }
        }
      }
    }
    ```

    <Note>
      Gemini CLI uses `httpUrl` for streamable HTTP. Plain `url` selects SSE, a
      different transport.
    </Note>
  </Tab>

  <Tab title="Windsurf">
    Add the server to `~/.codeium/windsurf/mcp_config.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "metamanager": {
          "serverUrl": "https://mcp.metamanager.dev/mcp"
        }
      }
    }
    ```

    Reload the server list in Windsurf's MCP settings, then complete the OAuth
    consent to finish connecting.

    **With an API key**, add a header:

    ```json theme={null}
    {
      "mcpServers": {
        "metamanager": {
          "serverUrl": "https://mcp.metamanager.dev/mcp",
          "headers": { "Authorization": "Bearer ${env:METAMANAGER_API_KEY}" }
        }
      }
    }
    ```

    <Note>
      Windsurf calls the field `serverUrl`, not `url`.
    </Note>
  </Tab>

  <Tab title="Claude Desktop">
    No file to edit. Open **Settings → Connectors → Add custom connector**,
    give it a name, and paste:

    ```text theme={null}
    https://mcp.metamanager.dev/mcp
    ```

    Claude runs the OAuth flow and shows you the approval screen. The same steps
    work on claude.ai and in the mobile apps, since a custom connector belongs to
    your Claude account rather than to one device.
  </Tab>
</Tabs>

### A client that is not listed

Every client needs the URL and, for a key, an `Authorization: Bearer` header.
What differs is only where the file lives and what the fields are called — which
is where most failed setups come from, because a config copied from another
client's documentation is usually ignored in silence rather than rejected.

| Client      | File                                  | Top-level key        | URL field                     |
| :---------- | :------------------------------------ | :------------------- | :---------------------------- |
| Claude Code | `claude mcp add`                      | —                    | `--transport http`            |
| Codex       | `~/.codex/config.toml`                | `[mcp_servers.NAME]` | `url`                         |
| Cursor      | `~/.cursor/mcp.json`                  | `mcpServers`         | `url`                         |
| VS Code     | `.vscode/mcp.json`                    | `servers`            | `url` (with `"type": "http"`) |
| Zed         | `settings.json`                       | `context_servers`    | `url`                         |
| Gemini CLI  | `~/.gemini/settings.json`             | `mcpServers`         | `httpUrl`                     |
| Windsurf    | `~/.codeium/windsurf/mcp_config.json` | `mcpServers`         | `serverUrl`                   |

The bare host without `/mcp` also works, so an existing configuration keeps
running. Other paths do not: a typo returns a 404 naming the right address
rather than appearing to succeed.

## What you are approving

The approval screen lists one permission per checkbox, and you can hand over
less than the client asked for:

| Permission | Covers                                                                               |
| ---------- | ------------------------------------------------------------------------------------ |
| `checks`   | Score a page, explain what is wrong, write the fix. Reads nothing from your account. |
| `read`     | Your saved URLs, site audits and monitors.                                           |
| `write`    | Starting audits and saving URLs, which spend your monthly allowance.                 |

Untick anything you would rather not grant. A connection that gets only `checks`
can still do most of what an agent does — check a page and fix it — without
seeing your account at all. Approved connections are listed at
[connected agents](https://metamanager.dev/agents), where revoking one stops it
immediately.

<Note>
  Nothing is shared with the agent beyond your own account. Connections are
  per-account and can be revoked at any time without touching your API keys.
</Note>

## Fixing a missing image

`missing_og_image` is the most common finding, and the usual fix is an image
somebody has to design and host. MetaManager draws it instead, so an agent can
close that loop by itself:

```text theme={null}
check_url         →  missing_og_image
get_card_snippet  →  <meta property="og:image" content="https://og.metamanager.dev/card?k=…">
                     paste it; each page's card is drawn from its own title
check_url         →  the issue is gone
```

The domain has to be verified on your account first. If it is not,
`get_card_snippet` says so and where to do it, rather than handing back a line
that would not draw.

## Teach your agent the workflow

Connecting the server gives your agent the tools. A companion **skill** teaches
it when to reach for them — and, more importantly, that a metadata change is not
finished until it has been re-checked.

```bash theme={null}
npx skills add davmixcool/skills --skill metamanager
```

For Claude Code you can also copy it in directly:

```bash theme={null}
cp -r skills/metamanager ~/.claude/skills/
```

The skill is optional. Without it your agent can still call every tool; it just
has to be told what to do with them.

## The tools

| Tool                           | What it does                                                            | Needs                           |
| :----------------------------- | :---------------------------------------------------------------------- | :------------------------------ |
| `check_url`                    | Score one page and list its issues                                      | Any plan                        |
| `get_fix_prompt`               | Framework-specific instructions to fix a page                           | Any plan                        |
| `explain_issue`                | What an issue code means and how much it weighs                         | Any plan                        |
| `list_audits`                  | Audits on the account, newest first                                     | Pro                             |
| `get_audit`                    | One audit in full, filterable by issue or severity                      | Pro                             |
| `compare_audits`               | What changed since the previous run of a site                           | Pro                             |
| `start_audit`                  | Crawl a whole site                                                      | Pro, **write**                  |
| `list_monitors`                | Sites watched on a schedule                                             | Pro                             |
| `list_saved_urls` / `save_url` | Read and add saved URLs                                                 | Pro, `save_url` needs **write** |
| `get_card_snippet`             | The `og:image` line to paste, drawing a real card for a verified domain | Any plan                        |
| `list_card_templates`          | The layouts this account can use, and which are published               | Any plan                        |
| `describe_card_schema`         | The schema a card layout is written in                                  | Any plan                        |
| `create_card_template`         | Create or replace one of your layouts, as a draft                       | **write**                       |
| `publish_card_template`        | Publish a layout — **changes what live cards draw**                     | **write**                       |

## What it costs

`check_url`, `get_fix_prompt` and `explain_issue` draw only on your hourly rate
limit, so an agent can use them freely — including to re-check a page after every
edit, which is exactly what you want it doing.

`start_audit` spends one of your **monthly audits** and takes minutes rather than
seconds. That is why it needs a key with write access: an agent in a loop can
exhaust a month's allowance without noticing, and giving that power away should
be a decision rather than a default.

<Note>
  Every key on your account shares one hourly bucket. Connecting three agents
  with three keys does not give you three times the limit.
</Note>

## What a Free key reaches

Single-page work: `check_url`, `get_fix_prompt` and `explain_issue`, at Free's
100 checks an hour. The Pro tools answer with a message saying they need Pro,
which your agent will report rather than retry.

## If something goes wrong

Errors come back as readable sentences rather than status codes, and say whether
retrying will help:

* **Not authenticated** — the key is missing, wrong, or revoked. Not retryable.
* **This API key is read-only** — the tool needs a key with write access. Not
  retryable with that key.
* **This endpoint needs Pro** — not retryable on the current plan.
* **Limit reached** — the hourly rate limit; retryable after waiting.
