Neon MCP Server

repository·main·Indexed 18 days ago

https://github.com/neondatabase/mcp-server-neon

An open-source Model Context Protocol (MCP) server that enables LLMs to interact with Neon Postgres databases using natural language. It provides tools for database management, migrations, SQL querying, branch management, and query optimization. The server supports both remote hosted deployment via OAuth or API keys and local development, offering specialized capabilities like safe migrations and observability tools.

Tokens
9.2K
Snippets
29
Records
45
Agent score
64%

What's inside @neondatabase/mcp-server-neon

  1. Understand the Remote Server Architecture

    main

    The Neon MCP Server can be used as a remote hosted service running at mcp.neon.tech. It is implemented as a Next.js App Router application on Vercel.

    Key architectural components include:

    • Transport Endpoints: Located at /mcp (Streamable HTTP) and /sse (Server-Sent Events).
    • OAuth Flow: Endpoints at /api/authorize, /api/callback, /api/token, and /api/revoke manage user authentication.
    • Discovery: OAuth metadata is available via .well-known/ endpoints.
    • Core Logic: The MCP server, tools, and handlers reside in the mcp/ directory, while Next.js helpers are in lib/.
  2. Implement stateless database migrations

    main

    Due to the stateless nature of serverless functions, the database migration tools (prepare_database_migration and complete_database_migration) no longer store state in memory. Instead, they follow a pattern where all context is passed back and forth through the tool calls.

    Workflow:

    1. prepare_database_migration returns all necessary context in its response.
    2. The LLM/Client stores this context in its conversation memory.
    3. complete_database_migration accepts all that context back as parameters to finalize the operation.

    Breaking API Change: Previously, complete_database_migration only required a migrationId. It now requires the full context to function in a serverless environment.

    // NEW: All context must be passed back to work in serverless
    complete_database_migration({
      migrationId: 'uuid',
      migrationSql: 'ALTER TABLE ...',
      databaseName: 'neondb',
      projectId: 'proj-xxx',
      temporaryBranchId: 'br-xxx',
      parentBranchId: 'br-main',
      applyChanges: true,
    });
  3. Understand the /callback (OAuth code-grant) SLO

    main

    The /callback Service Level Objective (SLO) tracks the success rate of the initial authorization flow. This flow occurs when an MCP client (like Cursor, Claude, or ChatGPT) sends a user through /api/authorize $\rightarrow$ upstream Hydra $\rightarrow$ back to the Neon /callback endpoint $\rightarrow$ and finally to the client's redirect URI.

    SLO Definition:

    • Goal: The fraction of /callback requests that successfully hand a code back to the downstream client OR cleanly surface an upstream user-driven cancel.
    • Target: 99.5%
    • Window: Rolling 28 days
    • Formula: SLO = 1 - (bad_outcomes / classified_outcomes)
  4. Enable Read-Only Mode for Neon MCP Server

    main

    Read-only mode restricts available tools, disabling write operations like creating projects, branches, or running migrations. It allows tools like list_projects, run_sql (for read-only queries), and describe_table_schema.

    You can enable it in two ways:

    1. OAuth scope selection: Uncheck Full access in the OAuth authorization UI.
    2. readonly query parameter: Add ?readonly=true to the MCP server URL.

    Behavioral Note: In the API key flow, the readonly=true parameter is the only way to enable read-only mode. In the OAuth flow, the parameter overrides the scope selected in the UI.

    {
      "mcpServers": {
        "Neon": {
          "url": "https://mcp.neon.tech/mcp?readonly=true"
        }
      }
    }
  5. Understand the Refresh-token endpoint SLO

    main

    The Refresh-token endpoint Service Level Objective (SLO) measures the Refresh chain integrity. This metric tracks the fraction of /api/token requests with grant_type=refresh_token that do not result in an upstream revocation (the 'cliff') or a server-side failure to provide a definitive answer.

    Target: 99.9% (bad outcomes < 0.1% per attempt) Window: Rolling 28 days

    Outcome Classification

    Outcome bucketCounts in denominator?Counts as bad?Description
    success_freshyesnoUpstream rotated successfully
    success_cache_replayyesnoCross-instance success cache hit
    correct_invalid_grantyesnoClient presented a known dead token (e.g., RT-not-found); system behaved correctly
    cliff_upstreamyesyesUpstream returned token_inactive or invalid_request. The chain is dead; user must re-auth
    transient_lock_timeoutyesyesLock-waiter timed out (returns 503). Should be 0 after PR #234
    transient_persist_failureyesyesUpstream succeeded but KV write failed, causing the client to retry a dead RT
    transient_upstream_5xxnon/aHydra returned an HTTP 5xx. Excluded from SLO
    transient_upstream_networknon/aNetwork-layer failures (e.g., ECONNRESET). Excluded from SLO
    bad_requestnon/aMalformed requests (e.g., missing refresh_token). Excluded from SLO
  6. Configure Analytics for Serverless environments

    main

    In serverless environments, analytics must be configured to flush events immediately because the function may terminate before batched events are sent.

    When initializing the Analytics instance, set flushAt: 1. Additionally, call flushAnalytics() (which uses analytics.closeAndFlush()) within a waitUntil block to ensure data is sent before the function exits.

    const analytics: Analytics | undefined = ANALYTICS_WRITE_KEY
      ? new Analytics({
          writeKey: ANALYTICS_WRITE_KEY,
          host: 'https://track.neon.tech',
          flushAt: 1, // Send immediately (required for serverless)
        })
      : undefined;
    
    export const flushAnalytics = async (): Promise<void> => {
      await analytics?.closeAndFlush();
    };
  7. Configure OAuth callback route and redirect URI

    main

    The OAuth callback route has been moved from /api/callback to /callback (outside the API directory) to comply with allowlisted redirect URI requirements.

    1. Ensure the file is located at landing/app/callback/route.ts.
    2. Update your OAuth client configuration to use the new REDIRECT_URI format: ${SERVER_HOST}/callback.
    3. Important: You must add this new callback URL to the allowlist in your upstream OAuth provider (Neon OAuth).
  8. Compute Refresh-token SLO from Vercel logs

    main

    The system emits structured info-level logs with the prefix [SLO] refresh to track outcomes. To compute the SLO, you must pull these logs using the Vercel CLI and aggregate them by outcome bucket.

    Important Note on Log Limits: Because success buckets are high-volume, they may hit the --limit 5000 cap in Vercel. For long time windows, it is recommended to issue one targeted query per outcome bucket. For the denominator (outcome=success), sample a small time slice (e.g., 5 minutes) to estimate the rate, then project to the full window.

    Extraction Script

    Run this script from the repository root so the Vercel project is correctly detected. It iterates through all outcome buckets and saves them to individual JSONL files in /tmp/.

    # Run from the repo root so the Vercel project is detected.
    for q in "outcome=success" "outcome=correct_invalid_grant" \
             "outcome=cliff_upstream" "outcome=transient_lock_timeout" \
             "outcome=transient_persist_failure" "outcome=transient_upstream_5xx" \
             "outcome=transient_upstream_network" "outcome=bad_request"; do
      fname=$(echo "$q" | tr ':=' '__')
      vercel logs --since 24h --environment production --no-follow --no-branch \
        --limit 5000 --query "[SLO] refresh $q" --json 2>/dev/null \
        > "/tmp/slo-${fname}.jsonl"
    done
  9. Set up Remote Hosted MCP Server with OAuth

    main

    The easiest way to connect to Neon's managed MCP server without managing local installations or API keys. This method uses OAuth for authentication. After running the command, an OAuth window will open in your browser to authorize your client.

    Note: By default, OAuth-based authentication operates on projects under your personal Neon account. To manage organization projects, you must explicitly provide the org_id or project_id in your prompt to the MCP client.

    npx add-mcp https://mcp.neon.tech/mcp
  10. Use Server-Sent Events (SSE) Transport (Deprecated)

    main

    If your LLM client does not yet support the recommended Streamable HTTP transport, you can use the deprecated SSE transport by changing the endpoint to https://mcp.neon.tech/sse.

    npx add-mcp https://mcp.neon.tech/sse --type sse
  11. Set up local development for Neon MCP Server

    main

    To develop the Neon MCP server locally, ensure you have pnpm installed (managed via Corepack).

    Installation:

    corepack enable
    pnpm install

    Running the Dev Server:

    pnpm dev

    Linting and Type Checking:

    pnpm lint
    pnpm typecheck

    Required Environment Variables:

    • SERVER_HOST: Server URL
    • UPSTREAM_OAUTH_HOST: Neon OAuth provider URL
    • CLIENT_ID: OAuth client ID
    • CLIENT_SECRET: OAuth client secret
    • COOKIE_SECRET: Secret for signed cookies
    • KV_URL: Vercel KV (Upstash Redis) URL
    • OAUTH_DATABASE_URL: Postgres URL for token storage
    corepack enable
    pnpm install
    pnpm dev
  12. Configure Vercel Fluid Compute for SSE support

    main

    To support long-running Server-Sent Events (SSE) connections required by the MCP protocol, you must configure Vercel Fluid Compute in your vercel.json. This enables streaming support and extends the function execution timeout.

    Set fluid: true and increase maxDuration (e.g., to 800 seconds) for the API routes handling MCP transports.

    {
      "fluid": true,
      "functions": {
        "app/api/**/*.ts": {
          "maxDuration": 800
        }
      }
    }