# Connect your agent

> One URL, sign in when asked. API keys are the fallback for scripts and CI.

Every agent connects to the same place:

```
https://mcp.formalogic.io/mcp
```

**You do not need an API key.** The server uses OAuth: the first time your agent
calls a Forma tool, it opens your browser, you sign in with your Forma account,
and the agent is connected. The [Overview](https://app.formalogic.io/overview) shows the moment it happens.

You need a Forma account first. Signing in from an agent never creates one.

## Claude

![Claude](https://docs.formalogic.io/art/clients/claude.svg)

Claude on the web and Claude Desktop use the same connector.

1. Settings → Connectors → **Add custom connector**.
2. Name it `Forma` and paste the URL above.
3. Click **Connect** and sign in.

## Claude Code

![Claude Code](https://docs.formalogic.io/art/clients/claude.svg)

```bash
claude mcp add --transport http forma https://mcp.formalogic.io/mcp
```

Then type `/mcp` in Claude Code, pick `forma`, and choose **Authenticate**.
`claude mcp list` should read `forma … ✔ Connected`.

## ChatGPT

1. Settings → Apps & Connectors → Advanced → turn on **Developer mode**.
2. Create a connector named `Forma` with the URL above and **OAuth** authentication.
3. Sign in when ChatGPT asks.

## Cursor

![Cursor](https://docs.formalogic.io/art/clients/cursor.svg)

[Add to Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=forma&config=eyJ1cmwiOiJodHRwczovL21jcC5mb3JtYWxvZ2ljLmlvL21jcCJ9), or add this to `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "forma": { "url": "https://mcp.formalogic.io/mcp" }
  }
}
```

Cursor shows **Needs login** next to `forma`. Click it and sign in.

## VS Code

```bash
code --add-mcp '{"name":"forma","type":"http","url":"https://mcp.formalogic.io/mcp"}'
```

Start `forma` from the MCP view and sign in when asked.

## Windsurf

![Windsurf](https://docs.formalogic.io/art/clients/windsurf.svg)

Add to `~/.codeium/windsurf/mcp_config.json`, then sign in when Windsurf asks:

```json
{
  "mcpServers": {
    "forma": { "serverUrl": "https://mcp.formalogic.io/mcp" }
  }
}
```

## Codex

```bash
codex mcp add forma --url https://mcp.formalogic.io/mcp
codex mcp login forma
```

The second command opens your browser to sign in.

## Any other client

Give it the URL. A client that supports MCP authorization finds everything else
itself: the server publishes its OAuth metadata at
`https://mcp.formalogic.io/.well-known/oauth-protected-resource/mcp` and supports
dynamic client registration, so there is nothing to pre-register.

## Confirm it worked

Ask your agent:

> Use forma_status and tell me which Forma account this session is signed in as.

It should answer with an organisation id and a plan. Nothing but a live,
authenticated connection can return that — `list_oracles` answers identically
for every caller, so it cannot tell you whose account you are on.

| | |
|---|---|
| the browser never opens | Your client needs a restart after adding the server, or it does not support MCP authorization yet. Use an API key below |
| "this account has no Forma organisation yet" | You signed in with an account that has never opened the [console](https://app.formalogic.io). Sign in to the console once, then connect again |
| the tools do not appear | Most clients start MCP servers once, at launch. Restart it |

## With an API key instead

For a client that cannot do OAuth, or for scripts and CI, send an API key as a
bearer token. Create one on the [API keys](https://app.formalogic.io/keys) page — it is shown once.

[Create an API key](https://app.formalogic.io/keys)

### Claude Code with a key

Two commands, so the key is never on a command line:

```bash
read -rs -p 'Forma API key: ' FORMA_KEY
```

```bash
claude mcp add --transport http forma https://mcp.formalogic.io/mcp \
  --header "Authorization: Bearer $FORMA_KEY"
```

Pasting the key into the command itself would work, and would also write it
verbatim into your shell history — persisted, often synced with your dotfiles,
and readable by anything running as you. `read -rs` takes it from the terminal
instead: not echoed, not recorded. (`claude mcp add` still stores the header in
`~/.claude.json`; that part is inherent to configuring a client.)

Check it registered:

```bash
claude mcp list
```

`forma` should read `✔ Connected`.

### Claude Desktop with a key

Settings → Developer → Edit Config, then add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "forma": {
      "type": "http",
      "url": "https://mcp.formalogic.io/mcp",
      "headers": {
        "Authorization": "Bearer fk_live_…"
      }
    }
  }
}
```

Restart Claude Desktop afterwards. MCP servers are started once, at launch.

### Cursor with a key

Add to `.cursor/mcp.json` in your project, or `~/.cursor/mcp.json` for every
project:

```json
{
  "mcpServers": {
    "forma": {
      "url": "https://mcp.formalogic.io/mcp",
      "headers": {
        "Authorization": "Bearer fk_live_…"
      }
    }
  }
}
```

### VS Code with a key

Add to `.vscode/mcp.json`:

```json
{
  "servers": {
    "forma": {
      "type": "http",
      "url": "https://mcp.formalogic.io/mcp",
      "headers": {
        "Authorization": "Bearer fk_live_…"
      }
    }
  }
}
```

### Windsurf with a key

Add to `~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "forma": {
      "serverUrl": "https://mcp.formalogic.io/mcp",
      "headers": {
        "Authorization": "Bearer fk_live_…"
      }
    }
  }
}
```

### Codex with a key

```bash
read -rs -p 'Forma API key: ' FORMA_KEY
```

```bash
codex mcp add forma --url https://mcp.formalogic.io/mcp \
  --header "Authorization: Bearer $FORMA_KEY"
```

Same reason as Claude Code above — the key stays out of your shell history.

### Any other client with a key

The server speaks streamable HTTP. Give your client the URL and one header:

```
POST https://mcp.formalogic.io/mcp
Authorization: Bearer fk_live_…
```

Everything is behind that header — `initialize` and `tools/list` included, so an
unauthenticated caller cannot enumerate what the server does. The one open route
is `/healthz`, which answers `{"ok": true}` and nothing else.

If your client cannot send a header, it cannot connect yet — every route except
`/healthz` is behind that header.

With a key, a `401` means the header is missing or the key is wrong; `KEY_REVOKED` and `KEY_EXPIRED` say which. A `403 ORG_DISABLED` means the key is fine and its organisation is not active — see [Authentication](https://docs.formalogic.io/authentication).

## A note on these instructions

**Claude Code is the only client tested end to end against this server**, with
OAuth and with a key. The other clients follow each vendor's published MCP
configuration as of **2026-09-28** — the shape is right, but their settings
screens move, and if one of these is wrong we would rather you told us than
assumed it was you.
