Execution Server
The shape serve command starts an in-process execution server that accepts
Shape code over TCP using the wire protocol. Unlike shape wire-serve (which
shells out to shape run), shape serve runs code directly in an embedded
VM — eliminating process startup overhead and enabling persistent state.
Quick Start
Section titled “Quick Start”# Start with defaults (localhost:9527, no auth)shape serve
# With authentication and concurrency limitshape serve --auth-token secret --max-concurrent 8
# Custom addressshape serve --address 127.0.0.1:8000CLI Flags
Section titled “CLI Flags”| Flag | Default | Description |
|---|---|---|
--address | 127.0.0.1:9527 | Bind address |
--tls-cert | — | TLS certificate (PEM). Required for non-localhost. |
--tls-key | — | TLS private key (PEM). Required for non-localhost. |
--auth-token | — | Bearer token. If set, clients must authenticate first. |
--sandbox | strict | Sandbox level: strict, permissive, or none |
--max-concurrent | 4 | Max parallel executions (semaphore-guarded) |
-m, --mode | jit | Execution mode: vm or jit (matches the binary’s default — JIT v2 / MirToIR) |
Safety
Section titled “Safety”shape serve refuses to start on a non-loopback address without
--tls-cert and --tls-key. This prevents accidentally exposing code
execution to the network without encryption.
Protocol
Section titled “Protocol”The server uses the standard wire protocol: length-prefixed MessagePack
frames with optional zstd compression. Every message is a variant of the
WireMessage enum.
Frame Format
Section titled “Frame Format”[4-byte big-endian length] [flags: u8] [MessagePack body...]All V1 messages (Call, BlobNegotiation, Sidecar) and V2 messages (Execute, Validate, Auth, Ping/Pong) share the same framing.
Message Types
Section titled “Message Types”Ping / Pong
Section titled “Ping / Pong”Discover server capabilities:
→ WireMessage::Ping← WireMessage::Pong(ServerInfo { shape_version: "0.2.0", wire_protocol: 2, capabilities: ["execute", "validate", "call", "blob-negotiation"] })Execute / ExecuteResponse
Section titled “Execute / ExecuteResponse”Run Shape source code:
→ WireMessage::Execute(ExecuteRequest { code: "fn main() { 42 }", request_id: 1 })← WireMessage::ExecuteResponse(ExecuteResponse { request_id: 1, success: true, output: Some("42"), error: None, diagnostics: [], metrics: Some(ExecutionMetrics { wall_time_ms: 3, ... }) })Execution runs in a blocking thread pool (tokio::task::spawn_blocking)
with the full Shape engine pipeline: parse → compile → VM execute.
Validate / ValidateResponse
Section titled “Validate / ValidateResponse”Parse and type-check without executing:
→ WireMessage::Validate(ValidateRequest { code: "let x: int = \"oops\"", request_id: 2 })← WireMessage::ValidateResponse(ValidateResponse { request_id: 2, success: false, diagnostics: [WireDiagnostic { severity: "error", message: "...", line: Some(1), column: Some(16) }] })Auth / AuthResponse
Section titled “Auth / AuthResponse”Required when --auth-token is set. Must be sent before Execute/Validate/Call:
→ WireMessage::Auth(AuthRequest { token: "secret" })← WireMessage::AuthResponse(AuthResponse { authenticated: true, error: None })Without prior authentication, Execute/Validate/Call requests return an error.
Call / CallResponse
Section titled “Call / CallResponse”The function call protocol enables transparent remote execution of compiled
Shape functions. The @remote annotation builds on this — when you annotate a
function with @remote("addr"), calling it locally serializes the function
bytecode and arguments, sends them to the server, and returns the result:
from std::core::remote use { @remote }
@remote("127.0.0.1:9527")fn compute(data) { data.map(|x| x * 2) }
let result = compute([1, 2, 3])// result: Ok([2, 4, 6]) — executed on the remote serverThis works with all value types (integers, strings, arrays, objects, closures) and with foreign functions (Python, TypeScript) — the remote server executes the Python/TypeScript code using its loaded language extensions.
Under the hood, @remote sends a RemoteCallRequest with the compiled
BytecodeProgram and function arguments. Blob negotiation and sidecar
splitting work as documented in Wire Protocol.
See Standard Library: Remote for the full API.
Connection Lifecycle
Section titled “Connection Lifecycle”Client Server │ │ │── Ping ────────────────────────────────>│ │<── Pong(ServerInfo) ────────────────────│ │ │ │── Auth(token) ─────────────────────────>│ (if --auth-token set) │<── AuthResponse(ok) ───────────────────│ │ │ │── Execute(code) ───────────────────────>│ acquire semaphore │<── ExecuteResponse(output) ────────────│ release semaphore │ │ │── Execute(code) ───────────────────────>│ │<── ExecuteResponse(output) ────────────│ │ │ │ (disconnect) │Each TCP connection maintains its own state: authentication status, blob cache for negotiation, and pending sidecars.
MCP Integration
Section titled “MCP Integration”The MCP server (shape-mcp) can use shape serve as
its execution backend instead of shelling out to shape run.
Set the SHAPE_SERVE_ADDR environment variable:
# Terminal 1: start the execution servershape serve
# Terminal 2: start MCP with wire backendSHAPE_SERVE_ADDR=127.0.0.1:9527 shape-mcpWhen SHAPE_SERVE_ADDR is set, the MCP server connects to the execution
server via MessagePack-over-TCP. This eliminates per-request process
startup and provides faster code execution.
If the env var is unset, MCP falls back to CLI shell-out (shape run).
If the env var is set but the server is unreachable, MCP returns an error —
it does not silently fall back.
Compared to wire-serve
Section titled “Compared to wire-serve”shape serve | shape wire-serve | |
|---|---|---|
| Execution | In-process VM | Shell-out to shape run |
| Protocol | V2 (Execute, Validate, Auth, Ping) | V1 (custom WireRequest/WireResponse) |
| Auth | Bearer token | None |
| TLS safety | Refuses non-localhost without TLS | No check |
| Concurrency | Semaphore-limited | Unbounded |
shape wire-serve is preserved for backward compatibility but shape serve
is the recommended replacement.
See Also
Section titled “See Also”- Standard Library: Remote —
@remoteannotation andremote::executeAPI - Wire Protocol — framing, compression, blob negotiation, sidecars
- Transport Layer — TCP, QUIC, and the
Connectiontrait - MCP Server — AI tool integration