Skip to main content
Glama
llego
by llego

Anchor MCP

Implementierungsplan für einen kleinen MCP-Sidecar, der sichere Anchor-Notes-Tools über einen Tunnel-Client, der im selben Docker-Compose-Stack wie Anchor läuft, für ChatGPT bereitstellt.

Forschungsbasis: Upstream-Repository ZhFahim/anchor von Anchor, Standard-Branch main, geprüft am 20.08.2026. Anchor ist ein Nest.js-Backend mit authentifizierten REST-Endpunkten unter /api/*.

Ziel

Einen MCP-Server neben Anchor betreiben, damit externe Assistenten Anchor-Notizen auflisten, durchsuchen, lesen, erstellen, aktualisieren, importieren und Dateien anhängen können, ohne die Anchor-Datenbank oder die private API direkt offenzulegen.

Related MCP server: NotesBridge

Aktueller Stand

Der erste Meilenstein ist umgesetzt:

  • Streambarer HTTP-MCP-Endpunkt unter POST /mcp.

  • Health-Endpunkt unter GET /healthz.

  • Schreibgeschützte Anchor-Tools: anchor_list_notes, anchor_search_notes, anchor_get_note, anchor_list_tags, anchor_list_attachments.

  • Optionaler MCP-Bearer-Schutz mit ANCHOR_MCP_TOKEN.

  • Anchor-API-Aufrufe verwenden ANCHOR_TOKEN und ANCHOR_BASE_URL.

  • Dockerfile ist enthalten.

Schreib-Tools sind bewusst noch nicht implementiert.

Entwicklung

Unter NixOS nix-shell für Node/npm-Befehle verwenden:

nix-shell -p nodejs --run 'npm install'
nix-shell -p nodejs --run 'npm run typecheck'
nix-shell -p nodejs --run 'npm run build'

Lokal ausführen:

ANCHOR_BASE_URL=https://anchor.cri.su \
ANCHOR_TOKEN=... \
ANCHOR_MCP_TOKEN=... \
nix-shell -p nodejs --run 'npm run dev'

Der MCP-Endpunkt ist http://localhost:8000/mcp. Wenn ANCHOR_MCP_TOKEN gesetzt ist, müssen Aufrufer Authorization: Bearer <token> senden.

Bereitstellungsmodell

Der vorgesehene Stack umfasst drei Dienste:

services:
  anchor:
    # Existing Anchor service.

  anchor-mcp:
    build: /path/to/anchor-mcp
    environment:
      ANCHOR_BASE_URL: http://anchor:3000
      ANCHOR_TOKEN: ${ANCHOR_TOKEN}
      ANCHOR_MCP_TOKEN: ${ANCHOR_MCP_TOKEN}
    expose:
      - "8000"
    depends_on:
      - anchor

  chatgpt-tunnel-client:
    # Outbound tunnel client.
    environment:
      MCP_TARGET_URL: http://anchor-mcp:8000/mcp
      MCP_TARGET_TOKEN: ${ANCHOR_MCP_TOKEN}
    depends_on:
      - anchor-mcp

Der MCP-Server sollte nur im Docker-Netzwerk erreichbar sein. Der Tunnel-Client ist die einzige externe Brücke.

Bestätigte Anchor-API-Oberfläche

Alle unten aufgeführten Endpunkte sind durch den AuthGuard von Anchor geschützt und erwarten Authorization: Bearer <token>. Der Guard akzeptiert Anchor-Tokens, die zu einem aktiven Benutzer aufgelöst werden.

Notizen:

  • POST /api/notes

  • GET /api/notes?search=<query>&tagId=<tagId>&limit=<limit>

  • GET /api/notes/:id

  • PATCH /api/notes/:id

  • DELETE /api/notes/:id

  • DELETE /api/notes/:id/permanent

  • PATCH /api/notes/:id/restore

  • GET /api/notes/trash

  • GET /api/notes/archive

  • POST /api/notes/bulk/delete

  • POST /api/notes/bulk/archive

  • POST /api/notes/bulk/pin

  • POST /api/notes/bulk/tags

Tags:

  • POST /api/tags

  • GET /api/tags

  • GET /api/tags/:id

  • GET /api/tags/:id/notes

  • PATCH /api/tags/:id

  • DELETE /api/tags/:id

Anhänge:

  • POST /api/notes/:noteId/attachments

  • GET /api/notes/:noteId/attachments

  • GET /api/notes/:noteId/attachments/:id

  • DELETE /api/notes/:noteId/attachments/:id

  • PATCH /api/notes/:noteId/attachments/reorder

Import/Export:

  • POST /api/import/notes

  • POST /api/import/notes/:noteId/attachments

  • GET /api/export

Sync-API:

  • POST /api/sync

  • GET /api/sync/events als Server-Sent Events

Freigabe:

  • POST /api/notes/:id/shares

  • GET /api/notes/:id/shares

  • PATCH /api/notes/:id/shares/:shareId

  • DELETE /api/notes/:id/shares/:shareId

Der MCP-Server sollte mit den normalen Notizen-/Tags-/Anhänge-/Import-Endpunkten starten. Die Sync-API ist für konfliktbewusste Offline-Clients nützlich, aber ein MCP-Sidecar kann sie zunächst auslassen.

Datenstrukturen

Body zum Erstellen einer Notiz:

{
  "title": "string",
  "content": "optional string",
  "isPinned": false,
  "isArchived": false,
  "background": "optional string",
  "tagIds": ["tag-id"]
}

Der Body zum Aktualisieren einer Notiz ist ein partieller Create-Body plus optionaler optimistischer Sperre:

{
  "title": "optional string",
  "content": "optional string",
  "isPinned": false,
  "isArchived": false,
  "background": "optional string",
  "tagIds": ["tag-id"],
  "baseVersion": 1
}

Anchor gibt transformierte Notizen mit diesen wichtigen Feldern zurück:

{
  "id": "uuid",
  "title": "string",
  "content": "string or null",
  "version": 1,
  "isPinned": false,
  "isArchived": false,
  "background": null,
  "state": "active",
  "createdAt": "iso timestamp",
  "updatedAt": "iso timestamp",
  "userId": "uuid",
  "tagIds": ["tag-id"],
  "permission": "owner",
  "attachmentCount": 0,
  "imagePreviewIds": []
}

Body zum Importieren von Notizen:

{
  "notes": [
    {
      "ref": "external stable reference, max 256 chars",
      "id": "optional uuid",
      "title": "string",
      "content": "stringified Quill Delta JSON",
      "isPinned": false,
      "isArchived": false,
      "isTrashed": false,
      "background": "optional background id",
      "tagNames": ["tag name"],
      "createdAt": "iso timestamp",
      "updatedAt": "iso timestamp"
    }
  ],
  "tags": [{ "name": "tag", "color": "#8B5CF6" }],
  "skipExisting": true
}

Struktur des Importergebnisses:

{
  "results": [
    {
      "ref": "external reference",
      "status": "created | skipped | remapped | failed",
      "noteId": "uuid",
      "warning": "optional string",
      "error": "optional string"
    }
  ],
  "tags": { "created": 0, "reused": 0 }
}

Strukturen für Anhänge-Uploads:

  • Normaler Notiz-Upload: Multipart-Feld file an POST /api/notes/:noteId/attachments.

  • Import-Anhänge-Upload: Multipart-Feld file plus Formularfeld position an POST /api/import/notes/:noteId/attachments.

  • Die Anhänge-Antwort enthält id, noteId, type, originalFilename, mimeType, fileSize, position, uploadedByUserId und createdAt.

Grenzen und Validierung

Grenze für Notizliste:

  • GET /api/notes begrenzt limit auf 1..200.

Bulk-Grenzen:

  • noteIds: maximal 200.

  • tagIds: maximal 50.

Import-Grenzen:

  • Notizen pro Batch: 50.

  • Länge des stringifizierten Delta-Inhalts: 1.000.000 Bytes/Zeichen.

  • Titellänge: 1000.

  • Tags pro Notiz: 50.

  • Tags pro Import-Batch: 500.

  • Tag-Namenslänge: 100.

Anhänge-Grenzen:

  • Maximale Dateigröße: 50 MB.

  • Erlaubte Bilder: image/jpeg, image/png, image/webp, image/gif.

  • Erlaubte Audiodateien: audio/mpeg, audio/wav, audio/mp4, audio/x-m4a, audio/ogg, audio/aac, audio/webm.

  • PDFs, JSON, ZIP und generisches application/octet-stream werden von der aktuellen Quelle abgelehnt.

Von Import erlaubte Hintergrund-IDs:

  • color_red, color_orange, color_yellow, color_green, color_teal, color_blue, color_dark_blue, color_purple, color_pink, color_brown.

  • pattern_dots, pattern_grid, pattern_lines, pattern_waves, pattern_groceries, pattern_music, pattern_travel, pattern_code.

Inhaltsformat

Anchor speichert den Notiz-content als String. Vorhandene Importarbeit bestätigt, dass dies für Rich-Text-Import stringifiziertes Quill-Delta-JSON sein sollte.

Der MCP-Server sollte Markdown-freundliche Tools bereitstellen und Markdown intern in Quill Delta konvertieren. Er kann später auch native Delta-Tools im Expertenmodus bereitstellen.

Empfohlene Konvertierungsrichtlinie:

  • anchor_create_note akzeptiert Markdown, konvertiert in Delta und ruft POST /api/notes auf.

  • anchor_update_note akzeptiert Markdown, konvertiert in Delta und ruft PATCH /api/notes/:id mit optionalem baseVersion auf.

  • anchor_import_notes akzeptiert Markdown oder natives Delta und verarbeitet Batches über POST /api/import/notes.

  • anchor_get_note gibt Rohinhalt plus eine Best-Effort-Text-/Markdown-Projektion für die LLM-Lesbarkeit zurück.

Authentifizierungsmodell

Die Anchor-Quelle verwendet die Extraktion von Bearer-Tokens aus Authorization: Bearer <token>. Der MCP-Sidecar sollte daher zwei Authentifizierungsebenen pflegen:

  • ANCHOR_TOKEN: Token, das anchor-mcp beim Aufruf von Anchor verwendet.

  • ANCHOR_MCP_TOKEN: Token, das vom Tunnel-Client erwartet wird, bevor eine MCP-Anfrage bedient wird.

Der MCP-Server sollte niemals beliebige Aufrufer-Tokens an Anchor weiterleiten.

Quellreferenzen

Primär geprüfte Dateien im Upstream:

  • server/src/notes/controllers/notes.controller.ts

  • server/src/notes/controllers/note-attachments.controller.ts

  • server/src/notes/controllers/note-shares.controller.ts

  • server/src/tags/tags.controller.ts

  • server/src/import-export/import.controller.ts

  • server/src/import-export/export.controller.ts

  • server/src/sync/sync.controller.ts

  • server/src/sync/sync-events.controller.ts

  • server/src/notes/dto/create-note.dto.ts

  • server/src/notes/dto/update-note.dto.ts

  • server/src/import-export/dto/import-notes.dto.ts

  • server/src/import-export/dto/import-attachment.dto.ts

  • server/src/notes/constants/notes.constants.ts

  • server/src/import-export/constants/import.constants.ts

  • server/src/notes/utils/note-transformer.util.ts

  • server/src/notes/utils/attachment-storage.util.ts

MCP-Tools

Phase-1-Lesetools:

  • anchor_list_notes(limit, offset)

  • anchor_search_notes(query, limit)

  • anchor_get_note(note_id)

  • anchor_list_tags()

  • anchor_list_attachments(note_id)

Details zu implementierten Tools:

  • anchor_list_notes unterstützt limit, offset, include_content und tag_id. Da Anchor nur limit-basierte Auflistung bereitstellt, muss offset + limit höchstens 200 betragen.

  • anchor_search_notes unterstützt query, limit, include_content und tag_id.

  • anchor_get_note unterstützt note_id und include_content.

  • anchor_list_tags nimmt keine Eingaben entgegen.

  • anchor_list_attachments gibt nur Metadaten zurück und lädt keine Anhänge-Bytes herunter.

Phase-2-Schreibtools:

  • anchor_create_note(title, markdown)

  • anchor_update_note(note_id, markdown, base_version)

  • anchor_import_notes(notes)

  • anchor_create_tag(name, color)

  • anchor_upload_attachment(note_id, file, filename, mime_type)

Phase-3-Verwaltungstools:

  • anchor_archive_notes(note_ids)

  • anchor_pin_notes(note_ids, is_pinned)

  • anchor_add_tags(note_ids, tag_ids)

  • anchor_export(), wenn der Tunnel-Client ein gestreamtes Archiv verarbeiten kann.

Destruktive Tools vermeiden oder absichern:

  • anchor_delete_note(note_id, confirm) bildet Soft Delete ab und sollte confirm=true erfordern.

  • anchor_permanent_delete_note(note_id, confirm) sollte zunächst weggelassen werden.

  • anchor_delete_tag(tag_id, confirm) sollte zunächst weggelassen werden.

  • Kein rohes, beliebiges HTTP-Proxy-Tool bereitstellen.

Sicherheit

  • ANCHOR_TOKEN nur in der Docker-Stack-Umgebung oder .env speichern; nicht in das Image einbrennen.

  • Ein separates ANCHOR_MCP_TOKEN für Aufrufe vom Tunnel-Client an anchor-mcp hinzufügen.

  • Den MCP-Server nur an das Container-Netzwerk binden; keine Traefik-Labels hinzufügen, außer es ist eine absichtliche Freigabe gewünscht.

  • Tools eng und typisiert halten. Aufrufern nicht erlauben, beliebige Anchor-API-Pfade zu wählen.

  • Anfrage-Metadaten protokollieren, nicht Notizinhalt oder Tokens.

  • Standardmäßig schreibgeschützte Tools verwenden, bis der Tunnel-Authentifizierungspfad verifiziert ist.

  • Explizites confirm=true für Soft-Delete und destruktive Bulk-Aktionen erfordern.

  • Dauerhaftes Löschen verweigern, es sei denn, eine separate Einstellung ENABLE_DANGEROUS_TOOLS=true ist vorhanden.

Implementierungsphasen

  1. Einen minimalen TypeScript-MCP-HTTP-Server erstellen.

  2. Konfiguration aus der Umgebung hinzufügen: ANCHOR_BASE_URL, ANCHOR_TOKEN, ANCHOR_MCP_TOKEN, Host/Port binden.

  3. /healthz für Docker- und Tunnel-Diagnose implementieren.

  4. Einen kleinen Anchor-API-Client mit typisierten Methoden und ohne beliebigen Pfad-Escape-Hatch implementieren.

  5. anchor_list_notes, anchor_search_notes, anchor_get_note und anchor_list_tags implementieren.

  6. Antwort-Formung hinzufügen, die schwere Felder entfernt, sofern nicht explizit angefordert.

  7. Hilfsfunktionen und Tests für die Markdown-zu-Delta-Konvertierung implementieren.

  8. Erstellen/Aktualisieren mit optionaler optimistischer Sperre über baseVersion implementieren.

  9. Import-Batching mit den bekannten Import-Grenzen implementieren.

  10. Anhänge-Upload nur für erlaubte Bilder/Audiodateien implementieren.

  11. Dockerfile und Compose-Beispiel inklusive Tunnel-Client-Platzhalter hinzufügen.

  12. Tests mit gemockten Anchor-Antworten und Validierungsfehlern hinzufügen.

  13. Betriebsdokumentation für Token-Rotation und Anbindung des ChatGPT-Tunnel-Clients hinzufügen.

Offene Fragen

  • Genaues Tunnel-Client-Image, Umgebungsvariablen und Auth-Header-Format.

  • Ob Anchor konfiguriert oder gepatcht werden kann, um PDFs und andere Dateitypen zuzulassen.

  • Ob Notizinhalt als Markdown akzeptiert und in Quill Delta konvertiert werden soll, oder ob der MCP Anchors natives Inhaltsformat direkt bereitstellen soll.

  • Ob der Tunnel-Client binäre Nutzlasten gut genug für Anhänge-Upload und Export-Download durchreichen kann.

  • Ob offset clientseitig simuliert werden sollte, da GET /api/notes nur limit, aber keine Offset-Paginierung bereitstellt.

Empfohlener erster Meilenstein

Einen schreibgeschützten MCP-Server mit anchor_list_notes, anchor_search_notes, anchor_get_note und anchor_list_tags erstellen. Privat im Anchor-Stack hinter dem Tunnel-Client bereitstellen. Erstellen/Aktualisieren/Import erst hinzufügen, nachdem der Lesepfad und das Authentifizierungsmodell verifiziert sind.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for AI agents to read, write, and organize notes in a local-first, human-in-the-loop note-taking app.
    1 npm
    2
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Read-only MCP bridge that exposes secure search and fetch tools over an Obsidian-compatible Markdown vault, enabling ChatGPT to query notes without write access.
    1
    Apache 2.0