Zum Hauptinhalt springen

Einen Klar Attribution MCP-Server mit Claude bauen

Dieser Guide zeigt dir, wie du die Klar Attribution API über das Model Context Protocol (MCP) mit einem KI-Assistenten wie Claude verbindest.

Verfasst von Frank Birzle

Sobald das eingerichtet ist, kannst du Fragen stellen wie „Welche Kanäle hatten letzten Monat den besten ROAS unter datengetriebener Attribution?" oder „Wie sind Umsatz und Gewinn pro Produkt in diesem Quartal?" und Claude fragt deine Klar-Daten ab und antwortet direkt – ohne Dashboards, Exporte oder Copy-Paste.

Du musst den Server nicht von Hand schreiben. Wir stellen dir unten eine fertige CLAUDE.md-Spezifikation bereit: Leg sie in einen leeren Projektordner, richte Claude Code (oder einen beliebigen Coding-Agent) darauf und lass ihn den Server bauen. Die Datei enthält alles, was der Agent braucht – die API-Referenz, die Constraints und die bereitzustellenden Tools.

Was ist neu an dieser API

Klar hat heute eine neue Public API veröffentlicht, die die bisher in diesem Guide behandelte reine Attributions-API ersetzt. Falls du einen Server aus der früheren Version dieses Artikels gebaut hast, hier die Änderungen:

  • Die Authentifizierung ist einfacher. Ein einziger langlebiger API-Key kommt direkt in einen X-API-Key-Header. Es gibt keinen Refresh-Token-Austausch mehr für ein 5-Minuten-Access-Token.

  • Drei Report-Typen statt einem. Die API deckt jetzt Attribution, Marketing-Performance sowie Umsatz & Gewinn ab – jeweils abgesichert durch eine eigene Permission auf dem Key.

  • Accounts können mehrere Shops enthalten. Ein neuer Shops-Endpunkt listet die Shops auf, die ein API-Key sehen kann; jeder Report-Aufruf braucht jetzt eine shopId.

  • Ergebnisse werden per Cursor paginiert. Ein Report-Aufruf gibt kein JSON-Array mehr direkt zurück. Er liefert eine dataUrl; der folgst du und dann nextPage, bis es nicht mehr vorhanden ist.

  • Das Rate-Limit ist höher. 5 Requests pro 30 Sekunden pro Key, vorher 2.

  • Keine 31-Tage-Fenster-Grenze mehr. startDate und endDate sind beide inklusive, und die Dokumentation begrenzt nicht mehr, wie breit ein einzelner Zeitraum sein darf – stattdessen übernimmt die Pagination große Ergebnismengen.

Was ist MCP?

MCP ist ein offener Standard, mit dem KI-Assistenten externe Tools und Datenquellen über eine einzige, einheitliche Schnittstelle aufrufen können. Ein MCP-Server ist ein kleines Programm, das „Tools" bereitstellt, die der Assistent nutzen kann. Bau einen, der die Klar Public API kapselt, und jeder MCP-fähige Client – Claude Desktop, Claude Code und andere – kann deine Attributions-, Marketing- sowie Umsatz-&-Gewinn-Daten bei Bedarf abrufen.

Bevor du startest

Du brauchst:

  • Einen Klar Public API Key. Wird im Klar-Dashboard erstellt und verwaltet, beschränkt auf die Permissions, die du brauchst (public_api.attribution, public_api.marketing, public_api.revenue_and_profit). Behandle ihn wie ein Passwort.

  • Node.js 20 oder neuer installiert.

  • Claude Code (npm install -g @anthropic-ai/claude-code) oder einen anderen Coding-Agent, um den Server zu bauen, sowie Claude Desktop, falls du den fertigen Server dort nutzen möchtest.

Schritt 1 – Projektordner erstellen und CLAUDE.md hinzufügen

Erstelle einen leeren Ordner für das Projekt und speichere die Datei unten darin als CLAUDE.md. Das ist die Build-Spezifikation – sie sagt dem Coding-Agent genau, was er bauen soll und wie sich die Klar-API verhält.

Claude.md:

# CLAUDE.md — Klar Public API MCP Server

## Project goal

Build a Model Context Protocol (MCP) server that wraps the Klar Public API so
that any MCP-capable client (Claude Desktop, Claude Code, etc.) can query a
Klar account's attribution, marketing performance, and revenue & profit data
in natural language. The server exposes each report as a typed tool, handles
authentication, and absorbs Klar's API constraints (two-step cursor
pagination, the rate limit, and shop resolution) so the user never has to
think about them.

## Tech stack

- TypeScript + Node.js (Node 20+).
- @modelcontextprotocol/sdk (latest) — use McpServer and StdioServerTransport.
- zod for input validation.
- stdio transport (local), so the server plugs straight into Claude Desktop
and Claude Code.
- Native fetch (built into Node 20+) — no extra HTTP client needed.

Python is a fine alternative (official mcp / FastMCP SDK). If you go that
route, keep the same tool surface and the same constraint handling.

## Build steps

1. Scaffold the project: "type": "module" in package.json, a tsconfig.json
targeting NodeNext, output to ./build, and a build script (tsc).
2. Implement a Klar API client module (auth header, shop resolution, cursor
pagination, rate limiting) honoring the constraints in the API reference
below.
3. Implement the MCP server with the tools described under "Tool surface".
4. Compile (npm run build) and confirm it builds cleanly.
5. Write a README that includes the exact claude_desktop_config.json entry.

Do not call the live API during the build. The user supplies a real API key
at runtime via an environment variable.

## Klar Public API reference

Base URL: https://api.getklar.com

### Authentication

Every request needs the API key in a header:

X-API-Key: <KLAR_API_KEY>

Keys are 72 characters: the prefix klar_pk_ followed by 64 hex characters.
Read the key from the KLAR_API_KEY environment variable. Never hardcode it
and never write it to logs — mask it (e.g. show only the last 4 characters)
in any debug output.

A key is scoped to one account and to a set of permissions
(public_api.attribution, public_api.marketing,
public_api.revenue_and_profit). A call to an endpoint the key is not
permitted for returns 403 Forbidden with a message naming the missing
permission — surface that message to the user instead of a generic error.

### Shops — resolve the shopId

- Endpoint: GET /v1/public/shops
- Returns an array of { shopId, name }. shopId is a signed value — always
use the value returned here, never construct it yourself.

Every report endpoint requires a shopId. If the user names a shop instead of
supplying an ID, call list_shops, match by name (case-insensitive), and use
the returned shopId. Cache the shop list for the life of the process; refresh
it if a report call reports an unrecognized shopId.

### Reports — two-step, cursor-based

None of the report endpoints (attribution, attribution-detail, marketing,
revenue-and-profit) return data directly. Instead:

1. Initiate the report, e.g.
GET /v1/public/attribution?shopId=...&startDate=...&endDate=...
The response is { "dataUrl": "https://api.getklar.com/v1/public/results/<token>?page=1" }.
2. Fetch data by calling dataUrl. Each page returns
{ "results": [...], "nextPage": "<url>" } — up to 1,000 rows per page.
Follow nextPage until it is absent from the response, concatenating
results across pages.

The cursor token is valid for 10 minutes from the first /results call. If
paging takes long enough that the token could expire, or a page call returns
400/401 saying the cursor expired, re-initiate the report and resume paging.

All fields in result rows are camelCase.

### Report endpoints

- GET /v1/public/attribution — startDate, endDate (required, inclusive,
YYYY-MM-DD), metric (optional enum: first_touch, last_touch,
data_driven [default], linear, any_click, any_click_unique, u_shape,
time_decay, marketing_mix), window (optional enum: unlimited [default],
1_day, 7_day, 28_day), date_breakdown (optional enum: order [default],
touch). Requires public_api.attribution.
- GET /v1/public/attribution-detail — order-level detail. startDate, endDate
(required), lookbackWindow (optional enum, default unlimited), sortField
(optional, default order_id), sortDirection (optional: asc/desc, default
desc). Requires public_api.attribution.
- GET /v1/public/marketing — startDate, endDate (required). dimensions
(optional, comma-separated, up to 5 of: channel_name, channel_category,
channel_group, campaign, term, content, date, calendar_week,
calendar_month, quarter, country; default [date]). Optional filters:
customer_type, product_uids, order_tags, customer_tags, discount_codes
(comma-separated strings). date_granularity (optional: event [default] or
order). Requires public_api.marketing.
- GET /v1/public/revenue-and-profit — startDate, endDate (required).
dimensions (optional, comma-separated, up to 5, from a long enum covering
order/product/channel/customer/geo fields — see the full list in the
OpenAPI document at https://api.getklar.com/public/docs; default
[calendar_date]). debundle (optional: 0 [default] or 1, splits bundled
products into their components). date_granularity (optional: event
[default] or order). Requires public_api.revenue_and_profit.

All three report endpoints also require shopId (see "Shops" above).

### Constraints the server MUST enforce

1. Rate limit: 5 requests per 30 seconds per API key, shared across every
endpoint (shops, all report-initiations, and every /results page).
Implement a token-bucket or queue so the client never exceeds this,
including during long paging sequences.
2. Cursor pagination as described above — always follow dataUrl, then
nextPage. Never assume a report returns all its rows in one call.
3. Shop resolution as described above.
4. Logging: on stdio transport, never write to stdout — it corrupts the
JSON-RPC stream. Use console.error (stderr) only.
5. Errors: catch failures and return
{ isError: true, content: [{ type: "text", text: "..." }] } instead of
throwing, so a single bad call doesn't kill the client session. Include
Klar's own error message (the "message" field) when available.

## Metric glossary

These are working definitions for tool descriptions. Klar's attribution
model and revenue math did not change with this release — only how the
data is delivered did. For authoritative formulas, see Klar's margin-metrics
documentation in the help center.

- orders — attributed orders. Fractional values are expected: attribution
models split partial credit for an order across the touchpoints that led
to it.
- nc / rc — attributed New-Customer and Returning-Customer orders (also
fractional).
- grossRevenue / netRevenue — attributed gross and net revenue.
- cost — ad spend for the row. ROAS is netRevenue / cost.
- clicks / impressions — ad engagement.
- cm1 / cm2 — contribution-margin tiers (CM1 after product/variable costs,
CM2 after marketing cost).
- clv_30 / clv_60 / clv_90 — projected customer lifetime value at 30 / 60 /
90 days.
- acm2 — adjusted CM2.
- ncGrossRevenue / rcGrossRevenue / ncNetRevenue / rcNetRevenue — gross and
net revenue split by new vs. returning customers.

Exact field names depend on the report and the dimensions requested — treat
this glossary as a guide, not a fixed schema.

## Tool surface

Core — build these first:

- list_shops — no params. Returns the shops available to the API key
({ shopId, name }[]). Call this whenever a shopId is needed and the user
has not supplied one.
- get_attribution_report — shopId, startDate, endDate, metric, window,
date_breakdown. Pages through the full result set and returns the
combined rows.
- get_marketing_report — shopId, startDate, endDate, dimensions, and the
optional filters listed above. Pages through and returns combined rows.
- get_revenue_and_profit_report — shopId, startDate, endDate, dimensions,
debundle, date_granularity. Pages through and returns combined rows.

Optional extensions (add if useful to the user):

- get_attribution_detail_report — order-level attribution detail (see
endpoint above).
- summarize_by_channel — takes the same params as get_attribution_report or
get_marketing_report; aggregates rows by channel dimension (sum orders,
cost, netRevenue, grossRevenue; derive ROAS = netRevenue / cost).
- compare_periods — two date ranges; returns per-channel deltas for any of
the three report types.

Validate every param with zod and surface the exact accepted values in the
schema. Bake the key interpretation rules — fractional orders, inclusive
date ranges, ROAS = netRevenue / cost, camelCase field names — into the tool
descriptions so the model reads the results correctly.

## Run & test

- npm run build
- npx @modelcontextprotocol/inspector node ./build/index.js

## README must include

A one-line description; the exact claude_desktop_config.json snippet; the
tool list with one-line descriptions; the required env var (KLAR_API_KEY);
and a short troubleshooting section covering cursor-token expiry, rate-limit
pauses, 403s from missing permissions, and the no-stdout-logging rule.

Schritt 2 – Lass den Agent ihn bauen

Öffne den Ordner in Claude Code (oder deinem Agent der Wahl) und gib ihm einen kurzen Prompt:

Read CLAUDE.md and build the MCP server it describes. Use TypeScript with
the official MCP SDK and stdio transport. When you're done, run the build
and show me the claude_desktop_config.json entry I need.

Der Agent legt das Projekt an, implementiert den Klar-API-Client (mit Auth-Header, Shop-Auflösung, Rate-Limiting und Cursor-Pagination), definiert die Tools und kompiliert den Server. Prüfe den generierten Code und baue ihn dann.

Schritt 3 – Mit Claude Desktop verbinden

Öffne deine Claude-Desktop-Konfigurationsdatei und füge den Server unter mcpServers hinzu. Auf macOS liegt sie unter ~/Library/Application Support/Claude/claude_desktop_config.json, auf Windows unter %APPDATA%\Claude\claude_desktop_config.json.

{
"mcpServers": {
"klar-public-api": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/build/index.js"],
"env": {
"KLAR_API_KEY": "klar_pk_your-64-character-key"
}
}
}
}

Nutze den absoluten Pfad zur kompilierten build/index.js, füge deinen Public API Key ein, speichere die Datei und starte Claude Desktop neu. Die Klar-Tools erscheinen, sobald der Server sauber startet.

Wenn du stattdessen Claude Code nutzt, kannst du denselben Server über die Kommandozeile mit claude mcp add registrieren.

Schritt 4 – Leg los mit Fragen

Sobald verbunden, probiere Prompts wie:

  • „Welche Shops kann dieser Key sehen?"

  • „Zeig mir die Attribution der letzten Woche nach Kanal unter datengetriebener Attribution."

  • „Vergleiche den Neukunden-Nettoumsatz im März mit dem Februar."

  • „Schlüssele die Marketingausgaben für Q1 nach Kanal und Land auf."

  • „Wie sind Umsatz und Gewinn pro Produkt der letzten 30 Tage, mit Bundles aufgeteilt in ihre Komponenten?"

Claude ruft deinen Server auf, holt die Daten aus Klar und antwortet.

Gut zu wissen

  • API-Keys sind nach Permission beschränkt. Ein Key ohne alle drei Permissions bekommt auf den nicht freigegebenen Endpunkten ein 403 – das ist erwartet, kein Bug.

  • Cursor-Tokens laufen nach 10 Minuten ab. Der Server startet den Report automatisch neu, wenn das Paging länger dauert – du musst nichts tun.

  • Das Rate-Limit liegt bei 5 Requests pro 30 Sekunden pro Key – geteilt über Shops, jeden Report-Start und jede Ergebnisseite. Große Zeiträume oder breite Dimensions-Aufschlüsselungen dauern deshalb etwas länger.

  • Zeiträume sind an beiden Enden inklusive. Das ist eine Änderung gegenüber der bisherigen reinen Attributions-API, bei der endDate exklusiv war.

  • Attribuierte Bestellungen sind oft Bruchzahlen. Attributionsmodelle teilen die Gutschrift für eine Bestellung auf die Touchpoints auf, die zu ihr geführt haben – ein Kanal kann also z.B. 2,13 Bestellungen anzeigen.

  • Halte deinen API-Key geheim. Er ist an deinen Account und seine erteilten Permissions gebunden; committe ihn nie in ein Repository und teile ihn nicht.

Hat dies deine Frage beantwortet?