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

# Authentication

> How MCP API keys work, scopes, license tiers, and the security model

The MCP server authenticates with your Konstantly tenant using a single bearer token — an **MCP API key** — passed in the `X-MCP-Key` HTTP header on every request.

## Key anatomy

Every MCP key looks like this:

```
kmcp_a8f3b2k9X7yZqWpL3mN8RvU2sT5dH6jK4F1gA9oP
```

* **Prefix**: `kmcp_` — identifies the token type. Always present.
* **Body**: 43 URL-safe base64 characters (256 bits of randomness). Generated by `crypto.randomBytes(32)` server-side.
* **Total length**: 48 characters.

The full key is shown **once** at creation time. Konstantly stores only the SHA-256 hash; the plaintext is unrecoverable after the dialog closes.

## Key lifecycle

| Event            | Where it happens                                                                                                                 |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **Generate**     | Settings → MCP Keys → Generate. Full key shown once.                                                                             |
| **Store**        | In your AI tool's config file (Claude Desktop's `claude_desktop_config.json`, Cursor's `~/.cursor/mcp.json`, etc.) as an env var |
| **Authenticate** | Every MCP request sends `X-MCP-Key` header. Backend verifies via constant-time hash compare.                                     |
| **Track usage**  | `last_used_at` timestamp updated on every successful authentication                                                              |
| **Revoke**       | Settings → MCP Keys → trash icon. Takes effect within seconds; agent stops working immediately.                                  |

<Note>
  **Keys do not expire automatically.** Add a calendar reminder to rotate periodically. Optional key expiry is on the roadmap.
</Note>

## Who the key authenticates as

Each MCP key is **owned by a specific user** — the admin who created it. Every tool call executes with that user's permissions, exactly as if they had logged into the web UI and clicked the same button.

**Example**: a learner-issued key cannot read admin-only audit logs, cannot assign training to others, cannot ban users — because the owning learner can't do those things in the UI either.

This is the same authorization model as everywhere else in Konstantly. There is no parallel "MCP permission system" to learn or get wrong.

See the [Permissions matrix](/api-reference/mcp/permissions) for which role each tool requires.

## Scopes

When generating a key, you choose its scopes:

| Scope     | What it allows                                                                                               |
| --------- | ------------------------------------------------------------------------------------------------------------ |
| **read**  | All read tools (`find_user`, `get_compliance_status`, `list_certificates`, etc.)                             |
| **write** | All write tools (`assign_training`, `ban_user`, `clone_course`, etc.). Requires Business or Enterprise plan. |

The MCP server consults the [capabilities endpoint](#capabilities-endpoint) on startup to learn which scopes your key has, then registers only the tools the agent can actually call. **Tools the agent can't invoke aren't shown to the LLM** — the model never picks a tool that's guaranteed to 403.

## License tier interaction

Your tenant's license tier sets an **upper bound** on what any MCP key can do, regardless of how the key was issued:

| License tier (`restrictions.mcpAccess`) | Effect                                                                                                                                                            |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"none"` (default)                      | MCP server disabled. Requests return `403` with code `MCP_LICENSE_DISABLED` and the message *"MCP is not enabled on this workspace. Talk to your administrator."* |
| `"read"`                                | All keys treated as read-only, even keys originally issued with `write: true`                                                                                     |
| `"full"`                                | Read and write tools available per key scope                                                                                                                      |

If your tenant's `mcpAccess` is changed mid-life-of-a-key (e.g. clamped to `"read"` or `"none"`), write tools fail silently for that key — no re-issue required. The clamp is applied on every request.

## Capabilities endpoint

When the MCP server starts up, it calls:

```http theme={null}
GET /api/v2/mcp/capabilities HTTP/1.1
Host: your-instance.konstantly.com
X-MCP-Key: kmcp_a8f3b2k9...
```

Response:

```json theme={null}
{
  "tier": "full",
  "scopes": { "read": true, "write": true },
  "user": {
    "id": 42,
    "name": "Alex Admin",
    "email": "alex@acme.com"
  },
  "tools": [
    "find_user", "get_user", "find_course", "get_course",
    "list_assignments", "list_my_assignments",
    "get_compliance_status", "list_certificates",
    "get_statistics", "get_leaderboard", "get_audit_log",
    "assign_training", "bulk_assign", "assign_chain",
    "ban_user", "unban_user", "change_user_email",
    "clone_course"
  ],
  "resources": []
}
```

The `tools` array reflects what the calling key can actually invoke after permission + scope + license filtering. The MCP server uses this to register tools with the LLM.

Failed authentication on missing/invalid/revoked-key returns `401 Unauthorized` with no body — no information leaks. The `403 MCP_LICENSE_DISABLED` case is distinct: it's only reachable with a valid key, so it deliberately tells the caller MCP isn't enabled for the workspace instead of falling through to an opaque 401.

## Error semantics

| HTTP status | Code                    | When                                                                                  |
| ----------- | ----------------------- | ------------------------------------------------------------------------------------- |
| `401`       | `UNAUTHORIZED`          | Missing, invalid, or revoked key; banned user                                         |
| `402`       | `LICENSE_RESTRICTED`    | Session-authed key-management endpoints when the tenant has no MCP license at all     |
| `403`       | `MCP_LICENSE_DISABLED`  | Key is valid but tenant's `mcpAccess` is `"none"`                                     |
| `403`       | `MCP_AI_DISABLED`       | AI tool invoked while tenant's `mcpAiAccess` is `false`                               |
| `403`       | (varies per endpoint)   | Key is valid, but the owning user lacks permission for the requested action           |
| `429`       | `MCP_RATE_LIMITED`      | Per-key rate limit exceeded ([see Security](/api-reference/mcp/security#rate-limits)) |
| `429`       | `MCP_AUTH_RATE_LIMITED` | Per-IP rate limit on failed auth attempts (anti-enumeration)                          |

`429` responses include a `Retry-After` value in the error message body. The MCP server automatically retries 429s with exponential backoff up to 3 times.

## Programmatic verification

To verify a key works without going through an AI tool:

```bash theme={null}
curl -i https://your-instance.konstantly.com/api/v2/mcp/capabilities \
  -H "X-MCP-Key: kmcp_a8f3b2k9X7yZ..."
```

Expected: `200 OK` with the JSON body shown above.

Common failure modes:

```bash theme={null}
# Bad key
HTTP/2 401
{"error":"Unauthorized","code":"UNAUTHORIZED"}

# Tier is "none"
HTTP/2 403
{"error":"MCP is not enabled on this workspace. Talk to your administrator.","code":"MCP_LICENSE_DISABLED"}

# Too many bad-key attempts from this IP
HTTP/2 429
{"error":"Too many failed MCP authentication attempts. Retry after 47s.","code":"MCP_AUTH_RATE_LIMITED"}
```

## Multiple keys per user

A single Konstantly user can have multiple MCP keys — one per device, per agent, per environment (e.g., one for your laptop's Claude Desktop, one for your team's Slack bot). Each has its own `last_used_at` and its own revoke control.

There's no built-in limit on key count per user. Revoke unused keys regularly to keep the list manageable.

## Next steps

<CardGroup cols={2}>
  <Card title="Tools Reference" icon="wrench" href="/api-reference/mcp/tools-reference">
    All 25 tools, parameters, examples
  </Card>

  <Card title="Permissions matrix" icon="user-shield" href="/api-reference/mcp/permissions">
    Which role can invoke which tool
  </Card>

  <Card title="Security" icon="lock" href="/api-reference/mcp/security">
    Rate limits, audit log, best practices
  </Card>

  <Card title="Quickstart" icon="rocket" href="/api-reference/mcp/quickstart">
    Generate a key and try it
  </Card>
</CardGroup>
