← Back to dashboard

Neon Integration Guide

What is Neon?

The real model

Postgres on a content-addressed page store; per-branch compute; HTTP and pooler endpoints.

The branching model is the structural unlock — each Vercel preview / feature branch can carry its own database snapshot at near-zero cost, and you can `RESET` a dev branch to main with one command. The HTTP driver (`@neondatabase/serverless`) lets serverless functions skip the connection-pooling step entirely — important on edge runtimes where TCP is expensive. PgBouncer-flavour pooler exists for Node servers that prefer persistent connections. Point-in-time recovery is built in.

Five Neon primitives

Postgres you know, with serverless economics and branching baked in.

Text
███╗   ██╗███████╗ ██████╗ ███╗   ██╗
████╗  ██║██╔════╝██╔═══██╗████╗  ██║
██╔██╗ ██║█████╗  ██║   ██║██╔██╗ ██║
██║╚██╗██║██╔══╝  ██║   ██║██║╚██╗██║
██║ ╚████║███████╗╚██████╔╝██║ ╚████║
╚═╝  ╚═══╝╚══════╝ ╚═════╝ ╚═╝  ╚═══╝

Neon Integration Guide

Focus: Managing serverless Postgres, running migrations, and using branching workflows from Claude Code via the official Neon MCP server.

Overview

Neon is a serverless Postgres platform (acquired by Databricks in 2025; it now also powers Databricks Lakebase) with scale-to-zero, database branching, and instant provisioning. Its official MCP server lets Claude Code create projects, run SQL, branch databases per feature, apply migrations safely with a two-phase commit pattern, inspect a database's health, and query slow query logs — all from natural language inside your session.

Here is the branching workflow at a glance — copy-on-write branches let you experiment freely, then discard or reset with zero risk to main:

Official Documentation


MCP Server Setup

Neon's preferred approach is the hosted remote server — no API keys to manage. The hosted server speaks Streamable HTTP at https://mcp.neon.tech/mcp (the older /sse endpoint is deprecated and returns 410 Gone on or after 2026-10-01).

Bash
# Add via Claude Code CLI (HTTP + OAuth)
claude mcp add --transport http neon https://mcp.neon.tech/mcp

After adding, run /mcp inside Claude Code to authenticate via OAuth. (npx neon@latest init also scaffolds the MCP config for most editors if you prefer a guided setup.)

.mcp.json Configuration (Remote)

JSON
{
  "mcpServers": {
    "neon": {
      "type": "http",
      "url": "https://mcp.neon.tech/mcp"
    }
  }
}

The MCP server is for development/testing only — never point it at a production database. Restrict scope by appending URL parameters to the MCP URL:

ParameterEffect
?readonly=trueSELECTs + schema inspection only; branch/migration/auth writes disabled (OAuth can also grant read-only scope at authorize time)
?projectId=<id>Scope every operation to one project; cross-project search off
?category=<name>Enable only named tool categories (repeatable), e.g. ?category=querying&category=schema

Verify which tools a config exposes without authenticating: curl "https://mcp.neon.tech/api/list-tools?readonly=true&category=querying".

Local stdio fallback (deprecated npm package → use mcp-remote)

The old local package @neondatabase/mcp-server-neon is deprecated (npm marks it so). If your client only supports stdio servers, bridge to the hosted Streamable-HTTP server with mcp-remote instead:

JSON
{
  "mcpServers": {
    "neon": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote@latest", "https://mcp.neon.tech/mcp",
        "--header", "Authorization:${NEON_AUTH_HEADER}"
      ],
      "env": { "NEON_AUTH_HEADER": "Bearer ${NEON_API_KEY}" }
    }
  }
}

Drop the --header/env lines to use interactive OAuth instead of an API key.

Available MCP Tools

Tools are grouped into categories (projects, branches, schema, querying, neon_auth, data_api, observability, docs) — filter with ?category=. The commonly used ones:

ToolDescription
list_projects / list_organizationsList Neon projects / orgs
create_project / delete_projectCreate or delete a Neon project
describe_projectProject details incl. branch list + endpoint defaults
run_sqlExecute a SQL query on a branch
run_sql_transactionRun multiple SQL statements as a transaction
get_database_tablesList all tables in a database
describe_table_schemaGet column definitions and constraints
create_branchCreate a database branch (copy-on-write)
describe_branchDeep view of a branch (schema objects, etc.)
delete_branchDelete a branch
reset_from_parentReset a branch to match parent state
compare_database_schemaDiff a branch's schema against its parent
list_branch_computesPer-compute suspend timeout / CU / state (cost diagnosis)
get_connection_stringGet psql/prisma/drizzle connection string (pooled optional)
prepare_database_migrationStage a migration on a test branch
complete_database_migrationApply staged migration to main branch
inspect_databaseRun the 14 read-only health checks from neon inspect db
list_slow_queriesGet slow query log for analysis
prepare_query_tuning / complete_query_tuningTest + apply index/query fixes on a branch
explain_sql_statementGet EXPLAIN (ANALYZE) output
list_docs_resources / get_doc_resourceLook up Neon docs from the assistant (no OAuth)

There is no standalone list_branches tool — branches come back from describe_project.


CLI Integration

Installation

Bash
npm i -g neon      # CLI is invoked as `neon`; `neonctl` is a published alias (same binary)

The examples below use neonctl; every command works identically as neon. In agentic/CI contexts prefer npx neon ... (or npx neonctl ...) rather than assuming a global install.

Authentication

Bash
neonctl auth       # OAuth browser login
# Or use API key (CI/scripts):
export NEON_API_KEY=...

Key Commands

Bash
# Projects
neonctl projects list
neonctl projects create --name my-project
neonctl projects delete PROJECT_ID

# Branches
neonctl branches list --project-id PROJECT_ID
neonctl branches create --project-id PROJECT_ID --name feature/auth
neonctl branches delete BRANCH_ID --project-id PROJECT_ID
neonctl branches reset BRANCH_ID --project-id PROJECT_ID --parent

# Connection string
neonctl connection-string --project-id PROJECT_ID --branch main
neonctl connection-string --project-id PROJECT_ID --branch feature/auth

# SQL execution
neonctl sql --project-id PROJECT_ID --query "SELECT version();"
neonctl sql --project-id PROJECT_ID --file schema.sql

Connecting to Postgres

Get Connection String

Bash
# Via MCP tool: get_connection_string
# Or via CLI:
neonctl connection-string --project-id PROJECT_ID --branch main

Example connection string:

Text
postgresql://user:password@ep-cool-name-123456.us-east-2.aws.neon.tech/neondb?sslmode=require

Prisma Integration

Bash
npm install prisma @prisma/client

prisma/schema.prisma:

PRISMA
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
  // Use a separate direct URL for migrations (no connection pooling)
  directUrl = env("DIRECT_URL")
}

generator client {
  provider = "prisma-client-js"
}
Bash
npx prisma migrate dev --name add-users
npx prisma generate

Drizzle ORM Integration

Bash
npm install drizzle-orm pg drizzle-kit
TypeScript
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle(pool);

// Query
const users = await db.select().from(usersTable).limit(10);

pgvector on Neon

Neon ships the pgvector extension, so you can store embeddings and run nearest-neighbour search inside the same Postgres branch as the rest of your data — no separate vector database to provision. This pairs naturally with the AI routing policy: generate embeddings with Anthropic/OpenAI/HuggingFace, then query them here.

Here is the end-to-end flow from raw text to a ranked similarity result:

Enable the extension, create a table + HNSW index

Run this on a branch first (use create_branch) so you can validate before touching main:

SQL
-- 1. Enable pgvector
CREATE EXTENSION IF NOT EXISTS vector;

-- 2. Table with a vector column.
--    Dimension must match your embedding model:
--    OpenAI text-embedding-3-small = 1536, Cohere embed-v3 = 1024, etc.
CREATE TABLE items (
  id        BIGSERIAL PRIMARY KEY,
  content   TEXT,
  embedding VECTOR(1536)
);

-- 3. HNSW index. Pick the operator class that matches your distance metric:
--    vector_cosine_ops (cosine), vector_l2_ops (L2), vector_ip_ops (inner product).
--    Cosine is the usual choice for normalized OpenAI/Cohere embeddings.
CREATE INDEX ON items
  USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

Nearest-neighbour query

Use the distance operator that matches the index operator class: <=> (cosine), <-> (L2), <#> (negative inner product). The index is only used when the query has both ORDER BY on the distance operator and a LIMIT:

SQL
SELECT id, content
FROM items
ORDER BY embedding <=> '[0.012, -0.034, 0.567, ...]'   -- your query embedding
LIMIT 5;

From TypeScript (Drizzle + a query embedding)

TypeScript
import { drizzle } from "drizzle-orm/node-postgres";
import { sql } from "drizzle-orm";
import { Pool } from "pg";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle(pool);

// queryEmbedding is a number[] from your embeddings provider (e.g. 1536-dim).
async function searchSimilar(queryEmbedding: number[], k = 5) {
  // pgvector accepts the array as a bracketed string literal: '[0.1,0.2,...]'
  const literal = `[${queryEmbedding.join(",")}]`;
  const rows = await db.execute(sql`
    SELECT id, content
    FROM items
    ORDER BY embedding <=> ${literal}
    LIMIT ${k}
  `);
  return rows.rows;
}

Gotcha — HNSW dimension ceiling. An HNSW index on the standard vector type supports at most 2,000 dimensions. Models that exceed this (e.g. OpenAI text-embedding-3-large at 3072 dims) cannot be indexed as vector — use the halfvec type instead (16-bit floats, up to 4,000 dimensions indexed): CREATE INDEX ON items USING hnsw ((embedding::halfvec(3072)) halfvec_cosine_ops);. Without an HNSW index, large-dimension columns still work for storage and exact search, just without ANN acceleration.

See the pgvector on Neon guide for index tuning (ef_search), IVFFlat as an alternative index, and the full operator/type matrix.


Environment Variables

Bash
# Connection strings (from Neon dashboard or neonctl connection-string)
DATABASE_URL=postgresql://user:pass@ep-....neon.tech/neondb?sslmode=require
DIRECT_URL=postgresql://user:pass@ep-....neon.tech/neondb?sslmode=require

# API key (for CLI and npm MCP package)
NEON_API_KEY=...

# Project and branch (optional, for scripting)
NEON_PROJECT_ID=...
NEON_BRANCH_NAME=main

Automation Workflows

Safe Two-Phase Migration Pattern

Neon's MCP server implements a safe migration pattern that tests first on a branch:

You can apply schema changes with confidence — the migration is rehearsed on a throwaway branch before it ever touches main:

  1. Claude Code calls prepare_database_migration → creates a test branch, runs migration there
  2. Review the output — Claude Code checks for errors and data issues
  3. Call complete_database_migration → applies the migration to the main branch

This prevents irreversible migrations from running against production directly.

In practice, ask Claude Code:

"Apply this migration to the Neon database: ALTER TABLE users ADD COLUMN avatar_url TEXT"

Claude Code will automatically use the two-phase pattern via the MCP tools.

Claude Code Slash Command: Branch per Feature

.claude/commands/neon-branch.md:

Markdown
Create a Neon database branch for feature $ARGUMENTS.

1. Use the Neon MCP tool `create_branch` to create a branch named "feature/$ARGUMENTS"
2. Use `get_connection_string` to get the connection string for this branch
3. Update the `.env.local` file's DATABASE_URL to point to the new branch
4. Report the branch ID, connection string, and confirm `.env.local` was updated

Usage: /project:neon-branch user-auth

GitHub Actions: Branch per PR

YAML
# .github/workflows/neon-preview.yml
name: Neon Preview Branch
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  create-branch:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Create Neon branch
        uses: neondatabase/create-branch-action@v6
        id: create-branch
        with:
          project_id: ${{ secrets.NEON_PROJECT_ID }}
          api_key: ${{ secrets.NEON_API_KEY }}
          branch_name: preview/pr-${{ github.event.pull_request.number }}
      - name: Comment branch URL
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `DB branch id: \`${{ steps.create-branch.outputs.branch_id }}\`\nConnection: \`${{ steps.create-branch.outputs.db_url_pooled }}\``
            })

Common Use Cases

Use CaseApproach
Feature branch isolationMCP create_branch per PR
Safe schema migrationsTwo-phase: prepare → complete
Query analysisMCP explain_sql_statement + list_slow_queries
Database health checkMCP inspect_database (14 read-only checks)
Guided query tuningMCP prepare_query_tuning → complete_query_tuning
Instant dev environmentcreate_project in under 2 seconds
Prisma migrationsprisma migrate dev against branch URL
Reset dev DBMCP reset_from_parent

Troubleshooting

IssueFix
Connection timeoutAdd ?sslmode=require&connect_timeout=10 to URL
Branch creation failsCheck project branch limit (free tier: 10 branches)
Slow queriesUse MCP list_slow_queries + explain_sql_statement
Migration conflictsReset branch with MCP reset_from_parent
Prisma P1001Endpoint may be sleeping — first query wakes it (scale-to-zero)
API key invalidRegenerate at console.neon.tech → Account Settings