# I built a free QR code API + MCP server on Cloudflare Pages — here's the whole design

> Source: <https://dev.to/zane_bin_e6f335ca1e4e3960/i-built-a-free-qr-code-api-mcp-server-on-cloudflare-pages-heres-the-whole-design-93g>
> Published: 2026-10-19 03:14:50+00:00

Most QR code generators are either ad-covered landing pages or SaaS products with a paywall behind "dynamic codes". I wanted the opposite: a generator that's free, private by default, and natively callable by AI agents. So I built [FreeQrly](https://freeqrly.com) — open source ([BryceBin/freeqrly](https://github.com/BryceBin/freeqrly), MIT), running entirely on Cloudflare Pages + D1.

Here's the architecture, including the part I think is most interesting: **one shared auth/quota core serving three surfaces — web UI, REST API, and MCP.**

**1. Static QR API — no key, no signup, CORS wide open**

```
GET https://freeqrly.com/api/qr?data=https://example.com&size=1024&color=2F4A62&bgcolor=ffffff&margin=4&ecc=M
```

Returns an SVG. `data` is required (400 with a clear error otherwise), everything else has sane defaults, input is validated (data ≤ 2000 chars, color format checked). Static codes are generated 100% in the browser on the web UI — the API just mirrors that server-side for agents and scripts.

**2. Dynamic codes — login-gated, editable after print**

Dynamic codes resolve through short `/q/xxxxxxx` links, so you can change the destination after printing and see scan stats (date / country / device / city breakdowns). Free tier: 8 codes. Login via Google, GitHub, or email+password (Turnstile-protected). The `/q/` hosted pages carry `noindex` + `X-Robots-Tag` so they never end up in search results.

**3. MCP — Streamable HTTP, no auth for static, token for dynamic**

```
POST https://freeqrly.com/mcp
```

`generate_qr` works with zero auth. Send an `Authorization: Bearer <token>` header and five more tools appear in `tools/list`: `dynamic_qr_create`, `dynamic_qr_list`, `dynamic_qr_update`, `dynamic_qr_delete`, `dynamic_qr_stats`.

The API-token flow reuses the UI's auth and quota logic instead of duplicating it:

`fqly_`, are shown exactly once, and only a SHA-256 hash is stored. Max 20 per account, revocable anytime.`functions/_lib/codes_api.js`) with the UI and the MCP tools — same validation, same 8-code quota, same ownership checks. A token can't create or revoke other tokens (that requires a login session), and deleting your account wipes your tokens.`403 quota_exceeded`; cross-user access returns `403`; revoked/unknown tokens return `401`.
Because it's one code path, the three surfaces can't drift apart: what the UI enforces, the API and MCP enforce identically. I verified this end to end with a real test account in a real browser — login, token creation, REST create→list→stats→update→scan-redirect→delete, MCP tool calls, then 401s after revocation — and cleaned up all test data afterward.

I added a `server.json` and published to the official MCP Registry as `io.github.BryceBin/freeqrly` (v1.1.0). Directories like Smithery and PulseMCP sync from the Registry, so one publish covers several. The repo also ships `llms.txt` and a `skills/qr-generator/SKILL.md` so agents can discover the API without reading docs.

`qr_download`, `batch_generate`, `scan_decode`) that I query with plain SQL.
If you run an agent that needs QR codes — event badges, WiFi cards, vCards, batch generation (CSV in, ZIP out) — the static API and MCP are free with no signup: `https://freeqrly.com/developers/` has the full reference. Happy to answer questions about the token/quota design in the comments.
