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
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.
// 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',
},
});
nameis the project name. The first deploy uses it when it provisions the project on the platform.workspaceIdties the project to a Tray workspace and determines which authentications are available. It’s used both locally and on deploy.projectIdnames the deploy target: the production URL subdomain and the platform resources a deploy provisions. Required to deploy, not used byhelix dev.authenticationsmaps readable aliases to credential records stored on Helix’s servers, used asctx.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 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 for handlers and input validation.
Functions reach the platform through ctx: the project-scoped key-value store via ctx.kv(), authenticated third-party calls via aliases, and logging via ctx.log.
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.