npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

stitchkit

v0.70.2

Published

Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.

Readme


Why

  • One contract, five surfaces. Define your API once — get HTTP routes, MCP tools (for Claude/Cursor), AI SDK tools (for agents), a CLI (for scripts & Skills), and a typed client.
  • Zero HTTP framework deps. Built on Bun.serve() (Bun) or srvx (Node). No Hono, no Elysia, no Express.
  • Fullstack type safety. Server handlers, client calls, MCP tools — all typed from the same contract.
  • Inspectable. A focused core with explicit adapters. No generated application code or framework build step in your app.
  • Thin over what you already use. WebSocket = Socket.IO (createSocketIOClient / createSocketIOServer). React data layer = react-query-kit (createCursorQuery). stitchkit owns the contract and the transport — not its own competing WebSocket or hook engine.

The problem it solves

A modern backend exposes the same operations several ways — an HTTP API for the app, MCP tools for assistants like Claude and Cursor, tool definitions for AI agents, and a CLI for scripts and Skills. Written by hand, that is one surface described many times: many places to drift, many places to keep in sync. stitchkit collapses them into a single contract — change it once, every surface and the typed client move together.

Status

Pre-1.0. The core is stable and covered by tests, but the public API may still change between minor versions until 1.0. Bun is first-class; Node ≥ 22 is supported via stitchkit/node.

Install

Start a production-shaped Next.js, Stitchkit and PostgreSQL application:

bun create stitchkit my-app
cd my-app
# Point DATABASE_URL in .env at your PostgreSQL database.
bun run dev

To add Stitchkit to an existing project instead:

bun add stitchkit        # Bun
npm install stitchkit    # Node
pnpm add stitchkit       # pnpm

Import policy

Browser code imports browser-safe entrypoints:

import { defineContract, createClient, createHttpClient } from 'stitchkit'
import { createSocketIOClient } from 'stitchkit'
import { createCursorQuery, createCacheBridge } from 'stitchkit/react'
import { AgentRunSchema, AgentRuntimeEventSchema } from 'stitchkit/agent-runtime/browser'
import { parseSSE } from 'stitchkit'

Server code imports server entrypoints:

import { createServer, createHandler, implement } from 'stitchkit/server'
import { createSocketIOServer, createAuthHook } from 'stitchkit/server'
import { createMcpHandler, mountAgent } from 'stitchkit/tools'
import { createAgentRuntime, defineAgentProtocol } from 'stitchkit/agent-runtime'
import { createBunSqliteAgentRuntimeStore } from 'stitchkit/agent-runtime/sqlite/bun'
import { createApplication, defineManagedResource } from 'stitchkit/application'
import { implementRemote } from 'stitchkit/remote'

The root stitchkit entrypoint is browser-safe. Server, tool, optional managed application, AI-SDK-backed agent-runtime and peer-free remote-proxy code live behind stitchkit/server, stitchkit/tools, stitchkit/application, stitchkit/agent-runtime and stitchkit/remote respectively. Optional provider adapters are isolated further; importing stitchkit/application never resolves grammY or OpenTelemetry. Browser code that shares agent records imports stitchkit/agent-runtime/browser; it contains the canonical schemas and event cursor but no execution, persistence or sink graph. Durable embedded storage is isolated behind stitchkit/agent-runtime/sqlite/bun and stitchkit/agent-runtime/sqlite/node; neither runtime-specific built-in leaks into the neutral or browser entrypoint. Externally supervised sessions can compose the same runtime through stitchkit/agent-runtime/harness; optional explicit-root lazy resources, signed approval continuations and reconnectable control compose there. Bounded direct file, search, guarded patch, shell and opaque-artifact tools live in the peer-free stitchkit/agent-runtime/coding-tools leaf. Neither surface owns process placement, restart, credentials, model catalogs or OS isolation. The separately installed stitchkit-tui package adds a maintained Bun/OpenTUI host over those headless contracts; its stitchkit-tui/core entrypoint exposes renderer-neutral terminal state without React, OpenTUI or agent-runtime imports. Neither enters the core dependency graph.

Managed application kernel

Use stitchkit/application when several process-local resources must start, become ready, drain and stop as one application:

import {
  createApplication,
  createManagedSchedule,
  managedServerResource,
} from 'stitchkit/application'
import { bindProcessSignals } from 'stitchkit/server'

const app = createApplication({
  id: 'service',
  resources: [
    managedServerResource({ id: 'http', server }),
    createManagedSchedule({
      id: 'cleanup',
      everyMs: 60_000,
      run: ({ signal }) => removeExpiredRecords(signal),
    }),
  ],
})

bindProcessSignals(app)
await app.start()

The kernel owns dependency ordering, attempted-start rollback, readiness, process-local admission, ephemeral schedules and bounded shutdown. It does not own durable jobs, provider policy, retries, process restart or deployment. createApplicationOperationalHandlers projects conventional status/readiness/ liveness routes from the same snapshot. Applications that already own an OpenTelemetry SDK may opt into stitchkit/application/opentelemetry; the adapter registers pull-only observable gauges on an injected Meter and owns no exporter or SDK lifecycle.

Quick Start

1. Define a contract

// shared/contracts.ts
import { defineContract } from 'stitchkit'
import { z } from 'zod'

const UserSchema = z.object({ id: z.string(), name: z.string() })
const CreateUserSchema = z.object({ name: z.string() })
const IdSchema = z.object({ id: z.string() })

export const users = defineContract({ prefix: 'users' }, {
  list:   { method: 'GET',    path: '/',    desc: 'List all users',  output: z.array(UserSchema) },
  create: { method: 'POST',   path: '/',    desc: 'Create a user',   input: CreateUserSchema, output: UserSchema },
  get:    { method: 'GET',    path: '/:id', desc: 'Get user by ID',  params: IdSchema, output: UserSchema },
  delete: { method: 'DELETE', path: '/:id', desc: 'Delete a user',   params: IdSchema },
})

2. Implement handlers

// server/index.ts
import { implement, createServer } from 'stitchkit/server'
import { users } from '../shared/contracts'

const service = implement(users, {
  list:   (ctx) => db.users.findMany(),
  create: (ctx) => db.users.create({ name: ctx.input.name }),
  get:    (ctx) => db.users.findById(ctx.params.id),
  delete: (ctx) => db.users.delete(ctx.params.id),
})

createServer({ services: [service], port: 3000 })

3. Use from the client

// client/api.ts
import { createClient, createHttpClient } from 'stitchkit'
import { users } from '../shared/contracts'

const http = createHttpClient({ baseUrl: '/api' })
export const api = createClient(users, http)

await api.list()                  // GET /users → User[]
await api.create({ name: 'Max' }) // POST /users → User
await api.get({ id: '123' })      // GET /users/123 → User

For many contracts at once, use createClients(contractRegistry, http).

4. React data layer (react-query-kit)

stitchkit does not ship its own hook engine — pair the typed client with react-query-kit, wrapping the client methods directly:

import { createMutation, createQuery } from 'react-query-kit'
import { api } from './api'

export const useUsers = createQuery({ queryKey: ['users'], fetcher: () => api.list() })
export const useCreateUser = createMutation({ mutationFn: api.create })

The ordinary generated method contains only contract variables, so it remains safe to pass directly as a query or mutation callback. Imperative calls that need cancellation use the method's explicit transport-options surface:

await api.create.withOptions({ name: 'Max' }, { signal })
await api.health.withOptions({ signal }) // endpoint without contract arguments

For cursor-paginated lists, createCursorQuery is the canonical helper:

import { createCursorQuery } from 'stitchkit/react'
import { api } from './api'

export const useFeed = createCursorQuery({ queryKey: ['feed'], endpoint: api.feed.list })

It injects cursor from the page param and bakes in getNextPageParam. Page size is the server's call — the contract's limit default — never the client's.

5. MCP tools (for Claude, Cursor, etc.)

import { createMcpHandler, createMcpHttpRoute } from 'stitchkit/tools'

const mcp = createMcpHandler({
  serverInfo: { name: 'my-app', version: '1.0.0' },
  auth: (req) => resolveApiKey(req),     // → identity, or null for 401
  services: [service],                   // contract endpoints with expose: ['MCP']
})

createServer({
  services: [service],
  rawRoutes: [createMcpHttpRoute({ path: '/mcp', handler: mcp })],
})

// On shutdown: await mcp.close()

6. AI Agent tools

import { mountAgent } from 'stitchkit/tools'
import { generateText } from 'ai'

const tools = mountAgent(service, { context: { userId: 'agent-1' } })
const result = await generateText({ model, tools, prompt: 'Create a user named Max' })

For applications that want Stitchkit to own durable message/run transitions, stream checkpoints, interruption and managed-tool fencing, use the optional stitchkit/agent-runtime. mountAgent remains the smaller application-owned-loop path.

7. WebSocket (Socket.IO)

stitchkit's WebSocket layer is Socket.IO — polling fallback, heartbeat, acks, a mature client. The wrappers cover the boilerplate.

// Server
import { createServer, createSocketIOServer } from 'stitchkit/server'

const socket = await createSocketIOServer<ServerToClientEvents, ClientToServerEvents>({
  cors: { origin: 'https://app.example.com' },
})

socket.io.on('connection', (s) => { /* rooms — your domain logic; typed handshake auth via `handshake` */ })

createServer({
  services: [service],
  socket,                         // route + websocket + managed shutdown
})
// Client
import { createSocketIOClient } from 'stitchkit'

const socket = createSocketIOClient<ServerToClientEvents, ClientToServerEvents>({
  url: 'https://api.example.com',
})
socket.connect()
socket.on('notification', (data) => console.log(data))  // typed
socket.emit('join', { room: 'r1' })                     // typed

8. Cache Bridge

Sync Socket.IO events into the TanStack Query cache. Transport-agnostic — it takes any emitter with on(event, handler) => unsubscribe (the createSocketIOClient result qualifies).

import { createCacheBridge } from 'stitchkit/react'

const bridge = createCacheBridge({
  socket,
  queryClient,
  handlers: {
    notification: (data, ctx) => {
      if (ctx.isFresh(['notes'])) return            // skip echo of own mutation
      ctx.queryClient.setQueryData(['notes'], data)
    },
  },
})
bridge.connect()
// in a mutation: onSuccess: () => bridge.markFresh(['notes'])

9. SSE Streaming

import { streamSSE } from 'stitchkit/server'   // server: AsyncGenerator → SSE Response
import { parseSSE } from 'stitchkit'           // client: Response → AsyncGenerator

Features

| Feature | API | |---------|-----| | Contract | defineContract() — single source of truth for your API | | HTTP Server | createServer() / createHandler() — Bun.serve, validation, hooks, raw routes | | MCP Tools | createMcpHandler() / mountMcp() — MCP tools from contracts | | Agent Tools | mountAgent() — Vercel AI SDK tools from contracts | | Agent Runtime | createAgentRuntime() — optional durable history, stream loop, coordination and fencing | | Application Kernel | createApplication() plus optional bounded local diagnostics — process-local resources, readiness, schedules and bounded shutdown | | Typed Client | createClient() / createClients() — typed fetch from contracts | | Cursor Pagination | createCursorQuery()react-query-kit infinite query from a contract method | | WebSocket | createSocketIOClient() / createSocketIOServer() — typed Socket.IO wrappers | | Cache Bridge | createCacheBridge() — socket events → TanStack Query cache | | Auth | createAuthHook() / createBearerResolver() — scope-aware auth from contract.scope | | SSE Streaming | streamSSE() / parseSSE() — async generator ↔ SSE | | Events | createEventBus<EventMap>() — typed in-process pub/sub | | Multipart | typed buffered or streaming single/multi-file uploads with limits and MIME policy | | Rate Limiting | createRateLimiter() — token bucket, per-key | | Cache | createCache() — in-memory with TTL + cacheHeaders() | | Errors | AppError, notFound(), badRequest(), unauthorized() |

How it compares

A modern backend exposes the same operations as an HTTP API, as MCP tools and as AI-agent tools. Most stacks make you describe each surface separately.

| | Without stitchkit | With stitchkit | |--|----------------------|--------------------| | Define an operation | once per surface — HTTP, MCP, agent (3×) | once — defineContract() | | Keep the surfaces in sync | manual; they drift apart | cannot drift — one source | | Typed client | hand-written, or a codegen step | inferred from the contract | | Expose a new surface | re-describe every endpoint | flip expose — already typed |

Versus other typed-API tools:

| Capability | stitchkit | tRPC | ts-rest | Hono / Elysia | |------------|:---:|:---:|:---:|:---:| | Contract is plain data — no decorators, no codegen | ✅ | ⚠️ router type | ✅ | ❌ | | Inferred typed client | ✅ | ✅ | ✅ | ⚠️ Eden / hc | | Plain HTTP REST routes | ✅ | ⚠️ RPC-style | ✅ | ✅ | | MCP tools from the same contract | ✅ | ❌ | ❌ | ❌ | | AI-agent tools from the same contract | ✅ | ❌ | ❌ | ❌ | | No HTTP-framework dependency | ✅ | ✅ | ✅ | — it is one |

The line no other tool draws: the same contract becomes MCP tools and AI-agent tools — not just an HTTP API and a client. That is what stitchkit is for.

Lifecycle Hooks

createServer({
  services: [service],
  hooks: {
    onRequest(req) { },              // logging, rate limiting
    authorize(ctx, endpoint) { },    // auth before request-body reads
    beforeHandle(ctx, endpoint) { }, // validated-input preconditions
    afterHandle(ctx, result) { },    // transform, cache headers
    onError(ctx, error) { },         // error formatting
  },
})

Auth & Scopes

Contracts carry a scope; createAuthHook enforces it on every transport from one declarative rules map:

import { createAuthHook, createBearerResolver } from 'stitchkit/server'

const authHook = createAuthHook<User>({
  resolve: (ctx) => resolveSession(ctx),
  rules: {
    public: 'public',
    user: 'authenticated',
    admin: (user) => user.isAdmin,
  },
})

createServer({ services, hooks: { authorize: authHook } })

Dependencies

stitchkit ships with one runtime dependency. Everything else is an optional peer — an install pulls in only what the project actually uses.

| Dependency | Kind | Why this one | |------------|------|--------------| | ky | bundled, runtime | The HTTP client behind the typed client — ~13 KB, fetch-based, with retry, hooks and timeouts built in. The only thing stitchkit installs for you. | | zod | peer, required | Schemas are the single source of truth. A peer so your app and stitchkit share one zod instance — z.infer types and instanceof checks break across two copies. | | @modelcontextprotocol/server | peer, optional | MCP server surfaces in stitchkit/tools; SDK v2, protocol 2026-07-28. | | @modelcontextprotocol/client | development dependency, optional | Only consumers that run MCP client integration tests or build an MCP host. | | @modelcontextprotocol/ext-apps | peer, optional | Only MCP Apps (ui:// resources and UI metadata). | | ai | peer, optional | stitchkit/tools agent tools and the optional server-only stitchkit/agent-runtime. | | Headless harness | optional surface | stitchkit/agent-runtime/harness; uses the same optional ai peer and canonical runtime. | | Agent coding tools | optional surface | stitchkit/agent-runtime/coding-tools; peer-free, host-authorized direct file and shell tools. | | stitchkit-tui | separate optional package | Renderer-neutral ./core state plus a Bun/OpenTUI terminal controller, commands, model/session pickers and authenticated local attachment over a caller-composed harness. | | @openrouter/ai-sdk-provider | peer, optional | Only stitchkit/agent-runtime/openrouter; neutral runtime imports do not resolve it. | | SQLite | runtime built-in, optional | bun:sqlite through stitchkit/agent-runtime/sqlite/bun, or node:sqlite on Node ≥ 22.5 through the Node leaf. | | grammy | peer, optional | Only stitchkit/application/grammy; the neutral application kernel does not resolve it. | | @opentelemetry/api | peer, optional | Type-only boundary for stitchkit/application/opentelemetry; the adapter has no runtime import and owns no SDK/exporter. | | @tanstack/react-query + react-query-kit | peer, optional | Only stitchkit/reactcreateCursorQuery, createCacheBridge. | | socket.io / @socket.io/bun-engine / socket.io-client | peer, optional | Only the Socket.IO wrappers. @socket.io/bun-engine is Bun-only; shipping one self-contained artifact? Hand the loaders to createSocketIOServer({ peers }). | | @socket.io/component-emitter | peer, optional, types only | Referenced by the browser Socket.IO declarations. Arrives with socket.io-client; nothing imports it at runtime. | | srvx | peer, optional | Only serveNode — the Node ≥ 22 HTTP adapter. Bun uses Bun.serve and needs nothing. |

Why peers, not bundled. A peer is resolved once, by your app — framework and app code share a single instance. Bundled copies would double zod, split the react hook runtime and break instanceof. Optional peers mean an app that never touches MCP never installs the MCP SDK. → ADR 0011

The framework stays focused and inspectable: explicit adapters, no generated application code and no framework build step in your app.

Official starter

bun create stitchkit my-app generates the canonical application: separate Next.js and Bun API processes, Prisma/PostgreSQL, shared Zod contracts, typed HTTP/React Query clients, Socket.IO cache updates, OpenAPI, MCP, CLI and a full UI catalogue. Its only source is packages/create-stitchkit/template. The application owns its Prisma schema and migrations while PostgreSQL remains external infrastructure configured through DATABASE_URL. The template owns a committed Bun lockfile and an explicit Stitchkit catalog range, so framework and scaffolder releases advance independently.

For a minimal terminal coding-agent host, choose the second explicit profile:

bun create stitchkit my-agent --template agent
cd my-agent
cp .env.example .env
# Fill OPENROUTER_API_KEY, then choose a live model in the terminal.
bun run dev

Its editable stitchkit.agent.ts composes stitchkit-tui, OpenRouter, durable SQLite history, lazy skills and direct approval-gated coding tools over the same headless Agent harness. /model searches the full tool-capable catalog and shows weekly popularity independently from sourced benchmark observations. /status exposes the local session ID so another process can send or interrupt through the terminal's existing controller. The renderer and provider dependencies do not enter Stitchkit core or the default application. → ADR 0133

Documentation — two roads

This README is the quick start. Where you go next depends on what you're doing:

📦 Building an app with stitchkit

The full guide and API reference, in docs/:

🔧 Developing stitchkit itself

License

MIT © Max Listov