Skip to main content
20% off 3 months · code LAUNCH20 · until 31 October

Send in-app notifications from Claude Desktop

Every notification operation your secret key can do, as tools: send to a user id or broadcast to everyone, list what was sent with how many people read it, see what one user's bell shows, mark it read, and create the signing secret that lets your pages prove which user is asking. Useful when an agent runs a job on someone's behalf and should tell them when it is done, or when you ship something everyone should hear about.

Claude Desktop's config file starts local processes, so a remote server with a header is reached through the mcp-remote bridge. Node.js 18 or later must be installed.

Setup

You need a secret key (nv_live_…) from Dashboard → API keys.

01Add the bridge to the config file

claude_desktop_config.json (Settings → Developer → Edit Config)

{
  "mcpServers": {
    "nordva": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.nordva.dev",
        "--header",
        "Authorization:${NORDVA_AUTH}"
      ],
      "env": {
        "NORDVA_AUTH": "Bearer nv_live_..."
      }
    }
  }
}

The header value is passed through an environment variable because some platforms split arguments that contain a space. On macOS the file is in ~/Library/Application Support/Claude/, on Windows in %APPDATA%\Claude\.

02Check the connection

Quit and reopen Claude Desktop. The nordva tools should appear in the tools menu of a new chat. Ask it to run setup_check.

Then ask Claude Desktop

  • Notify user 4812 that their import finished, with a link to /imports/77.
  • Tell everyone about tonight's planned downtime, 22:00 to 22:30 UTC.
  • How many people read last week's broadcast?
  • Set up identity verification for the notifications bell.

For “Notify user 4812 that their import finished, with a link to /imports/77.” Claude Desktop calls send_notification with:

{
  "user_id": "4812",
  "title": "Import finished",
  "body": "1,204 rows imported, 3 skipped.",
  "action_url": "/imports/77"
}

Returned: The notification id, the user id, the time it was created and this month's usage against the cap. The user's bell shows it the next time it checks.

Notifications tools and arguments

send_notification

Confirms first

Send an in-app notification to one user or to everyone

user_idoptionalstring, max 256The developer's own id for one recipient.
audienceoptional"user" | "users" | "all""all" for a broadcast to every signed-in user; "users" is implied by the group fields.
user_idsoptionalarrayThe developer's own user ids.
emailsoptionalarrayPeople by email; each is reached through their known user id.
contact_idsoptionalarrayContacts by id (from list_contacts).
contactsoptionalobjectA Contacts filter, values as strings: { "stage": "customer" }, { "consent": "email_marketing" }, { "detail": "plan=pro" }, { "paying": "true" }.
titlerequiredstring, max 200Notification headline.
bodyrequiredstring, max 2,000Notification body, plain text.
action_urloptionalstring, max 2,048A full http(s) URL, or a path such as /billing.
iconoptional"check" | "warning" | "info" | "error"Visual icon hint.
categoryoptionalstringA short lower-case tab name, such as billing.
actionsoptionalarrayUp to two buttons.
dataoptionalobjectA small JSON object for the developer's page.
send_atoptionalstringISO time to show it, at most 60 days ahead.
pinnedoptionalbooleanKeep it at the top of the bell until each person archives it.
reactionsoptionaluniontrue: the project's changelog reaction set; or the names in order.

preview_notification_audience

Read-only

See who a group notification would reach

user_idsoptionalarrayThe developer's own user ids.
emailsoptionalarrayPeople by email; each is reached through their known user id.
contact_idsoptionalarrayContacts by id (from list_contacts).
contactsoptionalobjectA Contacts filter, values as strings: { "stage": "customer" }, { "consent": "email_marketing" }, { "detail": "plan=pro" }, { "paying": "true" }.

list_notifications

Read-only

List sent notifications with reach and reads

user_idoptionalstring, max 256
audienceoptional"user" | "users" | "all"
statusoptional"read" | "unread" | "all" | "scheduled"
categoryoptionalstring, max 24
limitoptionalnumber, 1–100
cursoroptionalstring, max 64

get_notification

Read-only

Read one sent notification

notification_idrequiredstring, max 64The ntf_... id.

delete_notification

Confirms first

Delete a sent notification

notification_idrequiredstring, max 64The ntf_... id.

get_unread_notifications

Read-only

See what a user's bell shows

user_idoptionalstring, max 256The person whose bell to read.
include_readoptionalbooleantrue for read and unread (the inbox).
limitoptionalnumber, 1–100Page size.
sinceoptionalstringOnly notifications created after this ISO timestamp.
categoryoptionalstringWith include_read: one category (a tab in the bell).
archivedoptionalbooleanWith include_read: true for only the archived ones.

mark_notifications_read

Mark notifications read for a user

notification_idoptionalstring, max 128Mark this one read.
user_idoptionalstring, max 256The person who read it. Required for a broadcast or a group.
categoryoptionalstringWithout notification_id: only this category.

archive_notification

Archive a notification for one user

notification_idrequiredstring, max 64The ntf_... id.
user_idrequiredstring, max 256
archivedoptionalbooleanfalse brings it back.

record_notification_click

Record a click on a notification

notification_idrequiredstring, max 64The ntf_... id.
user_idrequiredstring, max 256

set_notification_reaction

Set a user's reaction to a notification

notification_idrequiredstring, max 64The ntf_... id.
user_idrequiredstring, max 256
reactionrequired"like" | "celebrate" | "love" | "laugh" | "fire" | "eyes" | "confused" | "dislike"One the notification offers, or null to take it back.

get_notification_settings

Read-only

Notification settings, usage and warnings

No arguments.

update_notification_settings

Change notification settings

require_user_hashoptionalbooleanBrowser calls must prove their user with user_hash.
changelog_to_bell_defaultoptionalbooleanNew changelog entries post to the bell unless they say otherwise.

create_notifications_signing_secret

Create the notifications signing secret (shown once)

No arguments.

rotate_notifications_signing_secret

Confirms first

Rotate the notifications signing secret (new one shown once)

No arguments.

Behaviour worth knowing

  • Every plan can send notifications, capped per project per calendar month: Free 1,000, Indie 20,000, Builder 200,000. A broadcast counts once.
  • send_notification reaches real people, so the server marks it destructive and clients ask before calling it. With audience "all" it is one broadcast that shows in every signed-in user's bell for 30 days.
  • list_notifications shows, for a broadcast, how many people it reached (their bell checked after it was sent) and how many read it.
  • Your pages read the bell with the publishable key, the user's id and a user_hash your server computes with the signing secret, so one browser cannot read another user's notifications.
  • create_notifications_signing_secret and rotate_notifications_signing_secret return the secret once, in the text only; the agent is told to write it to your server's env file and never to the browser.
  • publish_changelog_entry with notify_in_app posts the entry to the bell once when it publishes.
  • Notifications older than 90 days are pruned.
  • The Connectors screen in Claude Desktop and claude.ai expects OAuth. The Nordva Launch server authenticates with an API key header instead, which is why the bridge is needed there.