I spent the last six weeks building an unofficial MCP server for Infomaniak — the Swiss hosting provider — that lets Claude (and any MCP client) drive web hosting, mail, kDrive, DNS, SSL certificates and AI tools from natural language. It's MIT, on npm as infomaniak-mcp-agent, runs locally over stdio. This post walks through what I learned, what's surprisingly hard, and what I'd do differently.
Repo: https://github.com/Mogacode-ma/infomaniak-mcp-agent
Why an MCP for Infomaniak specifically
I run 200+ websites for clients across Belgium, Luxembourg, France and Morocco. Most live on Infomaniak (managed cloud, shared hosting, mail, DNS). Day-to-day operations are: provision a new site, swap a DNS record, add a mailbox, request an SSL cert, audit which domains expire in the next 60 days.
These tasks are all doable from the manager UI, all doable via the public API — but only one or two clicks/calls each, and they don't compose. Claude is good at composition: "audit all my DNS zones for missing DNSSEC, list every domain whose certificate expires in the next 30 days, and create a redirect from www.legacy-site.be to legacy-site.be on the production hosting." That's three API calls minimum, and the cognitive overhead of remembering the right endpoint each time is the friction I wanted to remove.
MCP is the right shape for this:
- Tools are typed (Zod → JSON Schema → MCP)
- Side effects are explicit (idempotent? destructive? confirmation required?)
- The LLM doesn't need to know HTTP — it sees a catalogue of named operations
The architecture in one paragraph
A single Node 18+ binary, ESM, stdio transport. 54 tools registered with the MCP SDK, each backed by a thin function calling api.infomaniak.com (Bearer token) or manager.infomaniak.com/proxy/... (cookie-authenticated). A token-bucket throttles to 60 req/min (Infomaniak's hard cap). Confirmation tokens for destructive operations (TTL 60s by default). Per-tool tests, ESLint, Prettier, gitleaks, CodeQL, vitest with 35% coverage and climbing.
Install: npx -y infomaniak-mcp-agent. Config: one env var (INFOMANIAK_API_TOKEN), generated at https://manager.infomaniak.com/v3/api-token.
The first surprise: the public API is missing half of what the manager does
I started with the public Infomaniak API. Documented at https://developer.infomaniak.com, neat OpenAPI-ish spec, Bearer auth. Within a week I'd wrapped most read operations: list sites, list databases, list domains, list mailboxes.
Then I tried to create a site. The public POST endpoint returned 200 OK with a site ID. The site never appeared in the manager. No error. Just... nothing.
I diffed the network tab of the manager's "Create site" wizard against what I was sending. The manager wasn't calling the public API at all. It was calling manager.infomaniak.com/proxy/<int>/v3/api/proxypass_2/1/... with a different payload shape, and with two cookies (SASESSION + MANAGER-XSRF-TOKEN) instead of Bearer auth.
The "public API" silently ignores the operation. The "manager-private API" actually creates the site.
The same pattern holds for: database creation, FTP/SSH user creation, mailbox creation, redirection creation, password rotation. The public API is read-mostly. Real automation requires the manager-private surface.
This is documented honestly in the repo's REVERSE-ENGINEERING.md. The cookie extraction is done with chrome-cookies-secure in memory only — nothing is written to disk.
The second surprise: Infomaniak's rate limit is shared per token
60 req/min sounds generous until you write a workflow that iterates over 50 domains and makes 3 calls each. You hit the limit in 30 seconds and Infomaniak starts returning 429 with a 60-second cool-off.
I implemented a token-bucket in src/throttle/:
class TokenBucket {
private tokens: number;
private readonly capacity: number;
private readonly refillPerMs: number;
private lastRefill = Date.now();
constructor(capacityPerMinute: number) {
this.capacity = capacityPerMinute;
this.tokens = capacityPerMinute;
this.refillPerMs = capacityPerMinute / 60_000;
}
async acquire(): Promise<void> {
while (this.tokens < 1) {
this.refill();
if (this.tokens < 1) await sleep(50);
}
this.tokens -= 1;
}
private refill(): void {
const now = Date.now();
this.tokens = Math.min(this.capacity, this.tokens + (now - this.lastRefill) * this.refillPerMs);
this.lastRefill = now;
}
}
Wrapped around every HTTP call. Workflows like audit_dns_zones now run reliably across 50+ domains, just slower (1 second per call instead of 100 ms — but they finish).
The third surprise: destructive operations need a confirmation dance
Claude is enthusiastic. Give it a tool called delete_site and a thread of context saying "let's clean up old test sites", and it will happily delete production.
The MCP spec has tool annotations (destructiveHint, idempotentHint) but they're hints — they don't enforce anything. I added a requireConfirmation wrapper:
// First call: returns a confirmation token, no destructive action yet.
delete_site({ host_id: 12345 })
// → { confirmation_token: "abc...", expires_in_seconds: 60, "what_will_happen": "Site 'legacy-corp.be' (123 files, 2 databases) will be deleted." }
// Second call (within 60s): actually deletes.
delete_site({ host_id: 12345, confirmation: "abc..." })
// → { deleted: true, host_id: 12345 }
The first call describes what's going to happen and returns. The LLM has to ask the human (or itself) "are you sure?" before the second call. The token expires after 60s. Multiple in-flight tokens per resource are allowed.
This pattern saved me from production accidents twice already during dogfooding.
The fourth surprise: MCP JSON Schema strictness varies across clients
zod-to-json-schema produces JSON Schema Draft 7. Anthropic API and Claude Desktop are happy with that. The MCP Inspector tool? Stricter. Some clients use Draft 2020-12 and reject exclusiveMinimum: true (Draft 4 syntax) — they want exclusiveMinimum: <number> (Draft 6+).
A community contributor (@ruffzy) sent a PR fixing this by targeting jsonSchema7 explicitly in zodToJsonSchema config. I merged it and shipped 0.8.2 within a day. Open source working as intended.
What's hard about a hosting-provider MCP that isn't obvious
Idempotency is the LLM's responsibility, but the tool author has to surface enough information. The
list_hostingstool returnsis_locked: bool— if I hid that, the LLM would happily try operations on locked hostings and fail. Verbose output is fine; surprise failures aren't.Pagination has to be invisible. Some Infomaniak endpoints page at 25 items, others at 50. The MCP tool always pages through everything and returns the merged list. Letting the LLM do pagination = it forgets, gets the first page only, and reasons over incomplete data.
Error shapes must be normalized. Infomaniak's public API returns
{error: {code, description}}. The manager-private API returns either that or{"errors": [{"code", "description"}]}or raw HTML on auth failure. I wroteInfomaniakErrorto flatten everything into a consistent{kind, code, message, raw}so tools can handle errors uniformly.Logs go to stderr, not stdout. stdio transport mixes JSON-RPC and arbitrary writes on stdout, so any
console.logcorrupts the protocol. I usepinowith stderr destination. If you build an MCP server, do this from day one.npx -yrequiresbinfield + shebang in your built JS. tsup config:
banner: { js: "#!/usr/bin/env node" }
And in package.json:
"bin": { "infomaniak-mcp-agent": "dist/server.js" }
Missing either and npx -y either fails silently or runs the wrong entry point.
What I'd do differently next time
- Cookie-based manager auth is a maintenance debt. The session cookies expire every few hours. Users have to re-open the manager in Chrome to refresh them. A long-lived service account would be cleaner if Infomaniak ever ships one.
- Reverse-engineering needs a version pinning strategy. The manager-private endpoints change without notice. I'd add a smoke-test workflow that hits a known set of endpoints daily and opens an issue when something 404s.
- Start with tests, not tools. I built the tools first and added tests later. Inverted, I'd have caught the rate-limit issue 3 weeks earlier.
- Make the README the install path. Anyone who lands on the npm page should be able to copy 3 lines and have it running in Claude Desktop. That's the win condition.
Try it
npx -y infomaniak-mcp-agent
You'll need an Infomaniak API token (https://manager.infomaniak.com/v3/api-token) and to wire it into your MCP client. Full Claude Desktop / Claude Code config snippets in the repo README.
If you're on Infomaniak and you hit a bug, open an issue with the exact tool call + response (sanitize tokens). I'll usually patch within a day.
If you're building an MCP server for your niche provider, the patterns above (token bucket, confirmation dance, error normalization, stderr-only logging) are reusable. The repo is MIT, fork it as a starting point.
⭐ if it saved you time. PRs welcome.





















