8 April 2026
A question that used to take an afternoon now takes six seconds
I wanted deep, conversational access to my own smartwatch training data. Garmin doesn’t ship an official MCP server, and the community ones I tried all use the traditional tool-per-endpoint pattern: dozens of round-trips per question and painfully slow. So I built my own, using Code Mode instead.
That question used to be a CSV export and an afternoon in a spreadsheet. The two chats below are the same question asked two ways. First, a classic MCP server making sequential tool calls against typed Garmin endpoints. Second, the same underlying Garmin API wired up with Cloudflare’s Code Mode pattern, which collapses the whole thing into one code tool and one sandbox run.
The server is garmin-codemode-mcp: a Cloudflare Worker that registers exactly one tool instead of a dozen. It saves a lot of tokens, and it gives me a tighter security boundary than the tool-per-endpoint approach I would have shipped otherwise.
Approach 1: classic MCP, tool per endpoint
Many sequential round-trips. Each raw payload lands in the context window. Data has been changed for privacy.
Approach 2: Code Mode, one code tool
Same underlying Garmin API, wired up with Cloudflare's Code Mode pattern. One sandboxed round-trip. Only the fields the model asks for come back. Data has been changed for privacy.
How it fits together
The Worker still defines all the typed Garmin methods with zod schemas: list_activities, get_activity, health, trends, training, and a handful more. They just aren’t exposed as MCP tools. The MCP server registers a single tool, code, and uses the generated TypeScript surface of those methods as that tool’s description, so the model sees the full API at authoring time.
When the model calls code, the host Worker boots a child V8 isolate via Cloudflare’s Dynamic Worker Loader, injects a codemode proxy into it, and runs the model’s JavaScript inside. Each await codemode.list_activities(…) call from the sandbox is an RPC back across the boundary to the host, which holds the Garmin OAuth tokens and makes the actual HTTP request. The model writes ordinary await codemode.x(…) and never knows it crossed a process boundary.
Let the model choose what data comes back
Each Garmin activity is a JSON blob with dozens of fields: splits, stream samples, device metadata, HR zones, weather, training effect scores. A classic MCP server has no way to know which fields matter, so it hands the full payload to the model and hopes. Code Mode lets the model write code against a typed proxy instead. The methods are exposed with zod schemas, so it sees response shapes at authoring time and returns only the fields it needs.
Anthropic’s write-up on code execution with MCP puts a number on it: a Google Drive-to-Salesforce example dropped from 150,000 tokens to 2,000.
A tighter security boundary, for free
A classic MCP tool runs in your host process with network, env vars, and credentials all within reach. The Code Mode sandbox has none of that: no network, no env, no filesystem, just a typed proxy back to the host. Prompt-injection can’t exfiltrate because there’s no socket to open. Credentials can’t leak because the Garmin tokens never enter the sandbox. One boundary to audit instead of twelve, and it fails closed by default.
Reads and writes from the same tool
The same code tool that pulls data can push it back. Garmin’s workout API takes a structured JSON blob, so the model can read an article, design a session against it, build the JSON, and create the workout on the watch in one round-trip. No second tool, no second boundary.
Bonus: Code Mode can write back to Garmin too
Same single code tool, but this time it's building a structured Garmin workout JSON and pushing it to the watch in one sandboxed round-trip. Workout ID is fake.
Building and shipping it
I wrote zero lines of code in this repo.
I pointed Claude at the python-garminconnect repo as the reference for what the Garmin Connect API actually exposes, told it to use the Cloudflare docs MCP for anything Worker-related, and then iterated on the system prompt by hand until the model was reliably reaching for the right methods.
While iterating on the prompt I ran into an oversight in Cloudflare’s new agents package: the Code Mode helper didn’t expose an option to customise the description of the code tool itself, which is exactly where you want to tell the model about your specific proxy methods and give it any Code-Mode-specific guidance. I filed an issue that turned into a PR against the package. A nice side-effect of being an early user.
Shipping it was the shortest part of the build. A single wrangler deploy puts the Worker on Cloudflare’s edge, and Cloudflare’s Workers OAuth provider fronts the MCP server so that only I can connect. Claude Desktop (or any MCP client) hits the remote URL, bounces through a browser OAuth flow once, and then I can query my training data from anywhere Claude runs.
This works for any API, and across several at once
None of this is Garmin-specific. The same pattern drops onto any API with a decent surface area. A Xero Code Mode server could let the model reconcile invoices, pull a P&L, and draft a bill in one sandbox run instead of twenty tool calls. A Shopify one could answer “which variants under-performed last month and what would a 10% markdown do to margin?” by querying orders, joining against inventory, and returning just the summary. And because the sandbox is just code, a single server can expose proxies to several data sources at once: pull orders from Shopify, cross-reference them against Xero invoices, and hand back one answer. That is the shape of question that used to mean a CSV export and an afternoon in a spreadsheet.
The repo is on GitHub. If you want to run your own, setup and deploy instructions are in the README.