// MCP Server for Make.com with full tool implementations
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ToolSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
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 || !MAKE_TEAM_ID) {
throw new Error("Missing Make.com credentials or team ID");
}
function makeHeaders() {
return {
Authorization: `Token ${MAKE_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json",
};
}
// ========== Zod Schemas ==========
const ScenarioIdSchema = z.object({
scenario_id: z.number(),
draft: z.boolean().optional().default(false),
});
const CreateScenarioSchema = z.object({
name: z.string(),
folderId: z.number().optional(),
description: z.string().optional(),
});
const UpdateScenarioSchema = z.object({
scenario_id: z.number(),
name: z.string().optional(),
description: z.string().optional(),
});
const DescribeModuleSchema = z.object({
scenario_id: z.number(),
module_id: z.number(),
draft: z.boolean().optional().default(false),
});
const UpdateScenarioInterfaceArgsSchema = z.object({
scenario_id: z.number(),
inputs: z.array(
z.object({
name: z.string(),
type: z.string(),
label: z.string().optional(),
required: z.boolean().optional(),
multiline: z.boolean().optional(),
default: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),
})
),
});
const CheckModuleSchema = z.object({
scenario_id: z.number(),
module_id: z.number(),
draft: z.boolean().optional().default(false),
});
// Add PatchModuleInBlueprintSchema
const PatchModuleInBlueprintSchema = z.object({
scenario_id: z.number().describe("ID of the Make.com scenario to patch"),
module_id: z.number().describe("ID of the module in the blueprint to update"),
draft: z.boolean().optional().default(false).describe("Update the draft version if true"),
updates: z.object({
parameters: z.record(z.unknown()).optional().describe("Parameters to merge into existing ones"),
mapper: z.record(z.unknown()).optional().describe("Mapper to merge into existing ones"),
metadata: z.record(z.unknown()).optional().describe("Metadata to merge into existing ones")
}).describe("Patch object with parameters, mapper or metadata")
});
// ========== Server ==========
const server = new Server(
{ name: "mcp-server-make-dot-com", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// ========== Tool List ==========
const tools = [
{
name: "read_make_dot_com_scenario_blueprint",
description: "Retrieve blueprint for a scenario",
inputSchema: zodToJsonSchema(ScenarioIdSchema),
},
{
name: "create_make_dot_com_scenario",
description: "Create a new Make.com scenario",
inputSchema: zodToJsonSchema(CreateScenarioSchema),
},
{
name: "update_make_dot_com_scenario",
description: "Update an existing Make.com scenario",
inputSchema: zodToJsonSchema(UpdateScenarioSchema),
},
{
name: "describe_make_dot_com_module",
description: "Describe module parameters from blueprint",
inputSchema: zodToJsonSchema(DescribeModuleSchema),
},
{
name: "update_make_dot_com_scenario_interface",
description:
"Updates the input interface (form fields) for a scenario using Make's on-demand input feature.",
inputSchema: zodToJsonSchema(UpdateScenarioInterfaceArgsSchema),
},
{
name: "check_make_dot_com_module_data",
description: "Check if module required fields are filled",
inputSchema: zodToJsonSchema(CheckModuleSchema),
},
{
name: "list_make_dot_com_scenarios",
description: "List available Make.com scenarios",
inputSchema: { type: "object", properties: {}, required: [] },
},
{
name: "list_make_dot_com_connections",
description: "List Make.com connections and their validity",
inputSchema: { type: "object", properties: {}, required: [] },
},
// Add new tool definition
{
name: "patch_make_dot_com_module_in_blueprint",
description:
"Patches a module's configuration inside a Make.com scenario blueprint. Supports parameters, mapper, and metadata updates.",
inputSchema: zodToJsonSchema(PatchModuleInBlueprintSchema) as any,
},
];
// Add patchModuleInBlueprint function
async function patchModuleInBlueprint(args: z.infer<typeof PatchModuleInBlueprintSchema>, apiKey: string, baseUrl: string, teamId: string): Promise<string> {
const { scenario_id, module_id, updates, draft } = args;
const blueprintUrl = `https://${baseUrl}/api/v2/scenarios/${scenario_id}/blueprint?includeDraft=${draft}&teamId=${teamId}`;
const res = await fetch(blueprintUrl, {
headers: {
Authorization: `Token ${apiKey}`,
"Content-Type": "application/json",
Accept: "application/json"
}
});
if (!res.ok) {
throw new Error(`Failed to fetch blueprint: ${res.status} ${res.statusText}\n${await res.text()}`);
}
const data = await res.json();
const blueprint = data.response.blueprint;
const mod = blueprint.flow.find((m: any) => m.id === module_id);
if (!mod) throw new Error(`Module ${module_id} not found in scenario.`);
if (updates.parameters) {
mod.parameters = { ...mod.parameters, ...updates.parameters };
}
if (updates.mapper) {
mod.mapper = { ...mod.mapper, ...updates.mapper };
}
if (updates.metadata) {
mod.metadata = { ...mod.metadata, ...updates.metadata };
}
const patchRes = await fetch(blueprintUrl, {
method: "PATCH",
headers: {
Authorization: `Token ${apiKey}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ blueprint })
});
if (!patchRes.ok) {
throw new Error(`Failed to update blueprint: ${patchRes.status} ${patchRes.statusText}\n${await patchRes.text()}`);
}
return `✅ Module ${module_id} in scenario ${scenario_id} updated successfully.`;
}
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
server.setRequestHandler(CallToolRequestSchema, async ({ params: { name, arguments: args } }) => {
switch (name) {
case "read_make_dot_com_scenario_blueprint": {
const { scenario_id, draft } = ScenarioIdSchema.parse(args);
const url = `https://${MAKE_BASE_URL}/api/v2/scenarios/${scenario_id}/blueprint?includeDraft=${draft}&teamId=${MAKE_TEAM_ID}`;
const res = await fetch(url, { headers: makeHeaders() });
return { content: [{ type: "text", text: await res.text() }] };
}
case "create_make_dot_com_scenario": {
const { name, folderId, description } = CreateScenarioSchema.parse(args);
const res = await fetch(`https://${MAKE_BASE_URL}/api/v2/scenarios`, {
method: "POST",
headers: makeHeaders(),
body: JSON.stringify({ name, folderId, description, teamId: MAKE_TEAM_ID })
});
return { content: [{ type: "text", text: await res.text() }] };
}
case "update_make_dot_com_scenario": {
const { scenario_id, name, description } = UpdateScenarioSchema.parse(args);
const url = `https://${MAKE_BASE_URL}/api/v2/scenarios/${scenario_id}?teamId=${MAKE_TEAM_ID}`;
const res = await fetch(url, {
method: "PATCH",
headers: makeHeaders(),
body: JSON.stringify({ name, description })
});
return { content: [{ type: "text", text: await res.text() }] };
}
case "describe_make_dot_com_module": {
const { scenario_id, module_id, draft } = DescribeModuleSchema.parse(args);
const url = `https://${MAKE_BASE_URL}/api/v2/scenarios/${scenario_id}/blueprint?includeDraft=${draft}&teamId=${MAKE_TEAM_ID}`;
const res = await fetch(url, { headers: makeHeaders() });
const blueprint = await res.json();
const mod = blueprint.response?.blueprint?.flow?.find((m: any) => m.id === module_id);
if (!mod) throw new Error("Module not found");
return {
content: [{ type: "text", text: JSON.stringify(mod, null, 2) }]
};
}
case "update_module_parameters": {
const parsed = UpdateScenarioInterfaceArgsSchema.safeParse(args);
if (!parsed.success) {
throw new Error(
`Invalid arguments for update_make_dot_com_scenario_interface: ${parsed.error}`
);
}
const { scenario_id, inputs } = parsed.data;
const response = await fetch(
`https://${MAKE_BASE_URL}/api/v2/scenarios/${scenario_id}/interface?teamId=${MAKE_TEAM_ID}`,
{
method: "PATCH",
headers: {
Authorization: `Token ${MAKE_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
interface: {
input: inputs,
},
}),
}
);
if (!response.ok) {
throw new Error(
`Make.com API error: ${response.status} ${response.statusText}\n${await response.text()}`
);
}
const data = await response.json();
return {
content: [
{
type: "text",
text: `✅ Scenario interface updated successfully. Inputs: ${data.interface?.input?.length ?? 0}`,
},
],
};
}
case "check_make_dot_com_module_data": {
const { scenario_id, module_id, draft } = CheckModuleSchema.parse(args);
const url = `https://${MAKE_BASE_URL}/api/v2/scenarios/${scenario_id}/blueprint?includeDraft=${draft}&teamId=${MAKE_TEAM_ID}`;
const res = await fetch(url, { headers: makeHeaders() });
const blueprint = await res.json();
const mod = blueprint.response?.blueprint?.flow?.find((m: any) => m.id === module_id);
if (!mod) throw new Error("Module not found");
const required = mod.metadata?.interface?.filter((f: any) => f.required)?.map((f: any) => f.name) || [];
const filled = required.filter((name: string) => mod.parameters?.[name] !== undefined);
return {
content: [{ type: "text", text: `${filled.length}/${required.length} required fields filled.` }]
};
}
case "list_make_dot_com_scenarios": {
const url = `https://${MAKE_BASE_URL}/api/v2/scenarios?teamId=${MAKE_TEAM_ID}`;
const res = await fetch(url, { headers: makeHeaders() });
const data = await res.json();
const result = data?.scenarios?.map((s: any) => `• ${s.name} (ID: ${s.id})`)?.join("\n") || "No scenarios found.";
return { content: [{ type: "text", text: result }] };
}
case "list_make_dot_com_connections": {
const url = `https://${MAKE_BASE_URL}/api/v2/connections?teamId=${MAKE_TEAM_ID}`;
const res = await fetch(url, { headers: makeHeaders() });
const data = await res.json();
const result = data.map((c: any) => `• ${c.label || c.name} (${c.id}) ${c.valid ? "✅" : "❌"}`).join("\n");
return { content: [{ type: "text", text: result }] };
}
case "patch_make_dot_com_module_in_blueprint": {
const parsedArgs = PatchModuleInBlueprintSchema.parse(args);
const result = await patchModuleInBlueprint(parsedArgs, MAKE_API_KEY, MAKE_BASE_URL, MAKE_TEAM_ID);
return { content: [{ type: "text", text: result }] };
}
default:
throw new Error(`Unknown tool: ${name}`);
}
});
// Start the server
(async () => {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("✅ Make.com MCP Server running...");
})();