<?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: Vaishnavi Mishra</title>
    <description>The latest articles on DEV Community by Vaishnavi Mishra (@vaishnavi_mishra_9a68c0b2).</description>
    <link>https://dev.to/vaishnavi_mishra_9a68c0b2</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%2F3648395%2F73515a27-7a8b-473e-99eb-f53dfb2f68f2.png</url>
      <title>DEV Community: Vaishnavi Mishra</title>
      <link>https://dev.to/vaishnavi_mishra_9a68c0b2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vaishnavi_mishra_9a68c0b2"/>
    <language>en</language>
    <item>
      <title>GrassMapper: An offline AI trail guide that actually runs without Wi-Fi (and has a tiny fox that judges you)</title>
      <dc:creator>Vaishnavi Mishra</dc:creator>
      <pubDate>Sun, 11 Oct 2026 18:24:12 +0000</pubDate>
      <link>https://dev.to/vaishnavi_mishra_9a68c0b2/grassmapper-an-offline-ai-trail-guide-that-actually-runs-without-wi-fi-and-has-a-tiny-fox-that-1bdi</link>
      <guid>https://dev.to/vaishnavi_mishra_9a68c0b2/grassmapper-an-offline-ai-trail-guide-that-actually-runs-without-wi-fi-and-has-a-tiny-fox-that-1bdi</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  "Go touch grass," they said.
&lt;/h2&gt;

&lt;p&gt;Every developer loves joking about &lt;em&gt;touching grass&lt;/em&gt; right up until we actually have to step away from our dual monitors and mechanical keyboards. &lt;/p&gt;

&lt;p&gt;When this week's Hacktoberfest challenge dropped with the theme "Touch Grass", my first reaction was: &lt;em&gt;alright, how do I build something that genuinely gets people outside without turning into another screen-time trap?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because here's the thing about most "outdoor" apps: they want your eyeballs the entire time you're outside. You're supposed to look down at your screen, track your pace, check your notifications, and fiddle with settings while walking past actual trees.&lt;/p&gt;

&lt;p&gt;Worse yet, the moment you get to a real trailhead—the kind where you can't hear traffic anymore—your phone signal drops to zero bars. And when you open any modern cloud AI assistant to ask about trail conditions or elevation gain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[!] Network Error: Unable to reach server. Please check your connection.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your \$1,000 phone is suddenly a paperweight, and the cloud AI you relied on is completely useless.&lt;/p&gt;

&lt;p&gt;So I spent this week building &lt;strong&gt;GrassMapper&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/IbYrkVe3WPk" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;GrassMapper&lt;/strong&gt; is an offline-first trail companion and outdoor habit OS powered by &lt;strong&gt;Google's open-weight Gemma 3&lt;/strong&gt; model running 100% locally on your machine via Ollama.&lt;/p&gt;

&lt;p&gt;It has one core rule: &lt;strong&gt;The screen should be the shortest part of your day.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You open it, pick where you want to wander, hit &lt;strong&gt;"Pocket Audio Briefing"&lt;/strong&gt;, shove your phone or laptop in your backpack, and go outside. It talks to you through your earbuds while you lace your hiking boots, gives you the trail rundown, and leaves you alone to enjoy the walk.&lt;/p&gt;

&lt;p&gt;And because it's built for real people with different setups, it works two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A cute web dashboard&lt;/strong&gt; running on Flask with interactive Leaflet maps, audio, and live streaming tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A terminal CLI&lt;/strong&gt; for when you just want to run &lt;code&gt;grassmapper plan&lt;/code&gt; in your shell and get right to the point.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Fun Stuff (Features I Actually Enjoyed Building)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🦊 1. Sprout the Fox (Your Trail Mascot)
&lt;/h3&gt;

&lt;p&gt;Let's be honest: productivity gamification can be pretty annoying. But I couldn't resist giving this project a mascot. &lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;Sprout&lt;/strong&gt; 🦊. She floats in the corner of your dashboard, keeping an eye on your outdoor habits. When you log a walk, identify a plant, or tick off a daily nature quest, she showers the screen with confetti and plays a little 8-bit celebration chime.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk3cnjdos1cjw0jp7fn1j.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk3cnjdos1cjw0jp7fn1j.png" alt="Intro to Sprout, she will be scoring how good you have been, be honest!" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fun engineering detail? &lt;strong&gt;Zero audio files or sound CDNs.&lt;/strong&gt; The chime is synthesized in real time using the browser's Web Audio API—three oscillators (triangle wave for warmth, sine for sustain, and a snappy attack envelope) vibrating straight out of your browser's audio context. Totally offline, 0 KB download.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌱 2. Grass Points (GP)
&lt;/h3&gt;

&lt;p&gt;You earn Grass Points for real-world outdoor acts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logging a hike (+15 GP)&lt;/li&gt;
&lt;li&gt;Identifying a bird or plant (+10 GP)&lt;/li&gt;
&lt;li&gt;Keeping an outdoor streak alive (+25 GP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You level up from &lt;strong&gt;Seedling 🌱&lt;/strong&gt; (0 GP) → &lt;strong&gt;Trail Scout 🥾&lt;/strong&gt; (50 GP) → &lt;strong&gt;Forest Ranger 🌲&lt;/strong&gt; (200 GP) → &lt;strong&gt;Peak Bagger ⛰️&lt;/strong&gt; (500 GP). &lt;/p&gt;

&lt;p&gt;It sounds silly, but seeing your little level progress bar move because you actually went for a 30-minute walk around the neighborhood is surprisingly satisfying.&lt;/p&gt;

&lt;h3&gt;
  
  
  🗺️ 3. The OpenStreetMap Trail Radar
&lt;/h3&gt;

&lt;p&gt;I didn't want Google Maps tracking anyone or hitting an API billing threshold. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbqzckil364x0c0yy6nje.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbqzckil364x0c0yy6nje.png" alt="OpenStreetMap, makes it all so visual and cool💕" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GrassMapper uses &lt;strong&gt;Leaflet.js&lt;/strong&gt; with OpenStreetMap tiles. It renders your trail as an animated polyline with custom pins for the trailhead 🏁, water/vista points 💧, and wildlife hotspots 🦅.&lt;/p&gt;

&lt;p&gt;You can change locations three ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Search bar:&lt;/strong&gt; Checks an offline dictionary of 21 famous national parks, peaks, and trails first (so search works with zero internet!), then gracefully falls back to OSM Nominatim if you're online.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click-to-place:&lt;/strong&gt; Click anywhere on the map, and the route re-centers right there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPS button:&lt;/strong&gt; One tap pulls your actual coordinates via browser geolocation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📱 4. It works straight from your phone
&lt;/h3&gt;

&lt;p&gt;You don't need a native app store download. If your laptop is running GrassMapper on your home Wi-Fi (or a mobile hotspot at a campsite), you just open &lt;code&gt;http://YOUR_LOCAL_IP:5000&lt;/code&gt; on your phone's browser. I tuned the meta tags and viewport so it feels like a native full-screen app.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq0d4anpfk2fdf7yo7cj4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq0d4anpfk2fdf7yo7cj4.png" alt="Generated trail highlights for Chandrashekhar Azad Park, Prayagraj, Uttar Pradersh, India" width="800" height="380"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe17yp6fjaxw8ngftt2kc.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe17yp6fjaxw8ngftt2kc.png" alt="The journel of all the trails generated and saved to track" width="800" height="380"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwnpynaisisq1cvt39v4h.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwnpynaisisq1cvt39v4h.png" alt="Nature Sighting &amp;amp; Field ID Log" width="799" height="377"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F32zl06ufhpblqba7yet9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F32zl06ufhpblqba7yet9.png" alt="Offline Naturalist Field Assistant" width="799" height="377"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Technical Stuff I Worked Around
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Gemma 3 Tool-Calling
&lt;/h3&gt;

&lt;p&gt;When I started, my plan was classic agentic AI: give Gemma 3 a SerpApi search tool via Ollama so it could look up trail conditions autonomously whenever it needed to.&lt;/p&gt;

&lt;p&gt;Then I ran it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama._types.ResponseError: registry.ollama.ai/library/gemma3 does not support tools (status code: 400)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turns out, the current Ollama build for Gemma 3 throws an immediate HTTP 400 if you pass a &lt;code&gt;tools=&lt;/code&gt; list. &lt;/p&gt;

&lt;p&gt;My initial thought was to downgrade to another model. But I really wanted Gemma 3—Google's open weights are incredibly snappy and have surprisingly rich botanical and geographical knowledge for their size.&lt;/p&gt;

&lt;p&gt;So instead of tool-calling loops, I built a &lt;strong&gt;pre-inference grounding pipeline&lt;/strong&gt; in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ User Input: "Kedarkantha Trek, moderate" ]
                    │
                    ▼
       Is SERPAPI_KEY available &amp;amp; online?
           ├── YES ──► Search SerpApi for recent trail reports &amp;amp; weather
           │           Inject results into system prompt as [GROUNDING]
           └── NO  ──► Skip quietly (graceful offline fallback)
                    │
                    ▼
     Stream Gemma 3 locally via simple_stream()
                    │
                    ▼
   Token-by-token SSE stream directly to browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It ended up being way better than native tool calling:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero latency wasted&lt;/strong&gt; on the model deciding &lt;em&gt;whether&lt;/em&gt; to call a tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero tool-call hallucination&lt;/strong&gt; or format errors.&lt;/li&gt;
&lt;li&gt;When you're offline in the woods, it just skips SerpApi without a hitch and lets Gemma 3 run pure local inference.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Architecture: Zero Cloud Lock-In
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────┐
│             Web Dashboard / Phone / CLI                │
│       Leaflet Map • Web Audio • Audio Briefing         │
└──────────────────────────┬─────────────────────────────┘
                           │
┌──────────────────────────▼─────────────────────────────┐
│             Flask Server (SSE Streaming)               │
│        Token-by-token output with 0 WebSockets         │
└──────────────┬──────────────────────────┬──────────────┘
               │                          │
┌──────────────▼─────────────┐   ┌────────▼──────────────┐
│  Ollama Engine (Local)     │   │ SerpApi Grounding     │
│  Gemma 3 Open Weights      │   │ (Optional, live data) │
└──────────────┬─────────────┘   └────────┬──────────────┘
               │                          │
               └───────────┬──────────────┘
                           │
┌──────────────────────────▼─────────────────────────────┐
│   SQLite (~/.grassmapper.db)  •  Sentry Agent Tracing  │
└────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything that matters—your journal, hike logs, sightings, habit streaks—is stored in a local SQLite file in your home directory (&lt;code&gt;~/.grassmapper.db&lt;/code&gt;). No telemetry, no account creation, no analytics tracking where you walk.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏆 Hacktoberfest Prize Alignment
&lt;/h2&gt;

&lt;p&gt;Here is how GrassMapper addresses the challenge prize categories:&lt;/p&gt;

&lt;h3&gt;
  
  
  🌟 Best Use of Gemma ($200)
&lt;/h3&gt;

&lt;p&gt;Gemma 3 is the primary intelligence here. It powers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trail Architect:&lt;/strong&gt; Custom itineraries with gear checklists, elevation notes, and difficulty ratings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Naturalist Guide:&lt;/strong&gt; Explaining wildlife sightings, botany facts, and seasonal changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trail Debriefs:&lt;/strong&gt; Reflecting on your journal entries and encouraging healthy outdoor habits.
All running locally via open weights, zero closed API reliance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🚀 Best Use of Render ($200)
&lt;/h3&gt;

&lt;p&gt;GrassMapper comes with a production-ready &lt;code&gt;render.yaml&lt;/code&gt; Blueprint and &lt;code&gt;requirements.txt&lt;/code&gt;. With one click, an outdoor club, scout troop, or university hiking group can host a shared community instance of GrassMapper on Render backed by Gunicorn.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔍 Best Use of SerpApi ($100)
&lt;/h3&gt;

&lt;p&gt;AI models have knowledge cutoffs. Trails have seasonal mud seasons, flash flood warnings, and bridge closures. When internet is available, SerpApi grounds Gemma 3 with live trail reports and weather data right before generation runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 Best Use of Sentry Agent Tracing ($100)
&lt;/h3&gt;

&lt;p&gt;Every single AI interaction is wrapped with &lt;code&gt;sentry_sdk&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;op="ai.inference"&lt;/code&gt; traces Ollama token generation time and model latency.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;op="tool.search"&lt;/code&gt; traces SerpApi roundtrips.
You get crystal-clear flame graphs showing exactly where your time went during every trail plan generation.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It in 2 Minutes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Make sure you have Ollama &amp;amp; Gemma 3
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# From https://ollama.com&lt;/span&gt;
ollama pull gemma3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Clone &amp;amp; Install
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/your-username/grassmapper.git
&lt;span class="nb"&gt;cd &lt;/span&gt;grassmapper
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. (Optional) Add your API keys in &lt;code&gt;.env&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SERPAPI_KEY=your_key_here
SENTRY_DSN=your_dsn_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Both are optional! It works completely fine offline without them.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Fire it up!
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;To open the Web Dashboard:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:5000&lt;/code&gt; (or &lt;code&gt;http://YOUR_LOCAL_IP:5000&lt;/code&gt; on your phone).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Or stay in the terminal:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python grassmapper.py plan        &lt;span class="c"&gt;# Generate a trail plan&lt;/span&gt;
python grassmapper.py log         &lt;span class="c"&gt;# Log a finished hike&lt;/span&gt;
python grassmapper.py observe     &lt;span class="c"&gt;# ID a wildflower or bird&lt;/span&gt;
python grassmapper.py stats       &lt;span class="c"&gt;# Check your Grass Points &amp;amp; streak&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;We build so much software that tries to capture attention and hold it hostage inside a browser tab. &lt;/p&gt;

&lt;p&gt;Building GrassMapper was a breath of fresh air—literally. It was fun to build an AI app where the definition of success isn't "how many minutes did the user spend on this page?", but &lt;strong&gt;"did the user shut their laptop and step outside?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've been glued to your IDE all day: save your files, run &lt;code&gt;python grassmapper.py plan&lt;/code&gt;, and go touch some grass. You've earned it. 🌿🥾&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Disclosure
&lt;/h2&gt;

&lt;p&gt;This project utilizes Google's open-weight Gemma 3 model running locally via Ollama to generate offline trail itineraries, nature observations, and outdoor journaling reflections. AI assistance was also used during development for drafting documentation, structuring test cases, and writing code. All core application logic, database schemas, web endpoints, and Leaflet map integrations were implemented, tested, and verified with combined efforts of me and my coding buddies (bunch of AI coding tools an software).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Source Code:&lt;/strong&gt; &lt;a href="https://github.com/VaishnaviOnPC/grassmapper" rel="noopener noreferrer"&gt;github.com/VaishnaviOnPC/grassmapper&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Built with:&lt;/strong&gt; Python, Flask, Gemma 3, Ollama, Leaflet.js, SerpApi, Sentry, Render&lt;br&gt;&lt;br&gt;
&lt;strong&gt;License:&lt;/strong&gt; MIT&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>serpapi</category>
    </item>
    <item>
      <title>🧟‍♂️ Stitching the Spirit: Engineering a "Frankenstein" AI Agent with Kiro &amp; MCP</title>
      <dc:creator>Vaishnavi Mishra</dc:creator>
      <pubDate>Fri, 05 Dec 2025 18:22:50 +0000</pubDate>
      <link>https://dev.to/vaishnavi_mishra_9a68c0b2/stitching-the-spirit-engineering-a-frankenstein-ai-agent-with-kiro-mcp-4okb</link>
      <guid>https://dev.to/vaishnavi_mishra_9a68c0b2/stitching-the-spirit-engineering-a-frankenstein-ai-agent-with-kiro-mcp-4okb</guid>
      <description>&lt;p&gt;Most AI hackathon projects are just chatbots with a fancy wrapper. For &lt;strong&gt;Kiro-ween&lt;/strong&gt;, the theme was 'Frankenstein,' so I took that literally. I didn't want another polished, polite chatbot. I wanted to stitch together a bunch of messy, incompatible tech stacks into something that felt alive—and maybe a little dangerous.&lt;/p&gt;

&lt;p&gt;We are currently living in the era of "Chat." We ask an LLM a question, and it gives us text back. But the future of AI isn't just about retrieval; it's about &lt;strong&gt;agency&lt;/strong&gt;. It's about agents that can reach out of the chat window and touch the operating system, the file structure, and the hardware.&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;Kiro-ween Hackathon&lt;/strong&gt;, I entered the "Frankenstein" category with a specific goal: To stitch together incompatible technologies—Local File Systems, IoT simulation, and Reactive Frontend Animations—into a single, cohesive entity.&lt;/p&gt;

&lt;p&gt;The result is &lt;strong&gt;HauntHub&lt;/strong&gt;: A Victorian Ghost Butler super-agent that doesn't just talk to you; it &lt;em&gt;haunts&lt;/em&gt; your desktop.&lt;/p&gt;

&lt;p&gt;Here is the engineering story of how I used &lt;strong&gt;Kiro&lt;/strong&gt; and the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; to orchestrate a digital séance.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚡ The "Frankenstein" Problem
&lt;/h2&gt;

&lt;p&gt;The "Frankenstein" category challenged us to combine disparate tools. In modern web development, sticking a React Frontend, a Node.js Backend, and System-Level Operations together is common. But controlling them all &lt;em&gt;conversationally&lt;/em&gt; via a single LLM stream is a nightmare.&lt;/p&gt;

&lt;p&gt;I needed an architecture that could handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;State Management:&lt;/strong&gt; Remembering the lights are red while reading a text file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency:&lt;/strong&gt; Triggering a jump-scare animation &lt;em&gt;instantly&lt;/em&gt;, not waiting for the LLM to finish typing a paragraph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persona Integrity:&lt;/strong&gt; Ensuring the agent never breaks its 19th-century character, even when throwing JSON parsing errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To solve this, I treated the architecture like anatomy. The &lt;strong&gt;Frontend&lt;/strong&gt; is the face, the &lt;strong&gt;MCP Tools&lt;/strong&gt; are the limbs, and &lt;strong&gt;Kiro&lt;/strong&gt; is the nervous system connecting them.&lt;/p&gt;

&lt;p&gt;[Image of System Architecture]&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 The Brain: Spec-Driven Development
&lt;/h2&gt;

&lt;p&gt;One of the biggest time-sinks in hackathons is writing boilerplate code. I skipped that entirely.&lt;/p&gt;

&lt;p&gt;Using &lt;strong&gt;Kiro’s Spec-to-Code engine&lt;/strong&gt;, I wrote a rigorous System Specification (&lt;code&gt;ghost-butler-spec.md&lt;/code&gt;) first. I defined the state machine, the valid transitions, and the UI components required. Kiro then generated the React/Node.js scaffolding.&lt;/p&gt;

&lt;p&gt;This meant that instead of debugging &lt;code&gt;useEffect&lt;/code&gt; hooks for three hours, I was spending my time building the actual "haunted" logic. The spec became the single source of truth—the DNA of the monster.&lt;/p&gt;

&lt;h2&gt;
  
  
  🦾 The Limbs: Custom MCP Servers
&lt;/h2&gt;

&lt;p&gt;This is where the magic happens. Instead of hard-coding functions into the LLM's system prompt (which bloats context and increases cost), I utilized the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;MCP provides a standardized way for the agent to "discover" tools. I built four custom Node.js MCP servers to act as the ghost's supernatural powers:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;haunted-lights-tool.js&lt;/code&gt; (The IoT Hand)
&lt;/h3&gt;

&lt;p&gt;This tool simulates a smart home bridge. It accepts parameters like &lt;code&gt;brightness&lt;/code&gt; (0-100) and &lt;code&gt;color_hex&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input:&lt;/strong&gt; "Dim the lights for a séance."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution:&lt;/strong&gt; &lt;code&gt;set_light_state({ brightness: 10, color: "#880000" })&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;file-haunting-tool.js&lt;/code&gt; (The System Hand)
&lt;/h3&gt;

&lt;p&gt;A wrapper around Node's &lt;code&gt;fs&lt;/code&gt; module. This allows the ghost to &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, and &lt;code&gt;rename&lt;/code&gt; files.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Twist:&lt;/strong&gt; It’s not just a file reader. It’s a &lt;em&gt;haunted&lt;/em&gt; file reader. The output is formatted to look like ancient parchment in the UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;scare-effects-tool.js&lt;/code&gt; (The Poltergeist)
&lt;/h3&gt;

&lt;p&gt;This is a bridge to the frontend Event Bus. It allows the backend agent to trigger client-side CSS animations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Capabilities:&lt;/strong&gt; Screen shake, strobe lights, sudden "Glitch" artifacts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;spooky-ascii-tool.js&lt;/code&gt; (The Artist)
&lt;/h3&gt;

&lt;p&gt;A procedural generation tool that creates complex ASCII art on the fly, rendering it into code blocks in the chat interface.&lt;/p&gt;

&lt;p&gt;Configuring these in Kiro was shockingly simple. I just pointed &lt;code&gt;mcp.json&lt;/code&gt; to my scripts, and the agent immediately knew how to use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  🫀 The Nervous System: Agent Hooks &amp;amp; Routing
&lt;/h2&gt;

&lt;p&gt;The biggest technical hurdle was &lt;strong&gt;Latency vs. Impact&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If a user says &lt;em&gt;"Scare me!"&lt;/em&gt;, the agent usually does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Generates text ("BOO! Did I frighten you?").&lt;/li&gt;
&lt;li&gt; Calls the tool (&lt;code&gt;trigger_scare&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a standard LLM pipeline, the tool call often happens &lt;em&gt;after&lt;/em&gt; or during the text generation. This ruins the timing. A jump scare that happens 3 seconds after the ghost says "Boo" isn't scary; it's just a bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution: Kiro Agent Hooks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I set up specific hooks to intercept intents. When the intent &lt;code&gt;INTENT_SCARE&lt;/code&gt; is detected, the hook prioritizes the MCP payload execution immediately.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;User Voice&lt;/strong&gt; → &lt;strong&gt;Hook Detection&lt;/strong&gt; → &lt;strong&gt;DOM Manipulation (Shake)&lt;/strong&gt; → &lt;strong&gt;Audio Response&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This optimized the "Time-to-Scare" to under 500ms, creating a visceral, reactive experience that feels like a real presence in the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎭 The Soul: Steering &amp;amp; Vibe Coding
&lt;/h2&gt;

&lt;p&gt;You cannot have a Victorian Ghost Butler who says, &lt;em&gt;"I'm sorry, as an AI language model, I cannot rename that file."&lt;/em&gt; It kills the immersion instantly.&lt;/p&gt;

&lt;p&gt;Standard "Prompt Engineering" wasn't enough. I used &lt;strong&gt;Kiro’s Steering Documents&lt;/strong&gt; to create a system-level constraint on the model's output. I defined a "High Vibe" setting that penalized modern vocabulary and enforced an archaic dialect.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Error Handling" Test
&lt;/h3&gt;

&lt;p&gt;The true test of an agent's persona is how it handles failure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard AI:&lt;/strong&gt; "Error: File not found in directory."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HauntHub:&lt;/strong&gt; "Alas, the parchment you seek has vanished into the ether. The spirits deny me access to this realm."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The steering document ensures that even the system logs feel haunted. This turns technical errors into narrative elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔒 Security Spotlight: The "Chroot" Sandbox
&lt;/h2&gt;

&lt;p&gt;Giving an AI agent &lt;code&gt;fs.rename&lt;/code&gt; permissions on your local machine is inherently dangerous (and fits the "Frankenstein" theme of playing with dangerous forces).&lt;/p&gt;

&lt;p&gt;To make this safe for the judges to run, I implemented a strict &lt;strong&gt;Chroot-style sandbox&lt;/strong&gt; within the &lt;code&gt;file-haunting&lt;/code&gt; tool.&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="c1"&gt;// Inside file-haunting-tool.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HAUNTED_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../haunted-files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validatePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Resolve path relative to the haunted directory&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvedPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HAUNTED_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Security Check: Prevent directory traversal (../)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;resolvedPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HAUNTED_DIR&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The spirits are forbidden from leaving the haunted grounds!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resolvedPath&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;If the agent (or a malicious user) tries to access system files outside the ./haunted-files/ folder, the tool throws a thematic error, which Kiro then relays to the user in the Butler's voice.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Conclusion: Agents are Operators
&lt;/h2&gt;

&lt;p&gt;Building HauntHub taught me that the next wave of AI isn't about better chatbots; it's about Agents as Operators.&lt;/p&gt;

&lt;p&gt;By combining MCP for standardized tooling, Hooks for routing, and Steering for reliability, we can build software that doesn't just retrieve information—it manages our digital lives.&lt;/p&gt;

&lt;p&gt;HauntHub might be a spooky Halloween project, but the architecture underneath it is ready for the enterprise.&lt;/p&gt;

&lt;p&gt;Happy Kiro-ween! 👻&lt;/p&gt;

&lt;p&gt;HauntHub was a submission for the Kiroween Hackathon. You can view the source code and try the demo yourself on &lt;a href="https://github.com/VaishnaviOnPC/HauntHub" rel="noopener noreferrer"&gt;https://github.com/VaishnaviOnPC/HauntHub&lt;/a&gt; and Devpost.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>hackathon</category>
      <category>javascript</category>
      <category>kiro</category>
    </item>
  </channel>
</rss>
