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

# MCP Server

> Connect Claude Desktop, Cursor, claude.ai, ChatGPT, Google Antigravity, or any MCP-compatible client to HeyMilo and query your candidates, jobs, and interview data in natural language.

HeyMilo hosts a [Model Context Protocol](https://modelcontextprotocol.io)
(MCP) server at `https://mcp.heymilo.ai/mcp`. Any MCP-compatible client
can connect to it and let users query HeyMilo with natural language
(search candidates, summarize pipelines, pull interview reports) without
writing custom integration code.

The server is **read-only**. It exposes the same data surfaces an
authenticated user can see in `app.heymilo.ai`, scoped to whichever
HeyMilo workspaces the authenticated identity has access to. To create
postings, ingest candidates, or modify workflows, call the corresponding
REST endpoint on `https://api.heymilo.ai` directly. The
`milo_capabilities` MCP prompt describes every write endpoint with curl
examples your LLM can read and recommend.

<video src="https://storage.googleapis.com/heymilo-pub-docs/videos/MCP%20Server.mp4" controls />

## What you can ask

* "Who are my top 10 candidates for the Senior Backend Engineer role?"
* "Summarize last month's interviews: completion rate, average score,
  decisions."
* "What's the candidate pipeline for the Atlanta Sales Lead position?"
* "Pull the interview report for `int_abc123`."
* "How do I create a new interviewer?" *(load the `milo_capabilities`
  prompt first, see [Available tools](#available-tools), then the LLM
  will hand you a ready-to-paste curl example)*

## Authentication

The server accepts two auth methods, both passed as
`Authorization: Bearer <token>`:

| Method                               | Best for                                                                                            | Notes                                                                                                             |
| ------------------------------------ | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Sign in with HeyMilo** (OAuth 2.1) | claude.ai, Claude Desktop, Cursor, ChatGPT, Google Antigravity (anywhere a user is at the keyboard) | Standard OAuth flow; the token is tied to a HeyMilo user and grants access to every workspace they're a member of |
| **API key**                          | Programmatic clients, scripts, server-to-server agents                                              | Single shared key per workspace, no expiry; one key = one workspace                                               |

### Sign in with HeyMilo (OAuth)

Recommended for end-user clients. The MCP client discovers the OAuth
flow automatically via
`https://mcp.heymilo.ai/.well-known/oauth-authorization-server`, then
redirects through the HeyMilo login screen. After sign-in, the
client receives access and refresh tokens tied to that HeyMilo user.

#### claude.ai

1. **Settings → Connectors → Add custom connector.**
2. Server URL: `https://mcp.heymilo.ai/mcp`.
3. **Connect.** claude.ai walks you through the HeyMilo login.
4. The connector lights up; HeyMilo tools appear in any new chat's
   tools menu.

#### Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json theme={null} theme={null}
{
  "mcpServers": {
    "heymilo": {
      "url": "https://mcp.heymilo.ai/mcp"
    }
  }
}
```

Quit and reopen Claude Desktop. On first connect it discovers OAuth,
runs the HeyMilo login flow, and persists the token.

#### Cursor

`~/.cursor/mcp.json`:

```json theme={null} theme={null}
{
  "mcpServers": {
    "heymilo": {
      "url": "https://mcp.heymilo.ai/mcp"
    }
  }
}
```

#### Claude Code

Add the server to `~/.claude.json` (or run `claude mcp add`):

```json theme={null} theme={null}
{
  "mcpServers": {
    "heymilo": {
      "type": "http",
      "url": "https://mcp.heymilo.ai/mcp"
    }
  }
}
```

Restart Claude Code, then run `/mcp` → **heymilo** → **Authenticate** to
complete the HeyMilo login. Claude Code (like other native CLI clients)
uses an ephemeral loopback port for its OAuth callback; the server
handles this automatically, with no per-port configuration needed.

#### ChatGPT

ChatGPT can connect to remote MCP servers like HeyMilo, but only through
**developer mode**, which exposes full MCP connector support. Developer
mode is available on **Plus, Pro, Business, Enterprise, and Education**
plans (web), and ChatGPT only supports **remote** MCP servers reachable
over HTTPS — which is exactly what HeyMilo hosts.

1. **Enable developer mode.** In ChatGPT, go to **Settings → Apps &
   Connectors → Advanced settings** and turn on **Developer mode**.
2. **Add the connector.** Back in **Settings → Apps & Connectors**,
   choose **Create** (create app / add custom connector) and fill in:
   * **Name**: `HeyMilo`
   * **MCP Server URL**: `https://mcp.heymilo.ai/mcp`
   * **Authentication**: **OAuth**
3. Check the box confirming you trust the provider, then create the
   connector.
4. ChatGPT discovers the OAuth flow automatically (the server supports
   OAuth metadata discovery and dynamic client registration) and walks
   you through the HeyMilo login. After sign-in, the HeyMilo tools are
   available in chat — open the **+ / Developer mode** tools menu and
   enable them for a conversation.

#### Google Antigravity (Gemini)

[Google Antigravity](https://antigravity.google) shares a single MCP
configuration across its IDE and CLI at
`~/.gemini/config/mcp_config.json`. You can edit that file directly or
use the in-app **Manage MCP Servers → View raw config** editor.

```json theme={null} theme={null}
{
  "mcpServers": {
    "heymilo": {
      "serverUrl": "https://mcp.heymilo.ai/mcp"
    }
  }
}
```

To authenticate, open **Agent Settings → Customizations**, click
**Authenticate** next to **heymilo**, complete the HeyMilo login in your
browser, then copy the authorization code back into the settings panel
and click **Submit**. Antigravity registers itself with the server via
dynamic client registration, so no client ID or secret is needed.

<Tip>
  Use the `serverUrl` key for remote HTTP servers (the older `httpUrl`
  key is deprecated). To connect with an API key instead of OAuth, add a
  `headers` block: `"headers": { "Authorization": "Bearer YOUR_HEYMILO_API_KEY" }`.
</Tip>

### API key

Use this for non-interactive clients (scripts, server-to-server
agents) where there isn't a human to walk through OAuth.

1. In `app.heymilo.ai`, **Integrations → API**, generate a key.
2. Pass it as a Bearer token (or in the legacy `X-API-KEY` header):

```bash theme={null} theme={null}
curl -X POST https://mcp.heymilo.ai/mcp/ \
  -H "Authorization: Bearer YOUR_HEYMILO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

<Tip>
  The MCP endpoint is **`/mcp/`** (with a trailing slash). A request to
  `/mcp` returns a `307` redirect to `/mcp/`; standards-compliant MCP
  clients follow it automatically (a `307` preserves the method, body,
  and `Authorization` header), so the `https://mcp.heymilo.ai/mcp` URL
  in the config blocks above works as-is. For raw HTTP tooling like
  `curl`, target `/mcp/` directly (as above) or add `-L` to follow the
  redirect. Otherwise the request stops at the `307` and returns
  nothing.
</Tip>

Claude Desktop and Cursor accept API keys directly in the config block:

```json theme={null} theme={null}
{
  "mcpServers": {
    "heymilo": {
      "url": "https://mcp.heymilo.ai/mcp",
      "headers": { "Authorization": "Bearer YOUR_HEYMILO_API_KEY" }
    }
  }
}
```

<Warning>
  An API key has full read access to your workspace's data. Treat it like
  a password. Rotate any time under **Integrations → API**.
</Warning>

## Multi-workspace access

Some HeyMilo users belong to more than one workspace. When you sign in
via OAuth, the resulting token covers every active workspace the user
is a member of.

All tools accept an optional `workspace_id` argument:

* **Omit it** → the tool defaults to your "primary" / first-listed
  workspace.
* **Pass it explicitly** → scopes the call to that specific workspace.

To discover what's accessible, call the [`listWorkspaces`](#) tool. The
LLM can also use it implicitly: when a user asks about "Acme", the
model calls `listWorkspaces`, finds the matching `workspace_id`, and
includes it in the next tool call.

API-key auth is single-workspace by definition: one key = one
workspace, and `workspace_id` is fixed.

## Available tools

Every tool is read-only. Apart from `listWorkspaces` (a custom tool that
takes no arguments), the set is generated directly from the
[Public API v2](/api-reference/public-api/documentation) `GET` endpoints —
so it always matches what the API exposes — and each generated tool accepts
the optional `workspace_id` argument described above.

| Area                | Tools                                                                                                                              |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Workspaces          | `listWorkspaces`                                                                                                                   |
| Postings (jobs)     | `listPostings`, `getPosting`, `getPostingStats`                                                                                    |
| Candidates          | `listCandidates`                                                                                                                   |
| Interviews          | `getInterviewData`, `getInterviewMetadata`                                                                                         |
| Interview templates | `listInterviewTemplates`, `getInterviewTemplate`                                                                                   |
| Questions           | `listQuestions`, `getQuestion`                                                                                                     |
| Voices              | `listVoices`, `getVoice`, `getVoicePreview`                                                                                        |
| Email               | `listSenderEmails`, `getSenderEmail`, `listEmailTemplates`, `getEmailTemplate`, `listEmailTemplateGroups`, `getEmailTemplateGroup` |
| Design templates    | `listDesignTemplates`, `getDesignTemplate`, `listDesignTemplateGroups`, `getDesignTemplateGroup`                                   |
| Phone numbers       | `listPhoneNumbers`, `getPhoneNumber`                                                                                               |
| Domains             | `listDomains`, `getDomain`                                                                                                         |
| Webhooks            | `listWebhooks`, `getWebhook`                                                                                                       |
| Schema / metadata   | `list_agent_types_api_v2_schemas_agents_get`, `list_question_types_api_v2_schemas_question_types_get`                              |

To enumerate them programmatically, call the MCP `tools/list` method (see
the API-key curl example above).

Write endpoints (create posting, ingest candidate, archive, etc.) are
not callable as MCP tools. To help LLMs guide users through writes, the
server exposes a prompt named `milo_capabilities` (and a matching
`milo://capabilities` resource) that returns a structured description of
the **full** v2 surface, including writes, with curl examples and links
back to this documentation.

<Note>
  To explore everything the HeyMilo API can do, browse the
  [Public API documentation](https://docs.heymilo.ai/api-reference/public-api/documentation),
  or invoke the `/mcp__heymilo__milo_capabilities` prompt from your MCP
  client for the full catalogue with ready-to-run curl examples.
</Note>

## Discovery endpoints

For programmatic clients building against the server:

| Endpoint                                      | Purpose                                                                                                                                               |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /.well-known/oauth-authorization-server` | RFC 8414 authorization-server metadata                                                                                                                |
| `GET /.well-known/oauth-protected-resource`   | RFC 9728 protected-resource metadata                                                                                                                  |
| `POST /oauth/register`                        | RFC 7591 dynamic client registration                                                                                                                  |
| `GET /oauth/authorize`                        | OAuth authorization endpoint (redirects to the identity provider)                                                                                     |
| `GET /oauth/callback`                         | Identity-provider redirect target; forwards the auth code back to the client's redirect URI (handled automatically; clients never call this directly) |
| `POST /oauth/token`                           | OAuth token endpoint (proxied to the identity provider)                                                                                               |
| `POST /mcp/`                                  | MCP JSON-RPC endpoint (Streamable HTTP transport). `/mcp` `307`-redirects here                                                                        |
| `GET /healthz`                                | Liveness probe                                                                                                                                        |

Unauthenticated requests to `/mcp` respond with
`WWW-Authenticate: Bearer realm="mcp.heymilo.ai", resource_metadata="https://mcp.heymilo.ai/.well-known/oauth-protected-resource"`
so clients can discover OAuth on first contact.

## Security model

* **Per-request authorization.** Every `/mcp` request re-validates the
  token. For OAuth, the user's workspace memberships are re-checked
  against our datastore and cached briefly (a few minutes), so revoked
  access propagates within that window.
* **Strict workspace scoping.** A user can only call tools with
  `workspace_id` values present in their token's accessible workspaces.
  Tool calls server-side scope every database query by `workspace_id`.
* **Tokens are short-lived.** Access and refresh token lifetimes are
  enforced by HeyMilo's identity provider, and refresh tokens can be
  rotated on use.
* **All HTTPS, all the time.** TLS via managed certificates.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Claude doesn't see any HeyMilo tools">
    Check that your config is valid JSON and restart your client fully
    (Cmd-Q on macOS). The tools panel in any chat should list
    `listPostings`, `getInterviewData`, etc.
  </Accordion>

  <Accordion title="`401` or `403` when calling tools">
    A `401` means missing or expired credentials. For OAuth, your access
    token may have expired — disconnect and reconnect from your client's
    connector settings.
    An invalid or revoked **API key** returns `403`; generate a new one in
    `app.heymilo.ai → Integrations → API`.
  </Accordion>

  <Accordion title="Tool call says 'workspace_id is not in your accessible workspaces'">
    The LLM passed a `workspace_id` you don't have access to. Have it
    call `listWorkspaces` first to see what's available, then retry
    with one of those IDs.
  </Accordion>

  <Accordion title="Tool calls hang or time out">
    Searches over very large candidate pools can take up to 60 seconds.
    Increase your client's tool-call timeout if you're hitting limits.
  </Accordion>
</AccordionGroup>
