MainSDK
Search⌘K
Docs

Adopt an existing app

Bring a Lovable, Bolt, v0 or Cursor app onto MainSDK with mainsdk check and mainsdk adopt, one module and one pull request at a time, no rewrite.

Building for 0.5

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

The quickstart started from an empty folder. Most apps already exist, and you don't rewrite yours to use MainSDK: you check it, see the gaps, lock what works, and close the gaps one pull request at a time. Your coding agent can do the work through the migration skill, or you can run mainsdk adopt yourself.

Phase 1 covers Next.js and Node apps, including those generated by Lovable, Bolt and v0. Other stacks are reported as unsupported, never half-migrated. MainSDK is pre-release; these commands are the planned design and do not run yet. init, check and the framework adapters are planned for 0.2; mainsdk adopt and the migration skill are planned for 0.5.

Start with a check

Run it in the app's root. No account, nothing sent anywhere.

bash
npx mainsdk check

On a typical generated app, the first run finds things like a payment key in client code or a raw database client:

text
$ npx mainsdk check
mainsdk check · my-lovable-app · env dev · Main 27
 secrets    1 finding
  MAIN-SECRET-0002  critical  src/lib/stripe.ts:4
  fix: Move the key to secrets and use secret:STRIPE_SECRET_KEY
 data       1 finding
  MAIN-DATA-0001  high  src/lib/db.ts:12
  fix: Route queries through MainSDK (withPrisma, withDrizzle or db.<model>.for(user))
 config   network
2 gates failed · exit 1

Adopt, step by step

Plain npx mainsdk adopt runs the next step each time and is safe to re-run. The steps below run each one by name. Progress is kept in .mainsdk/adopt.json (git-ignored); main.yaml stays the source of truth.

1. Scan

bash
npx mainsdk adopt scan

A deterministic scan maps sign-in, data access, secrets and outbound calls, and writes a main.yaml describing the app as it is. It changes no code; the same repo always gives the same file.

It keeps what you already run. An app on Clerk and OpenFGA stays on them:

main.yaml (written by adopt scan)
schema: 1
project: prj_01JC8Q4W7ZK3M5N8P2R6T9V0XY
baseline: main27
auth: { driver: clerk }      # byo-identity: users, sessions and MFA stay in Clerk
access: { driver: openfga }  # byo-policy: your model and tuples stay in place
pay: { driver: stripe }
overlays: { order: [market, segment, env] }
exceptions: []

2. Report

bash
npx mainsdk adopt report

A plain-language gap report against Main 27, ordered by risk: secrets first, then access, then data, then the rest. Each item names the rule and the fix. Add --json for tools.

3. Lock key flows

bash
npx mainsdk adopt lock

Records the app's key flows, such as sign-in and checkout, as locked flows before anything changes. adopt next refuses to run until they exist (MAIN-ADOPT-0001 lock flows first), so migration can't silently change what your users see.

4. Migrate one module per pull request

bash
npx mainsdk adopt next --module secrets

Each step is one branch and one pull request, limited to one module, that must pass mainsdk check and every locked flow. If a step breaks a locked flow it's marked failed, the branch is kept for review, and your main branch is untouched.

A secrets step, before and after:

src/lib/stripe.ts (before)
import Stripe from "stripe";
export const stripe = new Stripe("sk_live_…");
src/lib/checkout.ts (after)
import { pay } from "mainsdk";

export function checkout(orderId: string, amountInCents: number) {
  return pay.checkout({ order: orderId, amount: amountInCents, currency: "USD",
    successUrl: "/orders/done", cancelUrl: "/cart" });
}

Code that can't move to an interface yet stays behind that interface's escape hatch, raw(), and check lists every use.

5. Finish

bash
npx mainsdk adopt finish

Produces the launch report: the app meets Main 27, or the report lists exactly what remains, with file and line.

Hono, Express and Node

If your app is a Hono or Express server rather than Next.js, three commands put it under the check:

bash
npx mainsdk init
src/server.ts
import express from "express";
import { mainsdk } from "mainsdk/express";   // Hono: import { mainsdk } from "mainsdk/hono"; app.use("*", mainsdk())

const app = express();
app.use(mainsdk());
// … your routes
app.use(mainsdk.errors());   // last
bash
npx mainsdk check

The adapters give every request a trace, its user and market context, and errors as problem details (see installation). What is and isn't specified for adopting these apps:

  • The adapters for Hono, Express and Node workers, and withPrisma and withDrizzle for the database, are specified for 0.2.
  • The adopt steps are scoped to "Next.js and Node apps". Hono and Express servers are Node apps, but the migration spec doesn't name them or say that adopt scan recognises their routes:.
  • Until that is settled, wire the adapter and run check yourself, and use adopt for Next.js apps.

Let your agent do it

The migration skill runs the same five steps through the MainSDK MCP server (adopt.scan, adopt.report, adopt.lock, adopt.plan_module, adopt.verify_module, adopt.finish). It works in Claude Code, Cursor and any MCP-capable agent, and intent checks run on your own agent and keys.

text
Bring this app onto MainSDK with the mainsdk-adopt skill. Start with secrets.

See Use MainSDK with your coding agent.

Next

Open questions

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet