Skip to main content

Build your first Mission Control in 15 minutes

Start with the example Mission Control — a three-panel Next.js dashboard that talks to a running Ethos server over RPC and SSE. By minute fifteen you have sessions listing, live chat streaming, personality switching, and memory inspection all working in the browser.

Goal

By the end, you have:

  • A running Ethos server with the web API enabled.
  • A minted API key with the correct scopes and allowed origin.
  • The example Mission Control running on localhost:3001, connected to Ethos on localhost:3000.
  • Live chat that streams tool events and assistant responses via SSE.
  • Session listing, creation, and deletion working through the SDK's RPC client.
  • Memory (MEMORY.md and USER.md) visible in the side panel.

Prereqs

  • Node 24+. Check with node --version.
  • pnpm 10. corepack enable or npm install -g pnpm@10.
  • A running Ethos server. Either pnpm dev from the monorepo root or ethos serve from a binary install.
  • An LLM provider configured in Ethos (~/.ethos/config.yaml with a valid Anthropic/OpenAI key).

1. Clone and install the SDK repo

git clone https://github.com/MiteshSharma/control-plane-sdk.git
cd control-plane-sdk
pnpm install

The workspace resolves @ethosagent/sdk and @ethosagent/web-contracts from packages/. No published npm packages are required.

2. Start Ethos with the web API

In a separate terminal, start the Ethos server:

# From the ethos monorepo
ethos serve

Confirm it is running:

curl http://localhost:3000/rpc

A JSON response (even an error shape) means the server is up.

3. Mint an API key

The Mission Control authenticates with a bearer token, not browser cookies. Mint a key from the Ethos CLI:

ethos api-key create \
--name "mission-control-dev" \
--scopes sessions:read,sessions:write,chat:send,personalities:read,memory:read,memory:write,events:subscribe \
--allowed-origins http://localhost:3001

Copy the returned sk-ethos-... secret. This is shown once — store it somewhere safe.

The --allowed-origins flag is mandatory. It pins the key to requests originating from http://localhost:3001. Requests from other origins are rejected, even with a valid key.

4. Configure .env

cd examples/mission-control
cp .env.example .env.local

Edit .env.local:

NEXT_PUBLIC_ETHOS_BASE_URL=http://localhost:3000
NEXT_PUBLIC_ETHOS_API_KEY=sk-ethos-your-key-here

Replace sk-ethos-your-key-here with the key from step 3.

Both variables are prefixed NEXT_PUBLIC_ because the SDK client runs in the browser. The API key is visible in client-side JavaScript — this is intentional for local development. For production deployments, see Deploy Mission Control with a remote Ethos.

5. Start the dashboard

pnpm dev

Open http://localhost:3001. Three panels appear:

PanelComponentWhat it does
LeftSessionListLists sessions from ethos.rpc.sessions.list(). Click to activate.
CenterChatPanelSends messages via ethos.rpc.chat.send(), streams responses via EventStream.
RightSidePanelPicks a personality from ethos.rpc.personalities.list(), shows MEMORY.md and USER.md from ethos.rpc.memory.list().

6. Send your first message

Type a message in the center panel and press Enter. The SDK fires a chat.send RPC:

const res = await ethos.rpc.chat.send({
sessionId: sessionId ?? undefined,
clientId: 'mission-control',
text,
personalityId: personalityId ?? undefined,
});

The server returns a sessionId and turnId. The ChatPanel subscribes to SSE on that session:

const sub = EventStream({
baseUrl: ethos.baseUrl,
apiKey: process.env.NEXT_PUBLIC_ETHOS_API_KEY ?? '',
sessionId,
onEvent: handleEvent,
onError: (err) => console.error('SSE error:', err),
});

Events stream in: text_delta (token-by-token text), tool_start / tool_end (tool execution), and done (turn complete). The chat panel renders each in real time.

7. Explore sessions and memory

Click the Refresh button in the left panel. Your new session appears — identified by the first 8 characters of its ID. Click it to reload the chat for that session.

In the right panel, select a personality from the dropdown. Below it, MEMORY.md and USER.md display the current memory content for that personality scope. Ask the agent to remember something, then click Refresh — the memory content updates.

8. Understand the wiring

The entire SDK connection lives in one file — src/lib/ethos.ts:

import { EthosClient } from '@ethosagent/sdk';

export const ethos = new EthosClient({
baseUrl: process.env.NEXT_PUBLIC_ETHOS_BASE_URL ?? 'http://localhost:3000',
apiKey: process.env.NEXT_PUBLIC_ETHOS_API_KEY ?? '',
});

EthosClient provides:

  • ethos.rpc — a typed RPC client. Every namespace (sessions, chat, personalities, memory, tools, etc.) is fully typed from the @ethosagent/web-contracts contract. TypeScript autocomplete covers every method and its input/output shapes.
  • ethos.baseUrl — passed to EventStream for SSE subscriptions.

EventStream is a separate function export from @ethosagent/sdk. It opens an SSE connection to /sse/sessions/:id, parses each data: line against SseEventSchema, and calls onEvent with typed events.

Troubleshooting

Connection refused on localhost:3000 — Ethos is not running. Start it with ethos serve or pnpm dev in the monorepo.

401 Unauthorized — The API key is missing or invalid. Re-check .env.local. Make sure NEXT_PUBLIC_ETHOS_API_KEY matches the minted key exactly.

403 Forbidden (origin mismatch) — The allowedOrigins on the key does not include http://localhost:3001. Revoke and re-create the key with the correct origin.

SSE connects but no events appear — Check the browser console for errors. Common cause: the events:subscribe scope is missing from the key.

What you learned

  • EthosClient wraps an oRPC client typed against @ethosagent/web-contracts.
  • EventStream opens an SSE connection with automatic reconnect and resume (via Last-Event-ID).
  • API keys are scoped by capability and pinned to allowed origins.
  • The three-panel layout is a starting point — fork it and reshape for your use case.

Next step

The example is a template, not a final product. Build on it: