@electric-ax/agents 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/entrypoint.js +474 -737
  2. package/dist/index.cjs +470 -733
  3. package/dist/index.d.cts +68 -35
  4. package/dist/index.d.ts +69 -36
  5. package/dist/index.js +489 -751
  6. package/docs/entities/agents/horton.md +12 -12
  7. package/docs/entities/agents/worker.md +18 -18
  8. package/docs/entities/patterns/blackboard.md +6 -6
  9. package/docs/entities/patterns/dispatcher.md +1 -1
  10. package/docs/entities/patterns/manager-worker.md +1 -1
  11. package/docs/entities/patterns/map-reduce.md +1 -1
  12. package/docs/entities/patterns/pipeline.md +1 -1
  13. package/docs/entities/patterns/reactive-observers.md +2 -2
  14. package/docs/examples/playground.md +42 -26
  15. package/docs/index.md +25 -23
  16. package/docs/quickstart.md +12 -12
  17. package/docs/reference/agent-config.md +20 -12
  18. package/docs/reference/agent-tool.md +1 -1
  19. package/docs/reference/built-in-collections.md +21 -21
  20. package/docs/reference/cli.md +39 -30
  21. package/docs/reference/entity-definition.md +9 -9
  22. package/docs/reference/entity-handle.md +2 -2
  23. package/docs/reference/entity-registry.md +1 -1
  24. package/docs/reference/handler-context.md +34 -18
  25. package/docs/reference/mcp-registry.md +189 -0
  26. package/docs/reference/mcp-server-config.md +226 -0
  27. package/docs/reference/runtime-handler.md +25 -23
  28. package/docs/reference/shared-state-handle.md +7 -7
  29. package/docs/reference/state-collection-proxy.md +1 -1
  30. package/docs/reference/wake-event.md +23 -23
  31. package/docs/usage/app-setup.md +24 -23
  32. package/docs/usage/clients-and-react.md +40 -36
  33. package/docs/usage/configuring-the-agent.md +25 -19
  34. package/docs/usage/context-composition.md +12 -12
  35. package/docs/usage/defining-entities.md +36 -36
  36. package/docs/usage/defining-tools.md +45 -45
  37. package/docs/usage/embedded-builtins.md +54 -43
  38. package/docs/usage/managing-state.md +12 -12
  39. package/docs/usage/mcp-servers.md +354 -0
  40. package/docs/usage/overview.md +50 -45
  41. package/docs/usage/programmatic-runtime-client.md +51 -48
  42. package/docs/usage/shared-state.md +32 -32
  43. package/docs/usage/spawning-and-coordinating.md +9 -9
  44. package/docs/usage/testing.md +14 -14
  45. package/docs/usage/waking-entities.md +13 -13
  46. package/docs/usage/writing-handlers.md +52 -26
  47. package/package.json +9 -4
  48. package/scripts/sync-docs.mjs +42 -0
  49. package/docs/examples/mega-draw.md +0 -106
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Built-in collections
3
- titleTemplate: '... - Electric Agents'
3
+ titleTemplate: "... - Electric Agents"
4
4
  description: >-
5
5
  Reference for the 17 runtime-managed collections: runs, steps, texts, toolCalls, inbox, errors, and more.
6
6
  outline: [2, 3]
@@ -43,7 +43,7 @@ All collections use `key` as the primary key.
43
43
  ```ts
44
44
  interface Run {
45
45
  key: string
46
- status: 'started' | 'completed' | 'failed'
46
+ status: "started" | "completed" | "failed"
47
47
  finish_reason?: string
48
48
  }
49
49
  ```
@@ -55,7 +55,7 @@ interface Step {
55
55
  key: string
56
56
  run_id?: string
57
57
  step_number: number
58
- status: 'started' | 'completed'
58
+ status: "started" | "completed"
59
59
  finish_reason?: string
60
60
  model_provider?: string
61
61
  model_id?: string
@@ -69,7 +69,7 @@ interface Step {
69
69
  interface Text {
70
70
  key: string
71
71
  run_id?: string
72
- status: 'streaming' | 'completed'
72
+ status: "streaming" | "completed"
73
73
  }
74
74
  ```
75
75
 
@@ -91,7 +91,7 @@ interface ToolCall {
91
91
  key: string
92
92
  run_id?: string
93
93
  tool_name: string
94
- status: 'started' | 'args_complete' | 'executing' | 'completed' | 'failed'
94
+ status: "started" | "args_complete" | "executing" | "completed" | "failed"
95
95
  args?: unknown
96
96
  result?: unknown
97
97
  error?: string
@@ -104,7 +104,7 @@ interface ToolCall {
104
104
  ```ts
105
105
  interface Reasoning {
106
106
  key: string
107
- status: 'streaming' | 'completed'
107
+ status: "streaming" | "completed"
108
108
  }
109
109
  ```
110
110
 
@@ -148,14 +148,14 @@ interface WakeEntry {
148
148
 
149
149
  interface WakeChangeEntry {
150
150
  collection: string
151
- kind: 'insert' | 'update' | 'delete'
151
+ kind: "insert" | "update" | "delete"
152
152
  key: string
153
153
  }
154
154
 
155
155
  interface WakeFinishedChildEntry {
156
156
  url: string
157
157
  type: string
158
- run_status: 'completed' | 'failed'
158
+ run_status: "completed" | "failed"
159
159
  response?: string // concatenated text deltas from the finished run
160
160
  error?: string // error message(s) if run_status is "failed"
161
161
  }
@@ -163,7 +163,7 @@ interface WakeFinishedChildEntry {
163
163
  interface WakeOtherChildEntry {
164
164
  url: string
165
165
  type: string
166
- status: 'spawning' | 'running' | 'idle' | 'stopped'
166
+ status: "spawning" | "running" | "idle" | "stopped"
167
167
  }
168
168
  ```
169
169
 
@@ -196,7 +196,7 @@ interface ChildStatusEntry {
196
196
  key: string
197
197
  entity_url: string
198
198
  entity_type: string
199
- status: 'spawning' | 'running' | 'idle' | 'stopped'
199
+ status: "spawning" | "running" | "idle" | "stopped"
200
200
  }
201
201
  ```
202
202
 
@@ -249,7 +249,7 @@ type Manifest =
249
249
 
250
250
  interface ManifestChildEntry {
251
251
  key: string
252
- kind: 'child'
252
+ kind: "child"
253
253
  id: string
254
254
  entity_type: string
255
255
  entity_url: string
@@ -259,7 +259,7 @@ interface ManifestChildEntry {
259
259
 
260
260
  interface ManifestSourceEntry {
261
261
  key: string
262
- kind: 'source'
262
+ kind: "source"
263
263
  sourceType: string
264
264
  sourceRef: string
265
265
  wake?: WakeConfig
@@ -268,16 +268,16 @@ interface ManifestSourceEntry {
268
268
 
269
269
  interface ManifestSharedStateEntry {
270
270
  key: string
271
- kind: 'shared-state'
271
+ kind: "shared-state"
272
272
  id: string
273
- mode: 'create' | 'connect'
273
+ mode: "create" | "connect"
274
274
  collections: Record<string, { type: string; primaryKey: string }>
275
275
  wake?: WakeConfig
276
276
  }
277
277
 
278
278
  interface ManifestEffectEntry {
279
279
  key: string
280
- kind: 'effect'
280
+ kind: "effect"
281
281
  id: string
282
282
  function_ref: string
283
283
  config: unknown
@@ -285,7 +285,7 @@ interface ManifestEffectEntry {
285
285
 
286
286
  interface ManifestContextEntry {
287
287
  key: string
288
- kind: 'context'
288
+ kind: "context"
289
289
  id: string
290
290
  name: string
291
291
  attrs: Record<string, string | number | boolean>
@@ -295,9 +295,9 @@ interface ManifestContextEntry {
295
295
 
296
296
  interface ManifestCronScheduleEntry {
297
297
  key: string
298
- kind: 'schedule'
298
+ kind: "schedule"
299
299
  id: string
300
- scheduleType: 'cron'
300
+ scheduleType: "cron"
301
301
  expression: string
302
302
  timezone?: string
303
303
  payload?: unknown
@@ -306,16 +306,16 @@ interface ManifestCronScheduleEntry {
306
306
 
307
307
  interface ManifestFutureSendScheduleEntry {
308
308
  key: string
309
- kind: 'schedule'
309
+ kind: "schedule"
310
310
  id: string
311
- scheduleType: 'future_send'
311
+ scheduleType: "future_send"
312
312
  fireAt: string
313
313
  targetUrl: string
314
314
  payload: unknown
315
315
  producerId: string
316
316
  from?: string
317
317
  messageType?: string
318
- status?: 'pending' | 'sent' | 'failed'
318
+ status?: "pending" | "sent" | "failed"
319
319
  sentAt?: string
320
320
  failedAt?: string
321
321
  lastError?: string
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: CLI
3
- titleTemplate: '... - Electric Agents'
3
+ titleTemplate: "... - Electric Agents"
4
4
  description: >-
5
5
  Command reference for the Electric Agents CLI: spawn, send, observe, inspect, list, and manage entities.
6
6
  outline: [2, 3]
@@ -16,14 +16,14 @@ npm install -g electric-ax
16
16
 
17
17
  ## Environment variables
18
18
 
19
- | Variable | Default | Purpose |
20
- | --------------------------------- | ----------------------- | --------------------------------------------- |
21
- | `ELECTRIC_AGENTS_URL` | `http://localhost:4437` | Server URL for entity commands and built-ins |
22
- | `ELECTRIC_AGENTS_IDENTITY` | `user@hostname` | Sender identity for messages |
23
- | `ELECTRIC_AGENTS_PORT` | `4437` | Port used by `start` / `quickstart` |
24
- | `ELECTRIC_AGENTS_BUILTIN_PORT` | `4448` | Webhook port for `start-builtin` |
25
- | `ELECTRIC_AGENTS_COMPOSE_PROJECT` | `electric-agents` | Docker Compose project name |
26
- | `ANTHROPIC_API_KEY` | - | Required for `start-builtin` and `quickstart` |
19
+ | Variable | Default | Purpose |
20
+ | -------------------------------- | ----------------------- | -------------------------------------------- |
21
+ | `ELECTRIC_AGENTS_URL` | `http://localhost:4437` | Server URL for entity commands and built-ins |
22
+ | `ELECTRIC_AGENTS_IDENTITY` | `user@hostname` | Sender identity for messages |
23
+ | `ELECTRIC_AGENTS_PORT` | `4437` | Port used by `start` / `quickstart` |
24
+ | `ELECTRIC_AGENTS_BUILTIN_PORT` | `4448` | Webhook port for `start-builtin` |
25
+ | `ELECTRIC_AGENTS_COMPOSE_PROJECT` | `electric-agents` | Docker Compose project name |
26
+ | `ANTHROPIC_API_KEY` | - | Required for `start-builtin` and `quickstart` |
27
27
 
28
28
  ## Commands
29
29
 
@@ -142,9 +142,9 @@ Start the built-in Horton runtime and register built-in agent types with the coo
142
142
  electric agents start-builtin --anthropic-api-key sk-ant-...
143
143
  ```
144
144
 
145
- | Option | Description |
146
- | --------------------------- | ------------------------------------------------ |
147
- | `--anthropic-api-key <key>` | Anthropic API key for the built-in Horton server |
145
+ | Option | Description |
146
+ | ------------------------------ | ---------------------------------------------- |
147
+ | `--anthropic-api-key <key>` | Anthropic API key for the built-in Horton server |
148
148
 
149
149
  ### <span class="cli-command"><code>quickstart [--anthropic-api-key &lt;key&gt;]</code></span> {#quickstart}
150
150
 
@@ -154,9 +154,9 @@ Start the coordinator server, print onboarding commands, and run the built-in ag
154
154
  electric agents quickstart --anthropic-api-key sk-ant-...
155
155
  ```
156
156
 
157
- | Option | Description |
158
- | --------------------------- | ------------------------------------------------ |
159
- | `--anthropic-api-key <key>` | Anthropic API key for the built-in Horton server |
157
+ | Option | Description |
158
+ | ------------------------------ | ---------------------------------------------- |
159
+ | `--anthropic-api-key <key>` | Anthropic API key for the built-in Horton server |
160
160
 
161
161
  ### <span class="cli-command"><code>stop [--remove-volumes]</code></span> {#stop}
162
162
 
@@ -171,6 +171,15 @@ electric agents stop --remove-volumes
171
171
  | ------------------ | ------------------------------ |
172
172
  | `--remove-volumes` | Remove Docker volumes as well. |
173
173
 
174
+ ### <span class="cli-command"><code>init [project-name]</code></span> {#init-project-name}
175
+
176
+ Scaffold a new Electric Agents project from the bundled starter template.
177
+
178
+ ```bash
179
+ electric agents init
180
+ electric agents init my-agents-app
181
+ ```
182
+
174
183
  ### <span class="cli-command"><code>completion [action]</code></span> {#completion-action}
175
184
 
176
185
  Set up shell completions. Without arguments, prints setup instructions.
@@ -202,32 +211,32 @@ import {
202
211
  createElectricProgram,
203
212
  getElectricCliEnv,
204
213
  run,
205
- } from 'electric-ax'
214
+ } from "electric-ax"
206
215
 
207
216
  const env = getElectricCliEnv({
208
- ELECTRIC_AGENTS_URL: 'http://localhost:4437',
209
- ELECTRIC_AGENTS_IDENTITY: 'docs@example.com',
217
+ ELECTRIC_AGENTS_URL: "http://localhost:4437",
218
+ ELECTRIC_AGENTS_IDENTITY: "docs@example.com",
210
219
  })
211
220
 
212
- const handlers = createElectricCliHandlers(env, 'electric agents')
221
+ const handlers = createElectricCliHandlers(env, "electric agents")
213
222
  const program = createElectricProgram({
214
223
  env,
215
224
  handlers,
216
- commandName: 'electric',
217
- commandPrefix: 'electric agents',
225
+ commandName: "electric",
226
+ commandPrefix: "electric agents",
218
227
  })
219
228
 
220
- await program.parseAsync(['node', 'electric', 'agents', 'types'])
229
+ await program.parseAsync(["node", "electric", "agents", "types"])
221
230
  ```
222
231
 
223
- | Export | Purpose |
224
- | ---------------------------------- | --------------------------------------------------------------------- |
225
- | `DEFAULT_ELECTRIC_AGENTS_URL` | Default server URL (`"http://localhost:4437"`). |
226
- | `getElectricCliEnv(env?)` | Reads `ELECTRIC_AGENTS_URL` and `ELECTRIC_AGENTS_IDENTITY`. |
227
- | `createElectricCliHandlers()` | Creates the default command handlers. |
228
- | `createElectricProgram()` | Creates the Commander program. |
229
- | `resolveCommandPrefix(argv, env?)` | Resolves help text examples for direct or package-manager invocation. |
230
- | `run(argv?)` | Runs the CLI entrypoint. |
232
+ | Export | Purpose |
233
+ | --------------------------------- | ------------------------------------------------------------------------ |
234
+ | `DEFAULT_ELECTRIC_AGENTS_URL` | Default server URL (`"http://localhost:4437"`). |
235
+ | `getElectricCliEnv(env?)` | Reads `ELECTRIC_AGENTS_URL` and `ELECTRIC_AGENTS_IDENTITY`. |
236
+ | `createElectricCliHandlers()` | Creates the default command handlers. |
237
+ | `createElectricProgram()` | Creates the Commander program. |
238
+ | `resolveCommandPrefix(argv, env?)` | Resolves help text examples for direct or package-manager invocation. |
239
+ | `run(argv?)` | Runs the CLI entrypoint. |
231
240
 
232
241
  The installed binaries are `electric` and `electric-ax`. The package also includes `electric-dev` for development workflows.
233
242
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: EntityDefinition
3
- titleTemplate: '... - Electric Agents'
3
+ titleTemplate: "... - Electric Agents"
4
4
  description: >-
5
5
  Type reference for EntityDefinition: description, state, schemas, and handler function signature.
6
6
  outline: [2, 3]
@@ -28,15 +28,15 @@ interface EntityDefinition {
28
28
 
29
29
  ## Fields
30
30
 
31
- | Field | Type | Required | Description |
32
- | ---------------- | ---------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
33
- | `description` | `string` | No | Human-readable description of the entity type. Used in type registration. |
34
- | `state` | `Record<string, CollectionDefinition>` | No | Custom state collections exposed via `ctx.db.actions` (writes) and `ctx.db.collections` (reads). |
31
+ | Field | Type | Required | Description |
32
+ | ---------------- | ---------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
33
+ | `description` | `string` | No | Human-readable description of the entity type. Used in type registration. |
34
+ | `state` | `Record<string, CollectionDefinition>` | No | Custom state collections exposed via `ctx.db.actions` (writes) and `ctx.db.collections` (reads). |
35
35
  | `actions` | `(collections) => Record<string, (...args) => void>` | No | Factory for custom non-CRUD actions. Receives TanStack DB collections, returns named action functions exposed on `ctx.actions`. |
36
- | `creationSchema` | `StandardJSONSchemaV1` | No | JSON Schema for spawn arguments validation. |
37
- | `inboxSchemas` | `Record<string, StandardJSONSchemaV1>` | No | JSON Schemas for inbound message types, keyed by message type. |
38
- | `outputSchemas` | `Record<string, StandardJSONSchemaV1>` | No | JSON Schemas for output event types. Defaults are provided by the runtime. |
39
- | `handler` | `(ctx, wake) => void \| Promise<void>` | Yes | The function invoked on each wake. Receives [`HandlerContext`](./handler-context) and [`WakeEvent`](./wake-event). |
36
+ | `creationSchema` | `StandardJSONSchemaV1` | No | JSON Schema for spawn arguments validation. |
37
+ | `inboxSchemas` | `Record<string, StandardJSONSchemaV1>` | No | JSON Schemas for inbound message types, keyed by message type. |
38
+ | `outputSchemas` | `Record<string, StandardJSONSchemaV1>` | No | JSON Schemas for output event types. Defaults are provided by the runtime. |
39
+ | `handler` | `(ctx, wake) => void \| Promise<void>` | Yes | The function invoked on each wake. Receives [`HandlerContext`](./handler-context) and [`WakeEvent`](./wake-event). |
40
40
 
41
41
  ## CollectionDefinition
42
42
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: EntityHandle
3
- titleTemplate: '... - Electric Agents'
3
+ titleTemplate: "... - Electric Agents"
4
4
  description: >-
5
5
  API reference for EntityHandle returned by spawn and observe: streams, status, text retrieval, and messaging.
6
6
  outline: [2, 3]
@@ -49,7 +49,7 @@ interface ChildStatusEntry {
49
49
  key: string
50
50
  entity_url: string
51
51
  entity_type: string
52
- status: 'spawning' | 'running' | 'idle' | 'stopped'
52
+ status: "spawning" | "running" | "idle" | "stopped"
53
53
  }
54
54
  ```
55
55
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: EntityRegistry
3
- titleTemplate: '... - Electric Agents'
3
+ titleTemplate: "... - Electric Agents"
4
4
  description: >-
5
5
  API reference for EntityRegistry: define, get, list, and clear entity type registrations.
6
6
  outline: [2, 3]
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: HandlerContext
3
- titleTemplate: '... - Electric Agents'
3
+ titleTemplate: "... - Electric Agents"
4
4
  description: >-
5
5
  API reference for HandlerContext: state, coordination, agent configuration, and execution control.
6
6
  outline: [2, 3]
@@ -44,11 +44,11 @@ interface HandlerContext<TState extends StateProxy = StateProxy> {
44
44
  }
45
45
  ): Promise<EntityHandle>
46
46
  observe(
47
- source: ObservationSource & { sourceType: 'entity' },
47
+ source: ObservationSource & { sourceType: "entity" },
48
48
  opts?: { wake?: Wake }
49
49
  ): Promise<EntityHandle>
50
50
  observe(
51
- source: ObservationSource & { sourceType: 'db' },
51
+ source: ObservationSource & { sourceType: "db" },
52
52
  opts?: { wake?: Wake }
53
53
  ): Promise<SharedStateHandle & ObservationHandle>
54
54
  observe(
@@ -64,6 +64,7 @@ interface HandlerContext<TState extends StateProxy = StateProxy> {
64
64
  payload: unknown,
65
65
  opts?: { type?: string; afterMs?: number }
66
66
  ): void
67
+ recordRun(): RunHandle
67
68
  setTag(key: string, value: string): Promise<void>
68
69
  removeTag(key: string): Promise<void>
69
70
  sleep(): void
@@ -74,27 +75,27 @@ interface HandlerContext<TState extends StateProxy = StateProxy> {
74
75
 
75
76
  ## Properties
76
77
 
77
- | Property | Type | Description |
78
- | --------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
79
- | `firstWake` | `boolean` | `true` on the entity's first-ever handler invocation. |
80
- | `tags` | `Readonly<EntityTags>` | Entity tags — key/value metadata associated with this entity. |
81
- | `entityUrl` | `string` | URL path of this entity (e.g. `"/chat/my-convo"`). |
82
- | `entityType` | `string` | Registered type name (e.g. `"chat"`). |
83
- | `args` | `Readonly<Record<string, unknown>>` | Spawn arguments passed when the entity was created. |
84
- | `db` | `EntityStreamDBWithActions` | The entity's TanStack DB instance with registered actions. |
85
- | `state` | `TState` | Proxy object keyed by collection name. Each property is a [`StateCollectionProxy`](./state-collection-proxy). |
86
- | `events` | `Array<ChangeEvent>` | Change events that triggered this wake. |
87
- | `actions` | `Record<string, (...args: unknown[]) => unknown>` | Custom non-CRUD actions from the entity definition's `actions` factory. Auto-generated CRUD actions live on `ctx.db.actions` and `ctx.state`. |
88
- | `electricTools` | `AgentTool[]` | Host-provided runtime-level tools to spread into agent config when needed. May be empty. |
78
+ | Property | Type | Description |
79
+ | ------------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
80
+ | `firstWake` | `boolean` | `true` during the initial setup pass while the entity has no persisted manifest entries. Use state checks for one-time plain state initialization. |
81
+ | `tags` | `Readonly<EntityTags>` | Entity tags — key/value metadata associated with this entity. |
82
+ | `entityUrl` | `string` | URL path of this entity (e.g. `"/chat/my-convo"`). |
83
+ | `entityType` | `string` | Registered type name (e.g. `"chat"`). |
84
+ | `args` | `Readonly<Record<string, unknown>>` | Spawn arguments passed when the entity was created. |
85
+ | `db` | `EntityStreamDBWithActions` | The entity's TanStack DB instance with registered actions. |
86
+ | `state` | `TState` | Proxy object keyed by collection name. Each property is a [`StateCollectionProxy`](./state-collection-proxy). |
87
+ | `events` | `Array<ChangeEvent>` | Change events that triggered this wake. |
88
+ | `actions` | `Record<string, (...args: unknown[]) => unknown>` | Custom non-CRUD actions from the entity definition's `actions` factory. Auto-generated CRUD actions live on `ctx.db.actions` and `ctx.state`. |
89
+ | `electricTools` | `AgentTool[]` | Host-provided runtime-level tools to spread into agent config when needed. May be empty. |
89
90
 
90
91
  ## Methods
91
92
 
92
93
  | Method | Return Type | Description |
93
94
  | --------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
94
95
  | `useAgent(config)` | `AgentHandle` | Configure the LLM agent. Must be called before `agent.run()`. See [`AgentConfig`](./agent-config). |
95
- | `useContext(config)` | `void` | Declare context sources with token budgets and cache tiers for the agent's context window. See [Context composition](/docs/agents/usage/context-composition). |
96
- | `timelineMessages(opts?)` | `Array<TimestampedMessage>` | Project the entity timeline into an ordered array of LLM messages. Typically used as the `content` function of a volatile source. See [Context composition](/docs/agents/usage/context-composition#timelinemessages). |
97
- | `insertContext(id, entry)` | `void` | Insert a durable context entry. Persists across wakes. Inserting with an existing `id` replaces the previous entry. See [Context entries](/docs/agents/usage/context-composition#context-entries). |
96
+ | `useContext(config)` | `void` | Declare context sources with token budgets and cache tiers for the agent's context window. See [Context composition](/docs/agents/usage/context-composition). |
97
+ | `timelineMessages(opts?)` | `Array<TimestampedMessage>` | Project the entity timeline into an ordered array of LLM messages. Typically used as the `content` function of a volatile source. See [Context composition](/docs/agents/usage/context-composition#timelinemessages). |
98
+ | `insertContext(id, entry)` | `void` | Insert a durable context entry. Persists across wakes. Inserting with an existing `id` replaces the previous entry. See [Context entries](/docs/agents/usage/context-composition#context-entries). |
98
99
  | `removeContext(id)` | `void` | Remove a context entry by id. |
99
100
  | `getContext(id)` | `ContextEntry \| undefined` | Get a context entry by id, or `undefined` if not found. |
100
101
  | `listContext()` | `Array<ContextEntry>` | List all context entries. |
@@ -103,6 +104,21 @@ interface HandlerContext<TState extends StateProxy = StateProxy> {
103
104
  | `observe(source, opts?)` | `Promise<EntityHandle \| SharedStateHandle \| ObservationHandle>` | Observe a source. Return type depends on source type: `EntityHandle` for entities, `SharedStateHandle & ObservationHandle` for db, `ObservationHandle` otherwise. Use `entity()`, `cron()`, `entities()`, `db()` helpers to build sources. |
104
105
  | `mkdb(id, schema)` | `SharedStateHandle<T>` | Create a new shared state stream. See [`SharedStateHandle`](./shared-state-handle). |
105
106
  | `send(entityUrl, payload, opts?)` | `void` | Send a message to another entity. `opts` accepts `type` and `afterMs` (delay in milliseconds). |
107
+ | `recordRun()` | `RunHandle` | Record a non-LLM run in the built-in `runs` collection, so observers using `wake: "runFinished"` are notified when external work completes. |
106
108
  | `setTag(key, value)` | `Promise<void>` | Set a tag on this entity. |
107
109
  | `removeTag(key)` | `Promise<void>` | Remove a tag from this entity. |
108
110
  | `sleep()` | `void` | End the handler without running an agent. The entity remains idle until the next wake. |
111
+
112
+ ## RunHandle
113
+
114
+ `recordRun()` is for handlers that perform work outside `ctx.agent.run()` but still want to expose run lifecycle events.
115
+
116
+ ```ts
117
+ interface RunHandle {
118
+ readonly key: string
119
+ end(opts: { status: "completed" | "failed"; finishReason?: string }): void
120
+ attachResponse(text: string): void
121
+ }
122
+ ```
123
+
124
+ `attachResponse()` appends text deltas linked to the run, which can be included in `runFinished` wake payloads.
@@ -0,0 +1,189 @@
1
+ ---
2
+ title: McpRegistry
3
+ titleTemplate: "... - Electric Agents"
4
+ description: >-
5
+ API reference for the MCP Registry — addServer, applyConfig,
6
+ subscribe, reauthorize, and the lifecycle of an MCP server entry
7
+ inside the runtime.
8
+ outline: [2, 3]
9
+ ---
10
+
11
+ # McpRegistry
12
+
13
+ The MCP registry owns the live set of [Model Context Protocol](https://modelcontextprotocol.io) servers a runtime is connected to. It manages stdio subprocesses and HTTP clients, drives the OAuth state machine, and emits push-based snapshots so embedders can render UI without polling.
14
+
15
+ **Source:** `@electric-ax/agents-mcp` (re-exported as `McpRegistry` from `@electric-ax/agents`)
16
+
17
+ `BuiltinAgentsServer.mcpRegistry` exposes the instance that the embedded runtime owns; the [usage guide](/docs/agents/usage/mcp-servers) walks through registering servers from agent-host code.
18
+
19
+ ```ts
20
+ interface Registry {
21
+ addServer(cfg: McpServerConfig): Promise<AddServerResult>
22
+ applyConfig(cfg: McpConfig): Promise<AddServerResult[]>
23
+ removeServer(name: string): Promise<void>
24
+ list(): ReadonlyArray<ListedEntry>
25
+ get(name: string): Entry | undefined
26
+ finishAuth(server: string, code: string, state?: string): Promise<AddServerResult>
27
+ reauthorize(name: string): Promise<void>
28
+ disable(name: string): Promise<void>
29
+ enable(name: string): Promise<AddServerResult>
30
+ subscribe(handler: RegistrySubscriber): () => void
31
+ }
32
+ ```
33
+
34
+ ## Methods
35
+
36
+ | Method | Description |
37
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
38
+ | `addServer(cfg)` | Register or reconfigure a single server. Idempotent on unchanged `(name, url, transport, authMode, scopes, command, args)`. |
39
+ | `applyConfig(cfg)` | Replace the full set of registered servers. Adds new ones, reconfigures changed ones, and removes anything not in `cfg`. |
40
+ | `removeServer(name)` | Tear down a single server: close the transport, drop tokens from the in-memory cache, remove the entry. |
41
+ | `list()` | Returns the current snapshot as a plain array — same shape as the `servers` field of [`RegistrySnapshot`](#registrysnapshot). |
42
+ | `get(name)` | Internal lookup of a single entry, with the live `transport` handle and the resolved `provider`. Used by IPC handlers. |
43
+ | `finishAuth(name, code, state?)` | Complete the OAuth authorization-code flow for an `authenticating` server. Called by the embedder after intercepting the redirect URI. |
44
+ | `reauthorize(name)` | Force a fresh OAuth flow without removing the entry. Closes the transport, drops cached tokens (hooks remain registered), and rebuilds in place. |
45
+ | `disable(name)` | Pause a server. Closes the transport but keeps the entry; tokens stay in the cache. |
46
+ | `enable(name)` | Re-add a previously-disabled server using its last-known config. |
47
+ | `subscribe(handler)` | Push-based view of registry state. The handler fires synchronously with a sentinel snapshot, then on every mutation. Returns an unsubscribe function. |
48
+
49
+ ## `addServer` vs `applyConfig`
50
+
51
+ Both feed the same internal pipeline; pick by what you have:
52
+
53
+ - **`addServer(cfg)`** — register one server. Use when you're adding an entry in response to a user action, a per-session tool, or a one-off integration.
54
+ - **`applyConfig({ servers })`** — replace the full set. Anything in the registry that isn't in `cfg.servers` is removed; existing entries with unchanged config are left alone (no transport churn). This is what the file-based loaders for `mcp.json` and the desktop `settings.json` compile down to.
55
+
56
+ Idempotency is the load-bearing property: editors save spuriously, file watchers fire double events on macOS, and most apps re-apply the same baseline on every restart. Calling `applyConfig` with the same shape twice does nothing the second time, so it's safe to wire to noisy upstreams.
57
+
58
+ ## `AddServerResult`
59
+
60
+ `addServer` and `finishAuth` return a discriminated union so the caller can react without inspecting the registry afterwards:
61
+
62
+ ```ts
63
+ type AddServerResult =
64
+ | { state: "ready"; id: string; toolCount: number }
65
+ | { state: "authenticating"; id: string; authUrl: string }
66
+ | { state: "error"; id: string; error: McpToolError }
67
+ ```
68
+
69
+ | State | Meaning |
70
+ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
71
+ | `ready` | Connected and tools listed; calls available at the next agent wake. |
72
+ | `authenticating` | OAuth required. `authUrl` is the URL to send the user to. The desktop's `openAuthorizeUrl` hook opens it in a sandboxed BrowserWindow automatically. |
73
+ | `error` | Connect, transport, or auth-config failure. `error.kind` and `error.message` describe what went wrong. |
74
+
75
+ `applyConfig` returns one `AddServerResult` per server in the supplied config (in the same order).
76
+
77
+ ## Lifecycle of an entry
78
+
79
+ Every entry transitions through one of five statuses, surfaced on the snapshot. The states that matter to UI:
80
+
81
+ | Status | Meaning |
82
+ | ---------------- | ------------------------------------------------------------------------------------------------ |
83
+ | `connecting` | Transport is being built (DCR, HTTPS discovery) or reconnecting. |
84
+ | `authenticating` | OAuth flow needed; `authUrl` is set, browser window is open. |
85
+ | `ready` | Transport connected; tools listed and callable. |
86
+ | `error` | Transport or auth-config failure. The entry stays in `list()` so the UI can surface the failure. |
87
+ | `disabled` | Operator paused the server via `disable(name)`. Recoverable through `enable(name)`. |
88
+
89
+ Transitions are atomic with respect to subscribers: every state change fires a single snapshot in which the entry shows its new status. `reauthorize` mutates entries in place — the row never disappears from `list()`, even mid-rebuild, so renderers don't see a flicker.
90
+
91
+ ## `subscribe(handler)` and `RegistrySnapshot`
92
+
93
+ ```ts
94
+ type RegistrySubscriber = (snapshot: RegistrySnapshot) => void
95
+
96
+ interface RegistrySnapshot {
97
+ seq: number
98
+ servers: ReadonlyArray<ListedEntry>
99
+ }
100
+ ```
101
+
102
+ Subscribing is the primary way to drive a UI off the registry. The first invocation is synchronous and carries `seq: 0` as a sentinel — embedders treat it as the bootstrap snapshot, not part of the event stream. After that, every mutation increments `seq` (1, 2, 3, …) and broadcasts the full snapshot. A late subscriber still sees `seq: 0` on its first delivery; emitted events continue from the registry's current counter.
103
+
104
+ Handlers must not throw. The registry catches exceptions per subscriber so a misbehaving consumer can't break the others, but the catch is a safety net, not a feature — log and swallow inside your handler.
105
+
106
+ ```ts
107
+ const off = registry.subscribe((snap) => {
108
+ if (snap.seq === 0) {
109
+ // bootstrap — render the initial list
110
+ } else {
111
+ // diff against the previous snapshot, or just re-render
112
+ }
113
+ })
114
+ // ...
115
+ off()
116
+ ```
117
+
118
+ ## `ListedEntry`
119
+
120
+ The shape of each `servers[]` entry inside a snapshot:
121
+
122
+ ```ts
123
+ interface ListedEntry {
124
+ name: string
125
+ status: McpServerStatus
126
+ toolCount: number
127
+ transport?: "http" | "stdio"
128
+ authMode?: "none" | "apiKey" | "clientCredentials" | "authorizationCode"
129
+ authUrl?: string
130
+ error?: McpToolError
131
+ tools: Array<{ name: string; description?: string; inputSchema: unknown }>
132
+ capabilities?: unknown
133
+ }
134
+ ```
135
+
136
+ | Field | Type | Description |
137
+ | -------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------- |
138
+ | `name` | `string` | The server's stable identifier. |
139
+ | `status` | `McpServerStatus` | Current lifecycle state — see the table above. |
140
+ | `toolCount` | `number` | Number of tools the server advertises. `0` until `status === "ready"`. |
141
+ | `transport` | `"http" \| "stdio"` | The transport variant in use. |
142
+ | `authMode` | `string` | `none` / `apiKey` / `clientCredentials` / `authorizationCode`. UI badges + "show Authorize" check use this. |
143
+ | `authUrl` | `string` | Set while `status === "authenticating"`. The URL to open for OAuth consent. |
144
+ | `error` | [`McpToolError`](#mcptoolerror) | Set while `status === "error"`. |
145
+ | `tools` | `Array<{ name; description?; inputSchema }>` | Tool metadata as advertised by the server. Each becomes `mcp__<server>__<tool>` for the LLM. |
146
+ | `capabilities` | `unknown` | Server-declared MCP capabilities object (resources, prompts, etc.). |
147
+
148
+ ## `McpToolError`
149
+
150
+ ```ts
151
+ interface McpToolError {
152
+ kind: McpToolErrorKind
153
+ message: string
154
+ details?: unknown
155
+ }
156
+
157
+ type McpToolErrorKind =
158
+ | "auth_unavailable"
159
+ | "transport_error"
160
+ | "timeout"
161
+ | "server_error"
162
+ | "tool_not_found"
163
+ ```
164
+
165
+ The same shape surfaces on entry-level `error` (when `addServer` fails to connect) and on individual tool calls. See the [usage guide's failure modes table](/docs/agents/usage/mcp-servers#failure-modes).
166
+
167
+ ## `RegistryOpts`
168
+
169
+ `BuiltinAgentsServer` constructs the registry on your behalf. You only see this shape if you instantiate `agents-mcp` directly (e.g. from a custom embedder):
170
+
171
+ ```ts
172
+ interface RegistryOpts {
173
+ publicUrl?: string
174
+ openAuthorizeUrl?: (url: string, server: string) => void
175
+ }
176
+
177
+ function createRegistry(opts: RegistryOpts): Registry
178
+ ```
179
+
180
+ | Field | Description |
181
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
182
+ | `publicUrl` | Base URL used to construct the OAuth `redirect_uri` (full URI is `<publicUrl>/oauth/callback/<server-name>`). MUST be stable across restarts — DCR registers it with the auth server and persists it in the keychain, so a value that drifts forces re-authorization on every launch. Embedders that listen on an ephemeral port should pass a fixed loopback literal (the desktop uses `http://127.0.0.1:53117`); nothing actually listens at the URL — the embedder's BrowserWindow intercepts the redirect by prefix. |
183
+ | `openAuthorizeUrl` | Hook invoked when an `authorizationCode` server first needs consent. Receives the SDK-generated authorize URL. The desktop opens it in a sandboxed `BrowserWindow`; headless embedders can read the URL from the `authenticating` envelope of `addServer` and surface it themselves. |
184
+
185
+ ## See also
186
+
187
+ - [MCP servers usage guide](/docs/agents/usage/mcp-servers) — the practical walkthrough of registering servers, OAuth, persistence, and the per-agent allowlist.
188
+ - [`McpServerConfig`](/docs/agents/reference/mcp-server-config) — schema for the `cfg` argument to `addServer` / `applyConfig`.
189
+ - [`BuiltinAgentsServer`](/docs/agents/usage/embedded-builtins) — host options that affect MCP, including `extraMcpServers` and `openAuthorizeUrl`.