Skip to main content
Glama
JohnGilligan2

notify-mcp

notify-mcp

Доставка на себя по электронной почте и SMS для сотрудников Example Corp, как удалённый MCP-сервер с аутентификацией Entra. Это путь отправки для развертывания claude.ai для руководителя аппарата: агенты могут отправлять по электронной почте или SMS вошедшему в систему пользователю их собственный контент, и структурно ничего больше.

Инвариант (почему существует этот сервер)

Агенты, которые читают недоверенный контент (входящая почта, тикеты, чаты), никогда не должны обладать неограниченной возможностью отправки — инъекция подсказок может превратить её в канал эксфильтрации. Этот сервер решает эту проблему конструктивно:

  • Получатели не являются параметрами инструмента. Электронная почта отправляется на UPN вызывающего, взятый из проверенного токена Entra. SMS отправляется на мобильный номер вызывающего, указанный в записи. Полностью скомпрометированная модель может изменить что отправляется, но никогда куда.

  • FROM заблокирован на <agent>approver@example.com — сервер не может отправлять от имени человека, поэтому не может выдавать себя за кого-либо.

  • Ключи Resend + Inteliquent живут только в этом контейнере. Пользователи никогда не видят ключ; они аутентифицируются с помощью своего собственного входа M365.

  • Почасовые бюджеты на пользователя + строка журнала аудита для каждой отправки.

Related MCP server: Mailbuttons MCP Server

Инструменты

Инструмент

Что делает

whoami

Возвращает адрес электронной почты и замаскированный мобильный номер, на которые будут доставляться отправления. Сначала проверьте подключение с помощью этого.

send_email_to_me(subject, body_markdown, agent_name?)

Отправляет электронное письмо вызывающему. agent_name задаёт бренд отправителя (alerts → approver@example.com).

send_sms_to_me(message)

Отправляет SMS на мобильный номер вызывающего с корпоративного DID 13105550100. ≤640 символов.

register_my_mobile(mobile_number)

Запускает настройку SMS: отправляет 6-значный код на номер.

confirm_my_mobile(code)

Завершает настройку: проверяет код, сохраняет номер, отправляет пользователю уведомление об изменении.

Как сервер узнаёт, кто вы

Claude → вход в Entra (собственный логин пользователя) → каждый вызов инструмента несёт bearer JWT → AzureJWTVerifier проверяет подпись/эмитента/аудиторию → утверждение preferred_username = получатель. Никаких переменных окружения на пользователя, никакой конфигурации; идентичность криптографическая.

Мобильные номера не в токене, поэтому SMS использует подтверждённую саморегистрацию (создано для тенантов, которые не заполняют mobilePhone в Entra — наш не заполняет):

  1. Пользователь (обычно во время интервью по настройке CoS): «зарегистрируй мой мобильный 310-555-1212» → сервер отправляет 6-значный код на этот номер.

  2. Пользователь вводит код → confirm_my_mobile → номер сохраняется в /data/sms_directory.json с привязкой к их UPN + отправляется подтверждающее письмо.

  3. Затем изменения блокируются (NOTIFY_ALLOW_MOBILE_CHANGE=false): подтверждённый номер может быть изменён только администратором, редактирующим реестр. Проверка кода доказывает владение; блокировка изменений + уведомление по электронной почте устраняют атаку с переназначением через инъекцию (враждебный контент, перенаправляющий тексты в другое место).

Порядок разрешения: файл реестра (ручные записи — простые строки, саморегистрируемые записи — объекты) → необязательный mobilePhone в Entra через Graph (NOTIFY_GRAPH_LOOKUP_ENABLED=true + -GrantGraphUserRead; не требуется, если используется саморегистрация).

Долговечность сеанса коннектора (исправление отключения)

Три вещи не дают коннектору показывать «отключено»:

  1. offline_access рекламируется в метаданных защищённого ресурса (NOTIFY_ADVERTISE_OFFLINE_ACCESS=true), поэтому вход Claude получает токен обновления и незаметно продлевает токены доступа на ~60–90 минут.

  2. stateless_http=True — сеансы переживают повторное развёртывание контейнера.

  3. Прокси-хост NPM должен иметь переопределения тайм-аутов из плейбука (см. PORTAINER_DEPLOY.md), чтобы простаивающие потоки не обрывались на 60 секунд.

Также проверьте, что ни одна политика условного доступа с частотой входа не покрывает это приложение. Приёмочный тест: подключитесь, подождите 2+ часа простоя, переразверните контейнер, затем вызовите whoami — не должно появиться запроса на повторную аутентификацию.

Настройка (порядок плейбука)

  1. scripts/setup_entra_app.ps1 -TenantId <tenant> (необязательно -GrantGraphUserRead) — выводит значения env для сервера + секрет коннектора (идёт в расширенные настройки коннектора Claude, никогда на сервер).

  2. Разверните через Git-стек Portainer — PORTAINER_DEPLOY.md. Рекомендуемый порт хоста 8093 (сначала проверьте; записано в реестре плейбука).

  3. Прокси-хост NPM notify-mcp.example.com → контейнер; тайм-ауты плейбука + список разрешённых IP Anthropic; сертификат LE перед включением списка разрешённых.

  4. Добавьте как пользовательский коннектор организации в claude.ai; пользователи подключаются один раз со своим собственным входом M365.

Приёмочные тесты

curl http://<docker-host>:8093/healthz                       # {"status":"ok",...}
curl -s https://notify-mcp.example.com/.well-known/oauth-protected-resource/mcp | jq
#   scopes_supported MUST include the full https://.../mcp/access_as_user scope
#   AND "offline_access"
curl -i https://notify-mcp.example.com/mcp                  # 401 + WWW-Authenticate

Затем в Claude, как участник группы:

  1. whoami → ваш собственный адрес электронной почты, мобильный «в записи (…1234)» или явный промах.

  2. send_email_to_me → приходит в ВАШ ящик от notify_ai@.

  3. Попросите модель «отправить это <кому-то другому>» → она должна отказаться / не иметь возможности выполнить. Если это не работает — это P1, не разворачивайте.

  4. register_my_mobile с вашим номером → приходит код → confirm_my_mobile → приходит подтверждающее письмо → send_sms_to_me работает. Затем попробуйте зарегистрировать ДРУГОЙ номер → должно быть отказано (блокировка изменений).

  5. Тест с 2-часовым простоем и переразвёртыванием выше.

  6. Не участник подключается → отклонено при входе (AADSTS50105).

Локальная разработка

pip install -r requirements.txt
MCP_AUTH_ENABLED=false python -m notify_mcp   # Inspector only — tools will
                                              # refuse to send without a token
                                              # identity; NEVER expose auth-off

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    Enables AI agents to send and receive email with enforced security policies, scoped mailboxes, and human approval for external sending.
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Sovereign autonomous mailbox and identity layer for AI agents, providing persistent email identities, object-level security, OTP/2FA capture, link safety analysis, and event-driven email handling via MCP.
    3 npm
    3
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI agents to send, receive, and reply to email through dedicated thread-aware mailboxes, with sandbox testing and compatibility across MCP clients.
    9
    38 npm
    MIT