<?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: Zane Bin</title>
    <description>The latest articles on DEV Community by Zane Bin (@zane_bin_e6f335ca1e4e3960).</description>
    <link>https://dev.to/zane_bin_e6f335ca1e4e3960</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4193134%2F057a50f6-778a-49c2-addf-ca84c7ee60b4.png</url>
      <title>DEV Community: Zane Bin</title>
      <link>https://dev.to/zane_bin_e6f335ca1e4e3960</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zane_bin_e6f335ca1e4e3960"/>
    <language>en</language>
    <item>
      <title>I built a free QR code API + MCP server on Cloudflare Pages — here's the whole design</title>
      <dc:creator>Zane Bin</dc:creator>
      <pubDate>Mon, 19 Oct 2026 03:14:50 +0000</pubDate>
      <link>https://dev.to/zane_bin_e6f335ca1e4e3960/i-built-a-free-qr-code-api-mcp-server-on-cloudflare-pages-heres-the-whole-design-93g</link>
      <guid>https://dev.to/zane_bin_e6f335ca1e4e3960/i-built-a-free-qr-code-api-mcp-server-on-cloudflare-pages-heres-the-whole-design-93g</guid>
      <description>&lt;p&gt;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 &lt;a href="https://freeqrly.com" rel="noopener noreferrer"&gt;FreeQrly&lt;/a&gt; — open source (&lt;a href="https://github.com/BryceBin/freeqrly" rel="noopener noreferrer"&gt;BryceBin/freeqrly&lt;/a&gt;, MIT), running entirely on Cloudflare Pages + D1.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The three surfaces
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Static QR API — no key, no signup, CORS wide open&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://freeqrly.com/api/qr?data=https://example.com&amp;amp;size=1024&amp;amp;color=2F4A62&amp;amp;bgcolor=ffffff&amp;amp;margin=4&amp;amp;ecc=M
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns an SVG. &lt;code&gt;data&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Dynamic codes — login-gated, editable after print&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dynamic codes resolve through short &lt;code&gt;/q/xxxxxxx&lt;/code&gt; 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 &lt;code&gt;/q/&lt;/code&gt; hosted pages carry &lt;code&gt;noindex&lt;/code&gt; + &lt;code&gt;X-Robots-Tag&lt;/code&gt; so they never end up in search results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. MCP — Streamable HTTP, no auth for static, token for dynamic&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://freeqrly.com/mcp
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;generate_qr&lt;/code&gt; works with zero auth. Send an &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; header and five more tools appear in &lt;code&gt;tools/list&lt;/code&gt;: &lt;code&gt;dynamic_qr_create&lt;/code&gt;, &lt;code&gt;dynamic_qr_list&lt;/code&gt;, &lt;code&gt;dynamic_qr_update&lt;/code&gt;, &lt;code&gt;dynamic_qr_delete&lt;/code&gt;, &lt;code&gt;dynamic_qr_stats&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The design decision I'm happiest with
&lt;/h2&gt;

&lt;p&gt;The API-token flow reuses the UI's auth and quota logic instead of duplicating it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users create tokens in the Dashboard ("API Tokens" section). Tokens start with &lt;code&gt;fqly_&lt;/code&gt;, are shown exactly once, and only a SHA-256 hash is stored. Max 20 per account, revocable anytime.&lt;/li&gt;
&lt;li&gt;All five dynamic-code REST endpoints share one module (&lt;code&gt;functions/_lib/codes_api.js&lt;/code&gt;) 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.&lt;/li&gt;
&lt;li&gt;Quota violations return &lt;code&gt;403 quota_exceeded&lt;/code&gt;; cross-user access returns &lt;code&gt;403&lt;/code&gt;; revoked/unknown tokens return &lt;code&gt;401&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Publishing to the MCP ecosystem
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  What's deliberately boring
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No framework on the frontend for the generator core — vanilla JS, so the page stays fast and the logic stays auditable.&lt;/li&gt;
&lt;li&gt;SQLite via D1 for dynamic codes and token hashes. No ORM magic; the schema is tiny.&lt;/li&gt;
&lt;li&gt;Analytics: Cloudflare Web Analytics for pageviews + Analytics Engine events (&lt;code&gt;qr_download&lt;/code&gt;, &lt;code&gt;batch_generate&lt;/code&gt;, &lt;code&gt;scan_decode&lt;/code&gt;) that I query with plain SQL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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: &lt;code&gt;https://freeqrly.com/developers/&lt;/code&gt; has the full reference. Happy to answer questions about the token/quota design in the comments.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>api</category>
      <category>qrcode</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
