Skip to main content
Glama
llego
by llego

Anchor MCP

Anchor と同じ Docker Compose スタック内で動作するサイドカーとして、安全な Anchor Notes ツールを ChatGPT に公開する実装計画です。

調査根拠: Anchor の上流リポジトリ ZhFahim/anchor(デフォルトブランチ main、2026-08-20 時点)を確認。Anchor は Nest.js 製バックエンドで、/api/* 配下に認証付き REST エンドポイントを持ちます。

目標

Anchor と並行して MCP サーバーを実行し、外部アシスタントが Anchor のノートの一覧表示・検索・閲覧・作成・更新・インポート・ファイル添付を、Anchor のデータベースやプライベート API を直接公開せずにできるようにします。

Related MCP server: NotesBridge

現在の状況

最初のマイルストーンが実装済みです:

  • POST /mcp での Streamable HTTP MCP エンドポイント。

  • GET /healthz のヘルスチェックエンドポイント。

  • 読み取り専用ツール: anchor_list_notes、anchor_search_notes、anchor_get_note、anchor_list_tags、anchor_list_attachments。

  • ANCHOR_MCP_TOKEN によるオプションの MCP ガード。

  • Anchor API 呼び出しには ANCHOR_TOKEN と ANCHOR_BASE_URL を使用。

  • Dockerfile を含む。

書き込みツールは意図的に未実装です。

開発

NixOS では、Node/npm コマンドに nix-shell を使用します:

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

ローカル実行:

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

MCP エンドポイントは http://localhost:8000/mcp です。ANCHOR_MCP_TOKEN が設定されている場合、呼び出し元は Authorization: Bearer <token> ヘッダーを送信する必要があります。

デプロイモデル

想定するスタックは 3 つのサービスで構成されます:

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

MCP サーバーは Docker ネットワーク上でのみ到達可能にし、外部からのアクセスはトンネルクライアントのみを経由させます。

確認済みの Anchor API サーフェス

以下の全エンドポイントは Authorization: Bearer <token> ヘッダーを要求し、有効なユーザーに関連付けられた Anchor トークンのみがアクセスできます。

ノート:

  • POST /api/notes

  • GET /api/notes?search=<search>&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

タグ:

  • POST /api/tags

  • GET /api/tags

  • GET /api/tags/:id

  • GET /api/tags/:id/notes

  • PATCH /api/tags/:id

  • DELETE /api/tags/:id

添付ファイル:

  • 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

インポート/エクスポート:

  • POST /api/import/notes

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

  • GET /api/export

同期 API:

  • POST /api/sync

  • GET /api/sync/events(Server-Sent Events)

共有:

  • POST /api/notes/:id/shares

  • GET /api/notes/:id/shares

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

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

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

MCP サーバーは、まず notes / tags / attachments / import の各エンドポイントに対応します。同期 API は、競合を意識したオフラインクライアント向けのものであるため、MCP サイドカーでは初期段階では使用しません。

データ構造

ノート作成ボディ:

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

ノート更新ボディは部分更新に加え、楽観的ロック用のオプションフィールドを含みます:

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

Anchor が返すノートの主要フィールド:

{
  "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": []
}

インポートボディ:

{
  "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
}

インポート結果の構造:

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

添付ファイルのアップロード形式:

  • 通常のノート添付: POST /api/notes/:noteId/attachments へのマルチパート file フィールド。

  • インポート時の添付: POST /api/import/notes/:noteId/attachments へのマルチパート file フィールドと position フィールド。

  • 添付ファイルのレスポンスには id、noteId、type、originalFilename、mimeType、size、position、uploadedBy、createdAt が含まれます。

検証と制約

ノート一覧:

  • GET /api/notes の limit は 1 から 200 の範囲に制限されます。

一括操作:

  • noteIds: 最大 200 件。

  • tagIds: 最大 50 件。

インポート:

  • バッチあたりのノート数: 50。

  • 文字列化された Delta の最大長: 1,000,000 文字。

  • タイトル: 1000 文字。

  • ノートあたりのタグ数: 50。

  • インポートバッチあたりのタグ数: 50。

  • タグ名: 100 文字。

添付ファイル:

  • 最大ファイルサイズ: 50 MB。

  • 許可される画像 MIME タイプ: image/jpeg、image/png、image/webp、image/gif。

  • 許可される音声 MIME タイプ: audio/mpeg、audio/wav、audio/x-wav、audio/ogg、audio/aac、audio/mp4。

  • 許可されるドキュメント MIME タイプ: application/pdf、text/plain、application/json。

  • その他のファイルタイプ(例: ZIP、実行ファイル、その他のバイナリ)は Anchor API によって拒否されます。

インポートの制限:

  • バッチあたりのノート数: 最大 50。

  • 文字列化された Delta コンテンツの長さ: 1,000,000 文字。

  • タイトル長: 1000 文字。

  • ノートあたりのタグ数: 50。

  • インポートバッチあたりのタグ数: 50。

  • タグ名の長さ: 100。

添付ファイルの制限:

  • 最大ファイルサイズ: 50 MB。

  • 許可される画像 MIME タイプ: image/jpeg、image/png、image/webp、image/gif。

  • 許可される音声 MIME タイプ: audio/mpeg、audio/wav、audio/mp4、audio/ogg、audio/aac、audio/webm。

  • その他のファイルタイプは現在のソースでは拒否されます。

データモデル

ノート作成ボディ:

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

ノート更新ボディは部分更新に加えて、オプションの楽観ロックを含みます:

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

Anchor が返すノートで重要なフィールド:

{
  "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": []
}

一括インポートのボディ:

{
  "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
}

インポート結果の形状:

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

添付ファイルの形状:

  • 通常のノート添付: POST /api/notes/:noteId/attachments へのマルチパート file。

  • インポート時の添付: POST /api/import/notes/:noteId/attachments へのマルチパート file と position フォームフィールド。

  • 添付ファイルのレスポンスには id、noteId、type、originalFilename、mimeType、fileSize、position、uploadedBy、createdAt が含まれます。

検証と制限

  • ノート一覧の limit は 1..200 にクランプ。

  • 一括操作の noteIds は最大 200 件。

  • 一括操作の tagIds は最大 50 件。

  • インポート時のバッチあたりノート数は最大 200。

  • インポート時のノートあたりタグ数は最大 50。

  • タグ名は最大 100 文字。

  • インポート時のノート内容は、Stringified Quill Delta で最大 1,000,000 文字。

  • タイトルは最大 1000 文字。

  • 添付ファイルは最大 25 MB。

コンテンツ形式

Anchor はノートの content を文字列として保存します。既存のインポート処理により、リッチテキストインポートでは content が文字列化された Quill Delta JSON であることが確認されています。

MCP サーバーは Markdown 入力を受け付け、Anchor に送信する前に内部で Quill Delta に変換します。また、ネイティブ Delta 入力を受け付ける高級モードのツールも公開します。

推奨される変換ポリシー:

  • anchor_create_note は Markdown を受け付け、Delta に変換して POST /api/notes を呼び出す。

  • anchor_update_note は Markdown を受け付け、Delta に変換して PATCH /api/notes/:id を呼び出す(オプションで baseVersion を指定)。

  • anchor_import_notes は Markdown またはネイティブ Delta を受け付け、POST /api/import/notes でバッチ処理する。

  • anchor_get_note は生のコンテンツに加えて、LLM 可読性のためのベストエフォートなテキスト/Markdown 投影を返します。

認証モデル

Anchor は Authorization: Bearer <token> ヘッダーからのベアラートークン抽出を使用します。MCP サイドカーは2つの認証レイヤーを維持します:

  • ANCHOR_TOKEN: anchor-mcp が Anchor を呼び出すときに使用するトークン。

  • ANCHOR_MCP_TOKEN: トンネルクライアントが MCP リクエストを送信する前に提示することが期待されるトークン。

MCP サーバーは、呼び出し元のトークンを Anchor に転送してはなりません。

ソース参照

上流で確認されたファイル:

  • 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/note.constants.ts

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

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

MCP ツール

フェーズ 1 — 読み取り専用(実装済み):

  • anchor_list_notes(limit, offset, tag_id) — ノートを一覧表示します。

  • anchor_search_notes(query, limit) — ノートを検索します。

  • anchor_get_note(note_id) — ノートの内容とメタデータを返します。

  • anchor_list_tags() — タグを一覧表示します。

  • anchor_list_attachments(note_id) — 添付ファイルのメタデータのみを返し、バイトはダウンロードしません。

フェーズ 2 の書き込みツール:

  • 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)

フェーズ 3 の管理ツール:

  • anchor_archive_notes(note_ids)

  • anchor_pin_notes(note_ids, is_pinned)

  • anchor_add_tags(note_ids, tag_ids)

  • anchor_export()(トンネルクライアントがストリームされたアーカイブを処理できる場合)

破壊的ツールはゲート付きで段階的に導入:

  • anchor_delete_note(note_id, confirm) は論理削除にマップされ、confirm=true を要求します。

  • anchor_permanent_delete_note(note_id, confirm) は最初は省略。

  • anchor_delete_tag(tag_id, confirm) は最初は省略。

  • 生の任意 HTTP プロキシツールは公開しないでください。

セキュリティ

  • ANCHOR_TOKEN は、anchor-mcp が Anchor を呼び出すときにのみ使用します。

  • ANCHOR_MCP_TOKEN は、トンネルが MCP サーバーと話すことを許可する別のシークレットです。

  • 読み取り専用ツールを既定とし、破壊的ツールを最初のフェーズでは有効にしないでください。

  • ANCHOR_TOKEN には Anchor ユーザーアカウント専用のスコープを持つトークンを使用し、管理・昇格された認証情報を共有しないでください。

  • ノートの内容を Markdown に変換する際は、インライン画像の data URL を除去してください。これらは LLM コンテキストを膨張させ、外部アシスタントには有用でないためです。

  • プロンプトインジェクションは Anchor のノートコンテンツ経由で発生する可能性があります。読み取りツールはノートを外部コンテンツとして扱い、その中の指示に従わないでください。

実装済みツール

フェーズ 1(読み取り専用):

  • anchor_list_notes(limit, offset, tag_id)

  • anchor_search_notes(query, limit)

  • anchor_get_note(note_id)

  • anchor_list_tags()

  • anchor_list_attachments(note_id)

制限:

  • anchor_list_notes は limit を 1..200 にクランプし、デフォルトは 20 です。offset は 0 以上にクランプされます。

  • anchor_search_notes はクエリを必須とし、limit を 1..200 にクランプします。

  • anchor_get_note は、提供された note_id が数値であることを検証します。

  • anchor_list_tags は入力を受け付けません。

  • anchor_list_attachments はメタデータのみを返し、添付ファイルのバイト列をダウンロードしません。

フェーズ 2 の書き込みツール

  • anchor_create_note(title, markdown) — タイトル付きでノートを作成します。

  • anchor_update_note(note_id, markdown, base_version) — オプションの楽観ロックバージョンでノートを更新します。

  • anchor_import_notes(notes) — 拡張 Markdown 形式のノート配列を受け取り、バッチ処理します。

  • anchor_create_tag(name, color)

  • anchor_upload_attachment(note_id, file, filename, mime_type)

破壊的ツール

破壊的操作はガードを設けます:

  • anchor_delete_note(note_id, confirm) — ソフト削除。confirm=true が必要。

  • anchor_archive_notes(note_ids) — 復元可能。

  • anchor_unarchive_notes(note_ids) — 復元可能。

  • anchor_permanent_delete_note(note_id, confirm) — 最初は省略。

  • anchor_delete_tag(tag_id, confirm) — 最初は省略。

  • 生の任意 HTTP プロキシツールは公開しない。

セキュリティ

  • ANCHOR_TOKEN は anchor-mcp コンテナ内にのみ保存します。

  • トンネル上のすべての MCP リクエストには ANCHOR_MCP_TOKEN が必要です。

  • 実行を制限するツールを公開しないでください。

  • ファイルアップロードは Anchor の許可リストに制限されます(画像、音声、PDF)。実行ファイルやスクリプトは拒否されます。

  • エクスポートはストリーム処理されるため、トンネルクライアントが大きな応答を処理できる場合にのみ公開してください。

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