Initial commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.env
|
||||
2
index.d.ts
vendored
Normal file
2
index.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
index.d.ts.map
Normal file
1
index.d.ts.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":""}
|
||||
132
index.js
Normal file
132
index.js
Normal file
@@ -0,0 +1,132 @@
|
||||
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 GODADDY_API_KEY = process.env.GODADDY_API_KEY;
|
||||
const GODADDY_API_SECRET = process.env.GODADDY_API_SECRET;
|
||||
const ENVIRONMENT = process.env.GODADDY_ENVIRONMENT || "production";
|
||||
const baseURL = ENVIRONMENT === "ote" ? "https://api.ote-godaddy.com" : "https://api.godaddy.com";
|
||||
if (!GODADDY_API_KEY || !GODADDY_API_SECRET) {
|
||||
console.error("GODADDY_API_KEY and GODADDY_API_SECRET environment variables are required.");
|
||||
process.exit(1);
|
||||
}
|
||||
const apiClient = axios.create({
|
||||
baseURL,
|
||||
headers: {
|
||||
"Authorization": `sso-key ${GODADDY_API_KEY}:${GODADDY_API_SECRET}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const server = new McpServer({
|
||||
name: "godaddy-mcp",
|
||||
version: "1.0.0",
|
||||
});
|
||||
// GET /v1/domains/{domain}/records
|
||||
server.tool("godaddy_list_records", "Retrieve all DNS records for a domain", {
|
||||
domain: z.string().describe("The domain name (e.g., mintel.me)"),
|
||||
type: z.string().optional().describe("Filter by record type (A, CNAME, TXT, MX, etc.)"),
|
||||
name: z.string().optional().describe("Filter by record name (e.g., @, www)"),
|
||||
}, async ({ domain, type, name }) => {
|
||||
try {
|
||||
let url = `/v1/domains/${domain}/records`;
|
||||
if (type) {
|
||||
url += `/${type}`;
|
||||
if (name) {
|
||||
url += `/${name}`;
|
||||
}
|
||||
}
|
||||
const response = await apiClient.get(url);
|
||||
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,
|
||||
};
|
||||
}
|
||||
});
|
||||
// PATCH /v1/domains/{domain}/records
|
||||
server.tool("godaddy_add_records", "Add new DNS records to a domain without replacing existing ones", {
|
||||
domain: z.string().describe("The domain name (e.g., mintel.me)"),
|
||||
records: z.array(z.object({
|
||||
type: z.enum(["A", "AAAA", "CNAME", "MX", "NS", "SRV", "TXT"]),
|
||||
name: z.string(),
|
||||
data: z.string(),
|
||||
ttl: z.number().optional().describe("Time to live in seconds (default 3600)"),
|
||||
priority: z.number().optional().describe("Priority for MX or SRV records"),
|
||||
weight: z.number().optional(),
|
||||
port: z.number().optional()
|
||||
})).describe("Array of DNS records to add"),
|
||||
}, async ({ domain, records }) => {
|
||||
try {
|
||||
const response = await apiClient.patch(`/v1/domains/${domain}/records`, records);
|
||||
return {
|
||||
content: [{ type: "text", text: "Successfully added records." }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// DELETE /v1/domains/{domain}/records/{type}/{name}
|
||||
server.tool("godaddy_delete_record", "Delete specific DNS records by type and name", {
|
||||
domain: z.string().describe("The domain name (e.g., mintel.me)"),
|
||||
type: z.enum(["A", "AAAA", "CNAME", "MX", "NS", "SRV", "TXT"]).describe("Record type"),
|
||||
name: z.string().describe("Record name (e.g., @, www)"),
|
||||
}, async ({ domain, type, name }) => {
|
||||
try {
|
||||
const response = await apiClient.delete(`/v1/domains/${domain}/records/${type}/${name}`);
|
||||
return {
|
||||
content: [{ type: "text", text: "Successfully deleted record." }],
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
// PUT /v1/domains/{domain}/records/{type}/{name}
|
||||
server.tool("godaddy_replace_records", "Replace all DNS records for a specific type and name", {
|
||||
domain: z.string().describe("The domain name (e.g., mintel.me)"),
|
||||
type: z.enum(["A", "AAAA", "CNAME", "MX", "NS", "SRV", "TXT"]).describe("Record type"),
|
||||
name: z.string().describe("Record name (e.g., @, www)"),
|
||||
records: z.array(z.object({
|
||||
data: z.string(),
|
||||
ttl: z.number().optional().describe("Time to live in seconds (default 3600)"),
|
||||
priority: z.number().optional().describe("Priority for MX or SRV records"),
|
||||
weight: z.number().optional(),
|
||||
port: z.number().optional()
|
||||
})).describe("Array of DNS records to replace with"),
|
||||
}, async ({ domain, type, name, records }) => {
|
||||
try {
|
||||
const response = await apiClient.put(`/v1/domains/${domain}/records/${type}/${name}`, records);
|
||||
return {
|
||||
content: [{ type: "text", text: "Successfully replaced records." }],
|
||||
};
|
||||
}
|
||||
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("GoDaddy MCP Server running on stdio");
|
||||
}
|
||||
main().catch((error) => {
|
||||
console.error("Fatal error in main():", error);
|
||||
process.exit(1);
|
||||
});
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
index.js.map
Normal file
1
index.js.map
Normal file
File diff suppressed because one or more lines are too long
159
index.ts
Normal file
159
index.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
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 GODADDY_API_KEY = process.env.GODADDY_API_KEY;
|
||||
const GODADDY_API_SECRET = process.env.GODADDY_API_SECRET;
|
||||
const ENVIRONMENT = process.env.GODADDY_ENVIRONMENT || "production";
|
||||
|
||||
const baseURL = ENVIRONMENT === "ote" ? "https://api.ote-godaddy.com" : "https://api.godaddy.com";
|
||||
|
||||
if (!GODADDY_API_KEY || !GODADDY_API_SECRET) {
|
||||
console.error("GODADDY_API_KEY and GODADDY_API_SECRET environment variables are required.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const apiClient = axios.create({
|
||||
baseURL,
|
||||
headers: {
|
||||
"Authorization": `sso-key ${GODADDY_API_KEY}:${GODADDY_API_SECRET}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const server = new McpServer({
|
||||
name: "godaddy-mcp",
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
// GET /v1/domains/{domain}/records
|
||||
server.tool(
|
||||
"godaddy_list_records",
|
||||
"Retrieve all DNS records for a domain",
|
||||
{
|
||||
domain: z.string().describe("The domain name (e.g., mintel.me)"),
|
||||
type: z.string().optional().describe("Filter by record type (A, CNAME, TXT, MX, etc.)"),
|
||||
name: z.string().optional().describe("Filter by record name (e.g., @, www)"),
|
||||
},
|
||||
async ({ domain, type, name }) => {
|
||||
try {
|
||||
let url = `/v1/domains/${domain}/records`;
|
||||
if (type) {
|
||||
url += `/${type}`;
|
||||
if (name) {
|
||||
url += `/${name}`;
|
||||
}
|
||||
}
|
||||
const response = await apiClient.get(url);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// PATCH /v1/domains/{domain}/records
|
||||
server.tool(
|
||||
"godaddy_add_records",
|
||||
"Add new DNS records to a domain without replacing existing ones",
|
||||
{
|
||||
domain: z.string().describe("The domain name (e.g., mintel.me)"),
|
||||
records: z.array(z.object({
|
||||
type: z.enum(["A", "AAAA", "CNAME", "MX", "NS", "SRV", "TXT"]),
|
||||
name: z.string(),
|
||||
data: z.string(),
|
||||
ttl: z.number().optional().describe("Time to live in seconds (default 3600)"),
|
||||
priority: z.number().optional().describe("Priority for MX or SRV records"),
|
||||
weight: z.number().optional(),
|
||||
port: z.number().optional()
|
||||
})).describe("Array of DNS records to add"),
|
||||
},
|
||||
async ({ domain, records }) => {
|
||||
try {
|
||||
const response = await apiClient.patch(`/v1/domains/${domain}/records`, records);
|
||||
return {
|
||||
content: [{ type: "text", text: "Successfully added records." }],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// DELETE /v1/domains/{domain}/records/{type}/{name}
|
||||
server.tool(
|
||||
"godaddy_delete_record",
|
||||
"Delete specific DNS records by type and name",
|
||||
{
|
||||
domain: z.string().describe("The domain name (e.g., mintel.me)"),
|
||||
type: z.enum(["A", "AAAA", "CNAME", "MX", "NS", "SRV", "TXT"]).describe("Record type"),
|
||||
name: z.string().describe("Record name (e.g., @, www)"),
|
||||
},
|
||||
async ({ domain, type, name }) => {
|
||||
try {
|
||||
const response = await apiClient.delete(`/v1/domains/${domain}/records/${type}/${name}`);
|
||||
return {
|
||||
content: [{ type: "text", text: "Successfully deleted record." }],
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${error.response?.data?.message || error.message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// PUT /v1/domains/{domain}/records/{type}/{name}
|
||||
server.tool(
|
||||
"godaddy_replace_records",
|
||||
"Replace all DNS records for a specific type and name",
|
||||
{
|
||||
domain: z.string().describe("The domain name (e.g., mintel.me)"),
|
||||
type: z.enum(["A", "AAAA", "CNAME", "MX", "NS", "SRV", "TXT"]).describe("Record type"),
|
||||
name: z.string().describe("Record name (e.g., @, www)"),
|
||||
records: z.array(z.object({
|
||||
data: z.string(),
|
||||
ttl: z.number().optional().describe("Time to live in seconds (default 3600)"),
|
||||
priority: z.number().optional().describe("Priority for MX or SRV records"),
|
||||
weight: z.number().optional(),
|
||||
port: z.number().optional()
|
||||
})).describe("Array of DNS records to replace with"),
|
||||
},
|
||||
async ({ domain, type, name, records }) => {
|
||||
try {
|
||||
const response = await apiClient.put(`/v1/domains/${domain}/records/${type}/${name}`, records);
|
||||
return {
|
||||
content: [{ type: "text", text: "Successfully replaced records." }],
|
||||
};
|
||||
} catch (error: any) {
|
||||
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("GoDaddy MCP Server running on stdio");
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Fatal error in main():", error);
|
||||
process.exit(1);
|
||||
});
|
||||
1554
package-lock.json
generated
Normal file
1554
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
package.json
Normal file
24
package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "godaddy_mcp",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"axios": "^1.16.1",
|
||||
"dotenv": "^17.4.2",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.9.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
44
tsconfig.json
Normal file
44
tsconfig.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
// Visit https://aka.ms/tsconfig to read more about this file
|
||||
"compilerOptions": {
|
||||
// File Layout
|
||||
// "rootDir": "./src",
|
||||
// "outDir": "./dist",
|
||||
|
||||
// Environment Settings
|
||||
// See also https://aka.ms/tsconfig/module
|
||||
"module": "nodenext",
|
||||
"target": "esnext",
|
||||
"types": [],
|
||||
// For nodejs:
|
||||
// "lib": ["esnext"],
|
||||
// "types": ["node"],
|
||||
// and npm install -D @types/node
|
||||
|
||||
// Other Outputs
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
|
||||
// Stricter Typechecking Options
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
|
||||
// Style Options
|
||||
// "noImplicitReturns": true,
|
||||
// "noImplicitOverride": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
// "noFallthroughCasesInSwitch": true,
|
||||
// "noPropertyAccessFromIndexSignature": true,
|
||||
|
||||
// Recommended Options
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"moduleDetection": "force",
|
||||
"skipLibCheck": true,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user