Skip to main content

How to use the Retool CLI: Install, build, and deploy apps from your terminal

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.

If you want to..

Use

Why

Build a new app from a prompt, with the build happening in Retool

MCP

Retool's agent builds it server-side

Build or maintain an app locally, with code as the source of truth

CLI

Your agent writes to disk, and you review the diff

Import an existing React codebase

CLI

Large source trees stay on disk

Use Git, branches, or pull requests

CLI

The project is a normal repo

Work in a chat-only tool, such as ChatGPT in the browser

MCP

The CLI needs a bash tool

Look up apps and resources, or do admin work

MCP

One tool call, no clone

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 bash tool
  • 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 install

The 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.

Retool CLI account access request displaying a list of permissions and Authorize/Cancel buttons.

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-approvals
retool: 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 install

For 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:

    1.
  1. Loaded the retool-ready skill and read the spec.
  2. 2.
  3. Inspected the scaffold and the resource types, and found the refunds table.
  4. 3.
  5. Wrote two backend functions in backend/refunds/, one that reads the pending refunds and one that approves a refund.
  6. 4.
  7. Ran retool check to generate a React hook for each function.
  8. 5.
  9. Wrote the page in frontend/pages/PendingRefunds.tsx and pointed App.tsx at it.
  10. 6.
  11. 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 passed

retool 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.

A table displaying a list of pending refund requests, including customer, amount, reason, and "Approve" buttons.

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.ts
Publishing 507cf732-c6a4-11f1-b671-975912e4a0e2 @ 329f835…
✓ approved /backend/refunds/approveRefund.ts
✓ published "refunds-approvals".
Live: https://<your-instance>--refunds-approvals.retool.app

To 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.

Dashboard showing a list of 12 pending refund requests with customer, amount, reason, and options to approve or reject each.

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.ts

If 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.md

The 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:

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

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.

Retool CLI FAQs