@themkn/gandi-mcp 0.1.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,9 +13,23 @@ An [MCP server](https://modelcontextprotocol.io) for [Gandi LiveDNS](https://api
13
13
 
14
14
  ## Prerequisites
15
15
 
16
- - **A Gandi account** with a [Personal Access Token](https://admin.gandi.net/) (*Account → Security → Personal Access Tokens*) with LiveDNS access
16
+ - **A Gandi account**
17
17
  - **Node.js 24+**
18
18
 
19
+ ### Get a Gandi Personal Access Token
20
+
21
+ Gandi replaced legacy API keys with Personal Access Tokens (PATs). To create one for `gandi-mcp`:
22
+
23
+ 1. Sign in at [admin.gandi.net](https://admin.gandi.net/).
24
+ 2. Top-right menu → **User settings** → **Authentication options** (or visit [account.gandi.net/security](https://account.gandi.net/security)).
25
+ 3. Under **Personal Access Tokens**, click **Create a token**.
26
+ 4. Give it a name (e.g. `gandi-mcp`) and pick an expiration (max 1 year — Gandi will email you when it nears expiry).
27
+ 5. **Scope — which domains:** pick *Specific organizations* or *Specific domains* and select the ones you want this MCP to manage. Don't grant access to the whole account if you don't need it.
28
+ 6. **Permissions:** enable **"Manage domain technical configurations"** (covers LiveDNS read + write). If any of your domains are owned by a Gandi *organization* (not your personal account), also enable **"View organization information"** so `list_domains` can discover them — otherwise it will silently fall back to your personal-org domains only. Everything else (billing, transfers, etc.) can stay unchecked.
29
+ 7. Click **Create**. **Copy the token immediately** — Gandi only shows it once. If you miss it, delete and create a new one.
30
+
31
+ That token is your `apiKey` value in the config below.
32
+
19
33
  ## Install
20
34
 
21
35
  ```sh
@@ -81,7 +95,7 @@ By default Claude Code prompts on every tool call. To allow all `gandi-mcp` tool
81
95
 
82
96
  | Tool | What it does |
83
97
  |------|--------------|
84
- | `list_domains` | List all domains on your Gandi account |
98
+ | `list_domains` | List LiveDNS-managed domains. Auto-discovers organizations the user belongs to and unions their domains with the personal-org list, deduped by FQDN. Pass `sharing_id` to scope to a single organization. |
85
99
  | `list_records` | List records for a domain, with optional `type` and `nameFilter` (anchored, case-insensitive glob — `*` = any, `?` = one) |
86
100
  | `get_record` | Fetch a single record by name + type |
87
101
  | `add_record` | Add a new record; auto-backup runs first |
@@ -1,9 +1,10 @@
1
- import type { DnsRecord, Domain, RecordInput, Snapshot } from "./types.js";
1
+ import type { DnsRecord, Domain, Organization, RecordInput, Snapshot } from "./types.js";
2
2
  export declare class GandiClient {
3
3
  private readonly token;
4
4
  constructor(token: string);
5
5
  private request;
6
- listDomains(): Promise<Domain[]>;
6
+ listDomains(sharingId?: string): Promise<Domain[]>;
7
+ listOrganizations(): Promise<Organization[]>;
7
8
  listRecords(domain: string): Promise<DnsRecord[]>;
8
9
  getRecord(domain: string, name: string, type: string): Promise<DnsRecord>;
9
10
  addRecord(domain: string, input: RecordInput): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/gandi/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3E,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,MAAM;YAE5B,OAAO;IA8Bf,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIhC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAIjD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAMzE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5D,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/D,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAIlD,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAKvE"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/gandi/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKzF,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,MAAM;YAE5B,OAAO;IA8Bf,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAMlD,iBAAiB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI5C,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAIjD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAMzE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5D,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/D,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAIlD,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAQvE"}
@@ -1,5 +1,5 @@
1
1
  import { GandiError } from "./errors.js";
2
- const BASE_URL = "https://api.gandi.net/v5/livedns";
2
+ const BASE_URL = "https://api.gandi.net/v5";
3
3
  export class GandiClient {
4
4
  token;
5
5
  constructor(token) {
@@ -34,17 +34,23 @@ export class GandiClient {
34
34
  return undefined;
35
35
  return JSON.parse(text);
36
36
  }
37
- async listDomains() {
38
- return this.request("/domains");
37
+ async listDomains(sharingId) {
38
+ const params = new URLSearchParams({ per_page: "1000" });
39
+ if (sharingId)
40
+ params.set("sharing_id", sharingId);
41
+ return this.request(`/livedns/domains?${params.toString()}`);
42
+ }
43
+ async listOrganizations() {
44
+ return this.request("/organization/organizations");
39
45
  }
40
46
  async listRecords(domain) {
41
- return this.request(`/domains/${encodeURIComponent(domain)}/records`);
47
+ return this.request(`/livedns/domains/${encodeURIComponent(domain)}/records`);
42
48
  }
43
49
  async getRecord(domain, name, type) {
44
- return this.request(`/domains/${encodeURIComponent(domain)}/records/${encodeURIComponent(name)}/${encodeURIComponent(type)}`);
50
+ return this.request(`/livedns/domains/${encodeURIComponent(domain)}/records/${encodeURIComponent(name)}/${encodeURIComponent(type)}`);
45
51
  }
46
52
  async addRecord(domain, input) {
47
- await this.request(`/domains/${encodeURIComponent(domain)}/records`, {
53
+ await this.request(`/livedns/domains/${encodeURIComponent(domain)}/records`, {
48
54
  method: "POST",
49
55
  body: JSON.stringify({
50
56
  rrset_name: input.name,
@@ -55,7 +61,7 @@ export class GandiClient {
55
61
  });
56
62
  }
57
63
  async updateRecord(domain, input) {
58
- await this.request(`/domains/${encodeURIComponent(domain)}/records/${encodeURIComponent(input.name)}/${encodeURIComponent(input.type)}`, {
64
+ await this.request(`/livedns/domains/${encodeURIComponent(domain)}/records/${encodeURIComponent(input.name)}/${encodeURIComponent(input.type)}`, {
59
65
  method: "PUT",
60
66
  body: JSON.stringify({
61
67
  rrset_values: input.values,
@@ -64,16 +70,16 @@ export class GandiClient {
64
70
  });
65
71
  }
66
72
  async deleteRecord(domain, name, type) {
67
- await this.request(`/domains/${encodeURIComponent(domain)}/records/${encodeURIComponent(name)}/${encodeURIComponent(type)}`, { method: "DELETE" });
73
+ await this.request(`/livedns/domains/${encodeURIComponent(domain)}/records/${encodeURIComponent(name)}/${encodeURIComponent(type)}`, { method: "DELETE" });
68
74
  }
69
75
  async listSnapshots(domain) {
70
- return this.request(`/domains/${encodeURIComponent(domain)}/snapshots`);
76
+ return this.request(`/livedns/domains/${encodeURIComponent(domain)}/snapshots`);
71
77
  }
72
78
  async createSnapshot(domain, name) {
73
79
  const init = { method: "POST" };
74
80
  if (name !== undefined)
75
81
  init.body = JSON.stringify({ name });
76
- return this.request(`/domains/${encodeURIComponent(domain)}/snapshots`, init);
82
+ return this.request(`/livedns/domains/${encodeURIComponent(domain)}/snapshots`, init);
77
83
  }
78
84
  }
79
85
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/gandi/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/gandi/client.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,10 @@ export interface DnsRecord {
8
8
  export interface Domain {
9
9
  readonly fqdn: string;
10
10
  }
11
+ export interface Organization {
12
+ readonly id: string;
13
+ readonly name: string;
14
+ }
11
15
  export interface Snapshot {
12
16
  readonly id: string;
13
17
  readonly name?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/gandi/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/gandi/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"domains.d.ts","sourceRoot":"","sources":["../../src/tools/domains.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAInD,eAAO,MAAM,WAAW,EAAE,cAAc,CAAC,OAAO,CAAC,EAShD,CAAC"}
1
+ {"version":3,"file":"domains.d.ts","sourceRoot":"","sources":["../../src/tools/domains.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAUnD,eAAO,MAAM,WAAW,EAAE,cAAc,CAAC,OAAO,CAAC,EAiDhD,CAAC"}
@@ -1,12 +1,48 @@
1
1
  import { z } from "zod";
2
- const ListDomainsInput = z.object({}).strict();
2
+ import { GandiError } from "../gandi/errors.js";
3
+ const ListDomainsInput = z
4
+ .object({
5
+ sharing_id: z.string().min(1).optional(),
6
+ })
7
+ .strict();
3
8
  export const domainTools = [
4
9
  {
5
10
  name: "list_domains",
6
- description: "List all domains on this Gandi account.",
11
+ description: "List all LiveDNS-managed domains the authenticated user can see, including domains owned by any organization the user belongs to. Without `sharing_id`, results are the union of the personal organization and every reachable organization. Pass `sharing_id` to scope to one organization.",
7
12
  schema: ListDomainsInput,
8
- handler: async (_input, ctx) => {
9
- return ctx.client.listDomains();
13
+ handler: async (input, ctx) => {
14
+ const parsed = input;
15
+ if (parsed.sharing_id) {
16
+ return ctx.client.listDomains(parsed.sharing_id);
17
+ }
18
+ const personalDomains = await ctx.client.listDomains();
19
+ let orgs;
20
+ try {
21
+ orgs = await ctx.client.listOrganizations();
22
+ }
23
+ catch (err) {
24
+ if (err instanceof GandiError && (err.status === 403 || err.status === 404)) {
25
+ return personalDomains;
26
+ }
27
+ throw err;
28
+ }
29
+ const orgResults = await Promise.all(orgs.map((org) => ctx.client.listDomains(org.id).catch((err) => {
30
+ if (err instanceof GandiError && (err.status === 403 || err.status === 404)) {
31
+ return [];
32
+ }
33
+ throw err;
34
+ })));
35
+ const seen = new Set();
36
+ const merged = [];
37
+ for (const list of [personalDomains, ...orgResults]) {
38
+ for (const d of list) {
39
+ if (!seen.has(d.fqdn)) {
40
+ seen.add(d.fqdn);
41
+ merged.push(d);
42
+ }
43
+ }
44
+ }
45
+ return merged;
10
46
  },
11
47
  },
12
48
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"domains.js","sourceRoot":"","sources":["../../src/tools/domains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AAE/C,MAAM,CAAC,MAAM,WAAW,GAA8B;IACpD;QACE,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,yCAAyC;QACtD,MAAM,EAAE,gBAAgB;QACxB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;YAC7B,OAAO,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAClC,CAAC;KACF;CACF,CAAC"}
1
+ {"version":3,"file":"domains.js","sourceRoot":"","sources":["../../src/tools/domains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,MAAM,gBAAgB,GAAG,CAAC;KACvB,MAAM,CAAC;IACN,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACzC,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,CAAC,MAAM,WAAW,GAA8B;IACpD;QACE,IAAI,EAAE,cAAc;QACpB,WAAW,EACT,8RAA8R;QAChS,MAAM,EAAE,gBAAgB;QACxB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC5B,MAAM,MAAM,GAAG,KAAyC,CAAC;YAEzD,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACtB,OAAO,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACnD,CAAC;YAED,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAEvD,IAAI,IAAoB,CAAC;YACzB,IAAI,CAAC;gBACH,IAAI,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAC9C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,UAAU,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC;oBAC5E,OAAO,eAAe,CAAC;gBACzB,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACf,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACpD,IAAI,GAAG,YAAY,UAAU,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC;oBAC5E,OAAO,EAAc,CAAC;gBACxB,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC,CAAC,CACH,CACF,CAAC;YAEF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,KAAK,MAAM,IAAI,IAAI,CAAC,eAAe,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC;gBACpD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;oBACrB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;wBACtB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;wBACjB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBACjB,CAAC;gBACH,CAAC;YACH,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;KACF;CACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themkn/gandi-mcp",
3
- "version": "0.1.0",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server for Gandi LiveDNS — manage DNS records, domains, and zone snapshots from Claude Code or any MCP client.",
5
5
  "keywords": ["mcp", "mcp-server", "model-context-protocol", "gandi", "dns", "livedns", "claude", "claude-code"],
6
6
  "license": "MIT",