<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Prakash</title>
    <description>The latest articles on Forem by Prakash (@prakashm88).</description>
    <link>https://forem.com/prakashm88</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F116592%2F62d71578-345c-476d-b5f2-5b008f72cb0b.png</url>
      <title>Forem: Prakash</title>
      <link>https://forem.com/prakashm88</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/prakashm88"/>
    <language>en</language>
    <item>
      <title>The itgps-agent: Bringing Studio-Managed Configs to Your Local Workflow</title>
      <dc:creator>Prakash</dc:creator>
      <pubDate>Mon, 11 May 2026 04:14:20 +0000</pubDate>
      <link>https://forem.com/prakashm88/the-itgps-agent-bringing-studio-managed-configs-to-your-local-workflow-fhl</link>
      <guid>https://forem.com/prakashm88/the-itgps-agent-bringing-studio-managed-configs-to-your-local-workflow-fhl</guid>
      <description>&lt;h1&gt;
  
  
  The itgps-agent: Bringing Studio-Managed Configs to Your Local Workflow
&lt;/h1&gt;

&lt;p&gt;In &lt;a href="https://dev.tolink-to-part-1"&gt;Part 1&lt;/a&gt; and &lt;a href="https://dev.tolink-to-part-2"&gt;Part 2&lt;/a&gt;, we explored ITG Playwright Studio's web interface for managing tests. But what if you're a developer who prefers the command line? What if you want to run tests locally during development but still leverage Studio's centralized configuration management?&lt;/p&gt;

&lt;p&gt;That's exactly what the &lt;strong&gt;itgps-agent&lt;/strong&gt; solves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Configuration Drift
&lt;/h2&gt;

&lt;p&gt;Here's a common scenario: Your team uses ITG Playwright Studio to manage test configurations, environments, and datasets. Everything works great in CI and for scheduled runs. But when you're developing locally, you're back to managing your own &lt;code&gt;.env&lt;/code&gt; files, trying to keep them in sync with what's in the Studio.&lt;/p&gt;

&lt;p&gt;This leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Configuration drift&lt;/strong&gt; - your local setup doesn't match staging or production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging confusion&lt;/strong&gt; - "It works on my machine" because your env vars are different&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding friction&lt;/strong&gt; - new team members need to manually set up all the environment variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data duplication&lt;/strong&gt; - the same credentials and URLs exist in multiple places&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: itgps-agent
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;itgps-agent&lt;/code&gt; is a command-line tool that bridges the gap between local development and Studio-managed configurations. It lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run tests locally with Studio-managed environment variables and datasets&lt;/li&gt;
&lt;li&gt;Trigger remote test runs on the Studio server from your terminal&lt;/li&gt;
&lt;li&gt;Sync Git repositories via CLI&lt;/li&gt;
&lt;li&gt;Work offline with cached Studio data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as "Studio configuration, command-line execution."&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;The agent is distributed as an npm package. Install it globally for the best experience:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @itechgenie/itgps-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install it locally in your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @itechgenie/itgps-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Node.js 18 or later&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@playwright/test&lt;/code&gt; installed in your project&lt;/li&gt;
&lt;li&gt;Access to an ITG Playwright Studio instance&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Initial Setup: The Config Command
&lt;/h2&gt;

&lt;p&gt;The first thing you'll do is run the interactive configuration wizard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;itgps-agent config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wizard walks you through:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Studio URL&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Studio URL: https://studio.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Personal Access Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The wizard provides instructions on how to generate a PAT:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Studio in your browser&lt;/li&gt;
&lt;li&gt;Navigate to Settings&lt;/li&gt;
&lt;li&gt;Scroll to "Personal Access Tokens"&lt;/li&gt;
&lt;li&gt;Generate a new token&lt;/li&gt;
&lt;li&gt;Copy and paste it
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;? Personal Access Token (PAT): **********************
✓ Verified! Signed in as John Doe (john@example.com)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Credential Storage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Choose where to store your credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Where to save credentials?
  &amp;gt; Global config (~/.itgps/config.json)
    Local .env file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global config&lt;/strong&gt;: Good if you only work with one Studio instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local .env&lt;/strong&gt;: Better for multiple projects or multiple Studio instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Project Selection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent fetches your projects from Studio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Select project:
  &amp;gt; E-Commerce Tests
    Mobile App Tests
    API Integration Tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Environment &amp;amp; Dataset&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Choose which environment and dataset to use by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Select environment:
  &amp;gt; Staging
    Production
    Local Development

? Select dataset:
  &amp;gt; Test Users
    Edge Cases
    Performance Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 6: Bootstrap &amp;amp; Cache&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The agent downloads project configuration, environment variables, and dataset variables from Studio and caches them locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ Bootstrap complete
✓ Playwright config copied to .itgps/playwright.config.cjs
✓ Added .itgps/ to .gitignore

Configuration complete!

  Studio URL:  https://studio.example.com
  User:        John Doe (john@example.com)
  Project:     E-Commerce Tests
  Environment: Staging
  Dataset:     Test Users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Just Happened?
&lt;/h2&gt;

&lt;p&gt;The config command did several things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Authenticated&lt;/strong&gt; with Studio using your PAT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stored credentials&lt;/strong&gt; in either &lt;code&gt;~/.itgps/config.json&lt;/code&gt; or &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downloaded configuration&lt;/strong&gt; from Studio (project settings, env vars, dataset vars)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cached data&lt;/strong&gt; locally in &lt;code&gt;~/.itgps/cache/&lt;/code&gt; for offline use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copied Playwright config&lt;/strong&gt; to &lt;code&gt;.itgps/playwright.config.cjs&lt;/code&gt; in your project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updated .gitignore&lt;/strong&gt; to exclude &lt;code&gt;.itgps/&lt;/code&gt; folder&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your project structure now looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-test-project/
├── .itgps/
│   ├── cache/              # Cached Studio data
│   └── playwright.config.cjs  # Playwright configuration
├── .env                    # Studio credentials (if you chose local storage)
├── .gitignore              # Updated to ignore .itgps/
├── tests/
│   ├── login.spec.ts
│   └── checkout.spec.ts
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running Tests Locally
&lt;/h2&gt;

&lt;p&gt;Now you can run tests with Studio-managed configuration:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

&lt;ol&gt;
&lt;li&gt;Reads your Studio credentials&lt;/li&gt;
&lt;li&gt;Fetches the latest configuration (or uses cache if offline)&lt;/li&gt;
&lt;li&gt;Merges environment variables and dataset variables&lt;/li&gt;
&lt;li&gt;Runs &lt;code&gt;playwright test&lt;/code&gt; with the merged configuration&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[Config] Running on browser: chromium
Running 5 tests using 1 worker

✓ [chromium] › login.spec.ts:3:5 › User can log in (2.1s)
✓ [chromium] › login.spec.ts:15:5 › Invalid credentials show error (1.8s)
✓ [chromium] › checkout.spec.ts:3:5 › Complete checkout flow (4.3s)
✓ [chromium] › checkout.spec.ts:22:5 › Apply discount code (2.9s)
✓ [chromium] › checkout.spec.ts:35:5 › Guest checkout (3.2s)

5 passed (14.3s)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Studio Variables in Tests
&lt;/h2&gt;

&lt;p&gt;In your test files, you access Studio-managed variables via &lt;code&gt;process.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/login.spec.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user can log in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// These values come from Studio&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;siteUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;siteurl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_username&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;siteUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/dashboard/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent automatically injects these variables based on the environment and dataset you selected during &lt;code&gt;itgps-agent config&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variable Precedence
&lt;/h2&gt;

&lt;p&gt;Variables are merged with the following precedence (highest to lowest):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local &lt;code&gt;.env&lt;/code&gt; overrides&lt;/strong&gt; - Variables you define locally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset variables&lt;/strong&gt; - From the selected Studio dataset&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment variables&lt;/strong&gt; - From the selected Studio environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project defaults&lt;/strong&gt; - Default values from Studio project settings&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means you can override Studio values locally for debugging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env
# Override the site URL for local testing
siteurl=http://localhost:3000

# Use Studio values for everything else
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  All Playwright Commands Work
&lt;/h2&gt;

&lt;p&gt;The agent acts as a proxy for Playwright, so all native commands work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run tests in headed mode&lt;/span&gt;
itgps-agent &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--headed&lt;/span&gt;

&lt;span class="c"&gt;# Run specific tests&lt;/span&gt;
itgps-agent &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--grep&lt;/span&gt; &lt;span class="s2"&gt;"login"&lt;/span&gt;

&lt;span class="c"&gt;# Run on specific browser&lt;/span&gt;
itgps-agent &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;firefox

&lt;span class="c"&gt;# Generate test code&lt;/span&gt;
itgps-agent codegen

&lt;span class="c"&gt;# View reports&lt;/span&gt;
itgps-agent show-report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent bootstraps Studio data before passing control to Playwright.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote Execution: Trigger Studio Runs
&lt;/h2&gt;

&lt;p&gt;Want to trigger a test run on the Studio server from your terminal?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;itgps-agent remote-run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ol&gt;
&lt;li&gt;Connects to Studio&lt;/li&gt;
&lt;li&gt;Lets you select environment and dataset&lt;/li&gt;
&lt;li&gt;Triggers a test run on the Studio server&lt;/li&gt;
&lt;li&gt;Streams the output to your terminal in real-time&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;itgps-agent remote-run

? Select environment: Staging
? Select dataset: Test Users
? Trigger remote run? Yes

✓ Run triggered. Run ID: e1f86b12-f6b6-42f0-8a17-af6feb89001c

&lt;span class="o"&gt;[&lt;/span&gt;MR] clean output &lt;span class="nb"&gt;dir&lt;/span&gt; ...
Running 5 tests using 5 workers

✓ &lt;span class="o"&gt;[&lt;/span&gt;chromium] › login.spec.ts:3:5 › User can log &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;2.3s&lt;span class="o"&gt;)&lt;/span&gt;
✓ &lt;span class="o"&gt;[&lt;/span&gt;chromium] › checkout.spec.ts:3:5 › Complete checkout flow &lt;span class="o"&gt;(&lt;/span&gt;4.1s&lt;span class="o"&gt;)&lt;/span&gt;
...

✓ Run completed successfully &lt;span class="k"&gt;in &lt;/span&gt;12.4s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want to run tests on Studio's infrastructure (more powerful, different OS)&lt;/li&gt;
&lt;li&gt;You need to test against an environment only accessible from Studio&lt;/li&gt;
&lt;li&gt;You want the results recorded in Studio's execution history&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Git Sync from CLI
&lt;/h2&gt;

&lt;p&gt;Instruct Studio to pull the latest changes from Git:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;itgps-agent studio-git-sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This triggers a Git sync on the Studio server, useful in CI/CD pipelines or when you've pushed changes and want Studio to pick them up immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offline Support
&lt;/h2&gt;

&lt;p&gt;The agent caches Studio data locally, so you can work offline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;itgps-agent &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;offline] Using cached Studio data from 2024-05-10T14:23:45Z

Running 5 tests using 1 worker
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you're back online, run &lt;code&gt;itgps-agent config&lt;/code&gt; to refresh the cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing the Playwright Config
&lt;/h2&gt;

&lt;p&gt;The agent copies a Playwright config to &lt;code&gt;.itgps/playwright.config.cjs&lt;/code&gt;. This file is fully editable - customize it as needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .itgps/playwright.config.cjs&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;testDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;// Add custom reporters&lt;/span&gt;
  &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;junit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;outputFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;results.xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;  &lt;span class="c1"&gt;// Your addition&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="c1"&gt;// Customize projects&lt;/span&gt;
  &lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chromium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Desktop Chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// Add more browsers as needed&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent uses this config when running tests. If you run &lt;code&gt;itgps-agent config&lt;/code&gt; again, it will ask if you want to overwrite with the latest version from the agent package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language Support: JavaScript and TypeScript
&lt;/h2&gt;

&lt;p&gt;The agent works with both JavaScript and TypeScript projects. Your test files can be &lt;code&gt;.spec.js&lt;/code&gt; or &lt;code&gt;.spec.ts&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TypeScript example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/api.spec.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API health check&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_base_url&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/health`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JavaScript example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/api.spec.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API health check&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_base_url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/health`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both work identically with the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Workflow
&lt;/h2&gt;

&lt;p&gt;Here's how a typical development workflow looks with the agent:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Morning: Start Development&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pull latest code&lt;/span&gt;
git pull

&lt;span class="c"&gt;# Refresh Studio config (in case env vars changed)&lt;/span&gt;
itgps-agent config

&lt;span class="c"&gt;# Run tests to ensure everything works&lt;/span&gt;
itgps-agent &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;During Development&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run specific test you're working on&lt;/span&gt;
itgps-agent &lt;span class="nb"&gt;test &lt;/span&gt;tests/new-feature.spec.ts &lt;span class="nt"&gt;--headed&lt;/span&gt;

&lt;span class="c"&gt;# Override a variable locally for debugging&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"debug_mode=true"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .env
itgps-agent &lt;span class="nb"&gt;test &lt;/span&gt;tests/new-feature.spec.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Before Committing&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run full test suite&lt;/span&gt;
itgps-agent &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# If all pass, commit and push&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add new feature tests"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After Pushing&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Trigger Studio to sync with Git&lt;/span&gt;
itgps-agent studio-git-sync

&lt;span class="c"&gt;# Trigger a remote run to verify in Studio environment&lt;/span&gt;
itgps-agent remote-run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Authentication Errors&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Token expired or invalid&lt;/span&gt;
&lt;span class="c"&gt;# Regenerate token in Studio and reconfigure&lt;/span&gt;
itgps-agent config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Network Errors&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Studio unreachable&lt;/span&gt;
&lt;span class="c"&gt;# Agent automatically uses cached data&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;offline] Using cached Studio data from 2024-05-10T14:23:45Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Missing Browsers&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Playwright browsers not installed&lt;/span&gt;
npx playwright &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Config Issues&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Regenerate config file&lt;/span&gt;
itgps-agent config
&lt;span class="c"&gt;# Choose "Yes" when asked to overwrite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration Files Reference
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Global Config&lt;/strong&gt; (&lt;code&gt;~/.itgps/config.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"studioUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://studio.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-personal-access-token"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Local Environment&lt;/strong&gt; (&lt;code&gt;.env&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ITGPS_STUDIO_URL=https://studio.example.com
ITGPS_TOKEN=your-personal-access-token
ITGPS_PROJECT_ID=project-uuid
ITGPS_ENV_ID=environment-uuid
ITGPS_DATASET_ID=dataset-uuid

# Local overrides
siteurl=http://localhost:3000
debug_mode=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cache Location&lt;/strong&gt; (&lt;code&gt;~/.itgps/cache/&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.itgps/
├── config.json          # Global credentials
└── cache/
    ├── project.json     # Project list
    ├── environments.json # Environment configs
    └── datasets.json    # Dataset variables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Use the Agent?
&lt;/h2&gt;

&lt;p&gt;The agent is particularly valuable when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You prefer command-line tools&lt;/strong&gt; but want centralized config management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You're developing locally&lt;/strong&gt; and need the same env vars as CI/Studio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You work on multiple projects&lt;/strong&gt; with different Studio instances&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need offline capability&lt;/strong&gt; but want to sync when online&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want to trigger remote runs&lt;/strong&gt; without opening the browser&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your team uses Studio&lt;/strong&gt; but you want a local development workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Best of Both Worlds
&lt;/h2&gt;

&lt;p&gt;The beauty of the itgps-agent is that it gives you flexibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the &lt;strong&gt;Studio web interface&lt;/strong&gt; for scheduling, viewing reports, and managing configurations&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;agent CLI&lt;/strong&gt; for local development and command-line workflows&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;both&lt;/strong&gt; - they work together seamlessly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your tests remain standard Playwright tests. The agent just makes it easier to run them with the right configuration, whether you're working locally or triggering remote runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Install the agent and give it a try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install globally&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @itechgenie/itgps-agent

&lt;span class="c"&gt;# Configure connection to Studio&lt;/span&gt;
itgps-agent config

&lt;span class="c"&gt;# Run tests&lt;/span&gt;
itgps-agent &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more details, check out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/ITechGenie/itg-playwright-studio" rel="noopener noreferrer"&gt;https://github.com/ITechGenie/itg-playwright-studio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent README&lt;/strong&gt;: &lt;a href="https://github.com/ITechGenie/itg-playwright-studio/tree/main/itgps-agent" rel="noopener noreferrer"&gt;https://github.com/ITechGenie/itg-playwright-studio/tree/main/itgps-agent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Studio Documentation&lt;/strong&gt;: &lt;a href="https://github.com/ITechGenie/itg-playwright-studio#readme" rel="noopener noreferrer"&gt;https://github.com/ITechGenie/itg-playwright-studio#readme&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Series Recap
&lt;/h2&gt;

&lt;p&gt;In this three-part series, we've covered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1&lt;/strong&gt;: Why managing Playwright tests at scale is challenging and how ITG Playwright Studio addresses these challenges&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 2&lt;/strong&gt;: A detailed visual tour of the Studio interface, showing how to manage tests, view reports, schedule runs, and manage data&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 3&lt;/strong&gt; (this article): How to use the itgps-agent CLI tool to run tests locally while leveraging Studio-managed configurations&lt;/p&gt;

&lt;p&gt;Together, ITG Playwright Studio and the itgps-agent provide a complete solution for managing Playwright tests at scale - whether you prefer a web interface or command-line tools.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you tried the itgps-agent? What features would you like to see added? Let me know in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>automation</category>
      <category>cli</category>
    </item>
    <item>
      <title>A Visual Tour of ITG Playwright Studio: Managing Tests Through a Web Interface</title>
      <dc:creator>Prakash</dc:creator>
      <pubDate>Mon, 11 May 2026 04:13:42 +0000</pubDate>
      <link>https://forem.com/prakashm88/a-visual-tour-of-itg-playwright-studio-managing-tests-through-a-web-interface-3ahp</link>
      <guid>https://forem.com/prakashm88/a-visual-tour-of-itg-playwright-studio-managing-tests-through-a-web-interface-3ahp</guid>
      <description>&lt;h1&gt;
  
  
  A Visual Tour of ITG Playwright Studio
&lt;/h1&gt;

&lt;p&gt;In &lt;a href="https://dev.tolink-to-part-1"&gt;Part 1&lt;/a&gt;, we discussed why managing Playwright tests at scale is challenging and introduced ITG Playwright Studio as a solution. Now let's take a detailed tour of the interface and see how it actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started: Authentication
&lt;/h2&gt;

&lt;p&gt;The first thing you'll see is the login page. ITG Playwright Studio supports multiple authentication methods:&lt;/p&gt;

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

&lt;p&gt;You can authenticate via:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OAuth providers&lt;/strong&gt; (GitLab, GitHub) - useful for Git integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local admin account&lt;/strong&gt; - for self-hosted deployments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal Access Tokens&lt;/strong&gt; - for CLI and API access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The OAuth integration isn't just for login - it also enables direct Git repository access, letting you import projects and sync files without cloning repos locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Workspace: Your Test Suite Dashboard
&lt;/h2&gt;

&lt;p&gt;Once logged in, you land on the Projects page - your central hub for all Playwright test projects:&lt;/p&gt;

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

&lt;p&gt;Each project card shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project name and description&lt;/li&gt;
&lt;li&gt;Number of test files&lt;/li&gt;
&lt;li&gt;Recent execution status&lt;/li&gt;
&lt;li&gt;Quick actions (run tests, view files, settings)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can create projects in two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Import from Git&lt;/strong&gt; - Connect to a GitLab/GitHub repository&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local sync&lt;/strong&gt; - Point to an existing Playwright project on your filesystem&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Test Explorer: Browse and Manage Test Files
&lt;/h2&gt;

&lt;p&gt;Click into a project and you'll see the Test Explorer - a file browser specifically designed for test suites:&lt;/p&gt;

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

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tree view&lt;/strong&gt; of your test directory structure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File preview&lt;/strong&gt; with syntax highlighting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick actions&lt;/strong&gt; - run individual files or entire folders&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search and filter&lt;/strong&gt; to find specific tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monaco editor&lt;/strong&gt; for editing tests directly in the browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Monaco editor (the same one VS Code uses) gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full TypeScript/JavaScript IntelliSense&lt;/li&gt;
&lt;li&gt;Syntax highlighting&lt;/li&gt;
&lt;li&gt;Error detection&lt;/li&gt;
&lt;li&gt;Auto-completion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No need to clone the repo, open an IDE, make a change, commit, and push just to fix a typo in a test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Tests: The Prepare Drawer
&lt;/h2&gt;

&lt;p&gt;When you're ready to run tests, click "Prepare Tests" to open the runner drawer:&lt;/p&gt;

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

&lt;p&gt;This is where the Studio really shines. You can:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Select Test Files&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick individual files or entire folders&lt;/li&gt;
&lt;li&gt;Multi-select with checkboxes&lt;/li&gt;
&lt;li&gt;See how many tests will run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Configure Execution&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose browsers (Chromium, Firefox, WebKit, Chrome, Edge)&lt;/li&gt;
&lt;li&gt;Set worker count for parallelization&lt;/li&gt;
&lt;li&gt;Configure timeouts&lt;/li&gt;
&lt;li&gt;Enable/disable headless mode&lt;/li&gt;
&lt;li&gt;Set viewport dimensions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose Environment &amp;amp; Data&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select which environment to run against (staging, production, etc.)&lt;/li&gt;
&lt;li&gt;Pick a dataset for test data&lt;/li&gt;
&lt;li&gt;Override specific variables if needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Advanced Options&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable video recording&lt;/li&gt;
&lt;li&gt;Configure screenshot capture&lt;/li&gt;
&lt;li&gt;Set up HAR file recording&lt;/li&gt;
&lt;li&gt;Add custom Playwright flags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this without touching a config file or writing npm scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live Execution: Real-Time Feedback
&lt;/h2&gt;

&lt;p&gt;Hit "Run Tests" and you get live execution logs streamed directly to your browser:&lt;/p&gt;

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

&lt;p&gt;You see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time stdout/stderr&lt;/strong&gt; from Playwright&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progress indicators&lt;/strong&gt; for each test file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pass/fail status&lt;/strong&gt; as tests complete&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duration tracking&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ability to stop&lt;/strong&gt; the run if needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No more waiting for CI to finish and then downloading logs. You see everything as it happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution History: Track Every Run
&lt;/h2&gt;

&lt;p&gt;After tests complete, they're automatically added to the execution history:&lt;/p&gt;

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

&lt;p&gt;For each run, you can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt; (passed, failed, stopped)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duration&lt;/strong&gt; and timestamp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who triggered it&lt;/strong&gt; (user or schedule)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment and dataset&lt;/strong&gt; used&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Command executed&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct links&lt;/strong&gt; to HTML and Monocart reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reports are served directly from the Studio - no downloading, no extracting archives. Just click and view.&lt;/p&gt;

&lt;p&gt;Filter the history by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Date range (with a 2-week maximum for performance)&lt;/li&gt;
&lt;li&gt;Status (passed, failed, running)&lt;/li&gt;
&lt;li&gt;Environment&lt;/li&gt;
&lt;li&gt;Triggered by&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it easy to spot patterns: "Why do tests keep failing on Fridays?" or "Did the staging environment have issues last week?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling: Automate Test Runs
&lt;/h2&gt;

&lt;p&gt;Need tests to run automatically? Open the scheduler:&lt;/p&gt;

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

&lt;p&gt;Set up recurring runs with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cron-like scheduling&lt;/strong&gt; - run tests hourly, daily, weekly, or custom patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test selection&lt;/strong&gt; - which files to run&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment configuration&lt;/strong&gt; - which environment and dataset to use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser selection&lt;/strong&gt; - which browsers to test&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run smoke tests every hour during business hours&lt;/li&gt;
&lt;li&gt;Full regression suite nightly at 2 AM&lt;/li&gt;
&lt;li&gt;Checkout flow tests every 30 minutes&lt;/li&gt;
&lt;li&gt;Cross-browser tests weekly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No need to set up Jenkins jobs, GitHub Actions workflows, or cron jobs. It's all managed through the UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Management: Environments and Datasets
&lt;/h2&gt;

&lt;p&gt;One of the most powerful features is the data management system. Instead of hardcoding values or managing multiple &lt;code&gt;.env&lt;/code&gt; files, you define them in the Studio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Templates&lt;/strong&gt;:&lt;/p&gt;

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

&lt;p&gt;Define the schema for your test data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Field names (username, password, api_key, etc.)&lt;/li&gt;
&lt;li&gt;Field types (text, password, URL, etc.)&lt;/li&gt;
&lt;li&gt;Descriptions and defaults&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Environments&lt;/strong&gt;:&lt;/p&gt;

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

&lt;p&gt;Create environment-specific configurations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Staging&lt;/strong&gt;: staging URLs, test credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production&lt;/strong&gt;: production URLs, real credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local&lt;/strong&gt;: localhost URLs, mock data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Datasets&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Create reusable test data sets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Happy Path Users&lt;/strong&gt;: valid credentials, complete profiles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge Cases&lt;/strong&gt;: special characters, boundary values&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Scenarios&lt;/strong&gt;: invalid data, expired tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In your tests, you just reference the variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_username&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;siteUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;siteurl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Studio injects the right values based on which environment and dataset you selected for the run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variable Precedence&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Local &lt;code&gt;.env&lt;/code&gt; overrides (highest priority)&lt;/li&gt;
&lt;li&gt;Dataset variables&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Project defaults (lowest priority)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means developers can override values locally for debugging while CI uses Studio-managed values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Settings: Fine-Tune Configuration
&lt;/h2&gt;

&lt;p&gt;Each project has a settings page where you can configure Playwright defaults:&lt;/p&gt;

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

&lt;p&gt;Set project-wide defaults for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Default browser&lt;/li&gt;
&lt;li&gt;Worker count&lt;/li&gt;
&lt;li&gt;Timeout values&lt;/li&gt;
&lt;li&gt;Base URL&lt;/li&gt;
&lt;li&gt;Video and screenshot settings&lt;/li&gt;
&lt;li&gt;Viewport dimensions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These become the defaults for all test runs, but can be overridden per-run in the Prepare drawer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git Integration: Sync Without Cloning
&lt;/h2&gt;

&lt;p&gt;If you authenticated via OAuth, you get seamless Git integration:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import Projects&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browse your GitLab/GitHub repositories&lt;/li&gt;
&lt;li&gt;Import with one click&lt;/li&gt;
&lt;li&gt;Studio clones and syncs automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sync Files&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull latest changes from Git&lt;/li&gt;
&lt;li&gt;See what changed&lt;/li&gt;
&lt;li&gt;No local git commands needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Push Changes&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edit files in Monaco editor&lt;/li&gt;
&lt;li&gt;Write a commit message&lt;/li&gt;
&lt;li&gt;Push directly to the repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is particularly useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick fixes that don't warrant a full dev environment setup&lt;/li&gt;
&lt;li&gt;Non-technical team members who need to update test data&lt;/li&gt;
&lt;li&gt;Reviewing test code without cloning repos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Workflow Example
&lt;/h2&gt;

&lt;p&gt;Let's walk through a complete workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Morning: Schedule Smoke Tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open scheduler&lt;/li&gt;
&lt;li&gt;Create "Hourly Smoke Tests" schedule&lt;/li&gt;
&lt;li&gt;Select critical path tests (login, checkout, search)&lt;/li&gt;
&lt;li&gt;Choose staging environment&lt;/li&gt;
&lt;li&gt;Set to run every hour from 9 AM to 6 PM&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Mid-Day: Debug a Failing Test&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check execution history&lt;/li&gt;
&lt;li&gt;See that checkout test failed at 11 AM&lt;/li&gt;
&lt;li&gt;Click to view Monocart report&lt;/li&gt;
&lt;li&gt;See screenshot of the failure&lt;/li&gt;
&lt;li&gt;Open test file in Monaco editor&lt;/li&gt;
&lt;li&gt;Fix the selector&lt;/li&gt;
&lt;li&gt;Push change to Git&lt;/li&gt;
&lt;li&gt;Manually trigger a test run to verify fix&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Afternoon: Run Full Regression&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Test Explorer&lt;/li&gt;
&lt;li&gt;Select all test files&lt;/li&gt;
&lt;li&gt;Choose "Production" environment&lt;/li&gt;
&lt;li&gt;Select "Full Dataset"&lt;/li&gt;
&lt;li&gt;Enable all browsers (Chromium, Firefox, WebKit)&lt;/li&gt;
&lt;li&gt;Set workers to 5 for parallelization&lt;/li&gt;
&lt;li&gt;Run tests&lt;/li&gt;
&lt;li&gt;Watch live logs&lt;/li&gt;
&lt;li&gt;Tests complete in 8 minutes instead of 40 (thanks to parallelization)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. End of Day: Review Results&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check execution history&lt;/li&gt;
&lt;li&gt;Filter by today's date&lt;/li&gt;
&lt;li&gt;See 8 scheduled runs (hourly smoke tests)&lt;/li&gt;
&lt;li&gt;See 1 manual run (your regression)&lt;/li&gt;
&lt;li&gt;All passed&lt;/li&gt;
&lt;li&gt;Share report link with product manager&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this without writing a single line of CI/CD configuration or managing any infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Stack
&lt;/h2&gt;

&lt;p&gt;For those curious about what's under the hood:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React 18 with Vite&lt;/li&gt;
&lt;li&gt;Tailwind CSS for styling&lt;/li&gt;
&lt;li&gt;Radix UI (Shadcn) for components&lt;/li&gt;
&lt;li&gt;Monaco Editor for code editing&lt;/li&gt;
&lt;li&gt;Lucide Icons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js + Express&lt;/li&gt;
&lt;li&gt;TypeScript throughout&lt;/li&gt;
&lt;li&gt;SQLite with Drizzle ORM&lt;/li&gt;
&lt;li&gt;WebSockets for real-time logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spawns Playwright processes directly&lt;/li&gt;
&lt;li&gt;Streams stdout/stderr via WebSockets&lt;/li&gt;
&lt;li&gt;Generates HTML and Monocart reports&lt;/li&gt;
&lt;li&gt;Stores artifacts on filesystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Deployment&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker image available&lt;/li&gt;
&lt;li&gt;Can run on any Node.js host&lt;/li&gt;
&lt;li&gt;Minimal resource requirements&lt;/li&gt;
&lt;li&gt;SQLite means no separate database server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;In Part 3, we'll dive into the &lt;strong&gt;itgps-agent&lt;/strong&gt; - a CLI tool that brings Studio-managed configurations to your local development workflow. It's perfect for developers who prefer the command line but want centralized config management.&lt;/p&gt;

&lt;p&gt;You'll learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install and configure the agent&lt;/li&gt;
&lt;li&gt;Run tests locally with Studio data&lt;/li&gt;
&lt;li&gt;Trigger remote runs from your terminal&lt;/li&gt;
&lt;li&gt;Sync Git repositories via CLI&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Ready to try ITG Playwright Studio? It's open source and easy to get started:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker (fastest)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull ghcr.io/itechgenie/itg-playwright-studio:latest
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/data:/app/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; itg-playwright-studio &lt;span class="se"&gt;\&lt;/span&gt;
  ghcr.io/itechgenie/itg-playwright-studio:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;From Source&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ITechGenie/itg-playwright-studio.git
&lt;span class="nb"&gt;cd &lt;/span&gt;itg-playwright-studio
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; and start exploring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/ITechGenie/itg-playwright-studio" rel="noopener noreferrer"&gt;https://github.com/ITechGenie/itg-playwright-studio&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Coming up next&lt;/strong&gt;: Part 3: The itgps-agent CLI Tool - Run tests locally with Studio-managed configurations.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What features would you like to see in a Playwright management tool? Let me know in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>automation</category>
      <category>devops</category>
    </item>
    <item>
      <title>Playwright is Powerful, But Managing It at Scale? That's Another Story</title>
      <dc:creator>Prakash</dc:creator>
      <pubDate>Mon, 11 May 2026 04:12:45 +0000</pubDate>
      <link>https://forem.com/prakashm88/playwright-is-powerful-but-managing-it-at-scale-thats-another-story-2g37</link>
      <guid>https://forem.com/prakashm88/playwright-is-powerful-but-managing-it-at-scale-thats-another-story-2g37</guid>
      <description>&lt;h2&gt;
  
  
  Playwright is Powerful, But Managing It at Scale? That's Another Story
&lt;/h2&gt;

&lt;p&gt;If you've worked with Playwright, you know it's an incredible tool for end-to-end testing. The API is clean, the cross-browser support is solid, and the developer experience is generally great. But here's the thing: as your test suite grows and your team expands, managing Playwright tests becomes... complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Playwright Paradox
&lt;/h2&gt;

&lt;p&gt;Playwright gives you everything you need to write robust tests. But when it comes to &lt;strong&gt;managing&lt;/strong&gt; those tests across teams, environments, and schedules, you're mostly on your own. Let me paint a familiar picture:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: The Test Execution Shuffle&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your QA team wants to run a specific subset of tests against staging. Your CI/CD pipeline needs to run the full suite on every PR. Your product manager wants to trigger smoke tests on demand. Each of these requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different environment variables&lt;/li&gt;
&lt;li&gt;Different test data&lt;/li&gt;
&lt;li&gt;Different browser configurations&lt;/li&gt;
&lt;li&gt;Different reporting requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You end up with a maze of npm scripts, environment files, and CI/CD configurations that only three people on your team truly understand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: The Report Hunt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A test failed in CI last night. You need to see what happened. Where's the report? Is it in the CI artifacts? Did someone download it locally? Is it still available, or did it get cleaned up? You spend 10 minutes just finding the report before you can even start debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 3: The Schedule Dance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Can we run the checkout flow tests every hour during business hours?" Sure, let me just... set up a cron job? Write a GitHub Actions workflow? Deploy a Lambda function? For something that should be simple, it requires surprisingly complex infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Missing?
&lt;/h2&gt;

&lt;p&gt;Playwright itself is fantastic. What's missing is the &lt;strong&gt;control plane&lt;/strong&gt; - a centralized way to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manage test execution&lt;/strong&gt; across different environments without juggling config files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schedule recurring runs&lt;/strong&gt; without writing cron jobs or CI/CD workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View and share reports&lt;/strong&gt; without hunting through artifacts or local directories&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track execution history&lt;/strong&gt; to spot patterns and regressions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manage test data&lt;/strong&gt; (environments, datasets) separately from test code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaborate&lt;/strong&gt; with team members who may not be comfortable with command-line tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't Playwright problems - they're &lt;strong&gt;operational problems&lt;/strong&gt; that emerge when you're running tests at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter ITG Playwright Studio
&lt;/h2&gt;

&lt;p&gt;This is why we built &lt;strong&gt;ITG Playwright Studio&lt;/strong&gt; - a web-based control plane specifically designed to solve these operational challenges while keeping Playwright at the core.&lt;/p&gt;

&lt;p&gt;Think of it as the missing management layer for your Playwright tests. You still write tests the same way, you still use Playwright's excellent API, but now you have a centralized dashboard to:&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution Management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Select multiple test files and run them in parallel with a few clicks&lt;/li&gt;
&lt;li&gt;Configure browser settings, timeouts, and workers through a UI&lt;/li&gt;
&lt;li&gt;Trigger runs on-demand or schedule them for later&lt;/li&gt;
&lt;li&gt;Stream live execution logs in real-time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reporting &amp;amp; History
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;View HTML and Monocart reports directly in the browser&lt;/li&gt;
&lt;li&gt;Track every test run with full execution history&lt;/li&gt;
&lt;li&gt;Filter runs by status, date range, or environment&lt;/li&gt;
&lt;li&gt;Share report links with team members&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data Management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Define environment-specific variables (staging, production, etc.)&lt;/li&gt;
&lt;li&gt;Create reusable datasets for different test scenarios&lt;/li&gt;
&lt;li&gt;Override values without touching test code&lt;/li&gt;
&lt;li&gt;Manage secrets securely&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scheduling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Set up recurring test runs with cron-like precision&lt;/li&gt;
&lt;li&gt;Run smoke tests every hour, full regression nightly&lt;/li&gt;
&lt;li&gt;No infrastructure setup required&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  File Management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Browse and edit test files directly in the browser&lt;/li&gt;
&lt;li&gt;Monaco editor with full TypeScript/JavaScript support&lt;/li&gt;
&lt;li&gt;Sync with Git repositories (GitLab, GitHub)&lt;/li&gt;
&lt;li&gt;No need to clone repos locally just to make quick edits&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;ITG Playwright Studio is built as a distributed system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React + Vite dashboard with Monaco editor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Node.js + Express API with SQLite database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Engine&lt;/strong&gt;: Spawns Playwright processes with configured environments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI Agent&lt;/strong&gt;: Optional tool for running tests locally with Studio-managed configs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight: &lt;strong&gt;we don't replace Playwright, we orchestrate it&lt;/strong&gt;. Your tests remain standard Playwright tests. The Studio just makes them easier to manage, execute, and monitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Is This For?
&lt;/h2&gt;

&lt;p&gt;ITG Playwright Studio is particularly useful if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have a growing test suite that multiple team members need to run&lt;/li&gt;
&lt;li&gt;Need to run tests across different environments with different configurations&lt;/li&gt;
&lt;li&gt;Want non-technical stakeholders to trigger test runs without command-line access&lt;/li&gt;
&lt;li&gt;Are tired of hunting for test reports in CI artifacts&lt;/li&gt;
&lt;li&gt;Need scheduled test runs but don't want to manage cron jobs or CI/CD complexity&lt;/li&gt;
&lt;li&gt;Want a single source of truth for test execution history&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;In the next article, I'll give you a detailed tour of ITG Playwright Studio's interface, showing you exactly how it solves each of these problems. We'll walk through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up your first project&lt;/li&gt;
&lt;li&gt;Running tests with different configurations&lt;/li&gt;
&lt;li&gt;Viewing reports and execution history&lt;/li&gt;
&lt;li&gt;Managing environments and datasets&lt;/li&gt;
&lt;li&gt;Setting up scheduled runs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And in the third article, we'll dive into the &lt;strong&gt;itgps-agent&lt;/strong&gt; - a CLI tool that lets you run tests locally while still leveraging Studio-managed configurations. It's perfect for developers who prefer the command line but want centralized config management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;ITG Playwright Studio is open source and available on GitHub. You can spin it up with Docker in under a minute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull ghcr.io/itechgenie/itg-playwright-studio:latest
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;--name&lt;/span&gt; itg-playwright-studio &lt;span class="se"&gt;\&lt;/span&gt;
  ghcr.io/itechgenie/itg-playwright-studio:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; and start exploring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/ITechGenie/itg-playwright-studio" rel="noopener noreferrer"&gt;https://github.com/ITechGenie/itg-playwright-studio&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Coming up next&lt;/strong&gt;: Part 2: A Visual Tour of ITG Playwright Studio - Screenshots, features, and a complete walkthrough of the interface.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you faced similar challenges managing Playwright tests at scale? What solutions have you tried? Let me know in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>automation</category>
      <category>devops</category>
    </item>
    <item>
      <title>Open Source: A Friendly Guide for New Developers</title>
      <dc:creator>Prakash</dc:creator>
      <pubDate>Sat, 27 Dec 2025 13:50:53 +0000</pubDate>
      <link>https://forem.com/prakashm88/open-source-a-friendly-guide-for-new-developers-4mn5</link>
      <guid>https://forem.com/prakashm88/open-source-a-friendly-guide-for-new-developers-4mn5</guid>
      <description>&lt;h2&gt;
  
  
  Open Source: A Friendly Guide for New Developers
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article is written for first-time developers — a friendly, practical introduction to help you understand open source and take your first steps.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Open source powers much of the software you use every day — from operating systems and developer tools to the websites and services you depend on. If you’re a new or first-time developer, open source is an incredible way to learn, contribute, and make real-world impact. This short guide explains what open source is, how it differs from other models, why it matters, and how you can get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Open Source?
&lt;/h2&gt;

&lt;p&gt;Open source means the source code of a project is publicly available for anyone to view, modify, and distribute. This encourages transparency, peer review, and collaboration. While the code is open, projects still use licenses that define what others can do with the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source vs Free Software vs Commercial
&lt;/h2&gt;

&lt;p&gt;Before we compare them, here’s why the distinction matters: different projects have different goals and rules. Some focus on making code easy to reuse and build on, others focus on protecting users' freedoms, and some are designed mainly to make money. That changes what you are allowed to do with the code and how the project is run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In simple words:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt;: you can see and usually modify the code — specific rights depend on the license.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free software&lt;/strong&gt;: strong emphasis on the user's freedom to run, study, change, and share the software.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commercial software&lt;/strong&gt;: typically built to generate revenue; it may be closed-source or restricted.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Quick comparison&lt;/th&gt;
&lt;th&gt;Open Source&lt;/th&gt;
&lt;th&gt;Free Software&lt;/th&gt;
&lt;th&gt;Commercial&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Main idea&lt;/td&gt;
&lt;td&gt;Code is public and reusable&lt;/td&gt;
&lt;td&gt;User freedoms are protected&lt;/td&gt;
&lt;td&gt;Built to make money (may be closed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What you can do&lt;/td&gt;
&lt;td&gt;Read and modify (depending on license)&lt;/td&gt;
&lt;td&gt;Run, study, modify, and redistribute&lt;/td&gt;
&lt;td&gt;Often limited unless vendor permits it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who builds it&lt;/td&gt;
&lt;td&gt;Community contributors + companies&lt;/td&gt;
&lt;td&gt;Communities and activists for software freedom&lt;/td&gt;
&lt;td&gt;Companies and paid teams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Friendly for beginners?&lt;/td&gt;
&lt;td&gt;Yes — great for learning by reading real projects&lt;/td&gt;
&lt;td&gt;Yes — teaches important values, but legal terms matter&lt;/td&gt;
&lt;td&gt;Sometimes (paid support helps), but source may be hidden&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These categories overlap — for example, an open source project can also be free software, and companies often offer commercial services around open source projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Brief History
&lt;/h2&gt;

&lt;p&gt;The culture of sharing software predates modern open source. Key milestones include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1983: The GNU Project launched to create a free Unix-like OS.&lt;/li&gt;
&lt;li&gt;1991: Linus Torvalds released the Linux kernel, sparking widespread collaboration.&lt;/li&gt;
&lt;li&gt;2000s: Distributed version control and platforms like GitHub made contributing easier and more social.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together these developments shaped the modern open source ecosystem: collaborative, distributed, and accessible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Life-Changing Open Source Projects
&lt;/h2&gt;

&lt;p&gt;Open source has produced tools and platforms that changed how we build software — and many are things you already use every day. You don't have to be a developer to benefit: many smartphones include Android components that are open source, millions of websites run on WordPress, and popular apps like VLC and Firefox are open-source projects. If you're a developer, these projects' code is public and you can read or contribute; if not, you can still help by reporting bugs, translating, or donating.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linux&lt;/strong&gt; — the backbone of servers, mobile devices (Android), and cloud infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git&lt;/strong&gt; — the version control system used by millions of developers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; and &lt;strong&gt;Node.js&lt;/strong&gt; — languages and runtimes powering huge swathes of applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java &amp;amp; Android (AOSP)&lt;/strong&gt; — Java is a ubiquitous language used for backend systems, Android apps, and games (Minecraft was originally written in Java); Android's open-source components power many phones and devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WordPress &amp;amp; Forem (dev.to)&lt;/strong&gt; — WordPress powers a large portion of the web for blogs and sites; Forem is an open platform for developer communities and publishing — both demonstrate community-driven publishing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VS Code (OSS core)&lt;/strong&gt; — the editor's open-source core is used widely by developers; community extensions thrive on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firefox&lt;/strong&gt; — a modern web browser focused on privacy and extensibility; its codebase is open and accepts contributions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VLC&lt;/strong&gt; — a versatile media player that supports countless formats and is community-driven.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;7-Zip&lt;/strong&gt; — an open-source file archiver used for compressing and extracting files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;qBittorrent&lt;/strong&gt; — a community-maintained BitTorrent client commonly used for peer-to-peer file sharing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LibreOffice&lt;/strong&gt; and &lt;strong&gt;GIMP&lt;/strong&gt; — open alternatives for productivity and image editing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenGL&lt;/strong&gt; and &lt;strong&gt;Vulkan&lt;/strong&gt; — graphics APIs used in games and graphics applications; they power much of the visual software you interact with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes&lt;/strong&gt; and &lt;strong&gt;Docker&lt;/strong&gt; — changed how we build, ship, and run applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blender&lt;/strong&gt; — open-source 3D modeling and animation software used in films and design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenStreetMap&lt;/strong&gt; — volunteer-built mapping data that powers many navigation and location services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These projects are not just technologies; they’re products you've likely used — so the question is: if you can run them, why not read or change their code? That direct connection makes open source especially inviting for first-time contributors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Licenses and Their Nuances
&lt;/h2&gt;

&lt;p&gt;Licenses determine what others can do with your code. Choosing a license depends on your goals (encourage wide adoption, preserve freedoms, or limit certain commercial usages). Common license families:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;License family&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;th&gt;Key points&lt;/th&gt;
&lt;th&gt;When to choose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Permissive&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://opensource.org/licenses/MIT" rel="noopener noreferrer"&gt;MIT&lt;/a&gt;, &lt;a href="https://opensource.org/licenses/BSD-3-Clause" rel="noopener noreferrer"&gt;BSD-2/3&lt;/a&gt;, &lt;a href="https://www.apache.org/licenses/LICENSE-2.0" rel="noopener noreferrer"&gt;Apache 2.0&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Minimal restrictions; Apache 2.0 adds a patent grant&lt;/td&gt;
&lt;td&gt;When you want broad adoption and easy reuse, including commercial use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copyleft&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.gnu.org/licenses/gpl-3.0.en.html" rel="noopener noreferrer"&gt;GPLv3&lt;/a&gt;, &lt;a href="https://www.gnu.org/licenses/agpl-3.0.en.html" rel="noopener noreferrer"&gt;AGPLv3&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Requires derivative works to use the same license (AGPL extends to network use)&lt;/td&gt;
&lt;td&gt;When you want derivatives to remain open-source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source-available / Custom&lt;/td&gt;
&lt;td&gt;Business Source (e.g., &lt;a href="https://mariadb.com/bsl/" rel="noopener noreferrer"&gt;MariaDB BSL&lt;/a&gt;), commercial dual-licensing&lt;/td&gt;
&lt;td&gt;Limits certain uses (e.g., cloud providers); may not be OSI-approved&lt;/td&gt;
&lt;td&gt;When you need specific commercial protections or staged openness&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a broader list of licenses see &lt;a href="https://opensource.org/licenses" rel="noopener noreferrer"&gt;Open Source Initiative&lt;/a&gt; or the &lt;a href="https://spdx.org/licenses/" rel="noopener noreferrer"&gt;SPDX license list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each choice has trade-offs between adoption, control, and community expectations.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Commercial Support Adds Value
&lt;/h2&gt;

&lt;p&gt;Commercial offerings around open source help organizations use projects reliably at scale. Typical commercial services include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Long-term support (LTS) and enterprise builds&lt;/strong&gt;: stable releases with extended maintenance and security patches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service-level agreements (SLAs)&lt;/strong&gt;: guaranteed response times and dedicated support for critical incidents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed/hosted offerings&lt;/strong&gt;: cloud-hosted versions (e.g., MongoDB Atlas, Elastic Cloud, Redis Enterprise) that remove operational overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration, consulting, and training&lt;/strong&gt;: help with architectural design, migration, and bespoke integrations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and compliance&lt;/strong&gt;: coordinated security advisories, backported fixes, and assistance meeting regulatory needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well-known examples include &lt;strong&gt;Red Hat&lt;/strong&gt; (enterprise Linux support), &lt;strong&gt;MongoDB&lt;/strong&gt; (enterprise features and Atlas), &lt;strong&gt;Elastic&lt;/strong&gt; (Elastic Cloud and enterprise plugins), and &lt;strong&gt;Redis&lt;/strong&gt; (Redis Enterprise and hosted services). Paid support funds maintainers and organizations, improves documentation and testing, and underwrites the work that keeps widely used projects healthy.&lt;/p&gt;

&lt;p&gt;That said, commercial models sometimes introduce trade-offs: features may be gated behind paid tiers, or licensing changes can create tension with community expectations — so transparent governance and clear communication are important.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Commercialization Causes Friction
&lt;/h2&gt;

&lt;p&gt;Sometimes monetization strategies trigger community pushback. Here are a few concrete cases you may have heard about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vendor licensing shifts (Java/Oracle)&lt;/strong&gt;: Oracle changed the terms and long-term support model for its Oracle JDK binaries, which led many organizations to switch to community OpenJDK builds or paid commercial distributions. The change shows how vendor licensing can push users toward community-maintained alternatives (see OpenJDK: &lt;a href="https://openjdk.java.net/" rel="noopener noreferrer"&gt;https://openjdk.java.net/&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cloud-provider tensions and re-licensing (Redis, Elastic, MongoDB)&lt;/strong&gt;: Some projects moved parts of their code to more restrictive or "source-available" licenses (or SSPL-style terms) to stop cloud providers from offering managed services without sharing revenue. Those moves caused forks, ecosystem disruption, and heated debate — for example, Redis' 2024 relicensing of certain modules and the later discussions about returning to a more permissive stance (see Redis announcement and coverage: &lt;a href="https://redis.com/blog/introducing-redis-enterprise-modules/" rel="noopener noreferrer"&gt;https://redis.com/blog/introducing-redis-enterprise-modules/&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Server_Side_Public_License" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Server_Side_Public_License&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Platform policy changes (Chrome Manifest V2 → V3)&lt;/strong&gt;: Platform or policy changes can also affect ecosystems. Chrome's move from Manifest V2 to Manifest V3 changed extension APIs that many ad blockers relied on, prompting privacy and developer concerns and wider discussion about platform power (see the Chromium docs and coverage: &lt;a href="https://developer.chrome.com/docs/extensions/mv3/" rel="noopener noreferrer"&gt;https://developer.chrome.com/docs/extensions/mv3/&lt;/a&gt; and &lt;a href="https://www.license-token.com/wiki/ublock-origin-dead-in-chrome" rel="noopener noreferrer"&gt;https://www.license-token.com/wiki/ublock-origin-dead-in-chrome&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These examples show why transparent governance, clear communication, and diverse funding models matter — they reduce surprises and keep communities resilient when business needs change.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Developers Can Help Sustain Open Source
&lt;/h2&gt;

&lt;p&gt;You don’t need to be a core maintainer to have high impact. Ways to help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contribute small fixes&lt;/strong&gt;: docs, tests, or tiny bug fixes labeled “good first issue”.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report clear bugs&lt;/strong&gt; and include steps to reproduce.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sponsor or donate&lt;/strong&gt; to maintainers and organizations that support projects you rely on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Help with triage and reviews&lt;/strong&gt; to reduce maintainer burden.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share knowledge&lt;/strong&gt;: write blog posts, give talks, or mentor newcomers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together these actions make projects healthier and more sustainable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started — A Simple Roadmap
&lt;/h2&gt;

&lt;p&gt;A. Create your own project (learn by doing):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start small: a focused utility, library, or tool solves a real problem.&lt;/li&gt;
&lt;li&gt;Choose a license (see table above) and add a clear &lt;code&gt;README.md&lt;/code&gt; and &lt;code&gt;LICENSE&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;, issue and PR templates, and a short roadmap to guide contributors.&lt;/li&gt;
&lt;li&gt;Publish the repo (GitHub/GitLab) and announce it in relevant communities; welcome feedback.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;B. Contribute to an existing project (learn from others):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick a project you use and read its contribution guide and code of conduct.&lt;/li&gt;
&lt;li&gt;Look for labels like &lt;code&gt;good first issue&lt;/code&gt;, &lt;code&gt;help wanted&lt;/code&gt;, or &lt;code&gt;docs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Start with documentation, tests, or small bug fixes to build context and confidence.&lt;/li&gt;
&lt;li&gt;Communicate clearly: describe your changes, link tests, and be open to reviewer feedback.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;C. Be helpful, not a nuisance: small, respectful, well-explained contributions win friends. Avoid low-effort or noisy PRs and follow the project's contribution norms — a good reminder is this tongue-in-cheek example: &lt;a href="https://www.reddit.com/r/ProgrammerHumor/comments/1o0iqf6/lookingclosely/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/ProgrammerHumor/comments/1o0iqf6/lookingclosely/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open source is welcoming — start small and grow your impact over time.&lt;/p&gt;

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

&lt;p&gt;Open source offers an incredible way for new developers to learn, contribute, and shape software used around the world. Start small, be consistent, and you’ll find that helping others also accelerates your own growth. &lt;/p&gt;

</description>
      <category>opensource</category>
      <category>beginners</category>
      <category>developers</category>
    </item>
    <item>
      <title>ITG DocVerse - A Developer Knowledge Platform Powered Entirely by Redis</title>
      <dc:creator>Prakash</dc:creator>
      <pubDate>Sun, 10 Aug 2025 19:40:26 +0000</pubDate>
      <link>https://forem.com/prakashm88/itg-docverse-a-developer-knowledge-platform-powered-entirely-by-redis-1ong</link>
      <guid>https://forem.com/prakashm88/itg-docverse-a-developer-knowledge-platform-powered-entirely-by-redis-1ong</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/redis-2025-07-23"&gt;Redis AI Challenge&lt;/a&gt;: Beyond the Cache&lt;/em&gt;.&lt;/p&gt;

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

&lt;p&gt;Meet &lt;strong&gt;&lt;a href="https://github.com/ITechGenie/itg-docverse" rel="noopener noreferrer"&gt;ITG DocVerse&lt;/a&gt;&lt;/strong&gt; - an internal knowledge-sharing platform for organizations, inspired by the excellent community-driven approach of DEV.to. I wanted to create something that teams could use to document projects, share insights, and collaborate - but with the power of Redis driving everything under the hood.&lt;/p&gt;

&lt;p&gt;The platform allows team members to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📝 Share posts, thoughts, and auto generated documents from git repos (Work in progress)&lt;/li&gt;
&lt;li&gt;🔍 Search content using AI-powered semantic search&lt;/li&gt;
&lt;li&gt;💬 Engage with discussions and comments
&lt;/li&gt;
&lt;li&gt;🏷️ Organize content with tags and categories&lt;/li&gt;
&lt;li&gt;👤 Build profiles and connect with colleagues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What makes this special is the architecture - I used this hackathon as an opportunity to explore Redis as the complete backbone of the application. From storing user profiles to powering semantic search with vector embeddings, this project demonstrates how Redis can serve as a comprehensive data platform that scales from small teams to enterprise-wide knowledge bases.&lt;/p&gt;

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

&lt;p&gt;🚀 &lt;strong&gt;Live Demo&lt;/strong&gt;: &lt;a href="https://itg-docverse.onrender.com/static/index.html" rel="noopener noreferrer"&gt;https://itg-docverse.onrender.com/static/index.html&lt;/a&gt;&lt;br&gt;&lt;br&gt;
📚 &lt;strong&gt;API Documentation&lt;/strong&gt;: &lt;a href="https://itg-docverse.onrender.com/docs" rel="noopener noreferrer"&gt;hhttps://itg-docverse.onrender.com/docs&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Main Dashboard &amp;amp; Content Feed
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ctl884b6uvl6z20ism7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ctl884b6uvl6z20ism7.png" alt="Dashboard Screenshot" width="800" height="758"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The main feed showing posts with real-time interactions and engagement&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9t765ot7emsgml9wuuja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9t765ot7emsgml9wuuja.png" alt="Individual Post View" width="800" height="519"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Detailed post view with markdown rendering and discussion section&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Content Creation &amp;amp; Management
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqe06rfgu3clbohdiyxb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqe06rfgu3clbohdiyxb.png" alt="Create Post" width="800" height="427"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Rich markdown editor for creating technical documentation&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbf216seefh3ce7fedkw0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbf216seefh3ce7fedkw0.png" alt="Create Thought" width="800" height="415"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Quick thought sharing interface for team communication&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  AI-Powered Search &amp;amp; Discovery
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6gen4hys84aktmvtvay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6gen4hys84aktmvtvay.png" alt="Search Results" width="800" height="442"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Semantic search finding relevant content even without exact keyword matches&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fir1gdhp1hl7xk72nj87c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fir1gdhp1hl7xk72nj87c.png" alt="Code Documentation" width="800" height="330"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;AI-generated code summaries and documentation from repositories&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6buwsqk62q933k7y69g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6buwsqk62q933k7y69g.png" alt="Code Summaries" width="800" height="401"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Automated code analysis and summary generation&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Tag-Based Organization
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhqpwnpwqgrxo1oo9rea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhqpwnpwqgrxo1oo9rea.png" alt="Tag Cloud" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Visual tag cloud for content discovery and organization&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o0zzm02e9ejgxg259a5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o0zzm02e9ejgxg259a5.png" alt="Tag Grid View" width="800" height="432"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Grid-based tag organization for better navigation&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  User Engagement &amp;amp; Analytics
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyskb0yd0ndrtyi0lp5pg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyskb0yd0ndrtyi0lp5pg.png" alt="User Reactions" width="800" height="293"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Real-time reactions and engagement tracking&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1mshsvhqwp3m518wwto.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1mshsvhqwp3m518wwto.png" alt="Top Contributors" width="800" height="277"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Community leaderboard and contributor recognition&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fauciucbm6agdv3lsa4ml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fauciucbm6agdv3lsa4ml.png" alt="User Profile" width="800" height="445"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Comprehensive user profiles with activity tracking&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Redis Data Architecture in Action
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fky58w8r65qnm161m1dnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fky58w8r65qnm161m1dnf.png" alt="Redis Keys Overview" width="800" height="334"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Redis Insight showing our multi-model data structure&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii4g8j75q890fbbc7wbi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii4g8j75q890fbbc7wbi.png" alt="User Data in Redis" width="800" height="432"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;User profiles stored as Redis Hash sets&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefes7byzmr2dnn0m7sgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefes7byzmr2dnn0m7sgs.png" alt="Posts in Redis" width="800" height="386"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Post content stored as Redis JSON documents&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubbcl4xvn8yhzi43wg2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubbcl4xvn8yhzi43wg2f.png" alt="Tags in Redis" width="800" height="298"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Tag organization using Redis sorted sets&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44sz1urejcyuknj3lvc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44sz1urejcyuknj3lvc5.png" alt="Search Vectors" width="800" height="494"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Vector embeddings stored for semantic search&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyhudj8eb44j7ogbazja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyhudj8eb44j7ogbazja.png" alt="Vector Chunks" width="800" height="489"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Content chunking strategy for large documents&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How I Used Redis 8
&lt;/h2&gt;

&lt;p&gt;This project leverages Redis as the primary backbone - serving as our &lt;strong&gt;database&lt;/strong&gt;, &lt;strong&gt;search engine&lt;/strong&gt;, and &lt;strong&gt;real-time data platform&lt;/strong&gt;. The hackathon gave me the perfect excuse to see how far I could push Redis beyond traditional caching. Here's how:&lt;/p&gt;
&lt;h3&gt;
  
  
  🗄️ Redis as Primary Database
&lt;/h3&gt;

&lt;p&gt;Rather than reaching for PostgreSQL or MySQL, I decided to architect the application around Redis data structures to see what was possible:&lt;/p&gt;
&lt;h4&gt;
  
  
  User Management with Hash Sets
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# User profiles stored as Redis hashes
HSET user:prakash88 
  id "ac2402cf-9a84-46a5-8484-d32400e7a18d"
  username "prakash88" 
  display_name "Prakash M"
  email "prakash@example.com"
  bio "Full-stack developer passionate about Redis"
  avatar_url "https://..."
  joined_date "2025-01-15T10:30:00Z"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Content Storage with JSON Documents
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Posts stored as Redis JSON with rich metadata
JSON.SET post:technical-architecture $ '{
  "id": "post-technical-architecture",
  "title": "Building with Redis: Beyond the Cache",
  "content": "# How Redis Powers Modern Applications...",
  "author_id": "ac2402cf-9a84-46a5-8484-d32400e7a18d",
  "post_type": "posts",
  "tags": ["redis", "architecture", "database"],
  "created_at": "2025-01-15T14:22:00Z",
  "read_time": 8
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Indexing with Sorted Sets for Performance
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Time-based post feeds using sorted sets
ZADD posts:by_date 1737890520 "post-technical-architecture"
ZADD posts:by_tag:redis 1737890520 "post-technical-architecture" 
ZADD user:prakash88:posts 1737890520 "post-technical-architecture"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  🧠 AI-Powered Vector Search
&lt;/h3&gt;

&lt;p&gt;The real magic happens with Redis's vector search capabilities. I built a semantic search engine that understands context, not just keywords:&lt;/p&gt;
&lt;h4&gt;
  
  
  Vector Embeddings Storage
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 768-dimensional embeddings stored efficiently
HSET search:vector:post-architecture-chunk-0
  vector "\x3e\x9a\x12\x40..."  # Binary encoded float32 array
  metadata '{"post_id": "post-architecture", "content": "Redis serves as...", "title": "Technical Architecture"}'

# Index management
SADD search:chunks "post-architecture-chunk-0"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  The Search Pipeline
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Content Chunking&lt;/strong&gt;: Break posts into semantic chunks (~500 words)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedding Generation&lt;/strong&gt;: Use Ollama's &lt;code&gt;nomic-embed-text&lt;/code&gt; model to create 768-dim vectors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis Storage&lt;/strong&gt;: Store vectors as binary data with metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Similarity Search&lt;/strong&gt;: Cosine similarity against all stored vectors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Results Ranking&lt;/strong&gt;: Return top matches with relevance scores&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  📊 Data Architecture in Redis
&lt;/h3&gt;

&lt;p&gt;Here's how I structured the data across Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Redis Database Structure
├── Users (Hashes)
│   ├── user:{username} → Profile data
│   └── user:{id}:posts → User's post references
│
├── Posts (JSON Documents)  
│   ├── post:{id} → Full post content
│   ├── posts:by_date → Time-ordered feed
│   └── posts:by_tag:{tag} → Tag-based organization
│
├── Comments (Hashes)
│   ├── comment:{id} → Comment data
│   └── post:{id}:comments → Post comment references
│
├── Vector Search (Mixed Types)
│   ├── search:chunks → Set of all chunk IDs
│   ├── search:vector:{chunk_id} → Hash with vector + metadata
│   └── search:metadata:{post_id} → Post search metadata
│
└── Authentication (Strings)
    └── session:{token} → JWT session data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Redis Data Model Visualization
&lt;/h4&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TB
    subgraph "User Management"
        A[user:prakash88&amp;lt;br/&amp;gt;Hash: Profile Data] --&amp;gt; B[user:ac24...18d:posts&amp;lt;br/&amp;gt;Sorted Set: Post IDs]
        C[user:sarah_dev&amp;lt;br/&amp;gt;Hash: Profile Data] --&amp;gt; D[user:7576...9c08:posts&amp;lt;br/&amp;gt;Sorted Set: Post IDs]
    end

    subgraph "Content Storage"
        E[post:welcome-guide&amp;lt;br/&amp;gt;JSON: Full Content] --&amp;gt; F[posts:by_date&amp;lt;br/&amp;gt;Sorted Set: All Posts]
        G[post:architecture&amp;lt;br/&amp;gt;JSON: Full Content] --&amp;gt; F
        E --&amp;gt; H[posts:by_tag:tutorial&amp;lt;br/&amp;gt;Sorted Set: Tagged Posts]
        G --&amp;gt; I[posts:by_tag:redis&amp;lt;br/&amp;gt;Sorted Set: Tagged Posts]
    end

    subgraph "Vector Search Engine"
        J[search:chunks&amp;lt;br/&amp;gt;Set: All Chunk IDs] --&amp;gt; K[search:vector:post-guide-chunk-0&amp;lt;br/&amp;gt;Hash: Vector + Metadata]
        J --&amp;gt; L[search:vector:post-arch-chunk-0&amp;lt;br/&amp;gt;Hash: Vector + Metadata]
        K --&amp;gt; M[768-dim Binary Vector&amp;lt;br/&amp;gt;+ JSON Metadata]
        L --&amp;gt; N[768-dim Binary Vector&amp;lt;br/&amp;gt;+ JSON Metadata]
    end

    subgraph "Engagement Layer"
        O[comment:cm1&amp;lt;br/&amp;gt;Hash: Comment Data] --&amp;gt; P[post:welcome-guide:comments&amp;lt;br/&amp;gt;List: Comment IDs]
        Q[comment:cm2&amp;lt;br/&amp;gt;Hash: Comment Data] --&amp;gt; P
    end

    B -.-&amp;gt; E
    D -.-&amp;gt; G
    E -.-&amp;gt; K
    G -.-&amp;gt; L
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Data Flow for Search Operations
&lt;/h4&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant User
    participant API
    participant Redis
    participant Ollama

    User-&amp;gt;&amp;gt;API: Search "react architecture"
    API-&amp;gt;&amp;gt;Ollama: Generate embedding
    Ollama--&amp;gt;&amp;gt;API: 768-dim vector

    API-&amp;gt;&amp;gt;Redis: SMEMBERS search:chunks
    Redis--&amp;gt;&amp;gt;API: [chunk-0, chunk-1, ...]

    loop For each chunk
        API-&amp;gt;&amp;gt;Redis: HGET search:vector:chunk-N vector
        Redis--&amp;gt;&amp;gt;API: Binary vector data
        Note over API: Calculate cosine similarity
    end

    Note over API: Sort by similarity score
    API-&amp;gt;&amp;gt;Redis: HGET search:vector:best-chunks metadata
    Redis--&amp;gt;&amp;gt;API: Post metadata + content
    API--&amp;gt;&amp;gt;User: Ranked search results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Redis Key Patterns Used
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user:{username}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash&lt;/td&gt;
&lt;td&gt;User profile storage&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user:prakash88&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user:{id}:posts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sorted Set&lt;/td&gt;
&lt;td&gt;User's posts by time&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user:ac24...18d:posts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;post:{id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;Complete post content&lt;/td&gt;
&lt;td&gt;&lt;code&gt;post:welcome-guide&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;posts:by_date&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sorted Set&lt;/td&gt;
&lt;td&gt;Global feed timeline&lt;/td&gt;
&lt;td&gt;&lt;code&gt;posts:by_date&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;posts:by_tag:{tag}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sorted Set&lt;/td&gt;
&lt;td&gt;Tag-based organization&lt;/td&gt;
&lt;td&gt;&lt;code&gt;posts:by_tag:redis&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search:vector:{chunk_id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash&lt;/td&gt;
&lt;td&gt;Vector + metadata&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search:vector:post-arch-chunk-0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search:chunks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set&lt;/td&gt;
&lt;td&gt;All searchable chunks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search:chunks&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;comment:{id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash&lt;/td&gt;
&lt;td&gt;Comment data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;comment:cm1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;post:{id}:comments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List&lt;/td&gt;
&lt;td&gt;Post's comments&lt;/td&gt;
&lt;td&gt;&lt;code&gt;post:welcome-guide:comments&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ⚡ Performance Optimizations
&lt;/h3&gt;

&lt;p&gt;Redis's speed shines through several optimizations:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;Compound Queries with Pipeline&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fetch user, their posts, and engagement data in one roundtrip
&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hgetall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zrevrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:posts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Latest 20 posts
&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:followers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. &lt;strong&gt;Smart Caching with Expiration&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Cache frequently accessed data with TTL
&lt;/span&gt;&lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;popular:posts:today&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trending_posts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. &lt;strong&gt;Memory-Efficient Vector Storage&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Instead of storing vectors as JSON arrays, I serialize them as binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Convert float array to compact binary format
&lt;/span&gt;&lt;span class="n"&gt;vector_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;tobytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search:vector:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;chunk_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vector&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔄 Future Real-Time Features
&lt;/h3&gt;

&lt;p&gt;The current architecture is designed to support additional Redis capabilities in the roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chatbot Integration&lt;/strong&gt;: A knowledge-base powered chatbot using the existing vector search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis Streams&lt;/strong&gt;: For storing and processing chat queries and user interactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pub/Sub&lt;/strong&gt;: For real-time chat notifications and live updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Analytics&lt;/strong&gt;: Using the engagement data already being collected&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The "Aha!" Moments
&lt;/h2&gt;

&lt;p&gt;Building this taught me that Redis isn't just fast storage - it's a complete data platform:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Easy Migration&lt;/strong&gt;: Originally started with SQLite, but adding Redis support was surprisingly smooth thanks to our service layer architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON Support&lt;/strong&gt;: Redis JSON made complex document storage trivial - no more complex table joins!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector Search&lt;/strong&gt;: No need for separate vector databases like Pinecone - Redis handled it natively&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Model Flexibility&lt;/strong&gt;: Hash sets for profiles, sorted sets for feeds, JSON for posts - all in one system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Operations&lt;/strong&gt;: Complex updates in single commands instead of transaction blocks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Efficiency&lt;/strong&gt;: Surprisingly good performance even with a 12-chunk vector index&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: What started as a simple SQLite app easily scaled to support Redis's advanced features&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Technical Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: FastAPI (Python) with Redis as the primary database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React + TypeScript with Vite&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Framework&lt;/strong&gt;: Tailwind CSS + shadcn/ui components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI&lt;/strong&gt;: Ollama for local embeddings (nomic-embed-text model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search&lt;/strong&gt;: Redis vector operations with cosine similarity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: JWT tokens stored in Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: Docker-compose for easy setup&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This hackathon project was a great exploration of Redis's potential as an application backbone. I would like to explore other Redis capabilities for the :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complete Redis migration&lt;/strong&gt;: Since the project was started in Sqlite and migrated to Redis not all capabilities works in Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Chatbot&lt;/strong&gt;: Knowledge-base powered assistant using existing vector search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time Chat&lt;/strong&gt;: Redis Streams for storing chat history and Pub/Sub for live messaging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Analytics&lt;/strong&gt;: Redis TimeSeries for tracking user engagement patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geographic Features&lt;/strong&gt;: Redis GEO commands for location-based content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Search&lt;/strong&gt;: Combining full-text search with vector similarity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone and run with Docker&lt;/span&gt;
git clone https://github.com/ITechGenie/itg-docverse
&lt;span class="nb"&gt;cd &lt;/span&gt;itg-docverse
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Or run locally&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;apis &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python main.py
&lt;span class="nb"&gt;cd &lt;/span&gt;app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redis isn't just about caching anymore - it's a powerful platform for building modern applications. ITG DocVerse shows what's possible when you use Redis as your application's backbone.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The hackathon challenge gave me the perfect excuse to explore these capabilities!&lt;/em&gt; 🚀&lt;/p&gt;

</description>
      <category>redischallenge</category>
      <category>devchallenge</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>OpenFGA Studio - An Open Source Authorization Modeling Interface</title>
      <dc:creator>Prakash</dc:creator>
      <pubDate>Tue, 10 Jun 2025 18:22:48 +0000</pubDate>
      <link>https://forem.com/prakashm88/openfga-studio-an-open-source-authorization-modeling-interface-448a</link>
      <guid>https://forem.com/prakashm88/openfga-studio-an-open-source-authorization-modeling-interface-448a</guid>
      <description>&lt;h2&gt;
  
  
  Understanding OpenFGA
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://openfga.dev/" rel="noopener noreferrer"&gt;OpenFGA&lt;/a&gt; (Fine-Grained Authorization) is a high-performance authorization engine built for developers and inspired by Google's Zanzibar paper. It excels in handling complex authorization scenarios with features that make it stand out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relationship-Based Authorization&lt;/strong&gt;: Model complex access patterns through relationships&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Performance&lt;/strong&gt;: Process millions of authorization checks per second&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Support for RBAC, ABAC, and ReBAC models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-Based Access&lt;/strong&gt;: Define temporal access rules with built-in support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proven Architecture&lt;/strong&gt;: Based on Google's battle-tested Zanzibar system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While OpenFGA Playground provides a &lt;a href="https://play.fga.dev/sandbox/" rel="noopener noreferrer"&gt;hosted application&lt;/a&gt; for experimentation, it comes with limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not open source&lt;/li&gt;
&lt;li&gt;Cannot be deployed in air-gapped environments&lt;/li&gt;
&lt;li&gt;Limited customization options&lt;/li&gt;
&lt;li&gt;Dependency on external services&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Project?
&lt;/h2&gt;

&lt;p&gt;I built OpenFGA Studio to address these limitations and provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fully open-source solution deployable anywhere&lt;/li&gt;
&lt;li&gt;Enhanced user experience for authorization modeling&lt;/li&gt;
&lt;li&gt;Complete control over your authorization data&lt;/li&gt;
&lt;li&gt;Customizable interface for specific needs&lt;/li&gt;
&lt;li&gt;Seamless integration with existing systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OpenFGA Studio streamlines the process of creating, testing, and managing authorization models, making complex authorization logic more accessible and manageable. Built with modern web technologies including React, TypeScript, and Material-UI, it provides a robust and intuitive interface for working with OpenFGA.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffttct6ftsm1wlmzx6d5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffttct6ftsm1wlmzx6d5a.png" alt="OpenFGA Studio Dark Mode" width="800" height="415"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwxsresmd7xffi359lgh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwxsresmd7xffi359lgh.png" alt="OpenFGA Studio Light Mode" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Store Management
&lt;/h3&gt;

&lt;p&gt;The interface provides a straightforward way for managing OpenFGA stores. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create new authorization stores&lt;/li&gt;
&lt;li&gt;Switch between existing stores&lt;/li&gt;
&lt;li&gt;View and manage store configurations&lt;/li&gt;
&lt;li&gt;Track model versions and changes&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  2. Visual Authorization Model Editor
&lt;/h3&gt;

&lt;p&gt;The model editor is a powerful interface for defining authorization rules with features including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syntax highlighting for better readability&lt;/li&gt;
&lt;li&gt;Real-time validation against OpenFGA schema&lt;/li&gt;
&lt;li&gt;Support for both DSL and JSON formats&lt;/li&gt;
&lt;li&gt;Error highlighting and suggestions&lt;/li&gt;
&lt;li&gt;Easy switching between model versions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Interactive Graph Visualization
&lt;/h3&gt;

&lt;p&gt;Understand your authorization model at a glance with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual representation of relationships between types&lt;/li&gt;
&lt;li&gt;Interactive node exploration&lt;/li&gt;
&lt;li&gt;Relationship flow visualization&lt;/li&gt;
&lt;li&gt;Dynamic updates as you modify the model&lt;/li&gt;
&lt;li&gt;Zoom and pan controls for large models&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Advanced Tuple Management
&lt;/h3&gt;

&lt;p&gt;The tuple management interface makes it easy to define and manage relationships:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Assisted tuple creation with type suggestions&lt;/li&gt;
&lt;li&gt;Support for direct (freeform) tuple input&lt;/li&gt;
&lt;li&gt;Batch operations for efficient management&lt;/li&gt;
&lt;li&gt;Reverse chronological listing of tuples&lt;/li&gt;
&lt;li&gt;Quick delete operations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Conditional Relationship Support
&lt;/h3&gt;

&lt;p&gt;Handle complex authorization scenarios with conditional relationships:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Dynamic condition parameter inputs&lt;/li&gt;
&lt;li&gt;Type-aware parameter validation&lt;/li&gt;
&lt;li&gt;Timestamp and duration support&lt;/li&gt;
&lt;li&gt;Visual feedback for condition state&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Comprehensive Query Testing
&lt;/h3&gt;

&lt;p&gt;Test your authorization rules with an intuitive interface:&lt;/p&gt;

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

&lt;p&gt;Features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual query builder&lt;/li&gt;
&lt;li&gt;Direct query input support&lt;/li&gt;
&lt;li&gt;Real-time query validation&lt;/li&gt;
&lt;li&gt;Historical query tracking&lt;/li&gt;
&lt;li&gt;Quick query replay&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Conditional Access Testing
&lt;/h3&gt;

&lt;p&gt;Test complex conditional access patterns:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Test time-based access rules&lt;/li&gt;
&lt;li&gt;Validate contextual conditions&lt;/li&gt;
&lt;li&gt;Dynamic parameter inputs&lt;/li&gt;
&lt;li&gt;Clear success/failure indicators&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  8. Developer-Friendly Features
&lt;/h3&gt;

&lt;p&gt;OpenFGA Studio is built with developers in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean, modern UI with dark mode support&lt;/li&gt;
&lt;li&gt;Keyboard shortcuts for common operations&lt;/li&gt;
&lt;li&gt;Copy/paste support for all fields&lt;/li&gt;
&lt;li&gt;Detailed error messages&lt;/li&gt;
&lt;li&gt;Response timing information&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Implementation
&lt;/h2&gt;

&lt;p&gt;OpenFGA Studio is built with modern web technologies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React with TypeScript for type safety&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Framework&lt;/strong&gt;: Material-UI for consistent, responsive design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Management&lt;/strong&gt;: React hooks and context for efficient state handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Tool&lt;/strong&gt;: Vite for fast development and optimized production builds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Integration&lt;/strong&gt;: Axios for reliable API communication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graph Visualization&lt;/strong&gt;: React Flow for interactive model visualization&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Use Cases
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authorization Modeling&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design role-based access control (RBAC) systems&lt;/li&gt;
&lt;li&gt;Implement attribute-based access control (ABAC)&lt;/li&gt;
&lt;li&gt;Model relationship-based authorization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Testing and Validation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify access control rules&lt;/li&gt;
&lt;li&gt;Test time-based permissions&lt;/li&gt;
&lt;li&gt;Validate complex conditional access&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Development and Debugging&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debug authorization issues&lt;/li&gt;
&lt;li&gt;Prototype authorization models&lt;/li&gt;
&lt;li&gt;Document access control patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;OpenFGA Studio simplifies the complex task of fine-grained authorization modeling and testing. Whether you're designing a new authorization system or maintaining an existing one, this tool provides the features needed to work efficiently with OpenFGA.&lt;/p&gt;

&lt;p&gt;The combination of visual tools, intuitive interfaces, and powerful testing capabilities makes it an essential tool for developers working with authorization systems. The tool continues to evolve with new features and improvements based on community feedback and real-world usage patterns.&lt;/p&gt;

&lt;p&gt;Feel free to contribute or request features at &lt;a href="https://github.com/prakashm88/openfga-studio/issues" rel="noopener noreferrer"&gt;OpenFGA Studio&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>typescript</category>
      <category>openfga</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Enhancing the VS Code Agent Mode to integrate with Local tools using Model Context Protocol (MCP)</title>
      <dc:creator>Prakash</dc:creator>
      <pubDate>Sun, 18 May 2025 20:17:53 +0000</pubDate>
      <link>https://forem.com/prakashm88/enhancing-the-vs-code-agent-mode-to-integrate-with-local-tools-using-model-context-protocol-mcp-ccn</link>
      <guid>https://forem.com/prakashm88/enhancing-the-vs-code-agent-mode-to-integrate-with-local-tools-using-model-context-protocol-mcp-ccn</guid>
      <description>&lt;p&gt;Enhancing the VS Code Agent Mode to integrate with Local tools using Model Context Protocol (MCP)&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Todo List Server with Model Context Protocol (MCP)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/prakashm88/mcp-todo-server" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fbadge%2FGitHub-Repository-green.svg" alt="GitHub Repository" width="116" height="20"&gt;&lt;/a&gt;  &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fbadge%2Fnode-%253E%253D22.0.0-brightgreen.svg" alt="Node Version" width="100" height="20"&gt;&lt;/a&gt; &lt;a href="https://github.com/microsoft/modelcontextprotocol" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fbadge%2FMCP%2520SDK-Latest-blue.svg" alt="MCP SDK" width="104" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This blog post walks through creating a Todo List server using the Model Context Protocol (MCP), demonstrating how to build AI-friendly tools that integrate seamlessly with VS Code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;strong&gt;Source Code&lt;/strong&gt;: The complete implementation is available on &lt;a href="https://github.com/prakashm88/mcp-todo-server" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Evolution of AI-Assisted Development&lt;/li&gt;
&lt;li&gt;What is Model Context Protocol (MCP)?&lt;/li&gt;
&lt;li&gt;Architecture Overview&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Setting Up the Project&lt;/li&gt;
&lt;li&gt;Implementing the MCP Server&lt;/li&gt;
&lt;li&gt;VS Code Integration&lt;/li&gt;
&lt;li&gt;Using the Todo MCP Server&lt;/li&gt;
&lt;li&gt;Next Steps and Improvements&lt;/li&gt;
&lt;li&gt;Troubleshooting&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Evolution of AI-Assisted Development
&lt;/h2&gt;

&lt;p&gt;I have been using VS Code with GitHub Copilot for development purposes. The introduction of text-based chat, which brought GPT capabilities directly into the IDE, was revolutionary. &lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Copilot and the Local Tools Gap
&lt;/h3&gt;

&lt;p&gt;GitHub Copilot has revolutionized how developers write code by providing intelligent code suggestions and completions. While it excels at understanding code context and generating relevant snippets, there has been a notable gap in its ability to interact with local development tools, intranet KBs, and execute actions in the development environment. This limitation means that while Copilot can suggest code, it cannot directly help with tasks like running commands, managing files, or interacting with local services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Mode: Bridging the Gap
&lt;/h3&gt;

&lt;p&gt;The introduction of Agent Mode in GitHub Copilot represents a significant step forward in AI-assisted development. It enables Copilot to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execute terminal commands&lt;/li&gt;
&lt;li&gt;Modify files directly&lt;/li&gt;
&lt;li&gt;Interact with the VS Code environment&lt;/li&gt;
&lt;li&gt;Handle project-specific tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This advancement transformed Copilot from a passive code suggestion tool into an active development partner that can help manage your entire development workflow. Here are some powerful capabilities and example interactions:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Build and Test Automation
&lt;/h4&gt;

&lt;p&gt;Trigger Maven builds with specific profiles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Run mvn clean install with the 'production' profile for my project"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute JUnit test suites:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Execute all JUnit tests in the UserServiceTest class"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run code quality tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Run ESLint on all JavaScript files in the src directory"
"Start a local Sonar analysis with coverage and security scan"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Documentation and Release Management
&lt;/h4&gt;

&lt;p&gt;Generate release documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Generate release notes for changes between tag v1.2.0 and v1.3.0"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technical documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Create a technical design document for the authentication service"
"Update the API documentation in Confluence for the new endpoints"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Project Management Integration
&lt;/h4&gt;

&lt;p&gt;JIRA ticket management:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Create a JIRA ticket for the memory leak bug we found in the login service"
"Convert all TODO comments in AuthService.java to JIRA tickets"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sprint management:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Update the status of PROJ-123 to 'In Review' and add a comment with the PR link"
"Show me all JIRA tickets assigned to me that are blocking the current sprint"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Cross-Repository Operations
&lt;/h4&gt;

&lt;p&gt;Multi-repo analysis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Check if the latest changes in the API repo are compatible with our client library"
"Run integration tests across both the frontend and backend repositories"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try the above prompts in VSCode Agent mode and see the magic. &lt;/p&gt;

&lt;p&gt;While these capabilities demonstrate the power of Agent Mode, they highlight a crucial challenge: the need for external API integrations. Each of these tasks requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication with external services (JIRA, Confluence, Sonar)&lt;/li&gt;
&lt;li&gt;Managing different API versions and endpoints&lt;/li&gt;
&lt;li&gt;Handling various authentication methods&lt;/li&gt;
&lt;li&gt;Maintaining connection states and sessions&lt;/li&gt;
&lt;li&gt;Coordinating operations across multiple systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This complexity creates significant overhead for developers who need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implement and maintain integration code for each service&lt;/li&gt;
&lt;li&gt;Handle authentication and authorization&lt;/li&gt;
&lt;li&gt;Manage API versioning and changes&lt;/li&gt;
&lt;li&gt;Deal with different response formats and error handling&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  MCP: The New Standard for AI Tool Integration
&lt;/h3&gt;

&lt;p&gt;Model Context Protocol (MCP) emerges as the next evolution in this space, providing a standardized way for AI models to interact with development tools and services. Unlike traditional approaches where AI assistants are limited to suggesting code, MCP enables:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Direct Tool Integration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI models can directly invoke local tools&lt;/li&gt;
&lt;li&gt;Real-time interaction with development environment&lt;/li&gt;
&lt;li&gt;Standardized communication protocol&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extensible Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom tool definitions&lt;/li&gt;
&lt;li&gt;Plugin-based system&lt;/li&gt;
&lt;li&gt;Easy integration with existing services&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Development Environment Awareness&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context-aware assistance&lt;/li&gt;
&lt;li&gt;Access to local resources&lt;/li&gt;
&lt;li&gt;Real-time feedback loop&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What is Model Context Protocol (MCP)?
&lt;/h2&gt;

&lt;p&gt;Model Context Protocol (MCP) is a specification that enables AI models to interact with external tools and services in a standardized way. It defines how tools can expose their functionality through a structured interface that AI models can understand and use.&lt;/p&gt;

&lt;p&gt;Key benefits of MCP that I have personally benefited from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standardized tool definitions with JSON Schema&lt;/li&gt;
&lt;li&gt;Real-time interaction capabilities&lt;/li&gt;
&lt;li&gt;Session management&lt;/li&gt;
&lt;li&gt;Built-in VS Code integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;More about MCP Architecture Documentation&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How MCP Tools Work
&lt;/h3&gt;

&lt;p&gt;Each MCP tool follows a standardized structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;toolName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What the tool does&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// JSON Schema definition of inputs&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;returns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// JSON Schema definition of outputs&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an AI model wants to use a tool:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It sends a request with the tool name and parameters&lt;/li&gt;
&lt;li&gt;The MCP server validates the request&lt;/li&gt;
&lt;li&gt;The tool executes with the provided parameters&lt;/li&gt;
&lt;li&gt;Results are returned in a standardized format&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This structured approach ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistent tool behavior&lt;/li&gt;
&lt;li&gt;Type safety throughout the system&lt;/li&gt;
&lt;li&gt;Easy tool discovery and documentation&lt;/li&gt;
&lt;li&gt;Predictable error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Here's how the different components interact in our MCP Todo implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    A[GitHub Copilot] --&amp;gt;|Natural Language Commands| B[VS Code]
    B --&amp;gt;|MCP Protocol| C[MCP Todo Server]
    C --&amp;gt;|CRUD Operations| D[(LowDB/Database)]
    C --&amp;gt;|Real-time Updates| B
    B --&amp;gt;|Command Results| A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js (v22 or higher)&lt;/li&gt;
&lt;li&gt;VS Code&lt;/li&gt;
&lt;li&gt;Basic understanding of Express.js&lt;/li&gt;
&lt;li&gt;npm or yarn package manager&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up the Project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;First, create a new project and initialize npm:
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install required dependencies:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @modelcontextprotocol/sdk express lowdb zod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this demonstration, we're using lowdb to manage tasks in a JSON file without actual integration with an external system. In a production environment, the lowdb functions can be replaced with actual JIRA CRUD API calls for end-to-end implementation. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the basic directory structure:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mcp-todo-server/
├── src/
│   ├── config/
│   ├── tools/
│   ├── utils/
│   └── server.js
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing the MCP Server
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Basic Server Setup
&lt;/h3&gt;

&lt;p&gt;We started with a basic Express server that implements the MCP protocol. The server uses StreamableHTTP for real-time communication and session management.&lt;/p&gt;

&lt;p&gt;Key components in &lt;code&gt;server.js&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Express server setup&lt;/li&gt;
&lt;li&gt;MCP SDK integration&lt;/li&gt;
&lt;li&gt;StreamableHTTP transport configuration&lt;/li&gt;
&lt;li&gt;Session management for maintaining tool state&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Database Configuration
&lt;/h3&gt;

&lt;p&gt;We used lowdb, a lightweight JSON database, to persist our todos. The database configuration in &lt;code&gt;config/db.js&lt;/code&gt; handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON file storage&lt;/li&gt;
&lt;li&gt;Basic CRUD operations&lt;/li&gt;
&lt;li&gt;Data persistence between server restarts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Implementing Todo Tools
&lt;/h3&gt;

&lt;p&gt;We implemented four main tools for managing todos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;createTodo&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates new todo items&lt;/li&gt;
&lt;li&gt;Validates input using Zod schema&lt;/li&gt;
&lt;li&gt;Returns the created todo with a unique ID&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;listTodos&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lists all todos or filters by completion status&lt;/li&gt;
&lt;li&gt;Formats output for easy reading&lt;/li&gt;
&lt;li&gt;Supports real-time updates&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;updateTodo&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updates todo completion status&lt;/li&gt;
&lt;li&gt;Validates input parameters&lt;/li&gt;
&lt;li&gt;Returns updated todo information&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;deleteTodo&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removes todos by ID&lt;/li&gt;
&lt;li&gt;Provides completion confirmation&lt;/li&gt;
&lt;li&gt;Handles error cases gracefully&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  VS Code Integration
&lt;/h2&gt;

&lt;p&gt;To enable VS Code to use our MCP server, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable Agent mode in VS Code. Click on the drop down just before the model listing and select agent from it. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmveq05q73ypc2m7waot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmveq05q73ypc2m7waot.png" alt="Enable Agent Mode in VS Code" width="704" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Then click on the Gear icon next to the speaker icon in the above image and select "Add more tools" then select "Add MCP Server"&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Then select HTTP or Server-Sent Events and provide the URL based on the server we created. In this case, it's &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;. Then select a name for the server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2atmepkpzw4aafakmej2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2atmepkpzw4aafakmej2.png" alt="Select HTTP or Server-Sent events" width="743" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

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

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

&lt;ol&gt;
&lt;li&gt;Alternatively you can Press &lt;code&gt;Ctrl+Shift+P&lt;/code&gt; (Windows/Linux) or &lt;code&gt;Cmd+Shift+P&lt;/code&gt; (Mac) to open the Command Palette. Type "Open Settings (JSON)" and select it and add the following configuration:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"my-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000/mcp"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the 4th step to verify if the server is added correctly after the first 3 steps are done. The User Settings option has Start, Stop, and Restart options. This step helped me identify if there are any issues with the MCP tools server effectively.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Reload VS Code to apply the changes or use the Start, Stop, Restart options in the settings.json as shown above. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After successful addition of the MCP server, you should see the tools listed when you click the gear icon in the Copilot chat window. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Using the Todo MCP Server
&lt;/h2&gt;

&lt;p&gt;Here are some example prompts you can use in VS Code with GitHub Copilot to interact with the todo server. Each example includes a screenshot of the actual interaction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Creating a Todo&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Prompt: "Create a new todo item called 'Review PR #123'"
   Response: Successfully created todo "Review PR #123"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Far0v69zrb9rfxs3jvmm9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Far0v69zrb9rfxs3jvmm9.png" alt="Creating a new todo" width="800" height="509"&gt;&lt;/a&gt;&lt;br&gt;
   &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fb0269xvf3godi6dfge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fb0269xvf3godi6dfge.png" alt="Execute Add" width="800" height="544"&gt;&lt;/a&gt;&lt;br&gt;
   &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hphshtttocjkkc5z01t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hphshtttocjkkc5z01t.png" alt="Create TODO success" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Listing Todos&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Prompt: "Show me all my todos"
   Response: Here are your todos:
   - Review PR #123 (Not completed)
   - Update documentation (Completed)
   - Setup test environment (Not completed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwjfn4mrwc7qze1vmkyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwjfn4mrwc7qze1vmkyw.png" alt="Listing all TODOs execute" width="800" height="473"&gt;&lt;/a&gt;&lt;br&gt;
   &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4dd7iovhadm94m8uod5i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4dd7iovhadm94m8uod5i.png" alt="Listing all TODOs" width="800" height="341"&gt;&lt;/a&gt;&lt;br&gt;
   &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4da39iybq7lvelq8pdiu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4da39iybq7lvelq8pdiu.png" alt="Listing all TODOs" width="800" height="607"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Updating a Todo&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Prompt: "Mark the todo about PR review as completed"
   Response: Updated "Review PR #123" to completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deleting a Todo&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Prompt: "Delete the todo about documentation"
   Response: Successfully deleted "Update documentation"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Filtering Todos&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Prompt: "Show me only completed todos"
   Response: Completed todos:
   - Review PR #123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps and Improvements
&lt;/h2&gt;

&lt;p&gt;Potential enhancements for the project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add user authentication&lt;/li&gt;
&lt;li&gt;Implement role-based access&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Advanced Features&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Due dates for todos&lt;/li&gt;
&lt;li&gt;Categories/tags&lt;/li&gt;
&lt;li&gt;Priority levels&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caching&lt;/li&gt;
&lt;li&gt;Database optimization&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests&lt;/li&gt;
&lt;li&gt;Integration tests&lt;/li&gt;
&lt;li&gt;Load testing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Common Issues and Solutions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Server Connection Issues&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify the server is running on port 3000&lt;/li&gt;
&lt;li&gt;Check VS Code settings for correct server URL&lt;/li&gt;
&lt;li&gt;Ensure no firewall blocking the connection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool Registration Problems&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Error: Tool 'createTodo' not found
   Solution: Check if server is properly initializing tools in server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Schema Validation Errors&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure todo items match the required schema&lt;/li&gt;
&lt;li&gt;Check Zod validation rules in tool implementations&lt;/li&gt;
&lt;li&gt;Verify JSON payload format&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Real-time Updates Not Working&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm SSE (Server-Sent Events) connection is established&lt;/li&gt;
&lt;li&gt;Check browser console for connection errors&lt;/li&gt;
&lt;li&gt;Verify StreamableHTTP transport configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Source Code Reference
&lt;/h3&gt;

&lt;p&gt;Key implementation files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server setup: &lt;a href="//src/server.js"&gt;&lt;code&gt;src/server.js&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tool implementations:

&lt;ul&gt;
&lt;li&gt;&lt;a href="//src/tools/createTodo.js"&gt;&lt;code&gt;src/tools/createTodo.js&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//src/tools/updateTodo.js"&gt;&lt;code&gt;src/tools/updateTodo.js&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//src/tools/deleteTodo.js"&gt;&lt;code&gt;src/tools/deleteTodo.js&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//src/tools/listTodos.js"&gt;&lt;code&gt;src/tools/listTodos.js&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Database configuration: &lt;a href="//src/config/db.js"&gt;&lt;code&gt;src/config/db.js&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

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

&lt;p&gt;We've successfully built a fully functional MCP-compatible Todo server that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implements CRUD operations&lt;/li&gt;
&lt;li&gt;Maintains persistent storage&lt;/li&gt;
&lt;li&gt;Provides real-time updates&lt;/li&gt;
&lt;li&gt;Integrates seamlessly with VS Code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This implementation serves as a great starting point for building more complex MCP tools and understanding how AI models can interact with custom tools through the Model Context Protocol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/modelcontextprotocol" rel="noopener noreferrer"&gt;MCP SDK Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express.js Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/colinhacks/zod" rel="noopener noreferrer"&gt;Zod Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/typicode/lowdb" rel="noopener noreferrer"&gt;LowDB Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Report bugs by creating &lt;a href="https://github.com/prakashm88/mcp-todo-server/issues" rel="noopener noreferrer"&gt;issues&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Suggest improvements through &lt;a href="https://github.com/prakashm88/mcp-todo-server/discussions" rel="noopener noreferrer"&gt;discussions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Help improve documentation&lt;/li&gt;
&lt;li&gt;Originally posted at &lt;a href="https://itechgenie.com/myblog/2025/05/enhancing-the-vs-code-agent-mode-to-integrate-with-local-tools-using-model-context-protocol-mcp" rel="noopener noreferrer"&gt;ITechGenie&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agenticai</category>
      <category>mcp</category>
      <category>vscode</category>
      <category>ai</category>
    </item>
    <item>
      <title>Creating a browser extension for Chrome / Edge</title>
      <dc:creator>Prakash</dc:creator>
      <pubDate>Mon, 17 Jun 2024 20:32:40 +0000</pubDate>
      <link>https://forem.com/prakashm88/creating-a-browser-extension-for-chrome-edge-3d69</link>
      <guid>https://forem.com/prakashm88/creating-a-browser-extension-for-chrome-edge-3d69</guid>
      <description>&lt;p&gt;Creating a browser extension has never been easier, thanks to the comprehensive documentation and support provided by browser vendors. Below, I’ll walk through the steps to create a simple extension for both Chrome and Microsoft Edge using Manifest V3. We will use this tool to print the list of HTTP requests that are fired in a given webpage and list it in the extension.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basics of extensions:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Manifests – A manifest is a JSON file that contains metadata about a browser extension, such as its name, version, permissions, and the files it uses. It serves as the blueprint for the extension, informing the browser about the extension’s capabilities and how it should be loaded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Components of a Manifest File:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are the key components typically found in a Manifest V3 file:&lt;/p&gt;

&lt;p&gt;1. Manifest Version: There are different versions of the manifest file, with Manifest V3 being the latest and most widely adopted version. Manifest V3 introduces several changes aimed at improving security, privacy, and performance with lot of controversies around it. Read more about the controversies at Ghostery.&lt;br&gt;&lt;br&gt;
2. Name and Version: These fields define the name and version of the extension. Choose a unique name and version. An excellent guide of version semantics is available here.&lt;br&gt;&lt;br&gt;
3. Description: A short description of the extension’s functionality.&lt;br&gt;&lt;br&gt;
4. Action: Defines the default popup and icon for the browser action (e.g., toolbar button).&lt;br&gt;&lt;br&gt;
5. Background: Specifies the background script that runs in the background and can handle events like network requests and alarms.&lt;br&gt;&lt;br&gt;
6. Content Scripts: Defines scripts and stylesheets to be injected into matching web pages.&lt;br&gt;&lt;br&gt;
7. Permissions: Lists the permissions the extension needs to operate, such as access to tabs, storage, and specific websites.&lt;br&gt;&lt;br&gt;
8. Icons: Specifies the icons for the extension in different sizes. For this post I created a simple icon using &lt;a href="https://designer.microsoft.com/" rel="noopener noreferrer"&gt;Microsoft Designer.&lt;/a&gt; I gave a simple prompt with the description above and I got the below image. Extension requires different sizes for showing it in different places. I used &lt;a href="https://alexleybourne.github.io/chrome-extension-icon-generator/" rel="noopener noreferrer"&gt;Chrome Extension Icon Generator&lt;/a&gt; and generated different sizes as needed.&lt;/p&gt;

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

&lt;p&gt;9. Web Accessible Resources: Defines which resources can be accessed by web pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a project structure as follows:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HttpRequestViewer/
|-- manifest.json
|-- popup.html
|-- popup.js
|-- background.js
|-- history.html
|-- history.js
|-- popup.css
|-- styles.css
|-- icons/
    |-- icon.png
    |-- icon16.png
    |-- icon32.png
    |-- icon48.png
    |-- icon128.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Manifest.json&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "API Request Recorder",
  "description": "Extension to record all the HTTP request from a webpage.",
  "version": "0.0.1",
  "manifest\_version": 3,
  "host\_permissions": \[""\],
  "permissions": \["activeTab", "webRequest", "storage"\],
  "action": {
    "default\_popup": "popup.html",
    "default\_icon": "icons/icon.png"
  },
  "background": {
    "service\_worker": "background.js"
  },
  "icons": {
    "16": "icons/icon16.png",
    "32": "icons/icon32.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  },
  "content\_security\_policy": {
    "extension\_pages": "script-src 'self'; object-src 'self';"
  },
  "web\_accessible\_resources": \[{ "resources": \["images/\*.png"\], "matches": \["https://\*/\*"\] }\]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;popup.html&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
We have two options with the extension.&lt;/p&gt;

&lt;p&gt;1. A button with record option to start recording all the HTTP requests&lt;br&gt;&lt;br&gt;
2. Link to view the history of HTTP Requests recorded&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;API Request Recorder&amp;lt;/title&amp;gt;

    &amp;lt;link rel="stylesheet" href="popup.css" /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class="heading"&amp;gt;
      &amp;lt;img class="logo" src="icons/icon48.png" /&amp;gt;
      &amp;lt;h1&amp;gt;API Request Recorder&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;button id="startStopRecord"&amp;gt;Record&amp;lt;/button&amp;gt;

    &amp;lt;div class="button-group"&amp;gt;
      &amp;lt;a href="#" id="history"&amp;gt;View Requests&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;script src="popup.js"&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;popup.js&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Two event listeners are registered for recording (with start / stop) and viewing history.&lt;br&gt;&lt;br&gt;
First event is used to send a message to the background.js, while the second one instructs chrome to open the history page in new tab.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.getElementById("startStopRecord").addEventListener("click", () =&amp;gt; {
  chrome.runtime.sendMessage({ action: "startStopRecord" });
});

document.getElementById("history").addEventListener("click", () =&amp;gt; {
  chrome.tabs.create({ url: chrome.runtime.getURL("/history.html") });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;history.html&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;History&amp;lt;/title&amp;gt;
    &amp;lt;link rel="stylesheet" href="styles.css" /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;History Page&amp;lt;/h1&amp;gt;
    &amp;lt;table&amp;gt;
      &amp;lt;thead&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;th&amp;gt;Method&amp;lt;/th&amp;gt;
          &amp;lt;th&amp;gt;URL&amp;lt;/th&amp;gt;
          &amp;lt;th&amp;gt;Body&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;/thead&amp;gt;
      &amp;lt;tbody id="recorded-data-body"&amp;gt;
        &amp;lt;!-- Data will be populated here --&amp;gt;
      &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;
    &amp;lt;script src="history.js"&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;history.js&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Requests background.js to “getRecordedData” and renders the result in the html format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.addEventListener("DOMContentLoaded", () =&amp;gt; {
  chrome.runtime.sendMessage({ action: "getRecordedData" }, (response) =&amp;gt; {
    const tableBody = document.getElementById("recorded-data-body");
    response.forEach((record) =&amp;gt; {
      const row = document.createElement("tr");
      const urlCell = document.createElement("td");
      const methodCell = document.createElement("td");
      const bodyCell = document.createElement("td");

      urlCell.textContent = record.url;
      methodCell.textContent = record.method;
      bodyCell.textContent = record.body;

      row.appendChild(methodCell);
      row.appendChild(urlCell);
      row.appendChild(bodyCell);
      tableBody.appendChild(row);
    });
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;background.js&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Background JS works as a service worker for this extension, listening and handling events.&lt;br&gt;&lt;br&gt;
The background script does not have access to directly manipulate the user page content, but can post results back for the popup/history script to handle the cosmetic changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let isRecording = false;
let recordedDataList = \[\];

chrome.runtime.onMessage.addListener((message, sender, sendResponse) =&amp;gt; {
  console.log("Obtined message: ", message);
  if (message.action === "startStopRecord") {
    if (isRecording) {
      isRecording = false;
      console.log("Recording stopped...");
      sendResponse({ recorder: { status: "stopped" } });
    } else {
      isRecording = true;
      console.log("Recording started...");
      sendResponse({ recorder: { status: "started" } });
    }
  } else if (message.action === "getRecordedData") {
    sendResponse(recordedDataList);
  } else {
    console.log("Unhandled action ...");
  }
});

chrome.webRequest.onBeforeRequest.addListener(
  (details) =&amp;gt; {
    if (isRecording) {
      let requestBody = "";
      if (details.requestBody) {
        if (details.requestBody.formData) {
          requestBody = JSON.stringify(details.requestBody.formData);
        } else if (details.requestBody.raw) {
          requestBody = new TextDecoder().decode(new Uint8Array(details.requestBody.raw\[0\].bytes));
        }
      }
      recordedDataList.push({
        url: details.url,
        method: details.method,
        body: requestBody,
      });
      console.log("Recorded Request:", {
        url: details.url,
        method: details.method,
        body: requestBody,
      });
    }
  },
  { urls: \[""\] },
  \["requestBody"\]
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lets load the Extension&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All set, now lets load the extension and test it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Open Chrome/Edge and go to chrome://extensions/ or edge://extensions/ based on your browser.&lt;/li&gt;
&lt;li&gt;  Enable “Developer mode” using the toggle in the top right corner.&lt;/li&gt;
&lt;li&gt;  Click “Load unpacked” and select the directory of your extension.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcmkd7zf53t2hkmcejz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcmkd7zf53t2hkmcejz8.png" alt="Load extension" width="150" height="150"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ejm94igwpnlzm582mwj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ejm94igwpnlzm582mwj.png" alt="upload extension" width="150" height="150"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dx9vv32p5iln3q8aidw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dx9vv32p5iln3q8aidw.png" alt="upload extension 1" width="150" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Your extension should now be loaded, and you can interact with it using the popup.&lt;/li&gt;
&lt;li&gt;  When you click the “Record” button, it will start logging API requests to the console.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://itechgenie.com/myblog/wp-content/uploads/sites/2/2024/06/extention-loaded-2.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa9zvfq7fba6y6pxhnbhj.png" width="150" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Click the “Record” button again and hit the “View requests” link in the popup to view the history of APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have a sample page (&lt;a href="https://itechgenie.com/demos/apitesting/index.html" rel="noopener noreferrer"&gt;https://itechgenie.com/demos/apitesting/index.html&lt;/a&gt;) with 4 API calls, which also loads images based on the API responses. You could see all the API requests that is fired from the page including the JS, CSS, Images and API calls.&lt;/p&gt;

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

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

&lt;p&gt;Now its up to the developers imagination to build the extension to handle these APIs request and response data and give different experience.&lt;/p&gt;

&lt;p&gt;Code is available in GitHub at &lt;a href="https://github.com/ITechGenie/HttpRequestViewer" rel="noopener noreferrer"&gt;HttpRequestViewer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Post originally shared at &lt;a href="https://itechgenie.com/myblog/2024/06/browser-extension-sample-chrome-edge/" rel="noopener noreferrer"&gt;ITechGenie.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>chrome</category>
      <category>edge</category>
      <category>extension</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
