Netlify MCP Server
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { execSync } from "child_process";
// Create server instance
const server = new Server(
{
name: "netlify-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Helper function for executing Netlify CLI commands
async function executeNetlifyCommand(command: string): Promise<string> {
try {
return execSync(command).toString();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Netlify CLI error: ${error.message}`);
}
throw error;
}
}
// Define Zod schemas for validation
const DeploySiteSchema = z.object({
path: z.string(),
prod: z.boolean().optional(),
message: z.string().optional(),
});
const SetEnvVarsSchema = z.object({
siteId: z.string(),
envVars: z.record(z.string()),
});
const GetDeployStatusSchema = z.object({
siteId: z.string(),
deployId: z.string().optional(),
});
const AddDNSRecordSchema = z.object({
siteId: z.string(),
domain: z.string(),
type: z.enum(["A", "AAAA", "CNAME", "MX", "TXT", "NS"]),
value: z.string(),
ttl: z.number().optional(),
});
const DeployFunctionSchema = z.object({
path: z.string(),
name: z.string(),
runtime: z.string().optional(),
});
const ManageFormSchema = z.object({
siteId: z.string(),
formId: z.string(),
action: z.enum(["enable", "disable", "delete"]),
});
const ManagePluginSchema = z.object({
siteId: z.string(),
pluginId: z.string(),
action: z.enum(["install", "uninstall", "update"]),
config: z.record(z.unknown()).optional(),
});
const ManageHookSchema = z.object({
siteId: z.string(),
event: z.string(),
url: z.string(),
action: z.enum(["create", "delete", "update"]),
});
type DeploySiteParams = z.infer<typeof DeploySiteSchema>;
type SetEnvVarsParams = z.infer<typeof SetEnvVarsSchema>;
type GetDeployStatusParams = z.infer<typeof GetDeployStatusSchema>;
type AddDNSRecordParams = z.infer<typeof AddDNSRecordSchema>;
type DeployFunctionParams = z.infer<typeof DeployFunctionSchema>;
type ManageFormParams = z.infer<typeof ManageFormSchema>;
type ManagePluginParams = z.infer<typeof ManagePluginSchema>;
type ManageHookParams = z.infer<typeof ManageHookSchema>;
// Register Netlify tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "deploy-site",
description: "Deploy a site to Netlify",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "Path to the site directory",
},
prod: {
type: "boolean",
description: "Deploy to production",
},
message: {
type: "string",
description: "Deploy message",
},
},
required: ["path"],
},
},
{
name: "list-sites",
description: "List all Netlify sites",
inputSchema: {
type: "object",
properties: {},
required: [],
},
},
{
name: "set-env-vars",
description: "Set environment variables for a site",
inputSchema: {
type: "object",
properties: {
siteId: {
type: "string",
description: "Site ID or name",
},
envVars: {
type: "object",
description: "Environment variables to set",
additionalProperties: {
type: "string",
},
},
},
required: ["siteId", "envVars"],
},
},
{
name: "get-deploy-status",
description: "Get deployment status for a site",
inputSchema: {
type: "object",
properties: {
siteId: {
type: "string",
description: "Site ID or name",
},
deployId: {
type: "string",
description: "Deployment ID",
},
},
required: ["siteId"],
},
},
{
name: "add-dns-record",
description: "Add a DNS record to a site",
inputSchema: {
type: "object",
properties: {
siteId: {
type: "string",
description: "Site ID or name",
},
domain: {
type: "string",
description: "Domain name",
},
type: {
type: "string",
enum: ["A", "AAAA", "CNAME", "MX", "TXT", "NS"],
description: "DNS record type",
},
value: {
type: "string",
description: "DNS record value",
},
ttl: {
type: "number",
description: "Time to live in seconds",
},
},
required: ["siteId", "domain", "type", "value"],
},
},
{
name: "deploy-function",
description: "Deploy a serverless function",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "Path to the function file",
},
name: {
type: "string",
description: "Function name",
},
runtime: {
type: "string",
description: "Function runtime (e.g., nodejs, go)",
},
},
required: ["path", "name"],
},
},
{
name: "manage-form",
description: "Manage form submissions",
inputSchema: {
type: "object",
properties: {
siteId: {
type: "string",
description: "Site ID or name",
},
formId: {
type: "string",
description: "Form ID",
},
action: {
type: "string",
enum: ["enable", "disable", "delete"],
description: "Action to perform",
},
},
required: ["siteId", "formId", "action"],
},
},
{
name: "manage-plugin",
description: "Manage site plugins",
inputSchema: {
type: "object",
properties: {
siteId: {
type: "string",
description: "Site ID or name",
},
pluginId: {
type: "string",
description: "Plugin ID",
},
action: {
type: "string",
enum: ["install", "uninstall", "update"],
description: "Action to perform",
},
config: {
type: "object",
description: "Plugin configuration",
},
},
required: ["siteId", "pluginId", "action"],
},
},
{
name: "manage-hook",
description: "Manage webhook notifications",
inputSchema: {
type: "object",
properties: {
siteId: {
type: "string",
description: "Site ID or name",
},
event: {
type: "string",
description: "Event type",
},
url: {
type: "string",
description: "Webhook URL",
},
action: {
type: "string",
enum: ["create", "delete", "update"],
description: "Action to perform",
},
},
required: ["siteId", "event", "url", "action"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case "deploy-site": {
const params = DeploySiteSchema.parse(request.params.arguments);
let command = `netlify deploy --dir="${params.path}"`;
if (params.prod) {
command += " --prod";
}
if (params.message) {
command += ` --message="${params.message}"`;
}
const output = await executeNetlifyCommand(command);
return {
content: [{ type: "text", text: output }],
};
}
case "list-sites": {
const output = await executeNetlifyCommand("netlify sites:list");
return {
content: [{ type: "text", text: output }],
};
}
case "set-env-vars": {
const params = SetEnvVarsSchema.parse(request.params.arguments);
const results: string[] = [];
for (const [key, value] of Object.entries(params.envVars)) {
const output = await executeNetlifyCommand(
`netlify env:set ${key} "${value}" --site-id ${params.siteId}`
);
results.push(output);
}
return {
content: [{ type: "text", text: results.join("\n") }],
};
}
case "get-deploy-status": {
const params = GetDeployStatusSchema.parse(request.params.arguments);
let command = `netlify deploy:list --site-id ${params.siteId}`;
if (params.deployId) {
command += ` --id ${params.deployId}`;
}
const output = await executeNetlifyCommand(command);
return {
content: [{ type: "text", text: output }],
};
}
case "add-dns-record": {
const params = AddDNSRecordSchema.parse(request.params.arguments);
let command = `netlify dns:add ${params.domain} ${params.type} ${params.value}`;
if (params.ttl) {
command += ` --ttl ${params.ttl}`;
}
command += ` --site-id ${params.siteId}`;
const output = await executeNetlifyCommand(command);
return {
content: [{ type: "text", text: output }],
};
}
case "deploy-function": {
const params = DeployFunctionSchema.parse(request.params.arguments);
let command = `netlify functions:create ${params.name} --path ${params.path}`;
if (params.runtime) {
command += ` --runtime ${params.runtime}`;
}
const output = await executeNetlifyCommand(command);
return {
content: [{ type: "text", text: output }],
};
}
case "manage-form": {
const params = ManageFormSchema.parse(request.params.arguments);
const command = `netlify forms:${params.action} ${params.formId} --site-id ${params.siteId}`;
const output = await executeNetlifyCommand(command);
return {
content: [{ type: "text", text: output }],
};
}
case "manage-plugin": {
const params = ManagePluginSchema.parse(request.params.arguments);
let command = `netlify plugins:${params.action} ${params.pluginId} --site-id ${params.siteId}`;
if (params.config && params.action !== "uninstall") {
command += ` --config '${JSON.stringify(params.config)}'`;
}
const output = await executeNetlifyCommand(command);
return {
content: [{ type: "text", text: output }],
};
}
case "manage-hook": {
const params = ManageHookSchema.parse(request.params.arguments);
const command = `netlify hooks:${params.action} --site-id ${params.siteId} --event ${params.event} --url ${params.url}`;
const output = await executeNetlifyCommand(command);
return {
content: [{ type: "text", text: output }],
};
}
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}
} catch (error) {
return {
content: [{ type: "text", text: error instanceof Error ? error.message : "Unknown error occurred" }],
isError: true,
};
}
});
// Run the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Netlify MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});