import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import { CloudflareApi } from "./api.js";
import { DnsRecordType } from "./types.js";
// Configuration schema for Smithery
export const configSchema = z.object({
cloudflareApiToken: z.string().describe("Your Cloudflare API Token with Zone:Edit permissions"),
cloudflareZoneId: z.string().describe("The Zone ID of your domain in Cloudflare"),
cloudflareEmail: z.string().optional().describe("Your Cloudflare account email (only needed for legacy API keys)")
});
// Export default function for Smithery
export default function createServer({ config }: { config: z.infer<typeof configSchema> }) {
const server = new Server(
{
name: "mcp-cloudflare",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Helper function to configure API only when needed
const configureApiIfNeeded = () => {
try {
if (config?.cloudflareApiToken && config?.cloudflareZoneId) {
CloudflareApi.configure(config);
return true;
}
return false;
} catch (error) {
console.error('Error configuring Cloudflare API:', error);
return false;
}
};
// Register available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "list_dns_records",
description: "List all DNS records for the configured zone",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Filter by record name (optional)",
},
type: {
type: "string",
enum: ["A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV", "CAA", "PTR"],
description: "Filter by record type (optional)",
},
},
},
},
{
name: "get_dns_record",
description: "Get a specific DNS record by ID",
inputSchema: {
type: "object",
properties: {
recordId: {
type: "string",
description: "The DNS record ID",
},
},
required: ["recordId"],
},
},
{
name: "create_dns_record",
description: "Create a new DNS record",
inputSchema: {
type: "object",
properties: {
type: {
type: "string",
enum: ["A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV", "CAA", "PTR"],
description: "DNS record type",
},
name: {
type: "string",
description: "DNS record name",
},
content: {
type: "string",
description: "DNS record content",
},
ttl: {
type: "number",
description: "Time to live (TTL) in seconds (default: 1 for auto)",
minimum: 1,
},
priority: {
type: "number",
description: "Priority (for MX records)",
},
proxied: {
type: "boolean",
description: "Whether the record should be proxied through Cloudflare",
},
},
required: ["type", "name", "content"],
},
},
{
name: "update_dns_record",
description: "Update an existing DNS record",
inputSchema: {
type: "object",
properties: {
recordId: {
type: "string",
description: "The DNS record ID to update",
},
type: {
type: "string",
enum: ["A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV", "CAA", "PTR"],
description: "DNS record type",
},
name: {
type: "string",
description: "DNS record name",
},
content: {
type: "string",
description: "DNS record content",
},
ttl: {
type: "number",
description: "Time to live (TTL) in seconds",
minimum: 1,
},
priority: {
type: "number",
description: "Priority (for MX records)",
},
proxied: {
type: "boolean",
description: "Whether the record should be proxied through Cloudflare",
},
},
required: ["recordId"],
},
},
{
name: "delete_dns_record",
description: "Delete a DNS record",
inputSchema: {
type: "object",
properties: {
recordId: {
type: "string",
description: "The DNS record ID to delete",
},
},
required: ["recordId"],
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "list_dns_records") {
return await handleListDnsRecords(args as { name?: string; type?: string });
}
if (name === "get_dns_record") {
return await handleGetDnsRecord(args as { recordId: string });
}
if (name === "create_dns_record") {
return await handleCreateDnsRecord(args as { type: string; name: string; content: string; ttl?: number; priority?: number; proxied?: boolean });
}
if (name === "update_dns_record") {
return await handleUpdateDnsRecord(args as { recordId: string; type?: string; name?: string; content?: string; ttl?: number; priority?: number; proxied?: boolean });
}
if (name === "delete_dns_record") {
return await handleDeleteDnsRecord(args as { recordId: string });
}
throw new Error(`Unknown tool: ${name}`);
});
// Tool handlers
const handleListDnsRecords = async (args: { name?: string; type?: string }) => {
try {
if (!configureApiIfNeeded()) {
return {
content: [{ type: "text", text: "โ Configuration incomplete. Please configure Cloudflare API Token and Zone ID first." }],
};
}
const records = await CloudflareApi.findDnsRecords(args.name, args.type);
if (records.length === 0) {
return {
content: [{ type: "text", text: "No DNS records found matching the criteria." }],
};
}
const recordsText = records.map(record =>
`๐น ${record.name} (${record.type}) โ ${record.content} [ID: ${record.id}]${record.proxied ? ' ๐ Proxied' : ''}`
).join('\n');
return {
content: [{
type: "text",
text: `โ
Found ${records.length} DNS record(s):\n\n${recordsText}`
}],
};
} catch (error) {
return {
content: [{ type: "text", text: `โ Error listing DNS records: ${error instanceof Error ? error.message : 'Unknown error'}` }],
};
}
};
const handleGetDnsRecord = async (args: { recordId: string }) => {
try {
if (!configureApiIfNeeded()) {
return {
content: [{ type: "text", text: "โ Configuration incomplete. Please configure Cloudflare API Token and Zone ID first." }],
};
}
const record = await CloudflareApi.getDnsRecord(args.recordId);
return {
content: [{
type: "text",
text: `โ
DNS Record Details:
๐น Name: ${record.name}
๐น Type: ${record.type}
๐น Content: ${record.content}
๐น TTL: ${record.ttl}
๐น Proxied: ${record.proxied ? 'Yes' : 'No'}
${record.priority ? `๐น Priority: ${record.priority}` : ''}
๐น ID: ${record.id}
๐น Created: ${new Date(record.created_on).toLocaleString()}
๐น Modified: ${new Date(record.modified_on).toLocaleString()}`
}],
};
} catch (error) {
return {
content: [{ type: "text", text: `โ Error getting DNS record: ${error instanceof Error ? error.message : 'Unknown error'}` }],
};
}
};
const handleCreateDnsRecord = async (args: {
type: string;
name: string;
content: string;
ttl?: number;
priority?: number;
proxied?: boolean
}) => {
try {
if (!configureApiIfNeeded()) {
return {
content: [{ type: "text", text: "โ Configuration incomplete. Please configure Cloudflare API Token and Zone ID first." }],
};
}
const recordData: any = {
type: DnsRecordType.parse(args.type),
name: args.name,
content: args.content,
};
if (args.ttl !== undefined) recordData.ttl = args.ttl;
if (args.priority !== undefined) recordData.priority = args.priority;
if (args.proxied !== undefined) recordData.proxied = args.proxied;
const record = await CloudflareApi.createDnsRecord(recordData);
return {
content: [{
type: "text",
text: `โ
DNS record created successfully!
๐น Name: ${record.name}
๐น Type: ${record.type}
๐น Content: ${record.content}
๐น ID: ${record.id}
${record.proxied ? '๐ Proxied through Cloudflare' : ''}`
}],
};
} catch (error) {
return {
content: [{ type: "text", text: `โ Error creating DNS record: ${error instanceof Error ? error.message : 'Unknown error'}` }],
};
}
};
const handleUpdateDnsRecord = async (args: {
recordId: string;
type?: string;
name?: string;
content?: string;
ttl?: number;
priority?: number;
proxied?: boolean
}) => {
try {
if (!configureApiIfNeeded()) {
return {
content: [{ type: "text", text: "โ Configuration incomplete. Please configure Cloudflare API Token and Zone ID first." }],
};
}
const updates: any = {};
if (args.type) updates.type = DnsRecordType.parse(args.type);
if (args.name) updates.name = args.name;
if (args.content) updates.content = args.content;
if (args.ttl !== undefined) updates.ttl = args.ttl;
if (args.priority !== undefined) updates.priority = args.priority;
if (args.proxied !== undefined) updates.proxied = args.proxied;
const record = await CloudflareApi.updateDnsRecord(args.recordId, updates);
return {
content: [{
type: "text",
text: `โ
DNS record updated successfully!
๐น Name: ${record.name}
๐น Type: ${record.type}
๐น Content: ${record.content}
๐น ID: ${record.id}
${record.proxied ? '๐ Proxied through Cloudflare' : ''}`
}],
};
} catch (error) {
return {
content: [{ type: "text", text: `โ Error updating DNS record: ${error instanceof Error ? error.message : 'Unknown error'}` }],
};
}
};
const handleDeleteDnsRecord = async (args: { recordId: string }) => {
try {
if (!configureApiIfNeeded()) {
return {
content: [{ type: "text", text: "โ Configuration incomplete. Please configure Cloudflare API Token and Zone ID first." }],
};
}
await CloudflareApi.deleteDnsRecord(args.recordId);
return {
content: [{
type: "text",
text: `โ
DNS record deleted successfully! (ID: ${args.recordId})`
}],
};
} catch (error) {
return {
content: [{ type: "text", text: `โ Error deleting DNS record: ${error instanceof Error ? error.message : 'Unknown error'}` }],
};
}
};
return server;
}