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

# Authorization

> How the MCP server authenticates with the ai-cmo.dev API.

The MCP server resolves credentials using a three-tier priority order:

1. **`AICMO_API_KEY`** environment variable (fastest, no browser needed)
2. **`~/.ai-cmo/credentials.json`** file from a previous device authorization
3. **Browser device authorization flow** (interactive, first-run only)

> The legacy env var names `NOISEMAKER_API_KEY` and `NOISEMAKER_BASE_URL` are still recognized as fallbacks in tier 1.

If neither the env var nor an existing credential file is found, the server starts a device authorization flow.

## Device authorization flow

<Steps>
  <Step title="Server requests a device code">
    POST `/api/v1/device-authorizations` with an empty JSON body.

    The server responds with a `code` and `verificationUri`:

    ```json theme={null}
    {
      "code": "abc123def456",
      "verificationUri": "http://127.0.0.1:3000/authorize/device?code=abc123def456"
    }
    ```
  </Step>

  <Step title="Browser opens for approval">
    The MCP server opens `verificationUri` in your default browser. If it cannot open the browser (e.g., headless environment), it prints the URL to stderr.
  </Step>

  <Step title="Approve or deny in the browser">
    The `/authorize/device?code=...` page shows the requested scopes and lets you select a workspace. You must be signed in with a session cookie — API keys cannot approve device authorizations.

    The authorization creates an API key with exactly these 10 scopes:

    * `workspace:read`
    * `metrics:read`
    * `recs:read`
    * `recs:write`
    * `runs:read`
    * `runs:write`
    * `reports:read`
    * `chat:read`
    * `chat:write`
    * `settings:read`
  </Step>

  <Step title="Poll for the token">
    The MCP server polls `POST /api/v1/device-authorizations/{code}/token` every 2 seconds. The poll returns one of:

    | Status                                                                | Meaning                       |
    | --------------------------------------------------------------------- | ----------------------------- |
    | `{"status":"pending"}`                                                | Still waiting for user action |
    | `{"status":"denied"}`                                                 | User denied the request       |
    | `{"status":"approved","apiKey":"gp_live_...","workspace":"my-brand"}` | Success                       |

    The polling deadline is 10 minutes. After that, the server throws an error.
  </Step>

  <Step title="Credential is persisted">
    On approval, the API key and workspace are written to `~/.ai-cmo/credentials.json`:

    * Directory `~/.ai-cmo/` is created with mode `0700`
    * Credential file is created with mode `0600`
    * Write is atomic: written to a temp file (`credentials.json.<pid>.tmp`), then renamed
  </Step>
</Steps>

## Error states

| Response             | Meaning                                                                           |
| -------------------- | --------------------------------------------------------------------------------- |
| `410` (expired)      | Authorization expired or was already claimed. Retry the tool to start a new flow. |
| `410` (claimed)      | The code was already used. Start a fresh authorization.                           |
| `409` (not pending)  | The authorization was already approved or denied by another session.              |
| `429` (rate limited) | 5 device authorization starts per minute per IP. Wait and retry.                  |

## Credential resolution order

1. If `AICMO_API_KEY` (or legacy `NOISEMAKER_API_KEY`) is set in the environment, use it directly (no `workspace` — the tool call may need an explicit `workspace` parameter).
2. If `~/.ai-cmo/credentials.json` exists and parses correctly, use it (includes a `workspace` for default routing).
3. Otherwise, run the device authorization flow, persist the result, and use it.
