Overview
This guide shows you how to install the Retool CLI and use it with a coding agent to build a refunds approval app and publish it. It also covers when to use the CLI or the MCP server, and what to know during the public beta.
The Retool CLI lets you build, validate, and publish Retool-native React apps from the command line while Retool handles permissions, resource access, and deployment.
Building a Retool app from a coding agent used to mean going through the MCP server. Your agent sends a prompt to Retool’s agent, Retool’s agent builds the app in Retool, and your agent waits for the result. With the Retool CLI, the app lives as a React and TypeScript folder on your machine. You or your agent write the code, the CLI validates it locally, and Retool builds and runs it when you push for a preview or publish.
TL;DR: Install the CLI with pnpm i -g @tryretool/cli, sign in with retool auth login --host <your-instance>, then run five steps: 1) retool init (or retool clone), 2) pnpm install, 3) retool check, 4) retool push, and 5) retool publish. Code is written and validated locally, so during the public beta, CLI commands don’t use Retool AI credits.
What is the Retool CLI?
The Retool CLI is a command-line tool for building, managing, and publishing Retool-native React apps. You can run the commands yourself or have a coding agent, such as Claude Code or Codex, run them.
You write and validate the code on your machine. Retool’s agent and sandbox are only invoked for previewing or publishing, so there’s no agent-to-agent loop while you build.
Retool still enforces your organization’s permissions. The CLI signs in as you and inherits your permissions, so it can only do what your account can do. If you don’t have Edit access to an app, you can’t edit it from the CLI. Resource access is checked when you push, and a function that writes data has to be approved before the app can be published.
The CLI is in public beta for cloud customers on Team, Business, and Enterprise plans.
Retool CLI vs. MCP server: which should you use?
MCP builds run on Retool’s agent, so they use Retool AI credits unless you bring your own keys. CLI builds run on your local agent, so model usage counts against your existing subscription.
Use the CLI when you’re working in a repo, and the MCP server when you want Retool to build the app or you need to manage your organization. You don’t have to choose one. If you install both, your agent can pick whichever fits the task.
How to build and deploy a Retool app with the CLI
Building an app with the CLI takes five steps: start a project, install its dependencies, validate the code, push it for a preview, and publish.
Let’s work through an example. We have a refunds table in Retool Database with 20 refund requests, 12 still pending, and we want an app that lists the pending ones and lets you approve each with a button.
Before you start: prerequisites and installation
You need:
- Edit permissions in Retool so that you can create and build apps
- A terminal, or an agent environment with a
bashtool - Node.js 22, git, and pnpm (opens in a new tab)
Install the CLI, sign in, and install the agent skill:
pnpm i -g @tryretool/cli
retool auth login --host <your-instance>.retool.com
retool skill installThe first command installs the CLI from npm. If pnpm says its global bin directory isn’t in your PATH, run pnpm setup, restart your shell, and run the install again.
retool auth login opens your browser and asks you to authorize the CLI on your instance.

The authorization screen lists what the CLI can do: view and download app source code, create apps and upload source code changes, list resource names and types, and create previews and publish apps.
Once you authorize, the terminal confirms the sign-in:
Signed in as you@example.com.
Your token never exceeds your own scope; the server enforces that on every call.“Your token never exceeds your own scope” reminds you that the CLI uses your permissions.
retool skill install adds the retool-ready skill to every coding agent it finds on your machine. Retool recommends pairing the CLI with an agent such as Claude Code or Codex. In this example, we used Claude Code running Opus 5.5.
If you work in the Claude desktop app or in ChatGPT Work, you install the CLI as a plugin instead. In a container or SSH session with no browser, run retool auth login --device to sign in with a device code.
1. Start a project with retool init or retool clone
retool init creates a new app in Retool and scaffolds it on your machine. Run it somewhere outside a git repository, because init refuses otherwise. It also needs an empty directory. (--force overrides both checks).
retool init refunds-approvalsretool: using https://<your-instance>.retool.com (your default host)
Pulling resource types…
✓ pulled 158 resource type file(s) for env "production".
Scaffolded and provisioned "RefundsApprovals" (507cf732-c6a4-11f1-b671-975912e4a0e2) into refunds-approvals.
Read the app shape spec before writing code: retool skill show retool-ready
Next, in refunds-approvals, run:
pnpm install
retool check
retool push -m "<what changed>"init created an app called RefundsApprovals in Retool (the directory name in PascalCase) and wrote the project into refunds-approvals. It also pulled type declarations for the resources in our organization, so the code that queries Retool Database is typed.
To work on an existing app, use retool clone with the app’s UUID or editor URL. retool apps lists your apps and their UUIDs. clone works on apps, not classic apps, and it creates a new branch unless you pass --branch with one of the names from retool branches <app>.
2. Install dependencies with pnpm install
cd into the project and run pnpm install from the root of that app directory.
cd refunds-approvals
pnpm installFor our app, it installed 291 packages in about five seconds.
3. Validate your code with retool start and retool check
retool start watches the project and re-validates every time a file is saved. We ran retool start in one terminal while working with our agent in another.
✓ validate: 245 files, no violations. Skipped on the laptop (the gate runs these): bannedSymbols, cliFileAuthorization, resourceAcl, resourceEnvironmentAvailability, workflowAcl
watching ~/code/refunds-approvals for changes (Ctrl-C to stop)…Five checks are skipped locally, including resourceAcl and workflowAcl,which check your access to the resources and workflows the app needs. Retool runs those on its side when you push.
With start running, we opened Claude Code in the same directory and gave it one prompt:
Build an app that lists the pending refunds from the refunds table in Retool Database. Each row needs an Approve button that sets that refund's status to approved. Run retool check when you're done and fix anything it reports.Claude Code went to work and:
- Loaded the
retool-readyskill and read the spec. - Inspected the scaffold and the resource types, and found the
refundstable. - Wrote two backend functions in
backend/refunds/, one that reads the pending refunds and one that approves a refund. - Ran
retool checkto generate a React hook for each function. - Wrote the page in
frontend/pages/PendingRefunds.tsxand pointedApp.tsxat it. - Ran the read function against the live table with
retool resource explore, which runs read-only code by default, and got the 12 pending rows back.
Here’s our read function:
TypeScript
export type Refund = {
id: number
refund_id: string
order_id: string
customer_name: string
customer_email: string
amount: number
currency: string
reason: string
status: string
requested_at: string
}
// Lists refunds that are still awaiting a decision, oldest request first.
export default async function getPendingRefunds(): Promise<Refund[]> {
const result = await retoolDb.query<Refund>(
`SELECT id, refund_id, order_id, customer_name, customer_email, amount, currency, reason, status, requested_at
FROM refunds
WHERE status = 'pending'
ORDER BY requested_at ASC, id ASC`,
)
return result.data
}The function queries Retool Database through retoolDb and returns typed rows. Its directory and file name decide the React hook the frontend gets. backend/refunds/getPendingRefunds.ts becomes useGetPendingRefunds in frontend/hooks/backend/refunds.ts. retool check regenerates the hooks each time it runs.
When Claude Code finished, we ran the check ourselves using retool check:
✓ hooks: passed
✓ validate: passed
Not run locally: bannedSymbols, cliFileAuthorization, resourceAcl, resourceEnvironmentAvailability, workflowAcl
✓ typecheck: passed
✓ build: passed
✓ check: all required local phases passedretool check validates once and exits. It regenerates the hooks, validates the project, type-checks it, and runs a production build. It makes no network calls, and --skip-build skips the build while validation and type-checking still run.
Ours passed on the first full run. When something is wrong,though, retool check lets you know before it reaches Retool. Stackdrop (opens in a new tab), a firm that builds custom internal apps, used the CLI during the private beta. retool check caught two placeholder-ordering bugs in SQL they wrote: the query looked correct but wrote the wrong values into the wrong columns.
4. Preview with retool push
retool push runs retool check first; if it fails, nothing is pushed. If it passes, the CLI sends the code to Retool and starts a preview build. --wait blocks until the build finishes and prints the preview URL, and -m sets the commit message shown in the app’s Change History.
retool push --wait -m "Pending refunds list with approve button"Running `retool check` before push...
✓ check: all required local phases passed
✓ push accepted by the gate; branch retool-agent/b03805ef-805c-41c8-940d-00c2c1827104 advanced to 329f835.
Waiting for the preview build…
Preview: https://<your-instance>--d4a4cdf7-5520-4baf-add6-f369ba18fe81.retool.app
Function approvals: 1 pending
/backend/refunds/approveRefund.ts — treated as a write: UPDATE refunds SET status = 'approved' WHERE … on retool_db
The preview runs these without approval. The editor asks before running each one, and
`retool publish` blocks until they are approved.The server accepted the push, which means the checks that were skipped earlier (such as resourceAcl and workflowAcl) ran on Retool and passed. The code went to the app’s working branch, not to main.
The preview URL opens the working app.

We have a working preview of the refunds app. The table lists the 12 pending refunds with an Approve button on each row. The banner at the bottom shows that this is an Unpublished Preview.
Retool also read the functions and flagged approveRefund.ts as a write, because it runs an UPDATE on Retool Database that sets a refund’s status to approved. The preview runs the function without approval, so you can click Approve and test it. Ours was connected to the production environment, so approving a refund in the preview would change the real row. Publishing is blocked until the function is approved.
5. Ship with retool publish
retool publish puts the app on a live URL. The first publish needs --identifier, which sets the app’s URL slug. We had already reviewed the approve function, so we approved it in the same command.
retool publish --identifier refunds-approvals --approve-function /backend/refunds/approveRefund.tsPublishing 507cf732-c6a4-11f1-b671-975912e4a0e2 @ 329f835…
✓ approved /backend/refunds/approveRefund.ts
✓ published "refunds-approvals".
Live: https://<your-instance>--refunds-approvals.retool.appTo see what publishing does when a function hasn’t been approved, we asked Claude Code to add a Reject button. That added a second write function, rejectRefund.ts, and Claude Code pushed it to the same preview.

This time we ran retool publish with no flags:
Publishing 507cf732-c6a4-11f1-b671-975912e4a0e2 @ a0b2a44…
✗ publish blocked: 1 mutating serverless function(s) need approval at a0b2a44:
/backend/refunds/rejectRefund.ts (needs your approval)
Review them, then re-run with --approve-function <path> to approve one, or --approve-functions to approve all, before publishing.approveRefund.ts isn’t on the list. It was approved in the first publish and hadn’t changed, so we only needed to decide on the new function.
--approve-function <path> approves one function and can be repeated, and --approve-functions approves everything that’s pending. You need Edit access to every resource and workflow the functions use.
retool publish --approve-function /backend/refunds/rejectRefund.tsIf the app is protected with Source Control, publish opens a pull request and the app isn’t published until it’s merged. --wait waits for the merge and the deployment.
publish also takes --tag to apply a publish tag, --folder to move the app into a folder, and --release to mark a major, minor, or patch release. --release always has to be paired with --publish-behavior.
How to work on branches with the Retool CLI
retool status shows where your local copy stands against the server:
App: 507cf732-c6a4-11f1-b671-975912e4a0e2
Branch: retool-agent/b03805ef-805c-41c8-940d-00c2c1827104
Checkout: a0b2a44
Server branch: a0b2a44 (matches checkout)
Main: 152abff (contained in branch)
Preview: ready (https://<your-instance>--d4a4cdf7-5520-4baf-add6-f369ba18fe81.retool.app)
Function approvals: 1 pending
/backend/refunds/rejectRefund.ts (missing)
Resource types: current (cache 42, env production)We ran this after the second push. The local checkout and the server branch are on the same commit, and the branch already contains main, which now includes the first publish.
If the server branch moves because someone edited the app in Retool or an agent thread pushed to it, retool pull updates your local copy. retool pull --from main also merges in main. If your local copy and the server have diverged, pull stops and prints the git command to run.
retool checkout <branch> switches your local copy to another branch of the same app, such as one a teammate created in the app builder. The CLI refuses if the switch would overwrite uncommitted local changes, or if your local copy of that branch has commits the server doesn’t have.
We recommend using the CLI with Source Control to manage contributors and app versions.
How coding agents use the Retool CLI skills
A coding agent doesn’t know what a valid Retool app looks like. It can’t guess the file layout, how backend functions have to be written, or the type checks Retool applies. The retool-ready skill teaches it those rules, and retool skill install puts it where each agent looks for skills:
Installed retool-ready for claude-code: ~/.claude/skills/retool-ready/SKILL.md
Installed retool-ready for codex: ~/.codex/skills/retool-ready/SKILL.md
Installed retool-ready for cursor: ~/.cursor/skills/retool-ready/SKILL.mdThe installed file tells the agent to run retool skill show retool-ready, and that command prints the current spec from the same core that runs retool check. When the CLI updates, the agent reads the new rules without reinstalling anything. To install for one agent only, pass --agent claude-code, --agent codex, or --agent cursor.
In our build, loading the skill was the first thing Claude Code did. It then ran resource explore and retool push on its own, and the only command our prompt named was retool check. You can still look up every command and flag yourself.
Some of the CLI’s output is addressed to the agent. After the push, it printed “Review each function with the user,” and Claude Code stopped to ask us about the approve function instead of publishing.
How to connect the CLI to multiple Retool instances
Agencies and teams with separate staging and production instances can use CLI install with several Retool hosts. Run retool auth login --host once for each instance. After that, retool auth status lists every instance you’re signed in to, and retool auth use <host> changes the default.
Outside an app directory, the CLI picks the host from --host, then the RETOOL_HOST environment variable, then your saved default. Inside an app directory, it uses the host recorded in .retool/app.json, and commands like push don’t accept --host.
For CI, retool auth login --token stores an access token, which needs the react_apps:write scope, and reads it from RETOOL_TOKEN if you don’t pass one. Set RETOOL_CREDENTIALS_PATH to a writable path when the home directory is read-only.
How to deploy a vibe-coded app to Retool with the CLI
An app built in Lovable, Cursor, or Replit can work fine and still not be ready for internal use without SSO, access control, and audit logging. Deploying to Retool adds them: a published app sits behind Retool authentication, with SSO, role-based access control, and audit logging applied by the platform.
Importing a React app from another tool is one of the CLI’s main purposes. You scaffold an app with retool init, have your agent move the existing React code into it, and then run check, push, and publish as you would for any other app. Watch Angelik, our developer advocate, do it right here:
If you’d prefer not to use the terminal, you can also upload a zip in the app builder or import through the MCP server.
Retool CLI limitations and what to know in public beta
Several limits apply today:
- Self-hosted instances don’t have the CLI yet, but it’s coming in future edge and stable releases.
- It needs a shell, so it won’t run in a chat-only tool like ChatGPT in a browser.
retool clonedoesn’t work on classic apps.retool classic exportdownloads a classic app’s Toolscript so that you or an agent can rebuild it as a React app.- Functions triggered from the CLI are limited to 10 concurrent runs per user, compared with 50 from the app builder.
- CLI commands don’t use Retool AI credits during the public beta.
The CLI also comes in two parts that are versioned separately. The launcher is what you install from npm, and ours was version 0.1.10. When we signed in, the CLI printed retool: installing 0.4.111 (from https://<your-instance>.retool.com). That’s the core, which matches the version of Retool your instance runs, so one launcher can work with several instances on different versions. retool update installs the core version your instance recommends.
Next steps
- Install the CLI and sign in to your instance.
- Run the five steps on a small app of your own.
retool gallery listshows the App Gallery apps you can start from withretool init --gallery <id>. - Set up Source Control so that changes go through pull requests.
- Connect the MCP server for admin work and for looking up apps and resources.
Commands and plan availability can change during the beta, so check the CLI docs before you start. If you’d rather describe an app and have Retool build it, Retool can do that from a prompt, and the resource hub has more build tutorials.
Build your next Retool app from the terminal
Stackdrop’s CTO, Mahmoud, described building with the CLI like this: "Building a Retool app this way keeps us in our preferred software development workflow. We haven’t opened the app editor once for any of these builds."
If that’s how you want to build, start for free or book a demo.
