Skip to main content

@happyvertical/smrt-template-sveltekit

The minimal “learn and build from the ground up” SvelteKit template for s-m-r-t. The package exports copyTemplate(), getTemplatePath(), and templateInfo and is the sveltekit template used by smrt gnode create.

1. Install and run​

Scaffold through the public CLI:

node --input-type=module -e "import { copyTemplate } from './packages/template-sveltekit/index.js'; copyTemplate('./my-app', { name: 'my-app' })"
cd my-app
pnpm install
cp .env.example .env
pnpm app:install

Run the copy command from the monorepo root. smrt gnode create --template=sveltekit uses the same copyTemplate() export.

Generated projects require Node.js 26.0.0 or newer and pnpm 11.25.0. They pin all directly used @happyvertical/smrt-* packages to the current monorepo release, including opt-in MCP Apps dependencies (synced at publish by scripts/sync-template-versions.mjs; --check detects drift).

smrt gnode create <name> --mcp-apps supports installed, local, and Git subdirectory templates. Plugin names must satisfy the pinned Agent Plugins 1.0.0 schema before generation starts. smrt mcp-apps validate applies the official closed plugin/MCP schemas offline, then s-m-r-t endpoint and credential checks. Every existing scoped registry directive must be canonical; inline # and ; comments are supported. Hosted MCP caches its successfully created authenticator for the process, preserving JWKS keys across requests while principal resolution and task authorization still run for each request.

For programmatic scaffolding:

import { copyTemplate } from '@happyvertical/smrt-template-sveltekit';

copyTemplate('/absolute/path/to/my-app', {
name: 'my-app',
overwrite: false,
});

copyTemplate() turns an unscoped project name such as my-app into the runtime-safe package identity @smrt-app/my-app; explicitly scoped package names are preserved. It also excludes package-internal .svelte-kit and test fixtures.

2. Understand the generated files​

The checked-in template contains authored objects, one server module (src/lib/server/smrt.ts, the runtime and its options), thin hooks and routes that mount package handlers, pages, and configuration. It ships no operator scripts. Vite generates .smrt/manifest.json, .smrt/smrt-knowledge.json, .smrt/register.js, src/lib/server/smrt-register.ts, src/lib/types/smrt-generated/, and src/routes/api/**/+server.ts. All are ignored and excluded from scaffold fixtures.

vite.config.ts is plugins: [sveltekit(), smrt()]: the @happyvertical/smrt-core/vite preset applies the decorator transform, scans the local object directory, generates local routes and types, and consumes the packages smrt.config.ts lists under consumer.packages (profiles, tenancy, users). @happyvertical/smrt-cli is a direct dev dependency because every lifecycle script is smrt app <operation>.

3. Define the first object​

The generated src/lib/objects/Note.ts is one required-tenant object with title and body fields. Its small explicit NoteCollection constructor keeps generated CLI/MCP runtime commands constructible. Its @smrt() configuration exposes CRUD to REST, MCP, WebMCP definitions, and CLI while using an API writable allowlist.

Add objects beside Note and export them from src/lib/objects/index.ts. vite.config.ts already scans that directory.

4. Initialize or migrate the database​

Generated local projects use SQLite in the current user's operating-system application-data directory by default:

pnpm db:migrate

The script builds first to refresh manifests, registrations, types, and API routes, then runs smrt app migrate, which holds the operation lock and runs smrt db:migrate. The deprecated smrt db:setup command is not documented or shipped.

5. Understand tenant context​

src/hooks.server.ts mounts createSmrtSvelteKitRuntime() from @happyvertical/smrt-app-runtime/sveltekit. It resolves a URL tenant candidate into separate selectedTenantId/selectedTenantSlug locals and does not establish query context from that selection. The session step runs afterward and only its membership-authorized tenant enters the s-m-r-t tenancy context.

Untrusted tenant headers are ignored. Applications may pass selectTenant for a path, signed-cookie, or trusted-gateway selector, but selection must remain separate from authorization and session switches must use switchSessionTenant().

6. Understand users, profiles, memberships, roles, and permissions​

The generated project consumes the current profiles and users manifests and loads the signed session through the runtime, which enters tenant context only for a verified active membership. A User is the auth identity, Profile is person-facing metadata, Membership joins a User to a Tenant and Role, Role maps to Permission records, and Session publishes the resolved permission snapshot.

The home-page form action calls assertOperationPermission() with that exact snapshot. Provider-specific login and provisioning remain app-owned extension points.

7. Load data into a SvelteKit page​

The home page loads Note rows in +page.server.ts, returns plain serialized data, and declares depends('smrt:notes'). The Svelte 5 page reads hydrated $props() data and calls invalidate('smrt:notes') after a successful form mutation. It does not fetch initial data in onMount or $effect.

8. Use generated REST, MCP, WebMCP, and CLI interfaces​

REST routes are generated under src/routes/api. MCP and WebMCP descriptors come from the same action metadata. CLI commands are available through the declared dev dependency:

pnpm smrt objects
pnpm smrt schema Note
pnpm smrt generate-mcp --no-config --no-readme

The root layout wires the generated webMcpToolDefinitions into the Provider, so each browser page exposes only read-effect generated model tools by default. Write and destructive effects require explicit page-owned opt-in, and all custom actions execute through their authenticated generated REST routes. For a smaller page-specific surface, pass a filtered definitions array or use the framework-agnostic registerWebMcpTools() API from @happyvertical/smrt-web. The CLI's manifest-only objects and schema commands work in this source-first template. Local-object CRUD execution through the generic CLI additionally requires a compiled JavaScript project entry point.

9. Add optional live browser data​

@happyvertical/smrt-web is included on the synchronized release line because the root Provider loads the generated WebMCP definitions. No separate install is needed for WebMCP. Live browser collections remain opt-in per page; import the collection helpers only on pages that need interactive client data.

The generated-project README shows the current hydration pattern: createSmrtCollection(getCollectionDefinition('notes'), { initialData, basePath: '/api' }) followed by liveCollection(). The root layout's read-only WebMCP registration does not materialize live collections.

10. Graduate to smrt-start​

Use this template for learning and focused, ground-up applications. Choose smrt-start, the canonical application template, when you want a production-shaped baseline. This package intentionally avoids becoming a second production starter.

The generated project has one canonical runtime.profile and deterministic app:* operations. Local state stays outside source. The production baseline uses adapter-node and includes a container plus separate task/schedule workers; Compose requires operator-supplied database secrets, and cloud examples describe provider composition without pretending to provision it. Install/start/stop/recover are local-profile commands; deployed web processes run the production Node build or container directly.

Every supported local web entry point (app:start or pnpm dev) shares one writer lease outside the source checkout. Direct production startup requires an explicit loopback HOST; prefer app:start. Filesystem backup and logical import fail closed while a writer is alive; logical export reads every model table from one transaction snapshot and reports that uploaded assets are excluded. The production image retains the generated manifest and operator CLI required by doctor/export/import.