feat(mcp): agents can list, read and create notes
New scopes notes:read (list_notes, get_note, notes in search) and notes:create (create_note from Markdown). MCP only, REST stays closed; new notes go into shared folders only with write access. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c6f28aa816
commit
a8f6c1b97b
12 files changed
+312
-23
No files matched your search
+10
-3
@@ -49,7 +49,7 @@ Basis: `/api/v1`. Antworten sind JSON; Fehler haben die Form `{"error": "Text"}`
|
||||
| DELETE | `/user/account` | Eigenes Konto mit allen Daten löschen (`{"password"}` nötig); `403` für den letzten Server-Admin. `/auth/me` liefert dafür `isLastAdmin` |
|
||||
| GET | `/backups`, `/backups/{name}` | Eigene automatische Server-Backups auflisten bzw. herunterladen |
|
||||
| POST | `/backups/{name}/restore` | Server-Backup einspielen: `mode=merge` oder `restore` (gewählte Bereiche vorher leeren), `sections=notes,tasks,lists,inbox,themes`, `preview=1` |
|
||||
| GET/POST | `/keys` | API-Keys auflisten bzw. anlegen (Klartext nur bei Erstellung); `POST` mit `name` und `scopes` (`webhook:triage`, `webhook:task`, `webhook:list`, `tasks:read`, `tasks:write`, `lists:read`, `lists:write`; Standard `["webhook:triage"]`) |
|
||||
| GET/POST | `/keys` | API-Keys auflisten bzw. anlegen (Klartext nur bei Erstellung); `POST` mit `name` und `scopes` (`webhook:triage`, `webhook:task`, `webhook:list`, `tasks:read`, `tasks:write`, `lists:read`, `lists:write`, `notes:read`, `notes:create`; Standard `["webhook:triage"]`) |
|
||||
| POST | `/mcp` | MCP-Endpunkt für KI-Agenten, siehe unten |
|
||||
| PATCH | `/keys/{id}` | Name (`name`) und/oder Rechte (`scopes`) ändern; der Schlüssel selbst bleibt gleich |
|
||||
| DELETE | `/keys/{id}` | API-Key löschen |
|
||||
@@ -101,7 +101,10 @@ Betreiber können MCP abschalten (`MCP_ENABLED=false` oder Schalter in der Admin
|
||||
| `add_list_item` | `lists:write` | Eintrag hinzufügen (`listId`, `content`, `category`, `quantity`) |
|
||||
| `check_list_item` | `lists:write` | Eintrag abhaken (`completed` false = zurück) |
|
||||
| `update_list_item` | `lists:write` | Text, Kategorie, Menge ändern |
|
||||
| `search` | ein Lese-Scope | Volltextsuche in Aufgaben und Listeneinträgen (nur die Teile der eigenen Scopes, keine Notizen) |
|
||||
| `list_notes` | `notes:read` | Notizen ohne Inhalt, zuletzt geänderte zuerst (`folder` samt Unterordnern, `query` im Titel, `limit`) |
|
||||
| `get_note` | `notes:read` | Eine Notiz; `content` sind die Punkte als eingerückte Markdown-Liste (Tab je Ebene, `- [ ]` für To-dos, Bilder und Aufnahmen als Platzhalter) |
|
||||
| `create_note` | `notes:create` | Neue Notiz (`title`, `content` als Markdown wie beim Import, `folderPath`, `icon`); in geteilte Ordner nur mit Schreibrecht. Bestehende Notizen ändert kein Werkzeug |
|
||||
| `search` | ein Lese-Scope | Volltextsuche in Aufgaben, Listeneinträgen und Notizen (nur die Teile der eigenen Scopes); Notizen als `notes` mit `noteId`, `title`, `snippet` |
|
||||
| `capture_to_inbox` | `webhook:triage` | Gedanken in die Inbox |
|
||||
|
||||
Was ein Agent anlegt oder ändert, bekommt eine dauerhafte Herkunft (`origin`, siehe [API-Schlüssel](#api-schlüssel-rechte-und-herkunft)). `create_task` geht auch mit `webhook:task`, `add_list_item` auch mit `webhook:list`.
|
||||
@@ -123,7 +126,7 @@ Clients, die keinen `X-API-Key` setzen können, verbinden sich per OAuth 2.1 nac
|
||||
|
||||
Die Zustimmungsseite nutzt `GET`/`POST /api/v1/oauth/authorize` (nur mit Anmeldung, nicht mit Schlüsseln).
|
||||
|
||||
- **Rechte:** dieselben wie bei Schlüsseln, angeboten werden `tasks:read`, `tasks:write`, `lists:read`, `lists:write` und `webhook:triage`. Fragt der Client nichts Bestimmtes an, sind Lesen und Ändern für Aufgaben und Listen vorbelegt.
|
||||
- **Rechte:** dieselben wie bei Schlüsseln, angeboten werden `tasks:read`, `tasks:write`, `lists:read`, `lists:write`, `notes:read`, `notes:create` und `webhook:triage`. Fragt der Client nichts Bestimmtes an, sind Lesen und Ändern für Aufgaben und Listen vorbelegt.
|
||||
- **Verbindung = Eintrag unter `/keys`** mit `kind: "oauth"` und dem Namen „<Client> (OAuth)“. Rechte ändern, umbenennen und löschen wie bei jedem Schlüssel; Herkunft (`origin`) mit diesem Namen. Erneutes Zustimmen desselben Clients verwendet den Eintrag weiter.
|
||||
- **Tokens:** Code 10 Minuten und einmal gültig, Zugangs-Token (`mindsift_oat_…`) 1 Stunde, Refresh-Token (`mindsift_ort_…`) 90 Tage und bei jeder Erneuerung neu. Wird ein bereits ersetzter Refresh-Token noch einmal benutzt, enden alle Tokens der Verbindung. Zugangs-Tokens gelten nur für `POST /mcp`, nicht für REST oder Webhooks.
|
||||
- **Verfall:** Eine Verbindung, die `OAUTH_IDLE_DAYS` (Standard 90, `0` = nie) nicht benutzt wurde, wird gelöscht.
|
||||
@@ -142,6 +145,10 @@ Ein Schlüssel darf nur diese Endpunkte und braucht dafür eines der genannten R
|
||||
| `lists:read` | Listen lesen | `GET /lists`, `GET /lists/{id}` | `list_lists`, `get_list`, `search` |
|
||||
| `webhook:list` | Listeneinträge hinzufügen | `POST /lists/{id}/items`, `/webhooks/ingest` mit Ziel `list_item` | `add_list_item` |
|
||||
| `lists:write` | Listen ändern (schließt Hinzufügen ein) | dazu `PATCH`/`PUT /lists/{id}/items/{itemId}` | dazu `check_list_item`, `update_list_item`, `create_list` |
|
||||
| `notes:read` | Notizen lesen | – | `list_notes`, `get_note`, `search` |
|
||||
| `notes:create` | Notizen anlegen | – | `create_note` |
|
||||
|
||||
Notizen gibt es nur über MCP; per REST bleiben sie für Schlüssel gesperrt. Notizen tragen keine Herkunft (`origin`).
|
||||
|
||||
Die Ids `webhook:…` stammen aus der Zeit, als Schlüssel nur einliefern konnten, und bleiben für bestehende Schlüssel gleich.
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ Hinweis: `DATABASE_URL` mit `postgres…` führt dazu, dass der Standardpfad in
|
||||
- **Einladungscodes:** `INV-` plus 100 Zufallsbits in Base32. Die Registrierung beansprucht den Code, bevor das Konto entsteht (kein doppeltes Einlösen).
|
||||
- **JWT und Sitzungen:** HS256, Claims `userId`, `email`, `displayName`, `tokenVersion`, `jti`. Jede Anmeldung legt eine Zeile in `sessions` an (Gerät, IP, letzte Nutzung); `jti` ist deren ID. Gültig ist ein Token nur, wenn die Sitzung aktiv ist (nicht beendet, höchstens 30 Tage unbenutzt), das Konto aktiv ist und `tokenVersion` mit `users.token_version` übereinstimmt. Ist das Token älter als 24 Stunden, schickt der Server im Header `X-Session-Token` ein neues für dieselbe Sitzung; das Frontend übernimmt es (`request()` in `lib/api.ts`). Aktive Geräte bleiben so angemeldet, unbenutzte fallen nach 30 Tagen heraus (`internal/api/sessions.go`). Neue Passwörter brauchen mindestens 8 Zeichen (`auth.ValidatePassword`). `token_version` wird bei Passwortänderung, Rollenwechsel, Deaktivierung und „Alle Sitzungen beenden“ erhöht.
|
||||
- **Logout** beendet die Sitzung des Geräts; `GET /auth/sessions` listet die Geräte, `DELETE /auth/sessions/{id}` meldet eines ab. Passwort und Login-E-Mail ändern sich nur mit dem aktuellen Passwort (`currentPassword`, falsche Eingaben zählen für die Login-Sperre); ein neues Passwort meldet alle anderen Geräte ab, dieses Gerät erhält per `X-Session-Token` ein neues Token. Ist SMTP eingerichtet, geht eine Hinweismail an die bisherige Adresse.
|
||||
- **API-Keys:** `mindsift_sec_<hex>`, gespeichert als SHA-256, nur das Präfix bleibt sichtbar. Der Key gilt als Bearer-Token oder in `X-API-Key`. Scopes `webhook:*` liefern Daten an (`POST /inbox`, `POST /webhooks/ingest`), die Agenten-Scopes `tasks:read`, `tasks:write`, `lists:read`, `lists:write` gelten nur am MCP-Endpunkt (`POST /mcp`, `internal/api/mcp.go`); jedes Werkzeug prüft seinen Scope, `tools/list` zeigt nur erlaubte. Alle anderen Endpunkte antworten für Keys mit `403`; Notizen sind mit Keys nie erreichbar.
|
||||
- **API-Keys:** `mindsift_sec_<hex>`, gespeichert als SHA-256, nur das Präfix bleibt sichtbar. Der Key gilt als Bearer-Token oder in `X-API-Key`. Scopes `webhook:*` liefern Daten an (`POST /inbox`, `POST /webhooks/ingest`), die Agenten-Scopes `tasks:read`, `tasks:write`, `lists:read`, `lists:write` gelten nur am MCP-Endpunkt (`POST /mcp`, `internal/api/mcp.go`); jedes Werkzeug prüft seinen Scope, `tools/list` zeigt nur erlaubte. Alle anderen Endpunkte antworten für Keys mit `403`. Notizen erreichen Keys nur über MCP (`notes:read`: auflisten, lesen, suchen; `notes:create`: neue anlegen, Inhalt als Markdown über `MarkdownToBlocks`), nie per REST.
|
||||
- **OAuth für MCP** (`internal/api/oauth.go`, `internal/storage/oauth.go`): MindSift ist mit `BASE_URL` sein eigener OAuth-Server für `/api/v1/mcp` (Discovery nach RFC 9728/8414, dynamische Registrierung, Code-Flow mit PKCE S256, rotierende Refresh-Tokens), damit sich claude.ai und ChatGPT verbinden können. Die Zustimmungsseite ist Teil der App (`/oauth/authorize`, `OAuthConsentView`), weil die Anmeldung im `localStorage` liegt. Eine Zustimmung wird ein Eintrag in `api_keys` (`kind = 'oauth'`); Tokens (`mindsift_oat_`/`mindsift_ort_`) hängen daran und gelten nur am MCP-Endpunkt, Rechte und Herkunft laufen wie bei Schlüsseln. Abgelaufene Tokens und unbenutzte Verbindungen (`OAUTH_IDLE_DAYS`) räumt der stündliche Aufräum-Lauf weg. Im Demo-Modus aus, ebenso wenn MCP abgeschaltet ist (`MCP_ENABLED` bzw. Admin-Schalter).
|
||||
- **Passwort vergessen:** nur wenn SMTP und `BASE_URL` gesetzt sind (nicht im Demo-Modus; `passwordReset` in `/auth/status`). `POST /auth/forgot-password` antwortet immer gleich und verschickt die Mail im Hintergrund, höchstens alle 2 Minuten pro Konto. Der Link `BASE_URL/?resetToken=…` gilt eine Stunde und einmal; gespeichert wird nur der SHA-256-Hash (`password_reset_tokens`). `POST /auth/reset-password` setzt das Passwort, entwertet alle offenen Links des Kontos und erhöht `token_version` (alle Sitzungen enden). Die App entfernt den Token sofort aus der Adresszeile.
|
||||
- **Registrierung:** nur mit Einladungscode oder wenn `allow_registration` aktiv ist. Das erste Konto entsteht über `/auth/setup` und wird Administrator.
|
||||
|
||||
Reference in new issue
Block a user