MainSDK
Search⌘K
Docs

Prompts that work

Ten copy-paste prompts for a coding agent on MainSDK, sign-in, data, AI, uploads, MCP, payments, jobs, repairs and adoption. Each ends with a check.

Planned for 0.3

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

Each prompt below names the interface, the rule and the check the coding agent must run. Copy one, change the nouns, and paste it to your agent. About a minute per prompt to adapt.

MainSDK is pre-release: the commands are the planned design and do not run yet. mainsdk check --json --agent is planned for 0.3, mainsdk adopt for 0.5.

You need

  • A project with main.yaml and the MainSDK section in AGENTS.md (setup).
  • A coding agent that reads AGENTS.md: Claude Code, Cursor, Codex or any MCP agent.

A pass is the same every time: the JSON output has "passed": true and the exit code is 0. A failure is not the end: the agent follows the repair loop.

1. Add sign-in

text
Add sign-in to this app with the MainSDK auth interface. Run `npx mainsdk add auth` if
auth is not in main.yaml. Protect app/api/orders/route.ts with `auth.require(req)` and
scope every query with `db.orders.for(session.user)`. Do not write provider code or put
keys in files. When done, run `npx mainsdk check --json --agent` and fix every finding.
It passes when the output has "passed": true and the exit code is 0.

You should see: main.yaml gains auth, the route returns 401 with MAIN-AUTH-0001 and a fix when signed out, and 200 when signed in.

2. Add a database model owned by a user

text
Add an "invoices" model that belongs to the signed-in user. Use the MainSDK db interface:
read and write only through `db.invoices.for(session.user)`, and pass an idempotencyKey
on every create. Label any personal field in main.yaml under `db.fields.invoices`. Do not
write raw SQL and do not import a database client. Write a test with `@mainsdk/testing`
that proves a second user cannot read the first user's invoice (`MAIN-DATA-0007`). Run
`npx mainsdk check --json --agent`. It passes when "passed": true and the exit code is 0.

3. Add AI with a per-user spend limit

text
Add a "summarise this order" feature using `ai.generate` from mainsdk. Pass `feature:
"order-summary"` and `user`. In main.yaml, set `ai.limits` with a per-user and a total
spend limit so a single user cannot run up the bill, and keep `ai.data` at training: never,
retention: zero. If an app agent makes the call, also pass `agent: "<name>"` and add
`agents.<name>.budget: { spend: $5/day, calls: 500/day }` to main.yaml. Do not import a
provider SDK. Run `npx mainsdk check --json --agent`. It passes when "passed": true and
the exit code is 0; the limits show in `npx mainsdk info`.

At the limit, a call fails with MAIN-AI-0007 and nothing reaches the provider. A call that names an app agent is also charged to that agent's budget, and over it fails with MAIN-AGENT-0002. See Agents with an identity, a budget and approvals.

4. Stream an AI reply with typed output

text
Add a support-reply endpoint. Stream the reply to the browser with `ai.stream({ model,
messages, feature: "support-reply", user })`; use the same `ai.limits` spend limits as the
other AI features. For the triage step, call `ai.generate({ model, prompt, feature:
"ticket-triage", user, schema })` with an `s` schema, and use the typed result. Treat
model output as untrusted input: never run it as code or build a query from it. Do not
import a provider SDK. Run `npx mainsdk check --json --agent`. It passes when "passed":
true and the exit code is 0.

A reply that fails the schema twice throws MAIN-AI-0011; a stream that reaches the limit stops with MAIN-AI-0007. Streaming and schema are in the ai@1 spec (milestone 0.3); the interface page does not show them yet, so the agent follows the spec's call shapes only.

5. Upload files

text
Let signed-in users upload a receipt image. Use the MainSDK files interface with the
S3 driver (or Uploadthing, if main.yaml already uses it), key as a `secret:` reference
and a maxSize. Mint the upload target with `files.uploadUrl({ owner, maxSize, types })`
and confirm the upload with `files.complete(ref.id)` from a second route, owner set to the signed-in user. Return
links with `files.url(ref, { for: user, expiresIn: "1h" })`. Do not import a storage SDK
in route code, and do not build an upload store or a public bucket. Run
`npx mainsdk check --json --agent`. It passes when "passed": true and the exit code is 0.

The call shapes come from the files contract in the spec (files.uploadUrl({ owner, maxSize, types, idempotencyKey? }) returns { method, url, headers?, fields?, ref }; files.complete(id) scans the file and makes it readable). The driver is chosen in main.yaml; see File uploads and Upload files with S3, Uploadthing or Vercel Blob.

6. Expose an action as an MCP tool

text
Turn orders.list into an MCP tool for agents. Write it once with `action()` from
mainsdk: name "orders.list", an `intent`, an `s` `input`, `risk: "read"` and `mcp: true`.
Scope the data with `db.orders.for(user)`. In main.yaml add
`mcp.auth: { issuer: auth, scopes: [orders:read], token-ttl: 10m }`. Do not add a
second route that skips the action's checks. Run `npx mainsdk check --json --agent`.
It passes when "passed": true and the exit code is 0.

Details: MCP server and client.

7. Take a payment

text
Add checkout for an order. Use `pay.checkout({ order, amount, currency, successUrl,
cancelUrl })` after `auth.require(req)`; amount is in integer minor units. Put the
provider key and webhook secret in main.yaml as `secret:` references. Handle
`payment.succeeded` with `pay.on()` and send slow work to `jobs.run`. Do not import a
payments SDK and do not read card data. Run `npx mainsdk check --json --agent`.
It passes when "passed": true and the exit code is 0.

8. Add a background job

text
Move the monthly report export out of the request. Call `jobs.run("reports.export",
{ userId, month }, { idempotencyKey: "export:<userId>:<month>", retries: 3, backoff:
"exponential" })` from the route and return 202. A retry must never send or write twice.
Do not add a queue library. Run `npx mainsdk check --json --agent`. It passes when
"passed": true and the exit code is 0.

9. Fix a failing check without weakening it

text
`npx mainsdk check` fails. Run `npx mainsdk check --json --agent`, read each finding's
file and fix, and change the code to satisfy the rule. Run `npx mainsdk explain <code>`
if a fix is unclear. Do not add an exception, lower a severity, edit baseline, delete a
gate or use `raw()` to get around a finding. If the same finding is still open after
three tries, stop and ask me. Re-run the same command. It passes when "passed": true
and the exit code is 0.

10. Adopt an existing app one module at a time

text
Bring this app onto MainSDK with the mainsdk-adopt skill. Run `npx mainsdk adopt scan`,
then `npx mainsdk adopt report` and show me the gaps, secrets first. Run
`npx mainsdk adopt lock` before changing any code. Then migrate one module per branch
and pull request with `npx mainsdk adopt next --module <name>`, starting with secrets.
Keep what the app already runs (a sign-in provider, a database) instead of replacing
it. Never rewrite the whole app in one change. After each module run
`npx mainsdk check --json --agent`; it passes when "passed": true, the exit code is 0
and every locked flow is green. Finish with `npx mainsdk adopt finish`.

The scan changes no code. adopt next refuses to run until flows are locked (MAIN-ADOPT-0001). See Adopt an existing app.

Verify

bash
npx mainsdk check --json --agent

You should see "passed": true and exit code 0.

If it fails

CodeCauseFix
MAIN-SECRET-0002A literal key in codeMove it to a secret:NAME reference in main.yaml
MAIN-DATA-0007A query not scoped to the ownerQuery with db.<model>.for(user)
MAIN-AI-0007A spend limit was reachedRaise ai.limits in main.yaml on purpose, or wait for the period to reset
MAIN-AI-0011Typed output failed the schema twiceFix the schema or the prompt; never return the raw text as if valid
MAIN-ADOPT-0001adopt next ran before flows were lockedRun mainsdk adopt lock first

Any other code: npx mainsdk explain <code>.

For agents

text
Goal:    Do exactly what the prompt asks, on MainSDK interfaces only.
Files:   the code the change needs, plus main.yaml to declare what it uses.
Verify:  npx mainsdk check --json --agent   (pass = "passed": true and exit code 0)
Rule:    never weaken a check; never add an exception without an owner and expiry;
         after three unresolved rounds on one finding, stop and ask the developer.
Unknown: a call shape not in the docs or the skill is not yours to invent. Ask.

Next

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet