# qlim8 MCP Server — Quickstart

The qlim8 MCP (Model Context Protocol) server exposes **31 curated tools**, **3 resources**,
and **3 prompts** that let AI assistants (Claude, ChatGPT, Cursor, Copilot, Replit, Lovable,
and any MCP-compatible client) interact directly with carbon accounting data. The server
follows the MCP specification and is compatible with all standard MCP clients.

**Endpoint:** `https://app.qlim8.com/api/mcp`
**Transport:** Streamable HTTP, **stateless** — every request is a self-contained POST
**Auth:** **OAuth 2.1** (sign in with your qlim8 account — used by Claude and ChatGPT
connectors, no API key needed) **or** a `qk_live_…` / `qk_sandbox_…` API key sent as a
Bearer token (the same keys as `/api/v1`)

---

## Connect your AI chat — no code required

This section is for regular users: connect Claude or ChatGPT to your company's qlim8 data
and start asking questions in plain language. You authenticate by signing in to qlim8 in
the browser — **no API key, no configuration files**.

**Before you start:**

1. You need a **qlim8 user account** in your company's tenant.
2. A qlim8 **tenant admin** must approve the connection — the OAuth consent screen is
   admin-only. If you're not an admin, an admin colleague completes the sign-in step once;
   after that the connector works for the chat account it was added to.
3. Access is **read-only by default**. The admin can opt in to write access
   (create targets, generate reports, manage scenarios) on the consent screen.

### Claude (claude.ai, Claude Desktop & mobile)

Custom connectors are available on **all Claude plans, including Free** (Free is limited
to one custom connector).

1. Open **Settings → Connectors** in Claude.
2. Click **Add custom connector**.
3. Enter a name (`qlim8`) and the URL: `https://app.qlim8.com/api/mcp`
   — leave the OAuth Client ID / Secret fields in Advanced settings **blank**; the
   connector self-registers (Dynamic Client Registration, RFC 7591).
4. Click **Add**, then **Connect**. Your browser opens qlim8 — sign in and approve.
5. Back in Claude, enable the connector in a chat (search & tools menu) and ask away.

On **Team / Enterprise** plans, an organization **Owner** first adds the connector under
**Organization Settings → Connectors → Add connector → Custom**, after which each member
clicks **Connect** and signs in individually.

### ChatGPT

Custom MCP connectors (called **apps** in ChatGPT) require a paid plan —
**Plus, Pro, Business, Enterprise, or Edu**. They are not available on the Free tier.

1. Open **Settings → Apps → Advanced settings** and enable **Developer mode**.
   (On Business/Enterprise workspaces an admin may need to allow custom apps first.)
2. Go back to **Apps** and choose **Create / Add app**.
3. Enter a name (`qlim8`), the MCP server URL `https://app.qlim8.com/api/mcp`,
   and pick **OAuth** as authentication — leave client credentials blank.
4. Save, then **Connect**: your browser opens qlim8 — sign in and approve.
5. In a chat, enable the qlim8 app and ask your questions.

### Gemini

Google's Gemini app does **not yet support custom MCP connectors** for regular users —
neither on the web nor in the mobile app. Technical alternatives exist (Gemini CLI and
Gemini Enterprise support remote MCP servers, including this one), but there is no
consumer-friendly path yet. We'll add a guided setup here as soon as Google ships support.

### What to ask once connected

- *"What were our total CO2e emissions in 2025, split by scope?"*
- *"Show our emissions by month for 2025 — any seasonal pattern?"*
- *"Which categories drive most of our Scope 3? Use the GHG Protocol breakdown."*
- *"Are we on track for our 2030 reduction target?"*
- *"Summarise our supplier coverage for the 2025 value chain report."*

### How the sign-in works (for the curious)

The qlim8 MCP server is a full OAuth 2.1 protected resource and its own authorization
server. Clients discover it via `/.well-known/oauth-protected-resource` (RFC 9728) and
`/.well-known/oauth-authorization-server` (RFC 8414), self-register with Dynamic Client
Registration (RFC 7591), and use Authorization Code + PKCE (S256). Consent is restricted
to tenant admins, defaults to read-only scopes, and issues access tokens valid for 1 hour
with 60-day rotating refresh tokens. Connections can be revoked anytime from
**Collectors → API Keys** in the qlim8 app.

---

## Developer setup (API key)

For CLI tools, IDEs and custom agents, authenticate with a static API key instead of
OAuth. Mint one at **Collectors → API Keys → Generate Key** in the app. Request only the
scopes you need (see the [Tool reference](./mcp-tools-reference.md) for per-tool
requirements). Keys look like `qk_live_<64-hex>` or `qk_sandbox_<64-hex>`.

### Claude Code (CLI)

```bash
claude mcp add --transport http qlim8 https://app.qlim8.com/api/mcp \
  --header "Authorization: Bearer qk_live_<your-64-hex-key>"
```

Then in a session, the qlim8 tools appear automatically. Read-only tools (annotated
`readOnlyHint`) are safe to allow without confirmation; the write tools (`create_*`,
`generate_report`, `add_tiltag`, `submit_scenario_for_review`) mutate state.

### Claude Desktop (`claude_desktop_config.json`)

Claude Desktop also supports the no-code OAuth connector above (**Settings →
Connectors**). For key-based setups:

```json
{
  "mcpServers": {
    "qlim8": {
      "type": "http",
      "url": "https://app.qlim8.com/api/mcp",
      "headers": {
        "Authorization": "Bearer qk_live_<your-64-hex-key>"
      }
    }
  }
}
```

### Cursor (`.cursor/mcp.json` or settings)

```json
{
  "mcp": {
    "servers": {
      "qlim8": {
        "url": "https://app.qlim8.com/api/mcp",
        "headers": { "Authorization": "Bearer qk_live_<your-64-hex-key>" }
      }
    }
  }
}
```

> **Mistral Le Chat** auto-detects auth: it supports both the OAuth flow and a
> **static bearer token** — there you can paste a `qk_live_…` API key instead.

### Manual (curl / raw HTTP)

The transport is **stateless**: there is no session to establish and no
`mcp-session-id` header. Every request is an independent POST. Include the
`Accept: application/json, text/event-stream` header — responses arrive as a
single SSE-framed message.

```bash
# List available tools
curl -N -X POST https://app.qlim8.com/api/mcp \
  -H "Authorization: Bearer qk_live_<your-key>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

# Call a tool
curl -N -X POST https://app.qlim8.com/api/mcp \
  -H "Authorization: Bearer qk_live_<your-key>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "get_emissions_summary",
      "arguments": { "from": "2024-01-01T00:00:00Z", "to": "2024-12-31T23:59:59Z", "group_by": "month" }
    }
  }'

# Response framing (Content-Type: text/event-stream, one frame per response):
#   event: message
#   data: {"jsonrpc":"2.0","result":{...},"id":2}
```

Spec-compliant clients still send an `initialize` request first — that works too and is
equally stateless (the protocol version is negotiated there; `2024-11-05` and newer are
accepted). `GET /api/mcp` returns **405** (no server→client stream) and
`DELETE /api/mcp` returns **204** (nothing to close).

### Discover the full surface (no auth required)

```bash
curl https://app.qlim8.com/api/mcp/schema
```

The schema document lists every tool (name, title, description, scope, layer, tier gate,
annotations, and example I/O), the shared conventions for pagination/dates/errors, and the
read/write split that drives rate-limit bucketing. LLM clients can read it before minting a
key to learn the surface.

---

## The 31 tools at a glance

Tools are grouped into three access layers. Each tool requires the scope shown and carries
[annotations](#annotations) describing whether it mutates state.

### Layer 1 — core emissions & operations
`get_emissions_summary` (by scope, or by month with `group_by:"month"`),
`get_emissions_by_scope3_category`, `get_emissions_by_category`, `list_emissions`,
`get_emission_lineage`, `list_activities`, `list_reports`, `get_report_status`,
`generate_report`, `list_data_sources`, `get_quota_status`, `list_emission_categories`

### Layer 2 — value chain, targets, factors, scenarios
`list_suppliers`*, `get_value_chain_coverage`*, `get_value_chain_exposure`*, `list_targets`,
`create_target`, `list_emission_factors`, `get_factor_citations`, `list_scenarios`†,
`get_scenario`†, `list_tiltag`†, `create_scenario_draft`†, `add_tiltag`†,
`submit_scenario_for_review`†, `list_pcf_records`, `list_departments`,
`get_report_attestations`

### Layer 3 — webhooks
`list_webhooks`, `create_webhook`, `get_webhook_deliveries`

\* Enterprise plan required.  † Requires the `scenarioBuilder` plan feature.

See the [Tool reference](./mcp-tools-reference.md) for full per-tool documentation.

---

## Annotations

Every tool carries MCP behavioural hints. Agents and clients should use them to decide what to
auto-run:

| Annotation | Meaning |
|---|---|
| `readOnlyHint: true` | Does not mutate tenant state. **Safe to auto-approve / run freely.** |
| `destructiveHint` | Only meaningful for writes. `false` everywhere — qlim8 writes are additive, never delete/overwrite. |
| `idempotentHint: true` | Repeating with the same arguments has no extra effect (e.g. `generate_report`). |
| `openWorldHint: false` | Always — tools only touch the authenticated tenant's data. |

The read-only set is also what the server uses to **bucket rate limits**: read-annotated tools
draw from the read budget, the rest from the (much smaller) write budget.

---

## Resources

Resources are read-only reference context addressable by URI — no side effects. Pull them in
to ground reasoning before calling tools.

| URI | Type | What it gives you |
|---|---|---|
| `qlim8://emission-categories` | `application/json` | The live category taxonomy (Danish + English names, hierarchy level, parent, default scope, factor value). |
| `qlim8://ghg-scopes` | `application/json` | GHG Protocol scope 1/2/3 definitions as qlim8 applies them (market-based scope 2 by default; values in kg CO2e). |
| `qlim8://usage-guide` | `text/markdown` | Auth, scopes, pagination, date and error conventions for this server. |

List them with `resources/list` and read one with `resources/read`.

---

## Prompts

Prompts are reusable, parameterised task templates that chain qlim8 tools. Clients surface them
as slash-commands / starter prompts.

| Prompt | Arguments | What it does |
|---|---|---|
| `emissions_vs_target_analysis` | `year` | Compares actual emissions against the company's reduction targets and assesses whether it's on track. |
| `year_over_year_review` | `current_year`, `previous_year` | Compares two years of emissions and explains the main drivers of change. |
| `value_chain_briefing` | `year` | Summarises Scope 3 supplier coverage and the largest exposure (Enterprise). |

List them with `prompts/list` and fetch one with `prompts/get`.

---

## Error model

qlim8 distinguishes **tool-execution errors** from **protocol errors**:

**Tool-execution errors are returned in-band** — as a normal `tools/call` result with
`isError: true` and a JSON text body so the model can read it and self-correct:

```json
{
  "code": "auth.scope_required",
  "title": "Insufficient scope",
  "detail": "Requires scope 'targets:write'. Call GET /api/v1/me to see your key's scopes."
}
```

Common `code` values: `auth.scope_required`, `subscription.feature_required`,
`resource.not_found`, `request.validation_failed`. Branch on `code`. A scope-denied result does
**not** echo the key's granted scopes — call `GET /api/v1/me` to see what your key can do.

**Protocol-level problems use JSON-RPC error codes:**

| JSON-RPC code | Meaning |
|---|---|
| `-32001` | Auth / rate error — missing or invalid Bearer token, revoked key, or rate limit exceeded. |
| `-32603` | Internal server error. |
| HTTP 405 on `GET /api/mcp` | Expected — the stateless server has no server→client stream. |
| HTTP 204 on `DELETE /api/mcp` | Expected — there is no session to close. |

> Note: tool input-validation failures are surfaced as an in-band `request.validation_failed`
> result (`isError: true`), **not** as a JSON-RPC `-32602`. Read the result body, not just the
> envelope.

---

## Rate limits

Two layers apply:

1. **Edge IP limit (pre-auth):** a per-source-IP cap (default **300 req/min/IP**, configurable
   via `V1_EDGE_RPM`) sits in front of `/api/v1` and `/api/mcp`. Exceeding it returns a
   JSON-RPC `-32001` error with a `Retry-After` header before any key is even checked.
2. **Per-API-key limit:** after auth, requests draw from per-key buckets keyed on the read/write
   annotation:
   - Read operations: **600 req/min** per key
   - Write operations: **60 req/min** per key
   - Sandbox keys: **10× stricter** (60 read / 6 write)

A third-party consultant key serving many clients shares a single budget per key (not per
tenant).

---

## Transport & statelessness

The server runs the Streamable HTTP transport in **stateless mode**: every POST is
self-contained, handled by a fresh protocol instance, and torn down when the response
closes. There are **no sessions** — no `mcp-session-id` header, nothing that can expire,
and nothing to re-establish after a deploy or restart. A stale `mcp-session-id` header
sent by an older client is simply ignored. `GET /api/mcp` returns 405 (no standalone
server→client stream) and `DELETE /api/mcp` returns 204 (a no-op).

---

## Client compatibility

The server follows the MCP specification (protocol version negotiated at `initialize`;
`2024-11-05` and newer are accepted). It is compatible with:

| Client | Notes |
|---|---|
| **Claude (claude.ai, Desktop, mobile)** | No-code custom connector via OAuth (see above). All plans, incl. Free. |
| **ChatGPT** | Custom app via Developer mode + OAuth (Plus/Pro/Business/Enterprise/Edu). |
| **Claude Code** | `claude mcp add --transport http …` (see above) |
| **Cursor** | Full support via MCP server config |
| **Mistral Le Chat** | OAuth or static `qk_` bearer token |
| **Replit Agent** | HTTP transport supported |
| **Lovable** | HTTP transport supported |
| **Gemini** | No consumer support yet — Gemini CLI / Gemini Enterprise only |
| **Custom agents** | Any client that speaks JSON-RPC 2.0 over HTTP |

Tool descriptions and input schemas are written for LLM consumption — precise, concise, with
format hints on every parameter. They work across Claude, GPT and other LLMs.
