01Mokumfiets

Agentic SEO & llms.txt

An e-bike network rebuilt with a machine-first surface next to the human one, so assistants can read it and act on it.

Role
Architect, designer, engineer
Timeline
May–September 2026
Team
Solo
Scope
llms.txt, .md mirrors, 13 agent endpoints

Impact

  • 10,463First-time visitors, 4 May – 2 June 2026 (+2,682%)
  • 13Public agent endpoints, keyless
  • 39Hand-authored .md route mirrors
Agentic SEO & llms.txt — Mokumfiets
Contents

Premise

Discovery is moving from ten blue links to answers. When someone asks an assistant “where can I test-ride a Lemmo near Amsterdam Oost, and how do I cycle there?”, the winning site isn’t the one with the prettiest hero — it’s the one an agent can read and act on.

Mokumfiets is a Netherlands e-bike curation and recommended-dealer network. A React SPA is close to invisible to that class of reader: content lives behind hydration, and there is nothing to call once the agent has an intent. So I built a parallel, machine-first surface next to the human one and kept the two in lockstep.

The architecture at a glance

Three layers sit beside the React app, each answering a different question an agent asks: where do I look, what can I read, and what can I do. The fourth column is the part most sites skip, the governance that keeps the other three from drifting.

Agent-operable architecture — Mokumfiets
  1. 01

    Discovery — where do I look

    • /llms.txt directory
    • /.well-known/ai-plugin.json
    • /.well-known/mcp.json
    • /openapi.yaml
  2. 02

    Reading — what can I read

    • 39 hand-authored .md route twins
    • seo-render-markdown for the programmatic matrix
    • Canonical link back to the HTML page
    • AgentAffordance block in the HTML footer
  3. 03

    Action — what can I do

    • 13 keyless public endpoints
    • agent-list-* / agent-*-availability
    • agent-book-* with a 30-minute hold
    • agent-find-dealer and route tools
  4. 04

    Governance — how it stays true

    • seo-agent-localization skill
    • Triggers on any new user-facing feature
    • Seven pillars enforced before ship
    • Directory and .md updated in the same change

Discovery, reading, action and governance. The human SPA is unchanged; the machine surface runs beside it from the same content store.

Layer 1 — llms.txt, the front door

A single file at /llms.txt acts as the directory for every non-human reader, paired with /.well-known/ai-plugin.json, /.well-known/mcp.json and /openapi.yaml so ChatGPT, Claude and MCP-aware clients each find the format they expect.

Agentic permissions in plain English
Read every page and every .md sibling; act on a user’s behalf via the listed webhooks.
A courtesy rate, not a silent block
≤ 5 requests/sec from a single source, stated openly rather than enforced by mystery.
Explicitly welcomed crawlers
GPTBot, ClaudeBot, PerplexityBot, Google-Extended, CCBot and anthropic-ai.
A webhook registry
Full request/response shape of every public endpoint, so an agent can plan a call without a schema fetch.

Layer 2 — .md mirrors

Every important route has a Markdown twin. Thirty-nine are hand-authored and committed — /index.md, brand pages, /repair.md, /buy.md, /dealer.md, /manifesto.md, plus per-SKU sheets under /buy/* and /c/*. Each carries a canonical link back to the HTML page, so citation flows to the real URL.

Hand-writing a twin for a programmatic SEO matrix of thousands of URLs isn’t feasible, so those are generated on demand: the seo-render-markdown edge function resolves /compare/{a}-vs-{b}, /brands/{b}/for/{use-case}, /problems/{problem}/{brand} and /use-cases/{case}/in/{city}, pulls the stored editorial content, and returns clean Markdown with a one-hour edge cache. Pages still being drafted say so honestly rather than emitting thin content. An AgentAffordance block in the page footer closes the loop on the human side: it tells any crawler landing on the HTML where the Markdown twin, the llms.txt directory and the booking webhook live.

GET /repair.md
---
canonical: https://mokumfiets.com/repair
locale: en
updated: 2026-05-04
---

# Bike repair in Amsterdam

Drop-off, pickup and workshop repair across Oost, Centrum and Zuid.

## Book
POST /functions/v1/agent-book-repair { slot_id, email }
→ 30-minute hold, resume link, status_url
Every Markdown twin opens with a canonical link back to the HTML page, so a citation credits the real URL rather than the mirror.

Layer 3 — the skill that stops drift

The hard part isn’t building an agent surface; it’s stopping it from drifting the moment someone ships a new feature. The whole pipeline is encoded as an internal skill (seo-agent-localization) that triggers on any new user-facing feature and enforces seven pillars before it can ship.

The result: “agent-readable” is a property of the build process, not a project someone has to remember to re-run.

1 — SEO shell
Canonical, single H1, JSON-LD, page primitives.
2 — Markdown mirror
One .md twin per public route.
3 — Directory entries
llms.txt and openapi.yaml updated in the same change.
4 — Agent-callable endpoints
agent-list-*, agent-*-availability, agent-book-*.
5 — The hold pattern
30-minute email hold on reservations, resume link, expiry cron.
6 — Merchant feed variants
Feed shapes generated alongside the page.
7 — Localization
EN/NL/DE/FR/ES/DA with locale-prefixed URLs and full hreflang.
skills/seo-agent-localization.md
trigger: any new user-facing feature or public route

require:
  - seo_shell:        canonical + single H1 + JSON-LD
  - markdown_mirror:  one .md twin per public route
  - directory:        llms.txt + openapi.yaml updated in-change
  - endpoints:        agent-list-* / agent-*-availability / agent-book-*
  - hold_pattern:     30-min email hold + resume link + expiry cron
  - merchant_feed:    feed variant generated alongside the page
  - localization:     en/nl/de/fr/es/da + hreflang

on_fail: block release
The skill runs as a gate: a feature that fails any pillar does not ship, so the agent surface cannot drift behind the product.

Layer 4 — endpoints, including maps

The interesting bet was exposing actions, not just prose. Thirteen public functions are agent-facing today; all keyless, rate-limited and CORS-open: find and list dealers, book a test ride, book an at-home or in-shop repair, track a delivery, list tours and their availability, get a turn-by-turn cycling route, the maps-route / maps-semantic / maps-narrative core behind it, and mcp-fietsmaps exposing the same tools over MCP.

Maps is what made the network useful rather than merely listable. A route response returns polyline and GeoJSON, but also semantic_metadata, route_personality, ride_mood and a narrative_seed — enough for an assistant to say “twelve quiet minutes along the Amstel, one bridge, mostly brick” instead of dumping coordinates. Chained with agent-find-dealer, an assistant can answer the whole question — which shop, how far, how to ride there, and book the slot — without ever rendering the Mokumfiets UI.

Every action returns a status_url
The agent creates; the human owns, edits and cancels. No irreversible action hides inside a tool call.
Dealer listings are unweighted
No pay-to-rank ordering — which is exactly what makes an answer engine willing to quote them.
One question, four tool calls, a booked test ride

An assistant completes the job without rendering the Mokumfiets UI. Every action returns a status_url, so the human still owns the booking.

What happened

In the first full month after the agent surface and the programmatic matrix went live, 4 May to 2 June 2026, the site recorded 10,463 first-time visitors and 12,743 page views, against 376 first visits and 1,352 page views in the preceding 30 days: a 2,682% step-change in new visitors and 843% in views. Behaviour moved with it. Of 53,795 tracked events in the window, 3,639 were test-drive intents, up from 59 the month before.

The shape is a step-change rather than a curve. The month of May alone shows 7,738 views, up 546%, and 6,122 of them land on a single page: the free test-ride booking flow, which is also the page the agent endpoints resolve to. Discovery arrives in bursts because agents crawl and cite in bursts, but the floor it leaves behind is the point.

30 days before against 30 days after, same measurement window

Before — 4 Apr – 3 MayAfter — 4 May – 2 Jun

First visits
+2,682%
Before376
After10,463
Page views
+843%
Before1,352
After12,743
Test-drive intents
×62
Before59
After3,639

Each pair is scaled to its own row, so the before bars stay readable. First full month after launch.

Analytics event report for 4 May to 2 June 2026 showing 53,795 events, 12,743 page views and 10,463 first visits

4 May – 2 June 2026 vs 4 April – 3 May 2026: 10,463 first visits (+2,682%), 12,743 page views (+843%), 3,639 test-drive intents.

Analytics views report for 2 to 31 May 2026 showing 7,738 views led by the test-ride booking page

2–31 May 2026 vs 2 April – 1 May: 7,738 views (+546%), with 6,122 on the free test-ride booking flow the agent endpoints resolve to.

What I’d do differently

Three things would have been cheaper done first than retrofitted.

Brand the endpoint host from day one
llms.txt still publishes raw *.supabase.co function URLs while the rest of the docs moved to functions.mokumfiets.com; agents cache aggressively, so a host change is expensive after the fact.
Instrument the agent surface separately
Traffic spikes are visible, but per-endpoint attribution — which assistant, which tool, which conversion — should have been first-class from the start.
Ship the .md twin in the same commit as the page
Everything retrofitted cost more than the skill-enforced path did.

Next

Per-endpoint agent analytics, .md twins for the remaining long-tail matrix, and extending the maps tools with live dealer opening hours so an assistant can route to a shop that’s actually open.

Results

Outcomes

  • 10,463 first-time visitors and 12,743 page views between 4 May and 2 June 2026, up 2,682% and 843% on the preceding 30 days
  • Assistants can complete a booking end to end, so the network is reachable from surfaces Mokumfiets doesn’t own and doesn’t pay for
  • The Markdown twins made every programmatic page citable on day one instead of waiting on render budget for JS-heavy pages
  • MCP support means a Claude or ChatGPT user can attach Mokumfiets as a tool rather than a bookmark

Reflection

Learnings

  • Agent legibility is an architecture decision, not a content-marketing one
  • Exposing actions — not just prose — is what turns a listing into a destination an assistant can finish a task in
  • Trust is bought with reversibility and fairness: a status_url on every action, and rankings nobody paid for
  • Enforcing the pipeline in the build process is the only thing that keeps an agent surface true a month later
Back to all case studies

What I'm looking for

Product design, design systems, AI-native product.

Role
Senior / lead product design, design systems, AI product
Based
Amsterdam, Netherlands. Available in the EU and remote.
Availability
Open to conversations now