Anchor MCP
Anchor MCP
План реализации небольшого MCP-сайдкара, который предоставляет безопасные инструменты Anchor Notes для ChatGPT через туннельный клиент, работающий в том же Docker Compose стеке, что и Anchor.
Основа исследования: вышестоящий репозиторий Anchor ZhFahim/anchor, ветка по умолчанию main, проверено 2026-08-20. Anchor — это бэкенд на Nest.js с аутентифицированными REST-эндпоинтами в /api/*.
Цель
Запустить MCP-сервер рядом с Anchor, чтобы внешние ассистенты могли просматривать, искать, читать, создавать, обновлять, импортировать заметки Anchor и прикреплять к ним файлы, не раскрывая напрямую базу данных Anchor или его приватный API.
Related MCP server: NotesBridge
Текущий статус
Первый этап реализован:
Эндпоинт Streamable HTTP MCP на
POST /mcp.Эндпоинт проверки состояния на
GET /healthz.Инструменты Anchor только для чтения:
anchor_list_notes,anchor_search_notes,anchor_get_note,anchor_list_tags,anchor_list_attachments.Опциональная MCP-защита через bearer-токен с использованием
ANCHOR_MCP_TOKEN.Вызовы Anchor API используют
ANCHOR_TOKENиANCHOR_BASE_URL.Dockerfile включён.
Инструменты записи намеренно пока не реализованы.
Разработка
На NixOS используйте nix-shell для команд Node/npm:
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>.
Модель развёртывания
Предполагаемый стек состоит из трёх сервисов:
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-mcpMCP-сервер должен быть доступен только в Docker-сети. Туннельный клиент — единственный внешний мост.
Подтверждённая поверхность Anchor API
Все эндпоинты ниже защищены AuthGuard в Anchor и ожидают Authorization: Bearer <token>. Guard принимает токены Anchor, которые соответствуют активному пользователю.
Заметки:
POST /api/notesGET /api/notes?search=<query>&tagId=<tagId>&limit=<limit>GET /api/notes/:idPATCH /api/notes/:idDELETE /api/notes/:idDELETE /api/notes/:id/permanentPATCH /api/notes/:id/restoreGET /api/notes/trashGET /api/notes/archivePOST /api/notes/bulk/deletePOST /api/notes/bulk/archivePOST /api/notes/bulk/pinPOST /api/notes/bulk/tags
Теги:
POST /api/tagsGET /api/tagsGET /api/tags/:idGET /api/tags/:id/notesPATCH /api/tags/:idDELETE /api/tags/:id
Вложения:
POST /api/notes/:noteId/attachmentsGET /api/notes/:noteId/attachmentsGET /api/notes/:noteId/attachments/:idDELETE /api/notes/:noteId/attachments/:idPATCH /api/notes/:noteId/attachments/reorder
Импорт/экспорт:
POST /api/import/notesPOST /api/import/notes/:noteId/attachmentsGET /api/export
Sync API:
POST /api/syncGET /api/sync/eventsкак server-sent events
Совместный доступ:
POST /api/notes/:id/sharesGET /api/notes/:id/sharesPATCH /api/notes/:id/shares/:shareIdDELETE /api/notes/:id/shares/:shareId
MCP-сервер должен начинать с обычных эндпоинтов заметок/тегов/вложений/импорта. Sync 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 }
}Формы загрузки вложений:
Обычная загрузка вложения: multipart-поле
fileнаPOST /api/notes/:noteId/attachments.Загрузка вложения при импорте: multipart-поле
fileплюс поле формыpositionнаPOST /api/import/notes/:noteId/attachments.Ответ о вложении включает
id,noteId,type,originalFilename,mimeType,fileSize,position,uploadedByUserIdиcreatedAt.
Лимиты и валидация
Лимит списка заметок:
GET /api/notesограничиваетlimitзначениями1..200.
Массовые лимиты:
noteIds: максимум 200.tagIds: максимум 50.
Лимиты импорта:
Заметок в пакете: 50.
Длина строкового содержимого Delta: 1 000 000 байт/символов.
Длина заголовка: 1000.
Тегов на заметку: 50.
Тегов на пакет импорта: 500.
Длина имени тега: 100.
Лимиты вложений:
Максимальный размер файла: 50 МБ.
Разрешённые изображения:
image/jpeg,image/png,image/webp,image/gif.Разрешённый аудио:
audio/mpeg,audio/wav,audio/mp4,audio/x-m4a,audio/ogg,audio/aac,audio/webm.PDF, JSON, ZIP и универсальный
application/octet-streamотклоняются текущим исходным кодом.
Фоновые идентификаторы, разрешённые при импорте:
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.
Формат содержимого
Anchor хранит content заметки как строку. Существующая работа по импорту подтверждает, что для импорта форматированного текста это должен быть строковый JSON Quill Delta.
MCP-сервер должен предоставлять инструменты, дружественные к Markdown, и внутренне преобразовывать Markdown в 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возвращает исходное содержимое плюс проекцию текста/Markdown по мере возможности для читаемости LLM.
Модель аутентификации
Исходный код Anchor извлекает bearer-токен из Authorization: Bearer <token>. Поэтому MCP-сайдкар должен поддерживать два уровня аутентификации:
ANCHOR_TOKEN: токен, которыйanchor-mcpиспользует при вызовах Anchor.ANCHOR_MCP_TOKEN: токен, ожидаемый от туннельного клиента перед обработкой любого MCP-запроса.
MCP-сервер никогда не должен передавать произвольные токены вызывающих сторон в Anchor.
Ссылки на исходный код
Основные проверенные файлы в вышестоящем репозитории:
server/src/notes/controllers/notes.controller.tsserver/src/notes/controllers/note-attachments.controller.tsserver/src/notes/controllers/note-shares.controller.tsserver/src/tags/tags.controller.tsserver/src/import-export/import.controller.tsserver/src/import-export/export.controller.tsserver/src/sync/sync.controller.tsserver/src/sync/sync-events.controller.tsserver/src/notes/dto/create-note.dto.tsserver/src/notes/dto/update-note.dto.tsserver/src/import-export/dto/import-notes.dto.tsserver/src/import-export/dto/import-attachment.dto.tsserver/src/notes/constants/notes.constants.tsserver/src/import-export/constants/import.constants.tsserver/src/notes/utils/note-transformer.util.tsserver/src/notes/utils/attachment-storage.util.ts
Инструменты MCP
Инструменты чтения, фаза 1:
anchor_list_notes(limit, offset)anchor_search_notes(query, limit)anchor_get_note(note_id)anchor_list_tags()anchor_list_attachments(note_id)
Детали реализованных инструментов:
anchor_list_notesподдерживаетlimit,offset,include_contentиtag_id. Поскольку Anchor предоставляет только листинг на основе лимита,offset + limitдолжно быть не более 200.anchor_search_notesподдерживаетquery,limit,include_contentиtag_id.anchor_get_noteподдерживаетnote_idиinclude_content.anchor_list_tagsне принимает входных данных.anchor_list_attachmentsвозвращает только метаданные и не скачивает байты вложений.
Инструменты записи, фаза 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только в окружении Docker-стека или в.env; не встраивайте его в образ.Добавьте отдельный
ANCHOR_MCP_TOKENдля вызовов от туннельного клиента кanchor-mcp.Привяжите MCP-сервер только к контейнерной сети; не добавляйте метки Traefik, если только не планируется намеренное раскрытие.
Держите инструменты узкими и типизированными. Не позволяйте вызывающим сторонам выбирать произвольные пути Anchor API.
Логируйте метаданные запросов, а не содержимое заметок или токены.
По умолчанию используйте инструменты только для чтения, пока путь туннельной аутентификации не будет проверен.
Требуйте явный
confirm=trueдля мягкого удаления и массовых деструктивных действий.Отказывайте в необратимом удалении, если только не задана отдельная настройка
ENABLE_DANGEROUS_TOOLS=true.
Этапы реализации
Создать минимальный TypeScript MCP HTTP-сервер.
Добавить конфигурацию из окружения:
ANCHOR_BASE_URL,ANCHOR_TOKEN,ANCHOR_MCP_TOKEN, хост/порт привязки.Реализовать
/healthzдля диагностики Docker и туннеля.Реализовать небольшой клиент Anchor API с типизированными методами и без произвольного обходного пути по путям.
Реализовать
anchor_list_notes,anchor_search_notes,anchor_get_noteиanchor_list_tags.Добавить формирование ответов, которое отбрасывает тяжёлые поля, если они явно не запрошены.
Реализовать вспомогательные функции преобразования Markdown в Delta и тесты.
Реализовать создание/обновление с опциональной оптимистичной блокировкой через
baseVersion.Реализовать пакетный импорт с учётом известных лимитов импорта.
Реализовать загрузку вложений только для разрешённых изображений/аудио.
Добавить Dockerfile и пример Compose, включая заглушку туннельного клиента.
Добавить тесты с моками ответов Anchor и проверками валидации.
Добавить эксплуатационную документацию по ротации токенов и подключению туннельного клиента ChatGPT.
Открытые вопросы
Точный образ туннельного клиента, переменные окружения и формат заголовка аутентификации.
Можно ли настроить или пропатчить Anchor, чтобы разрешить PDF и другие типы файлов.
Следует ли принимать содержимое заметок в Markdown и преобразовывать в Quill Delta, или MCP должен напрямую предоставлять нативный формат содержимого Anchor.
Сможет ли туннельный клиент достаточно хорошо передавать бинарные полезные нагрузки для загрузки вложений и скачивания экспорта.
Следует ли имитировать
offsetна стороне клиента, посколькуGET /api/notesпредоставляет толькоlimit, а не постраничную навигацию со смещением.
Рекомендуемый первый этап
Создайте MCP-сервер только для чтения с anchor_list_notes, anchor_search_notes, anchor_get_note и anchor_list_tags. Разверните его приватно в стеке Anchor за туннельным клиентом. Добавляйте создание/обновление/импорт только после проверки пути чтения и модели аутентификации.
This server cannot be deployed
Maintenance
Related MCP Connectors
Search, read, and write your Apple Notes from ChatGPT/Claude via a local Mac agent + MCP relay.
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
MCP-native notes and memory for ChatGPT, Claude, and other AI tools.
Google Keep-style notes app with an MCP server for AI agents to read/write notes.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceMCP server for AI agents to read, write, and organize notes in a local-first, human-in-the-loop note-taking app.1 npm2MIT
- AlicenseNot gradedqualityBmaintenanceMCP server enabling ChatGPT to search, read, and write Apple Notes via a local Mac agent with a privacy-preserving relay.MIT
- AlicenseNot gradedqualityBmaintenanceSelf-hosted MCP server for private Obsidian vaults on GitHub, exposing tools to search, read, write, and analyze Markdown notes and their link graph.MIT
- AlicenseNot gradedqualityBmaintenanceRead-only MCP bridge that exposes secure search and fetch tools over an Obsidian-compatible Markdown vault, enabling ChatGPT to query notes without write access.1Apache 2.0