Skip to main content

Send in-app notifications from Claude Code

Three tools over a small API: send a notification to a user id, list what is unread, mark it read. Useful when an agent runs a job on someone's behalf and should tell them when it is done, or when you want to check what a user is seeing without writing a query.

Claude Code connects to remote MCP servers over HTTP directly. One command registers the server under the name nordva.

Setup

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

01Register the server

claude mcp add --transport http nordva https://mcp.nordva.dev \
  --header "Authorization: Bearer nv_live_..."

The name comes before the URL. By default the server is added for the current project only and stored outside the repository; add --scope user to make it available in every project.

02Or share it with the team without sharing the key

.mcp.json

{
  "mcpServers": {
    "nordva": {
      "type": "http",
      "url": "https://mcp.nordva.dev",
      "headers": {
        "Authorization": "Bearer ${NORDVA_API_KEY}"
      }
    }
  }
}

Claude Code expands ${NORDVA_API_KEY} from the environment, so the file can be committed while each developer keeps their own key in their shell profile.

03Check the connection

Run claude mcp list, or type /mcp inside a session. nordva should show as connected. Then ask Claude to run setup_check: it reports the project, the plan and which features are unlocked.

Then ask Claude Code

  • Notify user 4812 that their import finished, with a link to /imports/77.
  • What unread notifications does user 4812 have?
  • Mark all of user 4812's notifications as read.

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

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

Returned: The notification id, the user id and the time it was created. The user's client sees it the next time it polls the unread endpoint.

Notifications tools and arguments

send_notification

Confirms firstBuilder

Send an in-app notification to one of your users

user_idrequiredstring, max 256Developer-supplied identifier of the recipient.
titlerequiredstring, max 200Notification headline.
bodyrequiredstring, max 2,000Notification body text.
action_urloptionalstring, max 2,048URL the recipient should be sent to on click.
iconoptional"check" | "warning" | "info" | "error"Visual icon hint.

get_unread_notifications

Read-onlyBuilder

List unread notifications

user_idoptionalstring, max 256Filter to a specific recipient.
limitoptionalnumber, 1–100Page size.
sinceoptionalstringReturn only notifications created strictly after this ISO timestamp.

mark_notifications_read

Builder

Mark notifications as read

notification_idoptionalstring, max 128Mark this specific notification id read.
user_idoptionalstring, max 256Restrict bulk mark-read to one recipient.

Behaviour worth knowing

  • All three tools require the Builder plan. Other plans receive PLAN_UPGRADE_REQUIRED.
  • send_notification reaches a real end user, so the server marks it destructive and clients ask before calling it.
  • Delivery is by polling: your front end reads GET /v1/notifications/unread with a publishable key. Nothing is pushed.
  • mark_notifications_read works on one notification id, or on everything for a user id.
  • Notifications older than 90 days are pruned.
  • Tools appear to the model as mcp__nordva__<tool>, which is the form to use in permission allowlists.
  • Read-only tools are marked as such by the server; Claude Code still asks before the first call to each tool unless you allow it.