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

# Authentication

> A bearer key, an OAuth 2.1 flow, and the scope split that decides what a model may do.

**Preview.** There are two ways to authenticate against `POST /mcp`, and which
one you use is decided by the client, not by you.

| Path          | Credential                    | Clients                                                                                                                                                                           |
| ------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Bearer**    | A workspace API key, `tgcc_…` | Claude Code, Cursor, VS Code, Windsurf, Cline, Continue, Goose, Junie, LibreChat, Warp, n8n — and the OpenAI and xAI APIs' remote-MCP tools, both of which set the header for you |
| **OAuth 2.1** | A token the server issues     | The claude.ai, ChatGPT and Grok connector UIs — and Zed, which runs the flow when no `Authorization` header is configured                                                         |

That table is a guide, not a gate. Nothing in the server asks which client is
calling: a client that sends a bearer token is served, a client that discovers
the OAuth flow is served, and one nobody has heard of is served the same way.
[Any MCP client](/connect/clients) has the connection facts on their own.

Both arrive at the same place: a request pinned to one workspace, carrying a set
of scopes, checked by the same code that guards `/api/v2/`. There is no third
kind of principal and no MCP-only permission model.

## Bearer

Send the key as an ordinary bearer token. Nothing else is required.

```http theme={null}
POST /mcp HTTP/1.1
Host: api.impellabs.tech
Authorization: Bearer tgcc_xxxxxxxxxxxx
Content-Type: application/json
MCP-Protocol-Version: 2025-06-18
```

Keys are minted in the dashboard under **Developers → API keys**, are shown
once, store only a hash, carry an optional expiry, record their last use, and
can be revoked individually. They are the same credential `/api/v2/` takes —
see [Authentication and scopes](/v2/authentication) for the full registry.

<Warning>
  Backend and local-editor use only. A `tgcc_` key is a workspace credential: it
  identifies the workspace, and its scopes are the whole of what limits the
  damage if it leaks. Never put one in a browser, a mobile client, or a
  committed config file.
</Warning>

## OAuth 2.1

The chat connector UIs will not take a pasted token; they run a flow. The server
is both the resource server and the authorization server, and advertises
everything a client needs to find its way:

| Document                      | Path                                      | What it is                                                                                                                                                                                                                         |
| ----------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Protected-resource metadata   | `/.well-known/oauth-protected-resource`   | RFC 9728. Names the canonical resource URI, which authorization server issues for it, and every scope it understands. Also served at `/.well-known/oauth-protected-resource/mcp`, because some clients insert the resource's path. |
| Authorization-server metadata | `/.well-known/oauth-authorization-server` | RFC 8414. The endpoints below, and the grants and PKCE methods supported.                                                                                                                                                          |

| Endpoint               | Purpose                                                                                                                          |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `GET /oauth/authorize` | Consent. Requires a dashboard session, names the client and the exact scopes, and records the `resource` you are consenting for. |
| `POST /oauth/token`    | `authorization_code` and `refresh_token`.                                                                                        |
| `POST /oauth/register` | RFC 7591 dynamic client registration, for clients that still use it.                                                             |

Three properties are not optional and are worth knowing because they are what
make a leaked token useless somewhere else:

<AccordionGroup>
  <Accordion title="PKCE with S256 is required">
    Not `plain`, and not absent. An authorization code without a matching
    verifier is refused.
  </Accordion>

  <Accordion title="Tokens are bound to one resource (RFC 8707)">
    The `resource` indicator you consented for is baked into the token. The
    server checks that the audience is **us** and rejects anything else — an MCP
    server must not accept or pass on a token minted for somebody else, and this
    one does not.
  </Accordion>

  <Accordion title="The issuer is named on the response (RFC 9207)">
    So a client can tell which authorization server actually answered, rather
    than assuming.
  </Accordion>
</AccordionGroup>

## The scope split

This is the part that decides what a model can do with your workspace, and it
has two levels.

<Steps>
  <Step title="`mcp:connect` reaches the transport">
    Without it, `POST /mcp` is 403. With it, and nothing else, a client
    completes the handshake and lists every tool.
  </Step>

  <Step title="Each tool demands its own scope">
    Checked per call, inside the dispatcher, against the same registry
    `/api/v2/` uses. `agents:read` to list agents, `documents:create` to upload
    one, `extractions:create` to start an extraction.
  </Step>
</Steps>

<Warning>
  **`mcp:connect` alone lists the tools and calls none of them.** That is the
  design, not a misconfiguration. Connecting is one decision — an endpoint is
  pasted into somebody else's product, and from then on a model chooses when to
  call it. What that model may then *do* is a second decision, and a key minted
  for a nightly export should not become an agent's hands because a URL was
  shared.
</Warning>

Scopes never imply one another. There is no hierarchy and no wildcard: a key
that may read agents cannot run one, and a key that may upload a document cannot
extract from it.

## What a failure looks like

Auth failures are HTTP statuses; tool failures are not. The line is drawn where
the tool starts.

| Status  | Meaning                                   | Header                                                                                            |
| ------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------- |
| **401** | No credential, or one we do not recognise | `WWW-Authenticate: Bearer resource_metadata="…"` — this is how an OAuth client discovers the flow |
| **403** | Authenticated, but the scope is missing   | `WWW-Authenticate: Bearer error="insufficient_scope", scope="…"`                                  |

<Note>
  A 403 names **every** scope the operation needs, in one challenge. Emitting
  them one at a time would force a round trip per scope and teach integrators to
  ask for far more than they need.
</Note>

Anything that happens *inside* a tool — out of credit, not found, a bad argument
— comes back as **HTTP 200** with a tool result marked `isError: true` and
readable text. A JSON-RPC `error` member is reserved for a request that never
reached a tool at all. The reason is the same one that governs the platform as a
client: a tool that cannot answer must return something the model can say out
loud.

<Card title="Credits and the billing gate" icon="coins" href="/connect/credits" horizontal>
  What a call costs, and what an empty wallet returns.
</Card>
