Skip to main content

How to build an app in Claude Code and deploy it to Retool

Retool’s React app import lets you take an app you built in Claude Code and bring it into Retool’s governed environment. Connect to Retool’s MCP server from Claude Code, and Claude will import the app for you. It reads your Retool resources, connects your app's data to them, and creates the app in your workspace.

TL;DR: Connect Retool as an MCP server in Claude Code → build your React app → have Claude Code import it into Retool through MCP → map its data to a Retool resource → permissions, audit logs, and approval gates apply automatically.

What you'll build

By the end of this guide, you'll have a subscriptions admin dashboard running in Retool that reads live billing data from a Stripe resource. This internal tool will include:

  • A metrics row (MRR, active subscribers, ARPU, accounts needing attention)
  • MRR broken out by plan
  • A status breakdown
  • A searchable subscriber list with change-plan and cancel actions

The app starts in Claude Code as a normal React project with an Express backend that holds the Stripe key. The interesting part is what happens on import. Since Retool has no long-running Express server, the import moves that backend into Retool backend functions bound to your Stripe resource, and the front end keeps working unchanged.

When you're done, the app is deployed to Retool, wired to the Stripe resource (running in test mode here), with Retool's governance automatically enforced. Any function that can modify Stripe data is gated behind an approval step you never had to write.

Prerequisites

  • Claude Code installed and configured (Claude Code docs)
  • A Retool account, free plan works (sign up)
  • Retool connected to Claude Code as an MCP server (Retool MCP setup docs)
  • A data source connected as a Retool resource (this guide uses a Stripe resource in test mode)
  • Basic React familiarity

Retool's MCP server is in public beta and available on Retool Cloud for all plans. Self-hosted instances need version 4.0.0 or later.

Step 1: Connect Claude Code to Retool via MCP

This is the foundation for everything else. Once Retool is connected via the MCP server, Claude Code can see your workspace and drive the React app import without you leaving the terminal.

What Retool's MCP server exposes to Claude Code

Through MCP, Claude Code gets access to your Retool workspace: the resources you've connected (databases, REST APIs, the Stripe resource), the app builder (create, read, and stream app files), and the resource query interface, including each resource's TypeScript definitions and a way to run exploratory queries. The MCP tools reference lists every tool and the scope it needs.

How to add Retool as an MCP server in Claude Code

Add Retool as an MCP server from your terminal, then authorize the connection:

    1.
  1. Run claude mcp add --transport http retool https://<your-instance>.retool.com/mcp.
  2. 2.
  3. Exit the session and start a new one.
  4. 3.
  5. Run https://retool.com/mcp, select the Retool server, and authorize it. A browser window opens and asks which scopes to grant.

Retool offers three scopes:

    1.
  1. Read: view resources and metadata
  2. 2.
  3. Write: create and update apps and resources
  4. 3.
  5. Admin: administrative actions, which include reading a resource's TypeScript definitions and running queries against it

This import workflow touches all three, so grant read, write, and admin.

Retool MCP authorization screen requesting read, write, and admin access, with Authorize and Deny buttons.

Authorize the Retool MCP server from Claude Code. Read and write permissions cover the import workflow; admin permissions are only needed for administrative actions.

Confirming the connection

Run https://retool.com/mcp in Claude Code. A successful connection reports authentication and lists Retool as one of your connected servers. From here, Claude Code can list your resources and the folders it's allowed to write apps into. In our session, we could see the Stripe Admin Dashboard resource and an External apps folder to import into.

Step 2: Build your app in Claude Code

Build the app the way you'd build any project using Claude Code. At this stage, it runs locally and communicates with its own backend, with Retool not yet involved.

Our example app is a Vite, React, and TypeScript project with Tailwind for styling and a small Express backend. The Express layer holds the Stripe secret key and exposes a handful of routes the front end calls over fetch('/api/...'). The client never touches Stripe directly: a single data module maps raw Stripe objects into the app's view model, and components read only from that module.

Prompt Claude Code with your app requirements

Describe the dashboard you want: the metrics to surface, the subscriber table, the plan-change and cancel flows, and the Stripe endpoints behind each. Claude Code scaffolds the components and the Express routes (list plans, list subscriptions, get a subscription, change plan, cancel, preview proration).

Iterate on the UI

Refine the pieces that make up the dashboard:

  • A metrics panel for MRR, active subscribers, ARPU, and accounts needing attention
  • An MRR-by-plan breakdown and a subscriptions-by-status bar
  • A searchable, filterable subscriber list
  • A subscriber detail view with change-plan and cancel dialogs, plus toasts and status badges
A subscription administration dashboard displaying total MRR, active subscribers, ARPU, MRR breakdown by plan, subscription statuses, and a list of customer subscriptions.

The finished app runs locally against the Express backend before any Retool involvement. At this point, the app is complete and working locally. The one thing standing between it and Retool is the Express backend, which is what the import handles next.

Step 3: Import the app and map data to your Stripe resource

This is where the app stops using its local Express backend and starts reading from real systems through Retool. You tell Claude Code to do it in one instruction:

Import this React app into Retool, and map the subscription data to my Stripe resource.

Claude Code drives the React app import through Retool’s MCP server. There's no manual export or upload step: it reads your Stripe resource, translates the backend, and creates the app in your workspace.

Connect Stripe as a Retool resource

Before the import can map anything, you need the Stripe resource itself. Retool has a native Stripe integration with a dedicated resource type, so setup is short: create a Stripe resource, add your API key (a test-mode key here), and you're connected.

A screenshot of the Stripe Admin Dashboard's connection details page with API configuration fields and "production" environment selected.

Add an API key, and the full Stripe operation set is available to your apps. From there, you can query any Stripe endpoint through a structured operation picker instead of writing raw HTTP requests. Charges, subscriptions, balance, payouts, invoices, and refunds are all exposed as operations, among others.

Every query pulls live data from Stripe's API when the app loads or a user triggers a refresh, so there's no ETL pipeline to maintain and no exported copy that goes out of date.

This is the resource Claude Code inspects during the import and binds the new backend functions to.

Why the backend has to move

Locally, the React client calls an Express server that holds the Stripe key. Retool apps don't run a long-lived Express server, and the front end never holds secrets or calls third-party APIs directly. Secrets and external calls live in Retool resources and backend functions instead, and the front-end calls those functions through generated hooks.

So the import maps each Express route to a Retool backend function bound to your Stripe resource. The detail that keeps everything else unchanged is that each backend function returns the same raw Stripe shapes that the old Express route returned. The client's mapping layer ports over untouched, and only the transport changes.

Mapping Express routes to Retool backend functions

Claude Code inspects the Stripe resource first to learn its binding variable (here, stripeAdminDashboard) and its method names. The Retool Stripe resource mirrors the Stripe API but uses REST-style method names rather than the stripe-node SDK names, so it's worth reading the resource's own definitions rather than assuming the SDK surface.

Two details matter when you map a Stripe resource. List endpoints come back double-nested, so you read res.data.data. And in list responses, price.product arrives as a string ID that you hydrate with a separate batched fetch.

Wiring the front end to the resource

Because the backend functions return identical shapes, the client data module barely changes. Every mapper stays the same. Each public function replaces its fetch('/api/...') call with a backend function call, and the return handling stays the same.

Before, calling the Express backend:

const { data } = await api<{ data: RawSubscription[] }>(
  `/subscriptions?${query}`
);

After, calling the Retool backend function:

const { data } = await callBackend<{ data: RawSubscription[] }>(
  'listSubscriptions',
  {
    status: filters.status !== 'all' ? filters.status : undefined,
    planId: filters.planId,
    q: filters.q,
  }
);

The only difference is the call itself. api(url) becomes callBackend(name, params), the request goes from a query string to a named-params object, and everything downstream of the destructure is untouched.

Testing the connection before configuring governance

Verify the read path before you go further. The read-only functions (listPlans, listSubscriptions, getSubscription) can be run directly in Retool and should return your seeded test data. In our build, they came back with three subscriptions, five recurring prices with product objects, and invoices present, every shape matching the original Express logic.

A subscriptions admin dashboard with summary metrics, data visualizations, and a detailed table of customer subscriptions.

The same dashboard in Retool, now reading live data from the Stripe resource through backend functions. The resource connection you just made is also the point where governance is enforced.

Step 4: What governance you get automatically

Because external calls run through the Stripe resource, Retool enforces a set of controls at the resource layer, underneath the app. You wrote no code for any of them.

You see the first one during the import itself. When the build reached the functions that can write to Stripe, Retool flagged them for review and blocked them from running until approved. The read-only functions ran freely; changePlan, cancelSubscription, and previewProration landed in a "Needs review" state behind a toggle labeled "Require approval to run functions that may modify data."

previewProration is gated because Stripe's upcoming-invoice preview is a POST, so Retool classifies it as a mutation even though it changes nothing.

A "Subscriptions Admin" interface showing code for a `cancelSubscription` function with parameters, a "Run Function" button, and a banner indicating the function requires review.

The cancelSubscription function is held for review against the Stripe resource. Mutating functions can't run until a reviewer approves them.

Enforcement happens at the resource layer, not inside the app. This imported app inherits the same controls as every other app in the workspace that touches this resource, because of where the resource sits, not because of anything configured in the app code.

Update the resource's permissions or data connection once, and the change applies to every app that uses it. That's Retool's stated model: guardrails enforced at the platform level, not configured per app or per prompt.

Step 5: Configure role-based access (optional)

Not every team needs custom RBAC on day one. If you're evaluating Retool for a wider rollout, this is where the depth shows.

Resource permissions live in the resource's settings, not in the app. So, a rule you set on the Stripe resource applies to every app that uses it, not just this dashboard. Add a second internal tool on the same resource later, and it automatically inherits the same permissions.

A typical rule for this dashboard: read-only access for support agents who need to look up subscribers, read-write for billing admins who change plans and cancel.

Step 6: Deploy and share your app

The import lands on an unpublished preview branch. Publishing promotes it to the app's main version.

Upon publishing, Retool prompts you to approve the three mutating functions (changePlan, cancelSubscription, previewProration) before they can run in the live app. Approve them once, and the dashboard is fully operational.

Sharing internally

Share the app with your team, and access stays permissions-aware. Each person can see and do only what their role on the resource allows.

Broader deployment

For wider distribution, Retool's standard sharing and deployment options apply, including environment-specific deploys if you've separated dev, staging, and production.

You now have a Claude Code app running as a governed, production-deployed internal tool, imported through MCP, with no rebuild and no security code written by hand.

Can you keep editing in Claude Code after deploying?

App logic is editable in Claude Code—edit components or backend functions there, then import again through Retool’s MCP server to push updates. Resource connections and permission rules stay in Retool and persist across re-imports.

For teams that iterate frequently, put the app under source control and use Retool's source control integration to keep app changes and Retool-side configuration in sync.