Build with your agent

Open

Your coding agent builds and ships your product the way you would: it writes the files, pushes them to Test, fixes every error from the line and the fix each one names, and sends you the Test URL. Live waits for you.

Paste this into Claude Code, Cursor or any agent that runs terminal commands:

Prompt
Read https://saas.dev/AGENTS.md. Then build me a SaaS on SaaS.Dev that tracks
[what it tracks, and for whom]: start with
`npx saas.dev init my-product --from leaseclock.com`, push to Test, fix every
error, and send me the Test URL it prints. Go Live only through the approval
link, when I say so.

The agent can run npx saas.dev in a terminal, or call SaaS.Dev's MCP server.

Connect

SaaS.Dev's MCP server is https://api.saas.dev/mcp, over streamable HTTP. The first call opens SaaS.Dev's sign-in page, and the agent then works as you, on your products.

Terminal
claude mcp add --transport http saas-dev https://api.saas.dev/mcp

An agent on your own machine can run the same tools over stdio with npx saas.dev mcp, signed in as the CLI is. In CI, send SAAS_DEV_TOKEN as a bearer token in place of signing in.

The tools

SaaS.Dev's server has the same three tools as every product it generates: search and fetch read your products, their versions and their errors, and do runs a platform Verb. A Verb's input is checked before it runs, and every write lands in your product's history.

check

Checks files without saving anything, and returns every error at once.

InputTypeWhat it does
productstringThe product, such as my-product
filesobjectEach path and its text, such as { "Noun/Lease.mdx": "…" }

preview

Returns what a push would change on each surface, and whether any change is breaking.

InputTypeWhat it does
productstringThe product
filesobjectThe files to compare with the version Test serves

push

Checks the files and saves them as a new version in Test. It returns { ok, version, url }, or { ok: false, errors } with nothing saved.

InputTypeWhat it does
productstringThe product
filesobjectThe files
fixbooleanApplies every error's edit first
yesbooleanConfirms a breaking change

rollback

Serves an earlier version again, as a new version.

InputTypeWhat it does
productstringThe product
versionnumberThe version to serve
livebooleanRolls Live back instead of Test

go_live

Asks to serve a version Test already runs in Live. It returns an approval link for you to open. The version reaches customers only after you approve it there.

InputTypeWhat it does
productstringThe product
versionnumberThe version, which must already run in Test

Versions are records, read with search: { "type": "Version", "where": { "product": "my-product" } } lists them with who pushed each and where it runs.

The file your agent reads first

npx saas.dev init writes AGENTS.md into your product's folder, and the same file is at saas.dev/AGENTS.md:

AGENTS.md
# AGENTS.md

This folder is a product on SaaS.Dev. Its definition is the Markdown files in it: each file in `Noun/` is something the product tracks, with its fields and its Verbs, and each file in `Offer/` is a plan its customers buy. SaaS.Dev generates the app, API, CLI, MCP server, SDK, site, docs, blog and Stripe billing from these files, and runs them. None of the generated code lands in this folder.

## How to work here

1. Change the product by editing the files in `Noun/` and `Offer/`. Write only the keys in https://saas.dev/schema/noun.json and https://saas.dev/schema/offer.json (copies sit in `.saas/schema/`).
2. Push to Test: `npx saas.dev push`. Every file is checked, and every error comes back at once, each with its file, line, fix and edit.
3. Fix them all: `npx saas.dev push --fix` applies each error's edit, then previews.
4. Read what changes before it is saved: `npx saas.dev push --preview --json`.
5. Send the person the Test URL the push prints.

## What needs the person

- Live. `npx saas.dev push --live` prints an approval link, and Live serves the version only after the person approves it there.
- Tokens. The CLI keeps the person's sign-in for this machine. CI reads `$SAAS_DEV_TOKEN`. Write no token into a file.
- Money. `npx saas.dev stripe connect` opens Stripe for the person to sign in.

## The shape of a file

```mdx title="Noun/Lease.mdx"
---
name: Lease
fields:
  unit: { type: link, target: Unit, required: true }
  renter: { type: email, required: true }
  endsOn: { type: date, required: true, indexed: true }
  status: { type: choice, options: [{ key: active }, { key: ended }] }
verbs:
  renew:
    does: Move the end date forward
    input: { endsOn: { says: the new end date, type: date, required: true } }
    from: [active]
  end: { does: End the lease, from: [active], to: ended }
  remind:
    does: Email the renter that the lease ends in 60 days
    emails: renter
    from: [active]
    on: endsOn - 60 days
---
```

A Noun's name is PascalCase and singular. Field keys and Verb names are camelCase. A Verb with no function writes its input onto the record.

## An error

```json title="npx saas.dev push --json"
{
  "ok": false,
  "errors": [
    {
      "code": "invalid_name",
      "file": "Noun/Lease.mdx",
      "line": 6,
      "message": "field \"EndsOn\" must start lower-case.",
      "fix": "Rename it to \"endsOn\".",
      "edit": { "file": "Noun/Lease.mdx", "line": 6, "from": "EndsOn", "to": "endsOn" },
      "docs": "https://saas.dev/docs/errors#invalid_name"
    }
  ]
}
```

The command exits 1 on any error, and 2 on a breaking change without `--yes`.

## Commands

- `npx saas.dev init [folder] [--from <domain>]`: Asks for a name, signs you in on first run, starts from Leaseclock unless --from names another product, writes its files and AGENTS.md, and prints its Test URL
- `npx saas.dev login`: Signs you in through the browser and stores an id.org.ai token
- `npx saas.dev pull [name] [version]`: Writes a product's definition, or an earlier version, into a folder, with .saas/types.d.ts and .saas/schema/
- `npx saas.dev push`: Checks every file and pushes a new version to Test
- `npx saas.dev push --preview`: Checks every file and prints what changes on each surface, saving nothing
- `npx saas.dev push --live`: Pushes the version in Test to Live, after a preview
- `npx saas.dev versions`: Lists every version with who pushed it and where it runs
- `npx saas.dev rollback <version> [--live]`: Serves an earlier version again, as a new version
- `npx saas.dev open [path]`: Opens the product in Test in the browser
- `npx saas.dev domain add <domain>`: Serves the product on your own domain, with the DNS records to add: the site, and SPF and DKIM for its mail
- `npx saas.dev stripe connect`: Connects your Stripe account, once, for Test and Live
- `npx saas.dev token create <name> [--live]`: Creates a token for CI, which pushes to Test unless created with --live
- `npx saas.dev token revoke <name>`: Ends a token
- `npx saas.dev export`: Downloads the definition and every record in one file
- `npx saas.dev mcp`: Serves SaaS.Dev's MCP tools over stdio, for an agent on this machine

## More

- Every docs page as Markdown: https://saas.dev/llms.txt
- Every key a file takes: https://saas.dev/docs/agents.md
- Every error code: https://saas.dev/docs/errors.md

A session

Claude Code
> Build me a SaaS that tracks dog-walking bookings.

● Reading https://saas.dev/AGENTS.md
● npx saas.dev init my-product --from leaseclock.com
  https://my-product.test.saas.dev
● Renaming Lease to Booking, endsOn to startsAt, adding walker
● npx saas.dev push --json
  { "ok": false, "errors": [ { "code": "unknown_type", "file": "Noun/Booking.mdx",
    "line": 8, "fix": "Use \"team\" for a person in the workspace." } ] }
● npx saas.dev push --fix
  Pushed version 2 · https://my-product.test.saas.dev

Your product runs in Test at https://my-product.test.saas.dev. Bookings have a
start time, a walker and a status. Say when you want it Live, and open the
approval link I send.

Without MCP

Every command takes --json and prints one JSON object, so an agent reads results without parsing a terminal.

Terminal
npx saas.dev push --json
npx saas.dev push --preview --json

A push returns { ok, errors }. Each error carries code, file, line, message, fix, edit: { file, line, from, to } and docs, and push --fix applies every edit. A preview returns { version, changes: [{ surface, change, breaking }], breaking }.

Exit codeWhen
0The command did what it says
1Any error, such as a file that breaks a rule
2A breaking change without --yes

Without a terminal, push --live prints an approval link and waits until you approve it. Only a token made with token create --live pushes to Live without one.

Every key

Every key a definition file takes. The same grammar is JSON Schema at saas.dev/schema/noun.json and saas.dev/schema/offer.json, which init and pull save to .saas/schema/.

InKeyTakesWhat it does
NounnamePascalCase, singularThe Noun: the list, the API path, the SDK class and the docs page take their names from it
Nounfieldsa map of field key to fieldWhat each record holds
Nounverbsa map of Verb name to VerbWhat people and agents do with a record
Fieldtypeone of the field typesWhat the field holds, and the input, column and JSON it gets
FieldrequiredtrueA record cannot be saved without it
FieldindexedtrueSorts and filters the list, and takes .before and .after on dates
FieldmanytrueHolds a list of values
Fieldmaxa numberThe longest text, or the most values in a list
Fieldpatterna regular expressionEvery value must match it
FieldrichtrueAllows formatting in longText
Fieldoptionsa list of { key, label }The options of a choice or choices field
Fieldcurrencya three-letter codeA money field's currency, when it is not the Offer's
Fielddecimalsa numberDecimal places shown and stored
Fieldrange{ min, max }The least and most a number may be
FieldunittextA number's unit, shown beside it
Fieldstarta numberAn autoNumber's first value
FieldclassificationNAICS, O*NET or UNSPSCThe standard a classification field draws from
Fieldtargeta NounThe Noun a link points at
FieldonDeleteblock, clear or cascadeWhat happens to linking records when the target is deleted
FieldreverseLabeltextThe heading on the target's page that lists linking records
Fieldtakespeople, agents or bothWho a team field can hold
Fieldaccepta list of file typesThe files a file field takes
FieldmaxMba number up to 25A file's largest size
Fieldlookup{ via, field }Reads a field through a link
Fieldrollup{ via, fn, field }Counts or adds up over linking records
Verbdoesone sentenceWhat the Verb does: its button title, docs line and tool description
Verbinputa map of input key to { says, type, required }What the Verb asks for
Verbfroma list of statesThe states the Verb runs from
Verbtoa stateThe state the Verb leaves the record in
Verbemailsan email fieldSends the Verb's does line to that address when it runs, from the product's own domain
Verbona date field, plus or minus daysRuns the Verb by itself on that date, for each record in a from state, as the product
OffernametextThe plan's name on the pricing page, at checkout, in Stripe and on every invoice
Offerprice{ amount, currency, unit }The price in whole units of the currency, billed each month or year
Offerlimitsa map of Noun to a numberThe most records of each Noun one customer may hold
OfferpersonastextWho the plan is for
OfferstatusDraft, Live or RetiredWhere the Offer sells
Offertrialdays, such as 14 daysA free trial before the first charge
OffertaxautoStripe works out, collects and reports sales tax and VAT from each customer's address
Field typetextmax, patternOne line
Field typelongTextmax, pattern, richParagraphs
Field typeemailpatternAn email address, checked on save
Field typephonepatternA phone number, shown as a call link
Field typeurlpatternA web address, shown as a link
Field typenumberunit, decimals, rangeA count or a measure
Field typemoneycurrency, decimals, rangeAn amount in a currency's main unit
Field typepercentdecimals, rangeA rate
Field typeautoNumberstartA number assigned on create, counting up
Field typedate·A day
Field typedateTime·A moment, stored in UTC
Field typemonth·A month
Field typeyesNo·Yes or no
Field typechoiceoptionsOne option from a list
Field typechoicesoptionsAny of the options
Field typeclassificationclassificationA standard code
Field typelinktarget, many, onDelete, reverseLabelAnother record
Field typeteammany, takesPeople or agents in the customer's workspace
Field typefileaccept, maxMb, manyAn uploaded file
Field typelookuplookupA field read through a link
Field typerolluprollupA count or sum over linked records

Contents