Cursor + Cradler

How to add a database to a Cursor app

Cursor is an editor, not a hosted builder — it will not hand you a database the way Lovable or Bolt do. What it does have is native support for the Model Context Protocol. This guide uses that to give a Cursor-built app a real backend, with the agent working from your actual schema.

Short answer

Cursor has no built-in database, so you bring your own. The workflow that plays to Cursor's strengths is MCP-first: create a Cradler project, add @cradler/mcp to .cursor/mcp.json, and install @cradler/sdk. Cursor's agent then reads your real schema through MCP and writes SDK calls that match it — and Cradler grows tables and columns automatically, so there is no SQL or migration.

Why Cursor is a different case

Lovable, v0, and Bolt are hosted environments — they run your app and, increasingly, hand you a database alongside it. Cursor is not that. It is an AI code editor on your own machine, working on your own files. There is no “Cursor database” to toggle on. That sounds like more work, and for raw infrastructure it would be — but Cursor has one capability the hosted builders cannot match for this job.

Cursor speaks the Model Context Protocol natively. You can connect MCP servers in .cursor/mcp.json and the agent reads and writes through them directly. For a database, that is the difference between Cursor guessing at your tables and Cursor knowing them.

The MCP-first approach

There is a well-worn pattern of pointing Cursor at a raw Postgres MCP server so the agent can inspect a schema and run queries. It works — for a database you are already administering. The catch is that it assumes you are administering one: designing tables, writing migrations, granting access.

Cradler's MCP server keeps the same “agent sees the real schema” benefit but removes the administration underneath it. The schema auto-evolves, so there is nothing to migrate. Access is a set of dashboard toggles, not SQL grants. And the same project holds file storage. Cursor still works against ground truth — it just is not ground truth you have to maintain by hand.

Connecting Cradler to a Cursor project, step by step

The MCP server is what makes Cursor accurate; do that step first.

  1. 01

    Create a Cradler project

    Sign up at cradler.ai and create a project. You get an isolated PostgreSQL database, file storage, a gateway URL, a project ID, an anon key, and a service key.

  2. 02

    Add the Cradler MCP server to Cursor

    Add @cradler/mcp to your project's .cursor/mcp.json with your gateway URL, project ID, and key. Cursor's agent can then read your real schema and your data directly, instead of guessing.

  3. 03

    Install @cradler/sdk in your project

    Run your package manager to add @cradler/sdk, and put the gateway URL, project ID, and key in environment variables. The SDK is the typed client your app code will use at runtime.

  4. 04

    Prompt Cursor against the live schema

    Ask Cursor in the Composer to read and write your data. With MCP connected it inspects the actual tables, so the @cradler/sdk code it writes matches your data — and the schema grows as you add fields.

The MCP config

Project-specific tools belong in a project-scoped config. Create .cursor/mcp.json at the root of your repo — Cursor reads it automatically:

{
  "mcpServers": {
    "cradler": {
      "command": "npx",
      "args": ["-y", "@cradler/mcp"],
      "env": {
        "CRADLER_URL": "https://gateway.cradler.ai",
        "CRADLER_PROJECT_ID": "<your-project-id>",
        "CRADLER_KEY": "<your-service-key>"
      }
    }
  }
}

Reload Cursor and the cradlerserver appears in Settings. From then on, ask the agent “what columns does the orders table have?” and it answers from the live schema. Keep this file in .gitignore, or use a placeholder and an environment variable — the service key bypasses table permissions and must not be committed.

The runtime code

MCP is how Cursor understands your data while it codes. @cradler/sdk is what your app runs. Say you are building an orders feature; the data layer Cursor writes looks like this:

import { createClient } from "@cradler/sdk";

const cradler = createClient({
  url: process.env.CRADLER_URL!,
  projectId: process.env.CRADLER_PROJECT_ID!,
  apiKey: process.env.CRADLER_KEY!,
});

type Order = {
  id: string;
  customerEmail: string;
  total: number;
  status: "pending" | "paid" | "shipped";
  createdAt: string;
};

// A typed collection — Cursor knows every field.
const orders = () => cradler.from<Order>("orders");

export async function placeOrder(email: string, total: number) {
  const { rows } = await orders().insert({
    customerEmail: email,
    total,
    status: "pending",
  });
  return rows[0];
}

export async function markPaid(id: string) {
  await orders().update({ status: "paid" }).eq("id", id);
}

// Paid orders over $100, newest first — first page of 20.
export async function highValueOrders(page = 0) {
  const { rows, count } = await orders()
    .select("id", "customerEmail", "total", "createdAt")
    .eq("status", "paid")
    .gt("total", 100)
    .order("createdAt", { desc: true })
    .limit(20)
    .offset(page * 20);
  return { orders: rows, total: count };
}

// Look up one order, or null if it does not exist.
export async function findOrder(id: string) {
  return orders().select().eq("id", id).first();
}

Passing a row type to from<Order>() gives Cursor end-to-end typing — every filter field and every result property is checked. Combined with what MCP tells it about the real schema, Cursor is no longer guessing on either side.

File uploads

An invoice PDF for each order? Storage is part of the same Cradler project:

async function attachInvoice(orderId: string, pdf: Blob) {
  const path = `invoices/${orderId}.pdf`;

  await cradler.storage.upload(path, pdf, {
    contentType: "application/pdf",
  });

  await cradler
    .from("orders")
    .update({ invoicePath: path })
    .eq("id", orderId);

  // A temporary signed URL to hand back to the customer.
  return cradler.storage.getUrl(path);
}

Gotchas worth knowing

Never commit the service key. The MCP config and your .env both hold it. It bypasses every table permission, so it belongs in .gitignore or a secrets manager — not in your repo.

Trust MCP over the model's memory. If Cursor writes a query against a column that does not exist, the fix is usually that the MCP server was not connected for that turn. With it connected, the agent checks the schema instead of recalling it. Confirm cradler is listed and enabled in Settings.

Let inserts shape the schema. You do not write a CREATE TABLE. The first orders().insert(...) creates the table; a later insert with a new field adds the column. Renames and drops are deliberate, confirmed, backed-up dashboard actions — never something an agent does on its own.

Why Cradler suits Cursor

Cursor is at its best when it has real context. Cradler is built to give it: an MCP server for the live schema, a typed @cradler/sdk for the runtime, per-project generated types, and an llms.txt the agent can read. Underneath it is standard PostgreSQL, so nothing about the data is locked to Cradler — but you never have to administer it.

Frequently asked questions

Does Cursor have a built-in database?

No. Cursor is an AI code editor, not a hosted app builder, so it does not provision a database for you the way Lovable or Bolt do. You choose and connect a backend yourself. Cursor's strength is that it speaks the Model Context Protocol, so it can connect directly to a backend's MCP server and work against the real schema.

How do I add a database to an app built in Cursor without SQL?

Create a Cradler project, add @cradler/mcp to your .cursor/mcp.json, install @cradler/sdk, and prompt Cursor to read and write your data. Cradler runs PostgreSQL but you never write SQL: tables and columns are created automatically as your app saves data, and Cursor generates the SDK calls.

What does the Cradler MCP server do for Cursor?

@cradler/mcp is a Model Context Protocol server that connects Cursor's agent straight to a Cradler project. Cursor can inspect the real schema and read or write rows itself. That means the integration code it writes reflects the columns that actually exist, instead of a plausible guess — far fewer rounds of fixing.

Why not just point Cursor at a Postgres MCP server directly?

You can, and for an existing database it is a fine debugging tool. But a raw Postgres MCP server expects you to manage the schema, write migrations, and handle access yourself. Cradler's MCP server sits on top of a backend where the schema auto-evolves, permissions are dashboard toggles, and file storage is included — so the non-technical builder is not pulled back into database administration.

Where do uploaded files go for a Cursor-built app?

Cradler includes file storage in the same project as the database. You upload through client.storage.upload() in @cradler/sdk; bytes go straight to object storage via a presigned URL and are served over a CDN. There is no separate storage service to set up.

Keep reading

Wire a backend into your Cursor project

Create a Cradler project and get a database, file storage, an MCP server, and API keys in seconds. Free to start — no credit card.