# MCP-Anbindung

Accessful betreibt einen MCP-Server, mit dem deine KI-Anwendung PDFs verarbeitet, den Fortschritt abfragt und das Ergebnis abruft.
Du brauchst einen Accessful-API-Key und einen Client, der **entfernte Server über Streamable HTTP und eigene HTTP-Header** unterstützt.

## Verbindung einrichten

1. Öffne im Accessful-Portal **API-Keys** und erstelle einen Key für diese Integration.
   Kopiere ihn direkt; er wird nur einmal angezeigt. Siehe [API-Key erstellen](https://docs.accessful.de/de/authentication/#api-key-erstellen).
2. Füge in deinem Client einen entfernten MCP-Server namens **Accessful** hinzu und trage diese Werte ein:

   | Einstellung | Wert |
   | --- | --- |
   | Server-URL | `https://api.accessful.de/mcp` |
   | Transport | Streamable HTTP |
   | Header-Name | `X-API-Key` |
   | Header-Wert | Dein persönlicher Accessful-API-Key |

3. Hinterlege den Key im Geheimnisspeicher des Clients, speichere und verbinde dich. Der Header muss bei **jeder Anfrage** gesendet werden.
   Nutze pro Nutzer und Integration einen separaten, widerrufbaren Key. **OAuth-Login ist in V1 nicht verfügbar**.

Binde eigene Webanwendungen über ihr Backend an. Direkte Browseranfragen von einer anderen Origin werden mit `403` abgewiesen.

## Verbindung prüfen

Aktualisiere die Werkzeugliste im Client. Diese **vier Werkzeuge** müssen erscheinen:

| Werkzeug | Eingabe | Ergebnis |
| --- | --- | --- |
| `accessful_start_pdf` | `url`, `filename`, `requestId` | Startet einen Auftrag über einen Download-Link; liefert `caseId`, `pollAfterSeconds` |
| `accessful_start_pdf_file` | `file` (vom Host befüllt) | Startet einen Auftrag über einen Chat-Anhang; liefert `caseId`, `pollAfterSeconds` |
| `accessful_get_status` | `caseId` | Liefert `jobStatus`, `stage`, `terminal` und verfügbare Fortschrittsangaben |
| `accessful_get_result` | `caseId` | Liefert `downloadUrl`, `expiresAt`, `filename`, `mimeType` |

<details>
<summary>Optional: mit MCP Inspector prüfen</summary>

Mit Node.js ab 22.19 und deinem Key in der Umgebungsvariable `ACCESSFUL_API_KEY`
kannst du diesen Befehl in einem Bash- oder Zsh-Terminal ausführen. Er listet Werkzeuge auf, ohne einen PDF-Auftrag anzulegen:

```bash
npx @modelcontextprotocol/inspector --cli https://api.accessful.de/mcp \
  --transport http --header "X-API-Key: ${ACCESSFUL_API_KEY:?Set ACCESSFUL_API_KEY}" \
  --method tools/list
```

Die Ausgabe muss die vier Namen oben enthalten. Siehe [MCP-Inspector-Dokumentation](https://modelcontextprotocol.io/docs/tools/inspector).

</details>

## PDF verarbeiten

Starte einen Auftrag mit **einem der beiden Start-Werkzeuge**, frage dann den Status ab und lade das Ergebnis herunter.
Maximal **50 MiB**, ein PDF pro Auftrag.

### Option A: Chat-Anhang

Manche MCP-Hosts geben Chat-Anhänge direkt an Werkzeuge weiter, die einen Dateiparameter in `_meta["openai/fileParams"]` deklarieren.
`accessful_start_pdf_file` deklariert seinen Parameter `file` auf diese Weise. Unterstützt dein Host das, hängt der Nutzer das PDF an
und der Host befüllt `file` mit einem temporären Download-Link; das Modell muss also keine URL zusammensetzen:

```json
{
  "file": {
    "download_url": "https://files.example.com/temporary/abc123",
    "file_id": "file_abc123",
    "mime_type": "application/pdf",
    "file_name": "Jahresbericht.pdf"
  }
}
```

Die `download_url` des Hosts muss für Accessful öffentlich per HTTPS ohne Login oder zusätzliche Header erreichbar sein
und gültig bleiben, bis Accessful die Datei heruntergeladen hat. Der Download beginnt kurz nach der Annahme des Auftrags.
Eine `requestId` ist nicht nötig: Ein identischer Aufruf liefert dieselbe `caseId`, ein erneuerter Link für dieselbe Datei startet einen neuen Auftrag.

Hosts ohne diese Dateiunterstützung zeigen `file` als gewöhnliches Objekt an. Nutze dort Option B; lass das Modell keine `download_url` erfinden.

### Option B: Download-Link

Du brauchst einen **direkten HTTPS-Download-Link zum Original-PDF**, den Accessful ohne Login oder zusätzliche Header abrufen kann.
Ein vorhandener öffentlicher PDF-Link genügt. Für private Dateien muss deine Anwendung einen signierten Download-Link erzeugen.
Ein lokaler Dateipfad allein reicht nicht; ohne Dateiunterstützung des Hosts muss die Anwendung die Datei per URL bereitstellen.
Der Link muss noch mindestens **15 Minuten** gültig sein.

Rufe `accessful_start_pdf` mit diesen Argumenten auf. Ersetze die Beispiel-URL durch deinen PDF-Link
und erzeuge für jeden neuen Auftrag eine neue UUID als `requestId`:

```json
{
  "url": "https://files.example.com/document.pdf",
  "filename": "document.pdf",
  "requestId": "96d35d20-7f2a-4de2-9a85-0e7d03cc68a4"
}
```

Behalte bei Wiederholungen **dieselbe `requestId`, URL und denselben Dateinamen** bei, auch nach einem API-Key-Wechsel.

### Status abfragen und Ergebnis herunterladen

1. Speichere die `caseId`, die das Start-Werkzeug zurückgibt. Rufe damit `accessful_get_status` alle **10 Sekunden** auf, bis `terminal` den Wert `true` hat.
   Zeige nach **120 Sekunden** aktivem Warten den Zwischenstand und die `caseId`; die Verarbeitung läuft weiter, frage später erneut ab.
2. Sobald `jobStatus` den Wert `completed` hat, rufe `accessful_get_result` mit derselben `caseId` auf und zeige `downloadUrl` als klickbaren Link.
   Er funktioniert **fünf Minuten** ohne API-Key. Fordere nach Ablauf durch einen erneuten Werkzeugaufruf einen frischen Link an.

Wiederholungen und zeitgesteuerte Statusabfragen, ohne Dateiunterstützung des Hosts auch die Dateibereitstellung, muss die Anwendung übernehmen; das Hinzufügen des MCP-Servers richtet diesen Ablauf nicht ein.
Keys, signierte URLs und PDF-Inhalte gehören nicht in Logs oder Prompts. Zeige den Ergebnislink nur dem vorgesehenen Nutzer.

## Grenzen und häufige Fehler

| Problem | Lösung |
| --- | --- |
| `401` oder `AUTHENTICATION_REQUIRED` | Prüfe, ob `X-API-Key` bei jeder Anfrage gesendet wird und der Key gültig ist. |
| `403` beim Verbinden aus dem Browser | Verbinde über dein Backend; Anfragen von anderen Origins werden abgewiesen. |
| `RESULT_NOT_READY` | Frage den Status ab. Fordere das Ergebnis erst bei `completed` an. |
| `IDEMPOTENCY_CONFLICT` | Verwende mit dieser `requestId` die ursprüngliche URL und den ursprünglichen Dateinamen. Auch eine erneuerte signierte Quell-URL verändert die Eingabe. |
| `UNSUPPORTED_FILE_TYPE` | Die angehängte Datei ist kein PDF. Hänge das Original-PDF an. |
| `failed` mit Fehler `DOWNLOAD_FAILED` | Accessful konnte die Quelle nicht herunterladen. Prüfe, ob der Link öffentlich per HTTPS erreichbar und noch gültig ist, und starte einen neuen Auftrag; bei einem Chat-Anhang hänge die Datei erneut an. |
| Timeout | Der Auftrag kann bereits existieren. Wiederhole den Start mit unveränderten Eingaben: derselben `requestId` oder demselben `file`-Objekt. |
| Ergebnislink abgelaufen | Rufe `accessful_get_result` erneut auf. |
| `quota_exceeded` | Die Verarbeitung wurde wegen ausgeschöpften Kontingents beendet. Siehe [Limits & Aufbewahrung](https://docs.accessful.de/de/limits/). |

<details>
<summary>Technische Referenz</summary>

- `filename`: höchstens 255 Zeichen, passend zu `^[A-Za-z0-9_-]+\.pdf$`, zum Beispiel `document.pdf`.
- `url`: höchstens 8192 Zeichen, ohne eingebettete Zugangsdaten oder Fragmente. Interne Netzwerkziele werden abgewiesen;
  für den Quelldownload gelten maximal **120 Sekunden**. Siehe [Per URL hochladen](https://docs.accessful.de/de/reference/upload-by-url/).
- `file`: Objekt mit den Pflicht-Strings `download_url` und `file_id` sowie den optionalen Strings `mime_type` und `file_name`;
  weitere Eigenschaften werden abgewiesen. Für `download_url` gelten dieselben Regeln wie für `url`. `file_name` wird in einen gültigen `filename`
  umgewandelt (Ersatzwert `document.pdf`); ein anderer `mime_type` als `application/pdf` liefert `UNSUPPORTED_FILE_TYPE`.
- `caseId` und `requestId` sind UUIDs. Ungültige Argumente liefern `INVALID_ARGUMENT`.
- Endzustände: `completed`, `failed`, `analyzer_failed`, `canceled`, `quota_exceeded`.
  `queued`, `running` und `quota_pending` laufen weiter. Nur `completed` erlaubt den Ergebnisdownload.
- Der Status kann `queuePosition`, `queueTotal`, `estimatedWaitSeconds` und `error` enthalten; `score` erscheint nur bei `completed`.
  Fehlende Warteschlangenfelder bedeuten keinen Endzustand.
- Erfolgreiche Antworten enthalten identisches JSON in `structuredContent` und einem Textblock. Ergebnisse enthalten zusätzlich einen PDF-`resource_link`.
  Download-Links gewähren Zugriff auf eine Ergebnisversion und sind vertraulich.
- Werkzeugfehler enthalten `isError: true` und `{code, message, retryable}`. HTTP-Fehler beim Verbindungsaufbau können einen leeren Body haben;
  erwarte hier nicht das Problem-Details-Format der REST-API.

</details>