TypeScript developers should be able to deploy LangChain and LangGraph agents with the same feeling they get from Vite, Next.js, or Astro: write a typed config file, add extension packages, run one command, and get a production-ready deployment.
The proposed primitive is a TypeScript deployment compiler and CLI that lives in the LangChain ecosystem:
// langchain.config.ts
import { defineDeployment } from "langchain/deploy";
import { discord } from "@langchain/deploy-discord";
import { web } from "@langchain/deploy-web";
export default defineDeployment({
agents: {
support: "./src/agents/support.ts",
research: "./src/agents/research.ts:graph",
},
extensions: [
web({ agent: "support" }),
discord({ agent: "support", route: "/discord" }),
],
});Under the hood, this does not require a new LangSmith runtime. It compiles into the artifacts LangSmith Deployment already supports today:
- A
langgraph.jsonfile withgraphs,env,node_version,http.app,store,checkpointer,webhooks, and other supported fields. - A generated Hono app that aggregates all extension routes into the single
http.appentrypoint LangSmith already accepts. - Optional generated wrapper modules for intuitive agent paths that omit the
:exportNamesuffix. - CLI calls to the existing LangGraph TypeScript CLI for
dev,build,dockerfile,up, anddeploy.
The product idea is not "a new hosting platform." It is "deployment ergonomics for the hosting platform LangSmith already has."
Current LangSmith Deployment has the right primitives, but they are exposed at a low level:
- Applications are described by
langgraph.json. - A deployment can contain multiple graphs under the
graphskey. - TypeScript deployments use
node_version: 20and graph paths such as./src/graph.ts:graph. .envfiles are already supported through theenvkey, and the deploy CLI also reads API keys from.env.- Cloud deployments can be created or updated with
langgraph deploy. - Local development works with
npx @langchain/langgraph-cli dev. - Docker images can be built with
npx @langchain/langgraph-cli build. - Custom TypeScript routes are supported by exporting a Hono app and pointing
http.appat it. - Agent Server already exposes durable runs, threads, assistants, streaming, stores, webhooks, MCP, A2A, CORS, route toggles, and deployment logs.
The missing piece is not capability. The missing piece is a developer-facing composition layer.
Today, if a TypeScript developer wants multiple custom endpoints or integrations, they must hand-write an app.ts, import every integration directly, manually wire the routes, keep langgraph.json in sync, remember the Hono conventions, and understand how http.app interacts with deployment. That is workable for advanced users, but it is not an ecosystem primitive.
Call the primitive LangChain Deploy.
It has three parts:
defineDeployment()for typed deployment configuration.defineExtension()for reusable extension packages.langchain deployfor generating artifacts and delegating to the existing LangGraph CLI.
This gives TypeScript developers a first-class deploy experience while preserving LangSmith's existing deployment model.
- Compile to current infrastructure. The package should emit
langgraph.json, Hono route code, and optional wrapper files. It should not require Agent Server features that do not exist. - Keep the escape hatch. Developers can still provide raw
langgraphconfig, a custom Hono app, Dockerfile lines, or direct CLI flags. - Make the common path small. A one-agent deployment should need a config file and one command.
- Make extension authors powerful but bounded. Extensions can add custom routes, UI assets, webhook handlers, CORS settings, environment requirements, and generated files. They should not replace the Agent Server runtime.
- Treat agents as named resources. Extension code should get typed graph or assistant names from the deployment context, then use the existing LangGraph SDK client.
- Prefer generation over magic. Generated artifacts should be inspectable and committed or ignored depending on project preference.
npm create langchain-agent
cd my-agent
npm run deploy:devThe generated project contains:
my-agent/
langchain.config.ts
package.json
.env
src/
agents/
support.tsThe initial config is small:
import { defineDeployment } from "langchain/deploy";
export default defineDeployment({
agents: {
support: "./src/agents/support.ts",
},
});"./src/agents/support.ts" is intentionally nicer than ./src/agents/support.ts:graph. The compiler can generate a wrapper module when a file uses a default export, or resolve a named export when one is provided:
export default graph;becomes:
{
"graphs": {
"support": "./.langchain/generated/agents/support.ts:graph"
}
}If the developer wants exact control, they can write:
export default defineDeployment({
agents: {
support: "./src/agents/support.ts:supportGraph",
},
});Extensions feel like Vite plugins:
import { defineDeployment } from "langchain/deploy";
import { discord } from "@langchain/deploy-discord";
import { web } from "@langchain/deploy-web";
import { slack } from "@acme/langchain-deploy-slack";
export default defineDeployment({
agents: {
support: "./src/agents/support.ts",
research: "./src/agents/research.ts",
},
extensions: [
web({
agent: "support",
title: "Support agent",
route: "/",
}),
discord({
agent: "support",
route: "/discord",
}),
slack({
agent: "research",
route: "/slack/events",
}),
],
});The developer does not create app.ts. The compiler creates one generated Hono app and registers it through http.app.
export default defineDeployment({
agents: {
support: "./src/agents/support.ts",
},
targets: {
dev: {
name: "support-agent-dev",
type: "dev",
env: ".env",
},
production: {
name: "support-agent",
type: "prod",
env: ".env.production",
hostUrl: "https://api.host.langchain.com",
},
},
});Then:
langchain deploy --target dev
langchain deploy --target production
langchain deploy logs --target production --followThis maps to current CLI behavior:
namemaps to--nameorLANGSMITH_DEPLOYMENT_NAME.typemaps to--deployment-type.hostUrlmaps toLANGGRAPH_HOST_URL.envmaps to the generatedlanggraph.jsonenvfield.- The generated config path is passed with
-c.
Given:
export default defineDeployment({
agents: {
support: "./src/agents/support.ts",
},
extensions: [
web({ agent: "support" }),
discord({ agent: "support", route: "/discord" }),
],
});The compiler writes:
.langchain/
generated/
langgraph.dev.json
app.ts
agents/
support.ts
manifest.json
types.d.tslanggraph.dev.json:
{
"$schema": "https://langgra.ph/schema.json",
"node_version": "20",
"dependencies": ["."],
"graphs": {
"support": "./.langchain/generated/agents/support.ts:graph"
},
"env": ".env",
"http": {
"app": "./.langchain/generated/app.ts:app"
}
}app.ts:
import { Hono } from "hono";
import { createDeploymentContext } from "langchain/deploy/runtime";
import extension0 from "@langchain/deploy-web";
import extension1 from "@langchain/deploy-discord";
export const app = new Hono();
const ctx = createDeploymentContext({
app,
agents: {
support: {
id: "support",
},
},
});
extension0.setup(ctx, { agent: "support" });
extension1.setup(ctx, { agent: "support", route: "/discord" });The important trick: LangSmith only sees one Hono app. The developer sees many extensions.
An extension is a typed factory that receives options and returns hooks.
import { defineExtension } from "langchain/deploy";
import { Client } from "@langchain/langgraph-sdk";
export default function discord(options: DiscordOptions) {
return defineExtension({
name: "@langchain/deploy-discord",
config(ctx) {
ctx.requireEnv("DISCORD_PUBLIC_KEY");
ctx.requireEnv("DISCORD_BOT_TOKEN");
},
setup(ctx) {
ctx.route.post(options.route ?? "/discord", async (c) => {
const event = await c.req.json();
const client = new Client(ctx.clientOptions(c));
const result = await client.runs.wait(null, options.agent, {
input: {
messages: [
{
role: "user",
content: event.message,
},
],
},
raiseError: true,
});
return c.json(result);
});
},
});
}The API should feel familiar to Vite users but map to deployment concepts:
type DeploymentExtension = {
name: string;
enforce?: "pre" | "post";
apply?: "dev" | "build" | "deploy" | ((target: DeploymentTarget) => boolean);
config?: (ctx: ConfigContext) => void | Partial<DeploymentConfig>;
configResolved?: (ctx: ResolvedConfigContext) => void;
generate?: (ctx: GenerateContext) => void | Promise<void>;
setup?: (ctx: RuntimeContext) => void;
};Extensions can use current LangSmith infrastructure in these ways:
- Add Hono routes to the generated app.
- Add route-local middleware for the routes they own.
- Add static assets or generated UI bundles served by Hono routes.
- Declare required environment variables.
- Add
http.corssettings. - Add
webhookspolicy. - Add
storeandcheckpointerconfiguration. - Add
dockerfile_linesfor OS-level dependencies. - Emit generated files that are included in the project dependency.
- Expose LangGraph SDK client configuration for invoking deployed agents through the Agent Server API.
Extensions should not depend on unavailable runtime hooks. For example, TypeScript custom routes are supported today through Hono, but Python-only middleware and lifespan features should not be presented as TypeScript extension capabilities unless Agent Server adds that support.
Extensions should use the existing LangGraph TypeScript SDK instead of a new deployment-specific agent interface.
import { Client } from "@langchain/langgraph-sdk";
export default defineExtension({
name: "my-extension",
setup(ctx) {
// `ctx` is the runtime context passed to this extension by the
// generated deployment app. It is not a global variable.
ctx.route.post("/ask", async (c) => {
const client = new Client(ctx.clientOptions(c));
const thread = client.threads.stream({ assistantId: ctx.agents.support });
await thread.run.start({
input: {
messages: [{ role: "user", content: "hello" }],
},
});
const output = await thread.output;
await thread.close();
return c.json(output);
});
},
});Here, "support" is the graph ID from the agents map. Agent Server already treats graph IDs as default assistant IDs, and the SDK already exposes the concepts extension authors need: assistants, threads, runs, streaming, store access, and configuration.
The deployment primitive should only provide the missing context required to construct the SDK client inside generated Hono routes:
type RuntimeContext = {
route: Hono;
agents: {
support: "support";
research: "research";
};
clientOptions(c?: HonoContext): ConstructorParameters<typeof Client>[0];
};For real-time routes and UI extensions, extension authors can use the SDK's recommended thread-centric streaming API:
const client = new Client(ctx.clientOptions(c));
const thread = client.threads.stream({ assistantId: ctx.agents.support });
await thread.run.start({
input: {
messages: [{ role: "user", content: "hello" }],
},
});
for await (const message of thread.messages) {
// Forward tokens to a browser, Discord interaction, Slack response, etc.
}
await thread.close();For webhook-style or background integrations, authors can use non-streaming SDK methods such as client.runs.create(...), client.runs.wait(...), and client.threads.create(...).
Implementation options:
- For local dev,
ctx.clientOptions(c)can derive the current server origin from the Hono request and point@langchain/langgraph-sdkat the local dev server. - For deployed routes,
ctx.clientOptions(c)can point the SDK at the same deployment URL when available, or use relative Agent Server endpoints when the runtime supports it. - The context should not re-expose SDK methods. It should make
new Client(...)easy and type-safe. - Do not use
RemoteGraphto call the same deployment from inside a graph. Existing docs warn against same-deploymentRemoteGraphcomposition because it can cause resource exhaustion. Route handlers should use the Agent Server run APIs.
This is already possible by hand today. The primitive packages the setup and keeps extension code aligned with the first-party SDK.
The CLI should be a thin, friendly wrapper around the existing LangGraph CLI.
langchain deploy dev
langchain deploy build
langchain deploy dockerfile Dockerfile
langchain deploy up
langchain deploy --target production
langchain deploy logs --target production --follow
langchain deploy doctorGenerates artifacts and runs:
npx @langchain/langgraph-cli dev -c .langchain/generated/langgraph.dev.jsonGenerates artifacts and runs:
npx @langchain/langgraph-cli build -c .langchain/generated/langgraph.production.json -t <tag>Generates artifacts and runs:
npx @langchain/langgraph-cli deploy -c .langchain/generated/langgraph.production.json --name <name> --deployment-type <type>Validates the project before the slow deployment path:
- Confirms every agent path resolves.
- Confirms graph exports are usable.
- Confirms extension names are unique.
- Confirms route collisions.
- Confirms required environment variables exist for the selected target.
- Warns if a custom route shadows an Agent Server route.
- Warns if an extension adds Dockerfile lines that require Docker for local builds.
- Prints the generated
langgraph.jsonpath.
This is a major developer-experience win because most deployment failures today are configuration or packaging mistakes.
type LangChainDeploymentConfig = {
agents: Record<string, AgentEntry>;
extensions?: ExtensionInstance[];
targets?: Record<string, DeploymentTarget>;
env?: true | string | Record<string, string>;
nodeVersion?: 20;
apiVersion?: string;
http?: {
cors?: CorsConfig;
configurableHeaders?: HeaderPolicy;
loggingHeaders?: HeaderPolicy;
enableCustomRouteAuth?: boolean;
disableMeta?: boolean;
disableAssistants?: boolean;
disableRuns?: boolean;
disableThreads?: boolean;
disableStore?: boolean;
disableUi?: boolean;
disableMcp?: boolean;
disableA2a?: boolean;
disableWebhooks?: boolean;
mountPrefix?: string;
};
store?: StoreConfig;
checkpointer?: CheckpointerConfig;
webhooks?: WebhooksConfig;
dockerfileLines?: string[];
raw?: Record<string, unknown>;
};The raw escape hatch lets advanced users pass through LangGraph configuration fields before the wrapper supports first-class types.
By default:
defineDeployment({
agents: {
support: "./src/agents/support.ts",
},
});compiles with:
{
"env": ".env"
}if .env exists.
For multiple targets:
defineDeployment({
targets: {
dev: { env: ".env" },
staging: { env: ".env.staging" },
production: { env: ".env.production" },
},
});Each target gets a generated langgraph.<target>.json.
Secrets remain normal LangSmith deployment environment variables. The primitive can read .env for local dev and CLI deploys, but it should not invent a separate secrets store. If a team uses the LangSmith UI to manage production secrets, the target can set:
production: {
name: "support-agent",
type: "prod",
env: false,
}Extensions should be described as packaged deployment capabilities, not as thin option bags. Each extension owns a small slice of the generated Hono app and uses the LangGraph SDK to talk to one or more deployed agents. The developer configures intent: which agent, which route, and which integration-specific options. The extension handles the repeated deployment plumbing.
web({
agent: "support",
route: "/",
title: "Support agent",
theme: {
accent: "#1C3D5A",
},
});What it does:
- Serves a small chat UI from the same Agent Server deployment.
- Opens a streaming route that calls
client.threads.stream({ assistantId: "support" }). - Handles thread creation, message submission, token streaming, interrupt display, and final state rendering.
- Adds any required static assets to the generated deployment output.
Why this is helpful:
Without the extension, a developer has to create a frontend bundle, serve it from Hono, define an API route, instantiate the LangGraph SDK client, stream events, and keep that custom app wired into langgraph.json. With the extension, a working UI is attached to the deployed agent with one config entry. The route and agent are explicit, so the compiler can catch route conflicts and invalid agent names.
discord({
agent: "support",
route: "/discord/interactions",
});What it does:
- Adds the endpoint Discord calls for interaction events.
- Declares required environment variables such as the Discord public key and bot token.
- Verifies the incoming request, converts a Discord message into the agent's input format, calls the configured agent with the LangGraph SDK, and formats the response for Discord.
- Optionally maps Discord channel or user IDs to LangGraph thread IDs so conversations stay stateful.
Why this is helpful:
Discord integration has a lot of boilerplate that is unrelated to agent logic: request verification, response deadlines, thread mapping, retries, and route setup. The deployment config makes the binding obvious: Discord events at /discord/interactions go to the support agent. The extension author packages the operational details once, and every agent developer reuses the same deployment behavior.
twilioWhatsApp({
agent: "support",
route: "/twilio/whatsapp",
});What it does:
- Adds the webhook endpoint Twilio calls for inbound WhatsApp messages.
- Declares required Twilio credentials and webhook verification settings.
- Converts inbound messages into LangGraph run input.
- Uses the SDK to continue a thread for that phone number or start a stateless run.
- Returns TwiML or calls Twilio's API to send an async response.
Why this is helpful:
The deployment becomes the integration backend. The developer does not need a separate Express app, serverless function, or queue just to connect Twilio to the agent. The extension can standardize phone-number-to-thread mapping while still letting the developer choose which deployed agent handles the conversation.
api({
routes: {
"/api/ask": {
agent: "support",
input: z.object({ message: z.string() }),
stream: true,
},
},
});What it does:
- Adds a public or private HTTP endpoint for an application frontend.
- Validates request bodies with the provided schema.
- Converts the validated body into agent input.
- Streams the result or returns a final JSON response using the SDK.
- Can generate a typed client helper for the app that calls
/api/ask.
Why this is helpful:
Many teams do not want to expose raw Agent Server endpoints directly to their product frontend. They want an application-specific API such as /api/ask, /api/triage, or /api/search. This extension gives them that route without hiding LangSmith. It is still just a Hono route over Agent Server runs, but the repetitive validation and streaming glue are generated from a typed declaration.
policy({
disableDocsInProduction: true,
requireWebhookHttps: true,
corsOrigins: ["https://app.example.com"],
});What it does:
- Applies deployment configuration that already exists in
langgraph.json. - Sets production CORS rules.
- Disables metadata or docs routes for production targets.
- Restricts webhook destinations to HTTPS or approved domains.
- Fails
langchain deploy doctorif a target violates the policy.
Why this is helpful:
These settings are easy to forget because they are not part of the agent's graph code. A policy extension lets a platform team publish deployment standards once and have every agent project apply them consistently. It compiles to existing http and webhooks configuration, so it does not require new LangSmith infrastructure.
This creates a marketplace-shaped extension surface without creating a new platform surface.
Vite became powerful because framework authors could package conventions as plugins. Next.js became approachable because routing, build output, and deployment conventions are encoded into the framework. LangSmith already has the runtime for durable agents, but TypeScript developers still need to assemble deployment details manually.
LangChain Deploy can make the unit of sharing "an agent deployment extension":
@langchain/deploy-web@langchain/deploy-discord@langchain/deploy-slack@langchain/deploy-whatsapp@langchain/deploy-twilio@langchain/deploy-auth0@langchain/deploy-openapi@langchain/deploy-admin
Each package is just TypeScript, Hono routes, generated files, and current LangSmith config. That means the ecosystem can move quickly without waiting for new Agent Server primitives.
- A new runtime separate from Agent Server.
- A second deployment backend.
- A custom secrets manager.
- A new graph execution model.
- Same-deployment
RemoteGraphcomposition from inside graph execution. - TypeScript middleware or lifespan claims beyond what Agent Server supports for TypeScript custom routes.
- Automatic production secret upload unless the existing Control Plane API supports it directly.
- Add
defineDeployment()and config types. - Load
langchain.config.tswith a TS runtime or bundler. - Generate
langgraph.json. - Generate wrappers for default-export agents.
- Detect
.env. - Delegate
dev,build,up, anddeployto the LangGraph CLI. - Add
doctor.
- Add
defineExtension(). - Generate one Hono
app.ts. - Add route registration, env requirements, route collision detection, and target-aware
apply. - Add
ctx.clientOptions()and typedctx.agentsso extensions can construct@langchain/langgraph-sdkclients directly. - Ship
@langchain/deploy-webas the reference extension.
- Publish extension authoring docs.
- Add templates for Discord, Slack, WhatsApp, and custom UI extensions.
- Add a gallery of extension packages.
- Add generated type declarations for route and agent names.
- Surface generated manifest metadata in deployment details if supported.
- Show extension names and routes in build logs or deployment metadata.
- Link custom routes from the deployment page.
This phase should remain optional. The core value does not depend on LangSmith UI changes.
- Should the package live at
langchain/deploy,@langchain/deploy, or inside@langchain/langgraph-cli? - Should generated artifacts be committed, ignored, or configurable?
- Should extension route order follow Vite-style
enforce, array order only, or both? - Should the default agent export convention prefer
default,graph, or require explicit symbols in strict mode? - Can Agent Server expose an officially supported in-process helper for custom routes to create runs without HTTP loopback?
- Should the CLI support GitHub-based deployments, or only local build and deploy flows at first?
- Should production
.envloading be opt-in to avoid accidental secret inclusion in generated artifacts?
LangSmith Deployment already has the runtime. LangChain Deploy would provide the product-shaped developer interface.
The mental model becomes:
agents + extensions + targets => LangSmith deployment artifactsFor a TypeScript developer, that is a much stronger primitive than "write langgraph.json, write one custom Hono app, wire every integration by hand, then remember the right CLI flags."
It makes LangSmith Deployment feel native to the TypeScript ecosystem while staying honest about what the platform can run today.