<?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: Gaurav Kumar Singh</title>
    <description>The latest articles on DEV Community by Gaurav Kumar Singh (@gaurav101).</description>
    <link>https://dev.to/gaurav101</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%2F893926%2Faba99e1d-37d8-4b1a-ab42-802ed842d0e6.jpeg</url>
      <title>DEV Community: Gaurav Kumar Singh</title>
      <link>https://dev.to/gaurav101</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gaurav101"/>
    <language>en</language>
    <item>
      <title>The Secret Debugging Tool You're Not Using</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Sun, 26 Jul 2026 06:22:32 +0000</pubDate>
      <link>https://dev.to/gaurav101/the-secret-debugging-tool-youre-not-using-1gb3</link>
      <guid>https://dev.to/gaurav101/the-secret-debugging-tool-youre-not-using-1gb3</guid>
      <description>&lt;p&gt;We’ve all been there: It’s 11 PM, the bug is still alive, your tests are failing, and you’re about to throw your laptop out the window. &lt;/p&gt;

&lt;p&gt;We usually view debugging as a pure logic problem: stack traces, breakpoints, and logs. But &lt;strong&gt;Emotional Intelligence (EQ)&lt;/strong&gt; is often the real reason you fix a bug in 20 minutes instead of 3 hours. &lt;/p&gt;

&lt;p&gt;Here is how EQ actually applies to your daily workflow:&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Spotting Tunnel Vision Before It Wastes Your Time
&lt;/h2&gt;

&lt;p&gt;Frustration causes confirmation bias. You start forcing your initial hypothesis (&lt;em&gt;"It MUST be the cache!"&lt;/em&gt;) even when the logs say otherwise.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EQ Move:&lt;/strong&gt; Recognize physical signs like tight shoulders or rage-typing. Take a 5-minute bio-break. Stepping away resets your mental stack, which is usually faster than another hour of blind grinding.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Separating &lt;code&gt;code.hasBug()&lt;/code&gt; from &lt;code&gt;dev.isBad()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A stubborn bug easily triggers imposter syndrome: &lt;em&gt;"A senior dev would have solved this already."&lt;/em&gt; That inner voice just adds noise to your debugging stack.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EQ Move:&lt;/strong&gt; Reframe the problem objectively:

&lt;ul&gt;
&lt;li&gt;❌ &lt;em&gt;"I don't know what I'm doing."&lt;/em&gt; (Emotion)&lt;/li&gt;
&lt;li&gt;✅ &lt;em&gt;"This async function isn't returning the expected payload."&lt;/em&gt; (Fact)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debug the code, not your self-worth.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Handling Spicy Bug Reports
&lt;/h2&gt;

&lt;p&gt;A ticket comes in: &lt;em&gt;"This is completely broken, who let this ship?!"&lt;/em&gt; Your gut reaction might be to get defensive or send a passive-aggressive response.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EQ Move:&lt;/strong&gt; Filter out the noise. Translate panic or bad phrasing into actionable facts. Reply calmly to de-escalate, pull the missing repro steps, and ship the fix without unnecessary Slack drama.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Rubber Ducking and Asking for Help (Ego-Free)
&lt;/h2&gt;

&lt;p&gt;How many times have you fixed a bug &lt;em&gt;just&lt;/em&gt; by explaining it out loud to a peer? Sitting in silent frustration for hours doesn't make you a hero; it just delays the feature.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EQ Move:&lt;/strong&gt; Treat asking for help as an optimization tactic. Send a concise message with context:
&amp;gt; &lt;em&gt;"Hey, expecting X, getting Y. Already tried A and B. Got 5 mins to glance at this snippet?"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Staying Cool During Prod Outages
&lt;/h2&gt;

&lt;p&gt;Panicked devs make bad hotfixes and type typos straight into production consoles. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EQ Move:&lt;/strong&gt; The calmest dev in the room becomes the effective incident commander. Slow down, verify facts before firing off commands, and keep communication clear. Clear heads drop downtime.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Debugging isn't just &lt;strong&gt;Dev vs. Code&lt;/strong&gt;—it’s &lt;strong&gt;Dev vs. Emotional Reactivity&lt;/strong&gt;. The fastest problem solvers aren't always the ones who memorized the entire language spec. They’re the ones who catch their frustration early, communicate clearly under pressure, and keep their head clear.&lt;/p&gt;

&lt;p&gt;Next time you get stuck, ask yourself: &lt;strong&gt;"Am I debugging the system, or am I debugging my mood?"&lt;/strong&gt; Fix the mood first, and the logic gets a lot easier to trace.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>The Most Critical Architecture in Tech: Psychological Safety</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Fri, 17 Jul 2026 12:19:11 +0000</pubDate>
      <link>https://dev.to/gaurav101/the-most-critical-architecture-in-tech-psychological-safety-2el9</link>
      <guid>https://dev.to/gaurav101/the-most-critical-architecture-in-tech-psychological-safety-2el9</guid>
      <description>&lt;p&gt;Every engineer knows that sudden, cold dread when a production build breaks. But what happens &lt;em&gt;after&lt;/em&gt; the break tells you everything about your engineering culture. If the first instinct is to find someone to blame, your team has a critical bug in its foundation — and no amount of CI/CD tooling will patch it.&lt;/p&gt;

&lt;p&gt;Psychological safety isn't an HR buzzword. It's a technical property of the system, as real as uptime or test coverage. It's the human &lt;code&gt;try/catch&lt;/code&gt; block that keeps a team resilient under failure. Strip it out, and innovation stalls, code quality degrades, and tech debt quietly compounds until the whole thing collapses under its own weight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Psychological Safety Is a Codebase Concern
&lt;/h2&gt;

&lt;p&gt;When engineers are afraid of looking incompetent, they don't just feel bad — they ship worse software. That fear propagates directly into the codebase in a few predictable ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Silent outages, slow recovery.&lt;/strong&gt; When people fear reprimand, they hide mistakes, delay escalating bugs, or slap a quick patch over the symptom instead of digging into the root cause. Teams with high safety run &lt;strong&gt;blameless postmortems&lt;/strong&gt; that ask &lt;em&gt;how&lt;/em&gt; the system failed, never &lt;em&gt;who&lt;/em&gt; failed it — and they recover faster because of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tech debt that never gets paid down.&lt;/strong&gt; When failure isn't safe, people stop taking risks. Nobody volunteers to refactor the legacy monolith if a resulting bug could show up in their performance review. Teams default to "fragile but predictable" over "robust but risky," and the debt just piles up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull requests as combat instead of collaboration.&lt;/strong&gt; In low-safety environments, code review stops being a shared search for edge cases and turns into something to survive.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The data behind it:&lt;/strong&gt; When Google ran &lt;a href="https://rework.withgoogle.com/en/subjects/team-effectiveness" rel="noopener noreferrer"&gt;Project Aristotle&lt;/a&gt; to find out what made their best teams tick, they looked at everything — educational background, personality types, seniority mix. The answer had almost nothing to do with &lt;em&gt;who&lt;/em&gt; was on the team and everything to do with &lt;em&gt;how&lt;/em&gt; they treated each other. Psychological safety came out as the single strongest predictor of team performance, ahead of every other factor they measured.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Anti-Patterns That Quietly Rot Team Culture
&lt;/h2&gt;

&lt;p&gt;Just like code smells, these are easy to miss day-to-day but corrosive over time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The brilliant jerk.&lt;/strong&gt; A highly skilled developer who belittles others in reviews or design discussions. Tolerating this — even because they "ship fast" — tells the rest of the team that output matters more than how people are treated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weaponized &lt;code&gt;nit:&lt;/code&gt; comments.&lt;/strong&gt; Nitpicks used to assert dominance in a PR rather than to actually improve the code. The tell is volume and tone: constructive nitpicking is occasional and specific; weaponized nitpicking is constant and personal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treating bugs as personal failure.&lt;/strong&gt; An unhandled exception in prod is a gap in your test suite, not a character flaw in whoever wrote the line. Teams that internalize this stop hiding bugs and start writing tests for them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Engineering a Safer Culture
&lt;/h2&gt;

&lt;p&gt;Safety doesn't happen by accident — it's a design pattern, and it takes the same intentionality as any other piece of architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Model vulnerability from the top
&lt;/h3&gt;

&lt;p&gt;Leads and staff engineers need to actually say &lt;em&gt;"I don't know,"&lt;/em&gt; &lt;em&gt;"I was wrong,"&lt;/em&gt; and &lt;em&gt;"I need help here"&lt;/em&gt; — out loud, in public channels, not just in 1:1s. When a senior engineer tells the story of the time they dropped a production database, it gives everyone else permission to be human.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Critique the code, not the person
&lt;/h3&gt;

&lt;p&gt;This is the single highest-leverage habit for review culture, and it's a small rewrite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- Your code is inefficient here.
&lt;/span&gt;&lt;span class="gi"&gt;+ Let's look at the time complexity of this loop — if the dataset
+ grows, we might hit a bottleneck. What do you think about a Map
+ instead of nested iteration?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same technical point, zero identity attached to it. The reviewer isn't softening the feedback — the feedback is exactly as direct. It's just aimed at the loop instead of the person who wrote it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Kill the async anxiety loop
&lt;/h3&gt;

&lt;p&gt;In remote and hybrid teams, a context-free message is its own kind of memory leak — it sits in someone's head consuming cycles until it's resolved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- We need to talk about your PR.
&lt;/span&gt;&lt;span class="gi"&gt;+ Hey, I left a couple of architecture questions on your PR —
+ no rush, let's chat about options at tomorrow's sync 👍
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix costs one extra sentence. The anxiety it prevents can cost an afternoon of someone's focus.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;We pour money into high availability, automated pipelines, and fault-tolerant infrastructure. But the most critical system any team runs is the human one underneath all of it. If your developers don't feel safe to fail, your software will eventually fail the same way — quietly, and in production.&lt;/p&gt;

&lt;p&gt;Psychological safety isn't the soft stuff. It's the infrastructure everything else runs on.&lt;/p&gt;

</description>
      <category>culture</category>
      <category>leadership</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>How to Thrive (Not Just Survive) as a Developer in the Age of AI</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Sat, 11 Jul 2026 12:54:36 +0000</pubDate>
      <link>https://dev.to/gaurav101/how-to-thrive-not-just-survive-as-a-developer-in-the-age-of-ai-3bdp</link>
      <guid>https://dev.to/gaurav101/how-to-thrive-not-just-survive-as-a-developer-in-the-age-of-ai-3bdp</guid>
      <description>&lt;p&gt;The narrative around Artificial Intelligence and software engineering has shifted dramatically. We are no longer asking &lt;em&gt;if&lt;/em&gt; AI will change development, but rather how we change &lt;em&gt;with&lt;/em&gt; it. &lt;/p&gt;

&lt;p&gt;If your value as a developer is tied solely to how fast you can churn out boilerplate code, write standard API endpoints, or memorize syntax, the landscape is becoming challenging. AI can do those things in seconds. However, this isn't a death sentence for the engineering career—it is an evolution. The industry is moving away from pure "code generation" and shifting toward &lt;strong&gt;system architecture, integration, and governance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To remain indispensable, you need to know exactly where to direct your energy and what pitfalls to avoid.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where to Focus Your Energy
&lt;/h2&gt;

&lt;p&gt;To stay relevant, you must position yourself in the areas where AI struggles: high-level abstraction, complex contextual reasoning, and human leadership.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. System Design and Enterprise Architecture
&lt;/h3&gt;

&lt;p&gt;AI is excellent at writing isolated functions, but it struggles with massive, interconnected systems. Focus on how components interact at scale. Understanding how to slice a monolithic application into resilient microservices, orchestrate microfrontends, or design cloud-native solutions is where the high-value work lies. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Code Governance and Quality Assurance
&lt;/h3&gt;

&lt;p&gt;With AI generating code at unprecedented speeds, codebases are expanding faster than ever. The world doesn't just need people who can create code; it needs gatekeepers who can validate it. Your role will increasingly focus on setting quality standards, establishing robust CI/CD pipelines, and ensuring that AI-generated code adheres to strict security, compliance, and performance metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Mentorship and Team Leadership
&lt;/h3&gt;

&lt;p&gt;The influx of AI tools means junior engineers can produce code much earlier in their careers, but they often lack the foundational experience to spot subtle architectural flaws or security vulnerabilities. Senior developers must step up as leaders, guiding less experienced teammates on &lt;em&gt;why&lt;/em&gt; certain architectural patterns matter, preventing burnout, and maintaining engineering discipline within the team.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Deep Systems and Performance Optimization
&lt;/h3&gt;

&lt;p&gt;While AI can write functional code, it often writes resource-heavy or unoptimized code. Specializing in low-level systems programming, memory safety, and fine-tuning performance-critical applications gives you a massive edge. Knowing how to wring every ounce of efficiency out of a system remains a highly prized human skill.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to Take Care of (The Pitfalls to Avoid)
&lt;/h2&gt;

&lt;p&gt;Staying relevant isn't just about learning new skills—it's also about avoiding the bad habits that AI tools can induce.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The "Copilot Crutch" Warning:&lt;/strong&gt; Relying too heavily on AI tools for basic problem-solving can cause your foundational engineering muscles to atrophy. If you don't understand the underlying logic of the code your AI assistant suggests, you cannot safely maintain or debug it when things break.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Watch out for Architectural Drift:&lt;/strong&gt; AI generates code based on local context (the file you are working on). It doesn't inherently understand your entire organization's long-term technical roadmap or governance model. Left unchecked, AI-assisted development can lead to a bloated, fragmented codebase.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Don't skip the "Why":&lt;/strong&gt; Always question AI suggestions. If a Large Language Model gives you an elegant solution, take a moment to understand &lt;em&gt;why&lt;/em&gt; it works before accepting the pull request. &lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Guard your Domain Knowledge:&lt;/strong&gt; Technical skills matter, but understanding the business logic and user experience is what converts code into a successful product. AI doesn't know your customers; you do.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Evolution of the Developer Role
&lt;/h2&gt;

&lt;p&gt;To visualize this shift, look at how the daily responsibilities of a resilient software engineer are changing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional Developer Focus&lt;/th&gt;
&lt;th&gt;AI-Era Developer Focus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Writing boilerplate, configurations, and repetitive unit tests.&lt;/td&gt;
&lt;td&gt;Designing system boundaries, security models, and data flows.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memorizing language syntax and API frameworks.&lt;/td&gt;
&lt;td&gt;Evaluating and integrating complex, multi-system dependencies.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fixing syntax errors and basic debugging.&lt;/td&gt;
&lt;td&gt;Auditing AI-generated code for hidden vulnerabilities and tech debt.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Functioning as an isolated individual contributor.&lt;/td&gt;
&lt;td&gt;Acting as a technical leader, mentor, and strategic translator for business needs.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Moving Forward
&lt;/h2&gt;

&lt;p&gt;The goal is to stop competing with AI at what it does best (speed and volume) and start leveraging it to do what humans do best (strategy, empathy, and holistic design). By shifting your mindset from being a "code writer" to a "software architect and custodian," you ensure that your skills remain not just relevant, but entirely essential to the future of technology.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>software</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Sick of App Store Ads? So I Built My Daughter Her Own Drawing PWA</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Thu, 02 Jul 2026 14:14:45 +0000</pubDate>
      <link>https://dev.to/gaurav101/building-a-ad-free-kid-friendly-drawing-pwa-with-my-daughter-and-google-ai-studio-13p4</link>
      <guid>https://dev.to/gaurav101/building-a-ad-free-kid-friendly-drawing-pwa-with-my-daughter-and-google-ai-studio-13p4</guid>
      <description>&lt;p&gt;"Daddy, another ad?"&lt;/p&gt;

&lt;p&gt;If you have young kids who use an iPad, you may know this problem.&lt;/p&gt;

&lt;p&gt;Last week, my daughter was using a free drawing app. She was happy and busy making her art. But every few minutes, a loud ad came on the screen. She could not skip it. It stopped her drawing again and again.&lt;/p&gt;

&lt;p&gt;After this happened many times, she looked at me and said:&lt;/p&gt;

&lt;p&gt;"I just want to draw. Can we make our own app without ads?"&lt;/p&gt;

&lt;p&gt;At first, I could have said that making an app takes too much time. But then I thought, why not try?&lt;/p&gt;

&lt;p&gt;So we made it a small weekend project.&lt;/p&gt;

&lt;p&gt;My daughter became the product boss. She asked for rainbow brushes, a big drawing space, fun sounds, and no pop-ups. I worked on the code.&lt;/p&gt;

&lt;p&gt;Together, we built this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unicorn-draw.vercel.app/" rel="noopener noreferrer"&gt;unicorn-draw.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tool that helped us build it fast was Google AI Studio with Gemini.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;p&gt;I wanted the app to be easy to use on an iPad. I also did not want to deal with the App Store.&lt;/p&gt;

&lt;p&gt;So I made it as a Progressive Web App, or PWA. This means it works like an app, but it runs in the browser.&lt;/p&gt;

&lt;p&gt;Here is what I used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML5 Canvas API for drawing&lt;/li&gt;
&lt;li&gt;CSS for big buttons and bright colors&lt;/li&gt;
&lt;li&gt;Service Worker so the app can work offline&lt;/li&gt;
&lt;li&gt;Web App Manifest so it can be added to the home screen&lt;/li&gt;
&lt;li&gt;Google AI Studio and Gemini to help write and improve the code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because it is a PWA, my daughter can open it from the iPad home screen. It runs full screen. It has no ads, no tracking, and no hidden pop-ups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building with Google AI Studio
&lt;/h2&gt;

&lt;p&gt;Google AI Studio made the work much faster.&lt;/p&gt;

&lt;p&gt;First, I asked Gemini to make a simple drawing app using HTML5 canvas. I also asked it to support touch events, so it would work well on an iPad.&lt;/p&gt;

&lt;p&gt;Then my daughter tested the first version. She quickly asked for a magic rainbow brush and a simple clear button.&lt;/p&gt;

&lt;p&gt;I gave the code back to Gemini and asked it to add a rainbow brush. I also asked it to make the buttons look more fun for a drawing app.&lt;/p&gt;

&lt;p&gt;After that, I used Gemini to help create the PWA files, like &lt;code&gt;manifest.json&lt;/code&gt; and the service worker.&lt;/p&gt;

&lt;p&gt;In a short time, the app started to feel like a real tablet app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;The project is open source.&lt;/p&gt;

&lt;p&gt;You can see the full code here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gaurav101/unicorn-draw" rel="noopener noreferrer"&gt;GitHub Repository: gaurav101/unicorn-draw&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Install It on an iPad or Phone
&lt;/h2&gt;

&lt;p&gt;You can add the app to your home screen in a few seconds.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://unicorn-draw.vercel.app/" rel="noopener noreferrer"&gt;unicorn-draw.vercel.app&lt;/a&gt; in your browser.&lt;/li&gt;
&lt;li&gt;On iPhone or iPad, tap the Share button. On Android, tap the three dots menu.&lt;/li&gt;
&lt;li&gt;Tap "Add to Home Screen".&lt;/li&gt;
&lt;li&gt;Open it from the home screen and start drawing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now your child has a simple drawing app with no ads.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  1. AI Helps You Build Faster
&lt;/h2&gt;

&lt;p&gt;Gemini did not just write code for me. It helped me move fast.&lt;/p&gt;

&lt;p&gt;Instead of spending time on small setup work, I could focus on how the app should feel for my daughter.&lt;/p&gt;

&lt;p&gt;The whole project took only a couple of hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Kids Can Learn That They Can Build Things
&lt;/h2&gt;

&lt;p&gt;The best part was not the app.&lt;/p&gt;

&lt;p&gt;The best part was seeing my daughter understand something important:&lt;/p&gt;

&lt;p&gt;Technology is not something we only have to use. If something is annoying or broken, we can try to make a better version.&lt;/p&gt;

&lt;p&gt;That was the real win.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>google</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Markdown to Styled HTML in Java</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Sun, 28 Jun 2026 08:22:14 +0000</pubDate>
      <link>https://dev.to/gaurav101/stop-fighting-your-css-framework-markdown-to-styled-html-in-java-5c06</link>
      <guid>https://dev.to/gaurav101/stop-fighting-your-css-framework-markdown-to-styled-html-in-java-5c06</guid>
      <description>&lt;p&gt;If you have ever built a Java application that converts Markdown to HTML—whether for a blog engine, a documentation site, or user-generated comments—you have likely run into a frustrating roadblock. &lt;/p&gt;

&lt;p&gt;You pull in a solid library like &lt;code&gt;commonmark-java&lt;/code&gt;, pass your Markdown string, and get perfectly valid semantic HTML blocks. But then you render it on the frontend and realize &lt;strong&gt;it looks completely unstyled.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Modern CSS frameworks like &lt;strong&gt;Tailwind CSS&lt;/strong&gt;, &lt;strong&gt;Bootstrap&lt;/strong&gt;, or &lt;strong&gt;Bulma&lt;/strong&gt; rely heavily on utility classes. They don't automatically style raw &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;, or &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; tags unless you wrap them in heavy global stylesheet overrides. To fix this in Java, you are usually forced to write verbose custom tag renderers, map complex attribute providers, or write messy string-replacement wrappers.&lt;/p&gt;

&lt;p&gt;That is exactly the problem &lt;a href="https://github.com/gaurav101/FluentMarkdown" rel="noopener noreferrer"&gt;FluentMarkdown&lt;/a&gt; solves. &lt;/p&gt;




&lt;h2&gt;
  
  
  💡 What is FluentMarkdown?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/gaurav101/FluentMarkdown" rel="noopener noreferrer"&gt;FluentMarkdown&lt;/a&gt; is a lightweight, zero-boilerplate Java library built directly on top of &lt;code&gt;commonmark-java&lt;/code&gt;. It introduces a clean, fluent API that automatically injects the proper CSS framework classes into your HTML elements during the parsing phase.&lt;/p&gt;

&lt;p&gt;Instead of fighting your CSS framework, you can map your tags instantly using built-in presets or completely custom styles.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Installation
&lt;/h3&gt;

&lt;p&gt;The library is explicitly lightweight (only four core files) and requires &lt;strong&gt;Java 17 or later&lt;/strong&gt;. You can add it via Maven:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.gaurav101&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;fluentmarkdown&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0.4&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Quick Start: Use a Framework Preset
&lt;/h3&gt;

&lt;p&gt;If you use Tailwind, Bootstrap 5, or Bulma, FluentMarkdown has pre-mapped configurations ready to go.&lt;/p&gt;

&lt;p&gt;Here is how easily you can render fully-styled Tailwind HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.github.gaurav101.fluentmarkdown.Markdown&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;markdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"# Hello World\nThis is a paragraph."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// One fluent chain handles it all&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tailwindHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markdown&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withTailwind&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                                      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toHtml&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tailwindHtml&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-4xl font-extrabold tracking-tight mb-4 mt-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello World&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-4 leading-relaxed text-base"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This is a paragraph.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switching frameworks is as simple as swapping &lt;em&gt;.withTailwind()&lt;/em&gt; for &lt;em&gt;.withBootstrap()&lt;/em&gt; or &lt;em&gt;.withBulma()&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Advanced Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔒 Step 3: XSS Protection (Safe Mode)
&lt;/h3&gt;

&lt;p&gt;If you are processing untrusted user input (like blog comments), rendering raw HTML can expose your app to Cross-Site Scripting (XSS) attacks. FluentMarkdown includes a built-in .safe() modifier that strips out malicious inline scripts and raw HTML blocks seamlessly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;unsafeInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;script&amp;gt;alert('hack')&amp;lt;/script&amp;gt;\n\n# Safe Title"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;safeHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unsafeInput&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withTailwind&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;safe&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Strips dangerous HTML&lt;/span&gt;
                          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toHtml&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎨 Step 4: Custom Style Configurations
&lt;/h3&gt;

&lt;p&gt;Don't use a standard CSS framework? You can build an immutable StyleConfig to map your own internal design system classes to any HTML tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.github.gaurav101.fluentmarkdown.StyleConfig&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;StyleConfig&lt;/span&gt; &lt;span class="n"&gt;myCustomConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StyleConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"h1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"custom-main-title"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"p"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"body-text text-muted"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"global-link"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;customHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markdown&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withStyle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myCustomConfig&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toHtml&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔄 Step 5: Extend Existing Presets
&lt;/h3&gt;

&lt;p&gt;If you love the Tailwind preset but want to tweak just one or two elements (like changing the link color), you can merge and overwrite tags dynamically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.github.gaurav101.fluentmarkdown.Presets&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;StyleConfig&lt;/span&gt; &lt;span class="n"&gt;modifiedTailwind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StyleConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mergeFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Presets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tailwind&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// Start with Tailwind defaults&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text-indigo-600 underline"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Overwrite just the anchor tag&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎯Conclusion
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://central.sonatype.com/artifact/io.github.gaurav101/fluentmarkdown" rel="noopener noreferrer"&gt;FluentMarkdown&lt;/a&gt; provides a highly practical solution to a common backend UI problem. It keeps your Java code clean, readable, and perfectly married to modern frontend utilities.&lt;/p&gt;

&lt;p&gt;Give the project a look, try it out in your next Java project, and don't forget to drop a ⭐️ on the repository!&lt;/p&gt;

&lt;p&gt;👉 GitHub Repository: &lt;a href="https://github.com/gaurav101/FluentMarkdown" rel="noopener noreferrer"&gt;gaurav101/FluentMarkdown&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>java</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Hidden Cost of the AI Hype</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Thu, 25 Jun 2026 15:55:00 +0000</pubDate>
      <link>https://dev.to/gaurav101/the-hidden-cost-of-the-ai-hype-2f5k</link>
      <guid>https://dev.to/gaurav101/the-hidden-cost-of-the-ai-hype-2f5k</guid>
      <description>&lt;p&gt;We talk a lot about what AI can build.&lt;/p&gt;

&lt;p&gt;Code generation. Faster prototypes. Automated debugging. One-shot apps. Entire products created in hours.&lt;/p&gt;

&lt;p&gt;And yes, AI is powerful.&lt;/p&gt;

&lt;p&gt;But there is a quieter cost we are not talking about enough:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI hype is starting to weaken the motivation to learn core engineering deeply.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That should worry us.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The "Why Bother?" Mindset
&lt;/h2&gt;

&lt;p&gt;When the dominant narrative says AI can generate code instantly, many engineers start asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why should I spend months mastering frameworks, architecture, databases, networking, or system design?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At first, that sounds practical. If a tool can help, why not use it?&lt;/p&gt;

&lt;p&gt;But there is a difference between using AI to move faster and using AI to avoid understanding.&lt;/p&gt;

&lt;p&gt;Core engineering is not just about writing code. It is about knowing why something works, where it breaks, how it scales, and how to fix it when the generated answer is wrong.&lt;/p&gt;

&lt;p&gt;If we skip that learning, we create engineers who can prompt systems but cannot reason deeply about systems.&lt;/p&gt;

&lt;p&gt;That is a dangerous tradeoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Funding and Praise Monopoly
&lt;/h2&gt;

&lt;p&gt;Right now, AI gets most of the attention.&lt;/p&gt;

&lt;p&gt;Budgets move toward AI. Leadership praises AI initiatives. Teams are pushed to add AI features even when the fundamentals are still weak.&lt;/p&gt;

&lt;p&gt;Meanwhile, excellent core engineering often goes unnoticed.&lt;/p&gt;

&lt;p&gt;The people improving reliability, performance, developer experience, infrastructure, security, and maintainability are still doing high-impact work. But in many places, that work is being treated as less exciting simply because it is not branded as AI.&lt;/p&gt;

&lt;p&gt;This creates pressure.&lt;/p&gt;

&lt;p&gt;Engineers feel they must pivot to AI, not always out of interest, but out of fear. Fear of being left behind. Fear of being replaced. Fear that their existing expertise is no longer valued.&lt;/p&gt;

&lt;p&gt;That is not innovation. That is anxiety disguised as progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The "AI-First" Discount
&lt;/h2&gt;

&lt;p&gt;There is another subtle problem.&lt;/p&gt;

&lt;p&gt;When someone builds something impressive today, the reaction is often:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI probably generated that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That assumption discounts real skill.&lt;/p&gt;

&lt;p&gt;It ignores the planning, debugging, tradeoffs, refactoring, architecture, and judgment behind the work. AI may assist, but it does not automatically create good software.&lt;/p&gt;

&lt;p&gt;Great engineering still requires taste. Context. Discipline. Experience. The ability to make hard decisions when requirements are messy and systems are complex.&lt;/p&gt;

&lt;p&gt;When we assume every good output came from AI, we quietly devalue human craftsmanship.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Is an Accelerator, Not a Substitute
&lt;/h2&gt;

&lt;p&gt;AI can help us move faster.&lt;/p&gt;

&lt;p&gt;It can explain concepts, generate drafts, find bugs, write boilerplate, and help us explore ideas quickly.&lt;/p&gt;

&lt;p&gt;But AI is not a replacement for engineering fundamentals.&lt;/p&gt;

&lt;p&gt;If we stop learning the building blocks, we weaken the entire ecosystem AI depends on. Someone still needs to understand distributed systems. Someone still needs to design reliable APIs. Someone still needs to debug production failures at 2 AM. Someone still needs to know when the AI-generated solution is wrong.&lt;/p&gt;

&lt;p&gt;The future should not be "AI instead of engineers."&lt;/p&gt;

&lt;p&gt;It should be &lt;strong&gt;AI with stronger engineers&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Should Value
&lt;/h2&gt;

&lt;p&gt;We need to keep rewarding the work that keeps technology stable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong system design&lt;/li&gt;
&lt;li&gt;Clean architecture&lt;/li&gt;
&lt;li&gt;Reliable infrastructure&lt;/li&gt;
&lt;li&gt;Secure software&lt;/li&gt;
&lt;li&gt;Maintainable codebases&lt;/li&gt;
&lt;li&gt;Deep debugging skills&lt;/li&gt;
&lt;li&gt;Thoughtful technical leadership&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI can support this work, but it cannot replace the need for people who understand it deeply.&lt;/p&gt;

&lt;p&gt;The best engineers of the future will not be the ones who blindly rely on AI.&lt;/p&gt;

&lt;p&gt;They will be the ones who use AI wisely while continuing to sharpen their fundamentals.&lt;/p&gt;

&lt;p&gt;So the real question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How are you balancing the AI push while keeping your core engineering skills sharp?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's discuss.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>learning</category>
      <category>careerdevelopment</category>
    </item>
    <item>
      <title>Building a Safe, Local AI Coding Agent with Node.js</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Fri, 19 Jun 2026 03:57:12 +0000</pubDate>
      <link>https://dev.to/gaurav101/building-a-safe-local-ai-coding-agent-with-nodejs-46ol</link>
      <guid>https://dev.to/gaurav101/building-a-safe-local-ai-coding-agent-with-nodejs-46ol</guid>
      <description>&lt;p&gt;Welcome to the 4th article of the MCP and RAG with JS series.&lt;/p&gt;

&lt;p&gt;In this article, we will learn what AI agents are by building a practical, beginner-friendly coding agent in JavaScript. We will use a locally running LLM, Mistral on Ollama.&lt;/p&gt;

&lt;p&gt;You do not need any paid subscription or API key. Everything runs locally on your machine, so this is accessible for learning, testing, and experimenting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;We are building a local personal coding agent.&lt;/p&gt;

&lt;p&gt;It runs in the terminal and helps us understand a JavaScript project. It can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;list project files&lt;/li&gt;
&lt;li&gt;read project files&lt;/li&gt;
&lt;li&gt;search text&lt;/li&gt;
&lt;li&gt;explain code&lt;/li&gt;
&lt;li&gt;find possible bugs&lt;/li&gt;
&lt;li&gt;propose code changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important safety rule is this:&lt;/p&gt;

&lt;p&gt;The agent can inspect files, but it does not directly edit them. If it wants to change something, it only returns a patch proposal for a human developer to review.&lt;/p&gt;

&lt;p&gt;So in simple words, we are building a small local coding assistant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Helps Us Learn
&lt;/h2&gt;

&lt;p&gt;AI agents can sound complicated, but most agent systems use a few common patterns.&lt;/p&gt;

&lt;p&gt;In this project, we will learn those patterns with plain JavaScript:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent loop: repeat until the model gives a final answer.&lt;/li&gt;
&lt;li&gt;Tool calling: let the model request specific JavaScript functions.&lt;/li&gt;
&lt;li&gt;Tool allowlist: only allow approved tools to run.&lt;/li&gt;
&lt;li&gt;System prompt: tell the model how it should behave.&lt;/li&gt;
&lt;li&gt;JSON action protocol: make the model respond with structured JSON.&lt;/li&gt;
&lt;li&gt;Model adapter: keep the Ollama HTTP code in one small file.&lt;/li&gt;
&lt;li&gt;Safety boundary: keep file access inside the project root.&lt;/li&gt;
&lt;li&gt;Human-in-the-loop changes: propose patches instead of applying them.&lt;/li&gt;
&lt;li&gt;Tests for safety: verify path traversal, large file, and missing file behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These same ideas appear in larger AI-agent frameworks. This project keeps them small enough to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Big Idea
&lt;/h2&gt;

&lt;p&gt;A normal chatbot usually works like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User asks question -&amp;gt; Model answers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An agent works more like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User asks question
  -&amp;gt; Model decides what to do
  -&amp;gt; JavaScript runs a safe tool
  -&amp;gt; Model sees the tool result
  -&amp;gt; Model answers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model does not directly read your files or run commands. It asks for a tool, and your JavaScript code decides whether that tool is allowed.&lt;/p&gt;

&lt;p&gt;That is the main idea behind this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;You can explore and clone the complete codebase in the &lt;a href="https://github.com/gaurav101/ai-experiment/tree/main/coding-agents" rel="noopener noreferrer"&gt;coding-agents GitHub repository.&lt;/a&gt;&lt;br&gt;
The important files are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
|-- package.json
|-- src
|   |-- cli.js
|   |-- agent.js
|   |-- ollama.js
|   `-- tools.js
`-- test
    `-- tools.test.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each file has a clear job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src/cli.js&lt;/code&gt;: terminal entry point&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/agent.js&lt;/code&gt;: agent loop and tool dispatch&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/ollama.js&lt;/code&gt;: local Ollama API client&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/tools.js&lt;/code&gt;: safe filesystem tools&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test/tools.test.js&lt;/code&gt;: safety and tool behavior tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1: CLI Entry Point
&lt;/h2&gt;

&lt;p&gt;The app starts in &lt;code&gt;src/cli.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It imports the agent:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;runAgent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./agent.js&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;p&gt;Then it chooses the project root and model:&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;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OLLAMA_MODEL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mistral&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;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use &lt;code&gt;--root&lt;/code&gt; if the user provides it&lt;/li&gt;
&lt;li&gt;otherwise use the current folder&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;--model&lt;/code&gt; if provided&lt;/li&gt;
&lt;li&gt;otherwise use &lt;code&gt;OLLAMA_MODEL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;otherwise use &lt;code&gt;mistral&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CLI supports two modes.&lt;/p&gt;

&lt;p&gt;One-shot mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"Explain src/tools.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interactive mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both cases, the CLI eventually calls:&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;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;verbose&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the CLI is only responsible for input and output. The real agent behavior lives in &lt;code&gt;runAgent&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2: Agent Loop
&lt;/h2&gt;

&lt;p&gt;The main function is in &lt;code&gt;src/agent.js&lt;/code&gt;:&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;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;verbose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;At the start, it creates the available tools:&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createTools&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./tools.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTools&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passing &lt;code&gt;root&lt;/code&gt; is important. It tells the tools which folder they are allowed to inspect.&lt;/p&gt;

&lt;p&gt;Then the agent creates a message history:&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;messages&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buildSystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tools&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;goal&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;The &lt;code&gt;system&lt;/code&gt; message contains rules for the model. The &lt;code&gt;user&lt;/code&gt; message contains the developer's request.&lt;/p&gt;

&lt;p&gt;Then the agent runs a loop:&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;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;MAX_STEPS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateWithOllama&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// final answer or tool call&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MAX_STEPS&lt;/code&gt; is set to &lt;code&gt;8&lt;/code&gt;, so the agent cannot loop forever.&lt;/p&gt;

&lt;p&gt;This loop is the heart of the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;messages -&amp;gt; prompt -&amp;gt; model -&amp;gt; action -&amp;gt; tool or final answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3: System Prompt
&lt;/h2&gt;

&lt;p&gt;The system prompt tells the model how to behave.&lt;/p&gt;

&lt;p&gt;In this project, the prompt says the model should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inspect files before making project-specific claims&lt;/li&gt;
&lt;li&gt;never claim a patch was applied&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;propose_patch&lt;/code&gt; only for suggested changes&lt;/li&gt;
&lt;li&gt;return exactly one JSON object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also describes the available tools:&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;toolDescriptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`- &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Parameters: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="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="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&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;p&gt;This lets the model know what it can ask for.&lt;/p&gt;

&lt;p&gt;The model must respond in one of two shapes.&lt;/p&gt;

&lt;p&gt;To call a tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"read_file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"src/example.js"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To finish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"final"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"answer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Your answer here."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple JSON action protocol. It is easy to understand because there is no hidden framework magic.&lt;/p&gt;

&lt;h2&gt;
  
  
  4: Local Model Adapter
&lt;/h2&gt;

&lt;p&gt;The file &lt;code&gt;src/ollama.js&lt;/code&gt; keeps the Ollama API call separate from the rest of the app.&lt;/p&gt;

&lt;p&gt;The default local URL is:&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;DEFAULT_OLLAMA_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:11434&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;p&gt;The function sends the prompt to Ollama:&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/generate`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;temperature&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;Then it returns the model text:&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file has one job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prompt in -&amp;gt; Ollama HTTP request -&amp;gt; model response out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping this in one file makes it easier to swap models later.&lt;/p&gt;

&lt;h2&gt;
  
  
  5: Tool Calling
&lt;/h2&gt;

&lt;p&gt;After Ollama responds, the agent parses the response:&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;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&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 model gives a final answer, the agent returns 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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;final&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;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&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 model asks for a tool, the agent checks whether that tool exists:&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;tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;];&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;tool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Unknown tool: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;allowedTools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tools&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="k"&gt;continue&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;This is very important.&lt;/p&gt;

&lt;p&gt;The model cannot invent tools. It can only use tools that exist in the local JavaScript object.&lt;/p&gt;

&lt;p&gt;Then the agent runs the 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tool&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="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And sends the result back into the message history:&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="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;result&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 the model can use real project information instead of guessing.&lt;/p&gt;

&lt;p&gt;Example flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: Explain src/tools.js
Model: calls read_file
JavaScript: reads the file safely
Model: sees the file content
Model: gives final explanation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6: Safe Tools
&lt;/h2&gt;

&lt;p&gt;The tools live in &lt;code&gt;src/tools.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The project has four tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;list_files
read_file
search_text
propose_patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tool has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: tells the model what it does&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parameters&lt;/code&gt;: tells the model what arguments it accepts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run&lt;/code&gt;: the actual JavaScript function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example shape:&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="nx"&gt;read_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Read a UTF-8 text file from inside the project root.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;File path relative to project root.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// safe implementation&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;The tools are intentionally narrow.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;list_files&lt;/code&gt; lists files under the project root.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;read_file&lt;/code&gt; reads one text file if it is safe and not too large.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;search_text&lt;/code&gt; searches project files for a string or regex.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;propose_patch&lt;/code&gt; returns a patch proposal, but does not apply it.&lt;/p&gt;

&lt;p&gt;That last point matters. The model can suggest changes, but a human still reviews them.&lt;br&gt;
 ** use a safe regex engine if you are planning to expose the agent to external user inputs. ** &lt;/p&gt;
&lt;h2&gt;
  
  
  7: Path Safety With &lt;code&gt;safeResolve&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The most important safety function is:&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;safeResolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestedPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;absolute&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;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestedPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relative&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;relative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;relative&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;isAbsolute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;relative&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="s2"&gt;`Path escapes project root: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;requestedPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;absolute&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;This blocks path traversal.&lt;/p&gt;

&lt;p&gt;For example, this should be allowed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/tools.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this should be blocked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;../outside.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because the agent should only inspect the selected project folder. Model output is not trusted input, so every requested path goes through &lt;code&gt;safeResolve&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is one of the most important lessons in agent development:&lt;/p&gt;

&lt;p&gt;Give the model useful tools, but put real safety checks in code.&lt;/p&gt;

&lt;h2&gt;
  
  
  8: Size Limits
&lt;/h2&gt;

&lt;p&gt;The tool layer also avoids reading huge files:&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;MAX_READ_BYTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_SEARCH_FILE_BYTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;read_file&lt;/code&gt; throws an error if a file is too large.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;search_text&lt;/code&gt; skips files that are too large.&lt;/p&gt;

&lt;p&gt;This protects the model context and keeps the agent responsive.&lt;/p&gt;

&lt;h2&gt;
  
  
  9: Patch Proposals, Not Auto-Edits
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;propose_patch&lt;/code&gt; tool returns:&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;applied&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Patch proposal only. Review before applying.&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;p&gt;This is a human-in-the-loop design.&lt;/p&gt;

&lt;p&gt;The agent can help you think and propose changes, but it does not silently modify your files.&lt;/p&gt;

&lt;p&gt;For a beginner agent, this is a good safety tradeoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  10: Tests for Safety
&lt;/h2&gt;

&lt;p&gt;The project uses Vitest.&lt;/p&gt;

&lt;p&gt;From &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vitest run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:watch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vitest"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tests cover the dangerous parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;safeResolve&lt;/code&gt; allows normal paths&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;safeResolve&lt;/code&gt; blocks &lt;code&gt;../&lt;/code&gt; traversal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list_files&lt;/code&gt; handles missing directories&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read_file&lt;/code&gt; rejects missing paths and directories&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read_file&lt;/code&gt; rejects large files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;search_text&lt;/code&gt; skips large files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;propose_patch&lt;/code&gt; returns &lt;code&gt;applied: false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example traversal test:&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;safeResolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fixtureProjectRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../outside.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/escapes project root/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example large-file test:&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;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;read_file&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="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large-read.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/too large to read safely/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tests are not just for correctness here. They protect the safety boundary of the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete Request Flow
&lt;/h2&gt;

&lt;p&gt;If you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"Explain src/tools.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;src/cli.js&lt;/code&gt; receives the request.&lt;/li&gt;
&lt;li&gt;It calls &lt;code&gt;runAgent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/agent.js&lt;/code&gt; creates safe tools.&lt;/li&gt;
&lt;li&gt;The system prompt describes the rules and tools.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/ollama.js&lt;/code&gt; sends the prompt to local Ollama.&lt;/li&gt;
&lt;li&gt;The model returns a JSON action.&lt;/li&gt;
&lt;li&gt;If it asks for a tool, the agent checks the allowlist.&lt;/li&gt;
&lt;li&gt;The tool runs with safety checks.&lt;/li&gt;
&lt;li&gt;The tool result goes back to the model.&lt;/li&gt;
&lt;li&gt;The model returns a final answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is an AI agent in practical terms.&lt;/p&gt;

&lt;p&gt;It is an LLM connected to a controlled loop, safe tools, and clear rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Run It
&lt;/h2&gt;

&lt;p&gt;Requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18 or newer&lt;/li&gt;
&lt;li&gt;Ollama installed&lt;/li&gt;
&lt;li&gt;Mistral pulled locally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pull the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull mistral
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start Ollama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask one question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"Explain src/agent.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspect another project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--root&lt;/span&gt; /path/to/project &lt;span class="s2"&gt;"Find bugs in the main CLI file"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run syntax checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What to Remember
&lt;/h2&gt;

&lt;p&gt;An AI agent is not just an LLM.&lt;/p&gt;

&lt;p&gt;An AI agent is usually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM + loop + tools + context + safety rules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the CLI gets the user request&lt;/li&gt;
&lt;li&gt;the agent loop manages steps&lt;/li&gt;
&lt;li&gt;Ollama provides the local model&lt;/li&gt;
&lt;li&gt;tools provide controlled abilities&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;safeResolve&lt;/code&gt; protects file access&lt;/li&gt;
&lt;li&gt;tests protect the safety behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model can request actions, but JavaScript decides what actually runs.&lt;/p&gt;

&lt;p&gt;That is the key idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This project is intentionally small, but it teaches the foundation behind many larger agent systems.&lt;/p&gt;

&lt;p&gt;Once you understand this version, you can explore more advanced ideas like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory across chat turns&lt;/li&gt;
&lt;li&gt;streaming model output&lt;/li&gt;
&lt;li&gt;richer tool schemas&lt;/li&gt;
&lt;li&gt;patch validation&lt;/li&gt;
&lt;li&gt;confirmation-based patch applying&lt;/li&gt;
&lt;li&gt;MCP tools&lt;/li&gt;
&lt;li&gt;RAG over larger codebases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the core idea stays the same:&lt;/p&gt;

&lt;p&gt;Mistral is a good simple default for learning, but when you are building stronger coding agents, coding-focused models usually give better results. Good options to try are qwen2.5-coder, deepseek-coder, and Gemma.&lt;/p&gt;

&lt;p&gt;Build useful tools, keep them narrow, and let your application code enforce the safety boundaries.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>SOLSTICE: Planetary Balance</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:17:53 +0000</pubDate>
      <link>https://dev.to/gaurav101/solstice-planetary-balance-1hec</link>
      <guid>https://dev.to/gaurav101/solstice-planetary-balance-1hec</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/june-game-jam-2026-06-03"&gt;June Solstice Game Jam&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Solstice Balance is an interactive 3D calibration simulator where the player stabilizes Earth's obliquity (target 23.44°) and rotational speed to prevent a simulated thermal collapse.&lt;br&gt;
Players use thruster-style controls to correct tilt and spin while reacting to procedurally generated cosmic disturbances (solar flares, gravitational tides, meteors). &lt;br&gt;
The goal was to make a tactile, physics-driven experience that ties to the June Solstice theme—balancing seasonal geometry with emergent hazards.&lt;/p&gt;
&lt;h2&gt;
  
  
  Game Demo
&lt;/h2&gt;

&lt;p&gt;Play the live demo: &lt;a href="https://solstice-game.vercel.app/" rel="noopener noreferrer"&gt;https://solstice-game.vercel.app/&lt;/a&gt;&lt;br&gt;
(Recommended: open in a desktop browser for best WebGL and audio support.)&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://solstice-game.vercel.app/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;solstice-game.vercel.app&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Full source code and development history: &lt;a href="https://github.com/gaurav101/solstice-game" rel="noopener noreferrer"&gt;https://github.com/gaurav101/solstice-game&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI Assistance:&lt;/strong&gt; Utilized Google Gemini to pair-program, debug complex WebGL contexts, and rapidly iterate on the core physics calculations and WebGL Sound generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework:&lt;/strong&gt; React + TypeScript with Vite for fast iteration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3D &amp;amp; Physics:&lt;/strong&gt; Three.js for rendering and &lt;code&gt;cannon-es&lt;/code&gt; for rigid-body physics to simulate tilt, spin, and meteor impacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audio:&lt;/strong&gt; WebAudio-based synth engine and sampled effects for real-time atmospheric feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI:&lt;/strong&gt; Tailwind CSS for a clean, rapid HUD layout; React state syncs real-time telemetry (score, alignment, RPM) seamlessly with the WebGL loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Optimization:&lt;/strong&gt; Employed procedural textures, batched particle systems, and defensive resource disposal to prevent memory leaks across scene restarts. 
## How to play the game 
Below is your official flight manual to successfully calibrate Earth's rotation and orientation, countering severe space weather, and maintaining life-supporting equilibrium.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Notable design choice:&lt;/em&gt; The game loop runs inside a single &lt;code&gt;requestAnimationFrame&lt;/code&gt; driven effect that cleanly tears down Three/Cannon resources when switching game states to completely avoid leaking WebGL contexts.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Play the Game  (&lt;a href="https://solstice-game.vercel.app/" rel="noopener noreferrer"&gt;https://solstice-game.vercel.app/&lt;/a&gt;)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🛰️ 1. The Core Objective
&lt;/h3&gt;

&lt;p&gt;Your active task is to guide and maintain Earth's planetary parameters in specified target alignment zones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Target Axial Obliquity:&lt;/strong&gt; &lt;strong&gt;23.0° to 23.9°&lt;/strong&gt; (aiming precisely for the natural June Solstice angle of &lt;strong&gt;23.44°&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Target Rotational Velocity (Spin):&lt;/strong&gt; &lt;strong&gt;1.0 to 2.5 RPM&lt;/strong&gt; (Revolutions Per Minute).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Planetary Ecosystem Integrity:&lt;/strong&gt; Must remain above &lt;strong&gt;0%&lt;/strong&gt;. Integrity decays rapidly any second Earth remains outside optimal thresholds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🎮 2. Control Layout
&lt;/h3&gt;

&lt;p&gt;Counter-act deep space gravitational pull and harsh solar winds by applying immediate corrective torque.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Keyboard Input&lt;/th&gt;
&lt;th&gt;Control Board Thruster&lt;/th&gt;
&lt;th&gt;Operational Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;W&lt;/code&gt; or &lt;code&gt;▲ Up&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Pitch Forward&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tilts Earth's axis forward pointing towards the Sun&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;S&lt;/code&gt; or &lt;code&gt;▼ Down&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Pitch Backward&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tilts Earth's axis backward away from the Sun&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;A&lt;/code&gt; or &lt;code&gt;◀ Left&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Spin Faster&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Speeds up the Earth's west-to-east spin velocity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;D&lt;/code&gt; or &lt;code&gt;▶ Right&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Spin Slower&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Applies electromagnetic dragging to slow rotation down&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  ☀️ 3. Key Calibration Rules
&lt;/h3&gt;

&lt;h4&gt;
  
  
  📍 The Solstice sweet spot (23.44°)
&lt;/h4&gt;

&lt;p&gt;When Earth is tilted at &lt;strong&gt;23.44°&lt;/strong&gt;, the North Pole is angled perfectly toward the Sun. In this position, you will see a bright circular sunlit ring over the Arctic region (the Midnight Sun) while the Antarctic Circle remains in winter shadow. This is the &lt;strong&gt;June Solstice Threshold&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Maintaining the axis inside the green &lt;strong&gt;23.0° - 23.9°&lt;/strong&gt; band raises your &lt;strong&gt;Calibration Accuracy&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  Every second spent inside optimal zones awards &lt;strong&gt;Equilibrium Points&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  🌀 The Rotational Band (1.0 to 2.5 RPM)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Static Risk (Spin below 0.3 RPM):&lt;/strong&gt; If the Earth stops spinning, one side of the planet is cooked under permanent radiation, and the other freezes in absolute darkness. &lt;em&gt;Ecosystem integrity will drop exponentially!&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Erosion Risk (Spin above 3.5 RPM):&lt;/strong&gt; Rotating too fast generates cosmic atmospheric drag that will tear away the ozone layers. &lt;em&gt;Ecosystem integrity drops rapidly.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🚨 4. Galactic Disturbance Hazards
&lt;/h3&gt;

&lt;p&gt;The orbital field is subject to unexpected external solar interferences that will knock you out of alignment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Coronal Mass Ejections (Solar Flares):&lt;/strong&gt; Strong electromagnetic storms that apply extreme pitch forces, abruptly throwing off the planetary tilt obliquity.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Atmospheric Gravitational Tides:&lt;/strong&gt; Tidal fluctuations that can speed up or slow down Earth's spin speed.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Meteor Incursions:&lt;/strong&gt; Direct strikes that transfer kinetic momentum, rotating or tipping Earth suddenly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Listen for the Audio Synth’s pitch and tempo:&lt;/em&gt; The onboard audio system uses warning buzzers and frantic tempo modulation to signal when parameters are entering danger thresholds.&lt;/p&gt;




&lt;h3&gt;
  
  
  🏆 5. Tips for Pro-Commanders
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Pulsed Thrusters over Static Holding:&lt;/strong&gt; Do not hold down thruster buttons endlessly. Tap controls in short bursts to counteract drift, letting the friction settle the planet in the optimal zone.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Balance First, Tilt Second:&lt;/strong&gt; If you are knocked completely off-course, prioritize bringing the planetary spin back into the &lt;strong&gt;1.0 - 2.5 RPM band&lt;/strong&gt; first to stop thermal decay, then nudge the obliquity to the target solstice angle.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Observe the Shaders:&lt;/strong&gt; Watch the gorgeous daytime sunlight and glittering nightly city light shaders. If you see city light configurations rotating either too fast or not at all, immediately act!&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Initialize engine calibration, steady the core, and save the Solstice!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Google AI Usage:&lt;/strong&gt; Google Gemini was instrumental in prototyping the initial graphics setup, debugging tricky 3D coordinate transformations, and ensuring the physics-driven gameplay integrated perfectly with React's state management.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>devchallenge</category>
      <category>gamechallenge</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Build a Local RAG AI App with Ollama, Mistral, and Node.js</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Sun, 14 Jun 2026 15:19:13 +0000</pubDate>
      <link>https://dev.to/gaurav101/build-a-local-rag-ai-app-with-ollama-mistral-and-nodejs-4bk9</link>
      <guid>https://dev.to/gaurav101/build-a-local-rag-ai-app-with-ollama-mistral-and-nodejs-4bk9</guid>
      <description>&lt;p&gt;Most people start using Large Language Models by asking questions directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question -&amp;gt; LLM -&amp;gt; Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works well for general questions.&lt;/p&gt;

&lt;p&gt;But what happens when you ask about your own documents, company policy, product FAQ, or internal notes?&lt;/p&gt;

&lt;p&gt;The model may not know the answer. Even worse, it may guess confidently. This is called a hallucination.&lt;/p&gt;

&lt;p&gt;That is the problem &lt;strong&gt;RAG&lt;/strong&gt; solves.&lt;/p&gt;

&lt;p&gt;In this article, we will build a simple local RAG app using &lt;strong&gt;Ollama&lt;/strong&gt;, &lt;strong&gt;Mistral&lt;/strong&gt;, and &lt;strong&gt;Node.js&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Complete code is available here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gaurav101/ai-experiment/tree/main/rag" rel="noopener noreferrer"&gt;https://github.com/gaurav101/ai-experiment/tree/main/rag&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is RAG?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RAG&lt;/strong&gt; stands for &lt;strong&gt;Retrieval-Augmented Generation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That sounds complex, but the idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before asking the LLM to answer, first search your own documents and give the most useful parts to the model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So instead of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User question -&amp;gt; LLM -&amp;gt; Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User question
   -&amp;gt; Search local documents
   -&amp;gt; Find relevant text
   -&amp;gt; Send that text to the LLM
   -&amp;gt; Generate an answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of it like an open-book exam.&lt;/p&gt;

&lt;p&gt;The LLM is still doing the writing, but now it has the right page open before it answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why RAG Matters
&lt;/h2&gt;

&lt;p&gt;RAG is important because most real AI apps need private or updated information.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A chatbot that answers questions from company documents&lt;/li&gt;
&lt;li&gt;A support assistant that reads product FAQs&lt;/li&gt;
&lt;li&gt;A legal assistant that searches contracts&lt;/li&gt;
&lt;li&gt;A coding assistant that understands project docs&lt;/li&gt;
&lt;li&gt;A personal assistant that uses your own notes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without RAG, the model only uses what it already learned during training.&lt;/p&gt;

&lt;p&gt;With RAG, we can give the model fresh information at runtime.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need to retrain the model&lt;/li&gt;
&lt;li&gt;Documents can stay private&lt;/li&gt;
&lt;li&gt;Answers are based on your data&lt;/li&gt;
&lt;li&gt;The system is easier to update&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your refund policy changes, you update the document and rebuild the index. You do not retrain the LLM.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Will Build
&lt;/h2&gt;

&lt;p&gt;We will build a local RAG app that can answer questions from files stored on your machine.&lt;/p&gt;

&lt;p&gt;The app uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; for the code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ollama&lt;/strong&gt; to run models locally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mistral&lt;/strong&gt; to generate answers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nomic-embed-text&lt;/strong&gt; to create embeddings&lt;/li&gt;
&lt;li&gt;A local &lt;code&gt;data/docs&lt;/code&gt; folder for documents&lt;/li&gt;
&lt;li&gt;A local &lt;code&gt;data/index.json&lt;/code&gt; file as a simple vector index&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documents -&amp;gt; Chunks -&amp;gt; Embeddings -&amp;gt; Search -&amp;gt; Context -&amp;gt; Mistral -&amp;gt; Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not worry if words like "embeddings" or "vector index" feel new. We will walk through them step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install Ollama
&lt;/h2&gt;

&lt;p&gt;First, install Ollama:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;https://ollama.com/download&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On Linux or macOS, you can also install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start Ollama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now pull the two models we need.&lt;/p&gt;

&lt;p&gt;Mistral will generate the final answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull mistral
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;nomic-embed-text will convert text into embeddings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull nomic-embed-text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test Mistral with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run mistral
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Create the Node.js Project
&lt;/h2&gt;

&lt;p&gt;Create a new project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;local-rag
&lt;span class="nb"&gt;cd &lt;/span&gt;local-rag
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use ES modules by adding this to &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install &lt;code&gt;dotenv&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this project, we use these scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node src/index-docs.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ask:ollama"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node src/ask-ollama.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npm run index&lt;/code&gt; builds the searchable document index.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run ask:ollama&lt;/code&gt; asks a question using Ollama and Mistral.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Add Local Documents
&lt;/h2&gt;

&lt;p&gt;Create a folder for your documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; data/docs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data/docs/company-faq.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Refunds are allowed within 14 days of purchase.
Enterprise customers get priority email support.
The product supports SSO on the Business and Enterprise plans.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These documents are the knowledge base for our RAG app.&lt;/p&gt;

&lt;p&gt;Later, when the user asks a question, the app will search these files first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Add Configuration
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/config.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This file keeps all important settings in one place:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DOCS_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOCS_DIR&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data/docs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;INDEX_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;INDEX_FILE&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data/index.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OLLAMA_BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OLLAMA_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:11434&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OLLAMA_EMBED_ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/embeddings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OLLAMA_EMBED_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nomic-embed-text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OLLAMA_GEN_ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/generate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OLLAMA_GEN_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OLLAMA_MODEL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mistral&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;p&gt;This tells the app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where to read documents from&lt;/li&gt;
&lt;li&gt;Where to save the index&lt;/li&gt;
&lt;li&gt;Which model to use for embeddings&lt;/li&gt;
&lt;li&gt;Which model to use for answers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Read and Split Documents
&lt;/h2&gt;

&lt;p&gt;LLMs work better when we give them small, focused pieces of text.&lt;/p&gt;

&lt;p&gt;So we split long documents into smaller parts called &lt;strong&gt;chunks&lt;/strong&gt;.&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;chunkText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;overlap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;overlap&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;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&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;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;size = 900&lt;/code&gt; means each chunk is around 900 characters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;overlap = 150&lt;/code&gt; means the next chunk repeats 150 characters from the previous one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The overlap is useful because important meaning can sit between two chunks.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk 1: characters 0 to 900
Chunk 2: characters 750 to 1650
Chunk 3: characters 1500 to 2400
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Create Embeddings
&lt;/h2&gt;

&lt;p&gt;An embedding is a list of numbers that represents the meaning of text.&lt;/p&gt;

&lt;p&gt;For example, this sentence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The product supports SSO on Business and Enterprise plans.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is converted into a vector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0.12, -0.04, 0.89, ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact numbers are not important for us.&lt;/p&gt;

&lt;p&gt;What matters is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Similar text gets similar embeddings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why Do We Need &lt;code&gt;nomic-embed-text&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;Mistral is good at generating answers, but we also need a way to search our documents by meaning.&lt;/p&gt;

&lt;p&gt;That is what &lt;code&gt;nomic-embed-text&lt;/code&gt; does.&lt;/p&gt;

&lt;p&gt;It converts text into embeddings so our app can compare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user's question&lt;/li&gt;
&lt;li&gt;The chunks from our documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without embeddings, our app would only do simple keyword matching.&lt;/p&gt;

&lt;p&gt;For example, if the document says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The product supports SSO on the Business and Enterprise plans.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the user asks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which subscription includes single sign-on?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;keyword search may miss the connection because the words are different.&lt;/p&gt;

&lt;p&gt;But embeddings can understand that &lt;strong&gt;SSO&lt;/strong&gt; and &lt;strong&gt;single sign-on&lt;/strong&gt; are related.&lt;/p&gt;

&lt;p&gt;So in this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;nomic-embed-text&lt;/code&gt; is used for search&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mistral&lt;/code&gt; is used for answering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means a question like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which plans have SSO?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should be close to the document sentence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The product supports SSO on the Business and Enterprise plans.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the embedding function:&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;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:11434/api/embeddings&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nomic-embed-text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embedding&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;We use this same function for both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document chunks&lt;/li&gt;
&lt;li&gt;User questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is how we compare a question with our documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Build the Index
&lt;/h2&gt;

&lt;p&gt;Now we create the index.&lt;/p&gt;

&lt;p&gt;The index is a JSON file that stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The document name&lt;/li&gt;
&lt;li&gt;The chunk text&lt;/li&gt;
&lt;li&gt;The embedding for that chunk
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildIndex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readDocuments&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chunkText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&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="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data/index.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data/index.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your documents are searchable by meaning, not just by exact words.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Search the Best Chunks
&lt;/h2&gt;

&lt;p&gt;When the user asks a question, we need to find the document chunks that are closest to that question.&lt;/p&gt;

&lt;p&gt;To do that, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Convert the question into an embedding&lt;/li&gt;
&lt;li&gt;Compare it with every saved document embedding&lt;/li&gt;
&lt;li&gt;Sort the results&lt;/li&gt;
&lt;li&gt;Keep the best matches&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The comparison uses &lt;strong&gt;cosine similarity&lt;/strong&gt;:&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;function&lt;/span&gt; &lt;span class="nf"&gt;cosineSimilarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;normA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;normB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;dot&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;normA&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;normB&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;dot&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normB&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;Then we search the index:&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;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data/index.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryEmbedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&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;index&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;cosineSimilarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embedding&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="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;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="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&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;The result is a small set of document chunks that are most likely to contain the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: Give Context to Mistral
&lt;/h2&gt;

&lt;p&gt;Now we have the useful document chunks.&lt;/p&gt;

&lt;p&gt;Next, we send them to Mistral with the user's question.&lt;/p&gt;

&lt;p&gt;The prompt looks like this:&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;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
Answer using only the context below.
If the answer is missing, say you do not know.

Context:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;

Question:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line is very important:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Answer using only the context below.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It tells the model not to guess.&lt;/p&gt;

&lt;p&gt;Then we call Ollama's generation API:&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;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:11434/api/generate&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mistral&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&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;&lt;code&gt;temperature: 0.2&lt;/code&gt; makes the answer more focused and less random.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 10: Ask a Question
&lt;/h2&gt;

&lt;p&gt;Now run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run ask:ollama &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"What plans support SSO?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The product supports SSO on the Business and Enterprise plans.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This answer came from the local document.&lt;/p&gt;

&lt;p&gt;Mistral did not need to already know your product FAQ. The RAG pipeline found the right context and gave it to the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full RAG Flow
&lt;/h2&gt;

&lt;p&gt;Here is the complete flow again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Put documents in data/docs
2. Split documents into chunks
3. Convert chunks into embeddings
4. Save embeddings in data/index.json
5. User asks a question
6. Convert the question into an embedding
7. Find the most similar chunks
8. Add those chunks to the prompt
9. Ask Mistral to answer using that context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is RAG.&lt;/p&gt;

&lt;p&gt;Search first. Generate second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Local Version Is Useful
&lt;/h2&gt;

&lt;p&gt;This project is intentionally simple.&lt;/p&gt;

&lt;p&gt;It uses a JSON file instead of a vector database. That makes it easier to understand what is happening.&lt;/p&gt;

&lt;p&gt;For learning, this is perfect.&lt;/p&gt;

&lt;p&gt;For production, you may later replace &lt;code&gt;data/index.json&lt;/code&gt; with a vector database such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chroma&lt;/li&gt;
&lt;li&gt;Qdrant&lt;/li&gt;
&lt;li&gt;Weaviate&lt;/li&gt;
&lt;li&gt;pgvector&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the core idea stays the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Store embeddings -&amp;gt; Search similar chunks -&amp;gt; Send context to the LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;RAG is one of the most useful patterns for building practical AI apps.&lt;/p&gt;

&lt;p&gt;It helps LLMs answer using your data without retraining the model.&lt;/p&gt;

&lt;p&gt;In this article, we built a local RAG app with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ollama&lt;/li&gt;
&lt;li&gt;Mistral&lt;/li&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;nomic-embed-text&lt;/li&gt;
&lt;li&gt;Local documents&lt;/li&gt;
&lt;li&gt;A JSON-based vector index&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Search your documents first, then let the LLM answer with that context.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Complete implementation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gaurav101/ai-experiment/tree/main/rag" rel="noopener noreferrer"&gt;https://github.com/gaurav101/ai-experiment/tree/main/rag&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Setup references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;Ollama download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ollama.com/library/mistral" rel="noopener noreferrer"&gt;Mistral on Ollama&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ollama.com/library/nomic-embed-text" rel="noopener noreferrer"&gt;nomic-embed-text on Ollama&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>node</category>
      <category>javascript</category>
      <category>rag</category>
    </item>
    <item>
      <title>MCP Server and RAG: A Simple Introduction for Developers</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Fri, 12 Jun 2026 14:46:51 +0000</pubDate>
      <link>https://dev.to/gaurav101/mcp-server-and-rag-a-simple-introduction-for-developers-3kh8</link>
      <guid>https://dev.to/gaurav101/mcp-server-and-rag-a-simple-introduction-for-developers-3kh8</guid>
      <description>&lt;p&gt;AI tools are becoming more useful for developers, but they still have one big problem: they do not automatically know your private files, your latest docs, your local database, or your project rules.&lt;/p&gt;

&lt;p&gt;Two ideas help solve this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAG&lt;/strong&gt; gives AI the right information before it answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP servers&lt;/strong&gt; let AI tools safely use your local tools and data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, they make AI feel less like a generic chatbot and more like a useful coding partner.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is RAG?
&lt;/h2&gt;

&lt;p&gt;RAG means &lt;strong&gt;retrieval-augmented generation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In simple words, RAG means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user asks a question.&lt;/li&gt;
&lt;li&gt;Your app searches useful documents.&lt;/li&gt;
&lt;li&gt;Your app sends the best matching text to the AI.&lt;/li&gt;
&lt;li&gt;The AI answers using that text.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: What is our refund policy?

RAG search finds:
"Refunds are allowed within 14 days of purchase."

AI answer:
"Your refund policy allows refunds within 14 days of purchase."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without RAG, the AI may guess. With RAG, it can answer from your real data.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is an MCP Server?
&lt;/h2&gt;

&lt;p&gt;MCP means &lt;strong&gt;Model Context Protocol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of an MCP server as a small local app that gives AI tools extra abilities.&lt;/p&gt;

&lt;p&gt;For example, an MCP server can let an AI tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search your local documentation.&lt;/li&gt;
&lt;li&gt;Read selected project files.&lt;/li&gt;
&lt;li&gt;Query a database.&lt;/li&gt;
&lt;li&gt;Call an internal API.&lt;/li&gt;
&lt;li&gt;Look up company rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI does not need direct access to everything. It calls a specific tool, and you control what that tool can do.&lt;/p&gt;

&lt;p&gt;Example MCP tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tool name: search_docs
Input: "How do I reset my password?"
Output: The best matching paragraphs from your documentation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How RAG and MCP Work Together
&lt;/h2&gt;

&lt;p&gt;RAG is the search brain. MCP is the bridge.&lt;/p&gt;

&lt;p&gt;Here is the simple flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Claude, Codex, or another AI tool
        |
        asks MCP tool: search_docs("refund policy")
        |
Local MCP server
        |
Searches your local RAG index
        |
Returns useful document chunks
        |
AI writes a better answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is powerful because the same local RAG system can work with many AI tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Developers Benefit
&lt;/h2&gt;

&lt;p&gt;MCP and RAG help developers in practical ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Better answers:&lt;/strong&gt; AI uses your real docs instead of guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less copy-paste:&lt;/strong&gt; You do not need to paste long files into chat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private by design:&lt;/strong&gt; Your search index can stay on your computer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable tools:&lt;/strong&gt; Build one &lt;code&gt;search_docs&lt;/code&gt; tool and use it in Claude, Codex, Cursor, or other MCP clients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project-aware AI:&lt;/strong&gt; The assistant can understand your codebase rules, API docs, and team notes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, instead of asking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read all these files and explain our auth flow...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search the local docs and explain how login works.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI calls your MCP tool, gets the right context, and answers faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why JavaScript Is a Great Choice
&lt;/h2&gt;

&lt;p&gt;JavaScript is one of the easiest choices for building beginner-friendly MCP and RAG tools.&lt;/p&gt;

&lt;p&gt;Here is why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Most developers already know some JavaScript.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node.js is simple to run locally.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI SDKs support JavaScript well.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP has a JavaScript/TypeScript SDK.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You can reuse the same code in CLI tools, servers, and web apps.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A small JavaScript RAG function can look like this:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;answerQuestion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;searchLocalDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunks&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="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;askAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    Use this context to answer:
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;

    Question:
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
  `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And an MCP tool can expose that search function:&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="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;search_docs&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="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search local project documents&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;searchLocalDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;results&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="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&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="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;You do not need to start with a complex database. Beginners can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text files for documents.&lt;/li&gt;
&lt;li&gt;JSON for a small search index.&lt;/li&gt;
&lt;li&gt;Ollama for local embeddings.&lt;/li&gt;
&lt;li&gt;Node.js for the MCP server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Later, you can upgrade to SQLite, Postgres, Chroma, LanceDB, or another vector database.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Example
&lt;/h2&gt;

&lt;p&gt;Imagine you have a folder like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docs/
  refund-policy.txt
  api-guide.md
  onboarding-notes.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You build a JavaScript RAG app that searches those files. Then you expose it as an MCP server.&lt;/p&gt;

&lt;p&gt;Now you can ask Codex or Claude:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use my local docs to explain how refunds work.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI can call your local search tool and answer from your files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;RAG makes AI more accurate. MCP makes AI more connected. JavaScript makes both easier to build.&lt;/p&gt;

&lt;p&gt;If you are a beginner, start small: search a few text files, return the best matches, and connect that search to one AI tool. Once that works, you already understand the core idea behind many real-world AI developer tools.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>mcp</category>
      <category>rag</category>
    </item>
    <item>
      <title>Build a tiny MCP server in JavaScript -Claude, Codex friendly</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Fri, 12 Jun 2026 11:07:19 +0000</pubDate>
      <link>https://dev.to/gaurav101/build-a-tiny-mcp-server-in-javascript-claude-codex-friendly-pbh</link>
      <guid>https://dev.to/gaurav101/build-a-tiny-mcp-server-in-javascript-claude-codex-friendly-pbh</guid>
      <description>&lt;p&gt;TL;DR — Ship a tiny server that exposes tools to LLMs using the Model Context Protocol (MCP). This guide shows a minimal, runnable example, explains each part, and covers common pitfalls (ESM imports, transports, adapters).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MCP lets models call real tools (fetching data, running commands) instead of hallucinating.&lt;/li&gt;
&lt;li&gt;It’s a great way to extend models with live data: notifications, GitHub lookups, database queries, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you'll get
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A working server (index.js) exposing a single tool: get-repo-stats&lt;/li&gt;
&lt;li&gt;Quick instructions to run locally (stdio) or remotely (HTTP)&lt;/li&gt;
&lt;li&gt;Short adapter patterns for OpenAI (Codex/GPT), Google Gemini, and Anthropic Claude&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node 18+&lt;/li&gt;
&lt;li&gt;npm install (project already depends on @modelcontextprotocol/sdk and zod)&lt;/li&gt;
&lt;li&gt;Add "type": "module" to package.json for ESM&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick start (copy-paste)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) Install deps or verify existing:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2) Run locally:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see a MODULE_TYPELESS_PACKAGE_JSON warning, add "type": "module" to package.json (example below).&lt;/p&gt;

&lt;p&gt;Minimal index.js (copy to your repo)&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;McpServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@modelcontextprotocol/sdk/server/mcp.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StdioServerTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@modelcontextprotocol/sdk/server/stdio.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.github.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;McpServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;github-stats-server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get-repo-stats&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Get star count, fork count, and open issues for a GitHub repo&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="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GitHub username or org&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Repository name&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;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/repos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Could not find repo &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; — ⭐ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stargazers_count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; · 🍴 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forks_count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; · 🐛 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open_issues_count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StdioServerTransport&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code walkthrough
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;McpServer: high-level API to register tools and handle protocol&lt;/li&gt;
&lt;li&gt;&lt;p&gt;server.tool(name, description, schema, handler): register callable tools. Use zod for input validation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handler returns a result shaped like&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;content:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;text:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'...'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;StdioServerTransport: easiest local transport — reads/writes over stdin/stdout&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Call server.connect(transport) after registering tools so the initialization advertises available tools&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ESM / import gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add "type": "module" to package.json so Node treats your files as ESM and avoids MODULE_TYPELESS_PACKAGE_JSON warning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use package export entrypoints and include .js extension:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good: import { McpServer } from 
"@modelcontextprotocol/sdk/server/mcp.js";&lt;/li&gt;
&lt;li&gt;Bad: deep imports to dist/esm/… — Node may fail to resolve or duplicate path segments.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example package.json snippet&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@modelcontextprotocol/sdk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.29.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"zod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.25"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Transports at a glance
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;stdio: local adapters (Claude Desktop, local testing). Simpler to run.&lt;/li&gt;
&lt;li&gt;StreamableHTTP: model host calls an HTTP endpoint (preferred for cloud deployments).&lt;/li&gt;
&lt;li&gt;WebSocket: long-lived bidirectional connections for advanced flows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  how to connect an LLM
&lt;/h2&gt;

&lt;h3&gt;
  
  
  OpenAI / Codex (function-calling flow)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Add a Local MCP Server to Codex
&lt;/h4&gt;

&lt;p&gt;Codex can use local MCP servers through STDIO. MCP configuration is stored in&lt;br&gt;
&lt;code&gt;~/.codex/config.toml&lt;/code&gt; by default, or in a project-level &lt;code&gt;.codex/config.toml&lt;/code&gt;&lt;br&gt;
for trusted projects.&lt;/p&gt;
&lt;h6&gt;
  
  
  Option 1: Add with the Codex CLI
&lt;/h6&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex mcp add my-local-git-stat-server &lt;span class="nt"&gt;--&lt;/span&gt; node /path/to/your/project/index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If the server needs environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex mcp add my-local-server &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; node /path/to/your/project/index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Option 2: Add Manually in &lt;code&gt;config.toml&lt;/code&gt;
&lt;/h5&gt;

&lt;p&gt;Open &lt;code&gt;~/.codex/config.toml&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.my-local-server]&lt;/span&gt;
&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"node"&lt;/span&gt;
&lt;span class="py"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"/path/to/your/project/index.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify the Server
&lt;/h2&gt;

&lt;p&gt;Restart Codex, then check active MCP servers from the Codex TUI:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You can also inspect available MCP commands with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex mcp &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Google Gemini
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If the hosting environment supports HTTP callbacks, use StreamableHTTPClientTransport. Otherwise, run a bridge process that translates Gemini function calls into MCP client calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Anthropic Claude
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;For local Claude Desktop: the desktop app can spawn your server process and use stdio. Configure the desktop app to run &lt;code&gt;node /path/index.js&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For hosted Claude integrations, prefer HTTP transport.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Small adapter sketch (pseudo)&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;// spawn server then connect client-facing logic&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/client/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StdioClientTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/client/stdio.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StdioClientTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.js&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;await&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bridge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// When model asks for a tool: client.callTool({ method: 'get-repo-stats', params: { owner, repo } })&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using with OpenAI (Codex / GPT function-calling)
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Recommended patterns
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Local/testing: use stdio transport + StdioClientTransport (spawn the server as a child process from your adapter).&lt;/li&gt;
&lt;li&gt;Hosted: expose your MCP server with StreamableHTTPTransport and let your adapter call it via StreamableHTTPClientTransport.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimal adapter sketch (pseudo)&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;// adapter-openai.js — high level sketch&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// or your HTTP wrapper&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/client/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StdioClientTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/client/stdio.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// or StreamableHTTPClientTransport&lt;/span&gt;


&lt;span class="c1"&gt;// spawn local server and connect client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StdioClientTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.js&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;await&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai-bridge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// When a model function call arrives (pseudo):&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleModelFunctionCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Map function name to your tool method (e.g., 'get-repo-stats')&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toolResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`tools/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// Convert the MCP result into the expected function-response string or JSON and return it&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;toolResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Use your OpenAI SDK to call model and route function-calls through handleModelFunctionCall&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Notes
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Keep the mapping from model function names to MCP tool names explicit and documented.&lt;/li&gt;
&lt;li&gt;Don't commit API keys — use env vars, and sanitize when spawning processes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using with Anthropic Claude (Claude Desktop)
&lt;/h3&gt;

&lt;p&gt;Local desktop (stdio) — easiest&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Add your server to Claude Desktop configuration (absolute path to your index.js):
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github-stats"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/absolute/path/to/index.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Ensure your project has "type": "module" in package.json and your imports use .js extensions.
&lt;/h4&gt;

&lt;h4&gt;
  
  
  3. Start / restart Claude Desktop. The desktop app will spawn your server and talk over stdin/stdout. Ask Claude: "What are the stats for facebook/react?" and it should call the get-repo-stats tool.
&lt;/h4&gt;

&lt;h3&gt;
  
  
  Hosted Claude / remote
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If your Claude hosting supports HTTP callbacks, host the MCP server behind an HTTP wrapper and use StreamableHTTPClientTransport on the model side.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Run the server with node --trace-warnings index.js when debugging import/ESM issues.&lt;/li&gt;
&lt;li&gt;Use a small script that calls client.listTools() after connecting to verify the advertised tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Debugging checklist
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ERR_MODULE_NOT_FOUND for deep path? Switch to &lt;code&gt;@modelcontextprotocol/sdk/server/mcp.js&lt;/code&gt; imports.&lt;/li&gt;
&lt;li&gt;MODULE_TYPELESS_PACKAGE_JSON? Add "type": "module" to package.json.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;node --trace-warnings index.js&lt;/code&gt; for resolution traces.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security &amp;amp; deployment (brief)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If exposing HTTP, require auth (API key, OAuth) and use TLS.&lt;/li&gt;
&lt;li&gt;When spawning child processes, sanitize environment and avoid leaking secrets.&lt;/li&gt;
&lt;li&gt;Limit resource usage (timeouts, concurrency) on tools that call external services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Make it yours (next steps)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add more tools (issues, releases, search)&lt;/li&gt;
&lt;li&gt;Add authentication for private GitHub repos&lt;/li&gt;
&lt;li&gt;Wrap as an npm CLI and add a config file for easy local installs&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  References &amp;amp; where to look
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;SDK examples: node_modules/@modelcontextprotocol/sdk/dist/esm/examples&lt;/li&gt;
&lt;li&gt;Official docs: &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;https://modelcontextprotocol.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;example code : &lt;a href="https://github.com/gaurav101/ai-experiment" rel="noopener noreferrer"&gt;https://github.com/gaurav101/ai-experiment&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>mcp</category>
      <category>ai</category>
      <category>node</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Gaurav Kumar Singh</dc:creator>
      <pubDate>Tue, 02 Jun 2026 14:46:00 +0000</pubDate>
      <link>https://dev.to/gaurav101/-4eg1</link>
      <guid>https://dev.to/gaurav101/-4eg1</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/gaurav101/architectural-mastery-in-nodejs-implementing-the-factory-and-strategy-patternsfor-a-npm-package-2hpj" class="crayons-story__hidden-navigation-link"&gt;Architectural Mastery in Node.js: Designing a Robust, Multi-Platform NPM Package&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&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="/gaurav101" 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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F893926%2Faba99e1d-37d8-4b1a-ab42-802ed842d0e6.jpeg" alt="gaurav101 profile" class="crayons-avatar__image" width="422" height="422"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/gaurav101" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Gaurav Kumar Singh
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Gaurav Kumar Singh
                
              
              &lt;div id="story-author-preview-content-3803930" 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="/gaurav101" 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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F893926%2Faba99e1d-37d8-4b1a-ab42-802ed842d0e6.jpeg" class="crayons-avatar__image" alt="" width="422" height="422"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Gaurav Kumar Singh&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/gaurav101/architectural-mastery-in-nodejs-implementing-the-factory-and-strategy-patternsfor-a-npm-package-2hpj" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 2&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/gaurav101/architectural-mastery-in-nodejs-implementing-the-factory-and-strategy-patternsfor-a-npm-package-2hpj" id="article-link-3803930"&gt;
          Architectural Mastery in Node.js: Designing a Robust, Multi-Platform NPM Package
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/node"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;node&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/designpatterns"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;designpatterns&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/architecture"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;architecture&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/gaurav101/architectural-mastery-in-nodejs-implementing-the-factory-and-strategy-patternsfor-a-npm-package-2hpj#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;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;
            8 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

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

&lt;/div&gt;


</description>
      <category>architecture</category>
      <category>javascript</category>
      <category>node</category>
      <category>npm</category>
    </item>
  </channel>
</rss>
