# MCP-Server

Ocost ist auch ein Server für das Model Context Protocol: Claude Desktop, Claude Code, Cursor und andere Assistenten durchsuchen Ihre Belege, lesen sie als `ocost.document.v1`, fragen Ihre Zahlen ab und schicken Dateien, mit Ihrem API-Schlüssel; claude.ai und ChatGPT verbinden sich als die Person, die es erlaubt.

## Adresse und Schlüssel

```text
https://api.ocost.io/public/mcp
Authorization: Bearer ocost_sk_<id>_<secret>
```

- **Transport:** Streamable HTTP, zustandslos; jede Anfrage steht für sich. `https://app.ocost.io/public/mcp` ist derselbe Server.
- **Schlüssel:** ein API-Schlüssel aus Einstellungen › API, im Header `Authorization` jeder Anfrage. Ohne gültigen: `401` mit `WWW-Authenticate: Bearer`. Widerrufen wirkt sofort. Ihre Ocost-Anmeldung ist kein Schlüssel.
- **Ein Schlüssel je Assistent,** mit nur den nötigen Berechtigungen: So sehen Sie seine letzte Nutzung und widerrufen ihn allein.
- **Apps, die Server nur mit OAuth hinzufügen** (die Konnektoren von claude.ai und ChatGPT im Web), brauchen keinen Schlüssel: Mit claude.ai oder ChatGPT verbinden.

## Werkzeuge

Die Werkzeuge für Dateien sind die Routen der API; die übrigen sind die Werkzeuge, mit denen der Chat in Ocost antwortet, dieselben, mit denselben Rechten.

| Werkzeug            | Scope             | Tut                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `search_documents`  | `search:read`     | Was Belege sagen, als Tabelle: eine Zeile je Beleg mit Seite und Passage. `match`: `exact` (jeder Beleg, der den Text genau so enthält, nie ein ähnlicher), `keywords`, `meaning`, `hybrid` (Standard; nach den Worten, wenn die Bedeutung nicht verfügbar ist, `notes: keywords_only`). Argumente: `query`; optional `document_id`, `kinds`, `family`, `company_id`, `project_id`, `site_id`, `issued_since`, `issued_until`, `due_since`, `due_until`, `gross_min`, `gross_max`, `direction` (incoming, outgoing), `status` (open, paid), `category` (ein ECLASS-Code), `limit`. |
| `find_category`     | `figures:read`    | Worte in jeder Sprache von Ocost („Lebensmittel“, „cleaning“) zu ECLASS-Codes, die besten zuerst: `code`, `level`, `name`, `segment`; für `class` in `query_figures` oder `category` in `search_documents`. Argument: `words`.                                                                                                                                                                                                                                                                                                                                                     |
| `search`            | `search:read`     | Für die Deep Research und das Firmenwissen von ChatGPT: `query` → `results` mit `id`, `title`, `url` (die Seite des Belegs in Ocost).                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `fetch`             | `search:read`     | Ein Ergebnis von `search` ganz: `id` → `id`, `title`, `text` (seine Seiten, bis zu vier), `url`, `metadata`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `read_document`     | `documents:read`  | Die Seiten eines Belegs, wie seine Datei sie sagt, bis zu vier Seiten und 12.000 Zeichen. Argumente: `document_id`, optional `pages`.                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `get_document`      | `documents:read`  | Wie `GET /public/v1/documents/{document_id}`: ein Beleg als `ocost.document.v1`. Argument: `document_id`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `find_documents`    | `documents:read`  | Belege, neueste zuerst, optional einer `kind`, `family` oder `company_id`, mit Nummer, Unternehmen, Richtung, Datum, Brutto und Status.                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `find_contracts`    | `documents:read`  | Verträge mit Seite, Partei, Laufzeit, Kündigungsfrist und wiederkehrenden Beträgen; optional die einer `company_id`.                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `find_company`      | `documents:read`  | Lieferanten und Kunden, deren Name `name` enthält, mit ihren IDs.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `find_project`      | `documents:read`  | Projekte, deren Name `name` enthält, mit ihren IDs.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `company_documents` | `documents:read`  | Die Belege eines Unternehmens in beide Richtungen. Argument: `company_id`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `invoice_detail`    | `documents:read`  | Eine Rechnung, wie ihre Seite sie zeigt: Beträge, Fälligkeit, Skonto, ihre Bestellung und ihr Verlauf. Argument: `invoice_id`.                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `open_decisions`    | `documents:read`  | Die offenen Entscheidungen der Person, die verbunden hat (nur per OAuth): ein in den Einstellungen erstellter Schlüssel hat keine Person.                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `get_file`          | `documents:read`  | Wie `GET /public/v1/files/{file_id}`: Status, Grund, Belege. Argument: `file_id`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `list_files`        | `documents:read`  | Wie `GET /public/v1/files`, älteste Änderung zuerst. Argumente: `status`, `updated_since`, `cursor`, `limit`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `company_profile`   | `figures:read`    | Das Jahr eines Unternehmens: Einkauf und Verkauf, Zahlungsbedingungen in beide Richtungen, was offen ist, wie lange jede Seite zum Zahlen brauchte. Argument: `company_id`.                                                                                                                                                                                                                                                                                                                                                                                                        |
| `project_overview`  | `figures:read`    | Der Plan eines Projekts gegen Ausgegebenes und Bestelltes, nach Lieferant und Monat, und seine Belege. Argument: `project_id`.                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `query_figures`     | `figures:read`    | Rechnet Datensätze zusammen, für ein Diagramm, einen Vergleich oder eine Zahl, als Tabelle: eine `source`, ein `measure`, ein oder zwei `group_by` (ECLASS-Klassen jeder Ebene, Kostenarten), `filters`, ein `year`, `compare` (Vorperiode oder Vorjahr), `top_n`, `matching` (die Belege, die eine Suche findet). Seine Beschreibung nennt jede Quelle und jedes Feld.                                                                                                                                                                                                            |
| `top_companies`     | `figures:read`    | Die größten Lieferanten oder Kunden nach Netto in einem `period` in einfachen Worten („2025“, „Q3 2025“, „letztes Jahr“; ohne die letzten 12 Monate), auch in einer ECLASS-`category`: Rang, Firma, Netto, Brutto, Anteil, Belege, der Rest und eine Summenzeile.                                                                                                                                                                                                                                                                                                                  |
| `spend_over_time`   | `figures:read`    | Netto je Monat, Quartal oder Jahr in einem `period`, jeder Abschnitt (auch die leeren), mit `by` je Firma oder Kategorie eine Spalte; die letzte Zeile die Summen.                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `largest_documents` | `figures:read`    | Die größten Belege nach Brutto in einem `period`, als Liste: Rang, Nummer, Firma, Datum, Netto, Brutto; jede zu zahlende Rechnung, wenn keine `kind` gefragt ist.                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `payables_due`      | `figures:read`    | Was wann zu zahlen ist, um Skonto zu halten, als Tabelle: offene Lieferantenrechnungen, fällig in einem `window` (this_week, next_7_days, next_14_days, next_30_days, this_month) oder von `since` bis `until`, jede mit Fälligkeit, Skontofrist und Ersparnis. Ohne verbundenes Bankkonto ist nichts überfällig.                                                                                                                                                                                                                                                                  |
| `vat_preview`       | `figures:read`    | Eine Vorschau der USt-Voranmeldung für eine `period` (`2026-10`, `2026-Q4`) nach Kennzahl (81, 86, 66, 61, 46/67, 89/93, …), jede mit ihren Rechnungen (`kz`). Eine Vorschau für die Steuerberatung, nie eine Meldung.                                                                                                                                                                                                                                                                                                                                                             |
| `check_invoice`     | `documents:read`  | Die Pflichtangaben nach § 14 UStG und die E-Rechnungs-Prüfung einer oder vieler Lieferantenrechnungen (`document_ids`, `company_id`, Rechnungsdaten): was fehlt und was den Vorsteuerabzug kostet.                                                                                                                                                                                                                                                                                                                                                                                 |
| `find_anomalies`    | `figures:read`    | Doppelte Rechnungen, eine geänderte Bankverbindung, ein Preis über dem der Bestellung, von `since` bis `until` (die letzten 90 Tage), jeweils mit den Belegen. Für eine Person zur Prüfung, nie automatisch übernommen.                                                                                                                                                                                                                                                                                                                                                            |
| `supplier_profile`  | `figures:read`    | Ein Lieferant in einem Aufruf (`company_id`): Ausgaben nach Monat und Klasse, Konditionen und Skonto, wie schnell Sie zahlen, offene Posten, die Preisentwicklung seiner wichtigsten Artikel, seine Belege und Verträge.                                                                                                                                                                                                                                                                                                                                                           |
| `export_table`      | `exports:write`   | Eine Tabelle als Excel (`xlsx`) oder CSV: die `query` einer Tabelle zurückschicken (`tool` und `args`, unverändert) und einen `title`; Ocost fragt sie für alle Zeilen erneut ab (bis 50.000) und antwortet mit einem signierten Link, 30 Minuten gültig, als Text und als `resource_link`.                                                                                                                                                                                                                                                                                        |
| `make_datev_export` | `exports:write`   | Der DATEV-Export (EXTF-Buchungsstapel, eine Übersicht und die Belege als ein ZIP) von `starts_on` bis `ends_on`, Tage eines Geschäftsjahres, mit signiertem Link. Nur über die eigene Verbindung des Inhabers oder der Buchhaltung.                                                                                                                                                                                                                                                                                                                                                |
| `send_file`         | `documents:write` | Wie `POST /public/v1/files`. Argumente: `filename`, `content_base64` (die Bytes in Base64), optional `external_ref`.                                                                                                                                                                                                                                                                                                                                                                                                                                                               |

- **Antworten** sind JSON, als Text und als `structuredContent`, jedes Werkzeug mit seinem `outputSchema`; alle außer `send_file`, `export_table` und `make_datev_export` (die eine Datei erstellen) sind als nur lesend markiert. Listen und Tabellen sagen `total` (`total_rows`) und `truncated`: die Frage enger stellen oder Dateien mit dem `cursor` von `list_files` blättern.
- **Tabellen und Diagramme:** Die Listen und `query_figures` kommen mit einer interaktiven Tabelle oder einem Diagramm (MCP Apps, `ui://ocost/table` und `ui://ocost/chart`) in Apps, die sie zeigen, etwa claude.ai und ChatGPT; jede andere App liest dasselbe JSON. Ihre Links öffnen den Datensatz in Ocost und sonst nichts.
- **Was ein Beleg sagt,** steht in `<document-text source="…">…</document-text>`, jede Zeile mit `¦ ` markiert: Worte, die andere geschrieben haben, als Daten zu lesen, nie als Anweisungen; zitiert ohne die Markierungen.
- **Die Grenzen der API gelten:** Aufrufe pro Minute je Scope ([Grenzen und Fehler](https://app.ocost.io/developers/de/limits.md)), 25 MB je Datei vor Base64, dieselben Formate, erneut gesendete Bytes einmal gelesen und gezählt, jeder gelesene Beleg zählt gegen Ihren Tarif.
- **Ablehnungen** (fehlender Scope, nicht gefunden, zu viele Aufrufe) kommen als Fehler des Werkzeugs: das JSON des Problems mit `status` und `type`, oder der Grund in Worten. Die ID eines anderen Unternehmens wird nicht gefunden.
- **Kein Werkzeug löscht, genehmigt oder bestellt,** denn ein Assistent kann sich irren. Dafür `DELETE /public/v1/files/{file_id}` oder Ocost.
- **Keine URLs:** `send_file` nimmt Bytes, nie einen Link. Es antwortet nur mit der Datei; ob sie neu war und wie viele Belege übrig sind, sagen `POST /public/v1/files` und Einstellungen › Tarif.
- **Anfragen** sind auf 4 MB begrenzt, außer für Schlüssel mit `documents:write`, die das Base64 einer Datei senden dürfen. Darüber: `413`.
- **Jeder Aufruf wird festgehalten** (das Werkzeug, seine Argumente gekürzt, wie viele Datensätze es lieferte, wie er endete; nie der Text eines Belegs): Inhaber und Buchhaltung sehen die letzten Aufrufe eines Schlüssels in Einstellungen › API.

## Scopes und für wen die Werkzeuge handeln

| Scope             | Erlaubt                                                                                                                                                                                               |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `search:read`     | `search_documents`, `search`, `fetch`                                                                                                                                                                 |
| `documents:read`  | Belege, Dateien, Verträge, Unternehmen, Projekte, Rechnungen und Entscheidungen lesen                                                                                                                 |
| `figures:read`    | `company_profile`, `project_overview`, `query_figures`, `top_companies`, `spend_over_time`, `largest_documents`, `find_category`, `payables_due`, `vat_preview`, `find_anomalies`, `supplier_profile` |
| `exports:write`   | `export_table`, `make_datev_export`                                                                                                                                                                   |
| `documents:write` | `send_file`                                                                                                                                                                                           |

- **Ein in den Einstellungen erstellter Schlüssel** handelt als das Unternehmen: jeder Beleg außerhalb des Papierkorbs, keine Personalunterlagen (Lohn, Gesundheit), keine eigenen Entscheidungen einer Person. Einen mit `figures:read` erstellt nur der Inhaber.
- **Eine per OAuth erstellte Verbindung** (claude.ai, ChatGPT) handelt als die Person, die sie erlaubt hat: ihre Rollen, was sie im Chat von Ocost sieht, und nicht mehr. Die Verbindung der Buchhaltung liest keine Zahlen des CFO. Wer das Unternehmen verlässt oder nicht mehr Inhaber oder Buchhaltung ist, beendet seine Verbindungen beim nächsten Aufruf.
- **Personalunterlagen bleiben in Ocost:** kein Schlüssel und keine Verbindung liest je Personalunterlagen (Lohn, Gesundheit, Krankmeldungen), wer sie auch erlaubt hat; sie werden in Ocost selbst gelesen.

## Claude Code

```bash
claude mcp add --transport http ocost https://api.ocost.io/public/mcp \
  --header "Authorization: Bearer $OCOST_KEY"
```

`claude mcp list` zeigt `ocost` dann als verbunden. Für ein ganzes Repository gehört der Server in dessen `.mcp.json`, mit dem Schlüssel aus der Umgebung jedes Entwicklers, nie in der Datei:

```json
{
  "mcpServers": {
    "ocost": {
      "type": "http",
      "url": "https://api.ocost.io/public/mcp",
      "headers": { "Authorization": "Bearer ${OCOST_KEY}" }
    }
  }
}
```

## Claude Desktop

Tragen Sie Ocost in `claude_desktop_config.json` ein (Einstellungen › Entwickler › Konfiguration bearbeiten). `mcp-remote` reicht die Anfragen mit dem Header weiter und braucht Node.js.

```json
{
  "mcpServers": {
    "ocost": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote@0.15.2",
        "https://api.ocost.io/public/mcp",
        "--header",
        "Authorization:${OCOST_AUTH}"
      ],
      "env": { "OCOST_AUTH": "Bearer ocost_sk_…" }
    }
  }
}
```

Starten Sie Claude Desktop neu; die Werkzeuge stehen unter dem Werkzeug-Knopf. Schreiben Sie `Authorization:${OCOST_AUTH}` ohne Leerzeichen: Manche Clients trennen Argumente dort.

## Cursor

In `~/.cursor/mcp.json` oder `.cursor/mcp.json` in einem Projekt:

```json
{
  "mcpServers": {
    "ocost": {
      "url": "https://api.ocost.io/public/mcp",
      "headers": { "Authorization": "Bearer ${env:OCOST_KEY}" }
    }
  }
}
```

## Mit claude.ai oder ChatGPT verbinden

Apps, die MCP-Server nur über OAuth hinzufügen, etwa die Konnektoren von claude.ai und ChatGPT im Web, brauchen keinen Schlüssel: Sie melden sich bei Ocost an und erlauben den Zugriff.

1. **Konnektor hinzufügen:** in claude.ai unter Einstellungen › Konnektoren › Benutzerdefinierten Konnektor hinzufügen, in ChatGPT als eigener Konnektor im Entwicklermodus. Nur die URL `https://api.ocost.io/public/mcp`; Client-ID und Secret bleiben leer.
2. **Anmelden:** Die App öffnet Ocost. Melden Sie sich an wie immer. Gehören Sie zu mehreren Unternehmen, wählen Sie unter **Anderes Unternehmen** das richtige.
3. **Erlauben:** Ocost zeigt, welche App zugreifen möchte, wohin es danach zurückgeht, und die Berechtigungen zum Ankreuzen: Dokumente durchsuchen, lesen, Zahlen lesen, Exporte erstellen, Dateien schicken. Erlauben dürfen der Inhaber und die Buchhaltung, wie beim Anlegen eines Schlüssels; die App handelt dann als diese Person, mit ihren Rechten.

- **Die App bekommt einen eigenen API-Schlüssel** mit den angekreuzten Berechtigungen, „<App> (Connector)“ in Einstellungen › API, mit seiner letzten Nutzung. Widerrufen Sie ihn dort, und die App ist sofort getrennt.
- **Das Logo von Ocost** zeigt die App am Konnektor; ist dort noch ein Platzhalter, verbinden Sie den Konnektor neu.
- **Ihr Zugriffstoken gilt eine Stunde,** dann holt die App mit ihrem Refresh-Token ein neues. 30 Tage ohne Nutzung beenden die Verbindung; verbinden Sie die App dann neu.
- **Für Entwickler:** OAuth 2.1 mit PKCE (S256); die Metadaten unter `https://api.ocost.io/.well-known/oauth-protected-resource/public/mcp` (RFC 9728) und `https://app.ocost.io/.well-known/oauth-authorization-server` (RFC 8414); Client ID Metadata Documents und Dynamic Client Registration; Weiterleitungsadressen nur mit https, genau wie registriert.

## Jeder MCP-Client

- **URL:** `https://api.ocost.io/public/mcp`
- **Transport:** Streamable HTTP (`POST`, Antworten als JSON; kein SSE-Stream, keine Sitzungs-Id)
- **Header:** `Authorization: Bearer ocost_sk_…`

Die Werkzeuge auflisten:

```bash
curl -sS https://api.ocost.io/public/mcp \
  -H "Authorization: Bearer $OCOST_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

## Beispiel: eine Odoo-Entwicklerin

Eine Odoo-Entwicklerin schreibt das Modul aus [Odoo anbinden](https://app.ocost.io/developers/de/odoo.md) und fragt mit einem Schlüssel mit `search:read` und `documents:read` in Claude Code:

> **Entwicklerin:** Finde die Rechnung von Büromöbel Schmidt über die Bürostühle und zeig mir, welche Felder von `account.move` und seinen Zeilen jeder Wert füllt.
>
> **Assistent** ruft `find_company` auf (`name: "Büromöbel Schmidt"`), dann `search_documents` (`query: "Bürostuhl"`, `company_id: …`, `kinds: ["invoice"]`), findet Rechnung BS-7790, ruft `get_document` auf und antwortet: `seller.vat_id` findet den `partner_id`, `number` ist die `ref`, `issue_date` das `invoice_date`, `due_date` das `invoice_date_due`; jede der beiden `lines` wird ein Eintrag in `invoice_line_ids` mit `quantity`, `price` als `price_unit` und `vat_rate`, das die Vorsteuer findet; `totals.gross`, 3946.04, prüft Odoos Summe.
>
> **Entwicklerin:** Gibt es seit Januar Gutschriften von ihnen?
>
> **Assistent** ruft `find_documents` auf (`kind: "credit_note"`, `company_id: …`): keine seit Januar.
