Zum Inhalt springen

MCP nutzen

MCP gibt KI-Agenten Werkzeugzugriff auf Tibi — statt Klicks ruft der Agent tools/list und tools/call auf. Transport: Streamable-HTTP mit JSON-RPC 2.0. Route: fest GET + POST /api/v1/mcp (immer registriert; mcp.route wird ignoriert — siehe Schnittstellen).

Nur PAT via X-Auth-Token (tibi_pat_…). JWT, Admin-Token und anonyme Aufrufe scheitern — auch mit vollen Rechten. Jedes Werkzeug prüft zusätzlich Scope plus Live-Rechte des Token-Besitzers (siehe Rechte und Tokens, Token-Köpfe).

initialize — Handshake
curl -s -X POST http://localhost:8080/api/v1/mcp \
-H 'Content-Type: application/json' \
-H 'X-Auth-Token: tibi_pat_DEIN_TOKEN' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
# → {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05",
# "serverInfo":{"name":"tibi-server-mcp","version":"v1"},
# "capabilities":{"tools":{}}}}

Gehäuse: internal/mcp/tools_crud.go (10) + tools_discovery.go (12) + tools_system.go (14). Jede Antwort ist ein JSON-RPC-Envelope mit der Request-id.

Werkzeug Wofür Scope
documents_list Einträge listen (Filter, Sort, Limit wie Lesen und suchen) namespace.<ns>.<col>.read
document_get Ein Eintrag per ID .read
documents_count Nur Anzahl .read
documents_facet Werte je Feld zählen .read
queue_status Suchindex-Warteschlange Projekt-Leserecht
document_create Eintrag anlegen .create
document_update Eintrag ändern .update
document_delete Eintrag löschen .delete
file_read REST-only Datei lesen: GET /_/:project/:collection/:id/:field/*path .read
action_call Aktion aufrufen (method: get/post) Aktions-Scope
projects_list / project_get Projekte auflisten / ein Projekt project.read
collections_list Sammlungen eines Projekts project.read
collection_describe Schema + meta einer Sammlung project.read
actions_list / action_describe Aktionen auflisten / beschreiben project.read
jobs_list Jobs auflisten Projekt-Leserecht
openapi_get OpenAPI-Ausschnitt project.read
system_meta_get System-Metadaten project.read
agent_guide_get Agent-Anleitung (meta.agent) lesen project.read
assets_list / asset_read Projekt-Assets listen / REST-only lesen per GET /_/:project/_/assets/:name/*path project.read
users_list / user_get Benutzer listen / lesen user.read
user_create / user_update / user_delete REST-only (siehe unten) user.create / .update / .delete
orgs_list / org_get Orgs listen / lesen org.read
teams_list / team_get Teams listen / lesen team.read
audit_query Audit-Log abfragen audit.read + Sammlungs-Leserecht
project_reload REST-only project.reload
regenerate_search REST-only project.regenerate-search
backup_create REST-only project.backup
snapshots_list REST-only project.snapshot

Methoden-Mapping (method.go) ist sicherheitsrelevant: Lese-Werkzeuge laufen als GET, document_create als POST, document_update als PUT, document_delete als DELETE. So greifen deine Hooks (Stufen je Methode, Kontexte in Hook-Kontexte) genau wie bei REST.

Neun Werkzeuge antworten nur mit einem Verweis auf REST — sie führen per MCP nichts aus (erkennbar an "REST-only in this phase" in der Beschreibung von tools/list). allowAdminActions steuert nur, ob Admin-Werkzeuge überhaupt sichtbar sind — die Sperre bleibt.

Werkzeug Stattdessen
user_create, user_update, user_delete REST /api/v1/user
project_reload Admin-Reload-Endpunkt
regenerate_search Admin-Suchindex-Endpunkt
backup_create Admin-Backup-Endpunkt
snapshots_list Admin-Snapshots-Endpunkt
file_read GET /_/:project/:collection/:id/:field/*path
asset_read GET /_/:project/_/assets/:name/*path

Pro Werkzeug: Signatur (Pflichtparameter mit *), genau ein tools/call-Beispiel als Einzeiler und der wichtigste Fehlerfall aus dem Code (mcp_tools_impl.go, guards.go, query.go, method.go). Ohne PAT scheitert jedes Werkzeug mit 403 (mcp requires a personal access token (X-Auth-Token: tibi_pat_...), guards.go); ein Tippfehler im Namen gibt -32601 unknown tool (mcp_handler.go).

Signatur: namespace*, collection*, optional filter, sort, limit, offset, select, projection, lookup, aggregate, q, qName, count, extra. Scope: namespace.<ns>.<col>.read (bei lookup/aggregate zusätzlich Lese-Recht auf das Ziel). Methode: GET.

tools/call: {"name":"documents_list","arguments":{"namespace": "<ns>", "collection": "content", "limit": 5}}

Fehler: ohne namespace/collection → 400 (namespace and collection are required); lange Listen kappt maxResultItems (Standard 100).

Signatur: namespace*, collection*, id*, optional select, lookup, extra. Scope: namespace.<ns>.<col>.read. Methode: GET.

tools/call: {"name":"document_get","arguments":{"namespace": "<ns>", "collection": "content", "id": "<id>"}}

Fehler: unbekannte id → 404 (not found); unlesbare Hex-ID → 400.

Signatur: namespace*, collection*, optional filter, q, extra. Scope: namespace.<ns>.<col>.read. Methode: GET.

tools/call: {"name":"documents_count","arguments":{"namespace": "<ns>", "collection": "content"}}

Fehler: ohne Leserecht am Ziel → 403 (permission denied bzw. Scope-Fehler).

Signatur: namespace*, collection*, fields*, optional filter, q, qName, qLimit, extra. Scope: namespace.<ns>.<col>.read. Methode: GET.

tools/call: {"name":"documents_facet","arguments":{"namespace": "<ns>", "collection": "content", "fields": "status", "q": "kontakt", "qName": "combined"}}

Mit q/qName zählen die Werte nur über den Treffern der Suche; qLimit (Zahl oder all) weitet das Fenster über rrf.topK hinaus (Lesen und suchen).

Fehler: ohne fields → 400 (missing required parameter: facet auf Controller-Ebene).

Signatur: namespace*, collection*. Scope: namespace.<ns>.<col>.read. Methode: GET.

tools/call: {"name":"queue_status","arguments":{"namespace": "<ns>", "collection": "content"}}

Fehler: unbekannte Sammlung → 404 auf Controller-Ebene.

Signatur: namespace*, collection*, data*, optional items[] (Bulk). Datei-Felder als {src: "data:<mime>;base64,…", path: "name.jpg"} hochladen (Typ wird serverseitig erschnüffelt). Scope: namespace.<ns>.<col>.create. Methode: POST.

tools/call: {"name":"document_create","arguments":{"namespace": "<ns>", "collection": "content", "data": {"name": "Neu"}}}

Fehler: ohne Schreibrecht → 403; Pflichtfeld-Verletzung → 400 aus der Validierung.

Signatur: namespace*, collection*, dazu id (einzeln) oder filter (Bulk), plus data. Gespeicherte Datei-src zurückgeben behält die Datei. Scope: namespace.<ns>.<col>.update (+ Bulk-Freigabe). Methode: PUT.

tools/call: {"name":"document_update","arguments":{"namespace": "<ns>", "collection": "content", "id": "<id>", "data": {"name": "Neu"}}}

Fehler: unbekannte id → 404; Bulk ohne filter → 400 (filter query parameter is required).

Signatur: namespace*, collection*, dazu id (einzeln) oder filter (Bulk). Scope: namespace.<ns>.<col>.delete (+ Bulk-Freigabe). Methode: DELETE.

tools/call: {"name":"document_delete","arguments":{"namespace": "<ns>", "collection": "content", "id": "<id>"}}

Fehler: ohne Löschrecht → 403; Bulk ohne filter → 400 (filter query parameter is required for bulk delete).

REST-only Führt per MCP nichts aus. Signatur: namespace*, collection*, id*, field*, optional path. Scope: namespace.<ns>.<col>.read.

Fehler: immer 400 (use GET file endpoint (public like web)) — nutze stattdessen GET /_/:project/:collection/:id/:field/*path.

action_call — Aktion aufrufen (method: get/post)

Abschnitt betitelt „action_call — Aktion aufrufen (method: get/post)“

Signatur: namespace*, action*, optional method (get oder post, Standard post), data. Scope: namespace.<ns>._actions.<name>.read bzw. .create. Methode: GET oder POST je nach method.

tools/call: {"name":"action_call","arguments":{"namespace": "<ns>", "action": "versand", "method": "post", "data": {}}}

Fehler: method: "put" → 400 (action method must be get or post, method.go).

projects_list — Projekte auflisten / ein Projekt

Abschnitt betitelt „projects_list — Projekte auflisten / ein Projekt“

Signatur: optional limit, offset. Scope: project.read. Methode: GET.

tools/call: {"name":"projects_list","arguments":{"limit": 10}}

Fehler: ohne project.read-Scope → 403; fremde Orgs filtert die Sichtbarkeit still heraus (leere Liste trotz Daten).

Signatur: id*. Scope: project.read. Methode: GET.

tools/call: {"name":"project_get","arguments":{"id": "<id>"}}

Fehler: ohne id → 400 (id is required); unsichtbar/ unbekannt → 404 (not found).

Signatur: namespace*. Scope: project.read plus Lese-Recht je Sammlung. Methode: GET.

tools/call: {"name":"collections_list","arguments":{"namespace": "<ns>"}}

Fehler: ohne namespace → 400 (namespace is required); unbekanntes Projekt → 404 (project not found).

collection_describe — Schema + meta einer Sammlung

Abschnitt betitelt „collection_describe — Schema + meta einer Sammlung“

Signatur: namespace*, collection*, optional lang. Gibt Schema, agent- und query-Meta (bereinigt, ohne Secrets) plus yourPermissions zurück. Scope: project.read plus Lese-Recht auf die Sammlung. Methode: GET.

tools/call: {"name":"collection_describe","arguments":{"namespace": "<ns>", "collection": "content"}}

Fehler: unbekannte Sammlung → 404 (collection not found); ohne Leserecht → 403 (permission denied).

Signatur: namespace*. Scope: Lese-Recht auf den Namensraum oder je Aktion. Methode: GET.

tools/call: {"name":"actions_list","arguments":{"namespace": "<ns>"}}

Fehler: ohne namespace → 400 (namespace is required); Aktionen ohne Recht fehlen still in der Liste.

action_describe — Aktionen auflisten / beschreiben

Abschnitt betitelt „action_describe — Aktionen auflisten / beschreiben“

Signatur: namespace*, action*. Scope: namespace.<ns>._actions.<name>.read. Methode: GET.

tools/call: {"name":"action_describe","arguments":{"namespace": "<ns>", "action": "versand"}}

Fehler: unbekannte Aktion → 404 (action not found); ohne Aktions-Scope → 403.

Signatur: namespace*. Nur Configs, kein Trigger. Scope: project.read. Methode: GET.

tools/call: {"name":"jobs_list","arguments":{"namespace": "<ns>"}}

Fehler: ohne project.read-Scope → 403; unbekanntes Projekt → 404 (project not found).

Signatur: namespace*, optional lang. Laut Tool-Beschreibung öffentlich wie GET /_openapi/:project. Methode: GET.

tools/call: {"name":"openapi_get","arguments":{"namespace": "<ns>"}}

Fehler: unbekanntes Projekt → 404 (project not found).

Signatur: optional model (user/project). Laut Tool-Beschreibung öffentlich wie GET /_meta. Methode: GET.

tools/call: {"name":"system_meta_get","arguments":{"model": "user"}}

Fehler: ohne PAT → 403 (mcp requires a personal access token) — sonst antwortet das Werkzeug immer mit 200.

agent_guide_get — Agent-Anleitung (meta.agent) lesen

Abschnitt betitelt „agent_guide_get — Agent-Anleitung (meta.agent) lesen“

Signatur: namespace*, optional collection, action, lang. Liest kuratiertes meta.agent (Projekt → Sammlung/Aktion) plus yourPermissions. Scope: je Ebene das passende Lese-Recht. Methode: GET.

tools/call: {"name":"agent_guide_get","arguments":{"namespace": "<ns>", "collection": "content"}}

Fehler: ohne Sammlungs-Leserecht → 403 (permission denied); unbekanntes Projekt → 404 (project not found).

Signatur: namespace*. Scope: project.read. Methode: GET.

tools/call: {"name":"assets_list","arguments":{"namespace": "<ns>"}}

Fehler: unbekanntes Projekt → 404 (project not found).

asset_read — Asset lesen (REST-only: GET /_/:project/_/assets/:name/*path)

Abschnitt betitelt „asset_read — Asset lesen (REST-only: GET /_/:project/_/assets/:name/*path)“

REST-only Führt per MCP nichts aus. Signatur: namespace*, name*, path*. Methode: GET.

Fehler: immer 400 (use GET /_/:project/_/assets/:name/*path (public like web)) — nutze stattdessen den Asset-Endpunkt.

Signatur: optional filter, limit, offset (alle als String typisiert, tools_system.go). Scope: user.read. Methode: GET.

tools/call: {"name":"users_list","arguments":{"limit": "10"}}

Fehler: ohne user.read-Scope → 403; fremde Benutzer filtert die Eigentümer-Sicht heraus.

Signatur: id*. Scope: user.read. Methode: GET.

tools/call: {"name":"user_get","arguments":{"id": "<id>"}}

Fehler: ohne id → 400 (id is required); unsichtbar → 403 über den Scope-Filter.

user_create — Benutzer anlegen (REST-only: REST /api/v1/user)

Abschnitt betitelt „user_create — Benutzer anlegen (REST-only: REST /api/v1/user)“

REST-only Führt per MCP nichts aus. Signatur: optional data. Scope: user.create.

Fehler: immer 400 (user write via MCP is REST-only in this phase, use REST /api/v1/user) — nutze stattdessen REST /api/v1/user.

user_update — Benutzer ändern (REST-only: REST /api/v1/user)

Abschnitt betitelt „user_update — Benutzer ändern (REST-only: REST /api/v1/user)“

REST-only Führt per MCP nichts aus. Signatur: optional id, data. Scope: user.update.

Fehler: immer 400 (user write via MCP is REST-only in this phase, use REST /api/v1/user) — nutze stattdessen REST /api/v1/user.

user_delete — Benutzer löschen (REST-only: REST /api/v1/user)

Abschnitt betitelt „user_delete — Benutzer löschen (REST-only: REST /api/v1/user)“

REST-only Führt per MCP nichts aus. Signatur: id*. Scope: user.delete.

Fehler: immer 400 (user write via MCP is REST-only in this phase, use REST /api/v1/user) — nutze stattdessen REST /api/v1/user.

Signatur: optional limit (als String typisiert). Scope: org.read. Methode: GET.

tools/call: {"name":"orgs_list","arguments":{}}

Fehler: ohne org.read-Scope → 403; fremde Orgs filtert die Mitgliedschaft heraus.

Signatur: id*. Scope: org.read. Methode: GET.

tools/call: {"name":"org_get","arguments":{"id": "<id>"}}

Fehler: ohne id → 400 (id is required); fremde Org → 403 über den Scope-Filter.

Signatur: optional limit (als String typisiert). Scope: team.read. Methode: GET.

tools/call: {"name":"teams_list","arguments":{}}

Fehler: ohne team.read-Scope → 403.

Signatur: id*. Scope: team.read. Methode: GET.

tools/call: {"name":"team_get","arguments":{"id": "<id>"}}

Fehler: ohne id → 400 (id is required).

Signatur: optional filter, limit (als String typisiert). Scope: audit.read plus Lese-Recht je Sammlung; System-Ereignisse bleiben für Nicht-Admins verborgen. Methode: GET.

tools/call: {"name":"audit_query","arguments":{"limit": "20"}}

Fehler: ohne audit.read-Scope → 403.

project_reload — Projekt neu laden (REST-only: Admin-Reload-Endpunkt)

Abschnitt betitelt „project_reload — Projekt neu laden (REST-only: Admin-Reload-Endpunkt)“

REST-only Führt per MCP nichts aus. Signatur: namespace*. Scope: project.reload.

Fehler: bei allowAdminActions: false → 403 (admin actions disabled); sonst immer 400 (admin tool "project_reload" is REST-only in this phase, use REST admin endpoints).

regenerate_search — Suchindex neu aufbauen (REST-only: Admin-Suchindex-Endpunkt)

Abschnitt betitelt „regenerate_search — Suchindex neu aufbauen (REST-only: Admin-Suchindex-Endpunkt)“

REST-only Führt per MCP nichts aus. Signatur: namespace*. Scope: project.regenerate-search.

Fehler: bei allowAdminActions: false → 403 (admin actions disabled); sonst immer 400 (admin tool "regenerate_search" is REST-only in this phase, use REST admin endpoints).

backup_create — Backup erzeugen (REST-only: Admin-Backup-Endpunkt)

Abschnitt betitelt „backup_create — Backup erzeugen (REST-only: Admin-Backup-Endpunkt)“

REST-only Führt per MCP nichts aus. Signatur: namespace*. Scope: project.backup.

Fehler: bei allowAdminActions: false → 403 (admin actions disabled); sonst immer 400 (admin tool "backup_create" is REST-only in this phase, use REST admin endpoints).

snapshots_list — Snapshots auflisten (REST-only: Admin-Snapshots-Endpunkt)

Abschnitt betitelt „snapshots_list — Snapshots auflisten (REST-only: Admin-Snapshots-Endpunkt)“

REST-only Führt per MCP nichts aus. Signatur: namespace*. Scope: project.snapshot. Methode: GET.

Fehler: bei allowAdminActions: false → 403 (admin actions disabled); sonst immer 400 (admin tool "snapshots_list" is REST-only in this phase, use REST admin endpoints).

Siehe Grenzen: REST-only-Werkzeuge weiter oben — dort steht die vollständige Liste.

agent_guide_get liest, was du in api/config.yml und pro Sammlung pflegst. Pflege dort Zweck, Pflichtfelder, Lookups, Workflows und Fallstricke — der Agent folgt dem Wortlaut.

api/config.yml — meta.agent (Starter)
meta:
agent:
description: "Tibi Svelte starter: Svelte 5 SPA with pagebuilder blocks, goja SSR, per-language navigation."
frontend:
lookup: "content: blocks.heroImage.image:medialib, blocks.image:medialib; navigation: elements.page:content"
designGuide: "Future free code/HTML blocks: add block type in content.blocks …"
workflows: ["Create page (…) -> fill blocks -> link navigation -> … -> verify /de plus anonymous SSR …"]
hints: ["Public reads need active:true plus publication window (filter_public) …"]
api/collections/content.yml — meta.agent pro Sammlung
meta:
agent:
purpose: "CMS pages rendered by App.svelte + BlockRenderer (hero, features, richtext, accordion, contact-form)."
instructions: "Required: name, path, lang. Always set translationKey …"
fieldHints:
path: "lowercase, e.g. /ueber-uns; stored without language prefix"
workflows: ["filter before create (path+lang) to stay idempotent; 409 means exists", "…"]
hints: ["richtext image only renders when _lookup.image.file.src exists", "…"]
  1. purpose + instructions schreiben: Was rendert die Sammlung, welche Felder sind Pflicht?
  2. lookup-Zeile pflegen: Welche Fremdschlüssel muss der Agent immer mitladen?
  3. workflows pflegen: Erstellen → verknüpfen → anonym verifizieren (PAT-Erfolg allein zählt nicht).
  4. hints pflegen: active:true-Regel, Pfad-Muster, Eindeutigkeit (path+lang, translationKey+lang).
  5. Per agent_guide_get gegenlesen.

Zusatz: Lese-Werkzeuge akzeptieren extra-Parameter für eigene Query-Schlüssel aus meta.query (Hooks lesen sie per ctx.request().query(name)). REST ignoriert das Schema (offen per Design) und nutzt es als Doku; MCP validiert (query.go, Kern- und Auth-Schlüssel sind reserviert).

Antwort Ursache Lösung
404 "mcp disabled" mcp.enabled: false (Route bleibt, Handler antwortet 404) In config.yml aktivieren
403 + PAT-Hinweis JWT, Admin-Token oder anonym statt PAT X-Auth-Token: tibi_pat_… senden
-32601 method not found Unbekannte Methode initialize, tools/list, tools/call nutzen
-32601 unknown tool Tippfehler im Werkzeugnamen Namen aus tools/list kopieren
-32003 Scope oder Live-Recht fehlt PAT-Scopes und Rechte prüfen
-32602 invalid params Pflicht-Argument fehlt collection_describe-Schema bzw. Fehlerbilder lesen
Leere Listen trotz Daten maxResultItems (Standard 100) oder filter_public-Hook Limit erhöhen bzw. active:true + Zeitraum prüfen

Verifiziert am 68b4278 2026-10-17 (tibi-server: tools_crud, tools_discovery, tools_system, method, query, guards, registry, server, mcp_handler, mcp_tools_impl, setup), am e3f92eb 2026-10-17 (tibi-svelte-starter: config.yml und content.yml meta.agent)