App Server
Build custom controllers and UIs on top of Letta Code's local app-server websocket protocol
App Server is the local websocket control plane for Letta Code runtimes. Use it when you want to build your own controller, UI, daemon, or multi-agent orchestrator on top of Letta Code without shelling out to the CLI or using the higher-level Letta Code SDK.
App Server exposes native v2 websocket frames. A client connects to the local server, starts or resumes a runtime for an agent and conversation, sends turns, streams events, and handles controller-owned tools.
When to use App Server
Section titled “When to use App Server”Use App Server when you need a protocol-level control plane:
- Building a desktop, web, or mobile controller for a local Letta Code runtime
- Building a daemon that coordinates multiple Letta Code agents
- Registering application-owned tools that execute in your controller process
- Streaming detailed runtime events into your own UI
- Managing agent runtimes without invoking
lettathroughBash
Use the Letta Code SDK when you want a higher-level TypeScript library that owns the runtime lifecycle for you. Use the Letta API client SDKs when you want to build server-side agents directly on the Letta API without the Letta Code harness.
Core model
Section titled “Core model”App Server is organized around a small set of concepts:
| Concept | Meaning |
|---|---|
| App Server process | A local letta app-server process that hosts the websocket transport and a Letta Code listener runtime |
| Control channel | Websocket channel for commands, request responses, and controller-owned tool callbacks |
| Stream channel | Websocket channel for streamed agent output and runtime updates |
| Runtime | A resolved { agent_id, conversation_id } scope |
| Turn | One input command that sends user messages or approval responses to the runtime |
| External tool | A tool registered by the controller during runtime_start and executed through callbacks |
A client should treat both websocket channels as a single event stream. Request responses use request_id for correlation, but runtime state updates and turn events may be emitted independently of the command that caused them.
Protocol shape
Section titled “Protocol shape”App Server does not use JSON-RPC. Each websocket frame is a JSON object with a type field.
{ "type": "runtime_start", "request_id": "runtime-1", "agent_id": "agent-123", "conversation_id": "conv-123"}Responses and events use the same discriminated-union style:
{ "type": "runtime_start_response", "request_id": "runtime-1", "success": true, "runtime": { "agent_id": "agent-123", "conversation_id": "conv-123" }, "created": { "agent": false, "conversation": false }, "agent": {}, "conversation": {}}Main command flow
Section titled “Main command flow”Most clients follow this sequence:
- Start App Server with
letta app-server. - Connect both websocket channels.
- Send
runtime_startto resolve or create an agent and conversation. - Send
inputto start turns. - Read
stream_delta,update_loop_status,update_device_status,update_queue, and other events from both channels. - Send
syncafter reconnects or when a UI needs an authoritative replay. - Send
abort_messageto cancel active work.
What App Server owns
Section titled “What App Server owns”App Server owns runtime execution:
- Tool preparation and local tool execution
- CWD and permission mode for the runtime
- Agent and conversation resolution
- Turn queueing and streaming
- State replay through
syncandruntime_start - External tool callback routing
Your application should own product state:
- User accounts and UI state
- Team/task registries
- Dashboard state
- Durable job results
- Reconnect and retry policy
- Any domain-specific database
This separation keeps App Server as the execution substrate and keeps application state recoverable outside the Letta Code process.
Current constraints
Section titled “Current constraints”- App Server currently supports
ws://loopback listen URLs. - The default listen URL is
ws://127.0.0.1:0, which chooses an available port. - The default websocket path is
/ws. - Use
?channel=controland?channel=streamto open the two channels. - A process accepts one active control session at a time. A new control session replaces the existing listener runtime.
- The protocol is v2-only. Do not send legacy listener commands like
request_state,change_cwd, orcancel_run.
Subpages
Section titled “Subpages”- Quickstart - Start App Server and send the first turn.
- Protocol lifecycle - Understand runtime startup, turns, sync, and abort.
- External tools - Register tools that execute in your controller.
- Integration patterns - Design robust controllers and team orchestrators.