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

# Public API MCP

> The OpenAPI-driven MCP server — every tagged REST endpoint becomes a tool. What it covers, how to scope it, and a worked example.

export const APIBadge = ({method = "GET", path, scope}) => {
  const m = method.toUpperCase();
  return <span className="gv-api-badge">
      <span className={`gv-api-badge__method gv-api-badge__method--${m.toLowerCase()}`}>
        {m}
      </span>
      <code className="gv-api-badge__path">{path}</code>
      {scope && <span className="gv-api-badge__scope">
          <span className="gv-api-badge__scope-label">scope</span>
          <code>{scope}</code>
        </span>}
    </span>;
};

export const Def = ({term, type, defaultValue, required, children}) => <>
    <dt className="gv-def-list__term">
      <span className="gv-def-list__name">{term}</span>
      {type && <span className="gv-def-list__type">{type}</span>}
      {required && <span className="gv-def-list__required">required</span>}
      {defaultValue !== undefined && <span className="gv-def-list__default">
          default <code>{defaultValue}</code>
        </span>}
    </dt>
    <dd className="gv-def-list__description">{children}</dd>
  </>;

export const DefList = ({children}) => <dl className="gv-def-list">{children}</dl>;

export const BrandCard = ({eyebrow, title, href, icon, badge, cta, featured = false, horizontal = false, children}) => {
  const classes = ["gv-card", featured && "gv-card--featured", horizontal && "gv-card--horizontal"].filter(Boolean).join(" ");
  const inner = <>
      {icon && <span className="gv-card__icon" aria-hidden="true">
          <Icon icon={icon} />
        </span>}
      <div className="gv-card__main">
        {(eyebrow || badge) && <div className="gv-card__meta">
            {eyebrow && <Eyebrow label={eyebrow} tone="muted" />}
            {badge && <span className="gv-card__badge">{badge}</span>}
          </div>}
        <h3 className="gv-card__title">{title}</h3>
        {children && <div className="gv-card__body">{children}</div>}
        {cta && <span className="gv-card__cta">
            {cta}
            <svg className="gv-card__cta-arrow" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <line x1="7" y1="17" x2="17" y2="7" />
              <polyline points="7 7 17 7 17 17" />
            </svg>
          </span>}
      </div>
    </>;
  return href ? <a className={classes} href={href}>
      {inner}
    </a> : <div className={classes}>{inner}</div>;
};

export const FeatureTable = ({columns, rows, featuredRow, caption, variant}) => <div className={`gv-feature-table ${variant ? `gv-feature-table--${variant}` : ""}`}>
    <table>
      {caption && <caption className="gv-feature-table__caption">{caption}</caption>}
      <thead>
        <tr>
          {columns.map(col => <th key={col.key} scope="col">
              <span className="gv-feature-table__header">{col.label}</span>
            </th>)}
        </tr>
      </thead>
      <tbody>
        {rows.map((row, i) => {
  const rowClass = [i === featuredRow ? "gv-feature-table__row--featured" : "", row.chosen ? "gv-feature-table__row--chosen" : ""].filter(Boolean).join(" ");
  return <tr key={i} className={rowClass}>
              {columns.map(col => {
    const v = row[col.key];
    if (variant === "tradeoff" && Array.isArray(v)) {
      const pill = col.key === "pros" ? "gv-feature-table__pill--pro" : col.key === "cons" ? "gv-feature-table__pill--con" : "";
      return <td key={col.key}>
                      {v.map((item, j) => <span key={j} className={`gv-feature-table__pill ${pill}`}>{item}</span>)}
                    </td>;
    }
    return <td key={col.key}>{v}</td>;
  })}
            </tr>;
})}
      </tbody>
    </table>
  </div>;

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="MCP" />

The Public API MCP server (`@gavai/mcp-server`) generates its tool list directly from the platform's OpenAPI spec. Every operation tagged `x-mcp-tool: true` becomes a callable tool, so the AI surface tracks the REST API automatically — rotate a key, mint a domain, query logs, list deployments, all without the Builder MCP's curated 14. By the end of this page you will know how to install and authenticate the server, how tools are generated, and which scopes you should put on the API key it runs with.

## At a glance

|               |                                                                      |
| ------------- | -------------------------------------------------------------------- |
| **Package**   | `@gavai/mcp-server` (published to npm)                               |
| **Transport** | stdio (JSON-RPC)                                                     |
| **Command**   | `bunx @gavai/mcp-server`                                             |
| **Auth**      | `GAVAI_TOKEN` env var (workspace API key)                            |
| **Base URL**  | `GAVAI_PUBLIC_API_URL` env var (defaults to `https://api.gavai.app`) |
| **Tools**     | One per OpenAPI operation tagged `x-mcp-tool: true`                  |

## Install

`bunx` fetches and runs the latest published version. No global install:

```bash theme={null}
bunx @gavai/mcp-server
```

You will not run this command by hand once you are set up — Claude Desktop spawns it as a subprocess at startup.

## Configure

The server needs two environment variables: a workspace API key and the API base URL.

<DefList>
  <Def term="GAVAI_TOKEN" type="string" required>
    Workspace API key. Format: `gak_live_*` (production) or `gak_test_*` (sandbox).
  </Def>

  <Def term="GAVAI_PUBLIC_API_URL" type="string" defaultValue="https://api.gavai.app">
    API base URL. Override only when pointing at staging or a self-hosted endpoint.
  </Def>
</DefList>

Both go in the Claude Desktop config:

```json theme={null}
{
  "mcpServers": {
    "gavai-api": {
      "command": "bunx",
      "args": ["@gavai/mcp-server"],
      "env": {
        "GAVAI_TOKEN": "gak_live_...",
        "GAVAI_PUBLIC_API_URL": "https://api.gavai.app"
      }
    }
  }
}
```

Replace `gak_live_...` with the workspace API key you mint in **Settings → API keys**. Use a `gak_test_*` key against a test workspace while you are figuring out which tool calls your assistant will make. Restart Claude Desktop after saving the file.

## How the tool list is built

The server reads the OpenAPI spec at startup and registers one tool per operation tagged `x-mcp-tool: true`. The mapping is deterministic:

<FeatureTable
  columns={[
{ key: "field", label: "MCP tool field" },
{ key: "source", label: "OpenAPI source" }
]}
  rows={[
{ field: "Tool name", source: "Operation `operationId`" },
{ field: "Tool description", source: "Operation `summary` + `description`" },
{ field: "Input schema", source: "Operation `parameters` + `requestBody` schema" }
]}
/>

You do not configure individual tools. Add or remove the tag on the spec and the next server start picks up the change — there is no per-tool config to drift.

## Coverage

Every endpoint tagged with `x-mcp-tool: true` is reachable. The 15 public resources documented at [API reference](/api-reference/introduction) map one-to-one to operations, covering workspaces, tokens, pages, theme, forms, agents, secrets, domains, logs, deployments, billing, monetization, and approvals.

If you need to know exactly which operations are tagged, ask the connected client for `tools/list` after restart — the response is the canonical answer for the running server.

## Worked example: AI rotates an API key

Here is what happens when a user says **"Rotate the deploy bot's API key on the acme workspace":**

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant C as Claude
    participant M as Public API MCP
    participant A as gavAI API
    U->>C: Rotate the deploy bot's key on acme
    C->>M: tools/call listWorkspaceTokens { workspace: "acme" }
    M->>A: GET /v1/workspaces/acme/tokens (Authorization: Bearer gak_live_...)
    A-->>M: [{ id: "tok_01J...", name: "deploy-bot", ... }, ...]
    M-->>C: tokens
    C->>M: tools/call rotateWorkspaceToken { workspace: "acme", token_id: "tok_01J..." }
    M->>A: POST /v1/workspaces/acme/tokens/tok_01J.../rotate
    A-->>M: 200 { id: "tok_01J...", secret: "gak_live_..." }
    M-->>C: new secret (shown once)
    C-->>U: Rotated. New secret is gak_live_... — store it now.
```

Three points worth pulling out:

* **Tool names mirror OpenAPI operation IDs.** `listWorkspaceTokens` dispatches to <APIBadge method="GET" path="/v1/workspaces/:slug/tokens" scope="tokens:read" /> and `rotateWorkspaceToken` dispatches to <APIBadge method="POST" path="/v1/workspaces/:slug/tokens/:id/rotate" scope="tokens:write" />. Same `operationId`s you would call from the REST SDK.
* **The bearer token never leaves the server process.** The MCP server reads `GAVAI_TOKEN` from its env, attaches `Authorization: Bearer`, and the AI client only ever sees the response body.
* **The new secret is returned exactly once.** The chain ends with the client surfacing it to the user; if they don't capture it, they rotate again.

## Pinning scopes

<Warning>
  Mint a separate API key for MCP use and grant only the scopes your AI workflow needs. AI assistants chain tool calls across multiple operations — a broad-scope key gives them broad reach. Apply least-privilege so an unintended call cannot cause damage. See [API tokens](/api-reference/resources/tokens) for how to create and scope keys.
</Warning>

A practical default: start with read-only scopes (`workspaces:read`, `logs:read`, `deployments:read`), watch what the assistant tries to call for a session, and add the specific write scopes you decide to allow.

## Where to next

<Columns cols={2}>
  <BrandCard eyebrow="HOW-TO" title="Set up Claude Desktop" href="/mcp/claude-desktop">
    End-to-end config — Public API MCP, Builder MCP, or both side by side.
  </BrandCard>

  <BrandCard eyebrow="REFERENCE" title="API reference" href="/api-reference/introduction">
    Browse all 15 public resources and their endpoints.
  </BrandCard>

  <BrandCard eyebrow="REFERENCE" title="API tokens" href="/api-reference/resources/tokens">
    Mint, scope, and rotate the API keys this server runs with.
  </BrandCard>

  <BrandCard eyebrow="REFERENCE" title="Builder MCP" href="/mcp/builder-mcp">
    The hand-curated MCP server for page, theme, and block edits.
  </BrandCard>
</Columns>
