From d3697d5d231a159939c99fd561b8a18fe2aa6846 Mon Sep 17 00:00:00 2001 From: "s.savinel" Date: Mon, 30 Mar 2026 13:58:17 +0200 Subject: [PATCH] feat: add tool index and server entry point with stdio + HTTP transport --- src/index.ts | 88 ++++++++++++++++++++++++++++++++++++++++++++++ src/tools/index.ts | 11 ++++++ 2 files changed, 99 insertions(+) create mode 100644 src/index.ts create mode 100644 src/tools/index.ts diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..cae915d --- /dev/null +++ b/src/index.ts @@ -0,0 +1,88 @@ +#!/usr/bin/env node +/** + * Novu MCP Server + * + * An MCP server providing full coverage of the Novu notification + * infrastructure API. Supports stdio and Streamable HTTP transports. + */ + +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import express from "express"; +import { getApiKey } from "./constants.js"; +import { + registerEventsTools, + registerSubscribersTools, + registerTopicsTools, + registerWorkflowsTools, + registerNotificationsTools, + registerMessagesTools, + registerIntegrationsTools, + registerEnvironmentsTools, + registerTranslationsTools, + registerContextsTools, + registerChannelsTools, +} from "./tools/index.js"; + +// Validate API key early so we fail fast with a clear message +getApiKey(); + +const server = new McpServer({ + name: "novu-mcp-server", + version: "1.0.0", +}); + +// Register all tool domains +registerEventsTools(server); +registerSubscribersTools(server); +registerTopicsTools(server); +registerWorkflowsTools(server); +registerNotificationsTools(server); +registerMessagesTools(server); +registerIntegrationsTools(server); +registerEnvironmentsTools(server); +registerTranslationsTools(server); +registerContextsTools(server); +registerChannelsTools(server); + +async function runStdio(): Promise { + const transport = new StdioServerTransport(); + await server.connect(transport); + console.error("novu-mcp-server running via stdio"); +} + +async function runHttp(): Promise { + const app = express(); + app.use(express.json()); + + app.post("/mcp", async (req, res) => { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + enableJsonResponse: true, + }); + res.on("close", () => { + transport.close(); + }); + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + }); + + const port = parseInt(process.env.PORT || "3000", 10); + app.listen(port, () => { + console.error(`novu-mcp-server running on http://localhost:${port}/mcp`); + }); +} + +const transport = process.env.TRANSPORT || "stdio"; +if (transport === "http") { + runHttp().catch((err) => { + console.error("Fatal:", err); + process.exit(1); + }); +} else { + runStdio().catch((err) => { + console.error("Fatal:", err); + process.exit(1); + }); +} diff --git a/src/tools/index.ts b/src/tools/index.ts new file mode 100644 index 0000000..7c39a45 --- /dev/null +++ b/src/tools/index.ts @@ -0,0 +1,11 @@ +export { registerEventsTools } from "./events.js"; +export { registerSubscribersTools } from "./subscribers.js"; +export { registerTopicsTools } from "./topics.js"; +export { registerWorkflowsTools } from "./workflows.js"; +export { registerNotificationsTools } from "./notifications.js"; +export { registerMessagesTools } from "./messages.js"; +export { registerIntegrationsTools } from "./integrations.js"; +export { registerEnvironmentsTools } from "./environments.js"; +export { registerTranslationsTools } from "./translations.js"; +export { registerContextsTools } from "./contexts.js"; +export { registerChannelsTools } from "./channels.js";