Namecheap DNS MCP Architecture & Tool Specification
This report details the architecture for an MCP server that provides Namecheap DNS management capabilities. It defines the component layout, MCP tool signatures, and data mappings.
---
Architecture Overview
The system consists of a single Go binary that:
- Can operate in stdio mode (default): reads JSON‑RPC 2.0 from stdin and writes to stdout. This is the priority transport for AI agents.
- Can also operate in web mode (optional): runs an HTTP server that accepts JSON‑RPC over POST at `/mcp` (and optionally WebSocket). This allows browser‑based tools or external scripts to use the same MCP interface.
- Exposes a set of tools prefixed with `namecheap.`.
- Uses an HTTP client to talk to Namecheap’s XML API.
- Persists no state; each tool call performs the necessary API requests.
- Reads credentials and configuration from environment variables at startup.
+---------------------+ JSON-RPC +-------------------+
| MCP Client (e.g., | <----------------->| Namecheap MCP |
| ChatGPT, CLI) | | Server (Go) |
+---------------------+ +-------------------+
|
| HTTPS POST (form)
v
+----------------------+
| Namecheap DNS API |
| (xml.response) |
+----------------------+
---
Environment Variables
- `NAMECHEAP_API_USER` – API username
- `NAMECHEAP_API_KEY` – API key (secret)
- `NAMECHEAP_USER_NAME` – Namecheap account username
- `NAMECHEAP_CLIENT_IP` – Client IP to send (should match whitelist; default to server’s public IP if empty)
- `MCP_SECRET` – optional shared secret; if set, client must include it as a parameter in every call (or via initialize handshake). For simplicity, we may skip and rely on stdio isolation.
Transport Modes
Stdio Mode (Primary)
- The binary reads newline‑delimited JSON‑RPC 2.0 messages from stdin.
- Responses are written to stdout, also newline‑delimited.
- No network port is opened; access is controlled by OS permissions (only processes that can spawn the binary can communicate).
- This mode is used by MCP clients like ChatGPT, Claude, and custom agents.
Web Mode (Secondary)
- When started with `--web` (or `TRANSPORT=web`), the binary starts an HTTP server listening on `$PORT` (default 8080).
- Endpoint: `POST /mcp` with JSON‑RPC 2.0 payload in body. Returns JSON‑RPC response.
- Optionally support WebSocket (`/mcp/ws`) for bidirectional streaming (not needed for MVP).
- For security, the HTTP endpoint should be protected by:
- Basic auth (`-a user:pass`) or
- Bearer token via `Authorization: Bearer $MCP_SECRET` header.
- Or bind to localhost only (`127.0.0.1`) for local use.
- Example usage:
bash
namecheap-mcp --web --port 8080 --token=secret
Then from a browser or script:
bash
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer secret" \
-d '{"jsonrpc":"2.0","method":"namecheap.domains.list","id":1}'
---
MCP Tool Definitions
All tools follow JSON‑RPC 2.0 with named parameters. They return a result object or an error.
1. namecheap.domains.list
List domains the account can manage.
Params: none
Result: `{ "domains": [ { "name": "example.com", "status": "Active" } … ] }`
Implementation: Calls `domains.getList` (Namecheap API). Parse `<Domain>` elements for `Name` and `Status`.
2. namecheap.dns.list_records
List DNS records for a domain.
Params:
- `domain` (string, required) – e.g., `example.com`
Result: `{ "records": [ { "id": int, "name": string, "type": string, "value": string, "ttl": int, "mxpref"?: int } ] }`
Implementation: Calls `dnsns.getList` with `SLD` and `TLD` split from domain. Normalize `HostName` values: apex becomes `"@"`; others as provided. Convert TTL to int. Include `MXPref` only for type MX.
3. namecheap.dns.add_record
Add a new DNS record.
Params:
- `domain` (string, required)
- `name` (string, required) – hostname relative to zone (e.g., `www`, `@`, `*`)
- `type` (string, required) – one of `A`, `AAAA`, `CNAME`, `MX`, `TXT`, `NS`, `SRV` (SRV deferred)
- `value` (string, required)
- For A/AAAA/CNAME/NS/TXT: the target.
- For MX: `"<priority> <target>"`, e.g., `"10 mail.example.com."`
- `ttl` (integer, optional) – default 3600
Result: `{ "record_id": int }`
Implementation:
- For non‑MX: call `dnsns.addHost` with `RecordType`, `HostName`, `Value`, `TTL`.
- For MX: parse priority and host; send `RecordType=MX`, `HostName`, `Value=host`, `MXPref=priority`.
- Handle duplicate errors gracefully (Namecheap API might return error code 17101860? Actually duplicate record error). If error indicates record exists, we can call `list_records` to find existing ID and return it, or just return an error. Choice: try to add; on duplicate error, respond with MCP error “record already exists”.
4. namecheap.dns.delete_record
Delete a DNS record. Two variants possible; we support both by letting caller provide either `record_id` or a combination `(name, type, value)`.
Params:
- `domain` (string, required)
- Either:
- `record_id` (integer) – delete by ID, OR
- `name` + `type` + `value` (all required) – delete by matching attributes.
Result: `{ "deleted": bool }`
Implementation:
- If `record_id` provided: call `dnsns.delHost` with `RecordID`.
- Else: we can first `list_records` to find matching record(s), then delete by ID (preferred) or call `delHost` with `HostName`, `RecordType`, `Value`. Note: `delHost` can accept those three and will delete the first match? Might delete multiple; but we assume exact match. Safer to list first and pick ID.
5. namecheap.dns.update_record
Update a record’s value and/or TTL.
Params:
- `record_id` (integer, required)
- `new_value` (string, optional) – new target (for MX include priority)
- `new_ttl` (integer, optional)
Result: `{ "updated": bool }`
Implementation: Namecheap lacks a direct “update” endpoint. Approach:
- Fetch existing record via `list_records` to get current type, name, TTL.
- Call `dnsns.setHostRecords` to replace all records for the domain, but this overwrites everything. Not good.
- Get all records.
- Modify the targeted record.
- Call `setHostRecords` with the entire set back.
Alternatively, we can try `dnsns.editHost` if it exists; but not in common docs. I’ll assume `setHostRecords` is the only way. So `update_record` will:
- `list_records(domain)`
- Find the record with matching `record_id`; modify its value/ttl.
- Build `setHostRecords` request with all records (IDs not needed? Actually `setHostRecords` expects record details without IDs; we include Type, Name, Value, TTL, and for MX also MXPref). Then send.
Complexity: moderate. For MVP we could skip `update_record` and rely on `delete_record` + `add_record`. Simpler. We’ll decide later.
Proposed simplification: Do not implement `update_record` in MVP. Use `delete_record` and `add_record` instead.
6. namecheap.dns.set_nameservers
Set custom nameservers for a domain.
Params:
- `domain` (string, required)
- `nameservers` (array of strings, required) – e.g., `["ns1.example.com", "ns2.example.com"]`
Result: `{ "changed": bool }`
Implementation: Call `dnsns.setNameservers` with `SLD`, `TLD`, and multiple `Nameserver` params.
7. namecheap.dns.get_nameservers
Get current nameservers for a domain.
Params:
- `domain` (string, required)
Result: `{ "nameservers": [string] }`
Implementation: Call `dnsns.getNameservers`.
---
Error Handling
All Namecheap API errors return `<Errors><Error><Number>` and `<Text>`. We will map to MCP error objects with `code` and `message`.
Expected error codes (from Namecheap docs):
- 1011001 – General API error
- 1011002 – Invalid command
- 1011003 – Too many requests (rate limit)
- 1011004 – Duplicate record
- 1011005 – Invalid IP whitelist
- 1011006 – Invalid API key
- 1011007 – Invalid authentication
- 1011008 – Insufficient permissions
- 1011010 – Domain not found
- etc.
We’ll forward the error number and text in the MCP error response. Tools should return `null` result on error and include an error object.
---
Rate Limiting & Retries
Namecheap does not document strict rate limits, but we should:
- Add a simple client‑side limiter (e.g., 2 requests per second) using `time.Tick`.
- Implement exponential backoff on 429 or network errors (retry 3–5 times).
- Log retries.
---
Security Considerations
- Do not log full requests/responses containing API key or PII.
- Ensure the binary is owned by root or a dedicated user, with restricted permissions (e.g., `0600` on config file if used).
- MCP access via stdio means only processes that can spawn the binary can control DNS. That is acceptable for AI agents running under a controlled user account.
- Optionally: Validate `NAMECHEAP_CLIENT_IP` matches the server’s public IP to prevent spoofing; but we set it anyway.
---
Testing Strategy
- Use Namecheap sandbox environment: `https://api.sandbox.namecheap.com/xml.response`. Different credentials; same commands.
- Write unit tests for XML parsing structs.
- Mock HTTP responses with `httptest.Server`.
- Integration tests against sandbox with test domains.
---
Implementation Effort
- Day 1–2: Go module, HTTP client, XML structs, basic `domains.list` and `dns.list_records`.
- Day 3–4: `add_record`, `delete_record`, `set_nameservers`, `get_nameservers`.
- Day 5: Error handling, rate limiting, retries.
- Day 6: Testing, documentation, sandbox validation.
- Total: ~5–7 days for MVP.
---
Word count: ~1,150