MCP integration
LiveStore includes MCP (Model Context Protocol) integration that allows AI assistants like Claude to access LiveStore documentation, examples, and development tools.
For installation and general CLI usage, see the LiveStore CLI documentation.
What is MCP?
Section titled “What is MCP?”MCP (Model Context Protocol) is a standard for providing AI assistants with access to external resources and tools. LiveStore’s MCP server gives AI assistants access to:
- LiveStore documentation and guides
- Schema examples for common app types
- Development tools and utilities
Start the MCP server:
bunx @livestore/cli mcpAvailable commands
Section titled “Available commands”bunx @livestore/cli mcp coach
Section titled “bunx @livestore/cli mcp coach”Starts an AI coaching assistant with access to LiveStore documentation and best practices.
bunx @livestore/cli mcp tools
Section titled “bunx @livestore/cli mcp tools”Provides development tools and utilities for working with LiveStore projects.
LiveStore Runtime Tools
Section titled “LiveStore Runtime Tools”-
livestore_instance_connect-
Connects a single in-process LiveStore instance by dynamically importing a module that exports
schemaand asyncBackendfactory (and optionallysyncPayload). -
Notes:
- Only one instance can be active at a time; connecting again shuts down and replaces the previous instance.
- Reconnecting creates a fresh, in-memory client database. The visible state is populated by your backend’s initial sync. Until sync completes, queries may return empty or partial results.
-
Module contract (generic example):
import {makeWsSync } from '@livestore/sync-cf/client'const makeWsSync: (options: WsSyncOptions) => SyncBackendConstructor<SyncMetadata>Creates a sync backend that uses WebSocket to communicate with the sync backend.
export {schema } from './schema.ts'export schemaexport constsyncBackend =const syncBackend: SyncBackendConstructor<Struct.ReadonlySide<{readonly _tag: tag<"SyncMessage.SyncMetadata">;readonly createdAt: String;}, "Type">, Json>makeWsSync({function makeWsSync(options: WsSyncOptions): SyncBackendConstructor<SyncMetadata>Creates a sync backend that uses WebSocket to communicate with the sync backend.
url:WsSyncOptions.url: stringURL of the sync backend
The protocol can either
http/httpsorws/wssprocess.var process: NodeJS.Processenv.NodeJS.Process.env: NodeJS.ProcessEnvThe
process.envproperty returns an object containing the user environment. Seeenviron(7).An example of this object looks like:
{TERM: 'xterm-256color',SHELL: '/usr/local/bin/bash',USER: 'maciej',PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',PWD: '/Users/maciej',EDITOR: 'vim',SHLVL: '1',HOME: '/Users/maciej',LOGNAME: 'maciej',_: '/usr/local/bin/node'}It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other
Workerthreads. In other words, the following example would not work:node -e 'process.env.foo = "bar"' && echo $fooWhile the following will:
import { env } from 'node:process';env.foo = 'bar';console.log(env.foo);Assigning a property on
process.envwill implicitly convert the value to a string. This behavior is deprecated. Future versions of Node.js may throw an error when the value is not a string, number, or boolean.import { env } from 'node:process';env.test = null;console.log(env.test);// => 'null'env.test = undefined;console.log(env.test);// => 'undefined'Use
deleteto delete a property fromprocess.env.import { env } from 'node:process';env.TEST = 1;delete env.TEST;console.log(env.TEST);// => undefinedOn Windows operating systems, environment variables are case-insensitive.
import { env } from 'node:process';env.TEST = 1;console.log(env.test);// => 1Unless explicitly specified when creating a
Workerinstance, eachWorkerthread has its own copy ofprocess.env, based on its parent thread'sprocess.env, or whatever was specified as theenvoption to theWorkerconstructor. Changes toprocess.envwill not be visible acrossWorkerthreads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy ofprocess.envon aWorkerinstance operates in a case-sensitive manner unlike the main thread.LIVESTORE_SYNC_URL ?? 'ws://localhost:8787' })string | undefinedexport constsyncPayload = {const syncPayload: {authToken: string;}authToken:authToken: stringprocess.var process: NodeJS.Processenv.NodeJS.Process.env: NodeJS.ProcessEnvThe
process.envproperty returns an object containing the user environment. Seeenviron(7).An example of this object looks like:
{TERM: 'xterm-256color',SHELL: '/usr/local/bin/bash',USER: 'maciej',PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',PWD: '/Users/maciej',EDITOR: 'vim',SHLVL: '1',HOME: '/Users/maciej',LOGNAME: 'maciej',_: '/usr/local/bin/node'}It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other
Workerthreads. In other words, the following example would not work:node -e 'process.env.foo = "bar"' && echo $fooWhile the following will:
import { env } from 'node:process';env.foo = 'bar';console.log(env.foo);Assigning a property on
process.envwill implicitly convert the value to a string. This behavior is deprecated. Future versions of Node.js may throw an error when the value is not a string, number, or boolean.import { env } from 'node:process';env.test = null;console.log(env.test);// => 'null'env.test = undefined;console.log(env.test);// => 'undefined'Use
deleteto delete a property fromprocess.env.import { env } from 'node:process';env.TEST = 1;delete env.TEST;console.log(env.TEST);// => undefinedOn Windows operating systems, environment variables are case-insensitive.
import { env } from 'node:process';env.TEST = 1;console.log(env.test);// => 1Unless explicitly specified when creating a
Workerinstance, eachWorkerthread has its own copy ofprocess.env, based on its parent thread'sprocess.env, or whatever was specified as theenvoption to theWorkerconstructor. Changes toprocess.envwill not be visible acrossWorkerthreads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy ofprocess.envon aWorkerinstance operates in a case-sensitive manner unlike the main thread.LIVESTORE_SYNC_AUTH_TOKEN ?? 'insecure-token-change-me' }string | undefinedimport {Events,import EventsmakeSchema,const makeSchema: <TInputSchema extends InputSchema>(inputSchema: TInputSchema) => FromInputSchema.DeriveSchema<TInputSchema>Schema,import SchemaState } from '@livestore/livestore'import Stateconstevents = {const events: {entityCreated: State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>;}entityCreated:entityCreated: State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>Events.import Eventssynced({synced<"v1.EntityCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>(args: {name: "v1.EntityCreated";schema: Schema.Codec<Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<...>, never, never>;} & Omit<...>): State.SQLite.EventDef<...>export syncedCreates a synced event definition.
Synced events are sent to the sync backend and distributed to all connected clients. Use this for collaborative data that should be shared across users and devices.
Event names should be versioned (e.g.,
v1.TodoCreated) to support schema evolution over time.name: 'v1.EntityCreated',name: "v1.EntityCreated"schema:schema: Schema.Codec<Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">, never, never>Schema.import SchemaStruct({function Struct<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}>(fields: {readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}): Schema.Struct<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}>Defines a struct schema from a map of field schemas.
Details
Each field value is a schema. Use
optionalKey
or
optional
to mark fields as optional, and
mutableKey
to mark them as mutable.
The resulting schema's
Typeis a readonly object type with the fields' decoded types. TheEncodedform mirrors the field schemas' encoded types.Example (Defining a basic struct)
import { Schema } from "effect"const Person = Schema.Struct({name: Schema.String,age: Schema.Number,email: Schema.optionalKey(Schema.String)})// { readonly name: string; readonly age: number; readonly email?: string }type Person = typeof Person.Typeconst alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })console.log(alice)// { name: 'Alice', age: 30 }id:id: Schema.StringSchema.import SchemaString,const String: Schema.StringType-level representation of
String
.
Schema for
stringvalues. Validates that the input istypeof"string".title:title: Schema.StringSchema.import SchemaString,const String: Schema.StringType-level representation of
String
.
Schema for
stringvalues. Validates that the input istypeof"string".createdAt:createdAt: Schema.DateFromStringSchema.import SchemaDateFromString.const DateFromString: Schema.DateFromStringType-level representation of
DateFromString
.
Schema that decodes a string into a JavaScript
Date.When to use
Use to model string-encoded dates that decode to JavaScript
Dateobjects and encode back to strings.Details
Decoding: The string is passed to JavaScript
Dateconstruction.Encoding: A valid
Dateis encoded as an ISO string; an invalidDateis encoded as"Invalid Date".Gotchas
Invalid date strings can decode to invalid
Dateinstances.check(Bottom<unknown, unknown, unknown, unknown, Declaration, decodeTo<Date, String, never, never>, unknown, unknown, readonly [], unknown, "readonly", "required", "no-default", "readonly", "required">.check(checks_0: Check<Date>, ...checks: Check<Date>[]): Schema.DateFromStringSchema.import SchemaisDateValid()),function isDateValid(annotations?: Schema.Annotations.Filter): Filter<globalThis.Date>Validates that a Date object represents a valid date (not an invalid date like
new Date("invalid")).Details
JSON Schema:
This check does not have a direct JSON Schema equivalent, as JSON Schema validates date strings, not Date objects.
Arbitrary:
When generating test data with fast-check, this applies a
valid: trueconstraint to ensure generated Date objects are valid.}),}),}consttables = {const tables: {entities: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"entities", {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};readonly createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;}entities:entities: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"entities", {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};readonly createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>State.import StateSQLite.import SQLitetable({function table<"entities", {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};readonly createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};}, Partial<...>>(args: {...;} & Partial<...>): State.SQLite.TableDef<...> (+2 overloads)Creates a SQLite table definition from columns or an Effect Schema.
This function supports two main ways to define a table:
- Using explicit column definitions
- Using an Effect Schema (either the
nameproperty needs to be provided or the schema needs to have a title/identifier)
// Using explicit columnsconst usersTable = State.SQLite.table({name: 'users',columns: {id: State.SQLite.text({ primaryKey: true }),name: State.SQLite.text({ nullable: false }),email: State.SQLite.text({ nullable: false }),age: State.SQLite.integer({ nullable: true }),},})// Using Effect Schema with annotationsimport { Schema } from '@livestore/utils/effect'const UserSchema = Schema.Struct({id: Schema.Int.pipe(State.SQLite.withPrimaryKey).pipe(State.SQLite.withAutoIncrement),email: Schema.String.pipe(State.SQLite.withUnique),name: Schema.String,active: Schema.Boolean.pipe(State.SQLite.withDefault(true)),createdAt: Schema.optional(Schema.Date),})// Option 1: With explicit nameconst usersTable = State.SQLite.table({name: 'users',schema: UserSchema,})// Option 2: With name from schema annotation (title or identifier)const AnnotatedUserSchema = UserSchema.annotate({ title: 'users' })const usersTable2 = State.SQLite.table({schema: AnnotatedUserSchema,})// Adding indexesconst PostSchema = Schema.Struct({id: Schema.String.pipe(State.SQLite.withPrimaryKey),title: Schema.String,authorId: Schema.String,createdAt: Schema.Date,}).annotate({ identifier: 'posts' })const postsTable = State.SQLite.table({schema: PostSchema,indexes: [{ name: 'idx_posts_author', columns: ['authorId'] },{ name: 'idx_posts_created', columns: ['createdAt'], isUnique: false },],})name: 'entities',name: "entities"columns: {columns: {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};readonly createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};}id:id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;}State.import StateSQLite.import SQLitetext({const text: <string, string, false, typeof NoDefault, true, false>(args: {schema?: Schema.Codec<string, string, never, never>;default?: typeof NoDefault;nullable?: false;primaryKey?: true;autoIncrement?: false;}) => {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;} (+1 overload)primaryKey: true }),primaryKey?: truetitle:title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;}State.import StateSQLite.import SQLitetext({const text: <string, string, false, "", false, false>(args: {schema?: Schema.Codec<string, string, never, never>;default?: "";nullable?: false;primaryKey?: false;autoIncrement?: false;}) => {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;} (+1 overload)default: '' }),default?: ""createdAt:createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;}State.import StateSQLite.import SQLitetext({const text: <string, string, false, "", false, false>(args: {schema?: Schema.Codec<string, string, never, never>;default?: "";nullable?: false;primaryKey?: false;autoIncrement?: false;}) => {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;} (+1 overload)default: '' }),default?: ""},}),}constmaterializers =const materializers: {"v1.EntityCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>>;}State.import StateSQLite.import SQLitematerializers(const materializers: <{entityCreated: State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>;}>(_eventDefRecord: {entityCreated: State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>;}, handlers: {...;}) => {...;}Builder function for creating a type-safe materializer map.
This is the primary way to define materializers in LiveStore. It ensures:
- Every non-derived event has a corresponding materializer
- Materializer argument types match their event schemas
- Derived events are excluded from the required handlers
events, {const events: {entityCreated: State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>;}'v1.EntityCreated': ({id,id: stringtitle,title: stringcreatedAt }) =>createdAt: Datetables.const tables: {entities: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"entities", {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};readonly createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;}entities.entities: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"entities", {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};readonly createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>insert({insert: (values: {readonly id: string;readonly title?: string;readonly createdAt?: string;}) => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{readonly id: Schema.Codec<string, string, never, never>;readonly title: Schema.Codec<string, string, never, never>;readonly createdAt: Schema.Codec<string, string, never, never>;}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"entities", {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {...;};readonly createdAt: {...;};}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">Insert a new row into the table.
id,id: stringtitle,title?: stringcreatedAt:createdAt?: stringcreatedAt.createdAt: DatetoISOString() }),Date.toISOString(): stringReturns a date as a string value in ISO format.
})conststate =const state: InternalStateState.import StateSQLite.import SQLitemakeState({const makeState: <{tables: {entities: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"entities", {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};readonly createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;};materializers: {...;};}>(inputSchema: {tables: {entities: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"entities", {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};readonly createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;};materializers: {...;};}) => InternalStatetables,tables: {entities: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"entities", {readonly id: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: None<never>;nullable: false;primaryKey: true;autoIncrement: false;};readonly title: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};readonly createdAt: {columnType: "text";schema: Schema.Codec<string, string, never, never>;default: Some<"">;nullable: false;primaryKey: false;autoIncrement: false;};}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;}materializers })materializers: {"v1.EntityCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>>;}export constschema =const schema: FromInputSchema.DeriveSchema<{events: {entityCreated: State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>;};state: InternalState;}>makeSchema({makeSchema<{events: {entityCreated: State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>;};state: InternalState;}>(inputSchema: {events: {entityCreated: State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>;};state: InternalState;}): FromInputSchema.DeriveSchema<...>events,events: {entityCreated: State.SQLite.EventDef<"v1.EntityCreated", Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Type">, Schema.Struct.ReadonlySide<{readonly id: Schema.String;readonly title: Schema.String;readonly createdAt: Schema.DateFromString;}, "Encoded">>;}state })state: InternalState -
Params example:
{ "configPath": "livestore-cli.config.ts", "storeId": "<store-id>" } -
Returns example:
{ "storeId": "<store-id>", "clientId": "client-123", "sessionId": "session-abc", "schemaInfo": { "tableNames": ["..."], "eventNames": ["..."] } }
-
-
livestore_instance_query- Executes raw SQL against the client database (read-only).
- Notes:
- SQLite dialect; use valid SQLite syntax.
bindValuesmust be an array (positional?) or a record (named$key). Do not pass stringified JSON.
- Params example (positional):
{ "sql": "SELECT * FROM my_table WHERE userId = ?", "bindValues": ["u1"] } - Params example (named):
{ "sql": "SELECT * FROM my_table WHERE userId = $userId", "bindValues": { "userId": "u1" } } - Returns example:
{ "rows": [{ "col": "value" }], "rowCount": 1 }
-
livestore_instance_commit_events- Commits one or more events defined by your connected schema.
- Notes:
- Use the canonical event name declared in your schema (e.g.,
v1.EntityCreated). argsmust be a non-stringified JSON object matching the event schema. Date fields typically accept ISO 8601 strings.
- Use the canonical event name declared in your schema (e.g.,
- Params example:
{ "events": [{ "name": "v1.EntityCreated", "args": { "id": "e1", "title": "Hello", "createdAt": "2024-01-01T00:00:00.000Z" } }] } - Returns example:
{ "committed": 1 }
-
livestore_instance_status- Reports instance/runtime info.
- Returns example (connected):
{ "_tag": "connected", "storeId": "<store-id>", "clientId": "client-123", "sessionId": "session-abc", "tableCounts": { "my_table": 12 } } - Returns example (not connected):
{ "_tag": "disconnected" }
-
livestore_instance_disconnect- Disconnects the current LiveStore instance and releases resources.
- Returns:
{ "_tag": "disconnected" }
Sync export/import tools
Section titled “Sync export/import tools”These tools connect directly to the sync backend (without creating a full LiveStore instance) to export or import events. Useful for backup, migration, and debugging.
-
livestore_sync_export- Exports all events from a sync backend to JSON data.
- Notes:
- Connects directly to the sync backend and pulls all events.
- Returns the export data as a JSON object that can be saved or passed to import.
- Params example:
{ "configPath": "livestore-cli.config.ts", "storeId": "my-store" } - Returns example:
{ "storeId": "my-store", "eventCount": 127, "exportedAt": "2024-01-15T10:30:00.000Z", "data": { "version": 1, "storeId": "my-store", "events": [...] } }
-
livestore_sync_import- Imports events from export data to a sync backend.
- Notes:
- The sync backend must be empty before importing.
- Use
force: trueto import even if the store ID in the data doesn’t match. - Use
dryRun: trueto validate the import without actually importing.
- Params example:
{ "configPath": "livestore-cli.config.ts", "storeId": "my-store", "data": { "version": 1, "storeId": "my-store", "events": [...] } } - Params with options:
{ "configPath": "...", "storeId": "...", "data": {...}, "force": true, "dryRun": true } - Returns example:
{ "storeId": "my-store", "eventCount": 127, "dryRun": false }
Local Cloudflare sync (dev)
Section titled “Local Cloudflare sync (dev)”Run a local Cloudflare sync backend:
-
Start the sync worker (wrangler):
cd tests/integration/src/tests/adapter-cloudflare/fixtureswrangler dev- You should see an info page at
http://localhost:8787/.
-
Start the MCP server in another terminal:
bunx @livestore/cli mcp server
-
From your MCP client (e.g., Claude Desktop), call tools:
- Use your own config file path and storeId. The contrib repo provides an example:
examples/cf-chat/livestore-cli.config.ts. - Connect:
livestore_instance_connectwith{ "configPath": "livestore-cli.config.ts", "storeId": "<store-id>" } - Commit:
livestore_instance_commit_eventswith[ { "name": "v1.EntityCreated", "args": { "id": "e1", "title": "Hello", "createdAt": "2024-01-01T00:00:00.000Z" } } ] - Query:
livestore_instance_querywith{ "sql": "SELECT * FROM my_table ORDER BY createdAt DESC LIMIT 5" } - Status:
livestore_instance_status - Disconnect:
livestore_instance_disconnect
- Use your own config file path and storeId. The contrib repo provides an example:
Adding to Claude
Section titled “Adding to Claude”To use with Claude Desktop, add the MCP server to your Claude configuration:
{ "mcpServers": { "livestore": { "command": "bunx", "args": ["@livestore/cli", "mcp"] } }}Available resources
Section titled “Available resources”The MCP server provides access to:
- Documentation: Overview, features, getting started guides
- Architecture: Technical design and principles
- Schema Examples: Pre-built schemas for todo, blog, e-commerce, and social apps
- Development Tools: Project scaffolding and utilities
This enables AI assistants to provide context-aware help with LiveStore development.