<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[AIBoosted.dev]]></title><description><![CDATA[Building AI apps with AI, Typescript, React and Angular]]></description><link>https://www.aiboosted.dev</link><image><url>https://substackcdn.com/image/fetch/$s_!kOpo!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ccd56ab-f68a-4392-a818-6510ef7842dc_1280x1280.png</url><title>AIBoosted.dev</title><link>https://www.aiboosted.dev</link></image><generator>Substack</generator><lastBuildDate>Wed, 29 Apr 2026 01:58:10 GMT</lastBuildDate><atom:link href="https://www.aiboosted.dev/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Gergely Szerovay]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[aiboosteddev@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[aiboosteddev@substack.com]]></itunes:email><itunes:name><![CDATA[Gergely Szerovay]]></itunes:name></itunes:owner><itunes:author><![CDATA[Gergely Szerovay]]></itunes:author><googleplay:owner><![CDATA[aiboosteddev@substack.com]]></googleplay:owner><googleplay:email><![CDATA[aiboosteddev@substack.com]]></googleplay:email><googleplay:author><![CDATA[Gergely Szerovay]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Is This the Future of E2E Testing? How AI Automates Browser Tests from Plain English Requirements]]></title><description><![CDATA[How AI Transforms Your Requirements into Gherkin Scenarios and Executes Them via Chrome DevTools MCP]]></description><link>https://www.aiboosted.dev/p/e2e-testing-with-chrome-dev-tools-mcp-server</link><guid isPermaLink="false">https://www.aiboosted.dev/p/e2e-testing-with-chrome-dev-tools-mcp-server</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Thu, 30 Oct 2025 10:20:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t5z0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!t5z0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t5z0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!t5z0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!t5z0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!t5z0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!t5z0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png" width="1456" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:871825,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/177548178?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!t5z0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!t5z0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!t5z0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!t5z0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed256a2-fc24-48fd-ac5c-7d631f48ba65_1620x890.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>End-to-end testing has always been the gold standard for verifying that your application actually works the way users experience it. But let&#8217;s be honest: writing and maintaining E2E tests is often a thankless job. Flaky selectors, timing issues, brittle test code that breaks with every UI change, the pain points are real and numerous.</p><p>What if instead of writing test code, <strong>you could simply describe what you want to test in plain language</strong>, and have an <strong>AI assistant execute those tests</strong> for you, then generate a detailed <strong>report</strong> of what happened? That&#8217;s exactly what becomes possible when you combine the Chrome DevTools MCP server with AI-powered testing workflows.</p><p>This is Part 2 of our Chrome DevTools MCP series. In <a href="https://www.aiboosted.dev/p/chrome-devtools-mcp-server-guide">Part 1 (Chrome DevTools MCP Server Guide)</a>, we explored the <strong>fundamentals of the Chrome DevTools MCP server and its tools for browser automation.</strong> Now, we&#8217;re going to put those tools to work in a complete, practical workflow: taking a product requirements document, generating test scenarios, executing them automatically, and producing comprehensive test reports, all orchestrated by your AI assistant.</p><p><strong>By the end of this article, you&#8217;ll understand how to transform requirements documents into executable test scenarios and leverage AI to execute Gherkin-style tests via browser automation.</strong></p><p>Let&#8217;s dive in with a real example: a Tic-Tac-Toe game. I&#8217;ve generated both <strong>React</strong> and <strong>Angular</strong> versions of this demo app in the <a href="https://github.com/gergelyszerovay/articles/tree/e2e-testing-with-chrome-devtools-mcp">companion GitHub repository</a>, so you can follow along with whichever framework you prefer. The repository contains everything you need: the <a href="https://github.com/gergelyszerovay/articles/blob/e2e-testing-with-chrome-devtools-mcp/tic-tac-toe-react/src/features/tic-tac-toe/PRD.md">complete PRD</a>, <a href="https://github.com/gergelyszerovay/articles/blob/e2e-testing-with-chrome-devtools-mcp/tic-tac-toe-react/src/features/tic-tac-toe/ARCHITECTURE.md">architecture documentation</a>, <a href="https://github.com/gergelyszerovay/articles/tree/e2e-testing-with-chrome-devtools-mcp/e2e">generated Gherkin scenarios</a>, and <a href="https://github.com/gergelyszerovay/articles/blob/e2e-testing-with-chrome-devtools-mcp/e2e/core-gameplay.report.md">test reports</a>.</p><div><hr></div><blockquote><p>This article is also available on <a href="https://dev.to/this-is-angular/is-this-the-future-of-e2e-testing-how-ai-automates-browser-tests-from-plain-english-requirements-1d0">dev.to with better source code syntax highlighting.</a></p></blockquote><h2>The Traditional E2E Testing Workflow (The Old Way)</h2><p>Before we explore the AI-assisted approach, let&#8217;s acknowledge how E2E testing typically works and why it&#8217;s often neglected. The traditional workflow starts with writing specifications or a PRD to document what the application should do. Then you manually create test scenarios by thinking through user flows and edge cases. Next comes the real work: writing Puppeteer, Playwright, or Cucumber Gherkin <strong>scripts to translate those scenarios into code</strong>, followed by hours of <strong>debugging flaky selectors and timing issues</strong>. Finally, you <strong>maintain that test code</strong> as the UI inevitably changes, updating selectors, refactoring tests, and fixing breakages.</p><h2>Introducing the AI-Assisted Testing Workflow</h2><p>Here&#8217;s what changes when you let AI orchestrate your E2E testing using the Chrome DevTools MCP server:</p><h3>The Five-Step Process</h3><ol><li><p><strong>Document requirements</strong>: PRD, you&#8217;re doing this anyway</p></li><li><p><strong>Generate Gherkin scenarios</strong>: AI translates requirements into test scenarios</p></li><li><p><strong>AI executes tests</strong>: via Chrome DevTools MCP, no test code to write or maintain</p></li><li><p><strong>AI generates detailed test reports</strong></p></li><li><p><strong>Iterate and maintain</strong>: Update requirements and regenerate tests as needed</p></li></ol><h3>Why This Works</h3><p>The key insight is that we&#8217;re working in <strong>natural language throughout the entire process</strong>. Your <strong>requirements</strong> get written in natural language (PRD), then translated into natural language <strong>test scenarios</strong> (Gherkin). The AI orchestrates <strong>test execution</strong> by understanding these natural language instructions, and finally generates <strong>test reports</strong> in natural language that anyone can read. There&#8217;s no translation layer into brittle test code, no fighting with selectors, just clear descriptions of what should happen, executed and verified automatically.</p><p>Let&#8217;s see this in action with our Tic-Tac-Toe example.</p><h2>Step 1: Foundation: The Product Requirements</h2><p>Every good testing strategy starts with clear requirements. For our Tic-Tac-Toe game, we have a comprehensive Product Requirements Document (PRD) that lays everything out. You can find the <a href="https://github.com/gergelyszerovay/articles/blob/e2e-testing-with-chrome-devtools-mcp/tic-tac-toe-react/src/features/tic-tac-toe/PRD.md">complete PRD in the GitHub repository</a>.</p><h3>The Product Requirements Document (PRD)</h3><p>Our PRD covers everything we need to know about how the game should behave:</p><p><strong>User Stories</strong> organized by category:</p><ul><li><p>Core Gameplay: Placing marks, seeing turns, winning, draws</p></li><li><p>Game Management: New game, undo, move counter</p></li><li><p>User Experience: Hover effects, mobile support, accessibility</p></li></ul><p><strong>Functional Requirements</strong> (FR-1 through FR-19) that specify exact behaviors:</p><ul><li><p>FR-1: The game must support two players: X and O</p></li><li><p>FR-2: Player X always goes first</p></li><li><p>FR-3: Players alternate turns</p></li><li><p>FR-4: A player wins by placing three marks in a row</p></li><li><p>FR-5: The game ends in a draw if all cells are filled</p></li><li><p>FR-6: Cells cannot be overwritten once marked</p></li><li><p>...and so on</p></li></ul><p><strong>Non-Functional Requirements</strong> covering performance, usability, and accessibility:</p><ul><li><p>NFR-8: All interactive elements must have proper ARIA labels</p></li><li><p>NFR-9: Game must be fully keyboard navigable</p></li><li><p>NFR-10: Screen readers must be able to announce game state</p></li></ul><h3>Why This Documentation Matters</h3><p>This PRD serves as the <strong>single source of truth</strong> for both development and testing. When requirements are this clear and structured, AI can extract testable behaviors, generate appropriate test scenarios, understand the expected outcomes, and verify that the implementation matches the specification.</p><p><a href="https://www.aiboosted.dev/p/better-apps-with-vibe-coding-7-step-coding-guidelines">The investment in writing good requirements pays dividends throughout the entire development lifecycle</a>, but especially in testing. Think of it as building a solid foundation, everything else gets easier when you start with clarity.</p><h2>Step 2: From Requirements to Gherkin Scenarios</h2><p>Gherkin is a business-readable, domain-specific language for describing software behaviors without detailing how those behaviors are implemented. It uses a simple structure:</p><pre><code><code>Feature: Description of the feature
  Scenario: Description of a specific behavior
    Given [initial context]
    When [action taken]
    Then [expected outcome]</code></code></pre><p>If you&#8217;re new to Gherkin, the <a href="https://cucumber.io/docs/gherkin/reference/">Gherkin reference</a> has everything you need to know about the syntax and best practices. But don&#8217;t worry, you&#8217;ll pick up the basics just by following along with our examples.</p><h3>The Translation Process</h3><p>Here&#8217;s the simple prompt I used to transform our PRD into executable test scenarios:</p><pre><code><code>Create Gherkin feature files based on the PRD (Product Requirements Document) located at:
`tic-tac-toe-react/src/features/tic-tac-toe/PRD.md`

Save the generated feature files to the following directory:
`e2e`

Additionally, create a README.md file in the same `e2e` folder that explains:
- How each feature file maps to specific PRD requirements
- The relationship between test scenarios and product specifications</code></code></pre><p>That&#8217;s all it took. The AI read through the PRD, identified testable behaviors, and organized them into four focused feature files covering everything from basic gameplay to accessibility.</p><p>Let me show you how it translates requirements into Gherkin. The following examples are from the <a href="https://github.com/gergelyszerovay/articles/blob/e2e-testing-with-chrome-devtools-mcp/e2e/core-gameplay.feature">core-gameplay.feature</a> file in our repository. Take this functional requirement:</p><p><strong>From this requirement:</strong></p><blockquote><p>FR-2: Player X always goes first</p></blockquote><p><strong>To this Gherkin scenario:</strong></p><pre><code><code>Scenario: First move is always Player X
  When the game starts
  Then the status should display &#8220;Player X&#8217;s turn&#8221;
  And all cells should be empty
  And the move counter should show &#8220;0&#8221;</code></code></pre><p>Or this user story:</p><p><strong>From this user story:</strong></p><blockquote><p>As a player, I want to click on an empty cell to place my mark, so I can make my move</p></blockquote><p><strong>To this Gherkin scenario:</strong></p><pre><code><code>Scenario: Player places mark in empty cell
  When Player X clicks on cell 0
  Then cell 0 should display &#8220;X&#8221;
  And it should be Player O&#8217;s turn
  And the move counter should show &#8220;1&#8221;</code></code></pre><p>Pretty straightforward, right? The AI maintains the intent of each requirement while converting it into a testable format that anyone on your team can read and understand.</p><p>For our Tic-Tac-Toe game, the AI organized everything into four feature files covering core gameplay, game outcomes, controls, and accessibility. Rather than walk through all of them in detail, let me show you what the core gameplay scenarios look like, this will give you a good feel for how comprehensive these tests become.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>The Traceability Matrix</h3><p>One of the most valuable outputs is the <strong>traceability matrix</strong> that links each requirement to its corresponding test scenarios. You can see the <a href="https://github.com/gergelyszerovay/articles/blob/e2e-testing-with-chrome-devtools-mcp/e2e/README.md">complete traceability matrix in the repository</a>:<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NYgr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NYgr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png 424w, https://substackcdn.com/image/fetch/$s_!NYgr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png 848w, https://substackcdn.com/image/fetch/$s_!NYgr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png 1272w, https://substackcdn.com/image/fetch/$s_!NYgr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NYgr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png" width="594" height="344" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:344,&quot;width&quot;:594,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:25248,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/177548178?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NYgr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png 424w, https://substackcdn.com/image/fetch/$s_!NYgr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png 848w, https://substackcdn.com/image/fetch/$s_!NYgr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png 1272w, https://substackcdn.com/image/fetch/$s_!NYgr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c7060b-f0f1-4762-a8e7-b0d7d7308660_594x344.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This matrix ensures complete test coverage and makes it easy to verify that every requirement has been tested.</p><h2>Step 3: AI Executes Tests Using Chrome DevTools MCP</h2><p>Now comes the magic: executing these Gherkin scenarios automatically using the Chrome DevTools MCP server. Let&#8217;s walk through the actual conversation that orchestrated the entire test suite execution.</p><h3>The Initial Request</h3><p>Before running tests, you need your application running locally. In our case, we have both React and Angular implementations available in the <a href="https://github.com/gergelyszerovay/articles/tree/e2e-testing-with-chrome-devtools-mcp">GitHub repository</a>. To start the React version, run <code>npm start</code> from the <code>tic-tac-toe-react</code> directory, it&#8217;ll launch on port 4305. For Angular, navigate to <code>tic-tac-toe-ng</code> and use <code>ng serve</code>. Once your app is running, you can begin testing.</p><p>Here&#8217;s the prompt I used to kick off the test execution:</p><pre><code><code>I have a React app located in the `tic-tac-toe-react` directory that&#8217;s running on http://127.0.0.1:4305/.

Please use the Chrome DevTools MCP server to:
1. Execute the test steps defined in `e2e/core-gameplay.feature`
2. Generate a test report and save it to `e2e/core-gameplay.report.md`

For each Gherkin step in the report, include a text result indicating whether it passed or failed (and any relevant details).</code></code></pre><p>That&#8217;s it. No test code to write.</p><p><strong>Advanced Approach: Using Subagents for Large Test Suites</strong></p><p>For complex applications with many test scenarios, there&#8217;s a more scalable approach using subagents. Instead of executing all test steps in a single AI conversation context, <strong>you can delegate each scenario to its own subagent</strong>. The main orchestrator agent maintains only the Gherkin file, coordinates subagent executions, and generates the final consolidated report, while each subagent independently runs its assigned scenario&#8217;s steps.</p><p>This architecture provides significant benefits for <strong>large test suites</strong>. The main agent&#8217;s context stays clean and focused on orchestration rather than getting cluttered with hundreds of detailed test execution steps. <strong>Each scenario executes in isolation with its own fresh context</strong>, preventing context pollution and token limit issues. Most importantly, this approach allows you to run comprehensive test suites with many scenarios and steps without hitting context length limitations.</p><p>Here&#8217;s how you structure the prompt for subagent-based testing. The following example is for the Angular app and GitHub Copilot:</p><pre><code><code>I have an Angular app located in the `tic-tac-toe-ng` directory 
that&#8217;s running on http://127.0.0.1:4200/.

Please use the Chrome DevTools MCP server to execute the test 
scenarios defined in `e2e/core-gameplay.feature`.

For each scenario in the feature file:
1. Use `runSubagent` to execute the test steps for that specific scenario
2. Each subagent should test the scenario independently and return 
   a detailed text result indicating whether it passed or failed, including:
   - Pass/fail status for each Gherkin step in the scenario
   - Any relevant details, observations, or error messages
   - Verification data (e.g., DOM state, element content, computed styles)

After all subagents complete:
3. Aggregate all the scenario results and generate a comprehensive test report
4. Save the final report to `e2e/core-gameplay.report-ng-subagents.md`

The report should include the pass/fail status and details for 
each scenario returned by the subagents.</code></code></pre><p>This subagent approach becomes essential as your test suite grows beyond a dozen scenarios or when individual scenarios involve complex multi-step interactions.</p><h3>How AI Interprets Gherkin Steps</h3><p>The AI reads the Gherkin scenarios and translates them into Chrome DevTools MCP tool calls. Let&#8217;s look at a concrete example from the actual test execution:</p><h4>Example: Preventing Cell Overwrites</h4><p><strong>Gherkin:</strong></p><pre><code><code>Given Player X clicks on cell 4
When Player O attempts to click on cell 4
Then cell 4 should still display &#8220;X&#8221;
And it should still be Player O&#8217;s turn</code></code></pre><p><strong>AI&#8217;s internal process:</strong></p><pre><code><code>1. navigate_page to http://127.0.0.1:4305/
2. take_snapshot &#8594; Find cell 4 (uid: &#8220;48_8&#8221;)
3. click uid &#8220;48_8&#8221;
4. take_snapshot &#8594; Verify cell 4 now shows &#8220;Cell with X&#8221; (disabled)
5. click uid &#8220;49_8&#8221; &#8594; Attempt to click again
   Result: Click timed out (element disabled, click prevented)
6. take_snapshot &#8594; Verify state unchanged:
   - Cell 4: Still &#8220;Cell with X&#8221;
   - Status: Still &#8220;Current player: O&#8221;
   - Move counter: Still &#8220;Moves: 1&#8221;
</code></code></pre><p><strong>The insight:</strong> The click timeout actually confirms the test passed! The cell is disabled, so the click cannot complete, which is exactly the expected behavior.</p><h3>The Power of Accessibility-Driven Testing</h3><p>Notice how every test relies on the <strong>accessibility tree</strong> rather than implementation details. This approach provides remarkable stability since accessibility properties remain unchanged even when you refactor CSS or restructure DOM elements. Tests verify what users actually experience rather than just confirming that certain classes are applied. When your accessibility tree is well-structured, you&#8217;re simultaneously ensuring your app is accessible to screen reader users. The snapshots themselves are immediately understandable, showing &#8220;Button&#8221;, &#8220;Empty cell&#8221;, &#8220;Cell with X&#8221; instead of cryptic selectors or implementation details that make sense to humans.</p><h2>Step 4: The Test Report: Automated Documentation</h2><p>After executing all tests, the AI generates a comprehensive test report in Markdown format. Let&#8217;s examine the structure and value of this automatically generated documentation. You can view the <a href="https://github.com/gergelyszerovay/articles/blob/e2e-testing-with-chrome-devtools-mcp/e2e/core-gameplay.report.md">complete test report in the repository</a>.</p><p>The report opens with a summary table, this immediately tells you the health of your application at a glance.</p><p>For each Gherkin step, the report includes:</p><ol><li><p><strong>Pass/Fail status</strong> (&#9989; or &#10060;)</p></li><li><p><strong>Detailed result description</strong> explaining what was observed</p></li><li><p><strong>Element UIDs</strong> referenced during the test</p></li><li><p><strong>Evidence from Chrome DevTools interactions</strong></p></li></ol><h2>Limitations and Considerations</h2><p>While this AI-assisted testing approach offers significant advantages, let&#8217;s be realistic about where it fits in your testing strategy.</p><h4>Standardization for Production Use</h4><p>For real-world usage in production environments, you&#8217;ll want to establish <strong>consistency across your test artifacts</strong>. This means defining report templates that standardize how test results are formatted and presented, making it easier to compare results across different test runs and share reports with stakeholders. You&#8217;ll also want to establish a <strong>coding standard for Gherkin files</strong> that defines naming conventions, step patterns, and structural guidelines, ensuring that AI-generated scenarios follow your team&#8217;s conventions and integrate smoothly with your existing test documentation.</p><p>These standards become especially important when multiple team members are generating tests or when you&#8217;re maintaining a large test suite over time. Consider creating example templates and style guides that you can reference in your prompts to the AI.</p><h4>Requirements Are Your Foundation</h4><p>The quality of your tests directly reflects the quality of your requirements. <strong>Vague or incomplete PRDs lead to vague or incomplete tests</strong>, there&#8217;s no magic here. This approach shines when your requirements are specific, testable, and well-structured. The good news? The discipline of writing better requirements benefits your entire project, not just testing.</p><h4>Complex Interactions May Need Detailed Steps</h4><p>Simple interactions like clicking buttons and filling forms work automatically. But complex scenarios, think multi-step wizards, drag-and-drop operations, or dynamic content loading, often need more granular Gherkin steps. Instead of &#8220;When the user completes the checkout flow,&#8221; you&#8217;ll want to break it down: &#8220;When the user adds items to cart, And navigates to checkout, And fills in shipping address...&#8221; The AI can handle complexity, but it needs clear, step-by-step instructions.</p><h4>Human Review Still Matters</h4><p>AI can generate tests from your documented requirements, but it won&#8217;t catch missing requirements, undocumented edge cases, or critical integration points you forgot to mention. A <strong>human reviewer should verify test coverage</strong>, ensure both happy paths and error cases are tested, and confirm that scenarios match real-world usage patterns.</p><h4>Complement, Don&#8217;t Replace</h4><p>E2E tests through Chrome DevTools MCP excel at verifying user-facing behaviors in the actual browser. They complement but <strong>don&#8217;t replace unit tests</strong> for isolated functions, integration tests for component interactions, or API tests for backend functionality. Think of this as the top layer of your testing pyramid or <a href="https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications">thropy</a>, you still need the foundation underneath.</p><h2>About the Author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>, and I&#8217;ve worked as a data scientist and full-stack developer for many years. Currently, I work as a frontend tech lead focusing on Angular-based development. As part of my role, I&#8217;m constantly following how Angular and the frontend development scene in general is evolving.</p><p>To share my knowledge, I started the <a href="https://angularaddicts.com/">Angular Addicts</a> monthly newsletter and publication in 2022, sending subscribers the best resources each month. Whether you&#8217;re a seasoned Angular developer or just getting started, I&#8217;ve got you covered.</p><p>In the past year, with the rise of generative AI, our software development workflows have evolved rapidly. To closely follow this evolution, I decided to start building AI tools in public and publish my progress on <a href="https://aiboosted.dev">AIBoosted.dev</a>.</p><p><strong>Join me on this learning journey:</strong></p><ul><li><p>&#128293; <a href="https://www.angularaddicts.com/">Subscribe to Angular Addicts</a></p></li><li><p>&#128640; <a href="https://aiboosted.dev">Subscribe to AIBoosted.dev</a></p></li></ul><p><strong>Connect with me:</strong></p><ul><li><p><a href="https://medium.com/@GergelySzerovay">Medium</a></p></li><li><p><a href="https://dev.to/gergelyszerovay">Dev.to</a></p></li><li><p><a href="https://twitter.com/GergelySzerovay">Twitter</a></p></li><li><p><a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a></p></li></ul><p>Learn more about Angular, TypeScript, React, and how to build AI-assisted development workflows that make you more productive!</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Chrome DevTools MCP Server Guide]]></title><description><![CDATA[Give Your AI Assistant Eyes and Hands: Automate Browser Testing, Debugging, and Performance Analysis]]></description><link>https://www.aiboosted.dev/p/chrome-devtools-mcp-server-guide</link><guid isPermaLink="false">https://www.aiboosted.dev/p/chrome-devtools-mcp-server-guide</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Tue, 21 Oct 2025 07:44:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!p70d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!p70d!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!p70d!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!p70d!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!p70d!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!p70d!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!p70d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png" width="1456" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:850132,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/176656695?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!p70d!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!p70d!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!p70d!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!p70d!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F102f274f-b7e6-452c-b06e-0ef77d6b3d96_1620x890.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Ever wished your AI assistant could actually see and interact with a browser like you do?</strong> That&#8217;s exactly what the <strong>Chrome DevTools MCP server</strong> delivers: a direct connection between your AI coding assistant and Chrome&#8217;s powerful developer tools, enabling everything <strong>from automated testing to debugging and performance analysis.</strong></p><p>Instead of asking your AI to generate Puppeteer scripts or guess at how your web app behaves, it can now control a real Chrome browser, inspect network requests, capture screenshots, and even record performance traces. The Model Context Protocol (MCP) creates this bridge, giving your AI assistant the same view into your web application that you&#8217;d get from Chrome DevTools.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>But here&#8217;s where it gets really interesting. Need to debug why a form submission isn&#8217;t working? Your AI can take a snapshot of the page, examine the form elements, fill them out, and capture any console errors, all while explaining what&#8217;s happening. Want to understand why your page is loading slowly? It can record a performance trace and break down the Core Web Vitals for you with actionable insights.</p><div><hr></div><blockquote><p>This article is also available on <a href="https://dev.to/this-is-angular/chrome-devtools-mcp-server-guide-24fa">dev.to with better source code syntax highlighting.</a></p></blockquote><h2>Common Workflows</h2><p><strong>Before we dive into the individual tools,</strong> let me show you how they work together to solve real problems you face every day. These workflows demonstrate the power of combining different tools to automate complex testing and debugging scenarios.</p><h3>End-to-End Testing</h3><p>Testing a complete user flow from start to finish:</p><p>&#8220;Test the checkout process with these steps: add a product to cart, proceed to checkout, fill in shipping details, and verify the order confirmation appears.&#8221;</p><p>Your AI will:</p><ol><li><p>Take a snapshot to find the &#8220;Add to Cart&#8221; button (<code>take_snapshot</code>)</p></li><li><p>Click it and wait for the cart to update (<code>click</code>)</p></li><li><p>Navigate to checkout (<code>click</code>)</p></li><li><p>Take another snapshot to identify form fields (<code>take_snapshot</code>)</p></li><li><p>Fill in shipping details using realistic test data (<code>fill_form</code>)</p></li><li><p>Submit the form (<code>click</code>)</p></li><li><p>Wait for the confirmation message (<code>wait_for</code>)</p></li><li><p>Take a screenshot of the confirmation for your records (<code>take_screenshot</code>)</p></li></ol><p>If anything fails, it&#8217;ll capture console errors and network requests to help debug (<code>list_console_messages</code>, <code>list_network_requests</code>).</p><h3>Performance Debugging</h3><p>Understanding why a page loads slowly:</p><p>&#8220;Analyze the performance of example.com and tell me what&#8217;s slowing it down.&#8221;</p><p>Your AI will:</p><ol><li><p>Navigate to the URL (<code>navigate_page</code>)</p></li><li><p>Start a performance trace with page reload (<code>performance_start_trace</code>)</p></li><li><p>Wait for the trace to complete (<code>performance_stop_trace</code>)</p></li><li><p>Analyze Core Web Vitals (LCP, FID, CLS)</p></li><li><p>Examine specific insights like ImageDelivery breakdown (<code>performance_analyze_insight</code>)</p></li><li><p>Identify the largest contributors (render-blocking resources, slow server response, large images)</p></li><li><p>Suggest concrete optimizations with priority order</p></li></ol><h3>Responsive Design Testing</h3><p>Verifying layouts work across different screen sizes:</p><p>&#8220;Test how the navigation menu behaves on mobile, tablet, and desktop.&#8221;</p><p>Your AI will:</p><ol><li><p>Start at desktop size (1920x1080) (<code>resize_page</code>)</p></li><li><p>Take a snapshot and screenshot of the navigation (<code>take_snapshot</code>, <code>take_screenshot</code>)</p></li><li><p>Resize to tablet (768x1024) (<code>resize_page</code>)</p></li><li><p>Check if the menu collapses to a hamburger icon (<code>take_snapshot</code>)</p></li><li><p>Test that the hamburger menu opens correctly (<code>click</code>)</p></li><li><p>Resize to mobile (375x667) (<code>resize_page</code>)</p></li><li><p>Verify mobile navigation works (<code>take_snapshot</code>, <code>click</code>)</p></li><li><p>Report any layout issues or broken interactions</p></li></ol><h3>API Testing Through UI</h3><p>Debugging network requests triggered by user interactions:</p><p>&#8220;Submit the search form and show me what API calls are made.&#8221;</p><p>Your AI will:</p><ol><li><p>List network requests to establish a baseline (<code>list_network_requests</code>)</p></li><li><p>Take a snapshot to find the search input and button (<code>take_snapshot</code>)</p></li><li><p>Fill in the search term (<code>fill</code>)</p></li><li><p>Click the search button (<code>click</code>)</p></li><li><p>Wait for results to appear (<code>wait_for</code>)</p></li><li><p>List new network requests since the click (<code>list_network_requests</code>)</p></li><li><p>Show you the request details (URL, headers, payload, response) (<code>get_network_request</code>)</p></li></ol><p>This is incredibly useful for debugging API integration issues without opening DevTools manually.</p><h2>Available Tools</h2><p>Now that you&#8217;ve seen what&#8217;s possible, let&#8217;s explore the individual tools. The Chrome DevTools MCP server organizes its 26 tools into six categories, each targeting a specific aspect of browser automation and debugging.</p><h3>Input Automation (7 tools)</h3><p>Tools for interacting with page elements:</p><ul><li><p><code>click</code> - Click or double-click elements on the page</p></li><li><p><code>drag</code> - Drag one element onto another</p></li><li><p><code>fill</code> - Type text into inputs or select options from dropdowns</p></li><li><p><code>fill_form</code> - Fill out multiple form fields at once</p></li><li><p><code>handle_dialog</code> - Accept or dismiss browser dialogs (alerts, confirms, prompts)</p></li><li><p><code>hover</code> - Hover over elements to trigger hover states</p></li><li><p><code>upload_file</code> - Upload files through file input elements</p></li></ul><h3>Navigation Automation (7 tools)</h3><p>Tools for controlling browser tabs and navigation:</p><ul><li><p><code>close_page</code> - Close browser tabs by index</p></li><li><p><code>list_pages</code> - Get a list of all open tabs</p></li><li><p><code>navigate_page</code> - Navigate to a specific URL</p></li><li><p><code>navigate_page_history</code> - Go back or forward in browser history</p></li><li><p><code>new_page</code> - Open a new tab with a URL</p></li><li><p><code>select_page</code> - Switch context to a different tab</p></li><li><p><code>wait_for</code> - Wait for specific text to appear on the page</p></li></ul><h3>Emulation (3 tools)</h3><p>Tools for simulating different device and network conditions:</p><ul><li><p><code>emulate_cpu</code> - Throttle CPU to simulate slower devices (1-20x slowdown)</p></li><li><p><code>emulate_network</code> - Simulate network conditions (Offline, Slow 3G, Fast 3G, Slow 4G, Fast 4G)</p></li><li><p><code>resize_page</code> - Change viewport dimensions to test responsive designs</p></li></ul><h3>Performance (3 tools)</h3><p>Tools for analyzing page performance:</p><ul><li><p><code>performance_start_trace</code> - Begin recording a performance trace</p></li><li><p><code>performance_stop_trace</code> - Stop the active trace recording</p></li><li><p><code>performance_analyze_insight</code> - Get detailed information about specific performance insights (like LCP breakdown or document latency)</p></li></ul><h3>Network (2 tools)</h3><p>Tools for inspecting network activity:</p><ul><li><p><code>list_network_requests</code> - Get all network requests since the last navigation, with filtering options</p></li><li><p><code>get_network_request</code> - Retrieve detailed information about a specific request by its ID</p></li></ul><h3>Debugging (4 tools)</h3><p>Tools for inspecting and debugging pages:</p><ul><li><p><code>evaluate_script</code> - Execute JavaScript code in the page context and get JSON-serializable results</p></li><li><p><code>list_console_messages</code> - Retrieve all console logs, warnings, and errors</p></li><li><p><code>take_screenshot</code> - Capture screenshots of the full page or specific elements (PNG, JPEG, or WebP)</p></li><li><p><code>take_snapshot</code> - Get a text representation of the page based on the accessibility tree with unique element identifiers</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Configuration</h2><p>Let&#8217;s get you up and running! The Chrome DevTools MCP server is pretty flexible, so you can configure it to match your workflow. I&#8217;ll cover the essentials here, but if you want to dive deeper into all the configuration options, check out the <a href="https://github.com/ChromeDevTools/chrome-devtools-mcp/tree/main?tab=readme-ov-file#configuration">official configuration documentation</a>.</p><h3>Basic Setup</h3><p>The simplest way to get started is to let the server launch a fresh Chrome instance with default settings:</p><pre><code><code>{
  // or &#8220;mcpServers&#8221;, depending your IDE / tool
  &#8220;servers&#8221;: {
    &#8220;chrome-devtools&#8221;: {
      &#8220;command&#8221;: &#8220;npx&#8221;,
      &#8220;args&#8221;: [&#8221;-y&#8221;, &#8220;chrome-devtools-mcp@latest&#8221;]
    }
  }
}</code></code></pre><p>That&#8217;s it! This configuration will work great for most use cases.</p><h3>Connecting to a Running Chrome Instance from Docker</h3><p>Here&#8217;s a scenario I&#8217;ve run into a lot: you&#8217;re developing in a Docker container but want to use a Chrome instance that&#8217;s running on your host machine. Maybe you want to see the browser window, or you need to use your existing Chrome profile. Whatever the reason, connecting to a running instance is the way to go.</p><p>First, start Chrome on your host machine with remote debugging enabled. Chrome needs a user data directory when remote debugging is on, so make sure to specify one:</p><pre><code><code>&#8220;C:\Program Files\Google\Chrome\Application\chrome.exe&#8221; ^
  --remote-debugging-port=9222 ^
  --user-data-dir=&#8221;C:\Temp\chrome-profile-stable&#8221;</code></code></pre><p>Now for the Docker-specific part. Docker provides a special DNS name called <code>host.docker.internal</code> that resolves to your host machine. From inside your container, you can find the IP address:</p><pre><code><code>host host.docker.internal
# Output: host.docker.internal has address 192.168.65.254
</code></code></pre><p>Use that IP address in your MCP server configuration:</p><pre><code><code>{
  // or &#8220;mcpServers&#8221;, depending your IDE / tool
  &#8220;servers&#8221;: {
    &#8220;chrome-devtools&#8221;: {
      &#8220;command&#8221;: &#8220;npx&#8221;,
      &#8220;args&#8221;: [
        &#8220;-y&#8221;,
        &#8220;chrome-devtools-mcp@latest&#8221;,
        &#8220;--browserUrl=http://192.168.65.254:9222&#8221;
      ]
    }
  }
}</code></code></pre><p>You might be wondering: &#8220;Why can&#8217;t I just use <code>host.docker.internal</code> directly in the <code>browserUrl</code>?&#8221; If you&#8217;re curious about the details, check out <a href="https://github.com/puppeteer/puppeteer/issues/2242#issuecomment-544219536">this discussion in the Puppeteer GitHub issues</a>.</p><p>For more configuration options like headless mode, custom Chrome paths, network throttling, and debugging settings, take a look at the <a href="https://github.com/ChromeDevTools/chrome-devtools-mcp/tree/main?tab=readme-ov-file#configuration">full configuration guide</a>.</p><h2>Tool Reference</h2><h3>Input Automation Tools</h3><h3><code>click</code></h3><p>Clicks or double-clicks an element identified by its unique ID from a page snapshot.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  uid: string,        // Required: The uid of an element on the page from the page content snapshot
  dblClick?: boolean  // Optional: Set to true for double clicks. Default is false.
}</code></code></pre><p><strong>In Action:</strong> Let&#8217;s say you want to test a login form. First, take a snapshot to get element IDs, then use click to submit the form. Your AI assistant can chain these actions naturally when you ask &#8220;Can you test the login flow?&#8221; It&#8217;ll find the form, fill it out, click submit, and report what happened.</p><h3><code>drag</code></h3><p>Drags one element onto another, useful for testing drag-and-drop interfaces.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  from_uid: string,  // Required: The uid of the element to drag
  to_uid: string     // Required: The uid of the element to drop into
}</code></code></pre><p><strong>In Action:</strong> Testing a kanban board? Ask your AI to &#8220;move the task card from &#8216;In Progress&#8217; to &#8216;Done&#8217;&#8221; and it&#8217;ll locate both elements from a snapshot and perform the drag operation. It can even verify the move succeeded by checking the DOM afterward.</p><h3><code>fill</code></h3><p>Types text into input fields, text areas, or selects an option from a dropdown.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  uid: string,    // Required: The uid of an element on the page from the page content snapshot
  value: string   // Required: The value to fill in
}</code></code></pre><p><strong>In Action:</strong> When you ask &#8220;Fill out the contact form with test data,&#8221; your AI can take a snapshot, identify all the form fields, then use fill on each one. It&#8217;s smart enough to generate appropriate test data based on field names and types.</p><h3><code>fill_form</code></h3><p>Fills multiple form fields in a single operation, more efficient than calling fill repeatedly.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  elements: Array&lt;{
    uid: string; // The uid of the element to fill out
    value: string; // Value for the element
  }&gt;;
}</code></code></pre><p><strong>In Action:</strong> This is perfect for complex forms. Instead of watching your AI fill fields one by one, it batches everything together. Ask &#8220;Complete the registration form&#8221; and it&#8217;ll snapshot the page, identify all required fields, then fill them all at once.</p><h3><code>handle_dialog</code></h3><p>Responds to JavaScript alerts, confirms, or prompts.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  action: &#8216;accept&#8217; | &#8216;dismiss&#8217;,  // Required: Whether to dismiss or accept the dialog
  promptText?: string            // Optional: Optional prompt text to enter into the dialog.
}</code></code></pre><p><strong>In Action:</strong> Testing a &#8220;delete item&#8221; flow that shows a confirmation dialog? Your AI will click the delete button, detect the dialog, then use <code>handle_dialog</code> to confirm or cancel. You can specify which action you want: &#8220;Test the delete flow but cancel the confirmation.&#8221;</p><h3><code>hover</code></h3><p>Triggers hover states on elements, essential for testing dropdown menus and tooltips.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  uid: string; // Required: The uid of an element on the page from the page content snapshot
}</code></code></pre><p><strong>In Action:</strong> Want to test if your navigation dropdown works? Ask &#8220;Check if the Products menu shows all categories&#8221; and your AI will hover over the menu, wait for it to expand, then snapshot and verify the contents.</p><h3><code>upload_file</code></h3><p>Uploads a file through a file input element.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  uid: string,       // Required: The uid of the file input element or an element that will open file chooser on the page from the page content snapshot
  filePath: string   // Required: The local path of the file to upload
}</code></code></pre><p><strong>In Action:</strong> Need to test an image upload feature? Tell your AI &#8220;Upload test-image.jpg to the profile picture form&#8221; and it&#8217;ll locate the file input, handle the upload, and can even wait to verify the upload succeeded.</p><h3>Navigation Automation Tools</h3><h3><code>close_page</code></h3><p>Closes a browser tab by its index. You can&#8217;t close the last remaining tab.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  pageIdx: number; // Required: The index of the page to close. Call list_pages to list pages.
}</code></code></pre><p><strong>In Action:</strong> After opening multiple tabs for testing, clean them up: &#8220;Close all the test tabs we opened.&#8221; Your AI will list the pages, then close the ones that aren&#8217;t needed, keeping your main tab active.</p><h3><code>list_pages</code></h3><p>Returns all open browser tabs with their URLs and indices.</p><p><strong>Input Schema:</strong> None</p><p><strong>In Action:</strong> This runs automatically whenever your AI needs to know what tabs are open. When you ask &#8220;What pages do I have open?&#8221; or &#8220;Switch to the documentation tab&#8221; it uses this tool behind the scenes.</p><h3><code>navigate_page</code></h3><p>Navigates the current tab to a URL.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  url: string,        // Required: URL to navigate the page to
  timeout?: number    // Optional: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
}</code></code></pre><p><strong>In Action:</strong> The most straightforward tool. &#8220;Go to https://example.com&#8220; triggers this directly. Your AI can also chain it with other actions: &#8220;Navigate to the login page and check if the form is accessible.&#8221;</p><h3><code>navigate_page_history</code></h3><p>Moves backward or forward through the browser history.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  navigate: &#8216;back&#8217; | &#8216;forward&#8217;,  // Required: Whether to navigate back or navigate forward in the selected pages history
  timeout?: number                // Optional: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
}</code></code></pre><p><strong>In Action:</strong> Testing multi-step flows? &#8220;Complete the checkout process, then go back and verify the cart is still correct.&#8221; Your AI navigates forward through each step, then uses history navigation to go back and verify state.</p><h3><code>new_page</code></h3><p>Opens a new tab and navigates to a URL.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  url: string,        // Required: URL to load in a new page.
  timeout?: number    // Optional: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
}</code></code></pre><p><strong>In Action:</strong> Comparing pages side-by-side? &#8220;Open the production and staging sites in separate tabs and compare the homepage layouts.&#8221; Your AI opens both pages and can switch between them to analyze differences.</p><h3><code>select_page</code></h3><p>Changes which tab is active for subsequent tool operations.</p><p><strong>Input Schema:</strong><br></p><pre><code><code>{
  pageIdx: number; // Required: The index of the page to select. Call list_pages to list pages.
}
</code></code></pre><p><strong>In Action:</strong> When working with multiple tabs, this switches context. Ask &#8220;Check the console errors in the second tab&#8221; and your AI uses list_pages to find it, select_page to switch to it, then list_console_messages to check for errors.</p><h3><code>wait_for</code></h3><p>Waits for specific text to appear on the page, useful after triggering actions that update the UI.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  text: string,       // Required: Text to appear on the page
  timeout?: number    // Optional: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
}</code></code></pre><p><strong>In Action:</strong> After submitting a form, you want to verify success: &#8220;Submit the form and check for the success message.&#8221; Your AI clicks submit, then uses wait_for to ensure &#8220;Thank you for your submission&#8221; appears before continuing.</p><h3>Emulation Tools</h3><h3><code>emulate_cpu</code></h3><p>Throttles CPU performance to simulate slower devices.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  throttlingRate: number; // Required: The CPU throttling rate representing the slowdown factor 1-20x. Set the rate to 1 to disable throttling
}</code></code></pre><p><strong>In Action:</strong> Want to see how your app performs on older devices? &#8220;Test the page performance on a slow device&#8221; triggers CPU throttling, helping identify performance bottlenecks that only appear on less powerful hardware.</p><h3><code>emulate_network</code></h3><p>Simulates various network conditions.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  throttlingOption: &#8220;No emulation&#8221; |
    &#8220;Offline&#8221; |
    &#8220;Slow 3G&#8221; |
    &#8220;Fast 3G&#8221; |
    &#8220;Slow 4G&#8221; |
    &#8220;Fast 4G&#8221;; // Required: The network throttling option to emulate. Available throttling options are: No emulation, Offline, Slow 3G, Fast 3G, Slow 4G, Fast 4G. Set to &#8220;No emulation&#8221; to disable. Set to &#8220;Offline&#8221; to simulate offline network conditions.
}</code></code></pre><p><strong>In Action:</strong> Testing offline functionality? &#8220;Check if the app works without internet&#8221; sets the network to offline mode, then you can ask your AI to verify that your service worker and caching work correctly. You can also test on slow connections: &#8220;How does the page load on 3G?&#8221;</p><h3><code>resize_page</code></h3><p>Changes the viewport dimensions to test responsive designs.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  width: number,   // Required: Page width
  height: number   // Required: Page height
}</code></code></pre><p><strong>In Action:</strong> Checking mobile responsiveness? &#8220;Test the layout on iPhone 12 dimensions&#8221; resizes to 390x844, takes screenshots, and you can explicitly ask it to check that the mobile menu works correctly. Your AI can test multiple sizes in sequence to catch responsive design issues.</p><h3>Performance Tools</h3><h3><code>performance_start_trace</code></h3><p>Begins recording a performance trace to analyze page load and runtime performance.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  reload: boolean,    // Required: Determines if, once tracing has started, the page should be automatically reloaded.
  autoStop: boolean   // Required: Determines if the trace recording should be automatically stopped.
}</code></code></pre><p><strong>In Action:</strong> &#8220;Analyze the performance of this page&#8221; starts a trace with reload enabled and autoStop true. After recording, your AI gets Core Web Vitals (LCP, FID, CLS) and performance insights automatically.</p><h3><code>performance_stop_trace</code></h3><p>Stops the active performance trace and returns the analysis.</p><p><strong>Input Schema:</strong> None</p><p><strong>In Action:</strong> If you started a trace with autoStop disabled (to capture user interactions), you&#8217;d stop it manually: &#8220;Stop the performance recording and show me the results.&#8221; The trace includes detailed timing information and performance insights.</p><h3><code>performance_analyze_insight</code></h3><p>Gets detailed information about specific performance issues identified in a trace.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  insightName: string; // Required: The name of the Insight you want more information on. For example: &#8220;DocumentLatency&#8221; or &#8220;LCPBreakdown&#8221;
}</code></code></pre><p><strong>Here&#8217;s the list of all 18 Chrome DevTools Performance Insights and their names:</strong></p><p>Each of these insights focuses on a specific aspect of performance that can help you make your pages faster. When you run a performance trace, Chrome analyzes your page and highlights which of these insights are relevant to your situation:</p><ul><li><p><code>Cache</code>: Use efficient cache lifetimes</p></li><li><p><code>CLSCulprits</code>: Layout shift culprits</p></li><li><p><code>DocumentLatency</code>: Document request latency</p></li><li><p><code>DOMSize</code>: Optimize DOM size</p></li><li><p><code>DuplicatedJavaScript</code>: Duplicated JavaScript</p></li><li><p><code>FontDisplay</code>: Font display</p></li><li><p><code>ForcedReflow</code>: Forced reflow</p></li><li><p><code>ImageDelivery</code>: Improve image delivery</p></li><li><p><code>INPBreakdown</code>: INP breakdown</p></li><li><p><code>LCPBreakdown</code>: LCP breakdown</p></li><li><p><code>LCPDiscovery</code>: LCP request discovery</p></li><li><p><code>LegacyJavaScript</code>: Legacy JavaScript</p></li><li><p><code>ModernHTTP</code>: Modern HTTP</p></li><li><p><code>NetworkDependencyTree</code>: Network dependency tree</p></li><li><p><code>RenderBlocking</code>: Render-blocking requests</p></li><li><p><code>SlowCSSSelector</code>: CSS selector costs</p></li><li><p><code>ThirdParties</code>: Third parties</p></li><li><p><code>Viewport</code>: Optimize viewport for mobile</p></li></ul><p>Want to understand how Chrome generates these insights and what each one means for your page? The <a href="https://developer.chrome.com/docs/performance/insights">Performance Insights documentation</a> has all the details.</p><p><strong>In Action:</strong> After running a trace that shows &#8220;LCP could be improved,&#8221; ask &#8220;What&#8217;s causing the slow LCP?&#8221; Your AI uses this tool to get a breakdown of what contributed to the Largest Contentful Paint time and suggests specific optimizations.</p><h3>Network Tools</h3><h3><code>list_network_requests</code></h3><p>Retrieves all network requests since the page loaded.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  pageSize?: number,          // Optional: Maximum number of requests to return. When omitted, returns all requests.
  pageIdx?: number,           // Optional: Page number to return (0-based). When omitted, returns the first page.
  resourceTypes?: string[]    // Optional: Filter requests to only return requests of the specified resource types. When omitted or empty, returns all requests.
}</code></code></pre><p><strong>In Action:</strong> Debugging API calls? &#8220;Show me all the failed network requests&#8221; lists requests, filters to status codes 4xx and 5xx, and shows you what went wrong. You can also check &#8220;Are there any requests taking longer than 2 seconds?&#8221;</p><h3><code>get_network_request</code></h3><p>Gets a network request by URL. You can get all requests by calling list_network_requests.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  reqid: number; // Required: The reqid of a request on the page from the listed network requests
}</code></code></pre><p><strong>In Action:</strong> After listing requests, dive into details: &#8220;What headers were sent with that POST request?&#8221; Your AI uses the request ID to fetch complete information including headers, response body, timing breakdown, and status.</p><h3>Debugging Tools</h3><h3><code>evaluate_script</code></h3><p>Executes JavaScript code in the page context and returns JSON-serializable results.</p><p><strong>Input Schema:</strong><br></p><pre><code><code>{
  function: string,  // Required: A JavaScript function to run in the currently selected page
  args?: Array&lt;{     // Optional: An optional list of arguments to pass to the function.
    uid: string      // The uid of an element on the page from the page content snapshot
  }&gt;
}</code></code></pre><p><strong>In Action:</strong> Need to inspect page state? &#8220;What&#8217;s the current value of the search input?&#8221; triggers evaluation of <code>document.querySelector(&#8217;input[name=&#8221;search&#8221;]&#8217;).value</code>. You can also modify state, check global variables, or debug complex interactions.</p><h3><code>list_console_messages</code></h3><p>Retrieves console logs, warnings, and errors from the page.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  pageSize?: number,   // Optional: Maximum number of messages to return. When omitted, returns all requests.
  pageIdx?: number,    // Optional: Page number to return (0-based). When omitted, returns the first page.
  types?: string[]     // Optional: Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages.
}</code></code></pre><p><strong>In Action:</strong> After page interactions, check what happened: &#8220;Did any errors occur?&#8221; Your AI lists console messages, filters to errors, and explains what each one means. Great for catching JavaScript errors during testing.</p><h3><code>take_screenshot</code></h3><p>Captures visual snapshots of the page or specific elements.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  format?: &#8216;png&#8217; | &#8216;jpeg&#8217; | &#8216;webp&#8217;,  // Optional: Type of format to save the screenshot as. Default is &#8220;png&#8221;
  quality?: number,      // Optional: Compression quality for JPEG and WebP formats (0-100). Higher values mean better quality but larger file sizes. Ignored for PNG format.
  uid?: string,          // Optional: The uid of an element on the page from the page content snapshot. If omitted takes a pages screenshot.
  fullPage?: boolean,    // Optional: If set to true takes a screenshot of the full page instead of the currently visible viewport. Incompatible with uid.
  filePath?: string      // Optional: The absolute path, or a path relative to the current working directory, to save the screenshot to instead of attaching it to the response.
}</code></code></pre><p><strong>In Action:</strong> Visual regression testing made easy: &#8220;Take a screenshot of the homepage and compare it to yesterday&#8217;s version.&#8221; Your AI captures the image, compares layouts, and highlights any differences. You can also screenshot specific components: &#8220;Show me what the modal looks like when open.&#8221;</p><h3><code>take_snapshot</code></h3><p>Generates a text representation of the page based on the accessibility tree, with unique IDs for each element. Think of this as getting a bird&#8217;s eye view of your page&#8217;s structure: it shows you all the interactive elements, their roles, and how they&#8217;re organized, similar to how a screen reader would understand the page.</p><p>The accessibility tree is a simplified version of the DOM that exposes only the information needed for assistive technologies. By using this tree, the snapshot focuses on semantically meaningful elements while filtering out purely visual markup. This makes it perfect for understanding page structure and finding elements to interact with.</p><p>For a deeper dive into how Chrome&#8217;s accessibility tree works and what information it provides, check out the <a href="https://developer.chrome.com/blog/full-accessibility-tree">Full Accessibility Tree in Chrome DevTools</a> blog post.</p><p><strong>Input Schema:</strong></p><pre><code><code>{
  verbose?: boolean  // Optional: Whether to include all possible information available in the full a11y tree. Default is false.
}
</code></code></pre><p><strong>In Action:</strong> This is your AI&#8217;s eyes into the page structure. When you ask &#8220;What form fields are on this page?&#8221; it takes a snapshot to see all interactive elements. The unique IDs from this snapshot are what other tools use to interact with specific elements.</p><h2>&#128104;&#8205;&#128187;About the author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>, I worked as a data scientist and full-stack developer for many years, and I have been working as frontend tech lead, focusing on Angular based frontend development. As part of my role, I&#8217;m constantly following how Angular and the frontend development scene in general is evolving. To share my knowledge, I started the <a href="https://angularaddicts.com/">Angular Addicts</a> monthly newsletter and publication in 2022, so that I can send you the best resources I come across each month. Whether you are a seasoned Angular Addict or a beginner, I got you covered. Let me know if you would like to be included as a writer. Let&#8217;s learn Angular together! <a href="https://www.angularaddicts.com/">Subscribe here</a> &#128293;</p><p>Angular has evolved very rapidly over the past few years, and in the past year, with the rise of generative AI, our software development workflows have also evolved rapidly. In order to closely follow the evolution of AI-assisted software development, I decided to start building AI tools in public, and publish my progress on <a href="https://aiboosted.dev">AIBoosted.dev</a>. Join my on this learning journey: <a href="https://aiboosted.dev">Subscribe here</a> &#128640;</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">Twitter</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular, and how to build AI apps with AI, Typescript, React and Angular!</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to Create Better Apps with my 7-step Vibe Coding Workflow]]></title><description><![CDATA[Learn how to build better, more maintainable applications with AI]]></description><link>https://www.aiboosted.dev/p/better-apps-with-vibe-coding-7-step-coding-guidelines</link><guid isPermaLink="false">https://www.aiboosted.dev/p/better-apps-with-vibe-coding-7-step-coding-guidelines</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Wed, 07 May 2025 09:30:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!aZZI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aZZI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aZZI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png 424w, https://substackcdn.com/image/fetch/$s_!aZZI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png 848w, https://substackcdn.com/image/fetch/$s_!aZZI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png 1272w, https://substackcdn.com/image/fetch/$s_!aZZI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aZZI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png" width="1456" height="948" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:948,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4703444,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/162985312?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aZZI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png 424w, https://substackcdn.com/image/fetch/$s_!aZZI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png 848w, https://substackcdn.com/image/fetch/$s_!aZZI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png 1272w, https://substackcdn.com/image/fetch/$s_!aZZI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c045029-55d9-49af-b2dd-2c29369ebbc9_9336x6080.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>The main problem with vibe coding</h2><p>The term "vibe coding" was coined <a href="https://x.com/karpathy/status/1886192184808149383?lang=en">by Andrej Karpathy</a> to describe the process of letting AI assistants generate code while you "fully give in to the vibes" - essentially delegating the details to the AI and forgetting that code even exists.</p><p>While this approach delivers <strong>initial velocity</strong>, especially for quick prototypes, it <strong>becomes problematic as the projects grow</strong>. After my initial excitement with vibe coding wore off, I noticed something troubling: my codebase was becoming increasingly <strong>difficult to maintain and understand</strong>.</p><p>If this sounds familiar, you're not alone. Vibe coding delivers remarkable speed, especially when building on a solid boilerplate. But without proper guardrails, that speed comes at a hidden cost to your codebase's long-term health.</p><h2>3 Common Challenges with AI-Assisted Development</h2><p>After spending many hours experimenting with vibe coding myself, I've seen three common problems emerge:</p><ol><li><p><strong>Organizational chaos</strong>: Files end up in unpredictable locations, making the project structure increasingly difficult to navigate.</p></li><li><p><strong>Growing files</strong>: What starts as a reasonably-sized component gradually balloons into a 500+ line monstrosity as the AI keeps adding "just one more function" to existing files.</p></li><li><p><strong>Inconsistency</strong>: Your codebase becomes a patchwork quilt of different styles, patterns, and approaches as the AI draws from various influences or implements similar features differently across your application.</p></li></ol><p>I've learned the hard way: <strong>the key to successful vibe coding isn't just about getting the AI to write code faster - it's about getting it to write structured code following simple, clean guidelines</strong>.</p><p>While this article uses TypeScript and React as primary examples, it's important to note that these principles apply broadly across different programming languages and frameworks. Whether you're building with Angular and Material, or MCP servers using Node.js, the same four pillars of maintainable vibe coding will help you maintain quality and consistency. The specific tools and configurations will differ, but the underlying approach remains effective across technology stacks.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bYbD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bYbD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png 424w, https://substackcdn.com/image/fetch/$s_!bYbD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png 848w, https://substackcdn.com/image/fetch/$s_!bYbD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png 1272w, https://substackcdn.com/image/fetch/$s_!bYbD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bYbD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png" width="238" height="349" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:349,&quot;width&quot;:238,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89976,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/162985312?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bYbD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png 424w, https://substackcdn.com/image/fetch/$s_!bYbD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png 848w, https://substackcdn.com/image/fetch/$s_!bYbD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png 1272w, https://substackcdn.com/image/fetch/$s_!bYbD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15ca189a-33d3-4c51-bac8-cdf982c313c7_238x349.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><a href="https://www.reddit.com/r/ProgrammerHumor/comments/1jfyylv/viberagingnow/">Source: Reddit</a></figcaption></figure></div><h2>The 4 Pillars of Maintainable AI-Assisted Development</h2><p>After some trial and error, I've found that maintainable AI-assisted development rests on four pillars:</p><ol><li><p><strong>Choosing the right tech stack</strong>: Selecting technologies that are well-documented and well-understood by AI tools</p></li><li><p><strong>Coding guidelines</strong>: Clear, explicit rules that both humans and AI can understand and follow</p></li><li><p><strong>Enforcement tools</strong>: Linters, formatters, and validation systems that catch deviations from your guidelines</p></li><li><p><strong>Solid boilerplate</strong>: A minimal but complete starting point with all the necessary development tools pre-configured</p></li></ol><p>Let's dive into each of these areas and see how they work together to keep your vibe coding maintainable over time.</p><h2>1. Choosing the Right Tech Stack</h2><p>The foundation of maintainable vibe coding starts with choosing the right tech stack. Some technologies work notably better with AI assistance than others, usually because they're widely used, well-documented, and the AI has seen many examples of them in its training data.</p><p>For frontend development, I often use TypeScript, React, and Shadcn UI, paired with a Supabase backend. This combination leverages technologies that AI assistants understand well.</p><p>The TypeScript/React/Shadcn/Supabase stack works exceptionally well because:</p><ol><li><p><strong>TypeScript</strong> provides strong type safety that catches many issues that might otherwise slip through in AI-generated code, while ensuring data structures are cleanly defined</p></li><li><p><strong>React</strong> is widely used and most LLMs generate React components at higher quality by default compared to other frameworks</p></li><li><p><strong>Shadcn UI</strong> provides consistent, accessible components that follow predictable patterns</p></li><li><p><strong>Supabase</strong> handles authentication, database, and storage with minimal boilerplate</p></li></ol><p>I've <a href="https://github.com/gergelyszerovay/aibd-template-react-shadcn">shared a boilerplate</a> for projects with this tech stack on my GitHub that includes all the necessary configurations and enforcements to get started with maintainable vibe coding right away.</p><p>When selecting your tech stack for AI-assisted development, consider these factors:</p><ul><li><p><strong>Type safety</strong>: Strongly typed languages help catch errors in AI-generated code and ensure data structures are cleanly defined</p></li><li><p><strong>Documentation quality</strong>: Better documented tech leads to better AI-generated implementation</p></li><li><p><strong>Community size</strong>: More widely used technologies typically have better AI support</p></li><li><p>Technologies with <strong>clear patterns</strong> are easier for AI to follow</p></li></ul><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>2. Coding Guidelines: Teaching AI How You Work</h2><p>Think of coding guidelines as creating a shared language between you and your AI assistant. With clear guidelines, you can simply tell the AI to "follow our project's architectural patterns" instead of explaining your code organization philosophy from scratch every time.</p><p>One important practice I've discovered is to <strong>continuously remind the AI about your coding guidelines</strong> throughout the development session. AI assistants can sometimes drift away from your guidelines as the conversation progresses, especially during complex implementations. Simple reminders like "Remember to follow our AIBD guidelines" can dramatically improve consistency and prevent architectural drift.</p><p>Creating a <strong>unique abbreviation</strong> for your coding guidelines (like AIBD in my case) adds significant value to your workflow. When you use this abbreviation consistently throughout your guidelines and development process, it creates a <strong>clear reference point</strong> that you can use in your prompts. Simply mentioning "follow our AIBD guidelines" instantly communicates to the AI which specific rule set you're referring to.</p><p>I've shared my <a href="https://github.com/gergelyszerovay/aibd-coding-guidelines">AIBD (AI Boosted Development) guidelines for TypeScript-based development on my GitHub</a>. These guidelines have evolved through months of experimentation and have proven effective across multiple vibe coding projects, and I continuously improve and extend them. You're welcome to use them as a starting point and adapt them to your needs.</p><p>Once you've established your guidelines, you need a way to efficiently share them with your AI tools. Different AI-powered development environments offer various approaches:</p><ul><li><p><strong>Cursor</strong> has "Cursor Rules" that provide system-level guidance.</p></li><li><p><strong>Cline</strong> and <strong>Roo Code</strong> offers "Custom instructions" that can be applied to your development sessions.</p></li><li><p>For <strong>chat-based interfaces with filesystem access</strong> like Claude, you can ask the AI to read and follow your guidelines stored in project files.</p></li></ul><p>Regardless of which AI tool you're using, after you've shared your guidelines once, you can simply tell any AI assistant to "follow our AIBD guidelines" without explaining them from scratch each time.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tcea!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tcea!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png 424w, https://substackcdn.com/image/fetch/$s_!tcea!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png 848w, https://substackcdn.com/image/fetch/$s_!tcea!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png 1272w, https://substackcdn.com/image/fetch/$s_!tcea!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tcea!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png" width="1351" height="1041" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1041,&quot;width&quot;:1351,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:101011,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/162985312?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tcea!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png 424w, https://substackcdn.com/image/fetch/$s_!tcea!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png 848w, https://substackcdn.com/image/fetch/$s_!tcea!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png 1272w, https://substackcdn.com/image/fetch/$s_!tcea!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fff9033da-be5e-4b68-a053-ac0bfe9da06a_1351x1041.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Our guidelines should cover several key areas:</p><h3>Architecture</h3><p>A well-defined architecture provides the structural foundation for your codebase. It should clearly specify:</p><ul><li><p>How code is organized (folders, files, modules)</p></li><li><p>Boundaries between different parts of the application</p></li><li><p>Dependency rules (what can import what)</p></li><li><p>Shared code organization</p></li></ul><p>My AIBD (AI Boosted Development) guidelines use features as the building blocks of your app. <strong>A feature is simply a chunk of related functionality packaged together.</strong> Each feature gets its own folder, follows a standard structure, and keeps its internal code separate from what other features can access.</p><p>Instead of organizing by technical layers (models, components, services), we organize by features that map to business capabilities. This approach makes it much easier to describe what you want to the AI and ensures generated code fits naturally into your architecture. Example of a feature directory:</p><pre><code><code>src/
&#9500;&#9472;&#9472; features/            # Feature-based organization
&#9474;   &#9500;&#9472;&#9472; user-management/ # Each feature is self-contained
&#9474;   &#9474;   &#9500;&#9472;&#9472; prd.md       # Feature requirements
&#9474;   &#9474;   &#9500;&#9472;&#9472; UserProfile.ts  # Public exports
&#9474;   &#9474;   &#9500;&#9472;&#9472; UserSettings.ts
&#9474;   &#9474;   &#9492;&#9472;&#9472; internal/    # Private implementation details
&#9474;   &#9474;       &#9500;&#9472;&#9472; Theme.ts
&#9474;   &#9474;       &#9492;&#9472;&#9472; validateEmail.ts
&#9474;   &#9492;&#9472;&#9472; payment-processing/
&#9492;&#9472;&#9472; shared/              # Shared utilities and types
    &#9500;&#9472;&#9472; types/
    &#9500;&#9472;&#9472; utils/
    &#9492;&#9472;&#9472; http/
        &#9492;&#9472;&#9472; internal/    # Private implementation details</code></code></pre><p>Another crucial aspect of my AIBD architecture is using many small files, each containing a single declaration or definition. This approach offers several benefits:</p><ul><li><p><strong>The import graph directly represents connections</strong> between types, functions, and classes, making dependencies visually clear</p></li><li><p><strong>Reduces token usage with AI tools</strong> - the LLM processes multiple small files focused specifically on what it needs, rather than loading huge files with thousands of lines. Tools like Cursor have line number limits (e.g., reading only 200 lines from a file at once). By staying below these limits, the AI agent can be more efficient</p></li><li><p><strong>Improves focus</strong> - both you and the AI can concentrate on one specific piece of functionality at a time, rather than getting lost in a large file</p></li></ul><p>AIBD intentionally <strong>avoids using barrel files</strong> (index.ts exports), as they make it easier to inadvertently introduce circular imports and make it harder for LLMs to track and understand import relationships. Direct imports from specific files create a clearer dependency graph that both humans and AI assistants can follow more easily.</p><h3>Coding Style Guidelines</h3><p>Beyond architecture, you need guidelines for the code itself. Your coding style guidelines should cover how to define and document types, structure function parameters, handle errors consistently, and follow appropriate naming conventions for different kinds of variables. These seemingly small decisions, when applied consistently, create a codebase that feels cohesive rather than cobbled together from different sources.</p><p>For example, instead of using both interfaces and type aliases randomly throughout your codebase, set a clear guideline like:<br></p><pre><code><code>// Don't use interfaces
interface User {
  id: number;
  name: string;
}

// Do use type aliases
type User = {
  id: number;
  name: string;
};</code></code></pre><p>When the AI knows your preference, it can consistently apply it without you having to correct it repeatedly.</p><h3>Tech Stack-Specific Guidelines</h3><p>Different frameworks and libraries have their own best practices. Create specific guidelines for each major technology in your stack.</p><p>For example, my <a href="https://github.com/gergelyszerovay/aibd-coding-guidelines/tree/main/rules">React and Shadcn UI guidelines</a> specify how to properly structure React components, separate smart and UI components, and correctly import Shadcn UI components from the appropriate paths. These guidelines ensure that our components follow consistent patterns, maintain proper separation of concerns, and correctly utilize the UI library's built-in accessibility features.</p><h3>Feature Blueprints</h3><p>Blueprints provide templates for common types of features. They're incredibly valuable for AI assistance because they give the AI a starting point that already follows your patterns.</p><p>A feature blueprint might include:</p><ul><li><p>Required files and their purpose</p></li><li><p>Component structure</p></li><li><p>Testing requirements</p></li><li><p>Documentation format</p></li></ul><h3>Documentation Guidelines</h3><p>Documentation is often overlooked, but it's crucial for long-term maintenance. Guidelines should specify:</p><ul><li><p>What needs documentation (functions, types, components)</p></li><li><p>Documentation format (TSDoc, Markdown)</p></li><li><p>Required sections and information</p></li></ul><p>I use a three-level documentation approach that keeps different concerns properly separated:</p><p>1. <strong>Project-level documentation</strong> - Our guidelines contain high-level project rules, patterns, and architecture decisions. Additional project-level documentation lives in Markdown files at the repository root or in dedicated documentation directories.</p><p>2. <strong>Feature-level documentation</strong> - Each feature includes a <code>prd.md</code> file in its root directory that specifies business requirements, user stories, acceptance criteria, and scope boundaries. This serves as the single source of truth for what the feature should do. We call these "PRD" files (Product Requirements Document), though we're using this term for feature-level specifications rather than for entire product documentation.</p><p>A well-structured PRD file should include:</p><ul><li><p><strong>Title and Feature Name</strong>: Clear identification of the feature</p></li><li><p><strong>Objective</strong>: A concise statement describing the purpose and business value</p></li><li><p><strong>User Stories</strong>: Written as "As a [role], I want [goal] so that [reason]"</p></li><li><p><strong>Requirements</strong>: Functional and non-functional requirements</p></li><li><p><strong>Acceptance Criteria</strong>: Specific, testable criteria that define when the feature is complete</p></li><li><p><strong>Out of Scope</strong>: Explicitly listing what's not included to prevent scope creep</p></li></ul><p>3. <strong>Code-level documentation</strong> - We use TSDoc comments for all public functions, types, and components. This documentation focuses on implementation details, usage patterns, and technical considerations.</p><p>This multi-level approach ensures that developers (and AI assistants!) can find the right information at the right level of abstraction.</p><h3>Code Formatting Guidelines</h3><p>One especially important aspect of coding guidelines involves consistent code formatting. Tools like Prettier and EditorConfig help automatically format both human-written and AI-generated code to maintain consistency across your codebase. By defining these standards in <a href="https://github.com/gergelyszerovay/aibd-template-react-shadcn/blob/main/.prettierrc.json">configuration files</a>, you eliminate formatting debates and ensure all code follows the same conventions regardless of its source.</p><h2>3. Enforcement Tools</h2><p>Guidelines are only effective if they're followed. This is where enforcement tools come in - they provide an objective way to ensure your codebase remains consistent even as it grows through AI assistance.</p><h3>Linting Rules</h3><p>ESLint is your best friend for enforcing coding standards. Configure it to catch violations of your guidelines, like the file organization rules.</p><p>The magic of linters in AI-assisted development is that you can simply ask the AI to "fix the linting errors" after generating code, and it will refactor its own work to match your standards.</p><h4>Enforcing Module Boundaries</h4><p>One of the most powerful tools in our linting arsenal is <a href="https://sheriff.softarc.io/">Sheriff</a>, which enforces module boundaries and dependency rules. This is absolutely crucial for maintaining a clean architecture in AI-assisted development, as it prevents the AI from creating inappropriate dependencies that would violate your architectural principles.</p><p>In my boilerplate, I've set up these specific Sheriff rules:</p><ul><li><p>Features in the <code>features/</code> directory can import from each other and from the <code>shared/</code> directory</p></li><li><p>Shared modules in the <code>shared/</code> directory are forbidden from importing anything from the <code>features/</code> directory</p></li><li><p>Imports from features are only allowed from their root directory, not from their <code>internal/</code> directory</p></li></ul><p>This simple set of rules helps prevent circular dependencies and maintains proper encapsulation of internal implementation details. When the AI generates code that violates these boundaries, Sheriff will immediately flag the issue, allowing you to ask the AI to fix the architectural violation.</p><p>For larger projects, you can customize and refine these basic rules to match your specific architectural needs. You might define more granular boundaries between feature groups, create specialized rules for certain critical modules, or establish stricter constraints on how shared utilities can be used. The key is to start with these fundamental rules and then evolve them as your project's architecture matures.</p><p>These boundary rules are particularly effective because they're simple enough to explain to an AI assistant, yet powerful enough to maintain the structural integrity of your codebase as it grows.</p><h3>Pre-commit Hooks</h3><p>Git's pre-commit hooks provide a built-in mechanism to run checks before code is committed, creating a safety net that prevents guideline violations from making it into your repository.</p><p>A typical pre-commit configuration might:</p><ul><li><p>Run the linter</p></li><li><p>Run the code formatter</p></li><li><p>Run the unit tests</p></li><li><p>Run <a href="https://github.com/pahen/madge">madge</a> to detect circular references before they cause issues</p></li></ul><h3>Automated Testing</h3><p>One practice I've found particularly effective is directly mapping your test cases to the acceptance criteria in your PRD files. This creates a clear traceability between what was required and what was delivered. You can ask the AI to generate test cases based on the acceptance criteria, giving you a head start on comprehensive test coverage.</p><p>For example, if your acceptance criteria includes "On submit, disable all controls and show a loading spinner in the button" you'd have a corresponding test that verifies exactly that behavior. This approach ensures that your tests are focused on business requirements rather than implementation details.</p><h2>4. Solid Boilerplate: Start Off on the Right Foot</h2><p>The final piece of the puzzle is a well-crafted boilerplate - a minimal but complete starting point with all the necessary development tools pre-configured.</p><p>Your boilerplate should include:</p><ul><li><p>Project structure following your architectural guidelines</p></li><li><p>Development tools (linters, formatters, etc.) with pre-configured rules</p></li><li><p>Testing framework setup</p></li><li><p>UI component library integration</p></li><li><p>Authentication skeleton (if applicable)</p></li><li><p>API client structure</p></li><li><p>Build and deployment configurations</p></li></ul><h3>Example: AIBD React Template</h3><p>To demonstrate how these concepts work in practice, I've created an <a href="https://github.com/gergelyszerovay/aibd-template-react-shadcn">AIBD React Template</a> available on GitHub. This boilerplate implements all the principles we've discussed and provides a solid foundation for AI-assisted development.</p><h4>Technical Stack</h4><p>The template uses a modern, well-supported stack that AI tools understand well:</p><ul><li><p><strong>React 19</strong>: The latest version with improved performance features</p></li><li><p><strong>TypeScript</strong>: For type safety and better AI assistance</p></li><li><p><strong>Vite</strong>: Fast, modern build tooling</p></li><li><p><strong>TailwindCSS</strong>: Utility-first CSS that AI excels at generating</p></li><li><p><strong>Shadcn UI</strong>: Pre-built components based on Radix UI</p></li><li><p><strong>React Router v7</strong>: Declarative routing</p></li><li><p><strong>React Hook Form</strong>: Flexible and extensible forms with validation</p></li><li><p><strong>Zod</strong>: TypeScript-first schema validation</p></li><li><p><strong>Supabase Client</strong>: Authentication and backend services</p></li></ul><h4>Project Structure</h4><p>The boilerplate implements a feature-based architecture that maps directly to the AIBD guidelines.</p><p>The boilerplate comes pre-configured with a comprehensive set of enforcement tools:</p><ul><li><p><strong>ESLint</strong>: Configured with strict TypeScript and React rules</p></li><li><p><strong>Sheriff</strong>: Enforces module boundaries between features and shared code, preventing inappropriate dependencies.</p></li><li><p><strong>Husky and lint-staged</strong>: Run validation before each commit:</p><ul><li><p>Linting of staged TypeScript files</p></li><li><p>Automatic code formatting with Prettier</p></li><li><p>Checking for circular dependencies</p></li><li><p>Running unit tests to ensure nothing is broken</p></li></ul></li></ul><p>This enforcement setup creates guardrails that keep AI-generated code on track, even when you're moving quickly.</p><h2>Putting It All Together: A 7-step Vibe Coding Workflow</h2><p>Now that we've covered the four pillars, let's look at a practical workflow that brings them all together.</p><p>After each step in this workflow, always review and validate the AI's modifications. Remember that AI assistance should augment your judgment, not replace it. If you spot issues:</p><ul><li><p>Ask the AI to fix specific problems: "This component is missing error handling. Please add proper error states."</p></li><li><p>Fix things manually when the AI struggles with complex corrections</p></li><li><p>Use the AI to explain confusing generated code before making changes</p></li></ul><p>At the end of the workflow, you'll have a clean import graph (generated by <a href="https://github.com/pahen/madge">Madge</a>) - a visual representation of your feature dependencies and module boundaries. While you can analyze these graphs in detail, a simple "squint test" is surprisingly helpful: a healthy codebase will have a tree-like structure rather than a chaotic, tangled web of dependencies, and should never contain red lines or nodes (which indicate circular dependencies that can cause runtime issues):</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TRUH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TRUH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png 424w, https://substackcdn.com/image/fetch/$s_!TRUH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png 848w, https://substackcdn.com/image/fetch/$s_!TRUH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png 1272w, https://substackcdn.com/image/fetch/$s_!TRUH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TRUH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png" width="1456" height="283" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:283,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:574224,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/162985312?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TRUH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png 424w, https://substackcdn.com/image/fetch/$s_!TRUH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png 848w, https://substackcdn.com/image/fetch/$s_!TRUH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png 1272w, https://substackcdn.com/image/fetch/$s_!TRUH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde8aa216-35bc-44a0-8562-19b7477a9ceb_3267x636.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><p>Here are the steps of the workflow:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MN1J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MN1J!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png 424w, https://substackcdn.com/image/fetch/$s_!MN1J!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png 848w, https://substackcdn.com/image/fetch/$s_!MN1J!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png 1272w, https://substackcdn.com/image/fetch/$s_!MN1J!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MN1J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png" width="1456" height="1634" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1634,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:109051,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/162985312?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MN1J!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png 424w, https://substackcdn.com/image/fetch/$s_!MN1J!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png 848w, https://substackcdn.com/image/fetch/$s_!MN1J!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png 1272w, https://substackcdn.com/image/fetch/$s_!MN1J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea8fb784-fae7-4c59-8495-458c238ffd79_1964x2204.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><ol><li><p><strong>Start with the AIBD React Template</strong> or similar boilerplate that already includes the right tech stack and all necessary enforcement tools.</p></li></ol><ol><li><p><strong>Share your coding guidelines</strong> at the beginning of each session. With tools like Cursor, you can add these as persistent rules the AI will follow automatically.</p></li><li><p><strong>Request features in architectural terms</strong> rather than implementation details. Start by asking the AI to create the directory structure and PRD documentation before implementation.<br></p></li></ol><pre><code><code>&#129489;: I'd like to design three features for our application using Supabase 
for authentication:

1. A sign-in feature
2. A dashboard with sidebar that displays the user's email and has a 
   sign-out button
3. A sign-out feature

Please follow our AIBD guidelines.

Could you please first create the directory structure for these features 
following our feature-based architecture, and draft the prd.md files for 
each one?
</code></code></pre><p>This approach ensures your AI assistant establishes the right foundation before diving into implementation details. The PRD files will serve as the blueprint for implementation while maintaining architectural consistency.</p><ol start="4"><li><p><strong>Let the AI generate the initial implementation</strong> based on your guidelines and existing patterns in the codebase:</p></li></ol><pre><code><code>&#129489;: Now please implement all the features based on their PRDs, 
following our AIBD guidelines.
</code></code></pre><ol start="5"><li><p><strong>Ask the AI to implement tests for the Acceptance Criteria</strong> listed in the PRDs:</p></li></ol><pre><code><code>&#129489;: Now let's make sure we have test coverage for our business 
requirements. Please implement unit tests for all the acceptance 
criteria in our sign-in feature's PRD.
</code></code></pre><ol start="6"><li><p><strong>Commit and trigger pre-commit hooks with AI</strong> to validate your code and fix any issues that arise:</p></li></ol><pre><code><code>&#129489;: Generate a conventional commit message from uncommitted changes. 
It should reference the directory structure: 
include (feature/user-management) or (shared/utils) based on which 
features or shared modules were modified. Commit the changes.
</code></code></pre><p>This approach ties your commit messages directly to your codebase's architecture, making it easier to track which features or shared modules are changing over time.</p><p>If your pre-commit hook catches any issues, use a separate prompt to get help fixing them:</p><pre><code><code>&#129489;: The pre-commit hook failed with these errors.
Please help me fix them while maintaining our AIBD guidelines.
</code></code></pre><p>Having the AI fix linting or testing errors helps it learn from mistakes and reinforces your code standards.</p><ol start="7"><li><p><strong>Repeat</strong> for each feature or component, letting the AI build on its previous work.</p></li></ol><h2>Conclusion: Maintainable Vibe Coding</h2><p>Vibe coding with AI assistance <strong>isn't just about writing code faster</strong> - it's about maintaining <strong>quality</strong> and <strong>consistency</strong> while accelerating development. By choosing the right tech stack, establishing clear guidelines, implementing enforcement tools, and starting with a solid boilerplate, you can benefit from the power of AI without sacrificing the long-term health of your codebase.</p><p>Remember:</p><ul><li><p>Choosing the right tech stack provides a solid foundation for AI-assisted development</p></li><li><p>Guidelines communicate your standards to the AI</p></li><li><p>Enforcement tools verify adherence to those standards</p></li><li><p>Boilerplate encodes your best practices from the start</p></li><li><p>Properly sharing guidelines with AI tools keeps everything consistent</p></li></ul><p>With this framework in place, AI becomes more than just a speed boost - it helps you consistently produce maintainable, structured code that remains comprehensible as your project evolves.</p><p>Have you established guidelines for your AI-assisted development? What challenges have you faced with vibe coding? I'd love to hear about your experiences and what's worked for you.</p><h2>About the Author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>. I've worked as a data scientist and full-stack developer for many years, and I've been a frontend tech lead focusing on Angular development for the past three years. To closely follow the evolution of AI-assisted software development, I've decided to start building AI tools in public and publish my progress on <a href="https://aiboosted.dev">AIBoosted.dev</a>.</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular and how to build AI apps with AI, TypeScript, React, and Angular!</p><div><hr></div><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to make your development workflow more effective with Claude]]></title><description><![CDATA[A cost-efficient and secure coding process using Claude Desktop & Dev containers]]></description><link>https://www.aiboosted.dev/p/ai-development-workflow-claude-dev-containers</link><guid isPermaLink="false">https://www.aiboosted.dev/p/ai-development-workflow-claude-dev-containers</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Wed, 09 Apr 2025 07:20:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!UX_T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UX_T!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UX_T!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png 424w, https://substackcdn.com/image/fetch/$s_!UX_T!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png 848w, https://substackcdn.com/image/fetch/$s_!UX_T!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!UX_T!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UX_T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png" width="1456" height="948" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:948,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2123639,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/160921240?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UX_T!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png 424w, https://substackcdn.com/image/fetch/$s_!UX_T!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png 848w, https://substackcdn.com/image/fetch/$s_!UX_T!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!UX_T!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F045dc2cc-361c-479d-9ff1-6d9dc8c7aaf6_2334x1520.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In this article, I will talk about something that's been a game-changer for my development workflow - combining <strong>VS Code Dev Containers</strong> with <strong>Claude AI</strong>. This setup lets your AI assistant to access and interact with your project files directly.</p><p>By using the <strong>Model Context Protocol (MCP)</strong>, we create a connection that keeps Claude's access limited to just the files in your container, maintaining that important isolation and security we all want.</p><p>One thing I love about this approach is the <strong>full control</strong> it gives you over code generation. While there are many excellent dev tools available, many of them operate as black boxes with considerable functionality happening under the hood. With our MCP and Dev Container combination, your main IDE remains VS Code, but you gain the flexibility to use any chat client that supports MCP. Since these tools aren't VS Code plugins, they're typically <strong>easier to understand, tweak to your liking.</strong></p><p>Here's another compelling advantage: there are <strong>no API costs</strong> associated with this setup! All you need is a monthly Claude Desktop subscription. This setup also works with Claude's free plan, though you'll encounter lower usage limits there.</p><p>To show you how this works in practice, we'll build a simple Tic-Tac-Toe game in both <strong>React</strong> and <strong>Angular</strong>. You'll see how Claude can help implement the game, identify bugs, and streamline your development process regardless of which framework you prefer.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!23U5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!23U5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png 424w, https://substackcdn.com/image/fetch/$s_!23U5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png 848w, https://substackcdn.com/image/fetch/$s_!23U5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png 1272w, https://substackcdn.com/image/fetch/$s_!23U5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!23U5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png" width="1456" height="583" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:583,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:493203,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/160921240?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!23U5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png 424w, https://substackcdn.com/image/fetch/$s_!23U5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png 848w, https://substackcdn.com/image/fetch/$s_!23U5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png 1272w, https://substackcdn.com/image/fetch/$s_!23U5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fb62423-cbb7-4bc4-84d9-5c321f2b2d81_3438x1377.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Understanding Dev Containers</h2><h3>What Are Dev Containers?</h3><p>Dev Containers allow you to encapsulate your entire development environment - including tools, libraries, and configurations - in a containerized setup that runs directly within Visual Studio Code.</p><h3>Key Benefits</h3><p>The advantages of using Dev Containers extend beyond just environment consistency:</p><ul><li><p><strong>Isolation from your local system</strong>: Your projects and their dependencies exist in contained environments, preventing conflicts between different projects or with your host system.</p></li><li><p><strong>Portable configurations</strong>: Your development setup travels with your project, no matter who's working on it or where.</p></li><li><p><strong>Reproducibility</strong>: Every developer experiences the same environment, reducing debugging complexity.</p></li></ul><p>If you want to dive deeper, the <a href="https://code.visualstudio.com/docs/devcontainers/containers">VS Code Dev Containers documentation</a> provides excellent reference material.</p><h3>Windows Setup with WSL2</h3><p>I'm using Windows for my examples, but don't worry - Dev Containers work just as well on macOS and Linux.</p><p>For Windows users, I'd recommend using Docker with WSL2 the backend for optimal performance. This setup is particularly beneficial for development containers due to improved filesystem performance and better resource utilization.</p><p>If you haven't set this up before, check out the <a href="https://docs.docker.com/desktop/features/wsl/">Docker Desktop WSL2 backend documentation</a> - they've got great step-by-step instructions.</p><h2>Setting Up the Dev Container</h2><p>Let's start with a pre-configured Dev Container template that includes MCP server integration for Claude: <a href="https://github.com/gergelyszerovay/aibd-devcontainer">@gergelyszerovay/aibd-base-devcontainer</a>. This foundation saves significant time compared to creating the setup from scratch.</p><p>This dev container comes with all the essentials: Node.js, popular package managers (npm, yarn, and pnpm), and Git. One nice touch is that it automatically uses the Git credentials you've already set up on your host machine, so you can start pushing and pulling code right away without extra authentication steps.</p><p>Let's set up our development container with a few simple commands:</p><pre><code><code>mkdir your-project-name
cd your-project-name
git clone https://github.com/gergelyszerovay/aibd-devcontainer.git ./
code .</code></code></pre><h3>Opening the Container</h3><p>After opening the <code>your-project-name</code> directory in VS Code, you'll see a notification asking if you want to reopen the folder in a container&#8212;go ahead and accept this prompt. If you miss it, no worries! Just click the green button in the lower-left corner of VS Code and select "Reopen in Container."</p><p>VS Code will build and start your container, which might take a minute on the first run. Subsequent starts will be significantly faster.</p><p>The container creates a <strong>Docker volume</strong> named after your project directory (like <code>your-project-name-volume</code>) and mounts it at <code>/workspaces/your-project-name/volume/</code> inside the container. This approach ensures your development files persist across container restarts and rebuilds.<br>Why use a Docker volume instead of a simple host directory mount? <a href="https://github.com/microsoft/vscode-remote-release/issues/8470">It makes a huge difference for file watching functionality</a>, which you'll definitely appreciate when you want features like auto-recompiling to work properly.</p><h3>Project Setup</h3><p>Now that your container is running, let's add your project files. Open a terminal within VS Code, navigate to the <code>volume</code> directory, and clone your repository:</p><pre><code><code>cd /workspaces/your-project-name/volume
git clone https://github.com/your-username/your-project.git ./
pnpm install</code></code></pre><p>Your project files now live in a persistent volume that both VS Code and Claude can access.</p><p>For those looking to jumpstart development with modern frameworks, I've created two specialized boilerplate templates that work in this Dev Container setup:</p><h3>Angular Template</h3><p>The <a href="https://github.com/gergelyszerovay/aibd-template-angular">AIBD Angular Template</a> provides a ready-to-use Angular 19.2 foundation with:</p><ul><li><p>PNPM package manager for faster, more efficient dependency management</p></li><li><p>TailwindCSS 3</p></li><li><p>Testing configuration with Jasmine and Angular Testing Library</p></li></ul><h3>React Template</h3><p>If React is more your style, check out the <a href="https://github.com/gergelyszerovay/aibd-template-react">AIBD React Template</a> featuring:</p><ul><li><p>React 19 with Vite</p></li><li><p>TailwindCSS 3</p></li><li><p>Zustand for lightweight state management</p></li><li><p>TypeScript configuration for type safety</p></li><li><p>Vitest and Testing Library for comprehensive testing</p></li></ul><p>Both templates integrate seamlessly with the Dev Container approach we're discussing.</p><h2>Integrating Claude AI Assistant</h2><p>To connect Claude Desktop to your container's MCP server, you'll need to update your Claude configuration. Add this to your <code>claude_desktop_config.json</code>:</p><pre><code><code>{
  "mcpServers": {
    "devcontainer": {
      "command": "npx",
      "args": ["-y", "supergateway", "--sse", "http://localhost:3100/sse"]
    }
  }
}</code></code></pre><p>We're using <a href="https://github.com/supercorp-ai/supergateway">Supergateway</a> as a proxy between Claude Desktop and our container's MCP server. This proxy is necessary because Claude Desktop currently can't connect directly to an MCP server running in a container.</p><p>After updating your configuration, make sure to <strong>restart Claude Desktop</strong> so it can recognize the new settings and connect to the MCP server in your Dev Container.</p><h3>File Operations</h3><p>With the MCP server properly configured, Claude becomes much more than just a chatbot, it can now:</p><ul><li><p>Read your code to understand what you're working with</p></li><li><p>Examine directory structures to understand your project organization</p></li><li><p>Suggest code changes with proper context</p></li><li><p>Write or edit files</p></li><li><p>Run shell commands to test your code, install packages, or perform other operations</p></li></ul><p>This direct access transforms Claude from a conversational assistant to a coding partner that actually understands your project in context.</p><p>Here's a handy tip: when you want Claude to work with a specific file or directory in your project, VS Code makes it easy - just <strong>right-click on any file or directory in the VS Code Explorer and select "Copy Path"</strong>. Then you can paste that exact path when talking to Claude.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Real-World Examples (React and Angular)</h2><p>Let's walk through a practical example of this AI-assisted setup. I'll show you how to build a simple Tic-Tac-Toe game, which works whether you're using Angular or React.</p><p>Imagine you want to quickly prototype this game in your framework of choice. Here's what the workflow might look like:</p><h3>Clone a Framework-Specific Boilerplate</h3><p>First, let's clone one of our specialized boilerplates into the volume directory. When setting up our project, make sure to run these commands inside the dev container (not on your host machine):</p><p>For Angular:</p><pre><code><code>cd /workspaces/your-project-name/volume
git clone https://github.com/gergelyszerovay/aibd-template-angular.git ./
pnpm install
pnpm run start</code></code></pre><p>For React:</p><pre><code><code>cd /workspaces/your-project-name/volume
git clone https://github.com/gergelyszerovay/aibd-template-react.git ./
pnpm install
pnpm run start</code></code></pre><p>This gives us a solid foundation with all the essential tooling already configured - much better than starting from scratch!</p><h3>Let Claude Explore Your Project Structure</h3><p>Now, let's ask Claude to get a feel for our project:</p><pre><code><code>&#129489;: Could you analyze my project structure in /workspaces/your-project-name/volume/ 
and identify the main component files?</code></code></pre><p>This gives Claude context about your codebase organization before making changes - just like you'd want to orient yourself before diving into a new codebase.</p><h3>Request the Implementation</h3><p>For Angular:</p><pre><code><code>&#129489;: Create a Tic-Tac-Toe game by updating my app.component.ts file at 
/workspaces/your-project-name/volume/src/app/app.component.ts and any 
other necessary files.</code></code></pre><p>For React:</p><pre><code><code>&#129489;: Create a Tic-Tac-Toe game by updating my App.tsx file at 
/workspaces/your-project-name/volume/src/App.tsx and any 
other necessary files.</code></code></pre><h3>Test the Implementation</h3><p>Let's ask Claude to verify the code works correctly and catch any compilation errors:</p><pre><code><code>&#129489;: Can you run `pnpm run build` and tell me if there are any 
compilation errors? 
If you encounter any issues, could you help me fix them?</code></code></pre><h3>Refine the implementation</h3><p>After Claude creates the initial version, you might want to enhance it:</p><pre><code><code>&#129489;: Could you add a game history feature that lets players review 
previous moves?</code></code></pre><h2>Conclusion</h2><p>Combining Dev Containers with Claude AI through MCP creates a powerful development environment that changes how you interact with AI assistance while coding. You're firmly in control of your workflow, but you get to leverage AI capabilities in a secure, isolated environment.</p><p>Looking ahead, I'm planning to publish <strong>coding guidelines</strong> specifically designed for LLMs to ensure consistent coding style across your projects. These guidelines will help Claude generate code that follows our standards automatically, without you having to rewrite or adjust everything afterward. By establishing a shared understanding of your preferred patterns and practices, we can make AI assistance feel like a natural extension of your development team rather than just another tool.</p><p>Have you tried using AI assistants in your development workflow? I'd love to hear what's worked well for you!</p><h2>About the Author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>. I've worked as a data scientist and full-stack developer for many years, and I've been a frontend tech lead focusing on Angular development for the past three years. To closely follow the evolution of AI-assisted software development, I've decided to start building AI tools in public and publish my progress on <a href="https://aiboosted.dev">AIBoosted.dev</a>.</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular and how to build AI apps with AI, TypeScript, React, and Angular!</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[How to Get Better Technical Answers from Claude Using Your Own Documentation]]></title><description><![CDATA[From Basic Queries to Expert Documentation Retrieval Applying Retrieval-Augmented Generation (RAG)]]></description><link>https://www.aiboosted.dev/p/rag-in-action-advanced-retrieval-techniques</link><guid isPermaLink="false">https://www.aiboosted.dev/p/rag-in-action-advanced-retrieval-techniques</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Tue, 01 Apr 2025 07:26:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!oeat!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oeat!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oeat!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png 424w, https://substackcdn.com/image/fetch/$s_!oeat!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png 848w, https://substackcdn.com/image/fetch/$s_!oeat!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!oeat!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oeat!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png" width="1456" height="948" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:948,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2576748,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/160272048?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oeat!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png 424w, https://substackcdn.com/image/fetch/$s_!oeat!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png 848w, https://substackcdn.com/image/fetch/$s_!oeat!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!oeat!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab2ffdab-9c11-454e-b6eb-c0cb1ad5f7d3_2334x1520.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you're enjoying this article and have a Medium subscription, I'd greatly appreciate if you'd <a href="https://pub.towardsai.net/rag-in-action-advanced-retrieval-techniques-with-claude-desktop-a77837a2f37d">open and read it there</a> &#8212; your support helps me continue creating quality content like this!</p><p><strong>Update:</strong> New title (previously it was "RAG in Action: Advanced Retrieval Techniques with Claude Desktop").</p><div><hr></div><p>In the <a href="https://www.aiboosted.dev/p/building-your-own-rag-system-typescript">first part of this series</a>, we built the foundation of our RAG system by setting up <strong>Qdrant</strong>, processing our <strong>AI-friendly Angular documentation</strong>, and configuring the <strong>MCP server to connect with Claude Desktop</strong>. This built upon our earlier exploration of <a href="https://www.aiboosted.dev/p/getting-better-llm-responses-using-ai-friendly-docs">AI-friendly documentation</a>, where we saw how properly structured technical documentation significantly improves LLM responses.</p><p>Now that our infrastructure is in place, it's time to put it to work and explore increasingly sophisticated techniques for <strong>retrieving and leveraging documentation with Claude</strong>. The true power of RAG isn't just in having access to documentation&#8212;it's in how effectively you retrieve and use that information.</p><p>In this article, we'll progress from basic queries to advanced techniques that improve the quality and relevance of Claude's responses. We'll explore:</p><ol><li><p><strong>Basic</strong> <strong>RAG Queries</strong> for straightforward questions</p></li><li><p><strong>2-Step Method</strong> with Improved Response Synthesizer for better focus and precision</p></li><li><p><strong>3-Step Method</strong> with Multi-Query Retrieval for comprehensive coverage</p></li><li><p><strong>Best practices and optimization strategies</strong> based on real-world experience</p></li></ol><p>Whether you're using our Angular documentation or applying these techniques to <strong>your own knowledge base</strong>, these methods will transform how you interact with technical documentation through Claude.</p><h2>Basic RAG Queries: Your First Retrieval Example</h2><p>Let's see our RAG system in action with a simple example that builds on our standalone components question from the previous article. We'll ask: "Are standalone components default in Angular?"</p><p>In Claude Desktop, we'll use the following prompt with Claude 3.5 Haiku model since its knowledge cutoff date is before the release of Angular 19:</p><pre><code><code>&#129489;: Use qdrant_retrieve on angular-19.2.3 collection, retrieve documents matching for the question: "Are standalone components default in Angular?".
Retrieve 50 documents.</code></code></pre><p>When you send this message, several things happen behind the scenes:</p><ol><li><p>Claude recognizes the request to use the <code>qdrant_retrieve</code> provided by the <a href="https://www.npmjs.com/package/@gergelyszerovay/mcp-server-qdrant-retrieve">Qdrant Retrieve MCP Server</a></p></li><li><p>It sends your question to the MCP server</p></li><li><p>The server converts your question to a vector embedding</p></li><li><p>Qdrant searches for the 50 most similar document chunks</p></li><li><p>The retrieved chunks are sent back to Claude</p></li><li><p>Claude incorporates this information into its response</p></li></ol><h3>Understanding the Retrieval Request and Response</h3><p>The MCP server receives a request that looks something like this:</p><pre><code><code>{
  "topK": 50,
  "query": ["Are standalone components default in Angular?"],
  "collectionNames": ["angular-19.2.3"]
}</code></code></pre><p>Qdrant then performs a vector similarity search and returns the most relevant document chunks. These typically include sections from the Angular documentation discussing standalone components, their configuration options, and default settings.</p><p>Claude receives these chunks and now has the specific information needed to provide an accurate, version-specific answer.</p><h3>Analyzing Document Relevance Scores</h3><p>Each retrieved document comes with a <strong>relevance score</strong> indicating how closely it matches your query. These <strong>scores range from 0 to 1</strong>, with higher values indicating greater relevance.</p><p>For our question about standalone components, we might see results like:</p><ol><li><p>Documentation section on component creation options (score: 0.92)</p></li><li><p>Guide section about standalone components (score: 0.89)</p></li><li><p>API reference for ComponentOptions interface (score: 0.87)</p></li><li><p>Migration guide mentioning standalone defaults in Angular 19 (score: 0.86)</p></li><li><p>... (and so on)</p></li></ol><p>The documents clearly mention that in Angular versions before 19.0.0, the standalone option defaulted to false, but starting with Angular 19, standalone components are the default.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fWdT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fWdT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png 424w, https://substackcdn.com/image/fetch/$s_!fWdT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png 848w, https://substackcdn.com/image/fetch/$s_!fWdT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png 1272w, https://substackcdn.com/image/fetch/$s_!fWdT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fWdT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png" width="1456" height="1163" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1163,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:239095,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/160272048?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fWdT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png 424w, https://substackcdn.com/image/fetch/$s_!fWdT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png 848w, https://substackcdn.com/image/fetch/$s_!fWdT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png 1272w, https://substackcdn.com/image/fetch/$s_!fWdT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F37d4da7a-4fe4-440f-a60f-8bb80a2fd464_1717x1371.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>How Claude Uses the Retrieved Context</h3><p>With this information available, Claude constructs an accurate response:</p><pre><code><code>&#129302;: Yes, standalone components are now the default in Angular.
This changed in Angular version 19.0.0, where the standalone
option began defaulting to true. In previous versions (before 19.0.0),
the standalone option defaulted to false, which meant components
needed to be part of an NgModule by default.

This change reflects Angular's gradual move toward a modular
architecture that doesn't require NgModules for basic functionality.
Standalone components can be directly added to the imports array of
other components without needing a module.</code></code></pre><p>Notice how Claude's response incorporates specific details from the retrieved documentation, including the version number where the change occurred. This is the power of RAG in action&#8212;Claude is able to provide version-specific information that might not have been in its original training data.</p><h2>2-Step Method with Improved Response Synthesizer</h2><p>While basic retrieval works well for straightforward questions, we can implement a more sophisticated approach for complex queries. This 2-step method mimics how a human researcher might work: <strong>first gathering information</strong>, <strong>then focusing on what's most relevant.</strong></p><h3>Why the 2-Step Method Works Better</h3><p>The 2-Step Method breaks the retrieval and response process into two key stages:</p><ol><li><p><strong>Initial retrieval with filtering</strong>: Gather relevant documents and immediately identify the most valuable ones</p></li><li><p><strong>Focused response generation</strong>: Create a precise answer based only on the most relevant information</p></li></ol><p>This focused approach often improves response quality through enhanced synthesis capability. By concentrating on the most pertinent information, the LLM can create more precise, authoritative answers.</p><h3>Implementing the 2-Step Method</h3><p>Let's apply this technique to our standalone components question:</p><p><strong>Step 1: Initial retrieval with filtering</strong></p><pre><code><code>&#129489;: Use qdrant_retrieve on angular-19.2.3 collection, retrieve documents
matching for the question: "Are standalone components default in Angular?".
Retrieve 50 documents, then show me the 3 most relevant ones
for answering this question.</code></code></pre><p>This first prompt retrieves documentation and immediately identifies the <strong>most valuable chunks</strong>. Unlike the basic method, this approach intelligently filters for relevance before the final response generation.</p><p>From the 50 retrieved documents, Claude would identify three key ones:</p><ol><li><p>A section from the component creation guide that explicitly states: "Starting from Angular 19.0.0, the standalone option defaults to true."</p></li><li><p>API documentation for the Component decorator showing: "standalone?: boolean - If true, this Component is not part of any NgModule. Default is true starting from v19, default was false before v19."</p></li><li><p>A migration guide section explaining the change: "Angular 19 changes the default for components, directives, and pipes to be standalone by default. This makes the framework more approachable for new developers."</p></li></ol><p><strong>Step 2: Generating a precise, document-backed response</strong></p><pre><code><code>&#129489;: Based on these 3 most relevant documents, answer the question:
"Are standalone components default in Angular?"
</code></code></pre><p>With the filtered context, Claude produces a concise, accurate response:</p><pre><code><code>&#129302;: Yes, standalone components are the default in Angular, but this
is a relatively recent change. According to the Angular documentation:

1. Starting from Angular 19.0.0, the standalone option defaults to true
2. Before Angular 19.0.0, the standalone option defaulted to false
3. This change was made to make the framework more approachable for new developers

This means that if you're using Angular 19 or newer, any new components
you create will be standalone by default unless you explicitly set
`standalone: false` in the component decorator.</code></code></pre><p>Notice how this 2-step method yields a precise answer with specific version information and rationale for the change. The improved response synthesizer creates well-structured content directly tied to the most relevant documentation sources.</p><h2>3-Step Method with Multi-Query Retrieval</h2><p>Building on our 2-Step Method, the 3-Step Method with Multi-Query Retrieval takes result quality even further. By <strong>introducing query variation as a preliminary step</strong>, this technique addresses a fundamental limitation in RAG systems: the phrasing of your original query might not perfectly match how information is expressed in the documentation.</p><h3>Why Query Generation Improves Results</h3><p>A single query, no matter how well-formulated, can miss relevant documentation due to vocabulary mismatches or missing context. For example, when asking about "standalone components in Angular," relevant documentation might use phrases like:</p><ul><li><p>"Components without NgModules"</p></li><li><p>"Self-contained components"</p></li><li><p>"Components with standalone: true"</p></li><li><p>"Module-less components"</p></li></ul><p>By generating multiple alternative queries, we cast a wider net and increase our chances of finding all relevant information, even when it uses different terminology or approaches the concept from different angles.</p><h3>Implementing the 3-Step Method</h3><p>Let's apply this comprehensive technique to our standalone components question:</p><p><strong>Step 1: Generate multiple query variations</strong></p><pre><code><code>&#129489;: Generate 9 RAG queries based on the question "Are standalone components
default in Angular?". Then use qdrant_retrieve on angular-19.2.3
collection for retrieving 50 documents.
Do not reply, just retrieve the documents.</code></code></pre><p>In this first step, <strong>Claude creates multiple versions of the query</strong>, each phrased differently to maximize retrieval coverage. For example:</p><ol><li><p>"Are standalone components the default option in Angular 19?"</p></li><li><p>"Did Angular change the default value of standalone in component configuration?"</p></li><li><p>"What is the default value of the standalone property for Angular components?"</p></li></ol><p><strong>Step 2: Retrieve and filter results across all queries</strong></p><pre><code><code>&#129489;: Show the 3 documents that help most for answering
the original question better.
</code></code></pre><p>This step <strong>consolidates the results from all queries</strong>, identifying the most relevant documents across the entire retrieval set.</p><p><strong>Step 3: Enhanced response synthesis</strong></p><pre><code><code>&#129489;: Based on these 9 documents, answer the original question:
"Are standalone components default in Angular?"</code></code></pre><p>With input from multiple angles combined with the improved response synthesizer, Claude produces a comprehensive answer that incorporates insights from diverse query perspectives, creating the most authoritative and complete response possible.</p><pre><code><code>&#129302;: Yes, standalone components are the default in Angular.
Based on the documentation:

Components must be explicitly marked as standalone: false
to be declared in an NgModule, indicating that the default state
is standalone.

The Angular team officially recommends using standalone components
instead of NgModules for all new code.

When migrating existing code, the process involves removing
standalone: false flags and adding imports directly to components,
showing that standalone is now the standard approach.

Angular provides a strictStandalone compiler option that,
when enabled, reports errors if components aren't standalone,
reinforcing this as the preferred pattern.

The framework offers dedicated bootstrapping methods for
standalone components (bootstrapApplication), which is
recommended over the NgModule-based approach.

In summary, while Angular maintains backward compatibility
with NgModule-based applications, standalone components are
now the default and recommended approach for building Angular
applications.</code></code></pre><h2>Summary and Future Directions</h2><p>Throughout this series on AI-friendly documentation and RAG systems, we've discovered that:</p><ol><li><p><strong>Documentation structure matters</strong>: As we saw in the first article, clear headers and well-organized content dramatically improve retrieval quality</p></li><li><p><strong>Vector embeddings preserve meaning</strong>: Our LlamaIndex-powered pipeline converts documentation into semantic chunks that maintain their contextual relationships</p></li><li><p><strong>Retrieval strategy is as important as infrastructure</strong>: The techniques you use to query your documentation can be even more important than the underlying technical setup</p></li><li><p><strong>Multiple approaches yield better results</strong>: Combining different retrieval strategies provides more comprehensive coverage of complex topics</p></li></ol><h3>Future Directions</h3><p>This is just the beginning of what's possible with RAG systems for documentation. Here are some exciting directions to explore:</p><ul><li><p><strong>Multi-collection retrieval</strong>: Search across multiple documentation sources simultaneously (e.g., Angular, TypeScript, RxJS)</p></li><li><p><strong>Adaptive retrieval strategies</strong>: Automatically select the optimal retrieval technique based on question type. For example, we should use different approaches when searching <strong>inline comments in source code</strong> versus <strong>formal Angular documentation</strong></p></li><li><p><strong>Automated evaluation</strong>: Measure the quality of responses against ground truth answers</p></li><li><p><strong>Token-based retrieval limits</strong>: Rather than retrieving a fixed number of documents, optimize retrieval by setting <strong>token limits</strong>. This balances relevance with cost efficiency, as token usage directly impacts API costs while ensuring you get the maximum relevant information.</p></li><li><p><strong>Hierarchical document retrieval</strong>: Build retrieval systems that understand document structure and can pull in related sections from the same document. This maintains the contextual continuity between retrieved chunks while still focusing on the most relevant information.</p></li></ul><h3>Beyond Documentation</h3><p>While we've focused on Angular documentation in this series, the techniques we've explored apply to any domain with structured knowledge. Consider applying these approaches to:</p><ul><li><p>Internal company documentation and knowledge bases</p></li><li><p>Product manuals and support documentation</p></li><li><p>Legal documents and regulatory frameworks</p></li></ul><p>By understanding and implementing these RAG techniques, you're not just improving how you interact with Angular documentation&#8212;you're developing skills that transform how you work with information across all domains.</p><h2>About the Author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>. I've worked as a data scientist and full-stack developer for many years, and I've been a frontend tech lead focusing on Angular development for the past three years. To closely follow the evolution of AI-assisted software development, I've decided to start building AI tools in public and publish my progress on <a href="https://aiboosted.dev">AIBoosted.dev</a>.</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular and how to build AI apps with AI, TypeScript, React, and Angular!</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><p></p>]]></content:encoded></item><item><title><![CDATA[Building Your Own RAG System: Enhancing Claude with Your Documentation]]></title><description><![CDATA[Connecting Claude Desktop to Your Documentation Through MCP and Qdrant]]></description><link>https://www.aiboosted.dev/p/building-your-own-rag-system-typescript</link><guid isPermaLink="false">https://www.aiboosted.dev/p/building-your-own-rag-system-typescript</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Wed, 26 Mar 2025 09:06:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!v4UW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!v4UW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!v4UW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png 424w, https://substackcdn.com/image/fetch/$s_!v4UW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png 848w, https://substackcdn.com/image/fetch/$s_!v4UW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!v4UW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!v4UW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png" width="1456" height="948" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:948,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1452937,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/159897291?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!v4UW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png 424w, https://substackcdn.com/image/fetch/$s_!v4UW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png 848w, https://substackcdn.com/image/fetch/$s_!v4UW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!v4UW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe186795b-bb72-4c0a-a533-23477f05bd60_2334x1520.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you're enjoying this article and have a <strong>Medium</strong> subscription, I'd greatly appreciate if you'd <a href="https://medium.com/towards-artificial-intelligence/building-your-own-rag-system-enhancing-claude-with-your-documentation-6ec0b917cfc5">open and read it there</a> &#8212; your support helps me continue creating quality content like this!</p><div><hr></div><p>In the previous article, <a href="https://www.aiboosted.dev/p/getting-better-llm-responses-using-ai-friendly-docs">Getting Better LLM Responses Using AI-Friendly Documentation</a>, we explored how creating AI-friendly documentation can improve the quality of responses from Large Language Models (LLMs). We saw how properly structured Angular documentation helped ChatGPT provide more accurate answers about framework features. Let's advance our documentation strategy by implementing our own Retrieval-Augmented Generation (RAG) system, powered by Qdrant and designed to work hand-in-hand with Claude Desktop.</p><h2>Introduction: Taking RAG from Concept to Implementation</h2><p>Let's quickly revisit what makes documentation "AI-friendly." Remember those key principles? Clear headers, comprehensive single files, and focused feature-specific content. These organization techniques aren't just theoretical&#8212;they make a real difference in how LLMs understand our technical docs.</p><p>While the file upload approach we explored previously works reasonably well, it comes with limitations. Most notably, you're constrained by context window sizes, and you need to manually select which documentation to include in each conversation. This creates a frustrating cycle of starting new conversations whenever you need to reference different documentation sections.</p><p>Custom RAG systems solve these problems by automatically retrieving just the most relevant documentation fragments for any given question. Think of it as having a research assistant who instantly pulls exactly the right pages from your technical library without you having to specify which books to check.</p><p>In this guide, I'll walk you through:</p><ul><li><p>Setting up your own Qdrant vector database for documentation storage</p></li><li><p>Processing and indexing your AI-friendly Angular documentation</p></li><li><p>Configuring an MCP server that connects Qdrant to Claude</p></li><li><p>Building a RAG pipeline for documentation retrieval</p></li></ul><p>By the end, you'll have a RAG system that significantly enhances Claude's ability to assist with software development.</p><h2>Understanding RAG Components for Documentation</h2><p>Before diving into implementation details, let's explore the key components that make up our RAG system.</p><h3>Vector Embeddings</h3><p>At the heart of any RAG systems are vector embeddings. These are numerical representations of text that capture semantic meaning in a way that computers can process. When we convert text to vectors, we're essentially mapping the content into a mathematical space where similar concepts cluster together.</p><p>For example, the phrases "component initialization" and "component creation" would have vector representations that are close to each other in this space, even though they use different words. This allows us to find relevant documentation based on meaning rather than just keyword matching.</p><p>We create these vector embeddings using specialized embedding models, which transform text into these high-dimensional mathematical representations while preserving semantic meaning.</p><p><a href="https://qdrant.tech/">Qdrant</a> is a vector database optimized for storing and searching these embeddings.</p><h3>MCP: Standardized Tool Communication</h3><p>The <a href="https://modelcontextprotocol.io/introduction">Model Context Protocol (MCP)</a> is a standardized interface that allows external tools like our RAG system to communicate with Claude and other LLMs. Developed by Anthropic, MCP defines how Claude can interact with our custom <code>qdrant-retrieve</code> server, which in turn connects to our Qdrant vector database.</p><p>When you ask a question, the <code>qdrant-retrieve</code> MCP server:</p><ol><li><p>Receives the query from Claude</p></li><li><p>Translates it into a vector search for Qdrant</p></li><li><p>Processes the search results</p></li><li><p>Returns the most relevant documentation fragments to Claude</p></li></ol><p>This happens seamlessly in the background, giving Claude access to your entire documentation set without overloading its context window.</p><h3>Architecture Overview</h3><p>Our complete solution works like this:</p><p><strong>Documentation Processing Pipeline</strong>:</p><ul><li><p>AI-friendly documentation is divided into semantic chunks</p></li><li><p>Each chunk is converted to vector embeddings</p></li><li><p>Embeddings are stored in Qdrant collections</p></li></ul><p><strong>Retrieval Process</strong>:</p><ul><li><p>User asks a question in Claude Desktop</p></li><li><p>Question is sent to the <code>qdrant-retrieve</code> MCP server</p></li><li><p>MCP server converts the question to a vector</p></li><li><p>Qdrant retrieves the most similar documentation chunks</p></li><li><p>Relevant chunks are sent back to Claude</p></li><li><p>Claude generates an answer using the retrieved context</p></li></ul><p>This architecture gives us the flexibility to handle complex questions while maintaining high accuracy and context relevance.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Setting Up Your RAG Environment</h2><p>Now that we understand the theory, let's get our hands dirty and set up the environment. Don't worry if you're new to these tools &#8212; I'll guide you through each step.</p><h3>Installing and Configuring Qdrant</h3><p>Qdrant can be run as a Docker container, which makes setup relatively straightforward:</p><pre><code><code># Download the latest Qdrant image from Dockerhub
docker pull qdrant/qdrant

# Run the service
docker run -p 6333:6333 -p 6334:6334 \
    -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
    qdrant/qdrant</code></code></pre><p>This configuration stores all data in the <code>./qdrant_storage</code> directory on your machine. Using this shared directory gives you two big advantages: your data persists even if you restart the container, and you can directly access your vector database files during development.</p><p>Once Qdrant is running, you'll have access to:</p><ul><li><p>REST API: </p></li></ul><p>http://localhost:6333</p><ul><li><p> for programmatic interactions</p></li><li><p>Web UI: <a href="http://localhost:6333/dashboard">http://localhost:6333/dashboard</a> for visually exploring and managing your collections</p></li></ul><p>If you prefer not to run Qdrant locally, you can also use Qdrant Cloud, which offers a free tier with 1GB of storage - more than enough for our documentation use case. Check out the official guides for more detailed setup instructions: <a href="https://qdrant.tech/documentation/quickstart/">Local Quickstart</a> or <a href="https://qdrant.tech/documentation/quickstart-cloud/">Cloud Quickstart</a>.</p><h3>Preparing Angular Documentation for Qdrant with LlamaIndex.TS</h3><p>Now that we have Qdrant running, let's create our vector collection. In Qdrant, a collection is a named container for related vector embeddings - like a database table but optimized for similarity search. We'll use <a href="https://ts.llamaindex.ai/">LlamaIndex.TS</a>, a powerful TypeScript framework that simplifies the entire RAG pipeline from document processing to retrieval.</p><p>To create your own collection of Angular documentation embeddings, I've prepared a ready-to-use project that handles all the heavy lifting. Here's how to get started:</p><pre><code><code># Clone the repository
git clone https://github.com/gergelyszerovay/ai-friendly-docs
cd ai-friendly-docs

# Install dependencies
pnpm install

# Generate Angular documentation and embeddings
pnpm run generate:angular</code></code></pre><p>This script performs three key operations:</p><ol><li><p><strong>Repository Cloning</strong>: Automatically downloads the specific version of Angular documentation from GitHub</p></li><li><p><strong>Documentation Generation</strong>: Transforms the official Angular docs into AI-friendly formats</p></li><li><p><strong>Vector Embedding Creation</strong>: Creates and stores vector embeddings in your local Qdrant instance running at </p></li></ol><p>http://localhost:6333</p><p>The <a href="https://github.com/gergelyszerovay/ai-friendly-docs">AI-Friendly Documentation project</a> includes a <a href="https://github.com/gergelyszerovay/ai-friendly-docs/blob/main/src/shared/llamaindex/createCollectionFromMarkdownFiles.ts">function</a> that builds collections from the Angular documentation. Let's look at how this tool works to understand the underlying process.</p><h4>The Collection Pipeline</h4><p>Here's the high-level flow for creating a searchable vector collection from our AI-friendly Angular docs:</p><pre><code><code>async function generateQdrantCollection() {
  // 1. Configure your embedding model
  Settings.embedModel = new HuggingFaceEmbedding({
    modelType: "Xenova/all-MiniLM-L6-v2",
  });

  // 2. Locate your documentation files
  const docsDir = "./ai-friendly-docs/angular-19.2.3/sections";
  const files = findMarkdownFiles(docsDir);

  // 3. Create the vector collection
  await createCollectionFromMarkdownFiles("angular-19.2.3", files);
}</code></code></pre><p>Let's take a closer look at the code that processes our Angular documentation and creates embeddings:</p><pre><code><code>async function createCollectionFromMarkdownFiles(collectionName, files) {
  // Initialize parser for smart document chunking
  const markdownNodeParser = new MarkdownNodeParser();
  let allNodes = [];

  // Process each documentation file
  for (const filePath of files) {
    const text = fs.readFileSync(filePath, "utf-8");
    const document = new Document({
      text,
      metadata: { originalFile: path.basename(filePath) },
    });

    // Convert document to semantic chunks
    const nodes = markdownNodeParser([document]);
    allNodes = [...allNodes, ...nodes];
  }

  // Connect to Qdrant and store the embeddings
  const vectorStore = new QdrantVectorStore({
    url: "http://localhost:6333",
    collectionName,
  });

  // Create and store the embeddings
  return await VectorStoreIndex.fromDocuments(allNodes, {
    vectorStores: { TEXT: vectorStore },
  });
}</code></code></pre><p>The real power comes from how LlamaIndex processes our documentation. Rather than treating files as monolithic blocks or arbitrarily splitting text by character count, it intelligently chunks the content based on semantic structure.</p><p>The <code>MarkdownNodeParser</code> is what makes this possible. It analyzes the hierarchy of headers in our Markdown files (# for top-level, ## for second-level, etc.) and creates natural boundaries between content sections. For example:</p><ul><li><p>A section about component inputs under <code>## Inputs</code> becomes its own chunk</p></li><li><p>This chunk is separate from the <code>## Outputs</code> section that might follow it</p></li><li><p>Both chunks maintain metadata connecting them to their parent document</p></li></ul><p>This semantic chunking means when Claude searches for information about "component inputs," it gets precisely the relevant sections, not the entire components guide.</p><h4>Why This Matters for Retrieval Quality</h4><p>The quality of document chunking directly impacts how relevant your search results will be. Each chunk in our system:</p><ul><li><p>Preserves its hierarchical context (parent headers, document source)</p></li><li><p>Contains conceptually complete information (not cutting off mid-explanation)</p></li><li><p>Has the right granularity for targeted retrieval</p></li></ul><p>When LlamaIndex creates embeddings for these chunks and stores them in Qdrant, it maintains all this contextual information. This means when Claude asks Qdrant for the most similar documents to a query, it gets well-formed, semantically meaningful content &#8211; not random text fragments.</p><p>This approach works particularly well for technical documentation with consistent header structures, which is exactly what we created in our AI-friendly Angular docs.</p><h3>Setting Up the MCP Server</h3><p>The MCP server is the component that connects Claude to Qdrant. For our implementation, we'll use the <code>@gergelyszerovay/mcp-server-qdrant-retrieve</code> package, which provides a ready-to-use MCP server for Qdrant retrieval integration.</p><p>To integrate the MCP server with Claude Desktop, add the following to your <code>claude_desktop_config.json</code> file:</p><pre><code><code>{
  "mcpServers": {
    "qdrant": {
      "command": "npx",
      "args": ["-y", "@gergelyszerovay/mcp-server-qdrant-retrieve"],
      "env": {
        "QDRANT_API_KEY": "your_api_key_here"
      }
    }
  }
}</code></code></pre><p>Note that for a basic local Qdrant setup without authentication enabled, you can simply use an empty <code>env</code> object. If you're using Qdrant Cloud or have configured authentication on your local instance, you'll need to add your API key as an environment variable.</p><p>The server exposes a <code>qdrant_retrieve</code> tool that Claude can call to search for relevant documentation fragments based on your questions.</p><h3>A Quick Test: Your First Retrieval</h3><p>Before wrapping up, let's do a quick test to make sure everything is working. With your Qdrant server running and MCP configured, ask Claude this simple question about Angular:</p><pre><code><code>Use qdrant_retrieve on angular-19.2.3 collection, retrieve documents matching for the question: "Are standalone components default in Angular?". Retrieve 3 documents.</code></code></pre><p>If everything is set up correctly, Claude should make a request to your MCP server, which then searches Qdrant and returns the most relevant documentation fragments. You should see Claude incorporate information about standalone components becoming the default in Angular 19.0.0.</p><p>Note that the first vector database retrieval might be a bit slower, as the MCP server needs to download the required embedding model. Subsequent queries will be much faster.</p><p>This quick test confirms that:</p><ol><li><p>Your Claude Desktop can communicate with the MCP server</p></li><li><p>The MCP server can successfully query your Qdrant collection</p></li><li><p>The retrieved documents contain meaningful, relevant information</p></li></ol><p>Now that we've confirmed the basic functionality, we're ready to explore more sophisticated retrieval techniques in the next article.</p><h2>Conclusion and Next Steps</h2><p>In this first part of our RAG journey, we've laid the groundwork for a powerful documentation retrieval system. We've:</p><ol><li><p>Explored the key components of a RAG system (vector embeddings, vector databases, and MCP)</p></li><li><p>Set up Qdrant for vector storage and understood how to create collections</p></li><li><p>Processed AI-friendly Angular documentation into semantically meaningful chunks</p></li><li><p>Configured the MCP server to connect Claude Desktop with our vector database</p></li></ol><p>With these fundamentals in place, you now have a functioning RAG system ready to enhance your interactions with Claude. The infrastructure we've built provides a flexible foundation that can work with any documentation set, not just Angular.</p><p>In the next part of this series, we'll explore how to effectively use this system, including basic and advanced retrieval techniques that will significantly improve Claude's ability to answer technical questions. We'll walk through real-world examples and develop increasingly sophisticated approaches to get the most accurate and relevant information from our documentation.</p><p>Stay tuned for <a href="https://www.aiboosted.dev/p/rag-in-action-advanced-retrieval-techniques">"How to Get Better Technical Answers from Claude Using Your Own Documentation - From Basic Queries to Expert Documentation Retrieval Applying Retrieval-Augmented Generation (RAG)"</a> where we'll put this system to work!</p><h2>About the Author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>. I've worked as a data scientist and full-stack developer for many years, and I've been a frontend tech lead focusing on Angular development for the past three years. To closely follow the evolution of AI-assisted software development, I've decided to start building AI tools in public and publish my progress on <a href="https://aiboosted.dev">AIBoosted.dev</a>.</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular and how to build AI apps with AI, TypeScript, React, and Angular!</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Getting Better LLM Responses Using AI-Friendly Documentation (Updated June 2025)]]></title><description><![CDATA[How I Created AI-Friendly Documentation for Angular (Angular Now Provides Official LLMs.txt Files Too)]]></description><link>https://www.aiboosted.dev/p/getting-better-llm-responses-using-ai-friendly-docs</link><guid isPermaLink="false">https://www.aiboosted.dev/p/getting-better-llm-responses-using-ai-friendly-docs</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Wed, 19 Mar 2025 07:53:59 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!EKKe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!EKKe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!EKKe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png 424w, https://substackcdn.com/image/fetch/$s_!EKKe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png 848w, https://substackcdn.com/image/fetch/$s_!EKKe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!EKKe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!EKKe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png" width="1456" height="948" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:948,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2139992,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/159069267?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!EKKe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png 424w, https://substackcdn.com/image/fetch/$s_!EKKe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png 848w, https://substackcdn.com/image/fetch/$s_!EKKe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png 1272w, https://substackcdn.com/image/fetch/$s_!EKKe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa640a587-e885-47bf-8250-c80e6f6e741c_2334x1520.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If you're enjoying this article and have a <strong>Medium</strong> subscription, I'd greatly appreciate if you'd <a href="https://medium.com/towards-artificial-intelligence/getting-better-llm-responses-using-ai-friendly-documentation-8e64a399b3af">open and read it there</a> &#8212; your support helps me continue creating quality content like this!</p><div><hr></div><p><strong>Update (June 2025):</strong> Added reference to the Angular team's two official LLMs.txt files that help LLMs create better modern Angular code.</p><div><hr></div><p>Have you ever felt that moment of frustration when a Large Language Model completely misunderstands your coding question? We've all been there. The truth is, LLMs are a lot like our developer colleagues - the quality of their help depends heavily on the context we provide.</p><p>Think of it this way: just as you wouldn't ask a teammate for help with a complex bug without sharing the relevant code, LLMs need proper context to give you their best advice. That's where AI-friendly documentation comes in, and I'd like to share what I've learned while creating some for Angular.</p><h2>The Quest for Better Context</h2><p>That's what inspired me to start collecting AI-friendly documentation. Here are some notable examples:</p><ul><li><p>Model Context Protocol: <a href="https://modelcontextprotocol.io/llms-full.txt">https://modelcontextprotocol.io/llms-full.txt</a> offers complete documentation in a single file</p></li><li><p>Daisy UI v5: <a href="https://daisyui.com/llms.txt">https://daisyui.com/llms.txt</a> provides both comprehensive docs and component-specific versions</p></li></ul><p>There are several approaches to feeding this knowledge to your AI assistant:</p><ul><li><p>Sending documentation directly as a chat message to the LLM</p></li><li><p>Uploading relevant files to your conversation</p></li><li><p>Using a Retrieval-Augmented Generation (RAG) system to intelligently fetch information</p></li></ul><p>As part of this exploration, I've created AI-friendly documentation for Angular, which you can find here: <a href="https://github.com/gergelyszerovay/ai-friendly-docs/">https://github.com/gergelyszerovay/ai-friendly-docs/</a></p><p><strong>Update (June 2025):</strong> <br>The Angular team has developed two official LLMs.txt files to help LLMs and tools that use LLMs for code generation create better modern Angular code:</p><ul><li><p><a href="https://angular.dev/llms.txt">llms.txt</a> - an index file providing links to key files and resources.</p></li><li><p><a href="https://angular.dev/llms-full.txt">llms-full.txt</a> - a more robust compiled set of resources describing how Angular works and how to build Angular applications.</p></li></ul><h2>Beyond Basic File Uploads</h2><p>While most chat interfaces let you upload files, Claude Desktop takes this further with its Project Knowledge feature. It allows you to provide documents, code, and other materials that Claude can reference during conversations. With a massive context window of 200,000 tokens (roughly 500 pages of text), Claude can maintain a deeper understanding of complex projects, making its responses significantly more accurate.</p><p>A context window, by the way, is essentially how much information an LLM can "keep in mind" during a single conversation - think of it as the AI's working memory. It's measured in tokens, which are the basic units that LLMs process (roughly corresponding to word fragments). For example, the sentence "I love programming in TypeScript" might be broken down into tokens like ["I", "love", "program", "ming", "in", "Type", "Script"]. The larger the context window, the more information the LLM can consider when generating responses.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IVjM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IVjM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png 424w, https://substackcdn.com/image/fetch/$s_!IVjM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png 848w, https://substackcdn.com/image/fetch/$s_!IVjM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png 1272w, https://substackcdn.com/image/fetch/$s_!IVjM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IVjM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png" width="1216" height="798" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:798,&quot;width&quot;:1216,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:790585,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.aiboosted.dev/i/159069267?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IVjM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png 424w, https://substackcdn.com/image/fetch/$s_!IVjM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png 848w, https://substackcdn.com/image/fetch/$s_!IVjM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png 1272w, https://substackcdn.com/image/fetch/$s_!IVjM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F29416324-16f3-42f4-8849-9da1e6b47052_1216x798.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>The Magic Behind RAG Systems</h2><p>RAG (Retrieval-Augmented Generation) is what powers these context-aware AI interactions. Rather than simply dumping all information into the model, RAG systems intelligently retrieve the most relevant pieces for your specific question.</p><p>It's like having a helpful librarian instead of being handed a pile of books - the librarian knows exactly where to find the information you need. When your documentation has clear headers and organized sections, the retrieval system can pinpoint relevant information much more precisely.</p><p>Under the hood, RAG systems work by converting your documentation into vector embeddings&#8212;numerical representations that capture semantic meaning. When you ask a question, the system converts it to the same format and finds the most similar sections in your documentation. Pretty neat, right?</p><p>I'll be diving deeper into RAG systems in an upcoming article, so stay tuned if you're curious about setting up your own.</p><h2>Building Better Angular Documentation</h2><p>Here are the principles I've found most helpful when creating AI-friendly documentation:</p><ol><li><p><strong>Create a comprehensive single file</strong> containing the entire framework or library documentation for when you need broad knowledge</p></li><li><p><strong>Develop separate, focused files</strong> for specific features that can be included individually when you need targeted information</p></li><li><p><strong>Maintain clear header structures</strong> to help AI systems understand relationships between different documentation sections</p></li></ol><p>I've applied these ideas to my Angular documentation project. Starting with the official Angular docs, I've created both a comprehensive <code>angular-full.md</code> file and individual feature-specific files in the <code>sections</code> directory. This approach gives you flexibility in how you provide context to your AI assistants.</p><h2>The Importance of Clear Header Structures</h2><p>That third principle (maintaining clear header structures) deserves special attention because it dramatically affects how well AI systems can navigate your documentation.</p><p>Let me show you what I mean with an example from the Angular documentation. The original file <code>guide/components/inputs.md</code> begins with:<br></p><pre><code><code># Accepting data with input properties

Tip: This guide assumes you've already read the [Essentials Guide](essentials). 
Read that first if you're new to Angular.

Tip: If you're familiar with other web frameworks, input properties 
are similar to _props_.

When you use a component, you commonly want to pass some data to it. 
A component specifies the data that it accepts by declaring **inputs**:

// ...
</code></code></pre><p>From the file's name and content alone, it's not immediately obvious that this document explains component inputs within Angular's component architecture.</p><p>In my AI-friendly version in <code>guide-components.md</code>, I've added an additional header that provides this crucial context:<br></p><pre><code><code># Guide Components

// ...

## Accepting data with input properties

Tip: This guide assumes you've already read the [Essentials Guide](essentials). 
Read that first if you're new to Angular.

Tip: If you're familiar with other web frameworks, input properties 
are similar to _props_.

When you use a component, you commonly want to pass some data to it. 
A component specifies the data that it accepts by declaring **inputs**:

// ...
</code></code></pre><p>This hierarchical structure helps AI systems understand that component inputs are a subtopic within Angular components. This context is especially valuable for retrieval systems that might pull just a section of documentation, as it helps the AI properly frame the information within Angular's component architecture.</p><h2>Being Strategic About Context Size</h2><p>One valuable lesson I've learned is to be thoughtful about what you include in your AI's context window.</p><p>Sending the entire <code>angular-full.md</code> file isn't always the best move. For instance, if you're working on Angular components, you'll get much better results by including just <code>sections/guide-components.md</code> and perhaps a few related files like <code>guide-templates.md</code> or <code>guide-di.md</code>.</p><p>This targeted approach helps the AI focus on what matters most without getting distracted by unrelated documentation. Sometimes less really is more when it comes to context.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Seeing It in Action: A Real Example</h2><p>Let's look at a straightforward question: "Are standalone components default in Angular?"</p><p>This is an interesting case because before Angular 19.0.0, the standalone option defaulted to false, but in version 19 it changed to true. I tested this question with different LLMs, providing varying levels of documentation context.</p><h3>ChatGPT (GPT-4o)</h3><p><strong>Test 1: No documentation provided</strong></p><pre><code><code>&#129489;: Are standalone components default in Angular?

&#129302;: No, standalone components are not the default in Angular. By default, 
Angular components are part of NgModules, meaning they must be declared 
inside an @NgModule.

However, Angular introduced standalone components in Angular 14 to allow 
components to work without being declared inside an @NgModule. Instead, 
they use the standalone: true flag in their decorator.
</code></code></pre><p>This response is outdated and lacks awareness of Angular 19's changes: a perfect example of why context matters.</p><p><strong>Test 2: Full Angular documentation added</strong></p><pre><code><code>&#129489;: Are standalone components default in Angular?

&#129302;: No, standalone components are not the default in Angular, but they 
are encouraged as a simplified way to build applications. In Angular 
versions before 19.0.0, the standalone option defaults to false, meaning 
components are part of an NgModule by default&#8203;. However, newer Angular 
versions allow developers to migrate existing applications to standalone 
mode incrementally without breaking changes&#8203;.
</code></code></pre><p>Much better! The model correctly identifies the version cutoff, though <br>it's still a bit hesitant to clearly state they're now the default in v19+.</p><p><strong>Test 3: Only guide-components.md added</strong></p><pre><code><code>&#129489;: Are standalone components default in Angular?

&#129302;: Yes, in Angular, components are standalone by default. This means 
you can directly add them to the imports array of other components without 
needing an NgModule. However, components created with older versions of 
Angular may have standalone: false in their @Component decorator, 
requiring them to be part of an NgModule.

Important Note: In Angular versions before 19.0.0, the standalone option 
defaulted to false&#8203;.
</code></code></pre><p>Perfect! With the targeted documentation, we get the most accurate and confident answer.</p><p>These examples highlight how crucial proper documentation context is for accurate AI responses. When asking different LLMs this question without providing documentation context, you'll often get responses that sound plausible but might not reflect the latest updates. The LLM is working from its training data, which might be outdated or incomplete.</p><p>However, when you provide the relevant section of Angular documentation, the response changes dramatically. It's like the difference between asking a developer who last used Angular a year ago versus one who's working with the latest version daily.</p><h2>Adding Code Context to the Mix</h2><p>Documentation is just one piece of the puzzle. For truly effective AI assistance with development tasks, you often need to provide code alongside documentation.</p><p>If you're looking to optimize source code for AI consumption, check out Repomix (</p><p>https://repomix.com/</p><p>). It's a great tool that helps consolidate entire codebases into a single, AI-friendly file.</p><p>Repomix handles token count management (crucial for staying within context limits), excludes sensitive information, and produces output in various formats. It's the perfect companion to AI-friendly documentation when you need to provide both docs and code context.</p><h2>AI-Friendly Documentation vs. Web Retrieval</h2><p>Why create AI-friendly documentation when many LLMs can now access the web?</p><p>While web access is undoubtedly valuable, it presents several limitations compared to carefully curated documentation:</p><ol><li><p><strong>Version Specificity</strong>: Web searches may return information about different versions of a framework or library than the one you're using. AI-friendly documentation can be created specifically for your exact version, eliminating confusion and inconsistencies.</p></li><li><p><strong>Accuracy Control</strong>: Web content varies widely in quality and accuracy. Curated documentation ensures the LLM works with verified, reliable information rather than potentially outdated or incorrect sources.</p></li><li><p><strong>Contextual Coherence</strong>: When an LLM retrieves information from multiple web sources, it must reconcile potentially conflicting information. Curated documentation provides a unified, coherent context that leads to more consistent responses.</p></li><li><p><strong>Reduced Hallucinations</strong>: With clear, comprehensive documentation, LLMs are less likely to "fill in the gaps" with incorrect assumptions, reducing hallucinations and increasing accuracy.</p></li></ol><p>For example, in our earlier test case about Angular standalone components, a web search might return outdated information from various sources. Our curated documentation, however, clearly indicated the version-specific change in Angular 19, resulting in a more accurate response.</p><h2>Looking Forward</h2><p>We've explored how AI-friendly documentation can significantly improve the quality of responses from LLMs. The key takeaway from my work with Angular documentation and tools like Repomix is that structuring information specifically for AI consumption yields dramatically better results.</p><p>Based on my experience, there are two key approaches for enhancing AI capabilities:</p><p><strong>Context Enhancement:</strong> This is what we've discussed throughout this article - providing additional context through well-structured documentation and code. This approach works with existing models and gives them better information to work with.</p><p><strong>Tool Integration</strong>: The next evolution in AI assistance comes through standardized integration methods, particularly the Model Context Protocol (MCP) developed by Anthropic. MCP is a specification that defines how AI models can communicate with external tools in a standardized way. Unlike earlier approaches where each AI service had its own custom integration methods, MCP provides a common language for tool definitions, invocations, and responses.</p><p>These approaches complement each other: better documentation helps LLMs understand what tools to use, while integrated tools expand their capabilities. I am currently developing RAG tools that leverage MCP and will share these developments in my upcoming articles:</p><ul><li><p><a href="https://www.aiboosted.dev/p/building-your-own-rag-system-typescript">Building Your Own RAG System: Enhancing Claude with Your Documentation</a></p></li><li><p><a href="https://www.aiboosted.dev/p/rag-in-action-advanced-retrieval-techniques">How to Get Better Technical Answers from Claude Using Your Own Documentation</a></p></li></ul><h2>About the Author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>. I've worked as a data scientist and full-stack developer for many years, and I've been a frontend tech lead focusing on Angular development for the past three years. To closely follow the evolution of AI-assisted software development, I've decided to start building AI tools in public and publish my progress on <a href="https://aiboosted.dev">AIBoosted.dev</a>.</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular and how to build AI apps with AI, TypeScript, React, and Angular!</p><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.aiboosted.dev/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[A basic LangChain.js chain with prompt template, structured JSON output and OpenAI / Ollama LLMs]]></title><description><![CDATA[A step-by-step guide for Typescript developers]]></description><link>https://www.aiboosted.dev/p/llm-chain-with-structured-json-output</link><guid isPermaLink="false">https://www.aiboosted.dev/p/llm-chain-with-structured-json-output</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Thu, 27 Jun 2024 10:19:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!sf_m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sf_m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sf_m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!sf_m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!sf_m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!sf_m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sf_m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png" width="1456" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:81768,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sf_m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!sf_m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!sf_m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!sf_m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd98264bd-3b9e-453c-9122-66ccc9a74307_1620x890.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is the fourth part of my AI-Boosted Development series:</p><ul><li><p>The <a href="https://www.aiboosted.dev/p/lets-build-ai-tools-typescript">introduction</a> summarized recent changes enabling easy AI app prototyping in TypeScript.</p></li><li><p>The <a href="https://www.aiboosted.dev/p/install-jupyter-lab-deno-typescript-prototyping">second article</a> covered installing Jupyter Lab IDE and tools for rapid prototyping.</p></li><li><p>The <a href="https://www.aiboosted.dev/p/jupyter-lab-ide-basics-with-typescript">third part</a> explained how to use JupyterLab and demonstrated basic coding workflow.</p></li></ul><p>This article focuses on building the first part of the "Text Reviewer" app. As I explained in the <a href="https://www.aiboosted.dev/p/lets-build-ai-tools-typescript">introduction</a>, the app works in two steps:</p><ol><li><p>The user enters text, which an LLM model reviews and improves.</p></li><li><p>The tool compares the LLM's result to the original text and shows the changes.</p></li></ol><p>We'll cover the first step here, <strong>showing a basic LangChain chain that reviews and improves text.</strong></p><p>The second step doesn't use a language model. If we ask a language model to list changes compared to the original text, it often returns incorrect or incomplete results. These models can generate textual outputs or improve a text's quality, but they're not good at algorithmic tasks. They process prompts and try to predict the expected answer. They do not "think" or possess human-like logical capabilities. However, we can use a language model to generate the source code of a text comparison function. We'll use an LLM to generate code for text comparison in future articles.</p><p>Language models can respond in different formats, such as Markdown, JSON, or XML. In the examples below, <strong>we ask the models to provide JSON</strong> responses in a predefined schema. JSON responses work well if the schema is simple and the response doesn't contain many special characters.</p><p>This article is also available on <a href="https://dev.to/this-is-learning/a-basic-langchainjs-chain-with-prompt-template-structured-json-output-and-openai-ollama-llms-121p">dev.to with better source code syntax highlighting</a>.</p><h2>Prompt template + OpenAI model + JSON output</h2><p>In the example below, we implement the <code>reviewTextOpenAI</code> function with the following signature:</p><pre><code><code>/**
 * Reviews and corrects the input text using OpenAI's GPT-4o model.
 * 
 * @param instruction - Instructions to be given to the model.
 * @param inputText - The text to be reviewed and corrected.
 * @returns - The reviewed and corrected text, or undefined if the response is invalid.
 */
reviewTextOpenAI(instruction: string, inputText: string): Promise&lt;string | undefined&gt;</code></code></pre><p>Example function call and output:</p><pre><code><code>// Define the instruction and input text for the prompt
const instruction = "Fix the grammar issues in the following text.";
const inputText = "How to stays relevant as the developer in the world of ai?";

// Log the result of the review function (OpenAI version)
console.log(await reviewTextOpenAI(instruction, inputText));
// CONSOLE: How to stay relevant as a developer in the world of AI?</code></code></pre><p>The <code>reviewTextOpenAI</code> function does the following:</p><ul><li><p>Creates a prompt template.</p></li><li><p>Defines a JSON schema using Zod.</p></li><li><p>Creates an language model (GPT-4o) wrapper, that returns the response in the format we defined with our JSON schema. We use the <code>.withStructuredOutput</code> method to get JSON output from the model.</p></li><li><p>Connects the prompt template with the language model to create a chain.</p></li><li><p>Calls the chain with the provided <code>inputText</code> and <code>instruction</code></p></li></ul><p>Here is the complete code for the function (you can download it in a <a href="https://github.com/gergelyszerovay/aiboosted.dev/blob/main/a004/openai.ipynb">Jupyter Notebook</a>):</p><pre><code><code>/**
 * Reviews and corrects the input text using OpenAI's GPT-4o model.
 * 
 * @param instruction - Instructions to be given to the model.
 * @param inputText - The text to be reviewed and corrected.
 * @returns - The reviewed and corrected text, or undefined if the response is invalid.
 */
async function reviewTextOpenAI(instruction: string, inputText: string): Promise&lt;string | undefined&gt; {
  // Create a prompt template using the provided instruction and input text
  const prompt = PromptTemplate.fromTemplate(
`{instruction}
---
{inputText}`);

  // Initialize the OpenAI chat model with specified options
  const llm = new ChatOpenAI({
    modelName: "gpt-4o", // Use the GPT-4 model
    verbose: false, // Disable verbose logging
  });

  // Define the schema for the model's output, it contains the reviewed text  
  const reviewedTextSchema = z.object({
    reviewedText: z.string().describe("The reviewed text.") // The reviewed text must be a string
  });
  type ReviewedTextSchema = z.infer&lt;typeof reviewedTextSchema&gt;; // Infer the TypeScript type from the Zod schema

  // We expect structured JSON output, we achieve this using OpenAI's function calling feature
  const llmWithStructuredOutput = llm.withStructuredOutput(reviewedTextSchema, {
    method: "functionCalling",
    name: "withStructuredOutput"
  });  

  // Create a processing chain combining the prompt and the LLM
  const chain = prompt.pipe(llmWithStructuredOutput);

  // Invoke the chain with the instruction and input text, and wait for the response
  const response: ReviewedTextSchema = await chain.invoke({ instruction, inputText });

  // Return the reviewed text if present in the response, otherwise undefined  
  return response?.reviewedText;
}

// Define the instruction and input text for the prompt
const instruction = "Fix the grammar issues in the following text.";
const inputText = "How to stays relevant as the developer in the world of ai?";

// show the reviewed text returned by the LLM
console.log(await reviewTextOpenAI(instruction, inputText));</code></code></pre><p>Let's break down each part of the code. The function <code>reviewTextOpenAI</code> takes two parameters: an instruction and the input text. It returns a reviewed and corrected text or undefined if the response is invalid:</p><pre><code><code>async function reviewTextOpenAI(instruction: string, inputText: string): Promise&lt;string | undefined&gt; {</code></code></pre><p>We use the declarative LangChain Expression Language (LCEL) to compose chains. The first element of our chain is the <a href="https://js.langchain.com/v0.2/docs/concepts/#prompt-templates">prompt template</a> that has two parameters: <code>instruction</code> and <code>inputText</code>. We assign values to these parameters when we execute the chain.</p><pre><code><code>const prompt = PromptTemplate.fromTemplate(
`{instruction}
---
{inputText}`);</code></code></pre><p>We initialize the OpenAI chat model wrapper. We use the <code>gpt-4o</code> model and disable verbose logging. Learn more about the <code>ChatOpenAI</code> model wrapper <a href="https://js.langchain.com/v0.2/docs/integrations/chat/openai/">here</a>.</p><pre><code><code>const llm = new ChatOpenAI({
 modelName: "gpt-4o",
 verbose: false,
});</code></code></pre><p>We define the schema for the model's output using the <a href="https://zod.dev/">Zod</a> library. The schema specifies that the output should have a property <code>reviewedText</code>, which must be a string. Then, we use <code>z.infer</code> to create a TypeScript type from this schema. This ensures our code knows exactly what type of data to expect.</p><pre><code><code>const reviewedTextSchema = z.object({
 reviewedText: z.string().describe("The reviewed text.")
});
type ReviewedTextSchema = z.infer&lt;typeof reviewedTextSchema&gt;; // Infer the TypeScript type from the Zod schema</code></code></pre><p>We configure the model to produce structured JSON output using Langchain's <code>.withStructuredOutput()</code>&nbsp;<a href="https://js.langchain.com/v0.2/docs/how_to/structured_output/#the-.withstructuredoutput-method">method</a>:</p><pre><code><code>const llmWithStructuredOutput = llm.withStructuredOutput(reviewedTextSchema, {
 method: "functionCalling",
 name: "withStructuredOutput"
});  </code></code></pre><p>We create a processing chain that combines the prompt and the model configured for structured output. Learn more about Langchain's LCEL chains and the <code>pipe()</code> method <a href="https://js.langchain.com/v0.2/docs/how_to/sequence">here</a>.</p><pre><code><code>const chain = prompt.pipe(llmWithStructuredOutput);</code></code></pre><p>Finally, we invoke the processing chain with the instruction and input text, then wait for the response. We return the reviewed text if it is present in the response. Otherwise, we return undefined.</p><pre><code><code>const response: ReviewedTextSchema = await chain.invoke({ instruction, inputText });   
return response?.reviewedText;</code></code></pre><p>The OpenAI API requires an <a href="https://platform.openai.com/api-keys">API key</a>. In JupyterLab, we provide this key with the <code>OPENAI_API_KEY</code> environment variable, so we have to create a&nbsp;<code>.env</code>&nbsp;file with an OpenAI API key:</p><pre><code><code>OPENAI_API_KEY=[your OpenAI API key]</code></code></pre><p>Then we use the <code>import "https://deno.land/std@0.215.0/dotenv/load.ts";</code> statement for reading the&nbsp;<code>.env</code>&nbsp;file and set the environment variables.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Prompt template + Ollama model + JSON output</h2><p>Ollama-based models need a different approach for JSON output. LangChain's <code>.withStructuredOutput</code> doesn't support Ollama yet, so we use the <code>OllamaFunctions</code> wrapper's function calling feature.</p><p>Example function call and output:</p><pre><code><code>// Define the instruction and input text for the prompt
const instruction = "Fix the grammar issues in the following text.";
const inputText = "How to stays relevant as the developer in the world of ai?";

// Log the result of the review function (Ollama version)
console.log(await reviewTextOllama(instruction, inputText));
// CONSOLE: How to stay relevant as a developer in the world of AI?</code></code></pre><p>The steps are like those of the OpenAI implementations, with one difference: the method used to get JSON the output. The <code>reviewTextOllama</code> function does the following:</p><ul><li><p>Creates a prompt template.</p></li><li><p>Defines a JSON schema using Zod.</p></li><li><p>Creates an LLM (Ollama / Codellama) wrapper that returns the response in the format defined by our JSON schema. We use function calling to get JSON output from the model.</p></li><li><p>Connects the prompt template with the language model to create a chain.</p></li><li><p>Calls the chain with the given <code>inputText</code> and <code>instruction</code></p></li></ul><p>Here is the complete code for the function (you can download it in a <a href="https://github.com/gergelyszerovay/aiboosted.dev/blob/main/a004/ollama.ipynb">Jupyter Notebook</a>):</p><pre><code><code>/**
 * Processes a given text using a language model to review and correct it based on the provided instruction.
 * 
 * @param instruction - Instruction for the language model on how to process the text.
 * @param inputText - The text that needs to be reviewed and corrected.
 * @returns The reviewed text if successful, otherwise undefined.
 */
async function reviewTextOllama(instruction: string, inputText: string): Promise&lt;string | undefined&gt; {
  // Create a prompt template by combining the instruction and input text
  const prompt = PromptTemplate.fromTemplate(
`{instruction}
---
{inputText}`);

  // Define a schema for the expected output using zod
  const reviewedTextSchema = z.object({
    reviewedText: z.string().describe("The reviewed text.") // Define the structure and description of the reviewed text
  });
  type ReviewedTextSchema = z.infer&lt;typeof reviewedTextSchema&gt;; // Infer the TypeScript type from the zod schema

  // Initialize the language model with specific configuration
  const llm = new OllamaFunctions({
    baseUrl: "http://localhost:11434", // Base URL for the language model server
    model: "codellama:7b-code", // Specify the model to use
    verbose: false, // Disable verbose logging
  }).bind({
    functions: [
      {
        name: "storeResultTool", // Function name used in the language model
        description: "Gets the reviewed text", // Description of the function
        parameters: {
          type: "object", // Define the type of parameters expected by the function
          properties: zodToJsonSchema(reviewedTextSchema), // Convert zod schema to JSON schema
        },
      },
    ],
    function_call: {
      name: "storeResultTool", // Specify the function to be called
    },
  });

  // Create a processing chain: prompt -&gt; language model -&gt; JSON output parser
  const chain = prompt.pipe(llm).pipe(new JsonOutputFunctionsParser());

  // Invoke the chain with the instruction and input text
  const response = await chain.invoke({ instruction, inputText });

  // Return the reviewed text if available
  return response?.reviewedText;
}

// Define the instruction and input text for the prompt
const instruction = "Fix the grammar issues in the following text.";
const inputText = "How to stays relevant as the developer in the world of ai?";

// Log the result of the review function
console.log(await reviewTextOllama(instruction, inputText));</code></code></pre><p>Let's see how we set up the Ollama wrapper to use the <code>codellama</code> model with JSON response in our code.</p><pre><code><code>  // Initialize the language model with specific configuration
  const llm = new OllamaFunctions({
    baseUrl: "http://localhost:11434", // Base URL for the language model server
    model: "codellama:7b-code", // Specify the model to use
    verbose: false, // Enable verbose logging
  }).bind({
    functions: [
      {
        name: "storeResultTool", // Function name used in the language model
        description: "Gets the reviewed text", // Description of the function
        parameters: {
          type: "object", // Define the type of parameters expected by the function
          properties: zodToJsonSchema(reviewedTextSchema), // Convert zod schema to JSON schema
        },
      },
    ],
    function_call: {
      name: "storeResultTool", // Specify the function to be called
    },
  });</code></code></pre><p>When we create the Ollama wrapper (<code>OllamaFunctions</code>) , we pass a configuration object to it with the model's name and the <code>baseUrl</code> for the Ollama server.<br>We use the <code>.bind</code> function on the created <code>OllamaFunctions</code> instance to define the <code>storeResultTool</code> function. This function's parameter has the <code>reviewedTextSchema</code> schema, the schema for our expected response. The <code>function_call.name = 'storeResultTool'</code> configuration option forces the model send the response to the <code>storeResultTool</code> function.</p><p>We create a processing chain that combines the prompt and the model configured for structured output. This chain has an extra step compared to the OpenAI implementation: using the <code>JsonOutputFunctionsParser</code> to convert the JSON output string to an object:</p><pre><code><code>  // Create a processing chain: prompt -&gt; language model -&gt; JSON output parser
  const chain = prompt.pipe(llm).pipe(new JsonOutputFunctionsParser());</code></code></pre><h2>Summary</h2><p>Congrats on completing this tutorial! In the fourth part of the AI-Boosted Development series, I showed how to create a basic LLM chain using LangChain.js. We used prompt templates, got structured JSON output, and integrated with OpenAI and Ollama LLMs. In the next article, I will show how to generate a function that compares two strings character by character and returns the differences in an HTML string. It is going to be exciting, so make sure to <a href="https://aiboosted.dev/">subscribe</a>!</p><h2>&#128104;&#8205;&#128187;About the author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>, I worked as a data scientist and full-stack developer for many years, and I have been working as frontend tech lead, focusing on Angular-based frontend development. As part of my role, I'm constantly following how Angular and the frontend development scene in general are evolving.</p><p>Angular has been advancing very rapidly over the past few years, and in the past year, with the rise of generative AI, our software development workflows have also evolved rapidly. In order to closely follow the evolution of AI-assisted software development, I decided to start building AI tools in public, and publish my progress on <a href="https://aiboosted.dev/">AIBoosted.dev</a> , <a href="https://aiboosted.dev/">Subscribe here</a> &#128640;</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev/">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular, and how to build AI apps with AI, Typescript, React and Angular!</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Jupyter Lab IDE basics with Typescript and Deno]]></title><description><![CDATA[A step-by-step guide for Typescript developers]]></description><link>https://www.aiboosted.dev/p/jupyter-lab-ide-basics-with-typescript</link><guid isPermaLink="false">https://www.aiboosted.dev/p/jupyter-lab-ide-basics-with-typescript</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Tue, 18 Jun 2024 07:11:04 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!wcQD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wcQD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wcQD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!wcQD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!wcQD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!wcQD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wcQD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png" width="1456" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:76108,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wcQD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!wcQD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!wcQD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!wcQD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7cf7e1aa-ff51-421d-a09e-a0bc1504ef1f_1620x890.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In the <a href="https://www.aiboosted.dev/p/lets-build-ai-tools-typescript">introductory part</a> of this series, I summarized the recent changes that enable us to <strong>prototype and develop AI apps in Typescript easily.</strong></p><p>Haven&#8217;t read the first part? Here is what this project is all about: my goal in the coming months, and probably years, is to experiment with the new AI models and tools and show you everything I learn along the way. I'm going to &#8220;build in public&#8221; a lot and show you the important milestones of my journey. As a result, you&#8217;ll be able to apply this knowledge in your own practice.</p><p>In the <a href="https://www.aiboosted.dev/p/install-jupyter-lab-deno-typescript-prototyping">second article</a>, I showed you <strong>how to install the Jupyter Lab IDE and all the necessary tools</strong> for our rapid prototyping environment:</p><ul><li><p><a href="https://jupyter.org/">Jupyter Lab</a> web-based interactive development environment</p></li><li><p><a href="https://blog.jupyter.org/bringing-modern-javascript-to-the-jupyter-notebook-fc998095081e">Deno Typescript/Javascript kernel</a> for Jupyter notebooks, which enables us to create Jupyter Notebooks in Typescript</p></li><li><p><a href="https://ollama.com/">Ollama</a> for running LLMs locally</p></li><li><p><a href="https://js.langchain.com/docs/get_started/introduction">Langchain</a>, a framework for developing applications powered by language models</p></li></ul><p>If you&#8217;ve followed the steps in the second article, now you have a working Jupyter Lab environment with Typescript support, and a local Ollama installation for running LLMs locally.</p><p>In this article, <strong>I'll explain how to use Jupyter Lab, and show you a basic coding workflow in it.</strong></p><p>You can <a href="https://github.com/gergelyszerovay/aiboosted.dev/blob/main/a003/deno-demo.ipynb">download my sample Notebook</a> with all the examples.</p><h2>Jupyter Lab basics</h2><p>Jupyter Lab is an integrated development environment (IDE) for working with Jupyter notebooks, code, and data. Jupyter Notebooks contain live code, outputs, visualizations, and narrative text.</p><p>Jupyter Lab's user interface is similar to VSCode or JetBrains IDEs. It has two main parts: a "File browser", and a main work area with opened files:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oqrE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oqrE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png 424w, https://substackcdn.com/image/fetch/$s_!oqrE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png 848w, https://substackcdn.com/image/fetch/$s_!oqrE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png 1272w, https://substackcdn.com/image/fetch/$s_!oqrE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oqrE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png" width="1366" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1366,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oqrE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png 424w, https://substackcdn.com/image/fetch/$s_!oqrE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png 848w, https://substackcdn.com/image/fetch/$s_!oqrE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png 1272w, https://substackcdn.com/image/fetch/$s_!oqrE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50cbf8e3-0dc7-492f-b96b-c5ab0b7cbd30_1366x768.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can open and edit your source files from the "File browser". The root directory of the "File browser" is the directory, where you started Jupyter Lab.</p><p>The button with the plus icon above the "File browser" is the "Launcher". It provides shortcuts to create new notebooks, text files, terminals, consoles, and other tools:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FBro!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FBro!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png 424w, https://substackcdn.com/image/fetch/$s_!FBro!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png 848w, https://substackcdn.com/image/fetch/$s_!FBro!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png 1272w, https://substackcdn.com/image/fetch/$s_!FBro!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FBro!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png" width="1366" height="639" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:639,&quot;width&quot;:1366,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FBro!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png 424w, https://substackcdn.com/image/fetch/$s_!FBro!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png 848w, https://substackcdn.com/image/fetch/$s_!FBro!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png 1272w, https://substackcdn.com/image/fetch/$s_!FBro!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26454de4-2ab5-4c00-a942-0a42a6e95d74_1366x639.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The left sidebar contains these tools:</p><ul><li><p><strong>File Browser</strong>: Navigate and manage files</p></li><li><p><strong>Running Terminals and Kernels</strong>: Lists currently running terminals and kernels</p></li><li><p><strong>Extension Manager</strong>: Manage extensions to enhance the functionality of Jupyter Lab</p></li><li><p><strong>Command Palette</strong>: Quick access to commands, open it with <code>Ctrl+Shift+C</code></p></li></ul><p>In the "Command palette" you can see all Jupyter Lab commands and their shortcuts:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yjJF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yjJF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png 424w, https://substackcdn.com/image/fetch/$s_!yjJF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png 848w, https://substackcdn.com/image/fetch/$s_!yjJF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png 1272w, https://substackcdn.com/image/fetch/$s_!yjJF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yjJF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png" width="1364" height="642" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:642,&quot;width&quot;:1364,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yjJF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png 424w, https://substackcdn.com/image/fetch/$s_!yjJF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png 848w, https://substackcdn.com/image/fetch/$s_!yjJF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png 1272w, https://substackcdn.com/image/fetch/$s_!yjJF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fed9af1f9-bfe2-4a10-a146-1e3114f37108_1364x642.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2></h2><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Coding Workflow in Jupyter Lab</h2><h3><strong>Creating a New Notebook</strong></h3><p>Click the <code>+</code> icon in the left sidebar to open the "Launcher". Under "Notebook" select "Deno" to create a new notebook for TypeScript (or JavaScript). Save your notebook with <code>Ctrl+S</code>. Rename the file in the "File browser" by right clicking on it and selecting "Rename" (or press <code>F2</code>)</p><h3>Writing and Executing Code</h3><p>In the new notebook, you can write and execute TypeScript code within code cells. Each code cell lets you to enter and execute a snippet of code. For example, to display message, write:</p><pre><code><code>console.log('Hello Deno!')</code></code></pre><p>Execute the code by pressing <code>Shift+Enter</code>. The output will appear below the cell immediately:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4rXE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4rXE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png 424w, https://substackcdn.com/image/fetch/$s_!4rXE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png 848w, https://substackcdn.com/image/fetch/$s_!4rXE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png 1272w, https://substackcdn.com/image/fetch/$s_!4rXE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4rXE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png" width="927" height="65" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:65,&quot;width&quot;:927,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4rXE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png 424w, https://substackcdn.com/image/fetch/$s_!4rXE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png 848w, https://substackcdn.com/image/fetch/$s_!4rXE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png 1272w, https://substackcdn.com/image/fetch/$s_!4rXE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46fdff6e-c454-4dfe-b877-076e81b61cb0_927x65.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Kernels</h3><p>A kernel executes the code in Jupyter Notebooks. It maintains the state of variables, imports, and other runtime information between cell executions. Each notebook has its own kernel.</p><h3>Restarting a Kernel</h3><p>The notebook's kernel memory can get cluttered with variables from previous executions. Restarting clears this memory, providing a clean slate. Restarting also reloads updated modules.</p><p>You can restart the kernel from the "Kernel" menu.</p><h3>Markdown Cells</h3><p>Markdown cells add text and documentation to your notebook, making it more informative. To add a Markdown cell, click the <code>+</code> button in the toolbar to insert a new cell, then change the cell type to "Markdown" using the dropdown menu or by pressing <code>Esc + M</code>. To create a heading and formatted text, write:</p><pre><code><code>### Outputs

You can write to the output cells using JavaScript's standard output functions, such as `console.log`</code></code></pre><p>To render the Markdown, press <code>Shift+Enter</code>:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MiCN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MiCN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png 424w, https://substackcdn.com/image/fetch/$s_!MiCN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png 848w, https://substackcdn.com/image/fetch/$s_!MiCN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png 1272w, https://substackcdn.com/image/fetch/$s_!MiCN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MiCN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png" width="964" height="104" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2bd29741-202a-410d-8fec-2216395d5bad_964x104.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:104,&quot;width&quot;:964,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MiCN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png 424w, https://substackcdn.com/image/fetch/$s_!MiCN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png 848w, https://substackcdn.com/image/fetch/$s_!MiCN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png 1272w, https://substackcdn.com/image/fetch/$s_!MiCN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2bd29741-202a-410d-8fec-2216395d5bad_964x104.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3>Importing Packages in Deno</h3><p>In Deno, package import uses a URL-based syntax:</p><pre><code><code>import { escape } from "https://deno.land/std/html/mod.ts";</code></code></pre><p>Deno has a standard library available at <a href="https://deno.land/std">https://deno.land/std</a>. The line above imports the <code>escape()</code> function from the <code>std</code> library. To specify the version, use:</p><pre><code><code>import { escape } from "https://deno.land/std@0.224.0/html/mod.ts";</code></code></pre><p>Deno also has a repository for third-party modules at <a href="https://deno.land/x">https://deno.land/x</a>. To import the <code>html()</code> function, use:</p><pre><code><code>import { html } from "https://deno.land/x/display/mod.ts";</code></code></pre><p>Deno can import <code>npm</code> modules with the <code>npm:</code> prefix. For example to import <a href="https://www.npmjs.com/package/zod">zod</a>:</p><pre><code><code>import { z } from "npm:zod";</code></code></pre><h3>Writing in Output cells</h3><p>You can output from a Code cell by:</p><ul><li><p>Using <code>console.*</code> functions</p></li><li><p>With the <code>html</code> function (HTML output)</p></li><li><p>With the <code>md</code> function (Markdown output)</p></li></ul><p>Example:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oe3v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oe3v!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png 424w, https://substackcdn.com/image/fetch/$s_!oe3v!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png 848w, https://substackcdn.com/image/fetch/$s_!oe3v!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png 1272w, https://substackcdn.com/image/fetch/$s_!oe3v!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oe3v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png" width="961" height="305" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:305,&quot;width&quot;:961,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oe3v!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png 424w, https://substackcdn.com/image/fetch/$s_!oe3v!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png 848w, https://substackcdn.com/image/fetch/$s_!oe3v!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png 1272w, https://substackcdn.com/image/fetch/$s_!oe3v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87c4ed79-5fb4-4923-a8a3-a126f1e14edd_961x305.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Environment variables</h3><p>In Deno or Node, we use environment variables for settings. For example, Langchain's OpenAI wrappers get the key from <code>OPENAI_API_KEY</code>. Set it in a <code>.env</code> file:</p><pre><code><code>OPENAI_API_KEY=[insert your key here]</code></code></pre><p>Load this file with:</p><pre><code><code>import "https://deno.land/std@0.215.0/dotenv/load.ts";</code></code></pre><p>Show the value with:</p><pre><code><code>console.log(Deno.env.get("OPENAI_API_KEY"));</code></code></pre><h3>Importing local files</h3><p>Deno supports local file imports. After changing an external file, restart the kernel to reload it:</p><pre><code><code>import { f1 } from './1.ts';

f1();</code></code></pre><p>Deno also supports importing JSON files:</p><pre><code><code>import jsonData from "./data.json" with { type: "json" };
console.log(jsonData);</code></code></pre><h2>Summary</h2><p>Congrats on completing this little tutorial! We've covered the basics of Jupyter Lab, so in future articles, we can focus on AI-related topics. In the next article, I'll explain how I built the "Text reviewer app" prototype. It&#8217;s going to be exciting, so make sure to <a href="https://aiboosted.dev/">subscribe</a>!</p><h2>&#128104;&#8205;&#128187;About the author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>, I worked as a data scientist and full-stack developer for many years, and I have been working as frontend tech lead, focusing on Angular-based frontend development. As part of my role, I'm constantly following how Angular and the frontend development scene in general are evolving.</p><p>Angular has been advancing very rapidly over the past few years, and in the past year, with the rise of generative AI, our software development workflows have also evolved rapidly. In order to closely follow the evolution of AI-assisted software development, I decided to start building AI tools in public, and publish my progress on <a href="https://aiboosted.dev/">AIBoosted.dev</a> , <a href="https://aiboosted.dev/">Subscribe here</a> &#128640;</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev/">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular, and how to build AI apps with AI, Typescript, React and Angular!</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Create an AI prototyping environment using Jupyter Lab IDE with Typescript, LangChain.js and Ollama for rapid AI prototyping]]></title><description><![CDATA[A step-by-step guide for Typescript developers]]></description><link>https://www.aiboosted.dev/p/install-jupyter-lab-deno-typescript-prototyping</link><guid isPermaLink="false">https://www.aiboosted.dev/p/install-jupyter-lab-deno-typescript-prototyping</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Thu, 02 May 2024 07:08:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Wgdz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Wgdz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Wgdz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!Wgdz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!Wgdz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!Wgdz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Wgdz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png" width="1456" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:86109,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Wgdz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!Wgdz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!Wgdz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!Wgdz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7134e265-dfdd-466c-a05c-fdeced82334c_1620x890.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Intro</h2><p>This is the 2nd part of my article series describing my journey in AI-assisted development.</p><p>In the <a href="https://www.aiboosted.dev/p/lets-build-ai-tools-typescript">introductory part</a> of this series, I summarized the recent changes that enable us to prototype and develop AI apps in Typescript easily.</p><p>Haven&#8217;t read the first part? Here is what this project is all about: my goal in the coming months, and probably years, is to experiment with the new AI models &amp; tools and show you everything I learn along the way. I'm going to &#8220;build in public&#8221; a lot and show you the important milestones of my journey. As a result, you&#8217;ll be able to apply this knowledge in your own practice.</p><p>In this article, I show you how to install the Jupyter Lab IDE and all the necessary tools for our rapid prototyping environment. You&#8217;ll only need to set up this once, and I&#8217;ll explain everything step by step. After having this setup, you&#8217;ll be able to follow along with me and build all my future experiments. (So it&#8217;s worth the effort :) ).</p><p>For example, in the next parts of the article series, I'm going to explain you in more detail how I built the "Text reviewer app":</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Bd9J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Bd9J!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png 424w, https://substackcdn.com/image/fetch/$s_!Bd9J!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png 848w, https://substackcdn.com/image/fetch/$s_!Bd9J!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png 1272w, https://substackcdn.com/image/fetch/$s_!Bd9J!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Bd9J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png" width="1066" height="355" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:355,&quot;width&quot;:1066,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Bd9J!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png 424w, https://substackcdn.com/image/fetch/$s_!Bd9J!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png 848w, https://substackcdn.com/image/fetch/$s_!Bd9J!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png 1272w, https://substackcdn.com/image/fetch/$s_!Bd9J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2a28a2-68c0-4214-8768-53f8a38838ae_1066x355.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>My workflow and tools</h2><p>I use the following workflow for AI tool development:</p><ul><li><p>a Typescript- and Jupyter Lab-based AI tool prototyping environment for quickly prototyping AI tools</p></li><li><p>I extend the code of the prototype, create a store from it. In React apps I use the <a href="https://docs.pmnd.rs/zustand/getting-started/introduction">Zustand</a> React store, in Angular I use <a href="https://ngrx.io/guide/signals/signal-store">NgRx's SignalStore</a>, in Jupyter Lab, I use <a href="https://docs.pmnd.rs/zustand/getting-started/introduction">Zustand</a> Vanilla stores. These store solutions have a lot in common, so I can reuse the same selector, effect and updater code.</p></li><li><p>I build an Angular or React app or smart component that uses the store in an Nx monorepo. I use the <a href="https://ui.shadcn.com/">Shadcn UI</a> in React apps, and the Shadcn-based <a href="https://www.spartan.ng/documentation/installation">spartan/ui</a> in Angular apps, so I can use almost the same app architecture both in Angular and React.</p></li></ul><p>These are the most important tools of my Typescript-based AI tool prototyping environment:</p><ul><li><p><a href="https://jupyter.org/">Jupyter Lab</a> web-based interactive development environment</p></li><li><p><a href="https://blog.jupyter.org/bringing-modern-javascript-to-the-jupyter-notebook-fc998095081e">Deno Typescript/Javascript kernel</a> for Jupyter notebooks, it enables us to create Jupyter Notebooks in Typescript</p></li><li><p><a href="https://ollama.com/">Ollama</a> for running LLMs locally</p></li><li><p><a href="https://js.langchain.com/docs/get_started/introduction">Langchain</a>, a framework for developing applications powered by language models</p></li></ul><p>I'm going to also explain how to install and what are the most important commands of the following package managers:</p><ul><li><p><a href="https://chocolatey.org/">Chocolatey</a> Windows software management solution, we use this for installing Python and Deno</p></li><li><p><a href="https://pip.pypa.io/en/stable/">Package installer for Python (pip)</a>, we use this for installing the Python-based packages, such as Jupyter Lab, and we'll use this for installing other Python-based tools like the <a href="https://www.trychroma.com/">Chroma DB</a> vector database</p></li></ul><p>If you follow the steps of this article, you'll be able to run the prototype of the &#8220;Text reviewer app&#8221; in your local Jupyter Lab IDE (<a href="https://github.com/gergelyszerovay/aiboosted.dev/blob/article-001/juptyer-notebooks/text-reviewer.ipynb">downloadable Jupyter notebook</a>), and you&#8217;ll also be able to start creating your own app prototypes based on my examples:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!I5g-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!I5g-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png 424w, https://substackcdn.com/image/fetch/$s_!I5g-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png 848w, https://substackcdn.com/image/fetch/$s_!I5g-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!I5g-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!I5g-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png" width="1456" height="1245" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1245,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!I5g-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png 424w, https://substackcdn.com/image/fetch/$s_!I5g-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png 848w, https://substackcdn.com/image/fetch/$s_!I5g-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!I5g-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd35969e3-e35f-462f-8ce0-27332dbbae74_1481x1266.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Sidenote: what are the benefits of using Jupyter notebooks with Typescript for prototyping?</h2><p>Initially, when I started doing my experiments, I created a simple Angular app, and I wrote all the experimental AI code in the main component's constructor, and showed the output on the console. I found that this approach worked well with simple experiments, when I have a clear plan on how I want to build something, and I don't want to experiment with many different models, prompts or agent networks. However, in case of more complex experiments, this simple approach has its limitations:</p><ul><li><p>The whole experimental code runs all at once, if I would like to split it up into multiple parts and run the parts separately, I need UI controls, or I have to comment/uncomment parts of the code. In Jupyter, I can split the code into multiple cells, and run all the cells, or some of the cells separately</p></li><li><p>Package management: in Deno, this is automatic, I just write an <code>import</code> statement, and Deno downloads and stores the NPM or Deno package automatically in the background. There is no need for the <code>package.json</code> or <code>node_modules</code> directory. Once a specific version of a package is downloaded by Deno, we can use it from all the Jupyter notebooks</p></li><li><p>We can simply create a copy of a notebook in Jupyter. If I experiment with a new AI tool, I can just clone my notebook and change for example the prompt or the model. Then I can run my notebooks in parallel, and compare the results side-by-side</p></li><li><p>In Jupyter, if I need customized agents with RAG (Retrieval-Augmented Generation, we use this technique for optimizing the output of an LLM by extending its knowledge by additional information external to its training source), I can directly access the local files containing the additional knowledge. We can also do this in an Angular / React app, but it's more challenging, as we need some kind of a server to access the local files.</p></li><li><p>The Typescript code we create with Jupyter/Deno is portable: we can use it in a frontend project, but it's also possible to use the code in the backend.I believe that for experimenting purposes, Jupyter and Deno are perfect, while in the prototyping phase, we should create an Angular or React app. These prototypes can effectively support a single user's or smaller team's work. However, when our goal is to turn the prototype into a real service, we need to move at least some parts of the code to the backend in order to keep the resources and costs under control (e.g. API access and fees).</p></li><li><p>And finally, the biggest benefit of using Jupyter notebooks in my opinion: it is like having an interactive article, you have the code itself, the output of the code and the explanations (in Markdown) in one place (next to each other), so it is perfect for e.g. communicating ML / AI concepts to other developers.</p></li></ul><p>Now let&#8217;s get started with building our rapid prototyping environment! Remember, you&#8217;ll only need to set this up once :).</p><h2>Setup: Chocolatey</h2><h3>Installing Chocolatey CLI (Windows)</h3><p>As I mentioned, Chocolatey is a software management solution for Windows, we'll use this for installing Python and Deno. We need Python for installing and running the Jupyter Lab IDE, and Deno provides the Typescript language support for Jupyter Lab.</p><p>If you use Linux, skip these steps, and jump to the installing Python and Deno &#8220;On Linux" section.</p><p>To install Chocolatey, execute the following command from an elevated Command Prompt (<a href="https://docs.chocolatey.org/en-us/choco/setup#installing-chocolatey-cli">source</a>):</p><pre><code><code>@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "[System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))" &amp;&amp; SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"</code></code></pre><p>This command downloads and runs the remote <a href="https://community.chocolatey.org/install.ps1">install1.ps</a> script on your computer, so you should check its contents to ensure safety before running it.</p><p>Related article: <a href="https://docs.chocolatey.org/en-us/choco/setup#installing-chocolatey-cli">Chocolatey docs: Installing Chocolatey CLI</a></p><h3>The most important Chocolatey CLI commands</h3><ul><li><p><code>choco list -localonly</code>: list the locally installed packages</p></li><li><p><code>choco install PackageName</code>: installs a package (you can browse the available packages in the <a href="https://community.chocolatey.org/packages">Community Package Repository</a>)</p></li><li><p><code>choco upgrade PackageName</code>: upgrades a package</p></li><li><p><code>choco uninstall PackageName</code>: uninstalls a package</p></li><li><p><code>choco outdated</code>: list the locally installed packages that have a newer version</p></li></ul><h2>Setup: Installing Python and Deno</h2><h3>On Windows</h3><p>In the elevated Command Prompt, run <code>choco install python</code> for installing <a href="https://community.chocolatey.org/packages/python/3.12.2">Python</a>. Then run the <code>refreshenv</code> command for refreshing the <code>PATH</code> and other environment variables. Now if you run <code>python --version</code>, it returns the version of Python.</p><p>Run <code>python -m pip install -U pip</code> for upgrading the Python package manager (pip).</p><p>Then install the Deno Typescript Kernel:</p><pre><code><code>choco install deno</code></code></pre><h3>On Linux</h3><p>On Linux, you can use your distribution's package manager for installing Python. Then run <code>python -m pip install -U pip</code> for upgrading the Python package manager (pip).</p><p>Then install the Deno Typescript Kernel and the Pnpm package manager:</p><pre><code><code>curl -fsSL https://deno.land/install.sh | sh</code></code></pre><p>You should check the contents of these remote script files to ensure safety before running them.</p><p>Related article:</p><ul><li><p><a href="https://docs.deno.com/runtime/manual/getting_started/installation">Deno installation</a></p></li></ul><h3>Creating a Python virtual environment (venv)</h3><p>From this point on, the installation steps are very similar on both Windows and Linux, just use <code>/home/YourUser/</code> instead of <code>C:\Users\YourUser\</code> as your home directory.</p><p>A virtual environment contains a set of installed packages. I suggest creating a separate virtual environment for each Python-based project on your computer, so we create a new virtual environment for JupyterLab and Deno. You can create additional virtual environments later.</p><p>Open a Command Prompt (non-elevated), and create a new directory for the virtual environments: <code>mkdir C:\Users\YourUser\venv</code>, then create the new virtual environment with the following command: <code>python -m venv C:\Users\YourUser\venv\jupyter-deno</code></p><p>We have to activate the virtual environment before we install packages into it with the <code>C:\Users\YourUser\venv\jupyter-deno\Scripts\activate</code> command. After we executed this script, the prompt changed to <code>(jupyter-deno) C:\</code>.</p><p>Related article: <a href="https://docs.python.org/3/library/venv.html">Python documentation: Creation of virtual environments</a></p><h3>The most important Python package manager (pip) commands</h3><ul><li><p><code>pip list</code>: list the locally installed packages</p></li><li><p><code>pip install PackageName</code>: installs a package (you can browse the available packages in the <a href="https://pypi.org/">Python Package Index</a>)</p></li><li><p><code>pip install --upgrade PackageName</code>: upgrades a package</p></li><li><p><code>pip uninstall PackageName</code>: uninstalls a package</p></li><li><p><code>pip list --outdated</code>: list the locally installed packages that have a newer version</p></li></ul><h2>Setup: Installing JupyterLab with the Deno Kernel</h2><p>Inside the activated environment, we install Jupyter Lab: <code>pip install jupyterlab</code>, then we add the Deno kernel to Jupyter with the <code>deno jupyter --unstable --install</code> command.</p><p>Hidden files are not visible in Jupyter Lab by default, so for example we can't see the <code>.env</code> files in Jupyter Lab. We use the <code>.env</code> files e.g. for storing the key for OpenAI's API. To display these files in Jupyter Lab's file manager, we have to generate a new Jupyter config file with the following commands:</p><pre><code><code>mkdir C:\Users\YourUser\.jupyter
cd C:\Users\YourUser\.jupyter
jupyter server --generate-config</code></code></pre><p>The last command generates a <code>C:\Users\YourUser\.jupyter\jupyter_server_config.py</code> file. Open this file and set <code>ContentsManager.allow_hidden = True</code>. Restart Jupyter Lab, and now you can show or hide hidden files in Jupyter Lab using the <code>Show Hidden Files</code> menu item in the <code>View</code> menu.</p><p>As the last step of the installation process, create a directory for the Jupyter notebooks and start Jupyter Lab:</p><pre><code><code>mkdir C:\Users\YourUser\jupyter
cd C:\Users\YourUser\jupyter
jupyter lab</code></code></pre><p>Related articles:</p><ul><li><p><a href="https://blog.jupyter.org/bringing-modern-javascript-to-the-jupyter-notebook-fc998095081e">Bringing Modern JavaScript to the Jupyter Notebook</a></p></li><li><p><a href="https://jupyterlab.readthedocs.io/en/latest/user/files.html#displaying-hidden-files">JupyterLab docs: Displaying Hidden files</a></p></li></ul><h2>Setup: Installing and using Ollama</h2><h3>Installing Ollama and a model</h3><p>As I mentioned before, I use Ollama for running local LLM models.</p><p>You can download Ollama for Windows <a href="https://ollama.com/download/windows">here</a> and find the model library <a href="https://ollama.com/library">here</a>. After installing Ollama, I suggest you to download the <a href="https://ollama.com/library/codellama:7b-code">codellama:7b-code</a> model, it's great for testing purposes: <code>ollama pull codellama:7b-code</code>. This is a small model, requiring about 4 GB of RAM. I use this model on my older laptop that has only 16 GB RAM and a dual core CPU, and it gives responses with an acceptable speed. I use this local model in the prototype of the &#8220;Text reviewer app&#8221;, too.</p><h3>The most important Ollama commands:</h3><ul><li><p><code>ollama list</code> lists the downloaded models</p></li><li><p><code>ollama pull ModelName:version</code> downloads an LLM model</p></li><li><p><code>ollama serve</code> runs the Ollama server in the background</p></li></ul><h2>Try out our first AI prototype with a local model</h2><p>If you haven't started Jupyter Lab yet, activate your virtual env and start Jupyter Lab inside it with the <code>jupyter lab</code> command. You can open the UI in your browser: <a href="http://localhost:8888/">http://localhost:8888/</a>. Start Ollama with <code>ollama serve</code>.</p><p>Download the <a href="https://github.com/gergelyszerovay/aiboosted.dev/blob/article-001/juptyer-notebooks/text-reviewer.ipynb">notebook containing the app's prototype</a> into the <code>C:\Users\YourUser\jupyter</code> directory. Select the downloaded notebook from JupyterLab's file browser (left panel on the screen), then select the "Clear Outputs of All Cells" from the "Edit" menu to remove all the saved outputs from the cell, then click on the "Run all cells" menu item from the "Run" menu. After some processing, the results of the comparison should be visible above the last cell:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OhzB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OhzB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png 424w, https://substackcdn.com/image/fetch/$s_!OhzB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png 848w, https://substackcdn.com/image/fetch/$s_!OhzB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png 1272w, https://substackcdn.com/image/fetch/$s_!OhzB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OhzB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png" width="627" height="46" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:46,&quot;width&quot;:627,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OhzB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png 424w, https://substackcdn.com/image/fetch/$s_!OhzB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png 848w, https://substackcdn.com/image/fetch/$s_!OhzB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png 1272w, https://substackcdn.com/image/fetch/$s_!OhzB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f80fe9-f410-4c86-9ec3-585ddaadff5a_627x46.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2>What&#8217;s next</h2><p>In the following parts of my article series, I'm going to show you:</p><ul><li><p>How to use the Jupyter Lab IDE and the Deno Javascript/Typescript runtime that executes the Typescript code in a notebook</p></li><li><p>How the "Text reviewer app" prototype (screenshot above) works</p></li><li><p>The basics of LLMs and LangChain.js. (LangChain enables us to easily switch between the different LLM models and provider APIs, like the OpenAI platform API or the local Ollama API.)</p></li><li><p>How to create the state and store of the "Text reviewer app" based on the prototype, and using AI for code generation, and</p></li><li><p>How to build a React and an Angular app that uses the above mentioned store</p></li></ul><h2>Summary</h2><p>Congrats on completing this little tutorial! I know that it wasn&#8217;t the most exciting process you have ever followed along. But trust me, now you have a powerful prototyping environment for experimenting with building AI tools. It&#8217;s like getting all the ingredients needed for a great meal (and building a kitchen at the same time :D ). In the following articles, we are going to start cooking, so stay tuned for the fun part!</p><p>In the next two articles, I'm going to explain how to use the JupyterLab IDE and the Deno Javascript / Typescript runtime, and I&#8217;m going to show you in detail how the &#8220;Text reviewer app&#8221; prototype works.</p><h2>&#128104;&#8205;&#128187;About the author</h2><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>, I worked as a data scientist and full-stack developer for many years, and I have been working as frontend tech lead, focusing on Angular-based frontend development. As part of my role, I'm constantly following how Angular and the frontend development scene in general is evolving.</p><p>Angular has advancing very rapidly over the past few years, and in the past year, with the rise of generative AI, our software development workflows have also evolved rapidly. In order to closely follow the evolution of AI-assisted software development, I decided to start building AI tools in public, and publish my progress on <a href="https://aiboosted.dev/">AIBoosted.dev</a> , <a href="https://aiboosted.dev/">Subscribe here</a> &#128640;</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev/">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular, and how to build AI apps with AI, Typescript, React and Angular!</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Let’s build AI-tools with the help of AI and Typescript!]]></title><description><![CDATA[Follow along my journey in AI-boosted development]]></description><link>https://www.aiboosted.dev/p/lets-build-ai-tools-typescript</link><guid isPermaLink="false">https://www.aiboosted.dev/p/lets-build-ai-tools-typescript</guid><dc:creator><![CDATA[Gergely Szerovay]]></dc:creator><pubDate>Tue, 16 Apr 2024 06:01:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!d5KA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!d5KA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!d5KA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!d5KA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!d5KA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!d5KA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!d5KA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png" width="1456" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:71274,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!d5KA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png 424w, https://substackcdn.com/image/fetch/$s_!d5KA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png 848w, https://substackcdn.com/image/fetch/$s_!d5KA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png 1272w, https://substackcdn.com/image/fetch/$s_!d5KA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07eb7f4-1ced-43f7-9eed-5a0ef38cd1ab_1620x890.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>My background and why you should care</h3><p>I've been a developer for more than 30 years now, and I've never been so excited about the newest technological advancements. We live in a day and age that opens up so many possibilities that I could have never imagined before. I'm talking about <strong>developing AI tools</strong>.</p><p>Let's start with a simple example. Are there any non-native speaker readers here? If yes, this scenario might be familiar to you: let&#8217;s say you write an article, and ask an <strong>LLM (large language models, for example ChatGPT)</strong> if your text is grammatically correct or not.</p><p>The LLM answers with an updated version of our text. Then, if we ask it to list out the changes compared to the original text, it returns wrong or incomplete results (in many cases). These models can generate textual outputs or improve a given text's quality, but they're not so capable of performing algorithmic tasks, since they merely process our prompts and try to predict what answer we&#8217;d expect. They do not "think", they have no human-like logical capabilities. In order to solve this issue, I created a small AI tool to perform this comparison task in <strong>two simple steps</strong>:</p><ul><li><p>In the first step, the user can enter a text, and then the tool asks an LLM model to verify and improve it, and</p></li><li><p>In the second step, the tool compares the LLM's result with the original text, and visualizes the changes.</p></li></ul><p>Nowadays, I can develop apps like this in Typescript easily, as the barrier is lower than ever before, much lower. We <strong>only need Typescript development skills for developing AI-tool prototypes</strong>, additionally, if you are an experienced frontend developer, you can easily convert your prototypes into real applications with a user interface. This is the prototype of the <em>Text reviewer app</em> in the <strong>Jupyter Lab IDE</strong> (<a href="https://github.com/gergelyszerovay/aiboosted.dev/blob/article-001/juptyer-notebooks/text-reviewer.ipynb">downloadable Jupyter notebook</a>):</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SXcp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SXcp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png 424w, https://substackcdn.com/image/fetch/$s_!SXcp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png 848w, https://substackcdn.com/image/fetch/$s_!SXcp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!SXcp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SXcp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png" width="1456" height="1245" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1245,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SXcp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png 424w, https://substackcdn.com/image/fetch/$s_!SXcp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png 848w, https://substackcdn.com/image/fetch/$s_!SXcp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!SXcp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F666a37d1-18b4-4626-877a-f6464cc043ad_1481x1266.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Never heard of Jupyter Lab? No problem, let me explain.</h3><p>Jupyter Lab applies so-called notebooks, the screenshot above contains the notebook of the <em>Text reviewer app</em>&#8217;s code. A Jupyter Notebook contains Typescript code cells and Markdown cells. We can execute Typescript code cells instantly by pressing Control+Enter, there is no building time. After the execution is completed, Jupyter Lab shows us the output, as you can see on the screenshot.</p><p>Based on this prototype, we can of course build a real app. I built both an Angular and a React app, this is how they look like:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gDfl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gDfl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png 424w, https://substackcdn.com/image/fetch/$s_!gDfl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png 848w, https://substackcdn.com/image/fetch/$s_!gDfl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png 1272w, https://substackcdn.com/image/fetch/$s_!gDfl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gDfl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png" width="1066" height="355" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:355,&quot;width&quot;:1066,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gDfl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png 424w, https://substackcdn.com/image/fetch/$s_!gDfl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png 848w, https://substackcdn.com/image/fetch/$s_!gDfl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png 1272w, https://substackcdn.com/image/fetch/$s_!gDfl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3ba91e8-ba8a-4b9e-a1b4-2ffa46e5a040_1066x355.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Next to showing you a quick example, I do believe that this little app is actually useful: as a non-native speaker, my goal is to constantly improve my language skills. And this was just a very simple little app I built in some hours. I hope now you can better see the potential of building your own AI tools. The possibilities are endless.</p><h3>Follow along my journey, let&#8217;s learn together</h3><p>My goal with this article series is to show you how you can do similar experiments and apps. I'm going to show you all the tools and the mindset that's needed to build AI tools on your own.</p><p>In the next parts of the article series, I'm going to explain you how I built the <em>Text reviewer app</em> in more details.</p><p>Some of the next topics:</p><ul><li><p>How to install the Jupyter Lab IDE with all the required tools for quick AI prototyping in Typescript</p></li><li><p>How to use the Jupyter Lab IDE and the Deno Javascript/Typescript runtime that executes the Typescript code in a notebook</p></li><li><p>The basics of LLMs and LangChain.js</p></li><li><p>How the <em>Text reviewer app</em> prototype works</p></li><li><p>How to create the state and store of the <em>Text reviewer app</em> based on the prototype, and using AI for code generation, and</p></li><li><p>How to build a React and an Angular app that uses the above mentioned store</p></li></ul><p>After discussing these topics, I'm going to continue the series with building new, more complex AI apps. I'm also going to research and share <strong>AI-assisted software development workflows</strong>, and show you how to build <strong>custom</strong> <strong>AI tools that support these workflows</strong>. My goal in the coming months (probably in years) is to experiment with the new AI models and tools and document and show you everything I learn along the way. I'm going to build in public a lot, you should follow me on <a href="https://aiboosted.dev/">AIBoosted.dev</a> or on my social media accounts (<a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a>).</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>What changes have made it easier to build AI app prototypes quickly in TypeScript?</h3><p>If you are curious about the tech background (or in other words, here is the answer to the &#8220;Why now?&#8221; question), these are the recent advancements that have made AI-boosted development so much easier.</p><p>The most popular programming language for data scientists has traditionally been Python, so many AI and ML tools had Python integration only. Therefore, if someone wanted to try out or learn AI or ML tools, first they had to learn Python. Today, this has changed significantly as <strong>more and more tools contain Javascript and Typescript support.</strong></p><p>One of these tools is <a href="https://js.langchain.com/docs/get_started/introduction">LangChain.js</a>. LangChain is a framework for developing applications powered by language models. In 2023, they introduced the support of multiple JS environments, so now we can use LangChain.js in the browsers and with the Deno Javascript runtime.</p><p>Another one of these tools is <a href="https://jupyter.org/">Jupyter Lab</a>. Jupyter Lab is an interactive development environment, and as of 2023, it supports Typescript and Javascript Notebooks with the help of Deno. It means that now we can use Jupyter Notebooks to speed up our software prototyping and development processes. Don&#8217;t worry, I&#8217;m going to explain how in the next article. At this point, you must believe me :)</p><p>So we can create Typescript prototypes in Jupyter Lab, for example, if we would like to create an AI tool, we don't have to create a full-fledged React or Angular application first. We can design and test the AI tool in Jupyter Lab, then move its source code into our React or Angular application.</p><h3>What are the benefits of joining me on this journey?</h3><ul><li><p>Along the way, we can closely follow the evolution of LLMs and the development workflows, and gain experience with using and building AI tools. This will give you a competitive advantage.</p></li><li><p>AI-boosted development: with custom AI tools, you can create workflows that help you perform your daily development tasks more efficiently</p></li></ul><h3>The tech stack</h3><p>In the second part of the series, we are going to get a bit more technical: we are going to install a Typescript-based AI tool prototyping environment. These are the most important parts of it:</p><ul><li><p><a href="https://jupyter.org/">Jupyter Lab</a> web-based interactive development environment</p></li><li><p><a href="https://blog.jupyter.org/bringing-modern-javascript-to-the-jupyter-notebook-fc998095081e">Deno Typescript/Javascript kernel</a> for Jupyter notebooks, it enables us to create Jupyter Notebooks in Typescript</p></li><li><p><a href="https://ollama.com/">Ollama</a> for running LLMs locally</p></li><li><p><a href="https://js.langchain.com/docs/get_started/introduction">Langchain</a>, a framework for developing applications powered by language models</p></li></ul><p>I&#8217;m also going to explain how to install these package managers and what are the most important commands for them:</p><ul><li><p><a href="https://chocolatey.org/">Chocolatey</a> Windows software management solution, we use this for installing Python and Deno</p></li><li><p><a href="https://pip.pypa.io/en/stable/">Package installer for Python (pip)</a>, we use this for installing the Python-based packages, such as Jupyter Lab, and we're going to use this for installing other Python-based tools like the <a href="https://www.trychroma.com/">Chroma DB</a> vector database</p></li><li><p><a href="https://pnpm.io/">Pnpm</a> for install Javascript/Typescript packages, like LangChain.js</p></li></ul><h3>Summary</h3><p>So welcome to the AI-boosted Development article series, dear reader! I hope you enjoyed this first chapter! I'm going to show you the important milestones of my journey, and along the way I'm going to teach you how you can apply this knowledge in your own practice.</p><h3>&#128104;&#8205;&#128187; About the author</h3><p>My name is <a href="https://www.linkedin.com/in/gergelyszerovay/">Gergely Szerovay</a>, I worked as a data scientist and full-stack developer for many years, and I have been working as frontend tech lead for three years, focusing on Angular based frontend development. As part of my role, I'm constantly following how Angular and the frontend development scene in general is evolving.</p><p>Angular has evolved very rapidly over the past few years, and in the past year, with the rise of generative AI, our software development workflows have also evolved rapidly. In order to closely follow the evolution of AI-assisted software development, I decided to start building AI tools in public, and publish my progress on <a href="https://aiboosted.dev/">AIBoosted.dev</a> , <a href="https://aiboosted.dev/">Subscribe here</a> &#128640;</p><p>Follow me on <a href="https://www.angularaddicts.com/">Substack (Angular Addicts)</a>, <a href="https://aiboosted.dev/">Substack (AIBoosted.dev)</a>, <a href="https://medium.com/@GergelySzerovay">Medium</a>, <a href="https://dev.to/gergelyszerovay">Dev.to</a>, <a href="https://twitter.com/GergelySzerovay">X</a> or <a href="https://www.linkedin.com/in/gergelyszerovay/">LinkedIn</a> to learn more about Angular, and how to build AI apps with AI, Typescript, React and Angular!</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.aiboosted.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading AIBoosted.dev! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>