<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pawel Janda</title>
    <description>The latest articles on DEV Community by Pawel Janda (@paweljanda).</description>
    <link>https://dev.to/paweljanda</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%2F2843311%2Ff9b9c2e4-a877-43ae-82dc-8cb0375a0b1f.png</url>
      <title>DEV Community: Pawel Janda</title>
      <link>https://dev.to/paweljanda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paweljanda"/>
    <language>en</language>
    <item>
      <title>11 Things experienced developers do when Claude Code hits the limit.</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Sun, 08 Mar 2026 21:01:38 +0000</pubDate>
      <link>https://dev.to/paweljanda/11-things-experienced-developers-do-when-claude-code-hits-the-limit-27nk</link>
      <guid>https://dev.to/paweljanda/11-things-experienced-developers-do-when-claude-code-hits-the-limit-27nk</guid>
      <description>&lt;p&gt;If you use tools like &lt;strong&gt;Claude Code, Cursor, or other AI coding agents&lt;/strong&gt;, you've seen this message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Usage limit reached
Try again in 4h 59m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your flow stops. The AI is locked. And your first instinct is to scroll X or grab another coffee.&lt;/p&gt;

&lt;p&gt;I used to do the same. Then I started tracking how I spent those cooldown windows — and realized something:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The quality of my next AI session was almost entirely decided by what I did during the wait.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI tools are fast at &lt;strong&gt;generating code&lt;/strong&gt;. But they still depend on humans for architecture, debugging, product thinking, and context. If you feed them better input, you get dramatically better output.&lt;/p&gt;

&lt;p&gt;Here are &lt;strong&gt;11 things worth doing while the limit resets&lt;/strong&gt; — ranked roughly by impact.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Write Better Input for the Next AI Run
&lt;/h2&gt;

&lt;p&gt;When AI output is bad, most developers blame the model. But the problem is almost always the input — either the prompt was vague, or the AI lacked context about what already exists in the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of a prompt that backfires:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I had a prompt that said &lt;em&gt;"add email notifications for team invites"&lt;/em&gt;. Claude Code generated a complete email setup — installed Nodemailer, created SMTP transport config, built HTML templates from scratch with inline CSS. The problem: my project already had Resend integrated with a shared &lt;code&gt;sendEmail()&lt;/code&gt; helper and a template system in &lt;code&gt;src/lib/email/&lt;/code&gt;. The AI just didn't pick up on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What better input looks like:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a small task, a precise prompt with constraints is enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add email notification when a user is invited to a workspace.

Constraints:
- use existing sendEmail() helper from src/lib/email/client.ts
- use the existing template system in src/lib/email/templates/
- follow the same pattern as the welcome email in welcome.ts

Do NOT:
- install new email libraries
- create new transport/provider config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a larger feature, write a short spec before the session starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Feature: Team Invitations&lt;/span&gt;

&lt;span class="gs"&gt;**Goal:**&lt;/span&gt; Allow workspace owners to invite new members via email.

&lt;span class="gs"&gt;**Flow:**&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Owner clicks "Invite" → enters email
&lt;span class="p"&gt;2.&lt;/span&gt; System generates a signed invite link (expires in 48h)
&lt;span class="p"&gt;3.&lt;/span&gt; Recipient clicks link → creates account or joins if existing

&lt;span class="gs"&gt;**Data model:**&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; invitations table: id, workspace_id, email, token, status, expires_at

&lt;span class="gs"&gt;**Edge cases:**&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Invite clicked after expiry → show "expired" page with re-request option
&lt;span class="p"&gt;-&lt;/span&gt; User invited to workspace they're already in → friendly message, no duplicate
&lt;span class="p"&gt;-&lt;/span&gt; Owner revokes invite before acceptance → soft delete, invalidate token

&lt;span class="gs"&gt;**Out of scope:**&lt;/span&gt; role-based permissions (separate feature)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The common thread: &lt;strong&gt;negative constraints&lt;/strong&gt; (what not to do), &lt;strong&gt;pointers to existing code&lt;/strong&gt; (what to reuse), and &lt;strong&gt;explicit edge cases&lt;/strong&gt; (what not to forget). These three things eliminate most "redo" sessions. When I started writing input like this, the number of failed AI runs dropped by roughly half.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Break the Next Feature Into Atomic Tasks
&lt;/h2&gt;

&lt;p&gt;AI performs best with &lt;strong&gt;small, deterministic tasks&lt;/strong&gt; — not vague feature requests.&lt;/p&gt;

&lt;p&gt;You might think: &lt;em&gt;"Claude Code has plan mode — doesn't it break down tasks automatically?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It does, and it's good. But there's a key difference. Plan mode breaks down a task &lt;em&gt;within a single AI session&lt;/em&gt;. The AI decides the steps, the order, and the scope — and if it makes a wrong architectural decision in step 2, steps 3–6 inherit that mistake. By the time you notice, you've burned most of your session on code you'll throw away.&lt;/p&gt;

&lt;p&gt;The alternative: &lt;strong&gt;you&lt;/strong&gt; define the steps during the cooldown, each with a clear verification checkpoint.&lt;/p&gt;

&lt;p&gt;Bad:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Implement billing"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even with plan mode, this gives the AI too many architectural decisions at once.&lt;/p&gt;

&lt;p&gt;Better — a human-defined execution plan with checkpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Create Stripe customer on signup (use existing User model)
2. Add subscription table: user_id, stripe_subscription_id, status, plan, current_period_end
3. POST /api/billing/checkout — create Stripe Checkout session
4. POST /api/webhooks/stripe — handle checkout.session.completed, invoice.paid, customer.subscription.deleted
5. GET /api/billing/status — return current plan and expiry
6. Add billing UI component reading from /api/billing/status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't mean every step must be a brand new session. For smaller features, you can run through several steps in one conversation — just verify the output at each checkpoint before moving on. For larger features, separate sessions (or even parallel runs via &lt;code&gt;git worktree&lt;/code&gt;) give you cleaner isolation. Claude Code supports resuming conversations and rewinding to earlier checkpoints, so you have flexibility here.&lt;/p&gt;

&lt;p&gt;The key principle: &lt;strong&gt;you control the granularity and the verification points&lt;/strong&gt;, not the AI. Plan mode is great &lt;em&gt;within&lt;/em&gt; each step — but the high-level breakdown should be yours.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Make Your Repo AI-Readable
&lt;/h2&gt;

&lt;p&gt;Claude Code scans your repository to build context. But most repos are optimized for humans (at best) — not for LLMs.&lt;/p&gt;

&lt;p&gt;The single highest-impact thing you can do: &lt;strong&gt;maintain a &lt;code&gt;CLAUDE.md&lt;/code&gt; file in your project root.&lt;/strong&gt; This is Claude Code's native mechanism for persistent project instructions. When you run &lt;code&gt;/init&lt;/code&gt;, it generates a starter &lt;code&gt;CLAUDE.md&lt;/code&gt; automatically — but the real value comes from curating it over time.&lt;/p&gt;

&lt;p&gt;A good &lt;code&gt;CLAUDE.md&lt;/code&gt; includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md&lt;/span&gt;

&lt;span class="gu"&gt;## Build &amp;amp; test commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm run dev`&lt;/span&gt; — start dev server
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm run test`&lt;/span&gt; — run Vitest
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm run test:e2e`&lt;/span&gt; — run Playwright
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm run lint`&lt;/span&gt; — ESLint + Prettier check

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
Frontend (Next.js App Router) → API Routes → Background Workers (BullMQ) → PostgreSQL

&lt;span class="gu"&gt;## Key patterns&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; All DB access through Drizzle ORM (src/db/)
&lt;span class="p"&gt;-&lt;/span&gt; Auth via Supabase, middleware in src/middleware.ts
&lt;span class="p"&gt;-&lt;/span&gt; Stripe webhooks handled in src/app/api/webhooks/stripe/
&lt;span class="p"&gt;-&lt;/span&gt; Shared types in src/types/ — always import from here
&lt;span class="p"&gt;-&lt;/span&gt; Email sending via shared helper in src/lib/email/client.ts

&lt;span class="gu"&gt;## Naming conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; API routes: kebab-case (e.g., /api/billing-status)
&lt;span class="p"&gt;-&lt;/span&gt; Components: PascalCase, one component per file
&lt;span class="p"&gt;-&lt;/span&gt; Database columns: snake_case

&lt;span class="gu"&gt;## What to avoid&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Do not install new dependencies without checking if an existing helper covers the use case
&lt;span class="p"&gt;-&lt;/span&gt; Do not create new API clients — reuse the ones in src/lib/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more granular rules, you can also use &lt;strong&gt;&lt;code&gt;.claude/rules/&lt;/code&gt;&lt;/strong&gt; — a directory of markdown files scoped to specific parts of the codebase. For example, &lt;code&gt;.claude/rules/api.md&lt;/code&gt; could contain conventions that apply only when Claude works on API routes.&lt;/p&gt;

&lt;p&gt;Claude Code also has &lt;strong&gt;auto memory&lt;/strong&gt; — it can persist learnings from corrections you make during a session. If you tell it "always use pino, not console.log" during a run, it can remember that for future sessions. Between the &lt;code&gt;CLAUDE.md&lt;/code&gt;, scoped rules, and auto memory, you're building a persistent knowledge base that compounds over time.&lt;/p&gt;

&lt;p&gt;Beyond these Claude Code-specific mechanisms, general repo hygiene still matters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explicit type definitions.&lt;/strong&gt; AI hallucinates types less when real ones exist. A single &lt;code&gt;types/billing.ts&lt;/code&gt; file prevents dozens of wrong assumptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short comments on non-obvious logic.&lt;/strong&gt; Not &lt;code&gt;// increment counter&lt;/code&gt; — but &lt;code&gt;// Stripe sends checkout.session.completed before invoice.paid, so we must handle idempotency here&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I spent one cooldown window writing my first &lt;code&gt;CLAUDE.md&lt;/code&gt; and restructuring the &lt;code&gt;src/&lt;/code&gt; folder. The next Claude Code session required zero corrections.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Review and Refactor AI-Generated Code
&lt;/h2&gt;

&lt;p&gt;AI-generated code has a specific failure pattern: &lt;strong&gt;it works, but it's not good.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Common issues I keep finding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate logic.&lt;/strong&gt; The AI builds a similar helper to one that already exists three files away. You end up with &lt;code&gt;formatDate()&lt;/code&gt; in four different files. Claude Code's auto memory and &lt;code&gt;CLAUDE.md&lt;/code&gt; reduce this problem — if you've documented shared utilities, the AI is more likely to reuse them. But it still happens, especially across longer projects with many sessions. Code review catches what memory doesn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over-abstraction.&lt;/strong&gt; A simple API call wrapped in a factory pattern with a strategy interface — for a function called once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Naming that technically describes the function but doesn't communicate intent.&lt;/strong&gt; &lt;code&gt;handleData()&lt;/code&gt; vs. &lt;code&gt;syncSubscriptionStatusFromStripe()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing cleanup.&lt;/strong&gt; Dangling event listeners, unclosed connections, unused imports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This kind of review is hard to automate and hard to delegate to AI. It requires understanding the whole codebase — which is exactly what humans do better than LLMs right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical approach:&lt;/strong&gt; pick one file per cooldown and read it slowly, line by line. You'll find things.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Write the Tests AI Missed
&lt;/h2&gt;

&lt;p&gt;AI can generate tests, but it consistently misses the cases that matter.&lt;/p&gt;

&lt;p&gt;What AI writes: happy path tests confirming the function returns the expected output.&lt;/p&gt;

&lt;p&gt;What AI skips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens when the Stripe webhook fires twice for the same event? (idempotency)&lt;/li&gt;
&lt;li&gt;What if the database write succeeds but the API response times out? (partial failure)&lt;/li&gt;
&lt;li&gt;What if a user submits a form with valid-looking but malicious input? (validation edge cases)&lt;/li&gt;
&lt;li&gt;What if two users accept the same invite simultaneously? (concurrency)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the tests that catch production bugs. Spend the cooldown writing 3–5 of them for whatever feature you just built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework note:&lt;/strong&gt; if you're still on Jest for a Vite/Next.js project, consider switching to Vitest — it's faster, natively supports ESM, and the migration is usually trivial. For E2E, Playwright is the default choice I'd reach for today.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Actually Use Your Product
&lt;/h2&gt;

&lt;p&gt;This is the most underrated item on the list.&lt;/p&gt;

&lt;p&gt;AI writes code. It does &lt;strong&gt;not&lt;/strong&gt; experience the product. It doesn't feel the friction of a confusing onboarding flow, the frustration of a slow page load, or the confusion of an unclear error message.&lt;/p&gt;

&lt;p&gt;Things I've found by actually clicking through my own app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A signup form that accepted empty strings because validation only ran on blur&lt;/li&gt;
&lt;li&gt;A dashboard that took 4 seconds to load because it fetched all data on mount with no pagination&lt;/li&gt;
&lt;li&gt;A mobile layout where the primary CTA was hidden below the fold&lt;/li&gt;
&lt;li&gt;An error page that said "Something went wrong" with no way to recover&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these would show up in unit tests. None of them would be caught by AI code review. They required a human sitting in front of the screen and thinking &lt;em&gt;"would I use this?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Spend 15 minutes clicking through the app the way your users would.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Debug What AI Built — Properly
&lt;/h2&gt;

&lt;p&gt;AI generates code confidently. That confidence masks bugs that only appear at runtime.&lt;/p&gt;

&lt;p&gt;Most common patterns I've seen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wrong API assumptions.&lt;/strong&gt; AI may generate code against examples or docs that don't match your exact API version or current SDK behavior. It builds something that looks right, passes a quick glance, and breaks at runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async race conditions.&lt;/strong&gt; Two state updates that work individually but produce inconsistent UI when they resolve in unexpected order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silent failures.&lt;/strong&gt; A &lt;code&gt;catch&lt;/code&gt; block that logs the error but doesn't propagate it — so the function appears to succeed while the data is never saved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Debugging workflow that actually works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reproduce the bug with a specific, repeatable input&lt;/li&gt;
&lt;li&gt;Add structured logging around the suspected area (not &lt;code&gt;console.log("here")&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Isolate: does the bug come from the AI-written code or from how it interacts with existing code?&lt;/li&gt;
&lt;li&gt;Fix manually if small, or write a precise bug-fixing prompt for the next AI session&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key insight: AI is great at writing code but bad at understanding &lt;em&gt;why something doesn't work&lt;/em&gt;. Debugging is still a human skill.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Improve System Design and Architecture
&lt;/h2&gt;

&lt;p&gt;Cooldown windows are perfect for zooming out.&lt;/p&gt;

&lt;p&gt;When you're deep in implementation, it's easy to lose sight of the bigger picture. Use the break to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the current data model going to scale to the next 10x of users?&lt;/li&gt;
&lt;li&gt;Are there synchronous operations that should be async (webhooks, email sending, PDF generation)?&lt;/li&gt;
&lt;li&gt;Is there a single point of failure that would take down the whole system?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even a quick diagram clarifies thinking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → Next.js API → [sync: DB write] → Response
                     → [async: BullMQ] → Send email
                                        → Sync to Stripe
                                        → Generate invoice PDF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt; Excalidraw for quick sketches, Mermaid for version-controlled diagrams, Eraser.io for collaborative architecture docs. Pick whatever you'll actually use — the diagram itself matters less than the thinking you do while drawing it.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Audit Security and Error Handling
&lt;/h2&gt;

&lt;p&gt;AI-generated code is notoriously weak on security and error handling. It optimizes for "does it work?" not "does it fail safely?"&lt;/p&gt;

&lt;p&gt;This is partly a general software engineering problem — edge cases and failure modes have always been underrepresented in generated code. But working with an AI agent adds a specific layer: the agent can execute commands, modify files, and make network calls on your behalf. That means security review has two dimensions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application security checklist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are all API routes behind auth middleware? (AI often forgets to protect new endpoints)&lt;/li&gt;
&lt;li&gt;Is user input validated on the server, not just the client? (AI loves client-only validation)&lt;/li&gt;
&lt;li&gt;Are database queries parameterized? (Usually yes with ORMs, but check raw queries)&lt;/li&gt;
&lt;li&gt;Are rate limits in place for auth endpoints and public APIs?&lt;/li&gt;
&lt;li&gt;Are permissions checked — not just "is the user logged in" but "does this user have access to &lt;em&gt;this&lt;/em&gt; resource"?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Agent workflow security checklist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review commands the agent proposes before approving them, especially anything that modifies infrastructure, env files, or deployment configs&lt;/li&gt;
&lt;li&gt;Be cautious when the agent works with untrusted data — user-submitted content, webhook payloads, or third-party API responses should not be passed raw into prompts&lt;/li&gt;
&lt;li&gt;Keep critical files (production configs, secrets, CI/CD pipelines) outside the agent's default write scope&lt;/li&gt;
&lt;li&gt;For riskier operations, consider running Claude Code in an isolated environment or VM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Error handling checklist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do API routes return appropriate HTTP status codes, or is everything 200/500?&lt;/li&gt;
&lt;li&gt;Are external service calls (Stripe, email, S3) wrapped in retries with exponential backoff?&lt;/li&gt;
&lt;li&gt;Do errors include enough context to debug, without leaking internals to the client?&lt;/li&gt;
&lt;li&gt;Is there a global error boundary in the frontend?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I run through these checklists after every major feature. It takes 15 minutes and has prevented multiple production incidents.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Set Up Observability (Before You Need It)
&lt;/h2&gt;

&lt;p&gt;This is the thing you'll wish you'd done before the first production incident.&lt;/p&gt;

&lt;p&gt;AI rarely sets up observability well unless you ask for it explicitly. It writes application code, not operational infrastructure. But without logs, metrics, and traces, debugging production becomes guesswork.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minimum viable observability stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured logging&lt;/strong&gt; — JSON logs with consistent fields (event, user_id, duration_ms, error). Use pino for Node.js — it's fast and structured by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error tracking&lt;/strong&gt; — Sentry or equivalent. Captures unhandled exceptions with full stack traces and context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic metrics&lt;/strong&gt; — response times, error rates, queue depth. Prometheus + Grafana if self-hosted, or your cloud provider's built-in dashboards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tracing&lt;/strong&gt; (when you need it) — OpenTelemetry for distributed tracing across services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need all of this on day one. Start with structured logging and error tracking — those two alone cover 80% of debugging scenarios.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Prepare the Next AI Session
&lt;/h2&gt;

&lt;p&gt;Instead of improvising prompts when the limit resets, prepare a short session brief. The difference between a productive AI session and a wasted one is usually decided before the first prompt.&lt;/p&gt;

&lt;p&gt;What I write during every cooldown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Session prep — March 8

### Current state
Invitation feature is half-done. The data model and API endpoint (`POST /api/invitations`) are merged and working.

Still missing:
- acceptance flow
- email sending
- edge case handling

### Known issues from the last session
- invite token generation needs a cryptographically secure approach
- no expiry check on the acceptance endpoint yet
- email template exists but is not wired into the invite flow

### Tasks (in order)
1. **Fix token generation**
   Replace the current token generation in `src/api/invitations/create.ts`.
   Do this first so all new test data uses the correct format.

2. **Acceptance endpoint**
   Implement `GET /api/invitations/accept?token=xxx`
   - validate token exists and status = pending
   - check `expires_at &amp;gt; now` — return 410 if expired
   - if user exists → add to workspace
   - if not → redirect to `/signup?invite=xxx`
   - set invitation status to `accepted`

   Reference: existing membership creation logic in `src/api/workspaces/members.ts`

3. **Wire email sending**
   On successful invite creation, send email via the existing `sendEmail()` helper.
   Template: `src/lib/email/templates/invite.tsx`

4. **Integration tests**
   Cover:
   - happy path
   - expired token (410)
   - already accepted token (409)
   - existing user
   - new user
   - duplicate invite to the same email

   Use the test factory in `src/test/factories/`.

### Done when
- all tests pass
- manual test succeeds end-to-end
- no TypeScript errors
- no lint warnings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What makes this useful is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;current state&lt;/strong&gt; tells the AI what already works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;known issues&lt;/strong&gt; prevent rediscovering the same problems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ordered tasks&lt;/strong&gt; stop the AI from jumping ahead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;done when&lt;/strong&gt; gives both you and the model a stopping condition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the limit resets, I start with the current task, the relevant context, and the success criteria. No warmup, no context switching, no vague "continue this feature" prompt.&lt;/p&gt;

&lt;p&gt;This workflow — plan during cooldown, execute during active session — is one of the biggest productivity multipliers I've found in AI-assisted development.&lt;/p&gt;




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

&lt;p&gt;AI coding tools change the rhythm of development.&lt;/p&gt;

&lt;p&gt;It's no longer &lt;em&gt;write code → test → write more code&lt;/em&gt;. It's:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI executes → Human reviews → Human plans → AI executes again.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The developers getting the most out of these tools aren't the ones typing the fastest or writing the cleverest prompts. They're the ones who use every minute between sessions to give the AI better context, clearer specs, and fewer decisions to make.&lt;/p&gt;

&lt;p&gt;The limit gives you a built-in planning phase. Use it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;How do you spend the time between AI sessions? I'm curious what other developers have figured out — drop a comment below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Claude Code Club | Issue #2</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Wed, 11 Feb 2026 20:40:03 +0000</pubDate>
      <link>https://dev.to/paweljanda/claude-code-club-issue-2-385</link>
      <guid>https://dev.to/paweljanda/claude-code-club-issue-2-385</guid>
      <description>&lt;h1&gt;
  
  
  🤖 Claude Code Club | Issue #2
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Engineering Log: February 11, 2026&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Focus: Agent Teams, CI/CD Orchestration, and Opus 4.6 Benchmarks.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The honeymoon phase of "asking an AI to write a function" is over. We are now in the era of &lt;strong&gt;Agentic Swarms&lt;/strong&gt;. This week, the release of Opus 4.6 and the introduction of "Agent Teams" has fundamentally changed the modern SDLC (Software Development Life Cycle).&lt;/p&gt;




&lt;h2&gt;
  
  
  📺 Top 10 High-Signal Deep Dives (Uniquely for Issue #2)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Claude Code's New Agent Teams Are Insane (Opus 4.6)&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=VWngYUC63po" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Bart Slodyczka breaks down the "Lead Agent" architecture. Instead of a single worker, Claude now spawns specialized sub-agents for testing, docs, and logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;My Claude Code Just Cloned Itself (Agent Swarms)&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=ZvepfBm8lQo" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; A live test of "Agent Swarms"—orchestrating multiple Claude instances to build a complex project simultaneously. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Claude Code + GitHub Actions: Full Workflow Breakdown&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=x0IQKEEOawQ" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Engineering guide on "Auto-Healing" PRs. Learn how to let Claude monitor CI/CD logs and push its own fixes before the first human review.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Agentic Workflows Just Changed AI Automation Forever!&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=AO5aW01DKHo" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Nate Herk explores high-level automation where Claude makes architectural decisions without manual intervention.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Claude Code Workflows That Will 10x Your Productivity&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=yZvDo_n12ns" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Focuses on the "non-coding" parts of engineering—automating status updates, PR descriptions, and handoff documentation using CLI triggers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Stop Using Claude Code Like This (Use Sub-Agents Instead)&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=P60LqQg1RH8" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Leon van Zyl explains the "delegation meta"—why issuing broad goals to sub-agents is 3x more token-efficient than micromanaging the main thread.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Claude Code's MCP Problem Just Got Fixed&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=l7qVtHpctic" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Kenny Liao discusses the February patch that optimized parallel MCP server connections, reducing latency by 40%.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Only AI Coding Tools Worth Learning in 2026&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=-VTiqivKOB8" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Tech With Tim benchmarks the "Logic-First" tools, ranking Claude Code at the top for multi-file systems work.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Cursor vs Claude Code: The Lex Fridman Debate&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=KW35wHNU7pY" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Industry leaders discuss the shift from IDE-based assistants to terminal-based "virtual coworkers."&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How I Built an AI Council with Claude Code Subagents&lt;/strong&gt; | &lt;a href="http://www.youtube.com/watch?v=LpM1dlB12-A" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Mark Kashef demonstrates "Adversarial Debugging"—spawning two agents to argue against each other's logic to reach the optimal fix.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  📰 Technical Briefing: CI/CD &amp;amp; Model Updates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[Model Launch] Claude Opus 4.6:&lt;/strong&gt; Released Feb 5, 2026. This model introduces &lt;strong&gt;Adaptive Thinking&lt;/strong&gt;, allowing it to dynamically adjust its "thinking budget" based on task complexity. It has achieved a massive &lt;strong&gt;72.5% on SWE-Bench&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[The Swarm Architecture] Headless Engineering:&lt;/strong&gt; Anthropic's latest report highlights developers moving toward "Headless Engineering"—a practice where the terminal agent manages the entire stack, and the human acts strictly as a &lt;strong&gt;Validator&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[Context Compaction] Infinite Conversations:&lt;/strong&gt; The new session loading logic in v2.1.30 has reduced RAM usage by ~68%. You can now resume massive sessions without the "startup lag" that plagued earlier v2 builds.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Thoughts from the Terminal: The "Parallelism" Shift
&lt;/h2&gt;

&lt;p&gt;True productivity in 2026 isn't about &lt;em&gt;writing&lt;/em&gt; code; it's about &lt;strong&gt;Orchestration&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;High-performing engineers are moving to &lt;strong&gt;Parallel Workflows&lt;/strong&gt;. They launch a "Lead Agent," which delegates unit testing to Instance A and feature refactoring to Instance B. While the sub-agents grind, the engineer focuses on &lt;strong&gt;Strategic Problem Decomposition&lt;/strong&gt;. If you're working sequentially, you're running "Single Core" logic in a "Multi-Core" world.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Skill of the Week: &lt;code&gt;pr-agent&lt;/code&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Concept:&lt;/strong&gt; A custom skill to automate the entire Pull Request lifecycle from the CLI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Implementation:
&lt;/h3&gt;

&lt;p&gt;Create a skill that pulls the current branch diff, generates a markdown description, and pings your CI/CD manager server. &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
/skill create name="pr-agent" 
description="Generates PR descriptions and monitors GitHub Actions for failures."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>claudecode</category>
      <category>vibecoding</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Claude Code Club | Issue #1</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Tue, 03 Feb 2026 23:44:31 +0000</pubDate>
      <link>https://dev.to/paweljanda/claude-code-club-issue-1-106f</link>
      <guid>https://dev.to/paweljanda/claude-code-club-issue-1-106f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Engineering Log: February 4, 2026&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Focus: System Architecture, Context Engineering, and the Context Ceiling.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Welcome to the debut of &lt;strong&gt;Claude Code Club&lt;/strong&gt;. This isn't a hype-sheet. We are here to document the transition from "vibe coding" to &lt;strong&gt;Agentic Software Engineering&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In early 2026, the ecosystem has moved decisively toward the "Terminal-as-the-OS" model. Here is the high-signal data you need for your workflow this week.&lt;/p&gt;




&lt;h2&gt;
  
  
  📺 Top 10 Engineering-Focused Deep Dives
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;How I use Claude Code for real engineering&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=kZ-zzHVUrO4" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Matt Pocock (TS expert) walks through tackling large-scale projects using &lt;strong&gt;Plan Mode&lt;/strong&gt;. Essential for understanding how to maintain system integrity during mass refactors.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;800+ hours of Learning Claude Code in 8 minutes&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=Ffh9OeJ7yxw" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Edmund Yong provides a high-density dump of "unknown tricks" and keyboard shortcuts to minimize the friction between thought and terminal execution.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Claude Code Tutorial for Beginners (2026)&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=eMZmDH3T2bY" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Kevin Stratvert covers the essential "first 30 minutes" setup, including permission management and repository indexing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Turn Claude Code into Your Full Engineering Team with Subagents&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=-GyX21BL1Nw" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Cole Medin explores the "agent harness" meta—demonstrating how to delegate tasks to background sub-agents while you stay in your flow.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;How to Set Up Claude Code in 2026&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=kddjxKEeCuM" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Leon van Zyl deep-dives into the latest installation hooks and configuration flags for professional environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Ship working code while you sleep with the Ralph Wiggum technique&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=_IK18goX4X8" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Matt Pocock again, but this time on &lt;strong&gt;asynchronous delegation&lt;/strong&gt;. How to leave an agent running to solve complex bugs overnight.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Claude Code Skills: Automate Everything You Do&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=vOW1xAVbuNI" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; Brandon explores the &lt;code&gt;/skill&lt;/code&gt; command to create custom, reusable agent behaviors tailored to your specific tech stack.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Introducing Claude Code (Official Overview)&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=AJpK3YTTKZ4" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; The original Anthropic reveal. Still the best source for understanding the core architecture of the CLI.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Mastering Claude Code in 30 minutes&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=6eBSHbLKuN0" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; An official deep-dive into advanced features like &lt;strong&gt;MCP servers&lt;/strong&gt; and complex tool-use loops.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code Tutorial: Build an App with AI&lt;/strong&gt; | &lt;a href="https://www.youtube.com/watch?v=6q8joS_592k" rel="noopener noreferrer"&gt;Watch Video&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Signal:&lt;/strong&gt; A practical, start-to-finish build showing Claude Code managing a full-stack project structure from the terminal.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  📰 Technical Briefing: Articles &amp;amp; Updates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[Anthropic Engineering] The "Self-Correction" Meta:&lt;/strong&gt; Documentation was recently updated to highlight Claude’s improved ability to read linting errors and auto-fix them without human prompts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[Community Standard] The &lt;code&gt;CLAUDE.md&lt;/code&gt; Manifest:&lt;/strong&gt; Developers are coalescing around a standard &lt;code&gt;CLAUDE.md&lt;/code&gt; file in repo roots. This file acts as "System Memory," defining tech stacks and linting rules so you never have to repeat your project constraints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[Security Patch] OAuth-Based Scoping:&lt;/strong&gt; Documentation updated for 2026 releases. You can now scope Claude’s access to specific directories or APIs, a critical step for corporate compliance.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Thoughts from the Terminal: The "Context Ceiling"
&lt;/h2&gt;

&lt;p&gt;The most significant trend this week is the realization that &lt;strong&gt;Claude Code is not a Chatbot.&lt;/strong&gt; If you are "chatting" with your CLI, you are losing. High-performance engineers are treating the CLI as a &lt;strong&gt;Compiler of Intent.&lt;/strong&gt; &lt;strong&gt;Key takeaway for your workflow:&lt;/strong&gt; Stop asking Claude to "fix this bug." Start using &lt;code&gt;/plan&lt;/code&gt; to generate a markdown spec of the fix, review the spec as a Senior Architect, and only then allow the agent to execute. This "Review-First" pattern is what separates the noise from the signal in 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Performance Tip of the Week
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Run with &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;?&lt;/strong&gt; If you’re working in a version-controlled environment with a clean Git history, the constant permission popups are a productivity killer. Most power users are now running in "Dangerous Mode" and relying on &lt;code&gt;git checkout .&lt;/code&gt; as their safety net. Use with caution, but use it for speed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published by the Claude Code Club. See you on the next commit.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>llm</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Building for Europe? Meet Bielik (Open-Source LLM Made in Poland).</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Tue, 06 Jan 2026 13:23:45 +0000</pubDate>
      <link>https://dev.to/paweljanda/building-for-europe-meet-bielik-open-source-llm-made-in-poland-2ap0</link>
      <guid>https://dev.to/paweljanda/building-for-europe-meet-bielik-open-source-llm-made-in-poland-2ap0</guid>
      <description>&lt;p&gt;When you build for the European market—especially if you have Polish users—you need a model that handles language nuance, not just English-first patterns. Many teams default to the biggest global LLMs, but there’s a strong open-source alternative worth knowing: &lt;strong&gt;Bielik&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Bielik is a Polish-developed open-source LLM that performs especially well in Polish and holds its own across European languages in public evaluations. That makes it a great fit for apps that serve Polish-speaking markets—or multilingual products across Europe.&lt;/p&gt;

&lt;p&gt;If you’re building chatbots, translation, content workflows, or any AI feature where language quality matters, Bielik is a compelling model to evaluate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with Bielik in .NET
&lt;/h2&gt;

&lt;p&gt;The good news is that integrating Bielik into your .NET applications is straightforward, thanks to the MaIN.NET library. Let's walk through setting up a simple console application step by step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a New Console Application
&lt;/h3&gt;

&lt;p&gt;First, let's create a new .NET console project. Open your terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new console &lt;span class="nt"&gt;-n&lt;/span&gt; BielikExample
&lt;span class="nb"&gt;cd &lt;/span&gt;BielikExample
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a new console application project named "BielikExample" and navigates into the project directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Add the MaIN.NET Package
&lt;/h3&gt;

&lt;p&gt;Next, we need to add the MaIN.NET package, which provides a convenient .NET interface for working with Bielik and other compatible models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package main.net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will add the MaIN.NET package to your project, making all the necessary APIs available for interacting with Bielik models.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Create Your Application Code
&lt;/h3&gt;

&lt;p&gt;Now let's create a simple chat application that uses Bielik. We'll break this down into several substeps to understand each part of the code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3.1: Add Necessary Using Statements
&lt;/h4&gt;

&lt;p&gt;First, we need to import the required namespaces for MaIN.NET:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core.Hub&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Domain.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These imports give us access to the MaIN.NET framework classes and methods we'll need for model initialization and chat functionality. Note that with modern C# and implicit usings enabled, we don't need to explicitly import &lt;code&gt;System&lt;/code&gt; and &lt;code&gt;System.IO&lt;/code&gt; for basic operations.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3.2: Set Up the Model Path
&lt;/h4&gt;

&lt;p&gt;Next, we configure where the model files are stored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;projectRoot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFullPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;modelsFolder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectRoot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Models"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code determines the project root directory and creates a path to the &lt;code&gt;Models&lt;/code&gt; folder where your Bielik model files should be placed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3.3: Initialize the MaIN Bootstrapper
&lt;/h4&gt;

&lt;p&gt;The bootstrapper initializes the MaIN framework with your configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;MaINBootstrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configureSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelsPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modelsFolder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DisableLLamaLogs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we're telling MaIN to use a self-hosted backend (running the model locally) and specifying the path to our models folder. The &lt;code&gt;DisableLLamaLogs()&lt;/code&gt; call suppresses verbose logging from the underlying LLama library, keeping your console output clean and focused on your application's messages.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3.4: Configure the Model Name and Path
&lt;/h4&gt;

&lt;p&gt;Specify which Bielik model to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bielik-11B-v3.0-Instruct.Q8_0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fullPath&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;modelsFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.gguf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up the model name and constructs the full path to the model file, which will be used when creating the chat instance.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3.5: Create the Chat Loop
&lt;/h4&gt;

&lt;p&gt;Finally, implement the interactive chat interface. We'll create the chat instance once and reuse it in the loop for better performance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🗨️  Chat with Bielik (type 'exit' to quit)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithCustomModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nYou: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"exit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a chat instance configured with your Bielik model before entering the loop. The loop reads user input and sends it to Bielik, reusing the same chat context. The &lt;code&gt;interactive: true&lt;/code&gt; parameter enables real-time streaming of the model's response. By creating the chat instance outside the loop, we ensure better performance and maintain conversation context across multiple exchanges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Download the Bielik Model
&lt;/h3&gt;

&lt;p&gt;Before running your application, you'll need to download the Bielik model file. The model files (in GGUF format) need to be placed in a &lt;code&gt;Models&lt;/code&gt; folder within your project directory.&lt;/p&gt;

&lt;p&gt;The example above uses the &lt;code&gt;Bielik-11B-v3.0-Instruct.Q8_0&lt;/code&gt; model, which is an 11 billion parameter instruct-tuned model with 8-bit quantization. This provides an excellent balance between performance and resource requirements.&lt;/p&gt;

&lt;p&gt;You can download it from &lt;a href="https://huggingface.co/speakleash/Bielik-11B-v3.0-Instruct-GGUF" rel="noopener noreferrer"&gt;HuggingFace&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Run Your Application
&lt;/h3&gt;

&lt;p&gt;Once you have the model file in place, you can run your application:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll now have a working chat interface that communicates with Bielik!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Choose Bielik?
&lt;/h2&gt;

&lt;p&gt;Bielik represents a significant achievement in European language processing. The model has been specifically trained and optimized for European languages, with particular strength in Polish. This makes it invaluable for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multilingual European Applications&lt;/strong&gt;: Build applications that serve customers across the EU with native-level language understanding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polish Market Solutions&lt;/strong&gt;: Create applications specifically tailored for the Polish-speaking market with confidence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Localization Services&lt;/strong&gt;: Power translation and localization tools with models that understand cultural and linguistic nuances&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content Generation&lt;/strong&gt;: Generate natural, contextually appropriate content in European languages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The open-source nature of Bielik means you have full control over your implementation, and you're not locked into proprietary APIs or usage restrictions. Combined with the ease of use provided by MaIN.NET, integrating Bielik into your .NET applications is both powerful and straightforward.&lt;/p&gt;

&lt;p&gt;MaIN.NET is being built with the .NET community. If you’re experimenting with Bielik (or any local/open model), join in: star the project, drop feedback in issues, or share your use case so others can learn from it. Check out the repo on &lt;a href="https://github.com/wisedev-code/MaIN.NET" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;If you're building for the European market, especially for Polish-speaking audiences, Bielik offers a compelling, high-performing alternative to other language models. With MaIN.NET, getting started is just two commands away. Give it a try and experience the difference that a model optimized for European languages can make in your applications.&lt;/p&gt;

&lt;p&gt;The best part? This is pure .NET. Once you have Bielik integrated into your project, you can leverage the full power of the .NET ecosystem to build any type of solution—whether it's a web API with ASP.NET Core, a desktop application with WPF or MAUI, a microservice architecture, or even cloud-native applications on Azure. You have complete flexibility to extend this foundation and build sophisticated, production-ready applications tailored to your European market needs.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>llm</category>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>“I’ve never felt this much behind as a programmer” — why Andrej Karpathy’s post hits so close to home.</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Thu, 01 Jan 2026 17:06:17 +0000</pubDate>
      <link>https://dev.to/paweljanda/ive-never-felt-this-much-behind-as-a-programmer-why-andrej-karpathys-post-hits-so-close-to-45hk</link>
      <guid>https://dev.to/paweljanda/ive-never-felt-this-much-behind-as-a-programmer-why-andrej-karpathys-post-hits-so-close-to-45hk</guid>
      <description>&lt;p&gt;Recently, Andrej Karpathy posted a short reflection that immediately resonated with a lot of developers — especially those who’ve been in the industry for years.&lt;br&gt;&lt;br&gt;
(I’ll add a screenshot of the post here, because it’s worth reading in full.) &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%2Fxnm9tz7db6z2xiga11ac.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%2Fxnm9tz7db6z2xiga11ac.png" alt=" " width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s honest and direct, and that’s exactly why it feels a bit uncomfortable.&lt;/p&gt;

&lt;p&gt;I want to unpack what he’s pointing at — and why so many experienced engineers recognize themselves in it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Programming is quietly changing its center of gravity
&lt;/h2&gt;

&lt;p&gt;One line from the post stands out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The bits contributed by the programmer are increasingly sparse.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This matches what many of us see in daily work.&lt;/p&gt;

&lt;p&gt;Less time goes into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;writing everything from scratch
&lt;/li&gt;
&lt;li&gt;implementing standard patterns over and over
&lt;/li&gt;
&lt;li&gt;manually translating ideas into verbose code
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More time goes into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;connecting existing components
&lt;/li&gt;
&lt;li&gt;orchestrating systems
&lt;/li&gt;
&lt;li&gt;shaping workflows and behaviors
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Code still matters, but it’s no longer the only — or even the primary — unit of value. Increasingly, the leverage comes from &lt;em&gt;how&lt;/em&gt; things are composed rather than &lt;em&gt;how much&lt;/em&gt; code is written.&lt;/p&gt;




&lt;h2&gt;
  
  
  A new abstraction layer appeared — and it’s unavoidable
&lt;/h2&gt;

&lt;p&gt;Karpathy lists things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;agents and sub-agents
&lt;/li&gt;
&lt;li&gt;prompts and context
&lt;/li&gt;
&lt;li&gt;memory, tools, permissions
&lt;/li&gt;
&lt;li&gt;workflows, hooks, IDE integrations
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn’t a random collection of buzzwords. It describes a new, programmable layer that now sits on top of the traditional stack.&lt;/p&gt;

&lt;p&gt;In the past, developers had to internalize operating systems, networking, frameworks, and cloud infrastructure. Today, another layer is added:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;designing systems that collaborate with probabilistic models&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That requires a different kind of thinking. Not just “how does this function behave,” but “how does this system behave when part of it reasons, guesses, and adapts.”&lt;/p&gt;




&lt;h2&gt;
  
  
  Engineering meets stochastic systems
&lt;/h2&gt;

&lt;p&gt;Karpathy describes modern AI systems as stochastic, fallible, opaque, and constantly changing. Anyone who has worked seriously with LLMs recognizes this immediately.&lt;/p&gt;

&lt;p&gt;The mental model shifts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prompts start to feel like code, but without a compiler
&lt;/li&gt;
&lt;li&gt;failures are sometimes statistical, not logical
&lt;/li&gt;
&lt;li&gt;behavior can change even when your own code does not
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Developers now have to reason in probabilities, guardrails, feedback loops, and constraints. The skill isn’t just calling an API — it’s understanding where the model is strong, where it breaks, and how to design around that.&lt;/p&gt;




&lt;h2&gt;
  
  
  The “10× boost” is already available — if you can assemble it
&lt;/h2&gt;

&lt;p&gt;Another striking observation from the post is the sense that the productivity boost already exists, yet many people struggle to fully claim it.&lt;/p&gt;

&lt;p&gt;The tools are here.&lt;br&gt;&lt;br&gt;
The capabilities are real.&lt;br&gt;&lt;br&gt;
The acceleration is noticeable.&lt;/p&gt;

&lt;p&gt;What’s missing is a manual.&lt;/p&gt;

&lt;p&gt;Everyone is experimenting in real time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;building personal workflows
&lt;/li&gt;
&lt;li&gt;discovering failure modes
&lt;/li&gt;
&lt;li&gt;figuring out what scales and what doesn’t
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some developers are quietly becoming dramatically more effective — not because they type faster, but because they’ve learned how to combine these tools into a coherent system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why even senior developers feel behind
&lt;/h2&gt;

&lt;p&gt;One thing this post explains very well is a feeling many experienced engineers have lately: a strange mix of competence and disorientation.&lt;/p&gt;

&lt;p&gt;It’s not about losing fundamentals.&lt;br&gt;&lt;br&gt;
It’s about fundamentals no longer being sufficient on their own.&lt;/p&gt;

&lt;p&gt;Years of experience still matter — often more than ever — but they need to be extended with new mental models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how to reason about AI-driven components
&lt;/li&gt;
&lt;li&gt;how to design guardrails instead of strict rules
&lt;/li&gt;
&lt;li&gt;how to treat uncertainty as a first-class concern
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That transition can feel unsettling, even for people who have successfully navigated multiple tech shifts before.&lt;/p&gt;




&lt;h2&gt;
  
  
  A quiet refactor of the profession
&lt;/h2&gt;

&lt;p&gt;What Karpathy describes feels less like a sudden disruption and more like a deep refactor happening in slow motion.&lt;/p&gt;

&lt;p&gt;The core skills of engineering remain valuable.&lt;br&gt;&lt;br&gt;
The environment around them is changing rapidly.&lt;/p&gt;

&lt;p&gt;Developers who invest time in understanding this new abstraction layer — agents, workflows, AI-assisted reasoning — are likely to gain disproportionate leverage. Others may find themselves doing solid work, but at a very different pace.&lt;/p&gt;

&lt;p&gt;As Karpathy puts it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Roll up your sleeves to not fall behind.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That line doesn’t sound like panic.&lt;br&gt;&lt;br&gt;
It sounds like practical advice.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Master MCP integration: Building AI database tools with .NET</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Wed, 13 Aug 2025 09:32:44 +0000</pubDate>
      <link>https://dev.to/paweljanda/master-mcp-integration-building-ai-database-tools-with-net-50jd</link>
      <guid>https://dev.to/paweljanda/master-mcp-integration-building-ai-database-tools-with-net-50jd</guid>
      <description>&lt;p&gt;Learn how to create powerful AI database tools using Model Context Protocol (MCP) and MaIN.NET framework. This tutorial will teach you to build applications that can interact with any database using natural language queries.&lt;/p&gt;

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

&lt;p&gt;A console application that demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Natural language database queries&lt;/strong&gt; - Ask questions in plain English&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP server integration&lt;/strong&gt; - Connect to any database through standardized protocol&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-powered data operations&lt;/strong&gt; - Create, read, update, and delete records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive database assistant&lt;/strong&gt; - Chat with your data like never before&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Airtable?
&lt;/h2&gt;

&lt;p&gt;While this tutorial uses &lt;strong&gt;Airtable&lt;/strong&gt; for simplicity and ease of setup, the techniques you'll learn apply to &lt;strong&gt;any database engine&lt;/strong&gt; supported by MCP servers. You can easily adapt this code to work with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQL Databases&lt;/strong&gt;: MySQL, PostgreSQL, SQLite, SQL Server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NoSQL Databases&lt;/strong&gt;: MongoDB, Cassandra, Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Databases&lt;/strong&gt;: BigQuery, Snowflake, DynamoDB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File Systems&lt;/strong&gt;: Local files, CSV, JSON data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APIs&lt;/strong&gt;: REST APIs, GraphQL endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The beauty of MCP is that once you understand the pattern, you can swap out Airtable for any other database by simply changing the MCP server configuration!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;.NET SDK&lt;/li&gt;
&lt;li&gt;Node.js (for running MCP servers)&lt;/li&gt;
&lt;li&gt;Airtable account&lt;/li&gt;
&lt;li&gt;OpenAI API key&lt;/li&gt;
&lt;li&gt;basic C# knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Set up Airtable database
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Create Airtable base
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://airtable.com" rel="noopener noreferrer"&gt;Airtable&lt;/a&gt; and create a new base&lt;/li&gt;
&lt;li&gt;Name it "Employees"&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1.2 Import sample data
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a sample CSV file with the following content:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name,Email,Department,Position,Hire Date,Salary,Is Active
John Smith,john.smith@company.com,Engineering,Senior Developer,2024-01-15,85000,TRUE
Sarah Johnson,sarah.j@company.com,Marketing,Marketing Manager,2024-03-20,75000,TRUE
Mike Davis,mike.davis@company.com,Sales,Sales Representative,2024-06-10,65000,TRUE
Lisa Chen,lisa.chen@company.com,Engineering,Junior Developer,2024-08-05,70000,TRUE
Tom Wilson,tom.wilson@company.com,HR,HR Specialist,2024-02-28,60000,TRUE
Emma Rodriguez,emma.rodriguez@company.com,Finance,Financial Analyst,2023-04-12,72000,TRUE
David Kim,david.kim@company.com,Engineering,DevOps Engineer,2023-07-18,90000,TRUE
Maria Garcia,maria.garcia@company.com,Marketing,Content Specialist,2023-05-25,68000,TRUE
James Brown,james.brown@company.com,Sales,Sales Manager,2023-09-30,82000,TRUE
Anna Lee,anna.lee@company.com,Engineering,Frontend Developer,2023-11-08,78000,TRUE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;In Airtable, click &lt;strong&gt;"Add or import"&lt;/strong&gt; → &lt;strong&gt;"CSV file"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Upload the CSV file&lt;/li&gt;
&lt;li&gt;Airtable will auto-detect columns - verify the field types:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: Single line text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email&lt;/strong&gt;: Single line text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Department&lt;/strong&gt;: Single select (Engineering, Marketing, Sales, HR, Finance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Position&lt;/strong&gt;: Single line text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hire Date&lt;/strong&gt;: Date&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary&lt;/strong&gt;: Number&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is Active&lt;/strong&gt;: Checkbox&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1.3 Get Airtable API key
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://airtable.com/create/tokens" rel="noopener noreferrer"&gt;Airtable Personal Access Tokens&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a new token with scopes:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;schema.bases:read&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data.records:read&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data.records:write&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Important&lt;/strong&gt;: Make sure to grant access to your specific base&lt;/li&gt;
&lt;li&gt;Copy the token&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Create the console application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Create new project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create new console application&lt;/span&gt;
dotnet new console &lt;span class="nt"&gt;-n&lt;/span&gt; EmployeeMCP

&lt;span class="c"&gt;# Navigate to project&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;EmployeeMCP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.2 Add MaIN.NET package
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add MaIN.NET package&lt;/span&gt;
dotnet add package MaIN.NET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Build the application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Create Program.cs
&lt;/h3&gt;

&lt;p&gt;Replace the default &lt;code&gt;Program.cs&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core.Hub&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Domain.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Domain.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.DependencyInjection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Check environment variables&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;airtableApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AIRTABLE_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;openAiApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OPENAI_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;airtableApiKey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"❌ AIRTABLE_API_KEY not set!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please set: export AIRTABLE_API_KEY='your-airtable-token'"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;openAiApiKey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"❌ OPENAI_API_KEY not set!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please set: export OPENAI_API_KEY='your-openai-key'"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize MaIN.NET&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetBasePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetCurrentDirectory&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEnvironmentVariables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ServiceCollection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMaIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenAi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenAiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openAiApiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildServiceProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseMaIN&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🚀 Employee MCP Console Application"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"==================================="&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"✅ AIHub initialized successfully!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Configure Airtable MCP&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;airtableMcp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mcp&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Airtable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"airtable-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gpt-4o-mini"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenAi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;EnvironmentVariables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AIRTABLE_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;airtableApiKey&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🔧 Airtable MCP Configuration:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"   Model: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;airtableMcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"   Backend: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;airtableMcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Interactive mode&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🎯 Interactive Employee Query Mode"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"=================================="&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ask questions about your employee data!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Examples:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"- 'Show me all employees'"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"- 'List employees in Engineering department'"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"- 'Who was hired in 2023?'"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"- 'Add a new employee: John Doe, john@company.com, Engineering, Developer'"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type 'exit' to quit."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🤖 Query: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"exit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🔄 Processing..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Mcp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;airtableMcp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PromptAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"✅ Response:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"❌ Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"👋 Goodbye!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Understanding the code
&lt;/h2&gt;

&lt;p&gt;Let's break down the most important parts of our application:&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Environment variables &amp;amp; API keys
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;airtableApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AIRTABLE_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;openAiApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OPENAI_API_KEY"&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;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reads API keys&lt;/strong&gt; from your system environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Makes them available&lt;/strong&gt; to the application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeps secrets secure&lt;/strong&gt; (not hardcoded in source code)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.2 MaIN.NET configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetBasePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetCurrentDirectory&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEnvironmentVariables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&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;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creates configuration object&lt;/strong&gt; for MaIN.NET&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enables reading&lt;/strong&gt; environment variables and config files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sets up the foundation&lt;/strong&gt; for dependency injection&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.3 Service registration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ServiceCollection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMaIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenAi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenAiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openAiApiKey&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;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registers MaIN.NET services&lt;/strong&gt; in dependency injection container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configures AI backend&lt;/strong&gt; (OpenAI in this case)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sets up all required services&lt;/strong&gt; (MCP, Chat, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.4 Service provider initialization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildServiceProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseMaIN&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;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Builds the service container&lt;/strong&gt; with all registered services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initializes MaIN.NET framework&lt;/strong&gt; and makes it ready to use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creates the AIHub&lt;/strong&gt; instance for MCP operations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.5 MCP configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;airtableMcp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mcp&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Airtable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"airtable-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gpt-4o-mini"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenAi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;EnvironmentVariables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"AIRTABLE_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;airtableApiKey&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;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Defines MCP server&lt;/strong&gt; configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specifies how to run&lt;/strong&gt; the Airtable MCP server (&lt;code&gt;npx airtable-mcp-server&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sets AI model&lt;/strong&gt; and backend for processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passes API key&lt;/strong&gt; to the MCP server&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.6 How MCP servers work
&lt;/h3&gt;

&lt;p&gt;When you call &lt;code&gt;AIHub.Mcp()&lt;/code&gt;, MaIN.NET automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Starts the MCP server process&lt;/strong&gt; using the specified command and arguments:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   npx &lt;span class="nt"&gt;-y&lt;/span&gt; airtable-mcp-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Establishes communication&lt;/strong&gt; between your .NET app and the MCP server via stdio (standard input/output)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handles the protocol&lt;/strong&gt; - Your app sends requests, the MCP server processes them and returns responses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manages the lifecycle&lt;/strong&gt; - Starts the server when needed, stops it when the application closes&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The MCP server is a separate Node.js process&lt;/strong&gt; that runs alongside your .NET application. It acts as a bridge between your AI queries and the Airtable API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You need Node.js installed on your system for MCP servers to work, as they are typically Node.js applications. The &lt;code&gt;setup.sh&lt;/code&gt; script checks for Node.js installation and helps configure your environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.7 Interactive query loop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🤖 Query: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"exit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Mcp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;airtableMcp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PromptAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"❌ Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creates interactive loop&lt;/strong&gt; for user queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uses AIHub.Mcp()&lt;/strong&gt; to send queries to MCP server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processes natural language&lt;/strong&gt; through AI backend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handles errors gracefully&lt;/strong&gt; with try-catch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Displays results&lt;/strong&gt; to user&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.8 The magic: AIHub.Mcp()
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Mcp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;airtableMcp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PromptAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&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;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creates MCP client&lt;/strong&gt; connection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sends your query&lt;/strong&gt; to the Airtable MCP server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processes through AI&lt;/strong&gt; (OpenAI) to understand intent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Executes appropriate&lt;/strong&gt; Airtable API calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Returns natural language&lt;/strong&gt; response&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standardized way&lt;/strong&gt; for AI to interact with external tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your query&lt;/strong&gt; → &lt;strong&gt;AI understands&lt;/strong&gt; → &lt;strong&gt;MCP translates&lt;/strong&gt; → &lt;strong&gt;Airtable API&lt;/strong&gt; → &lt;strong&gt;Response&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;AIHub&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Central interface&lt;/strong&gt; for all MaIN.NET operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manages connections&lt;/strong&gt; to AI backends and MCP servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handles the complexity&lt;/strong&gt; of AI + MCP integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Dependency Injection&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modern .NET pattern&lt;/strong&gt; for managing services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Makes testing easier&lt;/strong&gt; and code more maintainable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MaIN.NET uses it&lt;/strong&gt; to organize all its components&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2 Set environment variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set your API keys&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AIRTABLE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"pat_your_airtable_token_here"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk_your_openai_key_here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 Build and run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build the application&lt;/span&gt;
dotnet build

&lt;span class="c"&gt;# Run the application&lt;/span&gt;
dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Test your application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Basic queries
&lt;/h3&gt;

&lt;p&gt;Try these example queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query: Show me all employees
Query: List employees in Engineering department
Query: Who was hired in 2023?
Query: Show employees with salary above 80000
Query: How many active employees do we have?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.2 Data operations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query: Add a new employee: Alex Brown, alex@company.com, Engineering, Junior Developer
Query: Update Sarah Johnson's salary to 80000
Query: Mark Tom Wilson as inactive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.3 Handling single select fields
&lt;/h3&gt;

&lt;p&gt;When working with Airtable Single Select fields (like Department), make sure to use exact values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query: What departments are available?
Query: Show me all employees and their departments
Query: Add a new employee: Alex Brown, alex@company.com, Engineering, Junior Developer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Single Select fields only accept predefined values. Common department values might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Engineering&lt;/li&gt;
&lt;li&gt;Marketing
&lt;/li&gt;
&lt;li&gt;Sales&lt;/li&gt;
&lt;li&gt;HR&lt;/li&gt;
&lt;li&gt;Finance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure to use the exact spelling and case as defined in your Airtable base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Using other backends
&lt;/h2&gt;

&lt;p&gt;MaIN.NET supports multiple AI backends. While this tutorial uses OpenAI, you can easily switch to other providers:&lt;/p&gt;

&lt;h3&gt;
  
  
  Groq
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GroqCloud&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GroqCloudKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GROQ_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gemini&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GeminiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GEMINI_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DeepSeek
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeepSeek&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeepSeekKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DEEPSEEK_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simply change the backend configuration and update the model name accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you've built
&lt;/h2&gt;

&lt;p&gt;You've created a powerful console application that:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integrates MCP servers&lt;/strong&gt; with MaIN.NET framework&lt;br&gt;
&lt;strong&gt;Provides natural language interface&lt;/strong&gt; to Airtable data&lt;br&gt;
&lt;strong&gt;Supports multiple AI backends&lt;/strong&gt; (OpenAI, Groq, Gemini, etc.)&lt;br&gt;
&lt;strong&gt;Handles real-time data operations&lt;/strong&gt; (CRUD)&lt;br&gt;
&lt;strong&gt;Offers interactive query mode&lt;/strong&gt; for easy data exploration&lt;br&gt;
&lt;strong&gt;Manages Single Select fields&lt;/strong&gt; properly in Airtable  &lt;/p&gt;
&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add more MCP servers&lt;/strong&gt;: GitHub, File System, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create web interface&lt;/strong&gt;: Build a Blazor web app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement caching&lt;/strong&gt;: Improve performance for frequent queries&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Explore more MCP servers
&lt;/h2&gt;

&lt;p&gt;Now that you've mastered Airtable integration, you can expand your application with other MCP servers! Here are some popular database and data-related servers you can try:&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Database servers&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BigQuery&lt;/strong&gt; - Google's data warehouse integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClickHouse&lt;/strong&gt; - High-performance analytical database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL&lt;/strong&gt; - Popular relational database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt; - Advanced open-source database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt; - NoSQL document database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite&lt;/strong&gt; - Lightweight file-based database&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Cloud &amp;amp; API servers&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; - Repository and issue management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File System&lt;/strong&gt; - Local file operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Scraping&lt;/strong&gt; - Extract data from websites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email&lt;/strong&gt; - Send and manage emails&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack&lt;/strong&gt; - Team communication integration&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;How to add new MCP servers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Simply update your MCP configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example: Adding GitHub MCP&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;githubMcp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mcp&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GitHub"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"@modelcontextprotocol/server-github"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gpt-4o-mini"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenAi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;EnvironmentVariables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GITHUB_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GITHUB_TOKEN"&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;// Use it in your queries&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Mcp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;githubMcp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PromptAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Show me recent commits in my repository"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Browse all available servers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Check out the complete collection of MCP servers at &lt;a href="https://mcpservers.org" rel="noopener noreferrer"&gt;mcpservers.org&lt;/a&gt;, especially the &lt;a href="https://mcpservers.org/category/database" rel="noopener noreferrer"&gt;Database category&lt;/a&gt; for more data integration options!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Popular categories:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://mcpservers.org/category/database" rel="noopener noreferrer"&gt;Database Servers&lt;/a&gt; - SQL, NoSQL, and data warehouses&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mcpservers.org/category/file-system" rel="noopener noreferrer"&gt;File System&lt;/a&gt; - Local file operations&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mcpservers.org/category/cloud-service" rel="noopener noreferrer"&gt;Cloud Services&lt;/a&gt; - AWS, Azure, Google Cloud&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mcpservers.org/category/communication" rel="noopener noreferrer"&gt;Communication&lt;/a&gt; - Email, Slack, Discord&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/wisedev-code/MaIN.NET" rel="noopener noreferrer"&gt;MaIN.NET Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mcpservers.org/servers/domdomegg/airtable-mcp-server" rel="noopener noreferrer"&gt;Airtable MCP Server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




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

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>mcp</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building AI-Powered .NET applications with Groq Cloud and MaIN.NET</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Mon, 11 Aug 2025 20:24:23 +0000</pubDate>
      <link>https://dev.to/paweljanda/building-ai-powered-net-applications-with-groq-cloud-and-mainnet-2o6d</link>
      <guid>https://dev.to/paweljanda/building-ai-powered-net-applications-with-groq-cloud-and-mainnet-2o6d</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we'll explore how to integrate Groq Cloud with the MaIN.NET framework to build powerful AI applications in .NET. We'll create a console application that demonstrates basic chat functionality and interactive conversations with proper conversation context management.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Groq Cloud?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://groq.com/" rel="noopener noreferrer"&gt;Groq Cloud&lt;/a&gt; is a cutting-edge AI platform that specializes in ultra-fast inference for large language models. Unlike traditional cloud AI services that can take several seconds to respond, Groq Cloud delivers responses in milliseconds, making it ideal for real-time applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key features of Groq Cloud:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lightning-fast inference&lt;/strong&gt;: Responses in milliseconds instead of seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple model support&lt;/strong&gt;: Access to models like Llama 3, Qwen or brand new GPT-OSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-effective&lt;/strong&gt;: Competitive pricing for high-speed inference and powerful free plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer-friendly&lt;/strong&gt;: OpenAI API and MaIN.NET compatibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Groq Cloud?
&lt;/h3&gt;

&lt;p&gt;Traditional AI services often have some latency, which can break the user experience in real-time applications. Groq Cloud solves this by using specialized hardware and optimized inference engines, making it perfect for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chatbots and conversational AI&lt;/li&gt;
&lt;li&gt;Real-time content generation&lt;/li&gt;
&lt;li&gt;Interactive applications&lt;/li&gt;
&lt;li&gt;Low-latency AI services&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why MaIN.NET?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/wisedev-code/MaIN.NET" rel="noopener noreferrer"&gt;Link to the repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the most impressive aspects of this integration is how &lt;strong&gt;clean and straightforward&lt;/strong&gt; the code becomes thanks to MaIN.NET's well-designed architecture. Unlike traditional AI integrations that often require complex HTTP client setup, authentication handling, and response parsing, MaIN.NET abstracts all of this complexity away.&lt;/p&gt;

&lt;p&gt;With just a few lines of code, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic conversation context management&lt;/strong&gt; - No need to manually track message history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in error handling&lt;/strong&gt; - Robust error management out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fluent API design&lt;/strong&gt; - Intuitive method chaining for configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple backend support&lt;/strong&gt; - Easy switching between different AI providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result? Clean, maintainable code that focuses on your business logic rather than infrastructure concerns. Let's see how this translates into a real application!&lt;/p&gt;

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

&lt;p&gt;Before we begin, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.NET SDK installed&lt;/li&gt;
&lt;li&gt;A Groq Cloud API key (get one at &lt;a href="https://console.groq.com/" rel="noopener noreferrer"&gt;console.groq.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Basic knowledge of C# and .NET&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Setting up the project
&lt;/h2&gt;

&lt;p&gt;First, let's create a new console application and set up the MaIN.NET framework:&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;# Create a new console application&lt;/span&gt;
dotnet new console &lt;span class="nt"&gt;-n&lt;/span&gt; MaINGroqDemo

&lt;span class="c"&gt;# Navigate to the project directory&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;MaINGroqDemo

&lt;span class="c"&gt;# Add MaIN.NET NuGet package&lt;/span&gt;
dotnet add package MaIN.NET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Set up environment variables
&lt;/h2&gt;

&lt;p&gt;Set your Groq Cloud API key as an environment variable:&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;# macOS/Linux&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GROQ_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_groq_api_key_here

&lt;span class="c"&gt;# Windows (PowerShell)&lt;/span&gt;
&lt;span class="nv"&gt;$env&lt;/span&gt;:GROQ_API_KEY&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_groq_api_key_here"&lt;/span&gt;

&lt;span class="c"&gt;# Windows (CMD)&lt;/span&gt;
&lt;span class="nb"&gt;set &lt;/span&gt;&lt;span class="nv"&gt;GROQ_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_groq_api_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Create the basic application
&lt;/h2&gt;

&lt;p&gt;Replace the contents of &lt;code&gt;Program.cs&lt;/code&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core.Hub&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core.Hub.Contexts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Domain.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🚀 MaIN.NET Groq Cloud Integration Demo"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"====================================="&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Check if API key is available&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GROQ_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"❌ GROQ_API_KEY is not set in environment variables."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   Set the GROQ_API_KEY environment variable with your Groq API key."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   Example: export GROQ_API_KEY=your_api_key_here"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize MaIN.NET with Groq Cloud configuration&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🔧 Initializing MaIN.NET with Groq Cloud..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;MaINBootstrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configureSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GroqCloud&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GroqCloudKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"✅ MaIN.NET initialized successfully!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Run interactive chat&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;RunInteractiveChat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"❌ Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;RunInteractiveChat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n🎯 Interactive Chat with Groq Cloud"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"================================="&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type 'exit' to quit, 'history' to see chat history, or ask me anything:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a single chat context that will maintain conversation history&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chatContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"openai/gpt-oss-120b"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithSystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You are a helpful AI assistant powered by Groq Cloud. Respond in English. Keep your responses concise and friendly."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userInput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nf"&gt;Trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"exit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Goodbye! 👋"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"history"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;ShowChatHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Add the user message to the conversation&lt;/span&gt;
            &lt;span class="n"&gt;chatContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Get the AI response while maintaining context&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"AI: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"❌ Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowChatHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChatContext&lt;/span&gt; &lt;span class="n"&gt;chatContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n📜 Chat History:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"----------------"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chatContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetChatHistory&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No messages yet."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"User"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"You"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"AI"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&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="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Understanding the code
&lt;/h2&gt;

&lt;p&gt;Let's break down the key components of our implementation:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. MaIN.NET initialization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;MaINBootstrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configureSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GroqCloud&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GroqCloudKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This initializes the MaIN.NET framework with Groq Cloud as the backend provider.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Chat context management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chatContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"openai/gpt-oss-120b"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithSystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You are a helpful AI assistant..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create a single &lt;code&gt;ChatContext&lt;/code&gt; instance that maintains conversation history throughout the session.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Message handling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;chatContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We add user messages to the conversation and get AI responses while preserving context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Running the application
&lt;/h2&gt;

&lt;p&gt;Build and run your application:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see output like:&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%2Fz0x7vbf2yr0d45egaoxb.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%2Fz0x7vbf2yr0d45egaoxb.png" alt="Console output" width="800" height="852"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Available Groq models
&lt;/h2&gt;

&lt;p&gt;Groq Cloud supports several high-performance models. For the latest list of available models and their capabilities, visit the &lt;a href="https://console.groq.com/docs/models" rel="noopener noreferrer"&gt;Groq Cloud Model Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can easily switch models by changing the model name in the &lt;code&gt;WithModel()&lt;/code&gt; call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chatContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"openai/gpt-oss-120b"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Switch to gpt-oss model&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithSystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Advanced features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Custom system prompts
&lt;/h3&gt;

&lt;p&gt;You can customize the AI's behavior with system prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithSystemPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You are a coding assistant. Always provide code examples in C# and explain your reasoning."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error handling
&lt;/h3&gt;

&lt;p&gt;The application includes robust error handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"AI: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"❌ Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance benefits
&lt;/h2&gt;

&lt;p&gt;One of the most impressive aspects of Groq Cloud is its speed. Here's what you can expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Response times&lt;/strong&gt;: 100-500ms vs 2-5 seconds with traditional services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No queue times&lt;/strong&gt;: Instant responses without waiting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent performance&lt;/strong&gt;: Reliable speed regardless of load&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost efficiency&lt;/strong&gt;: Fast responses and low costs per interaction&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Now that you have a working Groq Cloud integration, you can extend it with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Web API integration&lt;/strong&gt;: Create a REST API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blazor web application&lt;/strong&gt;: Build interactive web interfaces &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For some inspiration, you can check out my previous article: &lt;a href="https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-1-getting-started-with-llm-16j"&gt;Build a Local ChatGPT-like App with Blazor and MaIN.NET (Part 1)&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Integrating Groq Cloud with MaIN.NET opens up exciting possibilities for building high-performance AI applications. The combination of Groq's lightning-fast inference and MaIN.NET's comprehensive AI framework provides a powerful foundation for modern AI development.&lt;/p&gt;

&lt;p&gt;The key benefits of this integration include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: Ultra-fast response times for better user experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: Easy integration with existing .NET applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Support for multiple models and use cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-effectiveness&lt;/strong&gt;: Efficient resource utilization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're building chatbots, content generators, or complex AI workflows, this setup provides the performance and reliability needed for production applications.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://console.groq.com/" rel="noopener noreferrer"&gt;Groq Cloud Console&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maindoc.link/" rel="noopener noreferrer"&gt;MaIN.NET Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wisedev-code/MaIN.NET" rel="noopener noreferrer"&gt;MaIN.NET GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://console.groq.com/docs" rel="noopener noreferrer"&gt;Groq API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




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

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Vibe coding with .net &amp; Blazor in Cursor - set of rules.</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Thu, 31 Jul 2025 17:30:49 +0000</pubDate>
      <link>https://dev.to/paweljanda/vibe-coding-with-net-blazor-in-cursor-set-of-rules-1gil</link>
      <guid>https://dev.to/paweljanda/vibe-coding-with-net-blazor-in-cursor-set-of-rules-1gil</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In 2025, efficient development isn't just about writing code — it's about maximizing focus, automation, and simplicity. If you're using Cursor with Blazor, .NET 9, and C# 13 — this guide is your new development compass.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🚀 Why rules matter in Vibe Coding
&lt;/h2&gt;

&lt;p&gt;In 2025, we're no longer coding alone. Tools like Cursor enable &lt;strong&gt;vibe coding&lt;/strong&gt; — a fast, intuitive, AI-assisted way to build software where you stay in flow and the AI stays aligned.&lt;/p&gt;

&lt;p&gt;But here’s the problem: without rules, even the smartest AI can become noisy or inconsistent.&lt;/p&gt;

&lt;p&gt;That’s why &lt;strong&gt;defining a clear rule set is foundational to productive vibe coding&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It aligns the AI with your coding style, architecture decisions, and preferences.&lt;/li&gt;
&lt;li&gt;It reduces decision fatigue — so you don’t waste mental energy on naming, formatting, or structure.&lt;/li&gt;
&lt;li&gt;It lets you focus on product thinking and problem-solving, not boilerplate.&lt;/li&gt;
&lt;li&gt;It enables clean team collaboration — especially when rules are shared in Cursor or committed into your project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're solo or part of a team, your rules become your &lt;strong&gt;second brain&lt;/strong&gt; — guiding your assistant, enforcing standards, and preserving mental bandwidth.&lt;/p&gt;

&lt;p&gt;In short: rules make vibe coding &lt;strong&gt;faster, smarter, and smoother&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 How to add rules in Cursor
&lt;/h2&gt;

&lt;p&gt;Cursor has a built-in “Rules” system that tells your AI assistant how to think and respond. To enable these Blazor rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Cursor&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Open Settings option, then go to Cursor Settings&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;“Rules and Memories”&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Paste the entire rule set User Rules or Add new Project Rules&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;You can set different rules for different projects — use this set specifically for &lt;strong&gt;Blazor + .NET&lt;/strong&gt; development.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ✨ The 2025 Blazor + Cursor development rules
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Blazor &amp;amp; .NET Development Guidelines (2025) — Cursor Edition&lt;/span&gt;

You are a senior Blazor and .NET developer working exclusively in &lt;span class="gs"&gt;**Cursor**&lt;/span&gt; for all code editing, AI assistance, and project navigation. Your stack includes &lt;span class="gs"&gt;**Blazor (.NET 9)**&lt;/span&gt;, &lt;span class="gs"&gt;**C# 13**&lt;/span&gt;, and &lt;span class="gs"&gt;**Entity Framework Core**&lt;/span&gt;.

&lt;span class="gu"&gt;## Development Workflow &amp;amp; Tools&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="gs"&gt;**Cursor**&lt;/span&gt; for all development:
&lt;span class="p"&gt;  -&lt;/span&gt; Code editing, AI pair programming, and navigation.
&lt;span class="p"&gt;  -&lt;/span&gt; Built-in terminal for running and debugging.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="gs"&gt;**`dotnet` CLI**&lt;/span&gt; for builds, tests, and running apps:
&lt;span class="p"&gt;  -&lt;/span&gt; Example: &lt;span class="sb"&gt;`dotnet watch run`&lt;/span&gt;, &lt;span class="sb"&gt;`dotnet test`&lt;/span&gt;.

&lt;span class="gu"&gt;## Code Style &amp;amp; Architecture&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Write &lt;span class="gs"&gt;**idiomatic, modern C# 13**&lt;/span&gt; and &lt;span class="gs"&gt;**Blazor**&lt;/span&gt; code.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="gs"&gt;**component-first**&lt;/span&gt; design:
&lt;span class="p"&gt;  -&lt;/span&gt; Small, focused components.
&lt;span class="p"&gt;  -&lt;/span&gt; Extract logic into services or partial classes.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="gs"&gt;**partial class**&lt;/span&gt; with &lt;span class="sb"&gt;`@code`&lt;/span&gt; for separation.
&lt;span class="p"&gt;-&lt;/span&gt; Prefer &lt;span class="gs"&gt;**top-level statements**&lt;/span&gt; and &lt;span class="gs"&gt;**file-scoped namespaces**&lt;/span&gt;.

&lt;span class="gu"&gt;## Naming Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`PascalCase`&lt;/span&gt; for: components, public members, classes, methods.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`camelCase`&lt;/span&gt; for: private fields, locals, parameters.
&lt;span class="p"&gt;-&lt;/span&gt; Interfaces use &lt;span class="sb"&gt;`I`&lt;/span&gt; prefix: &lt;span class="sb"&gt;`IUserService`&lt;/span&gt;.

&lt;span class="gu"&gt;## Blazor-Specific Patterns&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use lifecycle hooks: &lt;span class="sb"&gt;`OnInitializedAsync`&lt;/span&gt;, &lt;span class="sb"&gt;`OnParametersSetAsync`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Bind data with &lt;span class="sb"&gt;`@bind`&lt;/span&gt;, &lt;span class="sb"&gt;`@bind-Value`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`[Parameter]`&lt;/span&gt;, &lt;span class="sb"&gt;`[CascadingParameter]`&lt;/span&gt;, &lt;span class="sb"&gt;`[Inject]`&lt;/span&gt; consistently.
&lt;span class="p"&gt;-&lt;/span&gt; Keep UI logic in components, business logic in services.
&lt;span class="p"&gt;-&lt;/span&gt; Use Razor Class Libraries (RCL) for reusable components.

&lt;span class="gu"&gt;## Modern C# Features&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Leverage:
&lt;span class="p"&gt;  -&lt;/span&gt; Required members
&lt;span class="p"&gt;  -&lt;/span&gt; List patterns
&lt;span class="p"&gt;  -&lt;/span&gt; Primary constructors
&lt;span class="p"&gt;  -&lt;/span&gt; Lambda method groups
&lt;span class="p"&gt;  -&lt;/span&gt; Collection expressions
&lt;span class="p"&gt;-&lt;/span&gt; Use global usings and file-scoped namespaces.

&lt;span class="gu"&gt;## Testing &amp;amp; Debugging&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`dotnet test`&lt;/span&gt; with &lt;span class="sb"&gt;`xUnit`&lt;/span&gt;, &lt;span class="sb"&gt;`NUnit`&lt;/span&gt;, or &lt;span class="sb"&gt;`MSTest`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Mock with &lt;span class="sb"&gt;`Moq`&lt;/span&gt; or &lt;span class="sb"&gt;`NSubstitute`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`ILogger&amp;lt;T&amp;gt;`&lt;/span&gt; and &lt;span class="sb"&gt;`try-catch`&lt;/span&gt; for backend error tracing.

&lt;span class="gu"&gt;## Performance &amp;amp; Optimization&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`async/await`&lt;/span&gt; throughout for non-blocking UI.
&lt;span class="p"&gt;-&lt;/span&gt; Reduce unnecessary renders:
&lt;span class="p"&gt;  -&lt;/span&gt; Use &lt;span class="sb"&gt;`ShouldRender()`&lt;/span&gt;.
&lt;span class="p"&gt;  -&lt;/span&gt; Use &lt;span class="sb"&gt;`@key`&lt;/span&gt; for DOM diffing.
&lt;span class="p"&gt;-&lt;/span&gt; Throttle/debounce high-frequency events.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`StateHasChanged()`&lt;/span&gt; intentionally.

&lt;span class="gu"&gt;### Blazor Server:&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`IMemoryCache`&lt;/span&gt; for in-memory caching.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`IDistributedCache`&lt;/span&gt; (e.g., Redis) for multi-user/session apps.
&lt;span class="p"&gt;-&lt;/span&gt; Remember about @rendermode InteractiveServer directive.

&lt;span class="gu"&gt;### Blazor WebAssembly:&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`localStorage`&lt;/span&gt; or &lt;span class="sb"&gt;`sessionStorage`&lt;/span&gt; (via Blazored packages).
&lt;span class="p"&gt;-&lt;/span&gt; Cache static or rarely changing API responses.

&lt;span class="gu"&gt;## State Management&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Basic state: &lt;span class="sb"&gt;`CascadingParameter`&lt;/span&gt;, &lt;span class="sb"&gt;`EventCallback`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Intermediate: Scoped services with custom state containers.
&lt;span class="p"&gt;-&lt;/span&gt; Complex: Use &lt;span class="sb"&gt;`Fluxor`&lt;/span&gt;, &lt;span class="sb"&gt;`BlazorState`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; For WebAssembly: persist state via &lt;span class="sb"&gt;`Blazored.LocalStorage`&lt;/span&gt;.

&lt;span class="gu"&gt;## API Integration&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`HttpClient`&lt;/span&gt; via &lt;span class="sb"&gt;`HttpClientFactory`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Structure DTOs with &lt;span class="sb"&gt;`record`&lt;/span&gt; types.
&lt;span class="p"&gt;-&lt;/span&gt; Wrap API calls with &lt;span class="sb"&gt;`try-catch`&lt;/span&gt; and provide feedback.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`Minimal APIs`&lt;/span&gt; or MVC-style controllers in backend.

&lt;span class="gu"&gt;## Security &amp;amp; Authentication&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Implement auth with ASP.NET Identity or JWT.
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`[Authorize]`&lt;/span&gt; and policies.
&lt;span class="p"&gt;-&lt;/span&gt; Enforce HTTPS.
&lt;span class="p"&gt;-&lt;/span&gt; Secure client-side storage of tokens.

&lt;span class="gu"&gt;## API Documentation&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use Swagger/OpenAPI via &lt;span class="sb"&gt;`Swashbuckle.AspNetCore`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Add &lt;span class="sb"&gt;`[ProducesResponseType]`&lt;/span&gt;, XML docs, summaries.
&lt;span class="p"&gt;-&lt;/span&gt; Use NSwag for generating C# clients if needed.

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

&lt;/div&gt;






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

&lt;p&gt;If you're serious about developing Blazor apps in 2025, your tools — and how you use them — matter more than ever. With Cursor, a minimal yet powerful setup, and a rulebook that helps you stay in the zone, you’ll ship faster and cleaner code.&lt;/p&gt;

&lt;p&gt;If this helped you, leave a ❤️ or comment — and feel free to reuse and modify the rules as you prefer!&lt;/p&gt;




</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>vibecoding</category>
      <category>cursor</category>
    </item>
    <item>
      <title>The easiest way to connect your .NET app to Gemini using MaIN.NET.</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Wed, 21 May 2025 19:23:18 +0000</pubDate>
      <link>https://dev.to/paweljanda/the-easiest-way-to-connect-your-net-app-to-gemini-using-mainnet-4d5p</link>
      <guid>https://dev.to/paweljanda/the-easiest-way-to-connect-your-net-app-to-gemini-using-mainnet-4d5p</guid>
      <description>&lt;p&gt;Integrating large language models like &lt;strong&gt;Gemini&lt;/strong&gt; from Google into a .NET application is often associated with setting up HTTP clients, managing API payloads, handling rate limits, and parsing responses. If you're just starting out or want a simpler way, the &lt;a href="https://deepmain.io" rel="noopener noreferrer"&gt;&lt;code&gt;MaIN.NET&lt;/code&gt;&lt;/a&gt; library offers a minimal setup to get you connected with just a few lines of code.&lt;/p&gt;

&lt;p&gt;This tutorial shows how to configure your app to use Gemini and includes a small example project to demonstrate interaction with the model.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You’ll need?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/en-us/download" rel="noopener noreferrer"&gt;.NET SDK&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A Gemini API key from &lt;a href="https://aistudio.google.com/" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt; or Vertex AI.&lt;/li&gt;
&lt;li&gt;An IDE or text editor (e.g., Visual Studio Code, Rider, or Cursor)&lt;/li&gt;
&lt;li&gt;Internet connection for API access&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Good to know&lt;/strong&gt;: Google currently offers a &lt;strong&gt;generous free quota&lt;/strong&gt; for Gemini API usage via both AI Studio and Vertex AI. This is a great opportunity to test LLM features without incurring any cost. Make sure to check your quota limits in your Google Cloud console or AI Studio account.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. What is MaIN.NET?
&lt;/h2&gt;

&lt;p&gt;MaIN.NET is a powerful open-source framework that standardizes how .NET apps connect to various LLM backends both localy and in the cloud. It supports models from providers like OpenAI, Gemini, Mistral, LLama, Deepseek, Qwen and others. It wraps the low-level HTTP calls and offers a uniform API surface for chat completion and future agent-based capabilities.&lt;/p&gt;

&lt;p&gt;You don’t need to write custom HTTP logic. Instead, you just configure the backend and use the built-in methods to orchestrate LLMs.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Minimal Gemini integration.
&lt;/h2&gt;

&lt;p&gt;Here’s the full setup needed to enable Gemini in your .NET application using MaIN.NET:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core.Hub&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Domain.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;MaINBootstrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configureSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gemini&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GeminiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YOUR_GEMINI_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"What is LLM?"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-2.0-flash"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;"YOUR_GEMINI_API_KEY"&lt;/code&gt; with your actual key.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Example: Simple console app to query Gemini.
&lt;/h2&gt;

&lt;p&gt;We’ll now create a simple app that accepts user input and returns a response from Gemini.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step-by-Step:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  a. Create a new project
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new console &lt;span class="nt"&gt;-n&lt;/span&gt; GeminiExample
&lt;span class="nb"&gt;cd &lt;/span&gt;GeminiExample
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  b. Add MaIN.NET package
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package MaIN.NET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  c. Add your code
&lt;/h4&gt;

&lt;p&gt;Edit &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Core.Hub&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MaIN.Domain.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="c1"&gt;// Initialize MaIN.NET with Gemini&lt;/span&gt;
&lt;span class="n"&gt;MaINBootstrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configureSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BackendType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackendType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gemini&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GeminiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YOUR_GEMINI_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ask Gemini something: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-2.0-flash"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nResponse:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  d. Run the app
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;p&gt;Try inputs like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;"Explain how JWT tokens work"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"Generate 3 creative names for a coffee startup"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"What’s the difference between async and parallel in C#?"&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Behind the scenes.
&lt;/h2&gt;

&lt;p&gt;Here’s what happens under the hood when you call &lt;code&gt;CompleteAsync()&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The MaIN.NET library prepares the API request with the selected backend (Gemini).&lt;/li&gt;
&lt;li&gt;It serializes your prompt and model choice into the format Gemini expects.&lt;/li&gt;
&lt;li&gt;It sends the HTTP request and handles the response parsing.&lt;/li&gt;
&lt;li&gt;You receive the output as a typed object, making it easier to access content directly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Where to go next?
&lt;/h2&gt;

&lt;p&gt;If you want to take it further, try building a Blazor-based website integrated with Gemini — &lt;a href="https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-1-getting-started-with-llm-16j"&gt;this guide on using MaIN.NET with Blazor&lt;/a&gt; can help you get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary.
&lt;/h2&gt;

&lt;p&gt;Using MaIN.NET, connecting your .NET application to Gemini is quick and clean. You don’t need to handle HTTP setup, JSON parsing, or authentication flows manually. If you’re experimenting with LLMs, Google’s current free tier provides a great environment to build and test ideas without worrying about cost. Happy coding!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>ai</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Is Blazor the Best Alternative to JavaScript Frameworks and Libraries?</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Sun, 27 Apr 2025 12:40:23 +0000</pubDate>
      <link>https://dev.to/paweljanda/is-blazor-the-best-alternative-to-javascript-frameworks-and-libraries-2j82</link>
      <guid>https://dev.to/paweljanda/is-blazor-the-best-alternative-to-javascript-frameworks-and-libraries-2j82</guid>
      <description>&lt;p&gt;Inspired by two YT videos about the Blazor's future (links in the comments) I decided to write this article. The web development ecosystem has long been dominated by JavaScript frameworks like React, Angular, and Vue.js. Recently, Microsoft's Blazor has emerged as a strong and compelling alternative, empowering developers to build interactive web applications using C# and the .NET platform. But can Blazor be considered the &lt;em&gt;best&lt;/em&gt; alternative to JavaScript frameworks? Increasingly, the answer is trending positively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blazor's Key Strengths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Seamless .NET integration:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Blazor’s tight integration with .NET offers developers immediate access to the expansive .NET ecosystem, including scalability with .NET Aspire and advanced AI capabilities through .NET libraries. Microsoft's long-term commitment to Blazor, as highlighted by Dan Roth, ensures that Blazor is well-supported, stable, and future-proofed within the .NET ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full-Stack development with C#:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
One of Blazor’s most significant advantages is the ability to use C# across the entire application stack. This not only eliminates the need to learn or use JavaScript but also simplifies code management and improves productivity. Developers proficient in C# find Blazor particularly intuitive and easier to adopt compared to JavaScript frameworks, significantly reducing the learning curve, especially for beginners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexible performance and hosting models:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Blazor provides versatile hosting models tailored to diverse performance and security requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blazor Server&lt;/strong&gt;: Offers fast load times and secure real-time interactions with server-side logic via SignalR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blazor WebAssembly (WASM)&lt;/strong&gt;: Executes .NET directly in browsers, enabling offline support and near-native performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-Side Rendering (SSR) and Streaming Rendering&lt;/strong&gt;: Enhances dynamic content delivery and performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Strongly typed development environment:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
C#’s strongly typed nature offers superior compile-time error checking, significantly reducing runtime errors compared to JavaScript. The robust tooling available in Visual Studio further enhances the developer experience, making Blazor especially appealing for those seeking reliability and ease of debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backed by Microsoft:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Continuous investment and robust support from Microsoft underscore Blazor’s stability and long-term viability, giving developers confidence in its strategic importance within the .NET ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Considerations compared to JavaScript frameworks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Job market and skill transferability:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
While the job market currently favors JavaScript frameworks, expertise in Blazor and the broader .NET ecosystem substantially enhances a developer’s versatility, making them highly marketable across diverse .NET-focused roles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rapidly growing community:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Though smaller than JavaScript frameworks' communities, Blazor’s ecosystem is expanding rapidly, driven by enthusiastic developers and substantial support from Microsoft. New libraries and components are continuously emerging, enriching its ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Front-End performance considerations:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Blazor WebAssembly requires downloading the .NET runtime, which slightly increases initial load times. However, for internal or enterprise applications deeply integrated with .NET, the productivity benefits often outweigh this minor overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  When should You choose blazor?
&lt;/h3&gt;

&lt;p&gt;Blazor excels for organizations already invested in .NET infrastructure. Beginners familiar with C# will find Blazor more accessible, significantly reducing complexity and learning requirements compared to JavaScript frameworks. By eliminating the necessity of using JavaScript, Blazor simplifies development, reduces errors, and improves overall productivity.&lt;/p&gt;

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

&lt;p&gt;Blazor is increasingly establishing itself as an exceptional alternative to JavaScript frameworks. Its ease of adoption for C# developers, comprehensive full-stack capabilities, robust tooling, and strong backing from Microsoft make it particularly appealing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ideal for:&lt;/strong&gt; Beginners familiar with C#, teams focused on .NET, and internal or enterprise-level applications seeking productivity and simplicity without JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less optimal for:&lt;/strong&gt; Applications strictly requiring minimal initial load times for wide-ranging device compatibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Blazor's strengths clearly position it as a leading choice for modern web development, especially for teams leveraging the .NET ecosystem. Developers evaluating web frameworks should seriously consider Blazor as an advantageous and highly practical alternative to traditional JavaScript frameworks.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>programming</category>
    </item>
    <item>
      <title>Build a Local ChatGPT-like App with Blazor and MAIN.NET – Part 3: Chatting with Your PDF Files.</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Fri, 25 Apr 2025 18:42:59 +0000</pubDate>
      <link>https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-3-chatting-with-your-pdf-files-3m98</link>
      <guid>https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-3-chatting-with-your-pdf-files-3m98</guid>
      <description>&lt;p&gt;Welcome back to the series! 👋&lt;br&gt;&lt;br&gt;
In &lt;a href="https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-2-adding-conversation-history-57n7"&gt;&lt;strong&gt;Part 2&lt;/strong&gt;&lt;/a&gt;, we added chat history support, allowing our app to maintain conversation context like a real assistant.&lt;/p&gt;

&lt;p&gt;Today, we’re taking another big step forward — enabling our chatbot to &lt;strong&gt;read and discuss uploaded PDF files&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Imagine uploading a research paper, a business report, or a contract — and asking the LLM to summarize, explain, or extract information from it.&lt;br&gt;&lt;br&gt;
Let's make it happen!&lt;/p&gt;


&lt;h2&gt;
  
  
  💡 What we’re building?
&lt;/h2&gt;

&lt;p&gt;By the end of this part, you’ll be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📎 Upload multiple PDF files&lt;/li&gt;
&lt;li&gt;🗂 See a list of uploaded documents&lt;/li&gt;
&lt;li&gt;🗑️ Remove individual files or clear all uploads&lt;/li&gt;
&lt;li&gt;💬 Chat with the LLM about the content inside the PDFs&lt;/li&gt;
&lt;li&gt;📄 Persist uploaded files between page reloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All files will be stored safely and reliably, with a clean user interface and correct file handling behind the scenes.&lt;/p&gt;


&lt;h3&gt;
  
  
  📦 One more thing before we begin: Embeddings model.
&lt;/h3&gt;

&lt;p&gt;Before we move on, we need to add &lt;strong&gt;one more model&lt;/strong&gt; to support document understanding — an &lt;strong&gt;embeddings model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This model is used behind the scenes to analyze and chunk your PDF content in a way that the LLM can actually understand and reference during conversation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🛠️ If you were using the &lt;strong&gt;MaIN.NET CLI&lt;/strong&gt;, this step would happen automatically.&lt;br&gt;&lt;br&gt;
I’ll cover the CLI setup in a separate tutorial, but for now let’s do it manually.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;👉 Download the model from Hugging Face:&lt;br&gt;&lt;br&gt;
&lt;a href="https://huggingface.co/Inza124/Nomic" rel="noopener noreferrer"&gt;https://huggingface.co/Inza124/Nomic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once downloaded, place the &lt;code&gt;.gguf&lt;/code&gt; file in the &lt;strong&gt;same folder&lt;/strong&gt; where you stored your Gemma model — for example /Documents/MainModels/.&lt;/p&gt;

&lt;p&gt;We’ll reference this embeddings model automatically later when chatting with PDFs. That’s all for setup — now let’s build!&lt;/p&gt;


&lt;h2&gt;
  
  
  🛠 Key features we’re adding.
&lt;/h2&gt;
&lt;h3&gt;
  
  
  🔼 File upload UI.
&lt;/h3&gt;

&lt;p&gt;We will enhance the message input area to not only send text messages but also easily attach PDF files.&lt;/p&gt;

&lt;p&gt;Find this part of code and remove it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex gap-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt; 
           &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;bind=&lt;/span&gt;&lt;span class="s"&gt;"messageToLLM"&lt;/span&gt; 
           &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;bind:event=&lt;/span&gt;&lt;span class="s"&gt;"oninput"&lt;/span&gt; 
           &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onkeydown=&lt;/span&gt;&lt;span class="s"&gt;"HandleKeyDown"&lt;/span&gt; 
           &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Type your message..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"SendMessage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Send&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll slightly reorganize the layout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the familiar text input field for typing your messages.&lt;/li&gt;
&lt;li&gt;Add a new &lt;strong&gt;file attachment button&lt;/strong&gt; (with a paperclip icon) to upload PDF files.&lt;/li&gt;
&lt;li&gt;Keep the &lt;strong&gt;Send&lt;/strong&gt; button aligned next to it for a smooth user experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, the entire input and button section should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border rounded p-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex gap-2 mb-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt; 
               &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;bind=&lt;/span&gt;&lt;span class="s"&gt;"messageToLLM"&lt;/span&gt; 
               &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;bind:event=&lt;/span&gt;&lt;span class="s"&gt;"oninput"&lt;/span&gt; 
               &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onkeydown=&lt;/span&gt;&lt;span class="s"&gt;"HandleKeyDown"&lt;/span&gt; 
               &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Type your message..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex gap-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-outline-secondary px-2 d-flex align-items-center"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"cursor: pointer;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bi bi-paperclip"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;InputFile&lt;/span&gt; &lt;span class="na"&gt;OnChange=&lt;/span&gt;&lt;span class="s"&gt;"@LoadFiles"&lt;/span&gt; &lt;span class="na"&gt;multiple&lt;/span&gt; &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;".pdf"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-none"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary px-4"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"SendMessage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Send&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This layout keeps everything compact and intuitive:&lt;br&gt;&lt;br&gt;
you can type, attach PDFs, and send messages — all from the same place!&lt;/p&gt;

&lt;p&gt;After the files are uploaded, we wand to display:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;clear files&lt;/strong&gt; button to remove all uploaded documents  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;list of uploaded files&lt;/strong&gt; with nice PDF icons and the original filenames  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;remove button&lt;/strong&gt; next to each file for individual deletion  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To achieve that, we need to add the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@if (uploadedFiles.Any())
    {
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border-top pt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex justify-content-between align-items-center mb-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;Attached files:&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-link btn-sm text-danger p-0"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"ClearFiles"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;Clear all&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex flex-wrap gap-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                @foreach (var file in uploadedFiles)
                {
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-light rounded p-2 d-flex align-items-center"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"font-size: 0.875rem;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bi bi-file-pdf text-danger me-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-truncate"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"max-width: 200px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;@file.Name&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-link text-danger p-0 ms-2"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"font-size: 0.875rem;"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"() =&amp;gt; RemoveFile(file)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bi bi-x"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                }
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt; Don't forget to create the "uploads" directory in the wwwroot. You can do it directly from Terminal inside the project by:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IMPORTANT-2😜&lt;/strong&gt; Don't forget to link the bootstrap icons as it's not a part of Blazor app boilerplate. In the App.razor file insert the line in the head section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🧰 File handling logic behind the scenes.
&lt;/h3&gt;

&lt;p&gt;Our backend logic ensures that files are properly managed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LoadFiles&lt;/code&gt; handles file uploads, saves them to the &lt;code&gt;wwwroot/uploads&lt;/code&gt; directory
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;LoadFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InputFileChangeEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetMultipleFiles&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;".pdf"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetRandomFileName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"_"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uploadsPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenReadStream&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;CopyToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="n"&gt;uploadedFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UploadedFile&lt;/span&gt; 
                &lt;span class="p"&gt;{&lt;/span&gt; 
                    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RemoveFile&lt;/code&gt; lets users delete specific uploaded files
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;RemoveFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UploadedFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;uploadedFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Error deleting file: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ClearFiles&lt;/code&gt; removes all uploaded files at once
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ClearFiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;uploadedFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;RemoveFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Automatic file cleanup happens when the component is disposed to keep the storage clean
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;ClearFiles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🌐 Handling web root and persistence.
&lt;/h3&gt;

&lt;p&gt;We made sure the file system logic is &lt;strong&gt;environment-agnostic&lt;/strong&gt; and &lt;strong&gt;persistent&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Injected &lt;code&gt;IWebHostEnvironment&lt;/code&gt; to dynamically get the correct &lt;code&gt;wwwroot&lt;/code&gt; path. Add this two lines below using declarations.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@using&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AspNetCore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hosting&lt;/span&gt;
&lt;span class="n"&gt;@inject&lt;/span&gt; &lt;span class="n"&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Created a computed &lt;code&gt;uploadsPath&lt;/code&gt; property that points to the uploads directory and a list of uploaded files:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UploadedFile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;uploadedFiles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;uploadsPath&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebRootPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"uploads"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;OnInitialized&lt;/code&gt;, we:

&lt;ul&gt;
&lt;li&gt;Ensure the uploads folder exists&lt;/li&gt;
&lt;li&gt;Load any pre-existing files if they were uploaded before
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnInitialized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Ensure uploads directory exists&lt;/span&gt;
        &lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uploadsPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Load existing files&lt;/span&gt;
        &lt;span class="nf"&gt;LoadExistingFiles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LoadExistingFiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;uploadedFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uploadsPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;originalName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFileName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Last&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;uploadedFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UploadedFile&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;originalName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, uploaded PDFs &lt;strong&gt;persist across page reloads&lt;/strong&gt;, and you always see the correct files on app startup.&lt;/p&gt;




&lt;h3&gt;
  
  
  💬 Updated chat functionality.
&lt;/h3&gt;

&lt;p&gt;We updated the &lt;code&gt;SendMessage&lt;/code&gt; method so that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It sends not only your chat message but also the &lt;strong&gt;paths to uploaded PDFs&lt;/strong&gt; to the LLM&lt;/li&gt;
&lt;li&gt;The LLM now has full access to the contents of the uploaded documents when responding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below you can find updated SendMessage method code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageToLLM&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Add user message to history&lt;/span&gt;
        &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageToLLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageToLLM&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;messageToLLM&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatInstance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;chatInstance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemma3:4b"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatInstance&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uploadedFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Add chat response to history&lt;/span&gt;
            &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also introduced a new &lt;code&gt;UploadedFile&lt;/code&gt; class to manage uploaded file states. You can create it below the ChatMessage class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UploadedFile&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 How it works now?
&lt;/h2&gt;

&lt;p&gt;✅ Upload one or more PDFs&lt;br&gt;&lt;br&gt;
✅ See uploaded files with original names and icons&lt;br&gt;&lt;br&gt;
✅ Remove individual files or clear all uploads&lt;br&gt;&lt;br&gt;
✅ Persist uploaded files across page reloads&lt;br&gt;&lt;br&gt;
✅ Chat about the content inside PDFs with the LLM!&lt;/p&gt;

&lt;p&gt;All operations are smooth, safe, and ready for real-world use cases.&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%2Fzryyf55xj8347rh4g4hk.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%2Fzryyf55xj8347rh4g4hk.png" alt="Image description" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What’s next?
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Part 4&lt;/strong&gt;, we’ll focus on making our app look and feel even better!&lt;br&gt;&lt;br&gt;
We’ll refine the chat UI, improve the layout, and make it more incredible.&lt;/p&gt;

&lt;p&gt;Expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design tweaks&lt;/li&gt;
&lt;li&gt;Chat bubble animations&lt;/li&gt;
&lt;li&gt;Typing indicators... and maybe even &lt;strong&gt;dark mode&lt;/strong&gt; 🌙&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&amp;lt;&amp;lt; To be continued &amp;gt;&amp;gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;💬 If you enjoyed this part, leave a comment, share the tutorial, and don’t forget to ⭐ the &lt;a href="https://github.com/wisedev-code/MaIN.NET" rel="noopener noreferrer"&gt;MAIN.NET GitHub repo&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>llm</category>
      <category>ai</category>
    </item>
    <item>
      <title>Build a Local ChatGPT-like App with Blazor and MaIN.NET – Part 2: Adding conversation history.</title>
      <dc:creator>Pawel Janda</dc:creator>
      <pubDate>Thu, 17 Apr 2025 17:13:19 +0000</pubDate>
      <link>https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-2-adding-conversation-history-57n7</link>
      <guid>https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-2-adding-conversation-history-57n7</guid>
      <description>&lt;p&gt;Welcome back to the series! 👋&lt;br&gt;&lt;br&gt;
In &lt;a href="https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-1-getting-started-with-llm-16j"&gt;&lt;strong&gt;Part 1&lt;/strong&gt;&lt;/a&gt;, we built a basic Blazor app that lets you chat with a local LLM using the &lt;strong&gt;MaIN.NET&lt;/strong&gt; framework. We connected to a local model (Gemma3 from Google), sent messages, and got responses — all without any external APIs.&lt;/p&gt;

&lt;p&gt;In this part, we’re going to take it one step further by adding &lt;strong&gt;chat history support&lt;/strong&gt;. Why is that important?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🗣️ Without conversation history, the model responds to each message as if it’s the first.&lt;br&gt;&lt;br&gt;
🧠 With history, it can remember context and carry on a more natural, coherent conversation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By the end of this tutorial, your app will be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep track of previous messages in the session&lt;/li&gt;
&lt;li&gt;Send a complete conversation log to the model&lt;/li&gt;
&lt;li&gt;Generate responses that reflect the ongoing chat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive in!&lt;/p&gt;


&lt;h2&gt;
  
  
  🛠 Step 1: Prepare for Chat History
&lt;/h2&gt;

&lt;p&gt;Before we start making our chatbot smarter, let’s make sure everything from &lt;strong&gt;Part 1&lt;/strong&gt; is up and running. If you haven’t completed it yet, check it out &lt;a href="https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-1-getting-started-with-llm-16j"&gt;here&lt;/a&gt; and follow along to get your base project ready.&lt;/p&gt;

&lt;p&gt;Now that your app can talk to a local LLM, we’ll make some adjustments to support &lt;strong&gt;chat history&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  👉 Update &lt;code&gt;ChatLLM.razor&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;First, we need to import two additional namespaces to help us manage context and serialize our message history.&lt;/p&gt;

&lt;p&gt;At the top of your &lt;code&gt;ChatLLM.razor&lt;/code&gt; file, add the following &lt;code&gt;@using&lt;/code&gt; statements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@using&lt;/span&gt; &lt;span class="n"&gt;MaIN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contexts&lt;/span&gt;
&lt;span class="n"&gt;@using&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧱 Step 2: Update the UI to Support Chat History
&lt;/h2&gt;

&lt;p&gt;To display and differentiate between user and LLM messages, we need to update the HTML in &lt;code&gt;ChatLLM.razor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We’ll simplify the layout and introduce new styles so it’s easy to see &lt;strong&gt;who said what&lt;/strong&gt; in the conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧼 1. Clean Up the Old HTML
&lt;/h3&gt;

&lt;p&gt;Let’s start by removing everything below the &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; header in your &lt;code&gt;ChatLLM.razor&lt;/code&gt;. We’ll be rebuilding it from scratch.&lt;/p&gt;

&lt;p&gt;So keep only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Chat with LLM&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Delete everything else below it.&lt;/p&gt;

&lt;h3&gt;
  
  
  💬 2. Add the Chat Message Display
&lt;/h3&gt;

&lt;p&gt;Now, paste the following &lt;strong&gt;chat bubble layout&lt;/strong&gt; directly under the &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; header in your &lt;code&gt;ChatLLM.razor&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border rounded p-3 mb-3 bg-light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @foreach (var message in chatHistory)
    {
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3 p-2 rounded @(message.IsUser ? "&lt;/span&gt;&lt;span class="na"&gt;bg-primary&lt;/span&gt; &lt;span class="na"&gt;text-white&lt;/span&gt; &lt;span class="na"&gt;ms-5&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;bg-light&lt;/span&gt; &lt;span class="na"&gt;me-5&lt;/span&gt;&lt;span class="err"&gt;")"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;@(message.IsUser ? "You" : "LLM chat"):&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-0 mt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;@message.Content&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    }
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🔍 What’s happening here?
&lt;/h4&gt;

&lt;p&gt;We’re looping through the &lt;code&gt;chatHistory&lt;/code&gt; list.&lt;/p&gt;

&lt;p&gt;Each message is styled based on the sender:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User messages&lt;/strong&gt; appear on the right, with a blue background.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM responses&lt;/strong&gt; appear on the left, with a light gray background.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helps visually distinguish who said what, making the conversation more readable and natural to follow.&lt;/p&gt;

&lt;h3&gt;
  
  
  📝 3. Add the Input Field and Send Button
&lt;/h3&gt;

&lt;p&gt;Below the chat display, add the following input section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex gap-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt; 
           &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;bind=&lt;/span&gt;&lt;span class="s"&gt;"messageToLLM"&lt;/span&gt; 
           &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;bind:event=&lt;/span&gt;&lt;span class="s"&gt;"oninput"&lt;/span&gt; 
           &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onkeydown=&lt;/span&gt;&lt;span class="s"&gt;"HandleKeyDown"&lt;/span&gt; 
           &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Type your message..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"SendMessage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Send&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  💡 This layout does the following:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Binds the input to the &lt;code&gt;messageToLLM&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;Allows users to press &lt;strong&gt;Enter&lt;/strong&gt; to send a message using &lt;code&gt;HandleKeyDown&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Provides a &lt;strong&gt;Send&lt;/strong&gt; button as an alternative method of submission.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next step, we’ll define the message model, initialize the &lt;code&gt;chatHistory&lt;/code&gt; list, and modify &lt;code&gt;SendMessage()&lt;/code&gt; to send the full conversation context to the LLM.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Step 3: Manage Chat History in the Code Section
&lt;/h2&gt;

&lt;p&gt;That’s it for the front-end part. Now let’s switch to the &lt;code&gt;@code&lt;/code&gt; section of your &lt;code&gt;ChatLLM.razor&lt;/code&gt; file. We'll refactor it heavily.&lt;/p&gt;

&lt;p&gt;You can &lt;strong&gt;keep only the first line&lt;/strong&gt; of your existing code block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;messageToLLM&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, directly below it, add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;chatHistory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ChatContext&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;chatInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatMessage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsUser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧾 What we just did:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;chatHistory&lt;/code&gt; – a list that stores the entire conversation, which we already connected to the front-end display.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ChatMessage&lt;/code&gt; – a small class that holds the message content and a flag (&lt;code&gt;IsUser&lt;/code&gt;) to determine whether the message came from the user or the model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You could extract this class into a separate file later, especially as your project grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔁 Replace the &lt;code&gt;SendMessage()&lt;/code&gt; Method
&lt;/h3&gt;

&lt;p&gt;Next, we’ll completely rewrite the &lt;code&gt;SendMessage()&lt;/code&gt; method to handle message history and preserve context across requests.&lt;/p&gt;

&lt;p&gt;Replace your existing method with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageToLLM&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Add user message to history&lt;/span&gt;
    &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageToLLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageToLLM&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;messageToLLM&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chatInstance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;chatInstance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AIHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemma3:4b"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chatInstance&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Add chat response to history&lt;/span&gt;
        &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ChatMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔍 What’s happening here?
&lt;/h3&gt;

&lt;p&gt;We check if the message is empty.&lt;/p&gt;

&lt;p&gt;If not, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the user's message to the &lt;code&gt;chatHistory&lt;/code&gt; list and mark it as &lt;code&gt;IsUser = true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Clear the input field.&lt;/li&gt;
&lt;li&gt;If it’s the first message, we create a new &lt;code&gt;ChatContext&lt;/code&gt; instance with our local model.&lt;/li&gt;
&lt;li&gt;Then we call &lt;code&gt;.WithMessage()&lt;/code&gt; to send the message within the current chat session — maintaining &lt;strong&gt;context awareness&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The LLM’s response is added to the chat history as a system message (&lt;code&gt;IsUser = false&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⌨️ Add Enter Key Support
&lt;/h3&gt;

&lt;p&gt;To improve the UX of your chat, let’s allow users to press &lt;strong&gt;Enter&lt;/strong&gt; to send a message.&lt;/p&gt;

&lt;p&gt;Add this method below your &lt;code&gt;@code&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;HandleKeyDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KeyboardEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Enter"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;SendMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 You’re all set! Your chatbot now supports full conversation history and feels much more like ChatGPT.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Summary
&lt;/h2&gt;

&lt;p&gt;In this part, we took a big step forward by adding &lt;strong&gt;chat history support&lt;/strong&gt; to our local ChatGPT-like app. Here's what we accomplished:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactored the UI to display a styled conversation thread.&lt;/li&gt;
&lt;li&gt;Built a message model to manage chat state.&lt;/li&gt;
&lt;li&gt;Used &lt;code&gt;ChatContext&lt;/code&gt; to keep the conversation alive between turns.&lt;/li&gt;
&lt;li&gt;Enabled sending messages with the &lt;strong&gt;Enter key&lt;/strong&gt; for smoother UX.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, your chatbot can maintain &lt;strong&gt;context across multiple messages&lt;/strong&gt;, just like real-world assistants!&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What’s Next?
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Part 3&lt;/strong&gt;, we’ll add &lt;strong&gt;file upload support&lt;/strong&gt;, allowing users to upload PDFs and have conversations about the content inside them.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Accept file uploads in Blazor&lt;/li&gt;
&lt;li&gt;Read and process PDF files&lt;/li&gt;
&lt;li&gt;Inject the content into the chat context dynamically&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🗂️ Imagine uploading a document and asking the model:&lt;br&gt;&lt;br&gt;
&lt;em&gt;"Can you summarize this?"&lt;/em&gt; or &lt;em&gt;"What are the key takeaways?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stay tuned — it's going to get even more powerful!&lt;/p&gt;

&lt;p&gt;💬 Got questions or feedback? Drop a comment below — I’d love to hear your thoughts!&lt;/p&gt;

&lt;p&gt;👉 Go to Part 3:&lt;a href="https://dev.to/paweljanda/build-a-local-chatgpt-like-app-with-blazor-and-mainnet-part-3-chatting-with-your-pdf-files-3m98"&gt;Here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>ai</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
