@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.