# Project structure

> What helix init scaffolds and why: helix.config.ts, file-based routing under functions/, the optional app/ front end, and .mcp.json.

A Helix project is a small tree of ordinary TypeScript: one config file, one database schema, a `functions/` directory where the file path is the URL path, and an optional React app. `helix init` scaffolds all of it, and the layout is the interface: the CLI, the SDK, and Claude Code all read the same files.

## The tree

```
my-app/
├── helix.config.ts          # Project config: name, projectId, workspaceId, auth aliases
├── package.json             # @trayai/helix-sdk, zod, typescript
├── tsconfig.json
├── .mcp.json                # MCP server config, auto-discovered by Claude Code
│
├── functions/               # Functions, file-based routing
│   ├── health.get.ts        # GET /health
│   ├── api/
│   │   └── v1/
│   │       ├── users.get.ts             # GET /api/v1/users
│   │       ├── users.post.ts            # POST /api/v1/users
│   │       └── users/
│   │           └── [userId]/
│   │               └── index.get.ts     # GET /api/v1/users/:userId
│   └── _shared/             # Shared utilities (not routable)
│
└── app/                     # Vite + React app, when you pass --with-app
    ├── index.html
    ├── vite.config.ts
    └── src/
        ├── main.tsx
        └── App.tsx
```

:::note{title="The exact scaffold is being confirmed"}
Helix hasn't published a full scaffold reference, so treat this layout as indicative and check what `helix init` writes in your version. HTTP is the only trigger type today, so a scaffolded project has no directories for scheduled functions or queue consumers.
:::

## helix.config.ts

The single source of truth for project-level settings, read by the CLI at build time, the dev server at startup, and the SDK at runtime. One config file describes exactly one target: one workspace, one project, one set of auth aliases.

```typescript
// helix.config.ts
import { defineConfig } from '@trayai/helix-sdk';

export default defineConfig({
  name: 'my-app',
  projectId: 'a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d',
  workspaceId: 'f8e7d6c5-4b3a-2190-8765-abcdef012345',
  authentications: {
    salesforce_prod: '1a0fbf5c-2c9e-4aa1-ada3-ccbba65019ab',
  },
});
```

- `name` is the project name. The first deploy uses it when it provisions the project on the platform.
- `workspaceId` ties the project to a Tray workspace and determines which authentications are available. It's used both locally and on deploy.
- `projectId` names the deploy target: the production URL subdomain and the platform resources a deploy provisions. Required to deploy, not used by `helix dev`.
- `authentications` maps readable aliases to credential records stored on Helix's servers, used as `ctx.http.authed('alias')` in function code.

The CLI writes to this file for you: `helix project set <id>`, `helix workspace set <id>` or `helix workspace select`, and `helix env set <environment>`. The [configuration concept](/documentation/concepts/configuration/) covers the full shape.

## functions/

Every file is a function, and the file system is the router: the file path becomes the URL path, and the method suffix sets the HTTP verb. `functions/api/v1/users.get.ts` handles `GET /api/v1/users`. Square brackets make dynamic segments: `users/[userId]/index.get.ts` handles `GET /api/v1/users/:userId`.

Directories starting with `_` are never routable. `_shared/` holds utilities other functions import.

HTTP is the only trigger type today, so every function here answers a request. See [functions and routing](/documentation/guides/functions-and-routing/) for handlers and input validation.

Functions reach the platform through `ctx`: the project-scoped [key-value store](/documentation/guides/key-value-store/) via `ctx.kv()`, authenticated third-party calls via [aliases](/documentation/guides/connected-services/), and logging via `ctx.log`.

:::roadmap{title="Not part of the scaffold today"}
A `db/schema.ts` Drizzle schema, `drizzle.config.ts`, `_scheduled/` and `_queues/` directories, and `_middleware.ts` all belong to the platform design. Helix ships no managed database, queues, or non-HTTP triggers, so `helix init` does not create them. See [limits and roadmap](/documentation/reference/limits/).
:::

## app/

An optional Vite + React single-page app, scaffolded with `helix init --with-app`. It's a standard Vite project: `helix dev` serves it with hot module reload, and a deploy publishes it to the CDN alongside your functions. Frontend and functions share one origin, so there's no CORS to configure. When a URL matches both, the function wins over the SPA's client-side route; server routes are explicit and predictable.

## .mcp.json

The MCP server configuration Claude Code reads when you start it in the project folder. It points the agent at the CLI's MCP server, which is how Claude understands the project and runs commands for you. The first Claude session asks you to trust the folder and allow the server; both are required for the workflow.

---

Canonical: https://helix.tray.ai/documentation/getting-started/project-structure/
Any link on this page is available as markdown by appending .md to its URL.
Full corpus: https://helix.tray.ai/documentation/llms-full.txt