Getting started
stitchkit turns one contract into an HTTP API, MCP tools, AI-agent tools and a typed client. This page gets a working app running; the rest of the guide goes deep on each piece.
Requirements
- Bun
>= 1.2(recommended) or Node.js>= 22. Bun is first-class; Node is supported viastitchkit/node.
Install
bun add stitchkit zod
zod is a required peer — schemas are the source of truth everywhere. Other
peers are optional and pulled in only when you use the matching feature
(@modelcontextprotocol/server for MCP servers, ai for agents, socket.io* for
realtime, @tanstack/react-query + react-query-kit for React). See
deps below.
Entrypoints
Every entrypoint is import-safe for one environment — keeping server-only code
(Bun.serve, the MCP SDK) out of browser bundles.
Each also declares how settled it is. Stable means the shape changes rarely
and only with a reason worth a migration. Evolving means the shape is still
being found and may be redefined in any minor — always with a
### ⚠️ Breaking changes entry and a migration section, never silently. Both
are legitimate choices; the difference is how often you should expect to read
the changelog. Moving an entrypoint from evolving to stable is a decision on its
own, recorded as an ADR.
| Import | Use in | Maturity | Holds |
|---|---|---|---|
stitchkit |
browser and server | stable | defineContract, createClient, createHttpClient, createSocketIOClient, parseSSE, the error model |
stitchkit/http |
browser, React Native/Hermes and server | evolving | typed HTTP client factories and ApiError, without optional realtime peers |
stitchkit/realtime |
browser, React Native/Hermes and server | evolving | typed realtime clients and retained topics with a required explicit Socket.IO peer loader |
stitchkit/contract |
browser and server | stable | the contract layer alone — defineContract, errors, pagination |
stitchkit/live |
browser and server | evolving | defineEvents — topic declarations beside the operation contract — and watched reads: createWatchClient, the value/difference frame and its reassembly |
stitchkit/primitives |
browser and server | evolving | generic values and declarations for lifecycle, access, audit, delivery and exports |
stitchkit/server |
server (Bun) | stable | createServer, implement, hooks, auth, Socket.IO server, server primitives |
stitchkit/node |
server (Node ≥ 22) | stable | serveNode + the runtime-agnostic core — the Node mirror of /server |
stitchkit/tools |
server | stable | createMcpHandler, mountMcp, mountAgent, the OAuth provider, native tools |
stitchkit/tools/mcp |
server (Bun or Node) | evolving | the canonical MCP HTTP/stdio server, schemas and typed MCP presenters; only Zod and the MCP server SDK peers |
stitchkit/tools/contract |
browser + server | evolving | the shapes a tool surface speaks — async-operation contract, snapshot and cancel schemas, view-file input/output — without the runtime that serves them |
stitchkit/tools/invoker |
server | stable | peer-free createToolInvoker over the canonical contract tool runner |
stitchkit/tools/connections |
server (Bun or Node) | evolving | consume external MCP servers and a bounded OpenAPI subset — typed reauthorization, a per-request host fence and per-call credentials |
stitchkit/cli |
server | stable | createCli / createCliInvoker — runtime and strict NodeNext declarations without MCP SDK / ai |
stitchkit/cli/publish |
build and release tooling (Bun or Node, POSIX) | evolving | publishCli — commit a verified CLI distribution and move its public manifest last; see CLI guide |
stitchkit/remote |
browser and server | stable | peer-free implementRemote for thin HTTP proxy processes |
stitchkit/process |
server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture, process-group cancellation and opt-in Linux/Darwin owner-loss cleanup |
stitchkit/process/owner-loss |
bundled server entry (Bun, POSIX) | evolving | narrow bootstrap that keeps the process runner lazy while carrying its owner-loss guard in a single-file bundle |
stitchkit/files/packaging |
build time (Bun or Node) | evolving | installed native asset graph, integrity, Bun packaging plugin and the artifact loader check; no runtime install policy |
stitchkit/files |
server (Bun or Node) | stable | peer-free managed local-file boundary |
stitchkit/telegram |
server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App initData verification, send-failure classification, resumable broadcasts, the operator channel with dedupe, a Bot API transport that separates a request that never left from an unknown outcome, local Bot API files, webhook ownership and durable webhook update intake |
stitchkit/telegram/html |
browser and server | evolving | Telegram’s HTML as a tree — clean any markup to what Telegram accepts, cut long text into valid parts by what the reader sees, check markup before it is sent |
stitchkit/telegram/screens |
server (Bun or Node) | evolving | a bot’s menus as declared screens over grammY — typed paths, actions and input, message reconciliation, state in any StorageAdapter, a test chat |
stitchkit/tracking |
browser and server | evolving | visitor-tracking mechanics — createTrackingClient, the tab-shared outbox, the page-leave beacon, attribution, the contract factory; no event vocabulary, no React |
stitchkit/tracking/server |
server (Bun or Node) | evolving | the decisions a tracking backend makes — dispositions, visit lease over an application-owned store, active intervals, presence; no database |
stitchkit/release |
browser and server | evolving | a page follows the release it was built for — createReleaseMarker on the server, createReleaseWatcher in the browser, the X-Build-Id header and a socket event between them |
stitchkit/voice |
browser and server | evolving | the mechanics of a live voice reply — SentenceCutter, speakableText, a SpeechQueue that synthesises ahead and stops on interruption, the conversation phases |
stitchkit/geo |
server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
stitchkit/oauth |
browser and server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
stitchkit/google |
server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
stitchkit/observability |
server | stable redefined in 2 of the 56 minors since 0.56.2, most recently 0.92.0 |
request/tool event projections — createObservability, trace context, sanitisation |
stitchkit/testing |
tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, the managed-resource conformance kit and manual time for schedules and retries |
stitchkit/declaration |
browser + build and deployment tooling (Bun or Node) | evolving | ProjectDeclarationSchema — the one machine-readable statement a repository makes about itself |
stitchkit/react |
browser + server rendering | stable | createCursorQuery, createCacheBridge, QueryClient and ApiError retry policy |
stitchkit/react/keyboard |
browser | evolving | which part of a screen gets a key — ordered layers, one Escape per level, list zones with real focus; see keyboard guide |
stitchkit/agent-runtime |
server | evolving redefined in 23 of the 56 minors since 0.56.2, most recently 0.105.0 |
optional durable conversation/run loop, history, models, prompts, fencing and events |
stitchkit/agent-runtime/sandbox |
server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see sandbox guide |
stitchkit/agent-runtime/testing |
tests on Bun or Node | evolving | credential-free replay, scripted provider faults, deterministic race controls and the agent-store conformance kit |
stitchkit/agent-runtime/harness |
server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
stitchkit/agent-runtime/coding-tools |
server (Bun or Node) | evolving | bounded host-authorized direct file and shell tools; a root boundary, not an OS sandbox |
stitchkit/agent-runtime/openrouter |
server | evolving | isolated OpenRouter language-model adapter |
stitchkit/agent-runtime/browser |
browser + server | evolving | canonical agent records, events, reconnect cursor and optional conversation controller |
stitchkit/agent-runtime/react |
browser + server | evolving, experimental: no committed consumer | React subscription to an application-owned agent controller |
stitchkit/agent-runtime/realtime |
server (Bun or Node) | evolving, experimental: no committed consumer | authorized agent control over the existing Socket.IO server |
stitchkit/agent-runtime/sqlite/bun |
server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
stitchkit/agent-runtime/sqlite/node |
server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
stitchkit-tui |
terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
stitchkit/application |
browser + server | evolving redefined in 14 of the 56 minors since 0.56.2, most recently 0.110.0 |
managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
stitchkit/application/grammy |
server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
stitchkit/application/opentelemetry |
server | evolving | maps application snapshots onto an injected OpenTelemetry Meter |
stitchkit/application/schemas |
browser + server | evolving | the application’s snapshot, health and shutdown schemas alone, without the kernel |
stitchkit/application/diagnostic-journal |
server | evolving | the local diagnostic journal — an exclusive bounded writer and snapshot reader with explicit anomalies |
stitchkit/application/directory-inbox |
server | evolving | the directory inbox — entries another program drops into a directory, delivered to the application at least once |
Rule of thumb: browser code imports stitchkit and stitchkit/react; server
code adds stitchkit/server (or stitchkit/node on Node) and opts into
stitchkit/tools, stitchkit/remote or stitchkit/files by capability.
The full export list of each is in the API reference.
Project layout
A contract is shared by both sides, so it lives in its own folder:
src/
├── shared/contracts.ts the contract — imported by server and client
├── server/index.ts implement() + createServer()
└── client/api.ts createClient()
A first app
1. Define the contract
// src/shared/contracts.ts
import { defineContract } from 'stitchkit'
import { z } from 'zod'
const Note = z.object({ id: z.string(), text: z.string() })
export const notes = defineContract({ prefix: 'notes' }, {
list: { method: 'GET', path: '/', desc: 'List notes', output: z.array(Note) },
create: { method: 'POST', path: '/', desc: 'Create note', input: z.object({ text: z.string() }), output: Note },
get: { method: 'GET', path: '/:id', desc: 'Get a note', params: z.object({ id: z.string() }), output: Note },
})
2. Implement and serve
// src/server/index.ts
import { implement, createServer } from 'stitchkit/server'
import { notes } from '../shared/contracts'
const service = implement(notes, {
list: () => db.list(),
create: (ctx) => db.create(ctx.input.text), // ctx.input is typed
get: (ctx) => db.get(ctx.params.id), // ctx.params is typed
})
createServer({ services: [service], port: 3000 })
3. Call it, typed
// src/client/api.ts
import { createClient, createHttpClient } from 'stitchkit'
import { notes } from '../shared/contracts'
export const api = createClient(notes, createHttpClient({ baseUrl: '/api' }))
await api.list() // GET /notes → Note[]
await api.create({ text: 'hi' }) // POST /notes → Note
await api.get({ id: '1' }) // GET /notes/1 → Note
Run the server with bun run src/server/index.ts. The contract is the single
source of truth — change it and both the handler and the client are re-typed at
once.
On Node
createServer is Bun’s Bun.serve. On Node ≥ 22, swap it for serveNode
(from stitchkit/node, built on srvx) — the contract, implement and the
client are identical:
import { serveNode } from 'stitchkit/node'
serveNode({ services: [service], port: 3000 })
Add @types/bun as a dev dependency on Node (an optional peer — it types the
shared stitchkit/server surface). See deployment.
Dependencies
ky is the only bundled runtime dependency. Everything else is an optional
peer — your app installs only what the features it uses need, and owns the
version (one shared instance, no dual-version skew). This matrix is the install
map — feature → packages:
| Feature you use | Install |
|---|---|
| anything (validation) | zod |
createServer (Bun) |
— (uses Bun.serve) |
serveNode (Node ≥ 22) |
srvx (+ @types/bun dev) |
In-process contract tools (stitchkit/tools/invoker) |
— |
MCP / agent adapters (stitchkit/tools) |
@modelcontextprotocol/server ai |
Agent application runtime (stitchkit/agent-runtime) |
ai |
Headless Agent harness (stitchkit/agent-runtime/harness) |
ai |
Agent coding tools (stitchkit/agent-runtime/coding-tools) |
— |
OpenRouter runtime adapter (stitchkit/agent-runtime/openrouter) |
ai @openrouter/ai-sdk-provider |
SQLite agent store (stitchkit/agent-runtime/sqlite/bun or /node) |
— (runtime built-in) |
| MCP host/client tests | @modelcontextprotocol/client |
| MCP Apps UI widgets | @modelcontextprotocol/ext-apps |
React data layer (stitchkit/react) |
@tanstack/react-query react-query-kit |
Keyboard layers and list zones (stitchkit/react/keyboard) |
react |
MaxMind GeoIP (stitchkit/geo) |
maxmind |
Browser OAuth client (stitchkit/oauth) |
— |
Google OIDC verifier (stitchkit/google) |
google-auth-library |
| Socket.IO server on Bun | socket.io @socket.io/bun-engine |
| Socket.IO server on Node | socket.io |
| Socket.IO client | socket.io-client (runtime peer; unrelated root declarations remain peer-free) |
grammY lifecycle adapters (stitchkit/application/grammy) |
grammy |
Telegram platform primitives (stitchkit/telegram) |
— (peer-free) |
Telegram message markup (stitchkit/telegram/html) |
— (peer-free) |
Telegram screens (stitchkit/telegram/screens) |
grammy (types only; the bot brings it) |
OpenTelemetry gauges (stitchkit/application/opentelemetry) |
@opentelemetry/api |
bun add socket.io @socket.io/bun-engine # e.g. the Socket.IO server on Bun
Dynamic optional-peer adapters fail with an actionable error naming the package
and install command. Static entrypoints such as stitchkit/tools fail during
ESM resolution by naming the exact missing package; the feature-to-peer matrix
below is the canonical install command.
The combined tools entry owns MCP and AI-agent adapters, so install both runtime
peers before importing stitchkit/tools:
bun add @modelcontextprotocol/server@^2 ai@^7
Browser, HTTP-client and React entrypoints remain usable without either MCP
package. MCP hosts and client E2E additionally install
@modelcontextprotocol/client@^2; Apps additionally install
@modelcontextprotocol/ext-apps.
Next
- Contracts — every endpoint field, in depth.
- HTTP server —
createServer, hooks, raw routes, primitives. - Typed client — the client, React data layer, SSE.
- MCP & agents — contracts as AI tools.
- Realtime — Socket.IO and the cache bridge.
- Auth & errors — scopes, auth hooks, the error model.
- Browser OAuth and Google OIDC — PKCE transaction and verified provider identity.
- Testing & deployment.
- API reference — every export, by entrypoint.
For a complete production-shaped app, run bun create stitchkit my-app. The
canonical generated topology is maintained in
packages/create-stitchkit/template.
For a terminal Agent host instead, run
bun create stitchkit my-agent --template agent, copy .env.example to .env,
set OPENROUTER_API_KEY, then run bun run dev and choose a live tool-capable
model with /model. The provider catalog owns the exact model id and context window and presents
weekly popularity separately from sourced benchmark observations; OPENROUTER_MODEL is only an
optional preferred row. That profile is a thin stitchkit.agent.ts composition over the official
stitchkit-tui package and canonical headless harness:
durable SQLite history, lazy skills, direct coding tools, approval continuations
and recovery stay framework-owned primitives, while model choice, permissions,
executables and OS isolation remain application policy.