Project Structure
Understand the full-stack TypeScript architecture of deco applications
Deco CMS apps are full-stack TypeScript monorepos. Backend (MCP server) and frontend (React app) share types, deploy together, and communicate via typed RPC, no REST APIs needed.
One codebase, one deploy: Backend changes instantly available in frontend with full type safety.
Directory Structure
my-ai-app/
├── package.json # Root workspace scripts (dev, gen, deploy)
├── tsconfig.json # Shared TypeScript configuration
│
├── server/ # Backend: MCP Server (Cloudflare Worker)
│ ├── main.ts # Server entry point
│ ├── deco.gen.ts # Auto-generated types (never edit)
│ │
│ ├── tools/ # Tool definitions by domain
│ │ ├── index.ts # Central export for all tools
│ │ ├── todos.ts # Example: Todo CRUD tools
│ │ └── user.ts # Example: User management tools
│ │
│ ├── schema.ts # Database schema (Drizzle ORM + SQLite)
│ ├── db.ts # Database connection helper
│ ├── drizzle.config.ts # Drizzle configuration
│ ├── drizzle/ # Database migration files
│ └── wrangler.toml # Cloudflare Workers config
│
└── view/ # Frontend: React App (Vite + TanStack Router)
├── src/
│ ├── main.tsx # App entry point with router
│ │
│ ├── routes/ # Page components (TanStack Router)
│ │ └── home.tsx # Example: Homepage route
│ │
│ ├── components/ # Reusable UI components
│ │ ├── ui/ # shadcn/ui design system
│ │ └── ... # Custom components
│ │
│ ├── hooks/ # Custom React hooks
│ │ └── useToolCalls.ts # Example: Tool call wrapper
│ │
│ ├── lib/ # Core utilities
│ │ ├── rpc.ts # Typed RPC client (calls backend)
│ │ └── utils.ts # General utilities
│ │
│ └── styles.css # Tailwind CSS styles
│
├── public/ # Static assets (images, fonts)
├── vite.config.ts # Vite bundler configuration
├── components.json # shadcn/ui configuration
└── package.json # Frontend dependencies
Development Flow
- Define tool in
server/tools/ - Run
npm run gen:selfto generate types - Call
client.tools.YOUR_TOOL(...)from React - TypeScript enforces correctness at compile time
No REST APIs, no version mismatches, no integration code.
Backend ( /server )
Your Cloudflare Worker serves:
/mcp- MCP server for AI agents/rpc- Typed RPC for your React UI/- Static assets (built React app)
main.ts
import { withRuntime } from "@deco/workers-runtime";
import { StateSchema } from "./deco.gen.ts";
import { tools } from "./tools/";
import { workflows } from "./workflows/";
import { views } from "./views";
const runtime = withRuntime<Env, typeof StateSchema>({
oauth: {
scopes: ["AI_GENERATE", "AI_GENERATE_OBJECT"],
state: StateSchema,
},
tools,
workflows,
views,
fetch: fallbackToView("/"), // Serves React assets
});
export const Workflow = runtime.Workflow;
export default runtime;
OAuth Scopes: Request permissions for AI generation capabilities.
State Schema: Configuration form users fill when installing your app. Extend with custom fields for API keys, environment settings, etc.
tools/
Organize tools by domain:
tools/
├── index.ts # Export all tools
├── customers.ts # Customer-related tools
└── billing.ts # Billing-related tools
// tools/index.ts
export { customerTools } from "./customers";
export { billingTools } from "./billing";
export const tools = [
...customerTools,
...billingTools,
];
See Building Tools for details.
workflows/
Organize workflows by domain:
// workflows/index.ts
export const workflows = [
// Add workflow creators here
];
views.ts
External React apps that integrate with your MCP:
export const views = () => [
{
title: "My App",
icon: "dashboard",
url: "https://my-app.deco.page",
},
];
deco.gen.ts
Auto-generated types. Never edit manually.
npm run gen # Regenerate after adding integrations
npm run gen:self # Generate types for your tools
schema.ts and db.ts
Database schema (Drizzle ORM + SQLite):
// schema.ts
export const customers = sqliteTable("customers", {
id: integer("id").primaryKey(),
name: text("name"),
email: text("email"),
});
// db.ts
export const getDb = (env: Env) => getDatabase(env, schema);
wrangler.toml
Cloudflare Workers configuration. Auto-updated when adding integrations.
Frontend ( /view )
React 19 + Tailwind v4 + TanStack Router.
view/src/
├── main.tsx # Entry point
├── routes/ # Pages
├── components/ui/ # UI components
├── lib/rpc.ts # Typed RPC client
└── styles.css # Tailwind styles
RPC Client
The rpc.ts client is auto-generated from your backend tools:
// view/src/lib/rpc.ts
import { createClient } from "@deco/workers-runtime/client";
import type { Env } from "../../server/deco.gen.ts";
export const client = createClient<Env["SELF"]>();
Usage in React:
import { client } from "./lib/rpc";
const result = await client.tools.EMAIL_SEND({
to: "user@example.com",
subject: "Welcome",
body: "Thanks for signing up",
});
TypeScript provides autocomplete and type checking for all tool inputs/outputs.
See Building Views for details.
Found an error or want to improve this page?
Edit this page