#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
const READ_SCENARIO_BLUEPRINT_TOOL: Tool = {
name: "read_make_dot_com_scenario_blueprint",
description:
"Reads the JSON blueprint of a Make.com scenario. Returns the complete blueprint structure including flow, connections, and metadata.",
inputSchema: {
type: "object",
properties: {
scenario_id: {
type: "number",
description: "Scenario ID to retrieve the blueprint for",
},
draft: {
type: "boolean",
description:
"If true, retrieves the draft version. If false, retrieves the live version.",
default: false,
},
},
required: ["scenario_id"],
},
};
const LIST_SCENARIOS_TOOL: Tool = {
name: "list_make_dot_com_scenarios",
description: "Lists all available Make.com scenarios with their IDs, names, and scheduling types.",
inputSchema: {
type: "object",
properties: {},
},
};
const DESCRIBE_MAKE_MODULE_TOOL: Tool = {
name: "describe_make_dot_com_module",
description: "Describes the configuration and parameters of a module inside a Make.com scenario.",
inputSchema: {
type: "object",
properties: {
scenario_id: {
type: "number",
description: "ID of the scenario containing the module",
},
module_id: {
type: "number",
description: "ID of the module to describe",
},
draft: {
type: "boolean",
description: "If true, retrieve from the draft version",
default: false,
},
},
required: ["scenario_id", "module_id"],
},
};
const CREATE_SCENARIO_TOOL: Tool = {
name: "create_make_dot_com_scenario",
description: "Creates a new Make.com scenario with optional folder and description",
inputSchema: {
type: "object",
properties: {
name: { type: "string", description: "Name of the new scenario" },
folderId: { type: "number", description: "Folder ID to create scenario in" },
description: { type: "string", description: "Optional scenario description" },
},
required: ["name"],
},
};
const UPDATE_SCENARIO_TOOL: Tool = {
name: "update_make_dot_com_scenario",
description: "Updates a Make.com scenario's name, description, or scheduling.",
inputSchema: {
type: "object",
properties: {
scenario_id: { type: "number", description: "ID of the scenario to update" },
name: { type: "string", description: "New name of the scenario" },
description: { type: "string", description: "New description of the scenario" }
},
required: ["scenario_id"],
},
};
const CHECK_MODULE_TOOL: Tool = {
name: "check_make_dot_com_module_data",
description: "Checks whether a module in a Make.com scenario has required configuration fields populated.",
inputSchema: {
type: "object",
properties: {
scenario_id: { type: "number", description: "Scenario ID containing the module" },
module_id: { type: "number", description: "Module ID to verify" },
draft: { type: "boolean", default: false },
},
required: ["scenario_id", "module_id"],
},
};
const server = new Server(
{
name: "mcp-server-make-dot-com",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
const MAKE_API_KEY = process.env.MAKE_DOT_COM_API_KEY!;
const MAKE_BASE_URL = process.env.MAKE_DOT_COM_BASE_URL || "eu2.make.com";
const MAKE_TEAM_ID = process.env.MAKE_DOT_COM_TEAM_ID;
if (!MAKE_API_KEY) {
console.error("Error: MAKE_DOT_COM_API_KEY environment variable is required");
process.exit(1);
}
if (!MAKE_TEAM_ID) {
console.error("Error: MAKE_DOT_COM_TEAM_ID environment variable is required");
process.exit(1);
}
async function getScenarioBlueprint(scenarioId: number, draft = false) {
const url = `https://${MAKE_BASE_URL}/api/v2/scenarios/${scenarioId}/blueprint?includeDraft=${draft}&teamId=${MAKE_TEAM_ID}`;
const response = await fetch(url, {
headers: {
Authorization: `Token ${MAKE_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
if (!response.ok) throw new Error(`Make.com API error: ${response.status}\n${await response.text()}`);
const data = await response.json();
return data.response.blueprint;
}
async function listScenarios() {
const url = `https://${MAKE_BASE_URL}/api/v2/scenarios?teamId=${MAKE_TEAM_ID}`;
const response = await fetch(url, {
headers: {
Authorization: `Token ${MAKE_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
if (!response.ok) throw new Error(`Make.com API error: ${response.status}\n${await response.text()}`);
const data = await response.json();
return data.scenarios.map((s: any) => `• ${s.name} (ID: ${s.id}, Type: ${s.scheduling.type})`).join("\n") || "No scenarios found.";
}
async function createScenario(name: string, folderId?: number, description?: string) {
const url = `https://${MAKE_BASE_URL}/api/v2/scenarios?teamId=${MAKE_TEAM_ID}`;
const body: Record<string, unknown> = { name };
if (folderId) body.folderId = folderId;
if (description) body.description = description;
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Token ${MAKE_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(body),
});
if (!response.ok) throw new Error(`Make.com API error: ${response.status}\n${await response.text()}`);
const data = await response.json();
return `✅ Scenario created: ${data.name} (ID: ${data.id})`;
}
async function updateScenario(scenarioId: number, updates: { name?: string; description?: string }) {
const url = `https://${MAKE_BASE_URL}/api/v2/scenarios/${scenarioId}?teamId=${MAKE_TEAM_ID}`;
const response = await fetch(url, {
method: "PATCH",
headers: {
Authorization: `Token ${MAKE_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(updates),
});
if (!response.ok) throw new Error(`Make.com API error: ${response.status}\n${await response.text()}`);
const data = await response.json();
return `✅ Scenario updated: ${data.name} (ID: ${data.id})`;
}
function checkModuleFields(mod: any): string {
const required = mod?.metadata?.interface?.filter((f: any) => f.required);
if (!required?.length) return "No required fields defined on this module.";
const filled = required.filter((f: any) => f.name in mod.parameters);
return filled.length === required.length
? `✅ All required fields are filled: ${filled.map((f: any) => f.name).join(", ")}`
: `❌ Missing fields: ${required.filter((f: any) => !(f.name in mod.parameters)).map((f: any) => f.name).join(", ")}`;
}
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
READ_SCENARIO_BLUEPRINT_TOOL,
LIST_SCENARIOS_TOOL,
DESCRIBE_MAKE_MODULE_TOOL,
CREATE_SCENARIO_TOOL,
UPDATE_SCENARIO_TOOL,
CHECK_MODULE_TOOL,
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === READ_SCENARIO_BLUEPRINT_TOOL.name) {
if (!args || typeof args.scenario_id !== "number") throw new Error("Invalid arguments: scenario_id required");
const draft = typeof args.draft === "boolean" ? args.draft : false;
const blueprint = await getScenarioBlueprint(args.scenario_id, draft);
return { content: [{ type: "text", text: JSON.stringify(blueprint, null, 2) }] };
}
if (name === LIST_SCENARIOS_TOOL.name) {
const result = await listScenarios();
return { content: [{ type: "text", text: result }] };
}
if (name === DESCRIBE_MAKE_MODULE_TOOL.name) {
if (!args || typeof args.scenario_id !== "number" || typeof args.module_id !== "number") throw new Error("Invalid arguments");
const draft = typeof args.draft === "boolean" ? args.draft : false;
const blueprint = await getScenarioBlueprint(args.scenario_id, draft);
const mod = blueprint.flow.find((m: any) => m.id === args.module_id);
if (!mod) throw new Error("Module not found");
return { content: [{ type: "text", text: `📦 ${mod.module} — Parameters: ${Object.keys(mod.parameters || {}).join(", ")}` }] };
}
if (name === CREATE_SCENARIO_TOOL.name) {
if (!args || typeof args.name !== "string") throw new Error("Invalid arguments: name is required");
const folderId = typeof args.folderId === "number" ? args.folderId : undefined;
const description = typeof args.description === "string" ? args.description : undefined;
const result = await createScenario(args.name, folderId, description);
return { content: [{ type: "text", text: result }] };
}
if (name === UPDATE_SCENARIO_TOOL.name) {
if (!args || typeof args.scenario_id !== "number") throw new Error("Invalid arguments: scenario_id is required");
const updateFields: any = {};
if (typeof args.name === "string") updateFields.name = args.name;
if (typeof args.description === "string") updateFields.description = args.description;
const result = await updateScenario(args.scenario_id, updateFields);
return { content: [{ type: "text", text: result }] };
}
if (name === CHECK_MODULE_TOOL.name) {
if (!args || typeof args.scenario_id !== "number" || typeof args.module_id !== "number") throw new Error("Invalid arguments");
const draft = typeof args.draft === "boolean" ? args.draft : false;
const blueprint = await getScenarioBlueprint(args.scenario_id, draft);
const mod = blueprint.flow.find((m: any) => m.id === args.module_id);
if (!mod) throw new Error("Module not found");
return { content: [{ type: "text", text: checkModuleFields(mod) }] };
}
throw new Error(`Unknown tool: ${name}`);
});
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Make.com MCP Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});