Build your product, not your setup. The Next.js starter where your agent reviews itself and your app speaks MCP from day one.
landing-page.mp4
Your agent checks its own work. Eight checks run the moment your agent stops: Biome, tsc, knip, jscpd, klint, madge, line endings, and secretlint. A failure goes back to the agent with the reason attached, so you review a green diff. The same checks run again before every commit and in CI.
Architecture as code. klint turns the rules in AGENTS.md into build failures. Prisma outside the DAO, raw radix-ui, or a template-literal className fails the check instead of waiting in review.
One setup, every agent. AGENTS.md, the hooks, and the skills work in Claude Code, Cursor, Codex, and OpenCode.
Agent-ready from day one. A read-only MCP server at /api/mcp with API keys, per-key rate
limits, user scoping, and a field allowlist. You list models, Catalyst handles the rest.
Keep only what you need. bun run configure removes auth, the database, email, MCP, Redis,
storage, Stripe, or tRPC cleanly.
Next.js · Bun · Prisma · tRPC · Tailwind CSS · shadcn/ui · Better Auth · Stripe · Zod · Winston · Amazon S3 · Amazon SES · React Email · Redis · pgBouncer · Docker · GitHub Actions
Ensure that you have the following tools installed on your machine:
- Bun: Install Bun via the command line by running:
curl -fsSL https://bun.sh/install | bashor
powershell -c "irm bun.sh/install.ps1 | iex"Or if you prefer, you can use other package managers like npm, yarn, or pnpm.
Install the dependencies, then remove the features you don't need:
bun install
bun run configureCopy the .env.sample file to .env to set up the environment variables. Then, run the development server:
bun dev
# or
npm run dev
# or
yarn dev
# or
pnpm devOpen http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying src/app/(public)/page.tsx. The page auto-updates as you edit the file.
This project uses next/font to automatically optimize and load Inter, a custom Google Font.
To start the development server using Docker, run:
make build
make devIf you'd like to start the production server using Docker:
- Set
outputtostandalonein the next.config.mjs file. - Set the
AUTH_TRUST_HOSTenvironment variable totruein the.env.samplefile. - Set the
AUTH_URLenvironment variable to the URL of your application in the.env.samplefile. - Build the project:
make build - Start the production server:
make prod
The Catalyst starter kit uses Prisma to interact with the database. By default, it uses PostgreSQL as the database engine.
To set up a local database for development, you can use Docker:
docker compose up -dThis command starts a PostgreSQL database in a Docker container and lets it run in the background.
You can find the database connection URL in the .env.sample file.
You can connect to the database with the following command:
docker compose exec database psql -U app_dev -d devOr, if you have make installed, you can use the following command:
make dbThere is already a User model defined in prisma/schema.prisma. The correspondent migration file is located in prisma/migrations/. To create the database schema and generate the Prisma client, run:
bun run migrateThe Catalyst starter kit uses Better Auth for authentication. You can find the authentication logic in src/auth.ts.
By default, a development secret is already set in the .env.sample file called AUTH_SECRET. Set this secret to a more secure random string at the hosting provider of your choice when deploying the application.
If you also need Google login, add your Google OAuth client ID and secret to the .env file.
All of these environment variables have placeholders if you copied the .env.sample file.
Catalyst exposes a read-only MCP server at POST /api/mcp, so external agents can query your hosted database directly. It is stateless Streamable HTTP, so any client that speaks the protocol and can set a header connects with no stdio bridge.
Authentication is a Better Auth API key sent as Authorization: Bearer <key>. Users mint keys in settings, and each key carries its own rate limit. A throttled key gets a 429 with Retry-After, not a misleading 401.
Access is defined by an allowlist in src/lib/mcp/registry.ts: a model is invisible unless it is listed, and a column is invisible unless it appears in that model's fields. Every query is scoped to the caller's own rows inside the DAO, never by the caller, so auth tables and columns like User.password are absent by omission.
Four tools are available: list_tables, describe_table, query_table, and get_record, plus a catalyst://schema resource. Writes are deliberately absent.
The config ships one entry per environment — catalyst-dev, catalyst-stage, and
catalyst-prod — so an agent can reach any of them without swapping variables. dev defaults
to localhost:3000; stage and prod carry example.com placeholder URLs, so point them at
your real domains (or set CATALYST_MCP_URL_STAGE / CATALYST_MCP_URL_PROD) once you deploy.
To connect an agent, mint a key in that environment's settings, export it, and sync the generated config for Claude Code, Cursor, and opencode:
export CATALYST_MCP_KEY_DEV="paste-the-key-from-settings-once"
bun run mcp:syncThis project uses GitHub Actions for continuous integration and deployment. An example workflow is defined in .github/workflows/build.yml.
It installs the dependencies, lints the code, and builds the project.
The project is configured to have a robots.txt, a sitemap.xml, and a manifest.webmanifest file. However, these files cannot be found directly in the repository. Instead, you can find TypeScript files with similar names in the src/app directory. Edit them to fit your app. These files use the Metadata API from Next.js.
Set the NEXT_PUBLIC_AUTHORITY environment variable in the .env file to the domain of your application. This variable is used in the robots.txt and sitemap.xml files.
It also sets various SEO-related tags in the root layout.tsx file. Modify and extend these tags to fit your application's needs.
User uploads live in S3 under a <userId>/<fileId> key, so ownership is encoded in the key
itself. The browser PUTs bytes straight to a presigned URL — they never transit the server.
Set AWS_S3_BUCKET (plus the shared AWS_REGION / AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)
to enable storage. Without them conf.s3Configured stays false and every read operation returns
null instead of throwing, so the app runs unconfigured.
Reads go through /api/files/<key>, which checks the session user owns the
key and then redirects to a freshly signed, short-lived CloudFront URL. Clients persist that
stable path rather than a time-bound URL, so links never go stale. Signing needs
AWS_CLOUDFRONT_KEY_PAIR_ID, AWS_CLOUDFRONT_PRIVATE_KEY_BASE64, and
AWS_CLOUDFRONT_DISTRIBUTION_DOMAIN; with any of them unset the route answers 404.
Call createUploadUrls(count) from src/lib/actions/uploads.ts to
mint upload targets. The primitives themselves (putObject, getObjectBytes, getObjectSize,
deleteObject, deleteObjectsUnderPrefix, copyObject) are in
src/lib/storage/s3.ts.
The Catalyst starter kit uses Stripe for payment processing. The /api/stripe endpoint is used to receive webhook events from Stripe. To enable this endpoint, set the STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET environment variables in the .env file.
In Stripe, set the webhook URL to https://your-app-url/api/stripe.
An example event handler is already set up for the customer.subscription.updated event. This event is triggered when a subscription is updated and is used in most subscription-based applications.
Configure the endpoint to listen for the events you need. To to this, extend the logic of the switch statement with the cases for the events you want to handle.
For ease of use, we suggest not to reinvent the wheel and use the Stripe Billing Portal to allow your users to manage their subscriptions. The Catalyst starter kit already has a helper function defined in src/lib/stripe.ts to create a session for the billing portal.
The helper function can only be used on the server side and ensures that the user is authenticated before creating the session. It has a single parameter: the Stripe customerId of the user.
As the example dashboard of Catalyst can be found at /dashboard, the return URL of the billing portal is http://localhost:3000/dashboard by default. You can change this to any URL by setting the STRIPE_PORTAL_RETURN_URL environment variable in the .env file.
Catalyst uses Winston as the default logger and the default log level is info. You can change this by setting the LOG_LEVEL environment variable in the .env file.
If you want to configure a log drain, set the LOG_DRAIN_URL environment variable in the .env file. This will send the logs to the specified URL as well as to the console.
Set the GOOGLE_ANALYTICS_ID and/or GOOGLE_TAG_MANAGER_ID environment variables in the .env file to enable Google Analytics and/or Google Tag Manager.