Deco
Full-Code Guides

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

  1. Define tool in server/tools/
  2. Run npm run gen:self to generate types
  3. Call client.tools.YOUR_TOOL(...) from React
  4. 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