# NeonDB Integration: First-Class Support for React & Next.js Templates

> Generated by swarm planning session on 2026-02-17

## Summary

Elevate Neon from a Portal-template-only experiment to a first-class database integration on par with Supabase. Users will be able to connect Neon to any React or Next.js app, with full AI agent support for schema introspection, SQL execution, and code generation — all without requiring the Portal template.

## Problem Statement

Today, Neon in Dyad is a second-class citizen:

- **Locked to the Portal template**: Only `portal-mini-store` has `requiresNeon: true` — users cannot use Neon with React or Next.js templates
- **No agent tools**: The AI agent has no `execute_sql`, `get_table_schema`, or `get_project_info` equivalents for Neon
- **No schema introspection**: The agent cannot read Neon table schemas, so it cannot generate correct queries
- **Hub-page-only connector**: `NeonConnector` lives on the hub page, not per-app like `SupabaseConnector`
- **Only creates projects**: No way to link existing Neon projects (only `neon:create-project` exists)

This violates the **Backend-Flexible** design principle — users who want serverless Postgres without a full BaaS (Supabase) have no viable path in Dyad.

## Scope

### In Scope (MVP / v1)

- Decouple Neon from Portal template — works with React and Next.js templates
- Per-app Neon project selector on the app-details page (matching Supabase's pattern)
- List existing Neon projects + create new projects
- Full branch selector with color-coded badges (production/development/preview)
- Agent tools: `get_neon_project_info`, `get_neon_table_schema`, `execute_neon_sql`
- Neon context in agent prompt with schema awareness
- `add_integration` tool support for `provider: "neon"`
- System prompt covering Drizzle ORM setup, API routes, connection patterns
- Auth guidance: recommend NextAuth.js / Clerk / Lucia (no homegrown JWT+bcrypt)
- `<dyad-execute-sql>` tag support routed to Neon when Neon is the active provider
- Neon and Supabase mutually exclusive per app
- Full stack example code in system prompt

### Out of Scope (Follow-up / v2)

- Multi-account Neon support (currently single-account; multi-org like Supabase can come later)
- Neon-specific auth integration in Dyad (e.g., a Clerk OAuth flow in the Dyad UI)
- Migration file writing with `enableNeonWriteSqlMigration` flag
- Agent-driven branch creation/deletion
- Connection URI caching (fetch on-demand for v1)
- Server-side backend scaffolding for React/Vite template (agent warns user needs a backend)

## User Stories

1. **As a user creating a new Next.js app**, I want to connect my Neon account and have the AI agent set up my database schema, so that I get a working database-backed app without manual SQL or connection configuration.

2. **As a user with an existing Neon project**, I want to link it to my Dyad app by selecting it from a dropdown, so that I don't have to create a new project or copy connection strings.

3. **As a user building a CRUD app**, I want to tell the agent "add a tasks table and build a task list page" and have it execute the SQL on Neon and generate the React components, so that I get an end-to-end working feature in one step.

4. **As a user who chose Neon over Supabase**, I want the AI to recommend NextAuth.js for authentication, so that I get a secure, production-ready auth solution that works with my Neon database.

5. **As a user working on a feature branch**, I want to select my Neon development branch and have the agent execute SQL against it, so that I can iterate safely without affecting production data.

6. **As a user with a React (Vite) app**, I want to connect Neon and have the agent create my schema and generate Drizzle models, so that I can use them when I add a backend layer.

## UX Design

### User Flow

#### First-Time Connection Flow

1. User creates or opens an app (React or Next.js template)
2. In the app-details page, user sees **"Connect Neon"** card in the integrations section (same position as SupabaseConnector)
3. User clicks "Connect Neon" → OAuth popup opens in browser
4. User authorizes Dyad → popup closes, tokens stored via deep link
5. Integration card updates to show **project selector dropdown** (grouped by organization)
   - Existing projects listed via `neon:list-projects`
   - "Create New Project" option at the top
6. User selects or creates a project → project linked to app
7. **Branch selector** appears with color-coded badges:
   - 🟢 Production (green)
   - 🔵 Development (blue, default selected)
   - 🟡 Preview (yellow)
8. AI agent now has full Neon context — can execute SQL, read schemas, generate code

#### AI Agent Interaction Flow

1. User asks "I need a database for my todo app"
2. If Neon is NOT connected: agent renders `<dyad-add-integration provider="neon">` prompt
3. If Neon IS connected: agent uses `get_neon_project_info` to check existing tables
4. Agent generates Drizzle schema, executes SQL via `<dyad-execute-sql>` tag
5. Agent generates client code appropriate to template:
   - **Next.js**: DB client + API routes + React components
   - **React/Vite**: DB client + Drizzle schema + note about needing a server for production
6. User sees SQL execution results in chat, previews updated app

### Key States

| State                           | Description                                                            | Visual                         |
| ------------------------------- | ---------------------------------------------------------------------- | ------------------------------ |
| **Not Connected**               | "Connect to Neon" card with Neon logo and brief description            | Card with connect button       |
| **Connected, No Project**       | Project selector dropdown visible, loading projects                    | Select component with skeleton |
| **Connected, Project Selected** | Project name in Badge, branch selector visible, "Open in Console" link | Full integration card          |
| **Loading**                     | Skeleton placeholders for project/branch lists                         | Skeleton components            |
| **Error**                       | Red text with retry button and actionable message                      | Error state with CTA           |

### Interaction Details

- **Branch selection**: Changing the branch updates the agent's SQL target (connection string). Toast notification confirms: "Switched to development branch"
- **Project disconnect**: Destructive button with confirmation. Clears `neonProjectId` from app record
- **External link**: "Open in Neon Console" button opens the project in browser
- **Auth context**: When user asks for auth, agent recommends NextAuth.js with Drizzle adapter — no homegrown auth

### Accessibility

- All interactive elements keyboard-focusable (connect button, Select dropdowns, branch selector)
- Branch type badges have `aria-label` (e.g., "production branch", not just color)
- Error messages associated with controls via `aria-describedby`
- Status changes announced via `aria-live` region
- Spinner animations respect `prefers-reduced-motion`

## Technical Design

### Architecture

Three-layer architecture for generated apps:

```
┌─────────────────────────────────────────────┐
│ Layer 3: Auth (NextAuth.js / Clerk / Lucia)  │
├─────────────────────────────────────────────┤
│ Layer 2: Backend                             │
│  Next.js: API Routes / Server Actions        │
│  React:   (user adds own backend)            │
├─────────────────────────────────────────────┤
│ Layer 1: Database                            │
│  @neondatabase/serverless + Drizzle ORM      │
│  Connection via DATABASE_URL env var         │
└─────────────────────────────────────────────┘
```

Within Dyad itself (management plane):

```
┌─────────────────────────────────────────┐
│ Agent Tools (get_neon_*, execute_sql)    │
├─────────────────────────────────────────┤
│ Neon Context Module (neon_context.ts)    │
├─────────────────────────────────────────┤
│ Neon Management Client (existing)        │
│ @neondatabase/serverless (SQL execution) │
│ @neondatabase/api-client (project CRUD)  │
├─────────────────────────────────────────┤
│ IPC Handlers (neon_handlers.ts)          │
├─────────────────────────────────────────┤
│ Settings Storage (encrypted credentials) │
└─────────────────────────────────────────┘
```

### Components Affected

| Component              | File(s)                                                                | Change Type                                                                                                             |
| ---------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Agent Context          | `src/pro/main/ipc/handlers/local_agent/tools/types.ts`                 | Modify — add `neonProjectId`, `neonDevelopmentBranchId`, `neonActiveBranchId`                                           |
| Agent Handler          | `src/pro/main/ipc/handlers/local_agent/local_agent_handler.ts`         | Modify — populate Neon context from `chat.app.*`                                                                        |
| Tool Definitions       | `src/pro/main/ipc/handlers/local_agent/tools/tool_definitions.ts`      | Modify — register 3 new Neon tools                                                                                      |
| Add Integration        | `src/pro/main/ipc/handlers/local_agent/tools/add_integration.ts`       | Modify — add `"neon"` to supported providers                                                                            |
| Neon Context           | `src/neon_admin/neon_context.ts`                                       | **New** — `getNeonClientCode()`, `getNeonContext()`, `getNeonProjectInfo()`, `getNeonTableSchema()`, `executeNeonSql()` |
| Neon Project Info Tool | `src/pro/main/ipc/handlers/local_agent/tools/get_neon_project_info.ts` | **New** — mirrors `get_supabase_project_info.ts`                                                                        |
| Neon Table Schema Tool | `src/pro/main/ipc/handlers/local_agent/tools/get_neon_table_schema.ts` | **New** — mirrors `get_supabase_table_schema.ts`                                                                        |
| Execute Neon SQL Tool  | `src/pro/main/ipc/handlers/local_agent/tools/execute_neon_sql.ts`      | **New** — mirrors `execute_sql.ts` with `@neondatabase/serverless`                                                      |
| Neon System Prompt     | `src/prompts/neon_prompt.ts`                                           | **New** — DB setup, API routes, auth guidance, security rules                                                           |
| Chat Stream Handlers   | `src/ipc/handlers/chat_stream_handlers.ts`                             | Modify — inject Neon prompt when `neonProjectId` present                                                                |
| Response Processor     | `src/ipc/processors/response_processor.ts`                             | Modify — route `<dyad-execute-sql>` to Neon executor when Neon is active                                                |
| Neon IPC Handlers      | `src/ipc/handlers/neon_handlers.ts`                                    | Modify — add `neon:list-projects`, `neon:set-app-project`, `neon:get-connection-uri`, `neon:execute-sql`                |
| Neon IPC Types         | `src/ipc/types/neon.ts`                                                | Modify — add new contract schemas                                                                                       |
| App Details Page       | `src/pages/app-details.tsx`                                            | Modify — add NeonProjectSelector component                                                                              |
| Neon Connector         | `src/components/NeonConnector.tsx`                                     | Refactor — use Card components, add per-app project/branch selection                                                    |
| Neon Integration       | `src/components/NeonIntegration.tsx`                                   | Enhance — show project info like SupabaseIntegration                                                                    |
| DB Schema              | `src/db/schema.ts`                                                     | Modify — add `neonActiveBranchId` column                                                                                |

### Data Model Changes

Existing columns (already in `apps` table — no migration needed):

- `neonProjectId: string | null`
- `neonDevelopmentBranchId: string | null`
- `neonPreviewBranchId: string | null`

New column needed:

- `neonActiveBranchId: string | null` — tracks which branch the agent's SQL execution targets (defaults to development branch)

Agent context additions:

```typescript
interface AgentContext {
  // ... existing fields
  neonProjectId: string | null;
  neonDevelopmentBranchId: string | null;
  neonActiveBranchId: string | null;
}
```

### API Changes

New IPC contracts in `src/ipc/types/neon.ts`:

| Contract                  | Input                             | Output                        |
| ------------------------- | --------------------------------- | ----------------------------- |
| `neon:list-projects`      | `{}`                              | `{ projects: NeonProject[] }` |
| `neon:set-app-project`    | `{ appId, projectId, branchIds }` | `{ success: boolean }`        |
| `neon:unset-app-project`  | `{ appId }`                       | `{ success: boolean }`        |
| `neon:execute-sql`        | `{ appId, query }`                | `{ result: string }`          |
| `neon:get-connection-uri` | `{ appId }`                       | `{ connectionUri: string }`   |
| `neon:get-table-schema`   | `{ appId, tableName? }`           | `{ schema: string }`          |
| `neon:set-active-branch`  | `{ appId, branchId }`             | `{ success: boolean }`        |

### Security Considerations

**CRITICAL: Connection string is a full-access credential**

Unlike Supabase (which has safe-for-client anon keys + RLS), Neon's connection string gives full read/write database access. The system prompt MUST:

1. **NEVER** place `DATABASE_URL` in client-side code
2. **NEVER** import `@neondatabase/serverless` in React components or browser code
3. **ONLY** use the connection string in:
   - Next.js API routes (`app/api/`)
   - Next.js Server Actions
   - Next.js Server Components
   - Environment variables (`.env.local`, not `.env`)
4. For React/Vite apps: generate Drizzle schema files only; API calls go through a separate backend

This must be the FIRST rule in `neon_prompt.ts`, with the same prominence as RLS rules in `supabase_prompt.ts`.

## Example Code: Full Stack Integration

### Layer 1: Database Client (`src/db/index.ts`)

```typescript
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
import * as schema from "./schema";

const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
```

### Layer 1: Schema Definition (`src/db/schema.ts`)

```typescript
import { pgTable, uuid, text, timestamp, boolean } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: uuid("id").defaultRandom().primaryKey(),
  email: text("email").notNull().unique(),
  name: text("name"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
});

export const todos = pgTable("todos", {
  id: uuid("id").defaultRandom().primaryKey(),
  title: text("title").notNull(),
  completed: boolean("completed").default(false).notNull(),
  userId: uuid("user_id")
    .references(() => users.id)
    .notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
});
```

### Layer 2: Next.js API Route (`app/api/todos/route.ts`)

```typescript
import { db } from "@/db";
import { todos } from "@/db/schema";
import { eq } from "drizzle-orm";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";

export async function GET() {
  const session = await getServerSession(authOptions);
  if (!session?.user?.id) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  const userTodos = await db
    .select()
    .from(todos)
    .where(eq(todos.userId, session.user.id));

  return Response.json(userTodos);
}

export async function POST(request: Request) {
  const session = await getServerSession(authOptions);
  if (!session?.user?.id) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  const { title } = await request.json();
  const [newTodo] = await db
    .insert(todos)
    .values({ title, userId: session.user.id })
    .returning();

  return Response.json(newTodo, { status: 201 });
}
```

### Layer 3: Auth Configuration (`src/lib/auth.ts`)

```typescript
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "@/db";

export const authOptions = {
  adapter: DrizzleAdapter(db),
  providers: [
    // Add providers as needed: Google, GitHub, Email, etc.
    // Example with Google:
    // GoogleProvider({
    //   clientId: process.env.GOOGLE_CLIENT_ID!,
    //   clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    // }),
  ],
  session: {
    strategy: "jwt" as const,
  },
};

export default NextAuth(authOptions);
```

### Frontend: React Component (`src/components/TodoList.tsx`)

```typescript
"use client";

import { useEffect, useState } from "react";

interface Todo {
  id: string;
  title: string;
  completed: boolean;
}

export function TodoList() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [newTitle, setNewTitle] = useState("");

  useEffect(() => {
    fetch("/api/todos")
      .then((r) => r.json())
      .then(setTodos);
  }, []);

  async function addTodo(e: React.FormEvent) {
    e.preventDefault();
    const res = await fetch("/api/todos", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ title: newTitle }),
    });
    const todo = await res.json();
    setTodos([...todos, todo]);
    setNewTitle("");
  }

  return (
    <div>
      <form onSubmit={addTodo}>
        <input
          value={newTitle}
          onChange={(e) => setNewTitle(e.target.value)}
          placeholder="Add a todo..."
        />
        <button type="submit">Add</button>
      </form>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            {todo.title} {todo.completed ? "(done)" : ""}
          </li>
        ))}
      </ul>
    </div>
  );
}
```

### Environment Variables (`.env.local`)

```bash
# Neon Database (injected by Dyad)
DATABASE_URL=postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/dbname?sslmode=require

# NextAuth
NEXTAUTH_SECRET=your-secret-here
NEXTAUTH_URL=http://localhost:3000
```

### Neon Client Code Generated by Agent (for Dyad context)

```typescript
// This is what getNeonClientCode() generates for the agent's context:
// "To connect to the Neon database, use this pattern:
//
// import { neon } from '@neondatabase/serverless';
// import { drizzle } from 'drizzle-orm/neon-http';
//
// const sql = neon(process.env.DATABASE_URL!);
// export const db = drizzle(sql);
//
// IMPORTANT: Only use this in server-side code (API routes, server actions, server components).
// NEVER import @neondatabase/serverless in client-side React components."
```

## Implementation Plan

### Phase 1: Agent Plumbing + UI Foundation (Small effort)

- [ ] Add `neonProjectId`, `neonDevelopmentBranchId`, `neonActiveBranchId` to `AgentContext` in `types.ts`
- [ ] Populate Neon context fields in `local_agent_handler.ts` from `chat.app.*`
- [ ] Add `"neon"` to `SUPPORTED_PROVIDERS` in `add_integration.ts`
- [ ] Add `neonActiveBranchId` column to `apps` table in `schema.ts`
- [ ] Move/refactor `NeonConnector` to app-details page using Card components (match SupabaseConnector pattern)
- [ ] Add `neon:list-projects` IPC handler (wire up API client's `listProjects`)
- [ ] Add `neon:set-app-project` and `neon:unset-app-project` IPC handlers
- [ ] Build NeonProjectSelector component with project dropdown + "Create New" option

### Phase 2: Neon Context + SQL Execution (Medium effort)

- [ ] Create `src/neon_admin/neon_context.ts` with:
  - `executeNeonSql()` — uses `@neondatabase/serverless` (extract from `neon_timestamp_utils.ts`)
  - `getNeonProjectInfo()` — project ID, branches, table names via `information_schema`
  - `getNeonTableSchema()` — columns, constraints, indexes via `information_schema`
  - `getNeonClientCode()` — generates Drizzle + Neon client boilerplate
  - `getNeonContext()` — full context for agent prompt
- [ ] Create `get_neon_project_info.ts` agent tool (mirrors `get_supabase_project_info.ts`)
- [ ] Create `get_neon_table_schema.ts` agent tool (mirrors `get_supabase_table_schema.ts`)
- [ ] Create `execute_neon_sql.ts` agent tool (mirrors `execute_sql.ts`, uses serverless driver)
- [ ] Register all 3 tools in `tool_definitions.ts`
- [ ] Add `neon:execute-sql`, `neon:get-connection-uri`, `neon:get-table-schema` IPC contracts
- [ ] Update `response_processor.ts` to route `<dyad-execute-sql>` to Neon executor when `neonProjectId` is set
- [ ] Add `neon:set-active-branch` IPC handler for branch switching

### Phase 3: System Prompt + Branch UI (Medium effort)

- [ ] Write `src/prompts/neon_prompt.ts`:
  - Connection security rules (NEVER client-side)
  - Drizzle ORM setup pattern
  - Next.js API route patterns
  - React/Vite guidance (schema only, warn about backend)
  - Auth recommendation (NextAuth.js with Drizzle adapter)
  - Migration patterns
  - Empty database first-run guidance
- [ ] Write `NEON_NOT_AVAILABLE_SYSTEM_PROMPT` (parallel to Supabase's)
- [ ] Integrate Neon prompt into `chat_stream_handlers.ts` (conditional on `neonProjectId`)
- [ ] Build branch selector UI in integration card with color-coded badges
- [ ] Wire branch selection to `neonActiveBranchId` on app record
- [ ] Ensure branch change updates connection URI for SQL execution
- [ ] Add Neon-specific error messages in response processor (auth failure vs query failure)

### Phase 4: Polish + Testing (Small-Medium effort)

- [ ] Enhance `NeonIntegration.tsx` settings page (show project info, usage)
- [ ] Handle Portal template migration (detect existing `neonProjectId`, show connected state)
- [ ] Unit tests for `neon_context.ts` functions with mocked `@neondatabase/serverless`
- [ ] Agent tool tests (enable/disable based on context)
- [ ] E2E tests: connect flow, project selection, SQL execution, schema introspection
- [ ] System prompt tests: verify Neon instructions injected correctly
- [ ] Integration test: full flow from connect → schema → SQL → code generation

## Testing Strategy

- [ ] **Unit**: `neon_context.ts` functions with mocked `@neondatabase/serverless` and `@neondatabase/api-client`
- [ ] **Agent tools**: Mock `AgentContext` with `neonProjectId` set/unset, verify tools enable/disable correctly
- [ ] **E2E (connect flow)**: Extend existing `neon:fake-connect` fixture for React/Next.js templates
- [ ] **E2E (SQL execution)**: Agent generates schema, executes `<dyad-execute-sql>`, verifies result
- [ ] **E2E (schema introspection)**: After table creation, verify `get_neon_table_schema` returns correct columns
- [ ] **System prompt**: Verify Neon instructions injected when `neonProjectId` present, NOT when Supabase connected
- [ ] **Security**: Verify system prompt prevents client-side `DATABASE_URL` usage in generated code
- [ ] **Branch switching**: Verify SQL execution targets correct branch after branch change

## Risks & Mitigations

| Risk                                           | Likelihood | Impact | Mitigation                                                                                                      |
| ---------------------------------------------- | ---------- | ------ | --------------------------------------------------------------------------------------------------------------- |
| Connection string exposed in client code       | Medium     | High   | System prompt rule #1: NEVER client-side. Code review patterns in prompt.                                       |
| AI generates insecure homegrown auth           | Medium     | High   | Prompt explicitly forbids JWT+bcrypt, recommends NextAuth.js only.                                              |
| React/Vite users confused about backend gap    | Medium     | Medium | Agent generates clear warning: "For production, add a server (Express, Fastify) to handle DB queries securely." |
| Neon free tier quota exhaustion                | Low        | Medium | Document in system prompt. Consider surfacing usage in `get_neon_project_info`.                                 |
| Branch switching causes data confusion         | Low        | Medium | Toast notification on branch change. Agent prompt mentions active branch.                                       |
| `@neondatabase/serverless` works in browser    | Medium     | High   | System prompt explicitly forbids browser imports. Response processor could warn.                                |
| Portal template users lose existing connection | Low        | High   | Detect existing `neonProjectId` in new flow, show connected state.                                              |
| On-demand connection URI fetch latency         | Medium     | Low    | Acceptable for v1 (~200-500ms). Cache in v2 if needed.                                                          |

## Open Questions

1. **Neon free tier limits**: Should `get_neon_project_info` surface current usage (storage, compute hours)? This would help users and the agent avoid heavy operations that burn through quotas.

2. **NeonConfigure preview panel**: The existing `NeonConfigure.tsx` shows branch visualization in the preview panel for Portal apps. Should this be removed/replaced by the new branch selector in the integration card, or kept as a read-only status display?

3. **Mutual exclusivity enforcement**: When a user has Supabase connected and tries to connect Neon (or vice versa), should we show a warning dialog, or silently hide the other provider's connect button?

4. **React/Vite backend story for v2**: When we eventually support a server layer for React/Vite + Neon, should it be (a) an Express server scaffold, (b) a Hono/Bun server, or (c) something else? This affects the template system design.

## Decision Log

| Decision                                 | Reasoning                                                                                                                                                    | Alternatives Considered                                                              |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| Both React + Next.js in v1               | SQL execution and schema tools run in Electron (template-agnostic). Agent generates different code per template. Avoids recreating template lock-in problem. | Next.js only (simpler but violates Backend-Flexible principle)                       |
| Recommend external auth providers only   | Neon has no auth service. Homegrown JWT+bcrypt is a security risk. NextAuth.js has a Drizzle adapter.                                                        | JWT+bcrypt (self-contained but risky); Clerk (requires additional OAuth integration) |
| Full branch selector in v1               | Branching is Neon's key differentiator over Supabase. Color-coded badges provide clear visual hierarchy.                                                     | Default to dev only (simpler); Read-only display (compromise)                        |
| Mutually exclusive providers per app     | Agent prompt can't cleanly handle both Supabase and Neon contexts. Avoids ambiguity in SQL execution target.                                                 | Allow both (complex, no clear user value)                                            |
| SQL execution in Dyad (management plane) | Matches Supabase pattern. Agent needs to create tables and seed data during build. Credentials already stored.                                               | Only in generated app (limits agent capabilities)                                    |
| Fetch connection URI on-demand           | Consistent with existing `neon_timestamp_utils.ts` pattern. Avoids credential rotation complexity.                                                           | Cache (faster but more complex)                                                      |
| `@neondatabase/serverless` for SQL       | Already a dependency. HTTP-based, works in Electron. Lower latency than Management API for queries.                                                          | Management API SQL endpoint (higher latency, fewer features)                         |

---

_Generated by dyad:swarm-to-plan_
