{
  "markdown": "<div align=\"center\">\n\n# eurodns-mcp\n\n**Manage your domains, DNS zones and subscriptions by asking for it.**\n\nA [Model Context Protocol](https://modelcontextprotocol.io) server for the EuroDNS User API —\ndomains, DNS zones, contacts, subscriptions, SSL, invoices and orders.\n\n[![CI](https://github.com/JigSawFr/eurodns-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/JigSawFr/eurodns-mcp/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![Node](https://img.shields.io/badge/node-%E2%89%A522-brightgreen.svg)](package.json)\n[![MCP](https://img.shields.io/badge/MCP-2026--07--28-8A63D2.svg)](https://modelcontextprotocol.io/specification/2026-07-28)\n[![npm](https://img.shields.io/npm/v/%40jigsawfr%2Feurodns-mcp.svg?color=CB3837&logo=npm)](https://www.npmjs.com/package/@jigsawfr/eurodns-mcp)\n[![Image](https://img.shields.io/badge/ghcr.io-eurodns--mcp-2496ED.svg)](https://github.com/JigSawFr/eurodns-mcp/pkgs/container/eurodns-mcp)\n[![MCP Badge](https://lobehub.com/badge/mcp/jigsawfr-eurodns-mcp)](https://lobehub.com/mcp/jigsawfr-eurodns-mcp)\n[![eurodns-mcp MCP server](https://glama.ai/mcp/servers/JigSawFr/eurodns-mcp/badges/score.svg)](https://glama.ai/mcp/servers/JigSawFr/eurodns-mcp)\n\n**[Documentation](docs/README.md)** · [Tools](docs/tools.md) · [Guardrails](docs/guardrails.md) · [Deploying](deploy/README.md)\n\n</div>\n\n> This is an independent open-source project. It is **not affiliated with, endorsed by, or\n> supported by EuroDNS**. \"EuroDNS\" is used only to identify the API this server talks to.\n\n> Written by a professional engineer with AI assistance. Every line was reviewed before it\n> was committed, and the responsibility for what it does is human.\n\n---\n\n## What you get\n\n- **Full API coverage in 44 tools** — every one of the 79 API operations, with each list/get\n  pair, profile create/update pair and on/off toggle folded into one tool; 63 tools once\n  billing and irreversible operations are enabled.\n- **Every tool and argument described** for the model that reads them: what it does, when to\n  prefer its neighbour, where an id comes from. A test holds the whole surface to it.\n- **Three DNS workflow tools** that make record edits safe, because saving a zone replaces it.\n- **Guardrails** so a deployment can refuse operations that spend money or destroy things.\n- **Four prompts** for the workflows worth naming, and **two resources**: the portfolio,\n  browsable, and one that says what this deployment allows — including why a tool you\n  expected is not in the list.\n- **Two transports** — `stdio` for a local client, streamable HTTP for a shared deployment.\n- **Both protocol eras on one endpoint** — speaks the 2026-07-28 revision natively and\n  still serves 2025-era clients, which is most of them today.\n- **OAuth 2.1 or a shared token** on HTTP, with an audit line per call.\n- **Queryable history** — ask the server what has been done, and by whom.\n- **1Password Connect** as an optional source for any secret it reads.\n\n## How it works\n\n```mermaid\nflowchart TD\n    client([\"MCP client\"])\n\n    client -->|stdio| registry\n    client -->|streamable HTTP| origin\n\n    subgraph http [\"HTTP transport only\"]\n        origin[\"Origin check\"] --> bearer[\"Bearer token<br/>OAuth 2.1 or shared secret\"]\n        bearer --> scopes[\"Scope gate\"]\n    end\n\n    scopes --> registry[\"Tool registry\"]\n    registry --> guard{\"Guardrails<br/>read-only, billing, destructive\"}\n    guard -->|refused| deny[\"Error naming the setting to change\"]\n    guard -->|allowed| api[(\"EuroDNS User API\")]\n\n    op[(\"1Password Connect\")] -.->|\"op:// refs, at startup\"| creds[\"Credentials\"]\n    creds -.-> api\n\n    registry -.-> log[(\"Audit log\")]\n    guard -.-> log\n    log -.->|eurodns_audit_query| client\n```\n\nTwo things are worth reading off that diagram. Authorisation has **two independent gates** —\nwhat the deployment permits at all, then what the caller's scopes permit within it. And the\naudit log is fed by **every** path, including refusals, because the upstream API\nauthenticates every caller with one shared key and cannot attribute anything itself.\n\n## Requirements\n\n- Node.js 22 or newer. Node 20 reached end of life on 30 April 2026 and receives no\n  security patches; the container image runs Node 24, the active LTS.\n- EuroDNS API credentials: an **Application ID** and an **API key**, created in the EuroDNS\n  dashboard under API access.\n- **The public IP of the machine running this server must be allowlisted** in the same\n  dashboard. A `403` from the API is almost always a missing allowlist entry, not bad\n  credentials.\n\n## Quick start\n\nFor a client that spawns the server itself, such as Claude Desktop:\n\n```json\n{\n  \"mcpServers\": {\n    \"eurodns\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@jigsawfr/eurodns-mcp\"],\n      \"env\": {\n        \"EURODNS_APP_ID\": \"your-application-id\",\n        \"EURODNS_API_KEY\": \"your-api-key\"\n      }\n    }\n  }\n}\n```\n\nTo try it in a terminal first:\n\n```bash\nnpx -y @jigsawfr/eurodns-mcp\n```\n\nWithout credentials it still starts, lists every tool, prompt and resource, and refuses each\ncall with a message naming the two variables — so a marketplace, or\n`npx @modelcontextprotocol/inspector`, can show the surface before you have a key.\n\nThe package ships two commands: `eurodns-mcp` for stdio, which is the one above, and\n`eurodns-mcp-http` for the HTTP transport — reachable as\n`npx -p @jigsawfr/eurodns-mcp eurodns-mcp-http`.\n\nFor a shared deployment over HTTP, use the container instead — see [Deployment](#deployment).\n\nTry not to put the API key in the client config file: [Secrets](docs/secrets.md) shows two\nways around it.\n\n## What you can ask it\n\n| Ask                                                   | Tool it reaches for                   |\n| ----------------------------------------------------- | ------------------------------------- |\n| \"What DNS records does example.com have?\"             | `eurodns_dns_get_zone`                |\n| \"Add a TXT record `_acme-challenge` on example.com\"   | `eurodns_dns_upsert_record`           |\n| \"What would change if I pointed www at 203.0.113.10?\" | `eurodns_dns_diff_zone`               |\n| \"Is example.lu available?\"                            | `eurodns_domain_check_availability`   |\n| \"Which of my domains have DNSSEC enabled?\"            | `eurodns_domain_search`               |\n| \"When does this SSL certificate expire?\"              | `eurodns_ssl_get_subscription`        |\n| \"What is my prepaid balance?\"                         | `eurodns_account_get_prepaid_balance` |\n| \"What did I change last week?\"                        | `eurodns_audit_query`                 |\n| \"What was refused, and why?\"                          | `eurodns_audit_query`                 |\n\n## Documentation\n\n| Page                                     | What it covers                                                          |\n| ---------------------------------------- | ----------------------------------------------------------------------- |\n| [Tools](docs/tools.md)                   | All 63 tools, the pairs they fold, the prompts and the two resources    |\n| [Guardrails](docs/guardrails.md)         | Risk classes, what a deployment can forbid, confirmation before a call  |\n| [Configuration](docs/configuration.md)   | Every environment variable, with its default                            |\n| [HTTP transport](docs/http-transport.md) | Serving several clients, static tokens, OAuth 2.1 and scopes            |\n| [Secrets](docs/secrets.md)               | Keeping the API key out of a client config, and 1Password Connect       |\n| [Audit log](docs/audit-log.md)           | What is recorded, the hash chain, asking the server what happened       |\n| [Entra ID](docs/entra-id.md)             | Step-by-step OAuth with Microsoft Entra ID, and its pitfalls            |\n| [Deploying](deploy/README.md)            | Containers, Fly.io, Render, Railway, and shipping the log to a SIEM     |\n| [Protocol](docs/protocol.md)             | Which MCP revisions are spoken, and how both are served at one endpoint |\n| [Development](docs/development.md)       | Building, testing, the generated tool surface, and how releases work    |\n\n## Deployment\n\n```bash\ncp .env.example .env    # credentials, plus a token: openssl rand -hex 32\ndocker compose up -d\ncurl localhost:3000/healthz\n```\n\nPublished images live at `ghcr.io/jigsawfr/eurodns-mcp`, built for `linux/amd64` and\n`linux/arm64` with a build provenance attestation.\n\nTwo things decide where this runs, and neither is the usual latency-or-price argument:\n\n- **The EuroDNS API filters by source IP**, so the host has to give you a stable — ideally\n  dedicated — egress address. An IP shared with other tenants keeps the mechanism and loses\n  the protection.\n- **The history query tool reads a file**, so the host needs a persistent disk. That rules\n  out platforms with an ephemeral filesystem.\n\nOn both counts Fly.io comes out ahead, at a couple of dollars a month for a dedicated IPv4\nagainst roughly $100 elsewhere. [`deploy/`](deploy/README.md) has the per-platform detail,\nready-made `fly.toml` and `render.yaml`, and the comparison in full.\n\nOne setting catches everyone once: inside a container the server listens on `0.0.0.0`, and\nit **refuses to start on a non-loopback address without authentication**. Set\n`EURODNS_MCP_AUTH` to `token` or `oauth`.\n\n### Which of those two\n\nThe choice is not about how secure you want to be — both are — but about whether callers need\nseparate identities, and it has a cost you should see coming.\n\n**`token`** is one shared secret in a header. It works in a minute, needs nothing but the\nserver, and is the right answer for a deployment one person uses. What you give up is\nattribution: the audit log records a label, so it can tell you a destructive call happened but\nnot who made it.\n\n**`oauth`** gives each person their own credential, lets the five scopes decide who may do\nwhat, and puts a real identity in the audit log. The scopes decide what a caller may do; your\nidentity provider decides who gets a token at all, and most default to everyone in the\ndirectory — so that setting is part of the configuration, not an afterthought. Its entry price is a hostname on a domain\nyour identity provider will accept — with Microsoft Entra ID that means a domain **verified in\nyour tenant**, because the server's public URL has to double as the Application ID URI. A\nplatform hostname like `*.fly.dev` cannot be verified, so the domain is not optional there.\n[Entra ID](docs/entra-id.md) works the whole thing through, including the errors it produces\nwhen the three names involved fall out of step, and how to go one step further and give each\nperson a _different_ set of scopes rather than the same one.\n\nStarting on `token` and moving to `oauth` later costs nothing but a restart: no data\nmigration, no change to how tools behave.\n\n## Contributing\n\n[CONTRIBUTING.md](CONTRIBUTING.md) has the workflow, and\n[docs/development.md](docs/development.md) the commands. Pull request titles are checked\nagainst [Conventional Commits](https://www.conventionalcommits.org), because that is what the\nchangelog is generated from.\n\n## License\n\n[MIT](LICENSE).\n\n\"EuroDNS\" is a trademark of EuroDNS S.A. and is not covered by that licence.\n",
  "bytes": 11297,
  "sha": "7c1fcfa7c3571aadbf8b2cd80d0886f4c91a47fde7c411830adf7fec2ea6f965",
  "repo_slug": "jigsawfr/eurodns-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_jigsawfr_eurodns_mcp_461d2c77/readme"
}