<?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: Kanak Waradkar</title>
    <description>The latest articles on DEV Community by Kanak Waradkar (@kakeroth).</description>
    <link>https://dev.to/kakeroth</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2765319%2F62f6bf3d-6a3d-4f3a-b5e4-83e952d677a1.jpg</url>
      <title>DEV Community: Kanak Waradkar</title>
      <link>https://dev.to/kakeroth</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kakeroth"/>
    <language>en</language>
    <item>
      <title>I put alot of effort into this one.I hope people appreciate it.</title>
      <dc:creator>Kanak Waradkar</dc:creator>
      <pubDate>Fri, 24 Apr 2026 16:11:11 +0000</pubDate>
      <link>https://dev.to/kakeroth/i-put-alot-of-effort-into-this-onei-hope-people-appreciate-it-1703</link>
      <guid>https://dev.to/kakeroth/i-put-alot-of-effort-into-this-onei-hope-people-appreciate-it-1703</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/kakeroth/the-boring-protocol-that-quietly-ate-the-cloud-50a4" class="crayons-story__hidden-navigation-link"&gt;The Boring Protocol That Quietly Ate the Cloud&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/kakeroth/the-boring-protocol-that-quietly-ate-the-cloud-50a4" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Google Cloud NEXT '26 Challenge Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/kakeroth" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2765319%2F62f6bf3d-6a3d-4f3a-b5e4-83e952d677a1.jpg" alt="kakeroth profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/kakeroth" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Kanak Waradkar
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Kanak Waradkar
                
              
              &lt;div id="story-author-preview-content-3546921" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/kakeroth" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2765319%2F62f6bf3d-6a3d-4f3a-b5e4-83e952d677a1.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Kanak Waradkar&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/kakeroth/the-boring-protocol-that-quietly-ate-the-cloud-50a4" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 24&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/kakeroth/the-boring-protocol-that-quietly-ate-the-cloud-50a4" id="article-link-3546921"&gt;
          The Boring Protocol That Quietly Ate the Cloud
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cloudnextchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cloudnextchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/googlecloud"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;googlecloud&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/kakeroth/the-boring-protocol-that-quietly-ate-the-cloud-50a4" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/kakeroth/the-boring-protocol-that-quietly-ate-the-cloud-50a4#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              1&lt;span class="hidden s:inline"&gt; comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            10 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>The Boring Protocol That Quietly Ate the Cloud</title>
      <dc:creator>Kanak Waradkar</dc:creator>
      <pubDate>Fri, 24 Apr 2026 16:09:52 +0000</pubDate>
      <link>https://dev.to/kakeroth/the-boring-protocol-that-quietly-ate-the-cloud-50a4</link>
      <guid>https://dev.to/kakeroth/the-boring-protocol-that-quietly-ate-the-cloud-50a4</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-cloud-next-2026-04-22"&gt;Google Cloud NEXT Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Act I — A Scene You've Already Lived
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdqkl7s937ng6k9076ld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdqkl7s937ng6k9076ld.png" alt="A Scene You've Already Lived" width="225" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is 2:47 AM.&lt;/p&gt;

&lt;p&gt;You have been staring at the same error trace for three hours. Your AI agent — the one you spent two weeks fine-tuning, the one your manager called "the future of the team's workflow" — is broken. Not because the model is wrong. Not because your logic is flawed. It is broken because the tool it needs to call speaks a different dialect than the one you wrote the connector for. The authentication handshake is slightly off. The schema your agent expects doesn't quite match what the API returns. You wrote a wrapper, then a wrapper for the wrapper, then a helper function to normalize the wrapper's output.&lt;/p&gt;

&lt;p&gt;You are not building intelligence. You are a translator in a room where everyone is shouting in a different language.&lt;/p&gt;

&lt;p&gt;This is not a fringe experience. This is the defining condition of modern AI development, and almost nobody is talking about it. Because while the conference halls of Las Vegas erupted for a new Gemini model and eighth-generation TPUs, something quieter happened at Google Cloud NEXT '26 that will matter far longer than any benchmark score.&lt;/p&gt;

&lt;p&gt;A protocol became the law of the land.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act II — The Tower of Babel, Serialized
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjazxh85inl5sk5uvepv5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjazxh85inl5sk5uvepv5.png" alt="The Tower of Babel" width="250" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To understand why the Model Context Protocol (MCP) is the most important announcement from Google Cloud NEXT '26, you have to understand the problem it kills. And to understand &lt;em&gt;that&lt;/em&gt; problem, you have to go back further than you think.&lt;/p&gt;

&lt;p&gt;In the beginning, there were APIs. This was supposed to solve everything.&lt;/p&gt;

&lt;p&gt;APIs were the handshake that let software systems talk to each other. They were elegant in theory: you expose endpoints, you document them, and any system in the world can connect. For a while, this worked. The internet was built on it.&lt;/p&gt;

&lt;p&gt;Then something changed. The internet didn't get smaller. It got bigger. Exponentially, chaotically bigger. And as it grew, every platform, every service, every product started developing its own dialect. REST. SOAP. GraphQL. gRPC. Webhooks. OAuth 1.0. OAuth 2.0. API keys in headers. API keys in query strings. Rate limits that differ per endpoint. Error codes that mean different things across different services. Every integration you built was, in reality, a custom translation layer — a bespoke diplomatic relationship between two systems that fundamentally did not understand each other's native tongue.&lt;/p&gt;

&lt;p&gt;This was tolerable when software was built by humans, for humans, at human speed.&lt;/p&gt;

&lt;p&gt;Then came the agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act III — The Shatter Point (December 2025, Largely Unnoticed)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fm%2F9Vuv9DxBJdEAAAAd%2Fbart-megaphone-bart.gif" class="article-body-image-wrapper"&gt;&lt;img width="560" height="429" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fm%2F9Vuv9DxBJdEAAAAd%2Fbart-megaphone-bart.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the moment historians will circle back to.&lt;/p&gt;

&lt;p&gt;In December 2025, before the spotlights, before the Vegas keynote stage, Google quietly launched fully managed remote MCP servers for Google Maps, BigQuery, Compute Engine, and Kubernetes Engine. No major press cycle. No splashy product announcement. Just a protocol, extended to four services, slipped into the changelog.&lt;/p&gt;

&lt;p&gt;That was the shatter point.&lt;/p&gt;

&lt;p&gt;Because here is what MCP actually is, stripped of the marketing language: it is a universal adapter for AI agents. Instead of an agent needing a custom-written integration for every tool it wants to use — instead of you, the developer, writing that integration — MCP defines a single, standardized way for agents to discover, connect to, and operate tools. One protocol. One handshake. Every tool that speaks MCP is instantly available to every agent that speaks MCP.&lt;/p&gt;

&lt;p&gt;It is the USB port for artificial intelligence. And in December 2025, Google plugged it into four of its services and said nothing about it.&lt;/p&gt;

&lt;p&gt;By April 2026, at Cloud Next NEXT '26, they plugged it into &lt;em&gt;everything.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Act IV — What Actually Happened in Las Vegas
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4u92vml7yyxw69o1042p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4u92vml7yyxw69o1042p.png" alt="What Actually Happened in Las Vegas" width="320" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While the crowd was applauding the Gemini 3.1 Pro benchmark numbers — and they are genuinely impressive numbers — the structural revolution was being announced three slides earlier, in language so dry it barely registered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quote from the Opening Keynote, Thomas Kurian:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"We've used the Model Context Protocol to turn every Google Cloud service into a tool that agents can orchestrate directly, enabling them to troubleshoot our own infrastructure using decades of our own telemetry."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read that sentence again. Not some Google Cloud services. Not the popular ones. &lt;em&gt;Every Google Cloud service&lt;/em&gt; is now an MCP server. Google turned its entire cloud infrastructure — the same infrastructure that runs YouTube, Search, and Android — into a unified, agent-readable tool surface.&lt;/p&gt;

&lt;p&gt;The implication is almost too large to process.&lt;/p&gt;

&lt;p&gt;If you are building an agent today on Google Cloud, you no longer write integration code for BigQuery. You do not write a connector for Cloud Storage. You do not negotiate with the IAM API. You point your agent at the MCP server and it figures out the tools available, their schemas, their capabilities — automatically, standardly, and with enterprise security baked in by default.&lt;/p&gt;

&lt;p&gt;The Workspace MCP Server, announced in preview at NEXT '26, extends this further. Agents can now synthesize Drive documents, draft Gmail responses, and manage Calendar logic — all through a single, standardized, open framework. The new Workspace CLI will allow agents to trigger these capabilities directly.&lt;/p&gt;

&lt;p&gt;And then there is Apigee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act V — The Apigee Revelation (The One Nobody Talked About)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20x15aw4hz2lf7tpkxhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20x15aw4hz2lf7tpkxhb.png" alt="The Apigee Revelation" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apigee is Google Cloud's API management platform. Most people think of it as a gateway — something that sits in front of your APIs and handles rate limiting and authentication. Boring infrastructure. Background noise.&lt;/p&gt;

&lt;p&gt;At NEXT '26, Apigee became something else entirely.&lt;/p&gt;

&lt;p&gt;Apigee now functions as an &lt;strong&gt;MCP bridge&lt;/strong&gt;. What this means in practice: any standard API — not just Google's, &lt;em&gt;any&lt;/em&gt; API — can be translated into a discoverable, agent-ready MCP tool. Existing security controls and governance policies carry over automatically.&lt;/p&gt;

&lt;p&gt;Think about what that sentence means. Every legacy REST API your organization has ever built. Every third-party service you've integrated. Every custom internal tool that runs on some vendor's proprietary endpoint. All of it can now be surfaced as a standardized, agent-accessible MCP server.&lt;/p&gt;

&lt;p&gt;The fragmentation problem doesn't get solved by replacing all your APIs. It gets solved by giving every existing API a universal translator. Apigee is that translator. And it quietly went live while everyone was watching the TPU benchmark charts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act VI — The Part Where It Gets Dangerous (And Why That's the Point)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fm%2Fg2vP0X3prSoAAAAC%2Fhomer-scared.gif" class="article-body-image-wrapper"&gt;&lt;img width="498" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fm%2Fg2vP0X3prSoAAAAC%2Fhomer-scared.gif" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the fear that lives at the center of every serious conversation about agentic AI: &lt;strong&gt;who controls what the agent can do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If an agent can call any tool, what stops it from calling the wrong one? What stops it from writing to a database it should only be reading? What stops a compromised agent from exfiltrating data through a tool it was never supposed to have access to in the first place?&lt;/p&gt;

&lt;p&gt;The old answer was: application logic. You write guardrails in code. You validate inputs. You build permissions into the software layer.&lt;/p&gt;

&lt;p&gt;The problem is that application logic is fragile. It can be bypassed. It can drift. In a world where agents are autonomous — where they run for days, chain tasks across dozens of tools, spawn sub-agents — trusting the application layer to enforce security is like trusting someone to lock the vault and also trusting them to not write the combination on the door.&lt;/p&gt;

&lt;p&gt;MCP, as implemented at Google Cloud NEXT '26, solves this at a different layer entirely.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Agent Gateway&lt;/strong&gt;, announced as the "air traffic control" system for the Gemini Enterprise Agent Platform, is the enforcement point. It understands MCP natively. Every tool call, every agent action, every data access request routes through the Gateway. And the Gateway is connected directly to Google Cloud's Identity and Access Management (IAM) system.&lt;/p&gt;

&lt;p&gt;The demo shown in the Developer Keynote made this tangible. A Planner Agent attempting to call a Finance MCP server — to read and write financial records — was stopped dead. Not by application code. Not by a hard-coded rule buried in a function somewhere. It was stopped because a developer had applied a single conditional IAM policy to the MCP server connection: &lt;code&gt;read-only = true&lt;/code&gt;. The write privilege evaporated, instantly, at the protocol layer.&lt;/p&gt;

&lt;p&gt;This is what zero-trust security looks like when it grows up. You do not write permission checks into every agent. You enforce permissions at the &lt;em&gt;protocol&lt;/em&gt; that every agent must use. Change the policy once, and every agent that touches that MCP server is immediately governed by it.&lt;/p&gt;

&lt;p&gt;One rule. Every agent. No exceptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act VII — The Ghost in the Protocol
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk6h018gyrvv9avzvzrh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk6h018gyrvv9avzvzrh.png" alt="The Ghost in the Protocol" width="250" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a concept in systems theory called &lt;strong&gt;antifragility&lt;/strong&gt; — the idea, coined by Nassim Nicholas Taleb, that some systems don't just survive stress and chaos; they actively grow stronger from it. They are built such that every shock to the system makes the structure more resilient, not less.&lt;/p&gt;

&lt;p&gt;The history of software integration has been, by every measure, fragile. Every new service added to your stack was a new point of failure. Every new agent you deployed was a new attack surface. Every new API you connected was a new diplomatic negotiation that could break at any time, without warning, for reasons entirely outside your control.&lt;/p&gt;

&lt;p&gt;MCP is not just a protocol. It is an antifragile architecture for the agentic era.&lt;/p&gt;

&lt;p&gt;Every new tool that adopts MCP makes every other MCP-compatible agent immediately more capable. Every new security policy applied at the Agent Gateway makes every connected agent immediately more secure. Every new service Google exposes as an MCP server makes the entire ecosystem more interconnected — not more fragile, but more robust. The network effect runs in the direction of stability, not entropy.&lt;/p&gt;

&lt;p&gt;Google adopted MCP — a protocol originally designed by Anthropic — as the foundation of its entire agentic infrastructure. Microsoft's A2A protocol, now at version 1.2 and running in production at 150 organizations, is designed as a &lt;em&gt;complement&lt;/em&gt; to MCP, handling agent-to-agent communication while MCP handles agent-to-tool communication. Salesforce runs it. ServiceNow runs it. SAP runs it. The Linux Foundation now governs A2A. The ecosystem is converging.&lt;/p&gt;

&lt;p&gt;This is what the end of the fragmentation era looks like. Not a single vendor winning. Not one cloud eating the others. A protocol becoming the shared grammar of an industry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act VIII — What This Means For You (The Practical Part)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkt0ag24hvrubb5n79hh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkt0ag24hvrubb5n79hh.png" alt="What This Means For You" width="300" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are building AI agents today — whether on Google Cloud, another platform, or your own infrastructure — MCP should change how you think about your architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop writing custom connectors.&lt;/strong&gt; Every hour you spend writing a custom integration between your agent and a tool is an hour you are spending on a problem that has already been solved. If the tool you need speaks MCP, you connect to it with a standard call. If it doesn't, Apigee can translate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Govern at the protocol layer.&lt;/strong&gt; Do not write security logic into your agents. Apply IAM policies to your MCP server connections. This gives you a single, auditable, enforceable control point for every agent in your ecosystem, regardless of which model is running underneath.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think in tool surfaces, not in integrations.&lt;/strong&gt; The mental model shift is from "this agent connects to these specific APIs" to "this agent has access to this governed surface of tools." The tools are discoverable. The capabilities are standardized. The security is inherited.&lt;/p&gt;

&lt;p&gt;Here is a minimal example of what connecting to a remote MCP server looks like in the Gemini Agent SDK today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.cloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;

&lt;span class="c1"&gt;# Connect your agent to the Cloud Storage MCP server
# No custom connector. No schema negotiation. No auth wrapper.
&lt;/span&gt;&lt;span class="n"&gt;agent_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AgentClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-agent-id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;List all objects in my-project-bucket and summarize the largest file.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mcp_servers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://storage.googleapis.com/mcp/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No custom authentication. No wrapper library. No three-hour debugging session at 2:47 AM. The agent discovers the tools, understands their schemas, and calls them — governed by whatever IAM policy you've already applied to your Cloud Storage resources.&lt;/p&gt;

&lt;p&gt;That is the reality MCP makes possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Circle
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frffk1ztul86h8u5c0gws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frffk1ztul86h8u5c0gws.png" alt="The Full Circle" width="225" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We began in a room where everyone was shouting in a different language. A developer, exhausted, writing wrappers for wrappers, trying to make a system that was supposed to be intelligent do something as simple as access the right tool in the right way.&lt;/p&gt;

&lt;p&gt;The Tower of Babel was not destroyed by building a better tower. It was dismantled by giving everyone a shared language.&lt;/p&gt;

&lt;p&gt;Google Cloud NEXT '26 was, on the surface, a showcase of computational power. Eighth-generation TPUs. Gemini 3.x models. New data center fabrics. A new IDE. The hardware was extraordinary. The models are genuinely capable.&lt;/p&gt;

&lt;p&gt;But the announcement that will define this era — the one that will matter in five years when we look back and ask how AI agents went from fascinating experiments to the operational backbone of every enterprise — was a protocol. A dry, specification-level decision about how agents should communicate with tools.&lt;/p&gt;

&lt;p&gt;The Gemini models get the headlines. The benchmarks get the LinkedIn posts.&lt;/p&gt;

&lt;p&gt;MCP gets the future.&lt;/p&gt;

&lt;p&gt;And while you were watching the keynote for the part with the big numbers, the boring protocol was quietly wiring itself into everything. Every service. Every tool. Every agent. Every organization.&lt;/p&gt;

&lt;p&gt;Not with a bang. Never with a bang.&lt;/p&gt;

&lt;p&gt;Just a handshake. A standard one. The same one, every time.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written in response to Google Cloud NEXT '26. All technical details sourced from official Google Cloud keynotes and blog posts, April 22–23, 2026.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/blog/topics/google-cloud-next/welcome-to-google-cloud-next26" rel="noopener noreferrer"&gt;Google Cloud Blog — Welcome to Google Cloud Next '26&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/blog/topics/google-cloud-next/next26-day-1-recap" rel="noopener noreferrer"&gt;Google Cloud Blog — Next '26 Day 1 Recap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://workspace.google.com/blog/product-announcements/10-more-announcements-workspace-at-next-2026" rel="noopener noreferrer"&gt;Google Workspace Blog — 10 More Announcements at Next '26&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thenextweb.com/news/google-cloud-next-ai-agents-agentic-era" rel="noopener noreferrer"&gt;The Next Web — Google Cloud Next 2026: AI Agents, A2A Protocol, and the Full-Stack Bet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=11PBno-cJ1g" rel="noopener noreferrer"&gt;Opening Keynote Livestream&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=A01DQ8_xy7Q" rel="noopener noreferrer"&gt;Developer Keynote&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>cloudnextchallenge</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>ChronoAgent: I Built an Agent That Reads My WhatsApp So I Stop Missing Deadlines</title>
      <dc:creator>Kanak Waradkar</dc:creator>
      <pubDate>Fri, 24 Apr 2026 06:07:39 +0000</pubDate>
      <link>https://dev.to/kakeroth/chronoagent-i-built-an-agent-that-reads-my-whatsapp-so-i-stop-missing-deadlines-34do</link>
      <guid>https://dev.to/kakeroth/chronoagent-i-built-an-agent-that-reads-my-whatsapp-so-i-stop-missing-deadlines-34do</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/openclaw-2026-04-16"&gt;OpenClaw Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I'm a second year computer engineering student.I often joke that my degree runs on Whatsapp.Often most students dont attend lectures but just see what someone has uploaded on whatsapp and do their work from there. I'm going to be honest with you about something embarrassing: I missed a lab submission last semester not because I didn't do the work, but because the deadline came through a WhatsApp group at 11 PM and I just didn't see it in time.&lt;/p&gt;

&lt;p&gt;The professor sent a reminder email three days before. Someone in the group chat forwarded it with "guys don't forget!!". Someone else sent a voice note I never opened. By the time I remembered, the portal was closed.&lt;/p&gt;

&lt;p&gt;This is not a unique experience. If you're a student in India — or honestly anyone whose professional life runs partially through WhatsApp — you know that deadlines don't live in one place. They're fragmented across group chats, Gmail threads, DMs, and the occasional Instagram message from a friend who remembered you hadn't registered for something yet.&lt;/p&gt;

&lt;p&gt;The "fix" people suggest is: "just use Google Calendar." Sure. Do you manually create an event every time someone texts you about something? I don't. Nobody does.&lt;/p&gt;

&lt;p&gt;So here comes our hero of the story OpenClaw.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExMHo0ZXhqOWU0azAycHI4NW9sZjlwN2xkenB5aW5sYnhpODA3dTNpdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2Fyr7n0u3qzO9nG%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img width="440" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExMHo0ZXhqOWU0azAycHI4NW9sZjlwN2xkenB5aW5sYnhpODA3dTNpdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2Fyr7n0u3qzO9nG%2Fgiphy.gif" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used OpenClaw
&lt;/h2&gt;

&lt;p&gt;I've played with n8n, with custom Python cron scripts, with Zapier. None of them had native WhatsApp access without either paying for a business API (which requires a separate phone number and a whole approval process) or running a fragile Selenium scraper.&lt;/p&gt;

&lt;p&gt;OpenClaw has WhatsApp built into its Gateway via Baileys. One command, one QR scan. Done. The agent immediately has access to every message I receive, treated as a first-class channel.&lt;/p&gt;

&lt;p&gt;The other thing that made OpenClaw the right fit was &lt;strong&gt;Standing Orders&lt;/strong&gt;. This is not a feature you'd expect to care about until you actually use it. The idea is simple: you write a file called &lt;code&gt;AGENTS.md&lt;/code&gt; in your workspace and the agent reads it every session. You define programs — "here's what you're authorized to do, here's when to do it, here's when to stop and ask me."&lt;/p&gt;

&lt;p&gt;For OpenClaw, the Standing Order looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Program: WhatsApp Deadline Monitor&lt;/span&gt;

&lt;span class="gs"&gt;**Authority:**&lt;/span&gt; Read all inbound WhatsApp messages. Extract and store deadline/event data.
Send review messages back to the user for ambiguous extractions.
&lt;span class="gs"&gt;**Trigger:**&lt;/span&gt; Every inbound WhatsApp message
&lt;span class="gs"&gt;**Approval gate:**&lt;/span&gt; Auto-write calendar for confidence ≥ 0.80. Request approval below that.
&lt;span class="gs"&gt;**Escalation:**&lt;/span&gt; If extraction fails 3 times in a row, alert me and pause.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single file is why it is an &lt;em&gt;agent&lt;/em&gt; and not a script. It has defined scope, defined escalation rules, defined approval gates. It knows when to act and when to ask. I wrote those rules once and now they govern every message that comes through, forever, without me thinking about it again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia3.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExejlmeTBkaXJoZ2o1bnlnbmo1YmRmNHExbHBxcDI5c2pvencxbm83ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2Fma4msCmcHpAoBLU6hl%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img width="324" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia3.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExejlmeTBkaXJoZ2o1bnlnbmo1YmRmNHExbHBxcDI5c2pvencxbm83ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2Fma4msCmcHpAoBLU6hl%2Fgiphy.gif" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;So I built ChronoAgent.&lt;/p&gt;

&lt;p&gt;ChronoAgent runs in the background on my laptop as an OpenClaw Gateway daemon. It has a WhatsApp channel connected (OpenClaw supports this natively through Baileys — no external service, no webhook nonsense, just scan a QR code). Every message that comes in gets silently processed. If it contains a deadline, a due date, a meeting, an exam — it gets extracted, deduplicated, and written to Google Calendar automatically.&lt;/p&gt;

&lt;p&gt;I don't prompt it. I don't open a chat window. It just runs.&lt;/p&gt;

&lt;p&gt;When it adds something to my calendar, it sends me a message back: "📅 Added: Assignment 3 submission on April 28, 11:59 PM". I can reply NO to undo it. That's the entire user interaction for 80% of cases.&lt;/p&gt;

&lt;p&gt;For things it's less sure about — "sometime next week we should meet bro" — it holds the event in a pending queue and asks me to confirm with a YES or NO reply. No calendar write until I say so.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnn5ltjdoer2eftr9h08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnn5ltjdoer2eftr9h08.png" alt="IT'S ALIVE" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code and setup instructions:&lt;/strong&gt; &lt;a href="https://github.com/Labreo/openclaw-calendar-agent" rel="noopener noreferrer"&gt;github.com/Labreo/openclaw-calendar-agent&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Here's the mistake I almost made: trying to have the LLM do everything.&lt;/p&gt;

&lt;p&gt;My first draft had the LLM reading messages, extracting dates, checking the calendar for duplicates, deciding whether to write, writing the event, formatting the confirmation message — all in one big chain of reasoning.&lt;/p&gt;

&lt;p&gt;It was slow. It was expensive. And it hallucinated duplicates constantly.&lt;/p&gt;

&lt;p&gt;The version that actually works is much dumber-looking on the surface, but it's solid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming Message
    │
    ▼
[Python: normalize to standard envelope]
{ source, sender, timestamp, raw_text }
    │
    ▼
[LLM: ONLY job is translation]
Input: raw text + today's date
Output: JSON array of extracted events with confidence scores
    │
    ▼
[Python: resolve relative dates]
"next Friday" → 2026-04-25T00:00:00
    │
    ▼
[Python: deduplicate]
Fuzzy title match + date proximity + semantic similarity
    │
    ▼
[Confidence router]
≥ 0.80 → write to calendar, notify me
0.50–0.79 → pending queue, ask me
&amp;lt; 0.50 → silent discard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM's job is exactly one thing: read messy human text and output clean structured JSON. That's it. Every other decision in the pipeline is deterministic Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjn3kfw4jmuguanbl23si.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjn3kfw4jmuguanbl23si.png" alt="Architecture Diagram" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The extraction prompt that actually worked
&lt;/h3&gt;

&lt;p&gt;Getting the LLM to reliably output JSON (and &lt;em&gt;only&lt;/em&gt; JSON) took more iteration than I expected. The trick was treating the model like a data parser in the system prompt, not like a conversationalist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;EXTRACTION_SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a deadline and event extraction engine.
Your ONLY output is a valid JSON array. No prose. No markdown. No explanation.

Given a message, extract every actionable deadline, due date, meeting, or event.
For each item output:
{
  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: string,
  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date_raw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: string,       // verbatim from the message
  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date_iso&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: string|null,  // resolved ISO 8601 if possible, else null
  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: float,      // 0.0 to 1.0
  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_quote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: string,   // the exact fragment that contains the deadline
  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: string      // deadline | meeting | exam | submission | event | other
}

Confidence guide:
  1.0  — explicit date + time + clear action
  0.85 — explicit date, no time
  0.70 — relative date that can be resolved
  0.55 — vague but likely actionable
  0.30 — might be an event, very unclear
  0.10 — references a past deadline

If NO events found, return: []
Today&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s date is injected in the user message.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;source_quote&lt;/code&gt; field was an afterthought that turned out to be the most useful field in the whole schema. Every Google Calendar event created by ChronoAgent has the original message fragment in its description. When I look at a calendar event three weeks later and don't remember what it's about, I can see "Original: 'bro the quiz is moved to Thursday 10am right?' — Source: WhatsApp, Sender: Abdullah". That's enough context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1g24ilk0nrtmcuxmfr9.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1g24ilk0nrtmcuxmfr9.webp" alt="Reading Emails" width="717" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The deduplication problem
&lt;/h3&gt;

&lt;p&gt;This was the hardest part of the whole project by a significant margin.&lt;/p&gt;

&lt;p&gt;The naive approach: ask the LLM "is this event already in my calendar?" Tried it. Terrible. Slow, expensive, and the model would confidently say "no duplicate found" when there obviously was one.&lt;/p&gt;

&lt;p&gt;The approach that works is a &lt;strong&gt;2-out-of-3 vote&lt;/strong&gt; between three deterministic checks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fuzzy title match&lt;/strong&gt; (Levenshtein ratio ≥ 0.85)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Date proximity&lt;/strong&gt; (within ±24 hours)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic similarity&lt;/strong&gt; (cosine similarity using a local &lt;code&gt;sentence-transformers&lt;/code&gt; model, threshold 0.80)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_duplicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embeddings_cache&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="c1"&gt;# Check 1: title similarity
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;lev_ratio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="c1"&gt;# Check 2: date proximity
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date_iso&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date_iso&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromisoformat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date_iso&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromisoformat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date_iso&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# 24 hours
&lt;/span&gt;            &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="c1"&gt;# Check 3: semantic similarity
&lt;/span&gt;    &lt;span class="n"&gt;vec_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_or_compute_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;embeddings_cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;vec_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_or_compute_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;embeddings_cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;cosine_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.80&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Assignment 3 due Friday" and "A3 submission end of week" — different titles, so Levenshtein fails. But they're semantically similar and the dates are within 24 hours, so they correctly merge as duplicates.&lt;/p&gt;

&lt;p&gt;The semantic model I used (&lt;code&gt;all-MiniLM-L6-v2&lt;/code&gt;) is 80MB and runs locally. No API call, no cost, ~10ms inference time. The LLM is completely out of the loop for dedup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia3.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExbTBidXdiZ281NGJ6b3hjcG53YW82dzgwNGo4am4zNTB2MWFyemhrdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2FeQACuze30PkNiS1eb7%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img width="480" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia3.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExbTBidXdiZ281NGJ6b3hjcG53YW82dzgwNGo4am4zNTB2MWFyemhrdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2FeQACuze30PkNiS1eb7%2Fgiphy.gif" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The 3 AM quota problem (a real thing that happened)
&lt;/h3&gt;

&lt;p&gt;Midway through building this, I burnt through my initial API credits testing the extraction pipeline on WhatsApp group traffic. A college group chat is... a lot of messages. Most of them "ok", "haha", "send notes pls" — but the extraction call fires on all of them before it knows they're non-events.&lt;/p&gt;

&lt;p&gt;I had two options: add pre-filtering, or switch to a cheaper model.&lt;/p&gt;

&lt;p&gt;I did both. Added a simple length and keyword pre-filter that drops messages under 15 words with no date-adjacent terms before they even hit the LLM. Dropped API usage by about 70% immediately.&lt;/p&gt;

&lt;p&gt;Then switched from Claude Sonnet to Gemini 2.5 Flash for the extraction step. Flash is significantly cheaper for this kind of structured output task and the JSON reliability was equivalent in my testing. I still use Claude for anything that requires more complex reasoning — the confidence routing logic and the digest generation — but for the high-volume extraction pass, Flash works well.&lt;/p&gt;

&lt;p&gt;The lesson: the best agentic systems aren't the ones throwing the strongest model at every task. They're the ones with smart orchestration about &lt;em&gt;when&lt;/em&gt; to call what.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia2.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExcnBnMTJla3NxcHVhd2J1bWNieHAxcmZvYmNjc2JreWZncGxxb2JmYSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2F3orifdO6eKr9YBdOBq%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img width="480" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia2.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExcnBnMTJla3NxcHVhd2J1bWNieHAxcmZvYmNjc2JreWZncGxxb2JmYSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2F3orifdO6eKr9YBdOBq%2Fgiphy.gif" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What "passive operation" actually means in practice
&lt;/h3&gt;

&lt;p&gt;The phrase "runs in the background" is easy to say and actually kind of hard to build well.&lt;/p&gt;

&lt;p&gt;OpenClaw's Gateway is a daemon — you run &lt;code&gt;openclaw onboard --install-daemon&lt;/code&gt; and it sets up a launchd/systemd service that starts automatically on login and stays running. The WhatsApp channel maintains a persistent Baileys session. Cron jobs handle the Gmail polling every 4 hours.&lt;/p&gt;

&lt;p&gt;The Standing Order hook fires on every inbound WhatsApp message without any scheduler. The agent just... responds to events. It's not polling. It's not a loop. It's closer to how a web server handles requests — always listening, processes when something arrives, goes quiet otherwise.&lt;/p&gt;

&lt;p&gt;The Gmail part is a Python script (&lt;code&gt;ingest_email.py&lt;/code&gt;) that the OpenClaw cron calls every 4 hours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw cron add &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; email-ingestion &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cron&lt;/span&gt; &lt;span class="s2"&gt;"0 */4 * * *"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout-seconds&lt;/span&gt; 120 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"Execute email ingestion per standing orders. Run ingest_email.py, process queue, run extraction and dedup on each envelope, route by confidence, write calendar entries, report summary."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cron message references the Standing Order rather than duplicating the logic. The agent reads its AGENTS.md, knows the full procedure, and executes it. I don't need to write a long prompt into the cron command because the authority is already defined.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia4.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExazd0aGs3azlwNXgyMWNtNDhsb3pqdXZ6a3EyYW14Zjk2ZmpoeTljYyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2FBn9AqquGZo7lqhJqHc%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img width="538" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia4.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExazd0aGs3azlwNXgyMWNtNDhsb3pqdXZ6a3EyYW14Zjk2ZmpoeTljYyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2FBn9AqquGZo7lqhJqHc%2Fgiphy.gif" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'd build next if I had more time
&lt;/h3&gt;

&lt;p&gt;The biggest gap right now is thread context. When someone says "bro don't forget about the thing on Friday" — that's a 0.3 confidence extraction at best. But if I had the last 5 messages of that WhatsApp thread, I'd probably know what "the thing" is. &lt;/p&gt;

&lt;p&gt;OpenClaw's session history tools make this possible but I didn't have time to implement it cleanly before the deadline. It's the next feature.&lt;/p&gt;

&lt;p&gt;The other thing is cross-source entity resolution. Right now if the same event appears in email and WhatsApp, the dedup engine usually catches it. But it runs independently per message — there's no weekly "let me look at everything I've collected and find clusters" pass. I have a consolidation script written but not wired into the cron yet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExcGozemM5NnEwcXhyeWVmcHE2b2ZpdWFqcmF6ZTQ5YmloMnJoazVxeiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2F3o6wNXmReHb4x5bIwU%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img width="480" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExcGozemM5NnEwcXhyeWVmcHE2b2ZpdWFqcmF6ZTQ5YmloMnJoazVxeiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2F3o6wNXmReHb4x5bIwU%2Fgiphy.gif" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ClawCon Michigan
&lt;/h2&gt;

&lt;p&gt;I didn't attend ClawCon Michigan this time around, but building ChronoAgent has made me want to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest summary
&lt;/h2&gt;

&lt;p&gt;ChronoAgent is not a product. It's a personal tool I built because I was genuinely failing at calendar management and the existing solutions didn't work for how I actually communicate (mostly WhatsApp, some email, basically no structured calendar input).&lt;/p&gt;

&lt;p&gt;OpenClaw made the WhatsApp part possible without fighting through Business API approvals. The Standing Orders made the "passive, always-on, doesn't need prompting" part possible without writing a custom daemon. The &lt;code&gt;exec&lt;/code&gt; tool made it easy to keep the heavy lifting in plain Python scripts that I can test independently.&lt;/p&gt;

&lt;p&gt;The thing I keep coming back to from this project: the right job for the LLM in an agentic system is usually much smaller than you initially think. Translation, intent recognition, natural language output — yes. Calendar math, deduplication logic, date resolution — no. The moment I moved those out of the LLM and into deterministic code, the whole system became faster, cheaper, and more reliable.&lt;/p&gt;

&lt;p&gt;Since I set it up, I've had zero missed deadline incidents. That's the only metric that matters.&lt;/p&gt;

</description>
      <category>openclawchallenge</category>
      <category>devchallenge</category>
      <category>openclaw</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Hijacked YouTube Music's DOM to Build a Custom Mini-Player 🎵</title>
      <dc:creator>Kanak Waradkar</dc:creator>
      <pubDate>Thu, 23 Apr 2026 01:58:18 +0000</pubDate>
      <link>https://dev.to/kakeroth/how-i-hijacked-youtube-musics-dom-to-build-a-custom-mini-player-3m23</link>
      <guid>https://dev.to/kakeroth/how-i-hijacked-youtube-musics-dom-to-build-a-custom-mini-player-3m23</guid>
      <description>&lt;h2&gt;
  
  
  The Challenge: Dealing with Aggressive UI "Pruning"
&lt;/h2&gt;

&lt;p&gt;YouTube Music is built with a highly responsive engine. When you narrow the window to a "mini" size—essential for a browser extension mini-player—YTM starts aggressively pruning the interface. One of the first things to go are the Like and Dislike buttons. They aren't just hidden; they are often completely stripped from the active layout to save space.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkerb19vqzzk3lje9dpvd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkerb19vqzzk3lje9dpvd.png" alt="Previous interface of the YouTube Music Player" width="712" height="1128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a multitasking user, having to expand the player just to "Like" a song is a major friction point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Strategy: Native Reparenting (DOM Hijacking)
&lt;/h2&gt;

&lt;p&gt;Initially, I tried simulating keyboard shortcuts (&lt;code&gt;Shift + =&lt;/code&gt;), but found it unreliable when the window lost focus. I also tried creating custom buttons, but syncing their state (Blue for liked, Red for disliked) with YTM's internal player was complex and prone to breaking.&lt;/p&gt;

&lt;p&gt;The solution? &lt;strong&gt;Native Reparenting.&lt;/strong&gt; Instead of building new buttons, I "stole" the official ones and moved them into my own persistent UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Locating the Native Renderer
&lt;/h3&gt;

&lt;p&gt;Even when hidden, the native &lt;code&gt;ytmusic-like-button-renderer&lt;/code&gt; component often persists in the DOM (hidden in the background player page). We simply need to find it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nativeRenderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ytmusic-like-button-renderer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Creating a Custom Pill Container
&lt;/h3&gt;

&lt;p&gt;To give the buttons a modern, floating feel, I created a custom "Pill Bar" with glassmorphism effects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ytm-pill-container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: The "Hijack"
&lt;/h3&gt;

&lt;p&gt;This is the magic line. By appending the native renderer to our new container, we move the actual functional component—including its click handlers and state management—out of its original location and into our floating UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nativeRenderer&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;nativeRenderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Clear any existing content to prevent duplicates&lt;/span&gt;
    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nativeRenderer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Overriding the Style Engine
&lt;/h3&gt;

&lt;p&gt;Because YTM's CSS still thinks these buttons should be hidden in narrow windows, we have to use high-priority &lt;code&gt;!important&lt;/code&gt; rules to force them back into visibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#ytm-pill-container&lt;/span&gt; &lt;span class="nt"&gt;ytmusic-like-button-renderer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;visible&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Result: A Robust, Draggable UI
&lt;/h2&gt;

&lt;p&gt;By using the native components, we get 100% reliable Like/Dislike functionality. We even added draggability so users can move the pill bar anywhere in the window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgihjnye6hof959dolqbm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgihjnye6hof959dolqbm.png" alt="New movable floating pill" width="754" height="1336"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/Labreo/ytm-miniplayer" rel="noopener noreferrer"&gt;Labreo/ytm-miniplayer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install Firefox:&lt;/strong&gt; &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/ytm-mini-mode/" rel="noopener noreferrer"&gt;YTM Mini Mode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chrome:&lt;/strong&gt; (Coming Soon)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Happy coding!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Tracking ML Experiments with MLflow: A Simple Guide for Beginners</title>
      <dc:creator>Kanak Waradkar</dc:creator>
      <pubDate>Thu, 10 Jul 2025 23:36:41 +0000</pubDate>
      <link>https://dev.to/kakeroth/tracking-ml-experiments-with-mlflow-a-simple-guide-for-beginners-4o38</link>
      <guid>https://dev.to/kakeroth/tracking-ml-experiments-with-mlflow-a-simple-guide-for-beginners-4o38</guid>
      <description>&lt;p&gt;Originally published on &lt;a href="https://diving-into-mlopsbeginners-guide.hashnode.dev/tracking-ml-experiments-with-mlflow-a-simple-guide-for-beginners" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction:
&lt;/h2&gt;

&lt;p&gt;This blog draws inspiration from the excellent &lt;a href="https://youtu.be/6ngxBkx05Fs" rel="noopener noreferrer"&gt;MLflow tutorial by CodeBasics&lt;/a&gt;, which clearly demonstrates the core concepts we will be discussing here. If you are looking for a more detailed or visual walk through, I highly recommend checking it out.&lt;/p&gt;

&lt;p&gt;This post is written from the perspective of a beginner and aims to offer a more hands-on, less theoretical explanation of MLflow, based on my own experience implementing it in a real project. Think of it as a beginner learning out loud.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is MLflow?
&lt;/h2&gt;

&lt;p&gt;MLflow is an open-source MLOps tool that helps you track, log, and manage everything involved in machine learning experiments including metrics, parameters, models, and other useful artifacts.&lt;/p&gt;

&lt;p&gt;It is especially useful when you are trying out different models or hyper parameters and need a way to compare them without losing track. Instead of manually noting results, MLflow stores everything in one place, helping you stay organized and productive.&lt;/p&gt;

&lt;p&gt;For example, in my Titanic survival prediction project, I used MLflow to log different models such as Logistic Regression and Random Forest, track accuracy scores, and compare results using the MLflow UI. This made it easy to identify the best-performing model for deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to install and setup MLflow?
&lt;/h2&gt;

&lt;p&gt;It is actually pretty straightforward to install into MLflow into your python notebook.Just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install mlflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now to setup MLflow we will be using my titanic-model code as a reference which can be located from my &lt;a href="https://github.com/Labreo/titanic-ml" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.For this example, I had trained and compared three different models: Logistic Regression, Random Forest, and Gradient Boosting Classifier.&lt;/p&gt;

&lt;p&gt;Here's a simplified version of the model setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.linear_model&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LogisticRegression&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.ensemble&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomForestClassifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GradientBoostingClassifier&lt;/span&gt;

&lt;span class="n"&gt;models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Logistic Regression&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;class_weight&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;random_state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8888&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;solver&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lbfgs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_iter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nc"&gt;LogisticRegression&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Random Forest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n_estimators&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;random_state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nc"&gt;RandomForestClassifier&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Gradient Boosting Classifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n_estimators&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;learning_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_depth&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;random_state&lt;/span&gt;&lt;span class="sh"&gt;"&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="nc"&gt;GradientBoostingClassifier&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we integrate MLflow to log the model parameters and performance metrics for each run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mlflow.sklearn&lt;/span&gt;

&lt;span class="c1"&gt;# Set the tracking URI (for local use)
&lt;/span&gt;&lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_tracking_uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:5000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Set the experiment name
&lt;/span&gt;&lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_experiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accuracy Model v3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;element&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="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Assume this contains the classification report for the model
&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_metrics&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recall_class_1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recall&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recall_class_0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recall&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;f1_score_macro&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;macro avg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;f1-score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="c1"&gt;# Log the trained model
&lt;/span&gt;        &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sklearn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registered_model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To view your runs visually, launch the MLflow tracking UI with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mlflow ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open your browser at &lt;a href="http://127.0.0.1:5000" rel="noopener noreferrer"&gt;&lt;code&gt;http://127.0.0.1:5000&lt;/code&gt;&lt;/a&gt;. You will see a dashboard that shows all your logged experiments, parameters, metrics, and models.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gpznqe8dbqau6d9gy0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gpznqe8dbqau6d9gy0l.png" alt="MLflow UI" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging Parameters, Metrics, and Models
&lt;/h2&gt;

&lt;p&gt;MLflow provides a clean API to track everything that matters in your experiments.&lt;/p&gt;

&lt;p&gt;Here are the three main functions you will use:&lt;/p&gt;

&lt;h3&gt;
  
  
  Logging Parameters
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;learning_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n_estimators&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the hyper parameters of your model that you might want to compare across runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logging Metrics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.875&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can log accuracy, precision, recall, or any other custom metric you calculate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logging the Model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sklearn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This saves your trained model in a format you can reload or even serve later.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Gets Stored and Where?
&lt;/h3&gt;

&lt;p&gt;Once you run your experiment, MLflow stores everything inside a folder called &lt;code&gt;mlruns&lt;/code&gt; in your project directory. Each experiment is given a unique ID, and inside that folder, you will find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;code&gt;params&lt;/code&gt; folder containing your logged parameters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;code&gt;metrics&lt;/code&gt; folder for evaluation scores&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;code&gt;artifacts&lt;/code&gt; folder which includes the saved model&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can compare runs side by side in the UI, which makes model selection and debugging much easier.These all can be then seen on the web application and MLflow will do most of the heavy lifting with management and data consolidation.Feel free to mess around with the UI and draw your own conclusions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc56tvuqd8okdh38qqpcy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc56tvuqd8okdh38qqpcy.png" alt="MLflow Comparison" width="800" height="826"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Things I Learned (or Struggled With)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Like any beginner working with a new tool, I ran into a few bumps while setting up MLflow and learned a lot in the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Confusion About MLflow Runs
&lt;/h3&gt;

&lt;p&gt;At first, I didn’t fully understand what a “run” was in MLflow. I was running multiple models, but everything was showing up under the same run or being overwritten. I realized I had forgotten to call &lt;code&gt;mlflow.start_run()&lt;/code&gt; with a unique &lt;code&gt;run_name&lt;/code&gt;.This could possibly fix it for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Gradient Boosting&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  MLflow UI Not Launching
&lt;/h3&gt;

&lt;p&gt;I tried running &lt;code&gt;mlflow ui&lt;/code&gt; and nothing happened. It turned out I had a port conflict on &lt;code&gt;5000&lt;/code&gt;, as another local server was already running.But it still wouldn’t run after making these changes.Using this command helped me launched it for the first time after which it launched normally using &lt;code&gt;mlflow ui&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mlflow server \
  --backend-store-uri sqlite:///mlflow.db \
  --default-artifact-root ./artifacts \
  --host 0.0.0.0 \
  --port 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you should be able to access everything as normal.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Next Steps&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now that I’ve successfully set up MLflow and tracked multiple models in my Titanic prediction project, here’s what I plan to learn next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DVC (Data Version Control):&lt;/strong&gt; To manage data and model files across versions and collaborators.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prefect:&lt;/strong&gt; To automate my training pipeline and possibly run it on a schedule.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m also working on a blog series covering each of these topics as I learn them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to Follow Along?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can check out my code and future updates here:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/Labreo/titanic-ml" rel="noopener noreferrer"&gt;https://github.com/Labreo/titanic-ml&lt;/a&gt;&lt;br&gt;&lt;br&gt;
I also post progress daily on Twitter:&lt;br&gt;&lt;br&gt;
&lt;a href="https://x.com/Kaker0th" rel="noopener noreferrer"&gt;https://x.com/Kaker0th&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're just starting with MLOps too, feel free to reach out or share what you're building. Let's learn and grow together!&lt;/p&gt;

</description>
      <category>mlflow</category>
      <category>mlops</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
  </channel>
</rss>
