Initial commit
This commit is contained in:
250
index.js
Normal file
250
index.js
Normal file
@@ -0,0 +1,250 @@
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import axios from "axios";
|
||||
import { z } from "zod";
|
||||
import * as dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
const LISTMONK_URL = process.env.LISTMONK_URL || "https://listmonk.infra.mintel.me";
|
||||
const LISTMONK_USERNAME = process.env.LISTMONK_USERNAME || "mmintel";
|
||||
const LISTMONK_PASSWORD = process.env.LISTMONK_PASSWORD || "Tim300493.";
|
||||
const apiClient = axios.create({
|
||||
baseURL: LISTMONK_URL,
|
||||
auth: {
|
||||
username: LISTMONK_USERNAME,
|
||||
password: LISTMONK_PASSWORD,
|
||||
},
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const server = new McpServer({
|
||||
name: "listmonk-mcp",
|
||||
version: "1.0.0",
|
||||
});
|
||||
// GET /api/subscribers
|
||||
server.tool("listmonk_list_subscribers", {
|
||||
query: z.string().optional().describe("Search query (email or name)"),
|
||||
page: z.number().optional().describe("Page number (default 1)"),
|
||||
per_page: z.number().optional().describe("Results per page (default 20)"),
|
||||
}, async ({ query, page, per_page }) => {
|
||||
try {
|
||||
const response = await apiClient.get("/api/subscribers", {
|
||||
params: { query, page, per_page },
|
||||
});
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// GET /api/subscribers/{id}
|
||||
server.tool("listmonk_get_subscriber", { id: z.number().describe("Subscriber ID") }, async ({ id }) => {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/subscribers/${id}`);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// POST /api/subscribers
|
||||
server.tool("listmonk_create_subscriber", {
|
||||
email: z.string().email().describe("Subscriber email address"),
|
||||
name: z.string().describe("Subscriber name"),
|
||||
status: z.enum(["enabled", "disabled", "blocklisted"]).describe("Subscriber status"),
|
||||
lists: z.array(z.number()).describe("Array of list IDs to subscribe to"),
|
||||
attribs: z.record(z.string(), z.any()).optional().describe("Optional JSON attributes"),
|
||||
preconfirm_subscriptions: z.boolean().optional().describe("Mark subscriptions as confirmed (opt-in bypassed)"),
|
||||
}, async ({ email, name, status, lists, attribs, preconfirm_subscriptions }) => {
|
||||
try {
|
||||
const response = await apiClient.post("/api/subscribers", {
|
||||
email,
|
||||
name,
|
||||
status,
|
||||
lists,
|
||||
attribs,
|
||||
preconfirm_subscriptions,
|
||||
});
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// PUT /api/subscribers/{id}
|
||||
server.tool("listmonk_update_subscriber", {
|
||||
id: z.number().describe("Subscriber ID"),
|
||||
email: z.string().email().optional().describe("Subscriber email address"),
|
||||
name: z.string().optional().describe("Subscriber name"),
|
||||
status: z.enum(["enabled", "disabled", "blocklisted"]).optional().describe("Subscriber status"),
|
||||
lists: z.array(z.number()).optional().describe("Array of list IDs to subscribe to"),
|
||||
attribs: z.record(z.string(), z.any()).optional().describe("Optional JSON attributes"),
|
||||
}, async ({ id, email, name, status, lists, attribs }) => {
|
||||
try {
|
||||
const data = {};
|
||||
if (email)
|
||||
data.email = email;
|
||||
if (name)
|
||||
data.name = name;
|
||||
if (status)
|
||||
data.status = status;
|
||||
if (lists)
|
||||
data.lists = lists;
|
||||
if (attribs)
|
||||
data.attribs = attribs;
|
||||
const response = await apiClient.put(`/api/subscribers/${id}`, data);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// GET /api/lists
|
||||
server.tool("listmonk_list_lists", {}, async () => {
|
||||
try {
|
||||
const response = await apiClient.get("/api/lists");
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// POST /api/lists
|
||||
server.tool("listmonk_create_list", {
|
||||
name: z.string().describe("List name"),
|
||||
type: z.enum(["public", "private"]).describe("List type"),
|
||||
optin: z.enum(["single", "double"]).describe("Opt-in type"),
|
||||
description: z.string().optional().describe("Optional description"),
|
||||
tags: z.array(z.string()).optional().describe("Optional tags"),
|
||||
}, async ({ name, type, optin, description, tags }) => {
|
||||
try {
|
||||
const response = await apiClient.post("/api/lists", {
|
||||
name,
|
||||
type,
|
||||
optin,
|
||||
description,
|
||||
tags,
|
||||
});
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// GET /api/campaigns
|
||||
server.tool("listmonk_list_campaigns", {
|
||||
page: z.number().optional().describe("Page number"),
|
||||
per_page: z.number().optional().describe("Results per page"),
|
||||
}, async ({ page, per_page }) => {
|
||||
try {
|
||||
const response = await apiClient.get("/api/campaigns", {
|
||||
params: { page, per_page },
|
||||
});
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// POST /api/campaigns
|
||||
server.tool("listmonk_create_campaign", {
|
||||
name: z.string().describe("Internal campaign name"),
|
||||
subject: z.string().describe("Email subject line"),
|
||||
lists: z.array(z.number()).describe("Array of list IDs to send to"),
|
||||
type: z.enum(["regular", "optin"]).describe("Campaign type"),
|
||||
content_type: z.enum(["richtext", "html", "markdown", "plain"]).describe("Content type"),
|
||||
body: z.string().describe("Email body content"),
|
||||
altbody: z.string().optional().describe("Optional plain text alternative body"),
|
||||
from_email: z.string().optional().describe("From email address"),
|
||||
messenger: z.string().describe("Messenger to use (e.g. 'email')"),
|
||||
template_id: z.number().optional().describe("Template ID to wrap the content"),
|
||||
tags: z.array(z.string()).optional().describe("Optional tags"),
|
||||
}, async (params) => {
|
||||
try {
|
||||
const response = await apiClient.post("/api/campaigns", params);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// POST /api/tx
|
||||
server.tool("listmonk_send_tx", {
|
||||
subscriber_email: z.string().email().describe("Subscriber email to send to"),
|
||||
subscriber_id: z.number().optional().describe("Subscriber ID (optional, alternative to email)"),
|
||||
template_id: z.number().describe("Template ID to use"),
|
||||
data: z.record(z.string(), z.any()).describe("JSON data for template variables"),
|
||||
messenger: z.string().optional().describe("Messenger type (default 'email')"),
|
||||
}, async ({ subscriber_email, subscriber_id, template_id, data, messenger }) => {
|
||||
try {
|
||||
const payload = {
|
||||
template_id,
|
||||
data,
|
||||
messenger: messenger || "email",
|
||||
};
|
||||
if (subscriber_email)
|
||||
payload.subscriber_email = subscriber_email;
|
||||
if (subscriber_id)
|
||||
payload.subscriber_id = subscriber_id;
|
||||
const response = await apiClient.post("/api/tx", payload);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error("Listmonk MCP Server running on stdio");
|
||||
}
|
||||
main().catch((error) => {
|
||||
console.error("Fatal error in main():", error);
|
||||
process.exit(1);
|
||||
});
|
||||
//# sourceMappingURL=index.js.map
|
||||
Reference in New Issue
Block a user