> ## 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.

# Security

> Rate limits, key hygiene, audit log, threat model, and operational best practices

The MCP server's security posture mirrors the rest of Konstantly's API: bearer-token auth, per-user permissions, audit log, rate limits. This page covers what's specific to MCP and what to do operationally.

## Rate limits

Three layers, each a sliding window:

### Per-key limits

Set by license tier:

| Tier       | Per minute | Per day |
| ---------- | ---------- | ------- |
| Business   | 60         | 1,000   |
| Enterprise | 600        | 100,000 |

When a limit is exceeded, the API returns:

```http theme={null}
HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{"error":"Rate limit exceeded (minute). Retry after 12s.","code":"MCP_RATE_LIMITED"}
```

The MCP server **automatically retries** up to 3 times with backoff, honoring the `Retry-After` indication. A well-behaved agent won't trip these in normal use.

### AI course-generation limits (separate, stricter)

Every AI-powered tool shares a single tighter quota on top of the standard per-tier limit:

* `create_course_from_prompt`
* `create_course_from_files`
* `refine_course`
* `regenerate_question`
* `regenerate_lesson`

Each call consumes one unit of the daily AI quota regardless of which of the five tools fired it:

| Scope      | Limit            |
| ---------- | ---------------- |
| Per key    | 5 calls per day  |
| Per tenant | 50 calls per day |

When either limit fires:

```http theme={null}
HTTP/1.1 429 Too Many Requests

{"error":"AI course generation per-key daily quota exceeded. Retry after 3600s."}
```

The error names which scope failed — `per-key` (your own quota) versus `per-tenant` (the workspace-wide quota). The MCP server retries with backoff like any other 429.

### Per-IP limits on failed auth

10 failed authentication attempts per minute per source IP. Designed to make brute-force enumeration noisy (you can detect it in logs) and to throttle bad actors without affecting legitimate use.

```http theme={null}
HTTP/1.1 429 Too Many Requests
{"error":"Too many failed MCP authentication attempts. Retry after 47s.","code":"MCP_AUTH_RATE_LIMITED"}
```

If you're seeing this in legitimate flows, your tool is misconfigured — most likely sending an expired or wrong key. Fix the env var.

## Key hygiene

### Treat keys like passwords

MCP keys are bearer tokens. **Anyone holding the key can act as the issuing user** within the bounds of that user's permissions and license tier. Same exposure model as a GitHub PAT, AWS access key, or Stripe API key.

Specifically:

* Don't commit keys to git
* Don't paste keys into Slack / Discord / email
* Don't share keys between team members (each person should issue their own)
* Don't reuse a key across multiple machines if you can avoid it (one per device makes revocation more surgical)

### Storage on customer devices

The key lives in plaintext in your AI tool's config file:

* **Claude Desktop (macOS)**: `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Claude Desktop (Windows)**: `%APPDATA%\Claude\claude_desktop_config.json`
* **Cursor**: `~/.cursor/mcp.json`

If your laptop is compromised, the key is too. Treat it accordingly.

OS-keychain integration is on the roadmap — for now, the file-on-disk model is the same as how AWS CLI and many other developer tools handle credentials.

### Revoke on device loss

If a device with a stored key is lost / stolen / sold / handed to another person:

1. Go to **Settings → MCP Keys** in Konstantly
2. Find the key for that device (use the name you gave it — *"My laptop"*, *"Production agent"*)
3. Click the trash icon → confirm

The key stops working within seconds. No re-deploy needed; the next MCP call from that device 401s.

### Rotation

Keys don't expire automatically. Best practice:

* Calendar reminder to rotate every 90 days
* After each org change (someone leaves, role changes)
* Whenever you suspect compromise

Optional expiry / auto-rotation is on the roadmap. Until then, rotation is manual.

## Audit log

Every MCP **write-tool call** writes two events to your tenant's audit log:

1. **The action event** — same as a UI-driven action (e.g., `COURSE_ASSIGN`, `USER_BAN`)
2. **`MCP_TOOL_CALL` event** — explicitly tags the call as MCP-driven, with the tool name and redacted args in the `data` column

This gives you two views:

* **"What happened?"** — filter audit log normally; results include both UI and MCP-driven actions, indistinguishable from each other (which is correct — they're the same logical action).
* **"What did MCP do?"** — filter on `eventType=88` (`MCP_TOOL_CALL`) for a fleet-wide view of agent-driven activity per quarter, per user, per tool.

**Read tools are not audited per-call** — too high-volume. Instead they're tracked in the AI Usage dashboard alongside other AI-driven activity.

## Telemetry you get for free

Each MCP call generates:

* **An AI Usage dashboard entry** — tool name, key, duration, success/failure. Visible in the existing AI Usage view alongside other AI-driven activity.
* **An internal analytics event** — fleet-wide tool-selection rates, success/failure ratios, per-tier usage.

For ops teams: a spike in failures for a given tool is a strong signal of either a misconfigured key, a tool description the LLM is misunderstanding, or a backend regression.

## Threat model

What the security posture defends against:

### Defended

| Threat                                   | Mitigation                                                                                                                                                                                                                                                                                                                                                |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Key brute-force**                      | 256 bits of entropy = \~10⁷⁷ guesses required. Mathematically infeasible. Per-IP rate limit on auth failures adds telemetry signal.                                                                                                                                                                                                                       |
| **Cross-tenant data access**             | Per-request tenant resolution by subdomain. Keys are stored per-tenant; a key from tenant A used against tenant B's URL fails at lookup.                                                                                                                                                                                                                  |
| **Privilege escalation**                 | Capabilities endpoint filters tools by permission BEFORE registering with the LLM. Each endpoint independently re-checks server-side. Layered defense.                                                                                                                                                                                                    |
| **License downgrade bypass**             | License tier read fresh per request. Pre-issued write keys silently clamped.                                                                                                                                                                                                                                                                              |
| **Permission revocation lag**            | No permission cache. Revoking a permission immediately blocks the next MCP call requiring it.                                                                                                                                                                                                                                                             |
| **Timing attack on key validation**      | Constant-time SHA-256 hash comparison (`crypto.timingSafeEqual`).                                                                                                                                                                                                                                                                                         |
| **Information leak via error responses** | Bad-key / revoked-key / banned-user failures return the same opaque 401 with no body — no distinction between them. The license-disabled case is a deliberate exception: it returns 403 + `MCP_LICENSE_DISABLED`, which is only reachable with a valid key, so the leak is intentional (admin can act on the message instead of filing a support ticket). |
| **CSRF**                                 | Not applicable — MCP server is server-to-server, browser never sees `X-MCP-Key`.                                                                                                                                                                                                                                                                          |
| **Session fixation via MCP**             | MCP auth synthesizes a SessionData but never sets a cookie. No persistent session created.                                                                                                                                                                                                                                                                |

### Acknowledged risks (mitigation = ops + customer hygiene)

| Risk                                                              | Why we accept it                                                                                                                                                                   | What you can do                                                                                                                                            |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Key stored plaintext on customer device                           | Industry-standard for developer tools today (AWS CLI, gh, npm). Keychain integration on roadmap.                                                                                   | Treat the file like a password store. Encrypt the disk. Revoke on device loss.                                                                             |
| Compromised npm publisher account → poisoned package              | Real but not preventable from your side. We publish from a locked-down account and plan to ship sigstore provenance signatures so customers can verify via `npm audit signatures`. | Pin a specific version in enterprise installs (`@konstantly/mcp-server@0.2.0` instead of just the latest tag) to limit auto-flow of a compromised release. |
| Customer types wrong `KONSTANTLY_URL` and ships key to wrong host | The MCP server refuses non-HTTPS URLs, but it still trusts whatever host the customer typed.                                                                                       | Use the install snippets from `/settings/api-keys` — copy-paste with the correct URL already filled in. Don't hand-edit URLs.                              |

## Operational checklist before public-facing use

If you're rolling MCP out to a team or customer base:

* [ ] **Audit your existing MCP keys** — are any held by users who've left? Revoke them.
* [ ] **Document key-naming convention** — *"alex-laptop"*, *"prod-slack-bot"* makes revoke decisions obvious. *"key1"* doesn't.
* [ ] **Set a rotation calendar** — 90 days is reasonable for human-held keys; CI bots can rotate weekly via automated re-issue.
* [ ] **Surface MCP activity to your security team** — the AI Usage dashboard shows all agent activity in near-real-time.
* [ ] **Wire audit log to your SIEM** — events 86 (`MCP_KEY_CREATE`), 87 (`MCP_KEY_REVOKE`), 88 (`MCP_TOOL_CALL`) are the MCP-relevant ones.
* [ ] **Train your admins** to revoke keys before offboarding (same workflow as disabling SSO accounts).

## AI Policy

How Konstantly handles AI processing — including which third-party providers receive customer data, how the MCP integration creates a sub-processing relationship with whichever AI agent you install (Claude, Cursor, etc.), and what we contractually disallow (e.g. training providers' models on customer data) — is documented in our published [AI Policy](https://konstantly.com/legal/ai-policy). Required reading for enterprise procurement reviews and EU AI Act compliance.

## Next steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/api-reference/mcp/authentication">
    Key format, scopes, license-tier interactions
  </Card>

  <Card title="Permissions" icon="user-shield" href="/api-reference/mcp/permissions">
    Role-by-role tool matrix
  </Card>

  <Card title="Tools Reference" icon="wrench" href="/api-reference/mcp/tools-reference">
    All 25 tools
  </Card>

  <Card title="Quickstart" icon="rocket" href="/api-reference/mcp/quickstart">
    Try it in 5 minutes
  </Card>
</CardGroup>
