<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Patrick Rary</title>
    <description>The latest articles on DEV Community by Patrick Rary (@mogacode).</description>
    <link>https://dev.to/mogacode</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3944426%2Fdb663121-f367-42ca-8f87-85118a0f3163.png</url>
      <title>DEV Community: Patrick Rary</title>
      <link>https://dev.to/mogacode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mogacode"/>
    <language>en</language>
    <item>
      <title>Building an MCP server for a Swiss hosting provider (and what reverse-engineering its manager taught me)</title>
      <dc:creator>Patrick Rary</dc:creator>
      <pubDate>Thu, 21 May 2026 16:46:58 +0000</pubDate>
      <link>https://dev.to/mogacode/building-an-mcp-server-for-a-swiss-hosting-provider-and-what-reverse-engineering-its-manager-2fg9</link>
      <guid>https://dev.to/mogacode/building-an-mcp-server-for-a-swiss-hosting-provider-and-what-reverse-engineering-its-manager-2fg9</guid>
      <description>&lt;p&gt;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 &lt;code&gt;infomaniak-mcp-agent&lt;/code&gt;, runs locally over stdio. This post walks through what I learned, what's surprisingly hard, and what I'd do differently.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/Mogacode-ma/infomaniak-mcp-agent" rel="noopener noreferrer"&gt;https://github.com/Mogacode-ma/infomaniak-mcp-agent&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why an MCP for Infomaniak specifically
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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: &lt;em&gt;"audit all my DNS zones for missing DNSSEC, list every domain whose certificate expires in the next 30 days, and create a redirect from &lt;code&gt;www.legacy-site.be&lt;/code&gt; to &lt;code&gt;legacy-site.be&lt;/code&gt; on the production hosting."&lt;/em&gt; That's three API calls minimum, and the cognitive overhead of remembering the right endpoint each time is the friction I wanted to remove.&lt;/p&gt;

&lt;p&gt;MCP is the right shape for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tools are typed (Zod → JSON Schema → MCP)&lt;/li&gt;
&lt;li&gt;Side effects are explicit (idempotent? destructive? confirmation required?)&lt;/li&gt;
&lt;li&gt;The LLM doesn't need to know HTTP — it sees a catalogue of named operations&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The architecture in one paragraph
&lt;/h2&gt;

&lt;p&gt;A single Node 18+ binary, ESM, stdio transport. 54 tools registered with the MCP SDK, each backed by a thin function calling &lt;code&gt;api.infomaniak.com&lt;/code&gt; (Bearer token) or &lt;code&gt;manager.infomaniak.com/proxy/...&lt;/code&gt; (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.&lt;/p&gt;

&lt;p&gt;Install: &lt;code&gt;npx -y infomaniak-mcp-agent&lt;/code&gt;. Config: one env var (&lt;code&gt;INFOMANIAK_API_TOKEN&lt;/code&gt;), generated at &lt;a href="https://manager.infomaniak.com/v3/api-token" rel="noopener noreferrer"&gt;https://manager.infomaniak.com/v3/api-token&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The first surprise: the public API is missing half of what the manager does
&lt;/h2&gt;

&lt;p&gt;I started with the public Infomaniak API. Documented at &lt;a href="https://developer.infomaniak.com" rel="noopener noreferrer"&gt;https://developer.infomaniak.com&lt;/a&gt;, neat OpenAPI-ish spec, Bearer auth. Within a week I'd wrapped most read operations: list sites, list databases, list domains, list mailboxes.&lt;/p&gt;

&lt;p&gt;Then I tried to &lt;strong&gt;create a site&lt;/strong&gt;. The public POST endpoint returned 200 OK with a site ID. The site never appeared in the manager. No error. Just... nothing.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;manager.infomaniak.com/proxy/&amp;lt;int&amp;gt;/v3/api/proxypass_2/1/...&lt;/code&gt; with a different payload shape, and with two cookies (&lt;code&gt;SASESSION&lt;/code&gt; + &lt;code&gt;MANAGER-XSRF-TOKEN&lt;/code&gt;) instead of Bearer auth.&lt;/p&gt;

&lt;p&gt;The "public API" silently ignores the operation. The "manager-private API" actually creates the site.&lt;/p&gt;

&lt;p&gt;The same pattern holds for: database creation, FTP/SSH user creation, mailbox creation, redirection creation, password rotation. The public API is &lt;strong&gt;read-mostly&lt;/strong&gt;. Real automation requires the manager-private surface.&lt;/p&gt;

&lt;p&gt;This is documented honestly in the repo's &lt;a href="https://github.com/Mogacode-ma/infomaniak-mcp-agent/blob/main/REVERSE-ENGINEERING.md" rel="noopener noreferrer"&gt;REVERSE-ENGINEERING.md&lt;/a&gt;. The cookie extraction is done with &lt;code&gt;chrome-cookies-secure&lt;/code&gt; in memory only — nothing is written to disk.&lt;/p&gt;




&lt;h2&gt;
  
  
  The second surprise: Infomaniak's rate limit is shared per token
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I implemented a token-bucket in &lt;code&gt;src/throttle/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TokenBucket&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;refillPerMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;lastRefill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;capacityPerMinute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;capacityPerMinute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;capacityPerMinute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refillPerMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;capacityPerMinute&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;acquire&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;refill&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastRefill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refillPerMs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastRefill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrapped around every HTTP call. Workflows like &lt;code&gt;audit_dns_zones&lt;/code&gt; now run reliably across 50+ domains, just slower (1 second per call instead of 100 ms — but they finish).&lt;/p&gt;




&lt;h2&gt;
  
  
  The third surprise: destructive operations need a confirmation dance
&lt;/h2&gt;

&lt;p&gt;Claude is enthusiastic. Give it a tool called &lt;code&gt;delete_site&lt;/code&gt; and a thread of context saying "let's clean up old test sites", and it will happily delete production.&lt;/p&gt;

&lt;p&gt;The MCP spec has tool annotations (&lt;code&gt;destructiveHint&lt;/code&gt;, &lt;code&gt;idempotentHint&lt;/code&gt;) but they're hints — they don't enforce anything. I added a &lt;code&gt;requireConfirmation&lt;/code&gt; wrapper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// First call: returns a confirmation token, no destructive action yet.&lt;/span&gt;
&lt;span class="nf"&gt;delete_site&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;host_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12345&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;// → { confirmation_token: "abc...", expires_in_seconds: 60, "what_will_happen": "Site 'legacy-corp.be' (123 files, 2 databases) will be deleted." }&lt;/span&gt;

&lt;span class="c1"&gt;// Second call (within 60s): actually deletes.&lt;/span&gt;
&lt;span class="nf"&gt;delete_site&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;host_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;confirmation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;abc...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;// → { deleted: true, host_id: 12345 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first call describes what's going to happen and &lt;strong&gt;returns&lt;/strong&gt;. 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.&lt;/p&gt;

&lt;p&gt;This pattern saved me from production accidents twice already during dogfooding.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fourth surprise: MCP JSON Schema strictness varies across clients
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;zod-to-json-schema&lt;/code&gt; 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 &lt;code&gt;exclusiveMinimum: true&lt;/code&gt; (Draft 4 syntax) — they want &lt;code&gt;exclusiveMinimum: &amp;lt;number&amp;gt;&lt;/code&gt; (Draft 6+).&lt;/p&gt;

&lt;p&gt;A community contributor (@ruffzy) sent a PR fixing this by targeting &lt;code&gt;jsonSchema7&lt;/code&gt; explicitly in &lt;code&gt;zodToJsonSchema&lt;/code&gt; config. I merged it and shipped 0.8.2 within a day. Open source working as intended.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's hard about a hosting-provider MCP that isn't obvious
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Idempotency is the LLM's responsibility, but the tool author has to surface enough information&lt;/strong&gt;. The &lt;code&gt;list_hostings&lt;/code&gt; tool returns &lt;code&gt;is_locked: bool&lt;/code&gt; — if I hid that, the LLM would happily try operations on locked hostings and fail. Verbose output is fine; surprise failures aren't.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pagination has to be invisible&lt;/strong&gt;. 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error shapes must be normalized&lt;/strong&gt;. Infomaniak's public API returns &lt;code&gt;{error: {code, description}}&lt;/code&gt;. The manager-private API returns either that or &lt;code&gt;{"errors": [{"code", "description"}]}&lt;/code&gt; or raw HTML on auth failure. I wrote &lt;code&gt;InfomaniakError&lt;/code&gt; to flatten everything into a consistent &lt;code&gt;{kind, code, message, raw}&lt;/code&gt; so tools can handle errors uniformly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logs go to stderr, not stdout&lt;/strong&gt;. stdio transport mixes JSON-RPC and arbitrary writes on stdout, so any &lt;code&gt;console.log&lt;/code&gt; corrupts the protocol. I use &lt;code&gt;pino&lt;/code&gt; with stderr destination. If you build an MCP server, do this from day one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;npx -y&lt;/code&gt; requires &lt;code&gt;bin&lt;/code&gt; field + shebang in your built JS&lt;/strong&gt;. tsup config:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;js&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#!/usr/bin/env node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"infomaniak-mcp-agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/server.js"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Missing either and &lt;code&gt;npx -y&lt;/code&gt; either fails silently or runs the wrong entry point.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd do differently next time
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cookie-based manager auth is a maintenance debt&lt;/strong&gt;. 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse-engineering needs a version pinning strategy&lt;/strong&gt;. 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with tests, not tools&lt;/strong&gt;. I built the tools first and added tests later. Inverted, I'd have caught the rate-limit issue 3 weeks earlier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make the README the install path&lt;/strong&gt;. 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.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; infomaniak-mcp-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need an Infomaniak API token (&lt;a href="https://manager.infomaniak.com/v3/api-token" rel="noopener noreferrer"&gt;https://manager.infomaniak.com/v3/api-token&lt;/a&gt;) and to wire it into your MCP client. Full Claude Desktop / Claude Code config snippets in the &lt;a href="https://github.com/Mogacode-ma/infomaniak-mcp-agent#install" rel="noopener noreferrer"&gt;repo README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;If you're building an MCP server for &lt;em&gt;your&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;⭐ if it saved you time. PRs welcome.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>claude</category>
      <category>typescript</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
