Skip to content
Backend
Skill

/csharp-client

SpacetimeDB C#/.NET client SDK reference. Use when building C# clients that connect to SpacetimeDB (console, desktop, or any .NET app).

From plugin
spacetimedb
26k11 skills1 MCP

Claude Code

$ npx -y skills add clockworklabs/spacetimedb --skill csharp-client --agent claude-code

SKILL.md

csharp-client.SKILL.md
name: csharp-client
description: SpacetimeDB C#/.NET client SDK reference. Use when building C# clients that connect to SpacetimeDB (console, desktop, or any .NET app).
license: Apache-2.0
metadata:
  author: clockworklabs
  version: "2.0"
  role: client
  language: csharp
  cursor_globs: "**/*.cs"
  cursor_always_apply: true

SpacetimeDB C# Client

Install: `dotnet add package SpacetimeDB.ClientSDK`

Generated bindings convert snake_case names to PascalCase, including row fields: a server column `trip_id` is `TripId` on client rows.

Connection

using SpacetimeDB;
using SpacetimeDB.Types;

var conn = DbConnection.Builder()
    .WithUri("http://localhost:3000")
    .WithDatabaseName("my-database")
    .WithToken(savedToken)
    .WithCompression(Compression.Brotli) // optional; this is the default
    .OnConnect((conn, identity, token) =>
    {
        Console.WriteLine($"Connected as: {identity}");
        // Save token for reconnection
        File.WriteAllText("auth_token.txt", token);

        conn.SubscriptionBuilder()
            .OnApplied(OnSubscriptionApplied)
            .SubscribeToAllTables();
    })
    .OnConnectError(err => Console.Error.WriteLine($"Connection failed: {err}"))
    .OnDisconnect((conn, err) =>
    {
        if (err != null) Console.Error.WriteLine($"Disconnected: {err}");
    })
    .Build();

Compression options are `Compression.Brotli`, `Compression.Gzip`, and `Compression.None`. The SDK uses Brotli when `WithCompression` is omitted.

Automatic Reconnect and Token Refresh

Automatic reconnect is opt-in: add `.WithAutomaticReconnect()` to the builder. Initial connection failures do not retry. After an established connection is lost, the SDK retries with exponential backoff and jitter from `MinDelay` (default 1 s) up to `MaxDelay` (default 30 s); tune them with `.WithAutomaticReconnect(new AutomaticReconnectOptions { MinDelay = ..., MaxDelay = ... })`. Values below the 500 ms and 1 s floors are raised with a warning, so that retrying clients cannot overwhelm the database. `Disconnect()` permanently stops recovery.

Keep calling `FrameTick()` while `IsActive` is false. `IsReconnecting` is true while recovering before the next handshake. Use `.OnDisconnect((conn, error, next) => ...)` and `.OnConnectError((error, next) => ...)` to inspect `NextReconnect?`: a non-null value provides `Attempt` and `Delay`; null means no retry is scheduled. Existing callback overloads still work.

`OnConnect` runs on every successful reconnect, so create the subscription in the example above only on the first connection, or move it after `Build()`. Register row callbacks once as well. The same connection, identity, table handles, and subscriptions survive; each attempt gets a fresh `ConnectionId`. Cached rows stay readable but stale during outages. The SDK replays subscriptions in one batch and emits only net row changes. Subscription `OnApplied` runs again after replay; use it to mark data ready, not for repeated one-time setup.

For expiring credentials, combine `.WithToken(initialToken)` with `.WithTokenProvider(() => RefreshTokenAsync())`, where your provider returns `Task<string>` for the same identity. The provider is not called for the initial connection. Before retries, it is called when remaining validity is at most 30 seconds or 5% of the token lifetime, whichever is greater, when expiry cannot be read, or after a reused token is rejected. Provider failures retry; rejection of a freshly provided token is terminal. No periodic refresh runs while connected. Disconnecting ignores a pending provider result but does not cancel the provider's own work.

Calls made while disconnected fail immediately. Pending reducer calls receive `Status.UnknownResult`; pending procedures and one-off queries fail with `UnknownResultException`. These calls may already have executed and are never replayed. Regenerate bindings so unhandled unknown reducer outcomes reach `OnUnhandledReducerError`.

Event Loop (Critical)

**`FrameTick()` must be called in your main loop.** The SDK queues all network messages and only processes them when you call `FrameTick()`. Without it, no callbacks fire.

while (running)
{
    conn.FrameTick();
    // Your application logic...
    Thread.Sleep(16); // ~60fps
}

**Thread safety**: `FrameTick()` processes messages on the calling thread. Do NOT call it from a background thread. Do NOT access `conn.Db` from background threads.

Subscriptions

// Subscribe to all tables
conn.SubscriptionBuilder()
    .OnApplied(ctx => Console.WriteLine("Subscription ready"))
    .SubscribeToAllTables();

// Subscribe with typed query builder (recommended)
conn.SubscriptionBuilder()
    .OnApplied(OnSubscriptionApplied)
    .AddQuery(q => q.From.Player().Where(p => p.Level.Gte(5u)))
    .AddQuery(q => q.From.GameState())
    .Subscribe();

// Or with raw SQL strings
conn.SubscriptionBuilder()
    .OnApplied(OnSubscriptionApplied)
    .Subscribe(new[] {
        "SELECT * FROM player WHERE level >= 5",
        "SELECT * FROM game_state"
    });

Submodule Accessors and View Arguments

A namespace accessor `MyAuth` is nested in the generated table, reducer, and procedure APIs:

conn.SubscriptionBuilder()
    .AddQuery(q => q.From.MyAuth.User())
    .Subscribe();
// Rows: conn.Db.MyAuth.User
// Reducers: conn.Reducers.MyAuth.Add(10)
// Procedures: conn.Procedures.MyAuth.CountUsers((ctx, result) => { ... })

The query builder and calls encode canonical names automatically. Raw SQL/HTTP/CLI calls use canonical qualified names, such as `auth_data.<table>` for a namespace explicitly named `auth_data`. All namespaces share the connection and cache.

For views with arguments (2.13+), use raw SQL; C# has no typed argument-bearing view query builder yet:

conn.SubscriptionBuilder().Subscribe(new[] { "SELECT * FROM by_id(42)" });

Use explicit queries for every table on that connection, not subscribe-al

Read more
@clockworklabs
Ships withspacetimedb

Development at the speed of light

26k11 skills1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
25.5k
Star history of clockworklabs/spacetimedb on GitHub, a chart by star-history.com

Other skills on spacetimedb.

cli

cli

SpacetimeDB CLI reference for initializing projects, building modules, publishing databases, querying data, and managing servers

concepts

concepts

Understand SpacetimeDB architecture and core concepts. Use when learning SpacetimeDB or making architectural decisions.

mcp

mcp

Operate a running SpacetimeDB database through MCP tools rather than the CLI - list databases, read schemas, run SQL, and call…

More skills from other plugins.

backend

backend

Use this skill when contributing to InsForge's backend package. This is for maintainers editing backend routes, services,…

insforge12d13k
@insforge@insforgeInstall Skill
exa-agent

exa-agent

Use Exa Agent for multi-step web research, list-building, enrichment, structured output, run continuation, and coverage…

exa9d5.2k
@exa-labs@exa-labsInstall Skill
fde-ontology

fde-ontology

多源输入→本体检出→AI审核→场景升级→代码生成 自动化管线

dgiot4w4.9k
@dgiot@dgiotInstall Skill

Questions about csharp-client.

Which coding agents can use csharp-client?
csharp-client installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.