Scout Public API v1 & MCP

Volltextsuche, Originaldokumente mit PDF-Seitenbelegen und RAG (semantische Suche & Frage/Antwort mit Quellen) über amtliche Gemeindemitteilungen — stabil, versioniert, synchrones JSON.

Schnellstart

Sie benötigen einen API-Key (wird von Scout-Admins erstellt, Format scout_…). Der Key wird bei der Erstellung genau einmal angezeigt.

export SCOUT_API_KEY="scout_xxxxxxxxxxxxxxxx"

curl -s "https://hugoscout.mad.stg.sternwald.cloud/v1/search?q=Baubewilligung&limit=3" \
  -H "Authorization: Bearer $SCOUT_API_KEY"

Alle Endpunkte antworten synchron mit JSON (UTF-8). Es gibt kein Streaming — die Antwort kommt vollständig, sobald sie fertig ist.

Authentifizierung

Jede REST-Anfrage an /v1/… braucht den Header Authorization: Bearer <key>. Ohne gültigen Key antwortet die API mit 401, mit deaktiviertem Key mit 403. Für KI-Assistenten gibt es zusätzlich den MCP-Zugang mit Bearer oder OAuth.

Behandeln Sie den Key wie ein Passwort: nicht in Client-Code oder Repositories einbetten, serverseitig speichern. Bei Verlust kann ein Admin den Key deaktivieren und einen neuen erstellen.

MCP-Zugang für KI-Assistenten

Über das Model Context Protocol (MCP) können Open WebUI, ChatGPT und Claude Scout direkt als Recherchewerkzeug nutzen. Voraussetzung ist ein Scout-API-Key (scout_…), erstellt unter Verwaltung → API-Schlüssel.

Server-URL

https://hugoscout.mad.stg.sternwald.cloud/mcp

Verbindungstyp: MCP / Streamable HTTP. Der Endpunkt liegt unter /mcp, nicht unter /v1/mcp. Er liefert synchrone JSON-Antworten und benötigt keine Server-Session. Die OpenAPI-Dateien beschreiben die REST-API, nicht das MCP-Protokoll.

Open WebUI: direkter Bearer-Zugang

  1. Unter Settings → Admin → Integrations → External Tool Servers eine Verbindung vom Typ MCP (Streamable HTTP) anlegen, nicht OpenAPI. Native MCP-Unterstützung setzt mindestens Open WebUI v0.6.31 voraus; Bezeichnungen können je nach Version abweichen.
  2. Die obige Server-URL eintragen und Authentifizierung Bearer wählen.
  3. Im Key-Feld den vollständigen Scout-API-Key eintragen, ohne zusätzliches Bearer davor. Keinen Open-WebUI-API-Key verwenden.
  4. Speichern, die Verbindung im Chat aktivieren und list_municipalities aufrufen.
Der direkte Bearer-Zugang benötigt keine OAuth-Freigabeseite. Erscheint trotzdem eine solche Seite, Auth-Modus und URL prüfen. Ein gemeinsam hinterlegter Key bedeutet gemeinsame Scout-Berechtigungen und Limits; Zugriff nur für vorgesehene Nutzergruppen freigeben.

OAuth: ChatGPT, Claude und alternativ Open WebUI

  1. Eine Remote-MCP-Verbindung mit obiger URL und OAuth einrichten, sofern das Konto bzw. der Workspace diese Funktion freigibt.
  2. Scout unterstützt dynamische Client-Registrierung und PKCE (S256). In Open WebUI OAuth 2.1 wählen, nicht „OAuth 2.1 (Static)“; Resource auf Automatic, Scope auf scout setzen. Es wird kein Client-Secret benötigt – den API-Key nicht als Client-Secret eintragen.
  3. Auf der Scout-Freigabeseite einmalig den Scout-API-Key eingeben und den Rücksprung im gleichen Browser und Client-Konto abschließen. Der Assistent erhält ein an den Key gebundenes OAuth-Token.
  4. Danach list_municipalities ausführen. Erfolgreiche Discovery allein bestätigt noch keine funktionierende Verbindung.

Die Oberfläche und Callback-URL des jeweiligen Clients sind maßgeblich. Open-WebUI-Betreiber benötigen eine korrekte öffentliche HTTPS-Adresse (WEBUI_URL) und einen stabilen WEBUI_SECRET_KEY für gespeicherte OAuth-Tokens.

Discovery: /.well-known/oauth-authorization-server und /.well-known/oauth-protected-resource. Registrierung, Freigabe und Token-Austausch liegen unter /mcp/oauth/register, /mcp/oauth/authorize und /mcp/oauth/token.

Verfügbare Tools

ToolAufgabe und Parameter
list_municipalitiesVerfügbare Gemeinden auflisten; optional name_filter.
find_documentsDokumente semantisch und nach Stichworten in einem begrenzten Ranking-Fenster suchen. Pflicht: query. Optional: municipality, date_from, date_to, only_with_pdf, limit (max. 25), offset, fields.
get_documentGespeicherten Artikeloriginaltext oder PDF-Text mit echten Seitenbelegen lesen. Pflicht: municipality_id, article_id; optional document_id, page, text_offset, max_chars, documents_offset. Siehe Dokumentabruf.
ask_scoutAntwort mit Quellen erstellen. Pflicht: question. Optional: municipality, date_from, date_to. Typisch 10–60 Sekunden; mindestens 120 Sekunden Client-Timeout vorsehen.

Gemeinden als Klarnamen angeben, z. B. Neuruppin; Datumswerte als YYYY-MM-DD. Die vollständigen Parameterschemata liefert tools/list. Ergebnisse enthalten Text und bei strukturierten Ergebnissen zusätzlich structuredContent.

Für Originalbelege find_documents mit fields: ["title", "municipality_id", "article_id", "url", "attachments"] aufrufen und die IDs sowie gegebenenfalls attachments.pdfs[].document_id an get_document übergeben. Nach Veröffentlichung eines neuen Tools die gespeicherte Tool-Liste im Client aktualisieren/neu scannen; bei Workspace-Apps kann eine erneute Freigabe nötig sein.

Der fields-Default ist title, municipality, date, summary, url, scout_url, article_id; für get_document ausdrücklich municipality_id, article_id und attachments anfordern. find_documents liefert result_window mit semantic_limit (max. 50), keyword_limit (max. 100), corpus_exhaustive: false und scope: "ranked_candidates". has_more beschreibt nur weitere Treffer innerhalb dieses Ranking-Fensters, keine Vollständigkeit des Korpus; total zählt nur die aktuelle Trefferseite.

Gemeindenamen und „In Scout öffnen“

Die MCP-Textausgabe von find_documents, ask_scout und get_document enthält Quellen bereits lesbar als Gemeindename · In Scout öffnen mit klickbarem Scout-Link. Technische IDs und Navigationsdaten bleiben unverändert in structuredContent für Folgeaufrufe. Den Textblock daher nicht als JSON parsen. Die REST-Antworten bleiben unverändert JSON.

find_documents, ask_scout-Quellen und get_document liefern municipality als Klarname und scout_url als direkten Scout-Artikellink. Die REST-API und get_document liefern zusätzlich den Alias municipality_name. url bleibt die Originalquelle. Bei eigener Feldprojektion municipality und scout_url aufnehmen; nach Veröffentlichung die Tool-Liste im Client aktualisieren.

Linkformat: /articles/{municipality_id}/{article_id}. Der Artikel öffnet sich unabhängig von der aktuellen Suchliste, auch nach Neuladen. Ohne Browsersitzung erscheint zunächst der Scout-Login; das Ziel bleibt erhalten. Kein öffentlicher Freigabelink: API-Key oder MCP-OAuth ersetzen nicht die Browseranmeldung. Unbekannte, entfernte oder nicht zugängliche Artikel geben keinen Inhalt aus.

Clients sollen Gemeindename und „In Scout öffnen“ statt technischer ID-Listen anzeigen. Die Tool-Hinweise empfehlen dies, die Darstellung des externen Assistenten bleibt aber dessen Entscheidung. Die vertrauenswürdige Basisadresse ist zentral konfiguriert (PUBLIC_API_BASE_URL, reine HTTPS-Origin), nicht aus Request-Headern abgeleitet.

Technischer Verbindungstest (Bearer)

Mit dem zuvor gesetzten SCOUT_API_KEY lässt sich der MCP-Handshake prüfen:

curl -sS -X POST "https://hugoscout.mad.stg.sternwald.cloud/mcp" \
  -H "Authorization: Bearer $SCOUT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"scout-check","version":"1.0"}}}'

Eine erfolgreiche Antwort enthält result.protocolVersion, result.capabilities und result.serverInfo. MCP-Clients führen anschließend notifications/initialized, tools/list und tools/call aus.

Berechtigungen und Fehlerdiagnose

Gemeinde-/Datumsbeschränkungen, Limits und Nutzungsprotokoll des Scout-Keys gelten auch über MCP. list_municipalities und get_document nutzen das Suchlimit; ask_scout und find_documents das RAG-Limit. Ein deaktivierter Key sperrt auch die daran gebundenen OAuth-Tokens.

Am MCP-Endpunkt ergeben fehlende, ungültige oder deaktivierte Zugangsdaten 401 mit WWW-Authenticate. Protokollfehler werden als JSON-RPC-Fehler zurückgegeben; Tool-Fehler und Rate-Limits können in einem Tool-Ergebnis mit isError: true stehen.

Für Support: Client-Version, Auth-Modus, Zeitpunkt, HTTP-Status und X-Request-ID nennen. Niemals API-Keys, Authorization-Header, Tokens, OAuth-Codes oder vollständige Callback-URLs weitergeben. Protokolltests mit dem offiziellen MCP-SDK ersetzen nicht die Abnahme in der konkreten Open-WebUI- oder ChatGPT-Installation.

Rate-Limits & Key-Scope

Limits gelten pro Key und Endpunkt-Klasse (Standardwerte, pro Key konfigurierbar):

KlasseEndpunkteStandard-Limit
Suche/v1/municipalities, /v1/search, /v1/documents/read; MCP list_municipalities, get_document60 Anfragen/Minute
RAG/v1/rag/search, /v1/rag/ask; MCP find_documents, ask_scout10 Anfragen/Minute

Bei Überschreitung: 429 mit Retry-After-Header — warten Sie die angegebene Zeit und wiederholen Sie die Anfrage.

Key-Scope

Ein Key kann auf bestimmte Gemeinden und/oder einen Datumsbereich eingeschränkt sein. Ihre Anfrage-Filter werden serverseitig mit dem Scope geschnitten (Schnittmenge): Anfragen ausserhalb des Scopes liefern schlicht keine Treffer — es gibt keinen Fehler und keinen Weg am Scope vorbei.

GET/v1/municipalitiesGemeinde-Lookup

Übersetzt Gemeinde-Nummern in Klarnamen — und umgekehrt. Die municipality_id ist der amtliche Gemeindeschlüssel (z. B. 2422 = Neuruppin). Nutzen Sie diesen Endpunkt, um die IDs für die municipality_ids-Filter der anderen Endpunkte zu finden. Ist Ihr Key auf bestimmte Gemeinden eingeschränkt, sehen Sie hier genau diese.

Query-Parameter

ParameterBeschreibung
qOptional: Namens-Teilstring, z. B. q=zür.
cantonOptional: Region/Bundesland, z. B. canton=Brandenburg.

Beispiel

curl -s "https://hugoscout.mad.stg.sternwald.cloud/v1/municipalities?q=rupp" \
  -H "Authorization: Bearer $SCOUT_API_KEY"

Antwort

{
  "count": 1,
  "municipalities": [
    {"municipality_id": 2422, "name": "Neuruppin", "canton": "Brandenburg", "district": "Ostprignitz-Ruppin"}
  ]
}

POST/v1/documents/readOriginaltext & PDF-Seiten

Gibt gespeicherten Artikeloriginaltext (Lead und Absätze) oder Text einer zugehörigen lokalen PDF-Datei zurück, keine KI-Zusammenfassung. Zuerst /v1/search mit include_attachments=1 oder /v1/rag/search mit include_attachments: true ausführen. municipality_id und article_id aus dem Treffer übernehmen; für ein PDF zusätzlich die opake 64-stellige Hex-document_id aus attachments.pdfs.

Request-Body (JSON)

FeldBeschreibung
municipality_id, article_idPflicht, positive Integer. Ohne document_id wird der Artikel gelesen.
document_idOptional: PDF-Selektor aus attachments.pdfs oder documents (64 Hex-Zeichen).
page, text_offsetDefault 1 bzw. 0; Seiten 1–100000, Offset 0–10000000. Beim Artikeltext ist nur Seite 1 zulässig.
max_charsMaximale Textlänge je Antwort, Default 20000, Bereich 1–20000.
documents_offsetAb welcher Dokumentreferenz die Liste documents beginnt, Default 0, max. 100000.

Synthetisches Beispiel (nur Formatillustration; nicht produktiv verifiziert)

curl -s -X POST "https://hugoscout.mad.stg.sternwald.cloud/v1/documents/read" \
  -H "Authorization: Bearer $SCOUT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"municipality_id":2422,"article_id":123}'

Für ein PDF document_id aus den Referenzen und page: 1 einsetzen. Die Beispiel-IDs sind keine nachgewiesenen Produktionsbelege.

Antwort und Fortsetzung

Die Antwort enthält municipality_id, article_id, title, url, date, document_id, source_url, content_kind, status, text, page, page_count, truncated, next, documents und next_documents_offset. content_kind: article_original hat page und page_count gleich null. content_kind: pdf_extracted und status: available liefert echte 1-basierte PDF-Seiten. next enthält die nächste Kombination aus page und text_offset; bei der nächsten Seite ist der Offset 0. Dieselben IDs und diese Werte erneut senden, bis next: null ist; truncated zeigt weitere Textabschnitte an.

documents enthält maximal 100 Referenzen unabhängig von der Textpagination. Mit documents_offset: next_documents_offset weiterblättern, bis next_documents_offset: null ist. availability: stored_reference bedeutet nur einen eingetragenen PDF-Pfad, text_reference einen Legacy-Textpfad, not_stored keinen lokalen Pfad — keine Garantie für eine lesbare Datei. Die Seitenzahl in einer Referenz kann 0 sein, selbst wenn das PDF Seiten hat.

Fehlende lokale Dateien werden nicht nachgeladen oder per OCR verarbeitet. Status wie not_stored, text_unavailable, page_has_no_text, file_unreadable, pdf_unreadable_or_limit_exceeded, file_too_large oder unsafe_storage_path statt Textbeleg beachten. legacy_extracted meldet page_provenance_unavailable: nur page: 1 mit Offset, keine verlässliche PDF-Seitenzuordnung; keine Seitenzahl daraus zitieren.

Artikel-url, source_url und Referenz-url werden konservativ gefiltert: Einfache öffentliche HTTP(S)-URLs bleiben erhalten. Ein Query-String ist nur zulässig mit höchstens 10 unterschiedlichen Parametern, deren Schlüssel exakt id, docId oder article_id sind und deren Werte jeweils 1–20 ASCII-Ziffern enthalten. Solche öffentlichen ID-Links bleiben unverändert (z. B. https://beispiel.de/dokument?id=123). Unbekannte, signierte oder zugangsdatenhaltige Parameter, doppelte Schlüssel, ungültige Werte oder mehr als 10 Parameter schwärzen die ganze URL. Dasselbe gilt für Fragmente, Benutzerinformationen, private/interne Hosts oder IPs, bekannte Storage-Provider-Hosts und nichtstandardmäßige Ports. Die IDs (municipality_id, article_id, document_id) bleiben trotzdem für den Abruf verwendbar; leerer Link bedeutet nicht fehlenden Inhalt. Das gilt auch für die url in MCP-find_documents-Treffern. Lokale Dateipfade bleiben auf das Gemeinde-Medienverzeichnis beschränkt. Der Key-Scope für Gemeinde und Datum gilt auch hier. Unbekannte oder nicht freigegebene Artikel/Dokumente ergeben 404, ungültige Parameter/Offsets 400. Quellen-URL und Status vor dem Zitieren prüfen.

POST/v1/rag/askFrage/Antwort mit Quellen

Vollständige RAG-Pipeline: Retrieval + LLM-Antwort mit Quellenangaben ([1]-Zitationsmarker im Markdown). Stateless — es gibt keine Server-Session; eine Gesprächshistorie können Sie optional selbst mitschicken.

Request-Body (JSON)

FeldBeschreibung
queryPflicht. Die Frage.
municipality_ids, date_from, date_to, has_pdfFilter wie bei /v1/rag/search.
historyOptionales Array [{"role": "user"|"assistant", "content": "…"}] — die letzten max. 20 Einträge werden berücksichtigt.
top_kAnzahl Kontext-Artikel für das LLM (optional).

Beispiel

curl -s -X POST "https://hugoscout.mad.stg.sternwald.cloud/v1/rag/ask" \
  --max-time 120 \
  -H "Authorization: Bearer $SCOUT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "Fasse die wichtigsten Bauprojekte 2026 zusammen.", "municipality_ids": [2422]}'

Antwort

{
  "query": "…",
  "answer": "Markdown-Antwort mit [1]-Zitationsmarkern",
  "sources": [{"article_id": 123, "municipality_name": "…", "title": "…", "url": "…"}],
  "articles_used": 8,
  "model": "…",
  "usage": {"input_tokens": 4210, "output_tokens": 640}
}
Timeout: Die LLM-Antwort dauert typischerweise 10–60 Sekunden. Setzen Sie clientseitig mindestens 120 s HTTP-Timeout (curl --max-time 120). Die Verbindung bleibt offen, bis die vollständige JSON-Antwort geliefert ist.

Fehlerbehandlung

REST-Fehler unter /v1/… kommen als JSON-Umschlag: {"error": {"code": "…", "message": "…"}}. Für MCP siehe MCP-Zugang.

StatusCodeBedeutung
400bad_requestUngültige Parameter (z. B. fehlendes q/query, kaputte history).
401unauthorizedAPI-Key fehlt oder ist ungültig.
403key_disabledKey wurde deaktiviert.
429rate_limit_exceededRate-Limit überschritten — Retry-After-Header beachten.
500internal_errorServerfehler — Anfrage später wiederholen.

Jede Anfrage wird pro Key protokolliert (Endpoint, Zeitpunkt, Status, Dauer, bei RAG-ask auch Tokenverbrauch). Eine Abrechnung findet derzeit nicht statt.