skein-js — Overview
skein-js is the open-source alternative to LangGraph Platform (now LangSmith Deployment) for TypeScript — a framework-agnostic library that implements LangChain's Agent Protocol on top of LangGraph.js, so you can self-host your agents on your own infrastructure. It also ships a CLI that is a drop-in replacement for the LangGraph CLI.
Built around LangGraph OSS, not a reimplementation of it. Its runtime executes your graphs, its checkpointers persist them, its parser reads your
langgraph.json, and the wire types come straight from@langchain/langgraph-sdk. skein-js rebuilds only what OSS lacks: durable production storage and queueing, the framework adapters, and the drop-in CLI.That is why compatibility holds. The SDK's own types are the contract, so
useStreamand every LangGraph client work against skein by construction — not through a layer someone maintains.
The problem
You've built an agent as a LangGraph.js graph. On its own, a graph is a function you call in-process. To put it behind a chat UI or expose it to other services you need a server — and a capable agent server is a lot of plumbing:
- Threads — conversations that persist, with full state and history.
- Runs — execute the graph and either wait for the result, stream it, or run it in the background.
- Streaming — push tokens, tool calls, and reasoning to the client as they happen, with reconnect/replay if a connection drops.
- Human-in-the-loop — pause a run for approval and resume it later.
- Long-term memory — storage that outlives a single conversation.
- Auth, CORS, persistence, and scaling — the production essentials.
There is no first-class, self-hostable way to get all of this for LangGraph.js in the Node ecosystem:
- LangGraph Platform (now LangSmith Deployment) is a paid product. You can self-host it, but production self-hosting is an Enterprise add-on requiring a commercial license key — the platform's server runtime is source-available under the Elastic License 2.0, not open source. The managed Plus plan is $39 / seat / month, includes one small serverless deployment, and meters additional deployment compute and storage through usage-based LCU/LSU rates. Fully self-hosted and hybrid deployment are Enterprise-only with custom pricing. (pricing · self-hosting docs, as of August 2026.)
- aegra — the leading open self-hosted alternative — is Python / FastAPI only.
TypeScript teams are left to adopt the paid platform, run a Python sidecar, or hand-roll an HTTP layer around a compiled graph.
What is the Agent Protocol?
The Agent Protocol is the open HTTP + SSE standard that describes an agent server's surface — assistants, threads, runs, streaming, interrupts, and a store. Because it's a standard, the entire LangChain client ecosystem speaks it:
@langchain/langgraph-sdk— the vanilla JS client (client.threads/client.runs/ …)@langchain/langgraph-sdk/react— theuseStreamhook, streaming over SSE- Agent Chat UI and LangGraph Studio
Implement the Agent Protocol and all of these clients work with your server — no custom SDK, no bespoke wire format. skein-js implements it; your existing clients keep working with only a URL change. See agent-protocol.md for the exact endpoints.
The solution
skein-js is an open, self-hostable alternative to LangGraph Platform for the TypeScript ecosystem — "aegra for TypeScript." It exposes the Agent Protocol wire format from any Node HTTP framework on Node, or native Fetch on Bun and Deno (Express, Fastify, NestJS, Next.js, and Fetch adapters ship today), so the whole LangChain client surface keeps working with only a URL change.
Unlike aegra — which had to reimplement the server in Python because the Python langgraph-api is proprietary — the JavaScript Agent Protocol server is open source and MIT (@langchain/langgraph-api). So skein-js is deliberately thin: it reuses as much LangGraph OSS as possible (runtime, checkpointers, parser, schemas, SDK/types) and rebuilds only the durable-production, multi-framework, drop-in-CLI layer that OSS lacks. The package-by-package ledger of what is reused versus rebuilt lives in reuse.md.
Starting from scratch
No graph, no langgraph.json, no LangGraph experience — one command writes a working project:
npm create skein-js@latest my-agent
cd my-agent && npm run dev # → http://localhost:2024, console at /consoleIt scaffolds a keyless graph you can edit, a test, and the whole dev → build → start lifecycle. Nothing it emits needs an API key or a database. Your first agent takes it from there to deployed and teaches the LangGraph you need on the way; scaffolding.md is the reference.
The drop-in promise
If you're coming from the LangGraph CLI, migration is zero-effort:
- "dev": "langgraph dev",
+ "dev": "skein dev",…while keeping the existing langgraph.json unchanged. Both the backend (config + graphs) and the frontend (useStream) point at skein-js by changing only a URL.
The other on-ramp: embed a graph you already have
If you already have a compiled graph in your own app, bring it in code — no langgraph.json, no CLI:
import { createExpressServer } from "@skein-js/express";
import { embedInMemoryGraphs } from "@skein-js/server-kit";
import { graph } from "./my-graph.js";
const server = await createExpressServer({ deps: embedInMemoryGraphs({ agent: graph }) });
await server.listen(2024);embedInMemoryGraphs assembles a ProtocolDeps (store, queue, bus, checkpointer) around your graphs; { deps } is the seam every adapter accepts. The two on-ramps produce the identical Agent Protocol server — see embedding.md.
Not building a chat app?
The full protocol (threads, assistants, runs) is what a chat UI needs. For a classifier, an extractor, or a workflow another service calls, there's a smaller surface: every graph mounted as POST /invoke/:graph_id, where the request body is the graph input and the response is its final state — no threads, no runs.
const { router } = await skeinInvokeRouter({ deps: embedInMemoryGraphs({ triage }) });
app.use(router);
// curl -X POST localhost:2024/invoke/triage -d '{"text":"…"}'Available on every adapter — see serving-a-single-graph.md.
Architecture at a glance
┌─────────────────────────────────────────────┐
clients │ @langchain/langgraph-sdk · /react useStream │
(unchanged) │ Agent Chat UI · LangGraph Studio │
└──────────────────────┬──────────────────────┘
│ Agent Protocol (HTTP + SSE)
┌──────────────────────▼──────────────────────┐
adapters │ express · fastify · nestjs · nextjs · native fetch │
├─────────────────────────────────────────────┤
protocol │ @skein-js/agent-protocol — handler table · │
│ run engine · streaming (SSE) │
├─────────────────────────────────────────────┤
contract │ @skein-js/core — wire types · SkeinStore + │
│ queue/bus interfaces · edge error │
├───────────────┬───────────────┬─────────────┤
│ @skein-js/config│ storage driver│ @skein-js/redis│
│ (langgraph. │ memory / │ queue + pub/ │
│ json loader) │ postgres+pgv │ sub │
└───────────────┴───────────────┴─────────────┘
│
LangGraph.js compiled graphs@skein-js/coreis the shared contract — wire types plus theSkeinStore, queue, and bus interfaces every other package depends on.@skein-js/agent-protocolholds the protocol logic once, against normalized request/response types, driven entirely by injected dependencies. Framework adapters are thin shims, and the package is publishable on its own. See each doc below for detail.
Examples
Runnable projects under examples/ — each proves a slice of the promise:
| Example | What it shows |
|---|---|
chat-app | Flagship — Gemini research assistant (thinking + web search + long-term memory) with a Next.js + shadcn/ui UI and full tests |
migrated-langgraph | The drop-in proof — a stock LangGraph project under skein dev, hot reload + persistence |
gemini-chat | Model-backed end-to-end — a Gemini ReAct agent streamed into a browser |
express-basic | Zero-setup echo + a Claude agent graph in one config |
embed-graph | In-code embedding — serve a graph you already have with no langgraph.json (embedInMemoryGraphs + { deps }) |
invoke-endpoint | The non-chat surface — graphs as plain POST /invoke/:graph_id endpoints, body-in / final-state-out |
fastify-basic / fastify-app | Fastify — standalone graph server, and the protocol embedded under /agent alongside a REST API |
nestjs-basic / nestjs-app | NestJS — standalone graph server, and SkeinModule alongside the app's own controller |
nextjs-basic / nextjs-app | Next.js — headless Pages Router API, and a full-stack App Router app serving the protocol same-origin behind a useStream UI |
react-usestream | Minimal useStream SSE-compatibility harness |
Documentation map
Start with the user-facing guides; the design docs at the bottom explain how skein-js is built. Brand new, with nothing built yet? your-first-agent.md starts from an empty directory. Already have a graph? getting-started.md is the guided path. Building an app with skein (especially as an AI agent)? using-skein.md is the terse cheat-sheet, and the machine-readable llms.txt / llms-full.txt index the whole set.
| Doc | Covers |
|---|---|
| your-first-agent.md | From an empty directory to deployed — no LangGraph assumed |
| scaffolding.md | npm create skein-js — every flag, and doing it by hand |
| getting-started.md | Guided walkthrough — zero to a running server, then prod |
| using-skein.md | Consumer/agent cheat-sheet — install, the seam, mount, call |
| recipes.md | Auth, HITL, memory, CORS, background runs, webhooks, deploy |
| langgraph-cli-compat.md | langgraph.json fields + CLI commands |
| embedding.md | The in-code on-ramp — embed a graph, no langgraph.json |
| serving-a-single-graph.md | The non-chat surface — a graph as a plain HTTP endpoint |
| agent-protocol.md | The REST + streaming endpoints skein-js implements |
| building-an-adapter.md | How to put skein-js on any HTTP framework (custom adapter) |
| streaming.md | LangGraph stream modes → Agent Protocol SSE |
| react-sdk.md | Frontend SDKs — useStream plus Vue, Svelte and Angular |
| storage.md | SkeinStore, in-memory + Postgres, pgvector, checkpointer |
| memory.md | Agent memory patterns — shapes, dedup, recall, background writes |
| runs-and-redis.md | Run engine, queue, cross-instance streaming |
| crons.md | Scheduled runs — the Crons resource and the scheduler |
| console.md | The skein console — a web UI served by the server itself |
| errors-and-logging.md | What a failed run reports, and where — wire, log, skein dev |
| observability.md | Tracing + metrics — LangSmith, PostHog, OpenTelemetry, custom |
| deploy.md | Deploy anywhere — Cloud Run, Railway, Fly, Render, AWS, K8s |
| performance.md | Sizing, every tuning knob, backpressure + drops, triage |
| profiling.md | Learn measurement/profiling; Node, Bun, Deno hands-on recipes |
| bundling.md | Bundling skein yourself — ESM, require(), what to externalize |
| roadmap.md | Milestones and post-MVP non-goals |
Working on skein-js rather than with it? The contributor docs live in the repo, not on this site: CONTRIBUTING.md, AGENTS.md, reuse.md (what we reuse vs. rebuild), code-practices.md and testing.md.
References
- Agent Protocol — https://github.com/langchain-ai/agent-protocol
- LangGraph.js docs — https://docs.langchain.com/oss/javascript/langgraph/overview
- LangGraph.js source — https://github.com/langchain-ai/langgraphjs
- aegra (Python prior art) — https://github.com/aegra/aegra · https://www.aegra.dev