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

# Connect via MCP

> Point Claude, Cursor, or VS Code at the hosted CreatorAudit MCP server and drive accounts, videos, creators, and analytics with your API key.

The [Model Context Protocol](https://modelcontextprotocol.io) (MCP) lets agents
like Claude, Cursor, and VS Code call tools through a standard interface.
CreatorAudit runs an **official, hosted MCP server** at
`https://mcp.creatoraudit.com/mcp` that exposes the full v2 API as agent tools —
list and track accounts and videos, group creators, and read analytics. There's
nothing to install: connect your client to the URL with your API key.

## Set up in 3 steps

<Steps>
  <Step title="Get your API key">
    In your CreatorAudit dashboard, open the **API keys** page from the sidebar,
    click **Generate key**, and copy it (it's shown only once). See
    [Manage API keys](/dashboard/api-keys) for the full flow.

    Keys are **organization-scoped**, so every call the agent makes is confined
    to your workspace. Reads work with any valid key; **tracking, creating, and
    deleting need a `write`-scoped key**.
  </Step>

  <Step title="Add the server to your client">
    Use an **HTTP** transport. Header-capable clients send the key in a header;
    clients that take only a URL (Claude Desktop, ChatGPT) put it in the URL.
    Restart the client fully after editing a config file.

    <Tabs>
      <Tab title="Claude Code">
        ```bash theme={null}
        claude mcp add --transport http creatoraudit https://mcp.creatoraudit.com/mcp \
          --header "Authorization: Bearer YOUR_API_KEY"
        ```
      </Tab>

      <Tab title="Cursor">
        `~/.cursor/mcp.json`:

        ```json theme={null}
        {
          "mcpServers": {
            "creatoraudit": {
              "type": "http",
              "url": "https://mcp.creatoraudit.com/mcp",
              "headers": { "Authorization": "Bearer YOUR_API_KEY" }
            }
          }
        }
        ```
      </Tab>

      <Tab title="VS Code">
        `.vscode/mcp.json` — the root key is `servers`, not `mcpServers`:

        ```json theme={null}
        {
          "servers": {
            "creatoraudit": {
              "type": "http",
              "url": "https://mcp.creatoraudit.com/mcp",
              "headers": { "Authorization": "Bearer YOUR_API_KEY" }
            }
          }
        }
        ```
      </Tab>

      <Tab title="Claude Desktop / ChatGPT">
        Their "add connector" UI can't attach a header, so put the key in the
        URL — add this as a custom connector:

        ```
        https://mcp.creatoraudit.com/mcp?api_key=YOUR_API_KEY
        ```

        For Claude Desktop, the one-click **plugin bundle** (`.mcpb`) is the
        cleaner option — open **Settings → Extensions**, add the bundle, and
        paste your key when prompted. It keeps the key in a config field instead
        of the URL.
      </Tab>
    </Tabs>

    <Note>
      A URL key can be captured by server access logs, so prefer a header where
      your client supports one. If a client reserves `Authorization` for its own
      auth, send the key as an `x-creatoraudit-api-key` header instead.
    </Note>
  </Step>

  <Step title="Verify the connection">
    Ask the agent to run `get_api_key_info` (or "which organization is my
    CreatorAudit key for?"). It should return your organization — you're
    connected. Tool **discovery** works without a key; every **data** call needs
    a valid one, which the v2 API validates.
  </Step>
</Steps>

## Tool surface

**Full v2 parity** — every v2 endpoint is a callable tool, plus a
`call_v2_endpoint` escape hatch and meta tools. To stay token-efficient, only a
small pinned core appears in the standing tool list; the rest are found via
`search_tools` or the built-in `get_help` catalog and stay **directly callable
by name**.

| Group     | Tools                                                                                                                                                                                         |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Accounts  | `list_accounts`, `get_account_details`, `track_account` ⚠️, `update_account`, `stop_tracking_account` ⚠️, `get_account_stats`                                                                 |
| Videos    | `list_videos`, `get_video_details`, `track_video` ⚠️, `update_video`, `stop_tracking_video` ⚠️, `list_account_videos`, `list_account_discovered_videos`, `get_account_discovered_video`       |
| Creators  | `list_creators`, `get_creator_details`, `create_creator` ⚠️, `update_creator`, `delete_creator` ⚠️, `link_account_to_creator` ⚠️, `unlink_account_from_creator` ⚠️                            |
| Analytics | `get_org_timeseries`, `get_account_analytics`, `get_account_timeseries`, `get_creator_analytics`, `get_creator_timeseries`, `get_video_analytics`, `get_video_timeseries`, `get_video_deltas` |
| Batch     | `get_accounts_windowed_metrics`, `get_videos_windowed_metrics`, `get_account_videos_windowed_metrics`, `get_video_deltas_batch`                                                               |
| Discovery | `search`, `get_top_creators`, `get_org_overview`                                                                                                                                              |
| System    | `get_api_key_info` (whoami), `check_api_health`, `get_api_meta`, `call_v2_endpoint`                                                                                                           |
| Meta      | `get_help(topic?)`, `search_docs(query)`, `search_tools`, `call_tool`                                                                                                                         |

⚠️ marks destructive writes — your client is prompted to confirm before they
run. `track_account` returns before metrics land; the first scrape is
asynchronous, so check back later. Call `get_help` for the full catalog plus
convention notes (auth, pagination, idempotency, errors, scopes, and the
**track → wait → read** workflow), and `search_docs` to query this site live.

<Tip>
  Anything not yet wrapped as a named tool is reachable through the escape hatch:
  `call_v2_endpoint(method, path, params, body)`, where `path` is relative to the v2
  root (e.g. `/accounts`). The same auth, idempotency, and RFC 9457 error surfacing
  apply.
</Tip>

## How auth works

You bring your own key; the server holds none of its own. It reads your key from
the request — **`Authorization: Bearer`** or **`x-creatoraudit-api-key`** header,
or an **`?api_key=`** URL query parameter (a header takes precedence) — and uses
it only for your calls. Concurrent callers are isolated, so one connection never
sees another's data. The key stays in your client config, never in tool
arguments or responses, and writes auto-attach an `Idempotency-Key` so retries
are deduped. Because keys are organization-scoped server-side, an agent can't
reach another org's data even if it tries. See [API setup](/api-setup) for the
shared API conventions.

## Search these docs over MCP

This documentation site also hosts an MCP server at its `/mcp` path, with
search and content-retrieval tools so your agent can look things up live.

<Tabs>
  <Tab title="Claude Code">
    ```bash theme={null}
    claude mcp add --transport http creatoraudit-docs https://docs.creatoraudit.com/mcp
    ```
  </Tab>

  <Tab title="Cursor">
    ```json theme={null}
    {
      "mcpServers": {
        "creatoraudit-docs": { "url": "https://docs.creatoraudit.com/mcp" }
      }
    }
    ```
  </Tab>

  <Tab title="VS Code">
    ```json theme={null}
    {
      "servers": {
        "creatoraudit-docs": { "type": "http", "url": "https://docs.creatoraudit.com/mcp" }
      }
    }
    ```
  </Tab>
</Tabs>

<Tip>
  Each page also offers **Copy page**, **View as markdown**, and **Add to Cursor / VS
  Code / Claude** in its context menu — handy for dropping a single page into your
  agent.
</Tip>

## Build a custom server

For most teams the official server is what you want — it covers the whole v2 API
and is maintained alongside it. Build your own only to expose a **narrow subset**
of endpoints, add **organization-specific logic** (extra validation, redaction,
defaults), or run on a **different runtime**.

The example below wraps three endpoints — `search`, `overview`, and
`list_accounts` — and passes responses through unchanged. Read the key from an
environment variable; never bake it into the tools or return it in a response.

<Steps>
  <Step title="Set your key as an environment variable">
    ```bash theme={null}
    export CREATORAUDIT_API_KEY="YOUR_API_KEY"
    ```
  </Step>

  <Step title="Implement the server">
    <CodeGroup>
      ```python server.py (Python) theme={null}
      import os
      import httpx
      from mcp.server.fastmcp import FastMCP

      BASE = "https://api.creatoraudit.com/v2"
      HEADERS = {"Authorization": f"Bearer {os.environ['CREATORAUDIT_API_KEY']}"}
      mcp = FastMCP("creatoraudit")


      async def _get(path: str, params: dict | None = None) -> dict:
          async with httpx.AsyncClient(base_url=BASE, headers=HEADERS) as client:
              resp = await client.get(path, params=params or {})
              # On 4xx/5xx the body is RFC 9457 problem+json; surface it as-is.
              return {
                  "status": resp.status_code,
                  "request_id": resp.headers.get("X-Request-ID"),
                  "body": resp.json(),
              }


      @mcp.tool()
      async def search(q: str, limit: int = 20) -> dict:
          """Search tracked accounts, creators, and videos."""
          return await _get("/search", {"q": q, "limit": limit})


      @mcp.tool()
      async def overview(period: str | None = None) -> dict:
          """Organization-level headline figures."""
          return await _get("/overview", {"period": period} if period else None)


      @mcp.tool()
      async def list_accounts(limit: int = 50, cursor: str | None = None) -> dict:
          """List tracked accounts (one page). Pass the returned next_cursor to page."""
          params = {"limit": limit}
          if cursor:
              params["cursor"] = cursor
          return await _get("/accounts", params)


      if __name__ == "__main__":
          mcp.run()  # stdio transport
      ```

      ```typescript server.ts (TypeScript) theme={null}
      import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
      import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
      import { z } from "zod";

      const BASE = "https://api.creatoraudit.com/v2";
      const headers = { Authorization: `Bearer ${process.env.CREATORAUDIT_API_KEY!}` };

      async function get(path: string, params: Record<string, unknown> = {}) {
        const url = new URL(`${BASE}${path}`);
        for (const [k, v] of Object.entries(params)) {
          if (v != null) url.searchParams.set(k, String(v));
        }
        const resp = await fetch(url, { headers });
        // On 4xx/5xx the body is RFC 9457 problem+json; surface it as-is.
        return {
          status: resp.status,
          requestId: resp.headers.get("X-Request-ID"),
          body: await resp.json(),
        };
      }

      const server = new McpServer({ name: "creatoraudit", version: "1.0.0" });

      server.tool(
        "search",
        "Search tracked accounts, creators, and videos.",
        { q: z.string(), limit: z.number().int().min(1).max(25).default(20) },
        async ({ q, limit }) => ({
          content: [{ type: "text", text: JSON.stringify(await get("/search", { q, limit })) }],
        }),
      );

      server.tool(
        "list_accounts",
        "List tracked accounts (one page). Pass next_cursor to page.",
        { limit: z.number().int().min(1).max(200).default(50), cursor: z.string().optional() },
        async ({ limit, cursor }) => ({
          content: [{ type: "text", text: JSON.stringify(await get("/accounts", { limit, cursor })) }],
        }),
      );

      await server.connect(new StdioServerTransport());
      ```
    </CodeGroup>
  </Step>

  <Step title="Register it with your agent">
    Point your client at the local server (Claude Code, Python example):

    ```bash theme={null}
    claude mcp add creatoraudit-custom -- python server.py
    ```
  </Step>
</Steps>

<Warning>
  Keep the key server-side, read from an environment variable. Never bake `YOUR_API_KEY`
  into a tool definition or return it in a tool response. Mint an `Idempotency-Key` on
  every write, and flag destructive tools so the client can prompt before mutating data
  — the official server does both for you.
</Warning>
