<?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: ImageAPI AI</title>
    <description>The latest articles on DEV Community by ImageAPI AI (@imageapi_ai_060a84ea83609).</description>
    <link>https://dev.to/imageapi_ai_060a84ea83609</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%2F4167083%2F99fb4465-e867-4000-9baf-8440c18ebc26.jpg</url>
      <title>DEV Community: ImageAPI AI</title>
      <link>https://dev.to/imageapi_ai_060a84ea83609</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/imageapi_ai_060a84ea83609"/>
    <language>en</language>
    <item>
      <title>I replaced my analytics dashboard with an MCP server on Cloudflare Workers</title>
      <dc:creator>ImageAPI AI</dc:creator>
      <pubDate>Tue, 06 Oct 2026 17:55:06 +0000</pubDate>
      <link>https://dev.to/imageapi_ai_060a84ea83609/i-replaced-my-analytics-dashboard-with-an-mcp-server-on-cloudflare-workers-4o85</link>
      <guid>https://dev.to/imageapi_ai_060a84ea83609/i-replaced-my-analytics-dashboard-with-an-mcp-server-on-cloudflare-workers-4o85</guid>
      <description>&lt;p&gt;Open source- &lt;a href="https://github.com/developeron29/agentlytics-mcp" rel="noopener noreferrer"&gt;https://github.com/developeron29/agentlytics-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I kept doing the same loop: open an analytics dashboard, stare at a chart, then paste numbers into my coding agent and ask "so what should I do next?". The agent was the part doing the thinking, and the dashboard was just a slow way to feed it data.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/developeron29/agentlytics-mcp" rel="noopener noreferrer"&gt;agentlytics-mcp&lt;/a&gt;: cookieless analytics for one website, with an &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP&lt;/a&gt; server built in. Claude Code, Cursor or Codex connect to it and query the traffic directly. It runs on your own Cloudflare account (one Worker plus a D1 database), and it's MIT licensed.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it looks like in practice
&lt;/h3&gt;

&lt;p&gt;You ask questions in plain words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Why did traffic jump yesterday?"&lt;/li&gt;
&lt;li&gt;"Where do people drop off between the homepage and signup?"&lt;/li&gt;
&lt;li&gt;"What's working, and what should I post next?"&lt;/li&gt;
&lt;li&gt;"Make a tracked link for my Reddit post, and check how it did next week."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent picks from 20 tools (&lt;code&gt;get_overview&lt;/code&gt;, &lt;code&gt;find_traffic_spikes&lt;/code&gt;, &lt;code&gt;explain_spike&lt;/code&gt;, &lt;code&gt;query_visits&lt;/code&gt;, &lt;code&gt;create_funnel&lt;/code&gt;, &lt;code&gt;create_tracked_link&lt;/code&gt;, &lt;code&gt;get_campaign_results&lt;/code&gt; and more) and answers with real numbers.&lt;/p&gt;

&lt;h3&gt;
  
  
  The architecture: one Worker, four jobs
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/t.js&lt;/code&gt;&lt;/strong&gt;: a tracker of about 1 KB. It counts pageviews (including SPA navigation) and custom events from &lt;code&gt;agentlytics.track("signup")&lt;/code&gt; or a &lt;code&gt;data-agentlytics-event&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/collect&lt;/code&gt;&lt;/strong&gt;: takes the hit, drops bots, derives the visitor id, and writes a row to D1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/mcp&lt;/code&gt;&lt;/strong&gt;: MCP as JSON-RPC over HTTP, behind a bearer key. I wrote it by hand, so the project has no runtime dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An hourly cron&lt;/strong&gt;: rolls events up into hourly totals, runs spike detection, and deletes raw events after 400 days.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Cookieless visitor counting
&lt;/h3&gt;

&lt;p&gt;There are no cookies and nothing is stored in the browser. To count unique visitors without tracking anyone, the visitor id is a two-step HMAC:&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;const&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;hmacHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HASH_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&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;floor&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="nx"&gt;DAY_MS&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;visitor&lt;/span&gt; &lt;span class="o"&gt;=&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;hmacHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The salt changes every day, so the same person gets a new id tomorrow, and the IP and user agent themselves are never stored. Only the browser and OS names and the country Cloudflare reports are kept.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spike detection an agent can explain
&lt;/h3&gt;

&lt;p&gt;"Traffic is up" isn't useful. "Traffic is up because of a Hacker News link to /blog/foo" is. Every hour, the cron:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compares each hour's pageviews with the &lt;strong&gt;median of the same hour-of-week over the previous 4 weeks&lt;/strong&gt; (or the same hour-of-day over the last 7 days for young sites).&lt;/li&gt;
&lt;li&gt;Uses the &lt;strong&gt;median absolute deviation&lt;/strong&gt; as the scale and flags hours with z ≥ 4 &lt;em&gt;and&lt;/em&gt; a minimum absolute lift, so tiny sites don't get alerts for 3 extra visits.&lt;/li&gt;
&lt;li&gt;Merges consecutive flagged hours into one spike, then &lt;strong&gt;attributes the excess&lt;/strong&gt; by comparing every source, page and event against its own baseline. Each cause gets a lift, a lift % and a share of the spike.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Annotations ("deployed v2", "posted on Reddit") made in the 24 hours before a spike show up next to it. When the agent publishes something through a tracked link and logs an annotation, the next spike explains itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;There's a Deploy to Cloudflare button that asks for three settings (your domain and two random keys). Or do it by hand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx wrangler d1 create agentlytics-mcp
npx wrangler secret put SITE_DOMAIN
npx wrangler secret put HASH_SECRET
npx wrangler secret put MCP_READ_KEY
npm run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then connect your agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--scope&lt;/span&gt; user &lt;span class="nt"&gt;--transport&lt;/span&gt; http agentlytics https://YOUR-WORKER/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_MCP_READ_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or point your coding agent at the repo's &lt;code&gt;AGENTS.md&lt;/code&gt; and let it do all of the above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limits
&lt;/h3&gt;

&lt;p&gt;One site per deploy, no dashboard (by design), and spike detection needs a few days of data before it's useful.&lt;/p&gt;

&lt;p&gt;If you'd rather not run it, I also run a hosted version, &lt;a href="https://measuremy.site" rel="noopener noreferrer"&gt;measuremy.site&lt;/a&gt;, with a dashboard, alerts and multi-site support. The repo has the same MCP tools.&lt;/p&gt;

&lt;p&gt;I'd love feedback, especially on the tool design: what would you want to ask your analytics that this can't answer?&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>cloudflare</category>
      <category>analytics</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
