> ## 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.

# Builder MCP

> The 14-tool MCP server bundled with the gavAI CLI — what each tool does, how it authenticates, and a worked end-to-end 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 Builder MCP server (`@gavai/builder-mcp`) is the hand-curated MCP surface for the operations a page editor actually performs — reading workspaces, patching the page document, publishing versions, and walking the block and component schemas. It is bundled with the gavAI CLI and reuses your CLI session, so once you have run `gavai login`, registering it with Claude is a one-line config change. By the end of this page you will know what tools the server exposes, how to wire it into Claude Desktop, and what an end-to-end edit looks like on the wire.

## At a glance

|               |                                                              |
| ------------- | ------------------------------------------------------------ |
| **Package**   | `@gavai/builder-mcp` (bundled in the gavAI CLI)              |
| **Transport** | stdio (JSON-RPC)                                             |
| **Command**   | `gavai mcp`                                                  |
| **Auth**      | `~/.config/gavai/credentials.json`, written by `gavai login` |
| **Tools**     | 14 (see below)                                               |

## Install and authenticate

The server ships inside the gavAI CLI, so installing the CLI is installing the server. See [CLI overview](/cli/overview) for the install command on your platform.

After install, run the login flow once:

```bash theme={null}
gavai login
```

This opens a browser, completes OAuth, and writes credentials to `~/.config/gavai/credentials.json`. The MCP server reads that file on every tool call and refreshes the token automatically — you do not handle bearer tokens by hand.

## Register the server

<Tabs>
  <Tab title="Via CLI (recommended)">
    The CLI knows where Claude Desktop's config lives on each OS and can write the entry for you:

    ```bash theme={null}
    gavai mcp install claude-desktop
    ```

    This adds a `gavai-builder` server entry pointing to `gavai mcp` and picks up the correct absolute binary path. Restart Claude Desktop to load the server.
  </Tab>

  <Tab title="Manual config">
    Open the Claude Desktop config — `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows — and add:

    ```json theme={null}
    {
      "mcpServers": {
        "gavai-builder": {
          "command": "gavai",
          "args": ["mcp"]
        }
      }
    }
    ```

    Save and restart Claude Desktop.
  </Tab>
</Tabs>

## Tools

The server registers 14 tools, grouped by what they touch.

### Workspace and app document

<DefList>
  <Def term="list_tenants" type="tool">List workspaces the authenticated user has access to.</Def>
  <Def term="get_tenant" type="tool">Fetch metadata for a single workspace.</Def>
  <Def term="get_tenant_app" type="tool">Fetch the current app document (page tree, schema, theme references).</Def>
  <Def term="patch_tenant_app" type="tool">Apply RFC 6902 JSON Patch operations to the app document.</Def>
  <Def term="publish_tenant_version" type="tool">Publish the current draft to live.</Def>
</DefList>

### Blocks and components

<DefList>
  <Def term="list_block_types" type="tool">Return every PageBlock type the platform supports.</Def>
  <Def term="get_block_schema" type="tool">Return the JSON Schema for a specific block type.</Def>
  <Def term="list_components" type="tool">Return the available sub-primitives that blocks compose.</Def>
  <Def term="get_component_schema" type="tool">Return JSON Schema for a component.</Def>
  <Def term="get_component_examples" type="tool">Return concrete usage examples for a component.</Def>
</DefList>

### Theme

<DefList>
  <Def term="get_tenant_theme" type="tool">Fetch the workspace theme.</Def>
  <Def term="patch_tenant_theme" type="tool">Apply JSON Patch operations to the theme.</Def>
  <Def term="publish_tenant_theme" type="tool">Publish the current theme draft.</Def>
</DefList>

Each tool dispatches to a single REST endpoint — for example, `patch_tenant_app` calls <APIBadge method="PATCH" path="/v1/workspaces/:slug/app" scope="pages:write" /> and `publish_tenant_version` calls <APIBadge method="POST" path="/v1/workspaces/:slug/versions/publish" scope="pages:publish" />.

### How clients discover them

The names and full input schemas are returned by the standard MCP `tools/list` call. Claude Desktop, Cursor, and other hosts use that response to present the tool list and validate inputs before invoking them — this is part of the protocol, not a gavAI extension.

## Worked example: AI adds a nav link and publishes

Here is the exchange when a user says **"Add a Pricing link to the navigation on acme and ship it":**

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant C as Claude
    participant M as Builder MCP
    participant A as gavAI API
    U->>C: Add a Pricing link to acme's nav and publish
    C->>M: tools/call get_tenant_app { slug: "acme" }
    M->>A: GET /v1/workspaces/acme/app
    A-->>M: { blocks: [{ block: "NavShell", props: { items: [...] } }, ...] }
    M-->>C: app document
    C->>M: tools/call patch_tenant_app { slug, operations: [{ op: "add", path: "/blocks/0/props/items/-", value: { label: "Pricing", href: "/pricing" } }] }
    M->>A: PATCH /v1/workspaces/acme/app
    A-->>M: 200 { version_id: "ver_01J..." }
    M-->>C: patched
    C->>M: tools/call publish_tenant_version { slug: "acme" }
    M->>A: POST /v1/workspaces/acme/versions/publish
    A-->>M: 200 { published_at: "2026-05-11T..." }
    M-->>C: live
    C-->>U: Pricing link is live on acme.
```

Three properties of the exchange are worth pulling out:

* **The AI never touches a credential.** The MCP server reads `~/.config/gavai/credentials.json` and attaches the bearer token on every API call.
* **Every edit is a JSON Patch.** `patch_tenant_app` rejects free-form rewrites; the AI must produce RFC 6902 operations against the current document.
* **Publishing is an explicit second call.** `patch_tenant_app` writes a draft. The draft is only visible on the live site after `publish_tenant_version`. A failed publish does not roll the patch back.

## Security

The server runs with the privileges of your CLI session. Any tool call the host makes — patch, publish, theme rewrite — executes against your active workspace with your credentials. Two practical rules:

* **Treat the connected workspace like production.** AI chains tool calls; a chain ending in `publish_tenant_version` reaches your live site. Point experimentation at a test workspace.
* **Revoke when done.** Run `gavai logout` to invalidate the session locally, or revoke it in the console under Settings → Sessions.

## Where to next

<Columns cols={2}>
  <BrandCard eyebrow="HOW-TO" title="Set up Claude Desktop" href="/mcp/claude-desktop">
    End-to-end config for one or both MCP servers, with troubleshooting.
  </BrandCard>

  <BrandCard eyebrow="REFERENCE" title="Public API MCP" href="/mcp/public-api-mcp">
    The OpenAPI-driven MCP server for operations beyond the Builder surface.
  </BrandCard>

  <BrandCard eyebrow="REFERENCE" title="JSON Patch on the app document" href="/pageblocks/json-patch">
    The operation shapes `patch_tenant_app` accepts and how they compose.
  </BrandCard>

  <BrandCard eyebrow="CLI" title="gavai mcp" href="/cli/commands/mcp">
    The CLI subcommand that hosts this server.
  </BrandCard>
</Columns>
