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

# Set up Claude Desktop

> Wire one or both gavAI MCP servers into Claude Desktop in under 5 minutes, with expected output and the common failure modes.

export const PageMeta = ({audience, readingTime, prereqs, level}) => {
  const items = [audience && ({
    key: "audience",
    label: "AUDIENCE",
    value: audience
  }), readingTime && ({
    key: "time",
    label: "READ",
    value: readingTime
  }), level && ({
    key: "level",
    label: "LEVEL",
    value: level
  }), prereqs && prereqs.length > 0 && ({
    key: "prereqs",
    label: "PREREQS",
    value: prereqs.join(" · ")
  })].filter(Boolean);
  if (items.length === 0) return null;
  return <div className="gv-page-meta" role="group" aria-label="Page metadata">
      {items.map(item => <div className="gv-page-meta__item" key={item.key}>
          <span className="gv-page-meta__label">{item.label}</span>
          <span className="gv-page-meta__value">{item.value}</span>
        </div>)}
    </div>;
};

export const ExpectedOutput = ({caption = "Expected output", children}) => <div className="gv-output">
    <div className="gv-output__label">
      <svg className="gv-output__icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="15 10 20 15 15 20" />
        <path d="M4 4v7a4 4 0 0 0 4 4h12" />
      </svg>
      <span>{caption}</span>
    </div>
    <div className="gv-output__body">{children}</div>
  </div>;

export const CTABanner = ({eyebrow, headline, subhead, ctaLabel, ctaHref, secondaryCtaLabel, secondaryCtaHref}) => <section className="gv-cta">
    <div className="gv-cta__copy">
      {eyebrow && <Eyebrow label={eyebrow} />}
      <h2 className="gv-cta__headline">{headline}</h2>
      {subhead && <p className="gv-cta__subhead">{subhead}</p>}
    </div>
    <div className="gv-cta__actions">
      {ctaLabel && ctaHref && <a className="gv-cta__button" href={ctaHref}>
          {ctaLabel}
        </a>}
      {secondaryCtaLabel && secondaryCtaHref && <a className="gv-cta__secondary" href={secondaryCtaHref}>
          {secondaryCtaLabel}
          <svg 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>
        </a>}
    </div>
  </section>;

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 NumberedStep = ({n, title, children}) => <li className="gv-step">
    <span className="gv-step__index">{n}</span>
    <div className="gv-step__body">
      <h3 className="gv-step__title">{title}</h3>
      <div className="gv-step__content">{children}</div>
    </div>
  </li>;

export const NumberedSteps = ({children}) => <ol className="gv-steps">{children}</ol>;

export const ArrowItem = ({children}) => <li className="gv-arrow-item">
    <span className="gv-arrow-item__arrow" aria-hidden="true">{"↳︎"}</span>
    <span className="gv-arrow-item__content">{children}</span>
  </li>;

export const ArrowList = ({children}) => <ul className="gv-arrow-list">{children}</ul>;

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="HOW-TO" />

<PageMeta audience="Developers" readingTime="5 min" prereqs={["Claude Desktop", "gavAI account", "gavai CLI for the Builder MCP path"]} level="Beginner" />

This page gets you from a fresh Claude Desktop install to "Claude is editing my workspace" in five minutes. You will edit one JSON file, restart the app once, and ask a single verification question. Pick the Builder MCP path, the Public API MCP path, or both — the configs compose.

## Prerequisites

<ArrowList>
  <ArrowItem>[Claude Desktop](https://claude.ai/download) installed and signed in.</ArrowItem>
  <ArrowItem>A gavAI workspace — sign in at [console.gavai.app](https://console.gavai.app).</ArrowItem>
  <ArrowItem>The gavAI CLI installed if you want the Builder MCP — see [CLI overview](/cli/overview).</ArrowItem>
</ArrowList>

## Option 1 — Builder MCP

The Builder MCP server is bundled with the CLI and authenticates with your CLI session. Fastest path to letting Claude edit pages.

<NumberedSteps>
  <NumberedStep n="01" title="Sign in to gavAI">
    Run the login command to authenticate the CLI:

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

    This opens a browser, runs the OAuth flow, and writes credentials to `~/.config/gavai/credentials.json`. The Builder MCP reads that file on every call.
  </NumberedStep>

  <NumberedStep n="02" title="Let the CLI write the config">
    ```bash theme={null}
    gavai mcp install claude-desktop
    ```

    The CLI locates Claude Desktop's config for your OS, adds a `gavai-builder` entry pointing at the `gavai mcp` command, and resolves the binary's absolute path. No manual JSON edits.
  </NumberedStep>

  <NumberedStep n="03" title="Restart Claude Desktop">
    Quit Claude Desktop completely — closing the window is not enough on macOS — and reopen it. MCP servers are loaded only at startup.
  </NumberedStep>

  <NumberedStep n="04" title="Verify the connection">
    Open a new conversation and ask: **"What are my workspaces?"**

    Claude should call `list_tenants` and respond with something like:

    <ExpectedOutput caption="Verification output">
      ```text theme={null}
      You have access to 2 workspaces:
      - acme (Acme Inc.)
      - acme-staging (Acme staging)
      ```
    </ExpectedOutput>

    If you see an authentication error instead, see [Authentication errors](#authentication-errors).
  </NumberedStep>
</NumberedSteps>

## Option 2 — Public API MCP

The Public API MCP runs with a workspace API key and exposes every tagged REST endpoint as a tool.

<NumberedSteps>
  <NumberedStep n="01" title="Mint a test API key">
    In [console.gavai.app](https://console.gavai.app), go to **Settings → API keys** and create a key. Use a test-mode key (`gak_test_*`) while you are still figuring out which tools your assistant will reach for. Copy the secret immediately — the console shows it once.
  </NumberedStep>

  <NumberedStep n="02" title="Open the Claude Desktop config">
    The file lives at:

    * **macOS** — `~/Library/Application Support/Claude/claude_desktop_config.json`
    * **Windows** — `%APPDATA%\Claude\claude_desktop_config.json`

    Open it in any text editor. If the file does not exist, create it containing `{}`.
  </NumberedStep>

  <NumberedStep n="03" title="Add the server entry">
    Paste the following into the `mcpServers` object and replace the placeholder:

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

  <NumberedStep n="04" title="Restart Claude Desktop">
    Quit and reopen so the new server is registered.
  </NumberedStep>

  <NumberedStep n="05" title="Verify the connection">
    Ask Claude: **"What workspace am I authenticated as?"**

    Claude should call `GET /v1/me` and reply with the user and workspace context attached to your API key — something like:

    <ExpectedOutput caption="Verification output">
      ```text theme={null}
      You're authenticated as gavin@gavai.io on the acme workspace (test mode).
      ```
    </ExpectedOutput>
  </NumberedStep>
</NumberedSteps>

## Run both at once

You can register both servers in the same config — Claude routes calls based on which server exposes the tool it needs.

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

Restart once after editing. After restart, asking Claude "list my workspaces" still calls `list_tenants` from the Builder MCP; asking it to "rotate the deploy bot's key" calls `rotateWorkspaceToken` from the Public API MCP.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Server doesn't appear in Claude Desktop" icon="triangle-alert">
    Check that the config file is at the correct path for your OS and that the JSON parses — no trailing commas, balanced braces. Paste it into a JSON linter if you are not sure.

    Quit Claude Desktop completely (not just close the window) and reopen it. MCP servers register at startup only.

    If the server still does not appear, the `gavai` or `bunx` binary is probably not on the PATH Claude Desktop sees. On macOS, apps launched from Finder do not inherit your shell's `$PATH`. See [Tools list is empty](#tools-list-is-empty) below for the fix.
  </Accordion>

  <Accordion title="Authentication errors">
    **Builder MCP** — run `gavai whoami` in your terminal. If it errors, run `gavai login` again to refresh credentials.

    **Public API MCP** — confirm the `GAVAI_TOKEN` value in the config is complete and not truncated. If you suspect it's compromised, rotate it in **Settings → API keys** and update the config.
  </Accordion>

  <Accordion title="Tools list is empty">
    An empty tool list usually means the server process failed to start. The most common cause on macOS is that the binary is not on the PATH visible to Claude Desktop.

    Use the absolute path to the binary in the config:

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

    Run `which gavai` (or `which bunx`) in your terminal to find the correct path.
  </Accordion>
</AccordionGroup>

<CTABanner eyebrow="AUTOMATE" headline="Let AI edit your pages" ctaLabel="Browse the Builder MCP tools" ctaHref="/mcp/builder-mcp" />

## Where to next

<Columns cols={2}>
  <BrandCard eyebrow="REFERENCE" title="Builder MCP tools" href="/mcp/builder-mcp">
    The 14 Builder tools, their inputs, and a worked end-to-end example.
  </BrandCard>

  <BrandCard eyebrow="REFERENCE" title="Public API MCP coverage" href="/mcp/public-api-mcp">
    How OpenAPI operations become MCP tools and how to scope the API key.
  </BrandCard>
</Columns>
