Novu MCP Server
An MCP (Model Context Protocol) server that provides full coverage of the Novu notification infrastructure API. Enables LLMs to manage subscribers, trigger notifications, author workflows and email layouts, localize content, and more through 116 tools across 13 API domains.
Features
- Full API coverage: 116 tools spanning Events, Subscribers, Topics, Workflows, Layouts, Notifications, Messages, Integrations, Environments, Environment Variables, Translations, Contexts, and Channels
- Current v2 API: Workflows, Layouts, Translations, Subscribers, Contexts and Topics target Novu's v2 endpoints
- Dual transport: stdio (for local use with Claude Code, OpenCode, etc.) and Streamable HTTP (for remote/multi-client use)
- Pagination support: Offset-based (v1) and cursor-based (v2) pagination
- Error handling: Actionable error messages with HTTP status-specific guidance
- Response truncation: Automatic truncation of large responses with pagination hints
Prerequisites
Installation
git clone <this-repo>
cd novu-mcp-server
npm install
npm run build
Configuration
| Environment Variable |
Required |
Default |
Description |
NOVU_SECRET_KEY |
Yes |
- |
Your Novu API key from the dashboard |
NOVU_API_URL |
No |
https://api.novu.co |
API base URL. Use https://eu.api.novu.co for EU region |
TRANSPORT |
No |
stdio |
Transport mode: stdio or http |
PORT |
No |
3000 |
HTTP server port (only used when TRANSPORT=http) |
Usage
With Claude Code / OpenCode (stdio)
Add to your MCP configuration:
{
"mcpServers": {
"novu": {
"command": "node",
"args": ["/path/to/novu-mcp-server/dist/index.js"],
"env": {
"NOVU_SECRET_KEY": "your-api-key-here"
}
}
}
}
With HTTP transport
NOVU_SECRET_KEY=your-key TRANSPORT=http PORT=3000 node dist/index.js
Then connect your MCP client to http://localhost:3000/mcp.
Available Tools (110)
Events (4 tools)
| Tool |
Description |
novu_trigger_event |
Trigger a notification workflow for specific subscribers |
novu_bulk_trigger_event |
Trigger multiple notification events in a single request (max 100) |
novu_broadcast_event |
Broadcast a notification to all subscribers |
novu_cancel_event |
Cancel a pending triggered event by transaction ID |
Subscribers (29 tools)
Core (v2, except bulk create / online-status / credentials which remain v1):
| Tool |
Description |
novu_create_subscriber |
Create a new subscriber (optional fail_if_exists) |
novu_get_subscriber |
Retrieve a subscriber by ID |
novu_update_subscriber |
Update subscriber attributes |
novu_delete_subscriber |
Delete a subscriber |
novu_search_subscribers |
Search/list subscribers (cursor pagination, filters) |
novu_bulk_create_subscribers |
Create multiple subscribers at once (max 500) |
novu_get_subscriber_preferences |
Get notification preferences for a subscriber |
novu_update_subscriber_preferences |
Update notification preferences |
novu_bulk_update_subscriber_preferences |
Bulk update preferences (max 100) |
novu_get_subscriber_subscriptions |
Get topic subscriptions for a subscriber |
novu_update_subscriber_online_status |
Set subscriber online/offline status |
novu_update_provider_credentials |
Replace push/chat provider credentials |
novu_upsert_provider_credentials |
Append push/chat provider credentials |
novu_delete_provider_credentials |
Delete provider credentials |
Inbox notification actions (v2):
| Tool |
Description |
novu_get_subscriber_notifications |
List inbox notifications, with filters and pagination |
novu_get_subscriber_notifications_count |
Count inbox notifications matching filters |
novu_mark_notification_as_read |
Mark one notification as read |
novu_mark_notification_as_unread |
Mark one notification as unread |
novu_mark_all_notifications_as_read |
Mark all matching notifications as read |
novu_mark_notifications_as_seen |
Mark notifications as seen |
novu_snooze_notification |
Snooze a notification until a time |
novu_unsnooze_notification |
Cancel a snooze |
novu_archive_notification |
Archive a notification |
novu_unarchive_notification |
Unarchive a notification |
novu_archive_all_notifications |
Archive all matching notifications |
novu_archive_all_read_notifications |
Archive all read notifications |
novu_delete_notification |
Delete a notification |
novu_delete_all_notifications |
Delete all matching notifications |
novu_complete_notification_action |
Mark a primary/secondary action complete |
novu_revert_notification_action |
Revert a primary/secondary action |
Topics (11 tools)
| Tool |
Description |
novu_create_topic |
Create a new topic |
novu_get_topic |
Retrieve a topic by key |
novu_update_topic |
Update a topic's name |
novu_delete_topic |
Delete a topic |
novu_list_topics |
List all topics (cursor-based pagination) |
novu_create_topic_subscriptions |
Subscribe subscribers to a topic |
novu_delete_topic_subscriptions |
Unsubscribe subscribers from a topic |
novu_update_topic_subscription |
Update a topic subscription |
novu_list_topic_subscriptions |
List subscribers of a topic |
novu_check_topic_subscriber |
Check if a subscriber belongs to a topic |
novu_get_topic_subscription |
Get subscription details |
Workflows (11 tools, v2)
| Tool |
Description |
novu_create_workflow |
Create a workflow with typed steps + control values (v2: origin: novu-cloud) |
novu_get_workflow |
Retrieve a workflow by ID |
novu_update_workflow |
Update a workflow (fetch-merge-PUT, step identity preserved) |
novu_update_workflow_payload_schema |
Replace a workflow's payloadSchema without touching steps |
novu_update_workflow_step |
Merge control values into one step, preserving its identity |
novu_delete_workflow |
Delete a workflow |
novu_list_workflows |
List workflows (filter by tag/status, sort) |
novu_list_notification_groups |
List notification groups and their IDs (legacy v1 endpoints only) |
novu_sync_workflow |
Sync a workflow to a target environment |
novu_get_workflow_step |
Get details of a specific workflow step |
novu_generate_workflow_step_preview |
Render a step preview from control values + payload |
Layouts (8 tools, v2)
Reusable email wrappers ("templates") that email steps render inside.
| Tool |
Description |
novu_list_layouts |
List email layouts |
novu_get_layout |
Retrieve a layout by ID |
novu_create_layout |
Create an email layout |
novu_update_layout |
Update a layout's name / email body / editor type |
novu_delete_layout |
Delete a layout |
novu_duplicate_layout |
Duplicate an existing layout |
novu_generate_layout_preview |
Render a layout preview |
novu_get_layout_usage |
List workflows that use a layout |
Step identity. The v2 workflow PUT is a full replace of the step list, and Novu matches an
incoming step to an existing one by _id. A step sent without _id is deleted and recreated with
a fresh _id, stepId and slug, which silently breaks trigger-time
overrides.steps['<stepId>']. These tools always echo identity back, and
novu_update_workflow_step fails loudly if the step is recreated anyway.
Payload schemas. When a template starts referencing new payload fields, move the schema with it
via novu_update_workflow_payload_schema. With validatePayload enabled, a template and schema
that disagree fail the trigger with 400 PAYLOAD_VALIDATION_ERROR in both directions.
Notifications (2 tools)
| Tool |
Description |
novu_list_notification_events |
List notification events with filters |
novu_get_notification_event |
Get detailed notification event with execution logs |
Messages (3 tools)
| Tool |
Description |
novu_list_messages |
List messages with filters |
novu_delete_message |
Delete a specific message |
novu_delete_messages_by_transaction |
Delete all messages for a transaction |
Integrations (8 tools)
| Tool |
Description |
novu_list_integrations |
List all integrations |
novu_list_active_integrations |
List only active integrations |
novu_create_integration |
Create a new provider integration |
novu_update_integration |
Update an integration |
novu_delete_integration |
Delete an integration |
novu_set_primary_integration |
Set an integration as primary for its channel |
novu_generate_chat_oauth_url |
Generate OAuth URL for chat integrations (deprecated) |
novu_auto_configure_integration |
Auto-configure a provider for inbound webhooks |
Environments (7 tools)
| Tool |
Description |
novu_list_environments |
List all environments |
novu_create_environment |
Create a new environment |
novu_update_environment |
Update an environment |
novu_delete_environment |
Delete an environment |
novu_list_environment_tags |
List tags across environments |
novu_compare_environment_resources |
Diff resources between two environments |
novu_publish_environment_resources |
Publish resources to a target environment |
Environment Variables (6 tools)
| Tool |
Description |
novu_list_environment_variables |
List environment variables (secrets masked) |
novu_create_environment_variable |
Create a {{env.KEY}} variable |
novu_get_environment_variable |
Retrieve a variable by key |
novu_update_environment_variable |
Update a variable |
novu_delete_environment_variable |
Delete a variable |
novu_get_environment_variable_usage |
List workflows referencing a variable |
Translations (11 tools, v2)
Localize workflow/layout content per subscriber locale (beta; Team/Enterprise plan). Keyed by resource_type + resource_id + locale.
| Tool |
Description |
novu_create_translation |
Create/update a translation for a resource + locale |
novu_get_translation |
Retrieve a translation |
novu_delete_translation |
Delete a translation (one locale) |
novu_get_translation_group |
Get a resource's translation group (all locales) |
novu_delete_translation_group |
Delete a resource's entire translation group |
novu_import_master_translations |
Import master JSON for a locale |
novu_get_master_translations |
Export master JSON for a locale |
novu_upload_translation_files |
Upload locale JSON files for a resource |
novu_upload_master_translations_file |
Upload a master JSON file (locale from filename) |
novu_upsert_workflow_translation |
Write keys for one locale of a workflow (merges by default) |
novu_upsert_layout_translation |
Write keys for one locale of a layout (merges by default) |
Translation writes merge by default. POST /v2/translations is a full replace: a call carrying
a subset of keys wipes every other key for that locale. The two upsert_*_translation tools
therefore read the locale first and deep-merge the supplied keys on top. Pass mode: "replace" to
get the raw overwrite. Both modes return keysBefore / keysAfter / keysAdded / keysRemoved,
plus a warning when anything was removed, so unintended loss is visible in the tool result.
novu_create_translation is the unguarded passthrough and still replaces the whole locale.
Contexts (5 tools, v2)
Keyed by type + id.
| Tool |
Description |
novu_list_contexts |
List all contexts |
novu_create_context |
Create a new context (type + id + data) |
novu_get_context |
Retrieve a context by type + id |
novu_update_context |
Update a context's data |
novu_delete_context |
Delete a context |
Channels (10 tools)
| Tool |
Description |
novu_list_channel_connections |
List channel connections |
novu_create_channel_connection |
Create a channel connection |
novu_get_channel_connection |
Get a channel connection |
novu_update_channel_connection |
Update a channel connection |
novu_delete_channel_connection |
Delete a channel connection |
novu_list_channel_endpoints |
List channel endpoints |
novu_create_channel_endpoint |
Create a channel endpoint |
novu_get_channel_endpoint |
Get a channel endpoint |
novu_update_channel_endpoint |
Update a channel endpoint |
novu_delete_channel_endpoint |
Delete a channel endpoint |
Usage notes & gotchas
Learned authoring the Monizze card-workflow communications end to end. These are
API behaviours, not bugs in the server — worth knowing before you script against it.
Workflows (v2)
- The
workflow_id you pass becomes the trigger identifier verbatim (e.g.
payment-received). Choose it deliberately; the backend triggers on it.
- Steps are
{ name, type, control_values }. Control shapes by type:
- email:
{ subject, body, editorType: "html" | "block", layoutId, replyTo, from, preheader }.
Pass editorType: "html" to store raw HTML in body; otherwise it's a Maily block doc.
layoutId is the layout slug (e.g. technical-generic), not the _id.
- delay:
{ type: "regular", amount, unit, extendToSchedule }.
- in_app:
{ subject, body, avatar, primaryAction: { label, redirect: { url } } }.
A primaryAction requires a label — you can't attach a redirect URL without one.
- push:
{ subject, body }.
is_translation_enabled: true must be set for {{t.*}} to resolve per locale.
- Update is a full replace (PUT): fetch, modify, send the whole definition.
Layouts (v2)
POST /v2/layouts ignores controlValues — a created layout gets a default
empty block-editor body. To store an HTML layout body you must PUT after create
with { controlValues: { email: { body, editorType: "html" } } }. Create the shell,
then PUT the body.
layoutId (slug) is what workflow email steps reference; GET /v2/layouts lists both
slug and _id.
- Layout bodies must contain the
{{content}} variable where the workflow content injects.
Translations (v2)
- Keyed by
(resourceType: workflow | layout, resourceId: slug, locale) — there is no
standalone "translation group" to create; the resource is the group.
- The target workflow/layout must exist first (
resolve workflow … cannot be found
otherwise).
- Beta feature — requires the Team/Enterprise plan. On other plans the endpoints 404.
create_translation is upsert (create or replace) per locale; import_master_translations
loads many resources at once from master JSON.
Auth / region
- Auth header is
Authorization: ApiKey <NOVU_SECRET_KEY>.
- Monizze is on the EU region → set
NOVU_API_URL=https://eu.api.novu.co.
Development
# Watch mode with auto-reload
npm run dev
# Build
npm run build
# Run the test suite (vitest; no live API calls)
npm test
# End-to-end tests against a real environment.
# Use a NON-PRODUCTION key. The suite creates and deletes its own
# `zz-mcp-test-*` workflow and never touches an existing one.
# Skipped automatically unless NOVU_MCP_INTEGRATION=1 is set.
NOVU_API_URL=https://eu.api.novu.co NOVU_SECRET_KEY=your-dev-key npm run test:integration
# Test with MCP Inspector
NOVU_SECRET_KEY=your-key npx @modelcontextprotocol/inspector node dist/index.js
License
MIT