Remote MCP Server on Cloudflare

by novuhq

1 stars
358 downloads
Not rated
GitHub Website

About

The Novu MCP Server is a fully managed Cloudflare Worker that gives AI assistants access to the Novu notification platform. It lets any MCP-compatible client fetch events, manage subscribers, trigger workflows, and adjust preferences directly.

Details

Author
novuhq
GitHub stars
1
Downloads
358
Categories
Cloud Service

- Fetch and filter notifications with full execution logs
- Search and manage subscribers by email, phone, name, or ID
- List, inspect, create, update, and trigger notification workflows
- Read and update subscriber channel preferences per workflow
- View environment configuration and manage provider integrations
- Authenticate via OAuth (Cloud only) or API key (Cloud or self‑hosted)

Setting up with Highlight

This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:

  1. Download and install Highlight from highlightai.com/download
  2. Navigate to the plugins tab and select "Add Custom Plugin"
  3. Configure the plugin with the settings below
    Plugin Name Remote MCP Server on Cloudflare
    Command (node, npx, python, etc.)

    Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.

  4. Enable "Start Automatically" if you want the plugin to start when Highlight launches

From the repository

Point your MCP client to one of two regional endpoints: US (https://mcp.novu.co/) or EU (https://eu.mcp.novu.co/). Authenticate via OAuth (recommended, supported by Cursor, Claude, Windsurf, etc.) or by supplying a Novu API key as a Bearer token. For stdio‑only clients or self‑hosted Novu, use the mcp-remote proxy with an Authorization header.

Claude Desktop / Cursor

Paste into your MCP client config file to install this server.

{
    "mcpServers": {
        "remote mcp server on cloudflare": {
            "novu-mcp-server": {
                "command": "npx",
                "args": [
                    "nx",
                    "dev",
                    "remote-mcp-server"
                ]
            }
        }
    }
}

McpServers

{
    "novu-mcp-server": {
        "command": "npx",
        "args": [
            "nx",
            "dev",
            "remote-mcp-server"
        ]
    }
}

<div align="center">
<a href="https://novu.co?utm_source=github" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/2233092/213641039-220ac15f-f367-4d13-9eaf-56e79433b8c1.png">
Novu Logo
</picture>
</a>
</div>

<br/>

<p align="center">
<a href="https://www.producthunt.com/products/novu">
Product Hunt
</a>
<a href="https://news.ycombinator.com/item?id=38419513">Hacker News</a>
<a href="https://www.npmjs.com/package/@novu/js">
npm downloads
</a>
</p>

<h1 align="center">Novu MCP Server</h1>

<div align="center">
The Model Context Protocol (MCP) server for Novu — bring AI assistants directly into your notification workflows. Manage subscribers, trigger workflows, inspect events, and tune preferences from any MCP-compatible client.
</div>

<p align="center">
<br />
<a href="https://github.novu.co?utm_campaign=gh_org_profile&utm_source=github" rel="dofollow"><strong>Visit our main GitHub Repository »</strong></a>
<br />
</p>

✨ Features

A single MCP server that unlocks your entire Novu workspace for AI agents:

- Notifications — fetch and filter events with full execution logs and delivery status
- Subscribers — search and manage recipients by email, phone, name, or ID
- Workflows — list, inspect, create, update, and trigger notification workflows
- Preferences — read and update subscriber channel preferences (email, SMS, in-app, push, chat)
- Environments — view environments and their configuration
- Integrations — manage provider integrations across channels
- Auth & Identitywhoami verifies your credential (OAuth or API key) and reports the active region

🚀 Quick Start

You don't need to host anything — the server is fully managed. Pick the endpoint
for your Novu Cloud region and point your MCP client at it:

| Region | Endpoint | Novu API |
| --- | --- | --- |
| US | https://mcp.novu.co/ | api.novu.co |
| EU | https://eu.mcp.novu.co/ | eu.api.novu.co |

Each host is a dedicated deployment pinned to its region — there is no ?region=
query param anymore. (For backward compatibility, a ?region= that doesn't match
the host's region returns a 400 pointing you at the correct endpoint.)

Authentication

The server supports two ways to authenticate, and both work identically on either
regional endpoint:

1. OAuth (recommended) — No API key to copy/paste. When your MCP client first
connects, the server responds with a 401 and an OAuth discovery document
(/.well-known/oauth-protected-resource) that points the client at Novu's
authorization server (Clerk). Your client opens the Novu sign-in + consent screen,
you pick an organization, and the client receives an access token automatically.

Any MCP client that supports remote OAuth (Cursor, Claude, ChatGPT, Windsurf, …)
handles this flow for you — just add the server URL with no header.

> Note: the client must request the user:org:read scope (advertised in the
> discovery document) so the Novu API can resolve your organization. If your Novu
> account belongs to multiple organizations, you'll be asked to select one during
> consent.

2. API key — Provide your key from the Novu Dashboard as a bearer token:

Authorization: Bearer <your-novu-api-key>

When you present an API key, the server treats the session as API-key mode and
will not trigger the OAuth sign-in flow — even on the hosted endpoints. The
key is bound to a single environment (and thus region), so no extra configuration
is needed; just connect to the endpoint for the region your account lives in.

> Self-hosted Novu? OAuth is only available for Novu Cloud (US/EU) — the
> flow runs against Novu Cloud's authorization server, which a self-hosted
> deployment has no access to. Self-hosted deployments always authenticate
> with an API key. See Deploying your own instance
> and Local Development.

🎯 Environments

How requests map to a Novu environment depends on how you authenticate:

- API key — the key itself is bound to a single environment; requests always
run against that environment.
- OAuth — the token is bound to your organization, and the Novu API
defaults to the Development environment.

For OAuth sessions, every tool accepts an optional environmentId parameter to
target a specific environment (forwarded to the Novu API as the
Novu-Environment-Id header). Call get_environments first to list your
environments, then pass the _id of the one you want — for example, to inspect
Production notifications. The Novu API validates that the environment belongs
to your organization. With an API key the parameter is ignored — the key
already pins the environment.

Local / self-hosted API — to point this MCP server at a Novu API running
elsewhere (for example a self-hosted instance on http://localhost:3000), set
NOVU_API_URL in .dev.vars and run the server locally (see
Local Development). This replaces the old ?region=local
query param. Self-hosted always uses an API key — OAuth is Novu Cloud only.

🛠️ Usage

The server speaks the Streamable HTTP MCP transport at https://mcp.novu.co/
(US) and https://eu.mcp.novu.co/ (EU).

Cursor, Windsurf, Claude & other OAuth-capable clients

Any client that supports remote MCP servers with OAuth can connect with no header
the client runs the sign-in flow for you:

- URL (US): https://mcp.novu.co/
- URL (EU): https://eu.mcp.novu.co/

On first connection the client opens the Novu sign-in + consent screen. Approve it,
select your organization, and the tools appear automatically.

API key (mcp-remote / stdio clients, self-hosted Novu)

For clients that only support stdio transports, if you prefer a static API key,
or if you run a self-hosted Novu instance (where OAuth is not available),
use the mcp-remote proxy with an
Authorization header. Presenting an API key keeps the client from launching the
OAuth flow:

{
  "mcpServers": {
    "novu": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://mcp.novu.co/",
        "--header",
        "Authorization:Bearer your-novu-api-key"
      ]
    }
  }
}

For the EU region, swap the URL for https://eu.mcp.novu.co/.

📦 Available Tools

| Tool | Description |
| ---- | ----------- |
| whoami | Show who is authenticated (verifies the credential against the Novu API) and the active region |
| get_environments | List all environments with their details and API keys |
| get_notifications | Fetch events with filtering by channel, template, subscriber, date, and more |
| get_notification | Get a specific notification with detailed execution logs |
| find_subscribers | Search subscribers by email, name, phone, or ID |
| get_subscriber_preferences | Get a subscriber's preferences across all channels and workflows |
| update_subscriber_preferences | Update a subscriber's channel preferences globally or per workflow |
| get_workflows | List all workflows with their basic information |
| get_workflow | Get a workflow's full definition, steps, and payload schema |
| trigger_workflow | Trigger a workflow for a subscriber with a custom payload |
| get_integrations | List configured provider integrations across channels |

💻 Local Development

Prerequisites: Node.js 20+ and pnpm.

# Clone and install
git clone https://github.com/novuhq/novu-mcp-server.git
cd novu-mcp-server
pnpm install

Start the local worker

pnpm dev

The server runs at http://localhost:8787. Point your MCP client at it the same way you would the hosted version:

{
  "mcpServers": {
    "novu": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://localhost:8787/",
        "--header",
        "Authorization:Bearer your-novu-api-key"
      ]
    }
  }
}

Configuration is read from .dev.vars (gitignored) — copy
.dev.vars.example to get started. The key variables are:

- NOVU_API_URL — the Novu API this server proxies to (e.g. http://localhost:3000
for a self-hosted API, or https://api.novu.co / https://eu.api.novu.co for cloud).
- NOVU_REGION — the display label surfaced by whoami.
- CLERK_OAUTH_ISSUER — the Clerk authorization server for OAuth. Leave empty to
disable OAuth entirely and run API-key-only (the self-hosted mode).

For local development against a self-hosted Novu API on http://localhost:3000,
set NOVU_API_URL="http://localhost:3000" in .dev.vars and use your instance's
API key (OAuth is Novu Cloud only):

{
  "mcpServers": {
    "novu-local": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://localhost:8787/",
        "--header",
        "Authorization:Bearer your-local-novu-api-key"
      ]
    }
  }
}

OAuth in local development

To exercise OAuth locally, set CLERK_OAUTH_ISSUER in .dev.vars to a Clerk
authorization server your Novu API trusts (e.g. https://clerk.dashboard.novu.co
for US). The MCP endpoint origin is derived from the request URL, so run the dev
server and point your MCP client at the same URL (e.g. http://localhost:8787/).
RFC 9728 requires the PRM resource field to exactly match the MCP endpoint URL;
connecting via a different host/port than the server is bound to causes clients
like Cursor to discard the metadata and register without the user:org:read scope.

When CLERK_OAUTH_ISSUER is empty, the OAuth discovery endpoints return 404 and
401 responses omit the OAuth metadata, so clients fall back to API-key auth.

Deploying your own instance

The server is a standard Cloudflare Worker. wrangler.jsonc is organized so the
top-level config is local-dev-only (no routes, so wrangler dev serves on
localhost and OAuth discovery advertises the localhost origin), while real
deployments live under named environments:

- pnpm deploy--env us (binds mcp.novu.co, NOVU_API_URL=https://api.novu.co)
- pnpm deploy:eu--env eu (binds eu.mcp.novu.co, NOVU_API_URL=https://eu.api.novu.co)

To deploy your own instance, fork the repo and add an environment under env
(or edit an existing one) with your own routes, NOVU_API_URL, and
NOVU_REGION, then deploy it with wrangler deploy --env <name>. A self-deployed
instance works out of the box with API-key authentication against whatever
NOVU_API_URL points at — including a self-hosted Novu API. OAuth on your own
deployment requires setting the CLERK_OAUTH_ISSUER secret
(wrangler secret put CLERK_OAUTH_ISSUER --env <name>) to an authorization
server that your Novu API trusts; leave it unset for API-key-only.

Scripts

- pnpm dev — Run the worker locally via Wrangler (top-level, route-free config)
- pnpm deploy — Deploy the US worker (mcp.novu.co, --env us)
- pnpm deploy:eu — Deploy the EU worker (eu.mcp.novu.co, --env eu)
- pnpm type-check — Run TypeScript type checking
- pnpm lint:fix — Fix linter issues with Biome
- pnpm format — Format the codebase with Biome

Project structure

src/
├── index.ts            # Worker entry — auth extraction and routing
├── oauth.ts            # OAuth discovery, 401 bootstrap, initialize-time probe
├── server/NovuMCP.ts   # Durable Object hosting the MCP agent
├── tools/              # One file per tool group (workflows, subscribers, …)
├── utils/              # API client, validation, tool factory
└── types/              # Shared TypeScript types

Add new tools by creating a register*Tools function under src/tools/ and wiring it in src/server/NovuMCP.ts.

🔒 Security

- The server is a pure OAuth pass-through: it does not mint, exchange, or
re-sign tokens. It never validates tokens itself — it advertises Novu's Clerk
authorization server and forwards the caller's Authorization header verbatim
to the Novu API, which validates it and resolves the org/permissions.
- OAuth access tokens (Clerk opaque oat_… tokens) are short-lived and revocable
from the Novu side, so they are far safer than a long-lived API key.
- Whether OAuth token or legacy API key, the credential is scoped to your MCP
session: it is handed to the session's Durable Object via the runtime's props
channel — never placed in URLs, where it would leak into request logs — and is
discarded with the session. The server holds no ambient credentials.
- Never commit API keys or issuer config. Use .dev.vars for local values
(already gitignored).
- Treat your Novu API key like a password — rotate it from the dashboard if you
suspect it has been exposed.

🤝 Contributing

1. Make Changes

   git checkout -b feat/your-change
   pnpm dev           # Test locally
   pnpm type-check    # Verify types
   git commit -m "feat: your change"
   git push origin feat/your-change
   

2. Open a Pull Request
- Use a descriptive title with feat:, fix:, docs:, or chore: prefix
- Include a short description of the change and, where relevant, a sample tool call

Guidelines:

- Keep tool descriptions concise — they are surfaced verbatim to LLMs
- Validate inputs with Zod schemas in src/utils/
- Prefer the ToolFactory helpers for standard CRUD endpoints
- Something missing? Open a GitHub issue

Need help? Email us at support@novu.co or join the Discord.

---

Thank you for contributing! 🙏

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.