Skip to content

millsymills-com/gandi-mcp

gandi-mcp logo

gandi-mcp

Production-grade Python MCP server for the Gandi v5 API.

Status

Stage: S3 (Complete). Under active development. See CLAUDE.md for the architectural overview.

Features

  • 71 MCP tools covering Gandi Domains, LiveDNS, Email, Billing, Organization, and Certificates (34 read / 29 write / 8 purchase)
  • Three-tier safety model: readonly (default) → readwrite → readwrite + purchases, gated at both tool-visibility and handler-runtime layers
  • No-purchasing mode: tools that spend money (domain registration, renewal, transfer-in, cert issuance, mailbox slots) are hidden by default even in readwrite mode
  • Bearer auth with optional sharing_id scoping for reseller / multi-org accounts
  • Typed, linted, tested: strict ty, ruff, pytest, bandit

Quick Start

# Install from source
git clone https://github.com/millsymills-com/gandi-mcp.git
cd gandi-mcp
uv sync --extra dev

# Configure
cp .env.example .env
# Edit .env: set GANDI_TOKEN at minimum

# Run
uv run gandi-mcp

Global install (system-wide CLI)

To install gandi-mcp as a global binary on PATH (useful when wiring it into Claude Code without a per-project checkout):

# From the cloned repo
uv tool install /absolute/path/to/gandi-mcp

# Refresh after pulling new commits
uv tool upgrade --reinstall gandi-mcp

# Verify it's on PATH (the entry point starts the stdio server; Ctrl-C to stop)
command -v gandi-mcp

Configuration

See .env.example for every option.

Variable Default Description
GANDI_TOKEN - Required. Personal Access Token from https://admin.gandi.net/
GANDI_SHARING_ID - Optional organization UUID to scope all requests to
GANDI_MODE readonly readonly hides all write tools; readwrite exposes them
GANDI_ALLOW_PURCHASES false When true AND GANDI_MODE=readwrite, exposes tools that spend money
GANDI_API_BASE_URL https://api.gandi.net Override only for testing
GANDI_REQUEST_TIMEOUT 30 Request timeout in seconds
GANDI_MAX_RETRIES 3 Total request attempts including the first (1 = no retry)

Safety model

Three tiers, two orthogonal gates:

Mode Purchases Tools visible
readonly (default) n/a Read tools only
readwrite false (default) Read + non-purchasing writes (DNS records, contacts, mailbox edits, cert revoke, gandi_domain_delete, …)
readwrite true Everything including gandi_domain_register, gandi_domain_renew, gandi_domain_transfer_in, gandi_email_create_mailbox, gandi_email_create_slot, gandi_email_renew_mailbox, gandi_cert_issue, gandi_cert_renew

Defense-in-depth: every write tool also checks the mode at handler time, and every purchase tool also checks the purchase flag, so a stale tool list cached by an MCP client can't slip a write through.

Claude Code integration

Register the server in ~/.claude.json (global) or .claude/settings.json (project).

With a global install (uv tool install, recommended for global config; no project directory required):

{
  "mcpServers": {
    "gandi": {
      "command": "gandi-mcp",
      "env": {
        "GANDI_TOKEN": "your-bearer-pat-here",
        "GANDI_MODE": "readonly",
        "GANDI_ALLOW_PURCHASES": "false"
      }
    }
  }
}

From a working copy (picks up local edits live):

{
  "mcpServers": {
    "gandi": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/gandi-mcp", "run", "gandi-mcp"]
    }
  }
}

In the working-copy form, environment variables are read from .env in the project directory. In the global-install form, set them under env in the JSON or via your shell environment.

Default the global config to GANDI_MODE=readonly until you actively need writes; every session inherits the mode you set here.

Limitations

Gaps in Gandi's v5 REST API that this server cannot work around. Each requires manual action in the Gandi web UI:

  • Registrar transfer-lock toggle. v5 reports clientTransferProhibited in gandi_domain_get_domain / gandi_domain_get_status responses but exposes no PATCH/PUT endpoint to set it. Unlock from Domains → <domain> → Transfer lock before initiating a registrar transfer-out.
  • Email subscription cancellation. gandi_email_refund_slot only refunds an unused slot within the refund window. There is no v5 endpoint to stop a recurring email subscription. Cancel from Billing → Subscriptions.
  • Outbound transfer status / approval. v5 does not surface outbound-transfer state. Gandi sends an FOA email; approve there.

Development

# Lint and format
uv run ruff check src/ tests/
uv run ruff format --check src/ tests/

# Type check
uv run ty check src/gandi_mcp/

# Unit tests
uv run pytest tests/unit/ -v

# Full suite with coverage (matches CI gate: >=85% total, >=70% tools/*.py, >=90% core)
uv run pytest -m "not live" --cov=gandi_mcp --cov-report=term-missing --cov-report=json
uv run python scripts/check_coverage_thresholds.py

# Pre-commit hooks
uv run pre-commit install

License

Apache-2.0. See LICENSE.

About

Python MCP server for the Gandi v5 API: domains, LiveDNS, email, billing, organizations, and certificates. Three-tier safety model gates writes and purchases.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors