You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/content/1.guide/4.rpc.md
+4-16Lines changed: 4 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,13 +56,7 @@ Scope with your devframe id, then a kebab-case action: `my-devframe:get-modules`
56
56
57
57
### Handler arguments
58
58
59
-
Handlers accept any serializable arguments. Declared `args` schemas validate each at the boundary; extra object fields still reach the handler.
60
-
61
-
> [!TIP]
62
-
> If your app already pulls in **zod**, use it for your RPC schemas to reuse a dependency you already ship.
63
-
64
-
> [!WARNING]
65
-
> Declared `args`/`returns` schemas are enforced at runtime; failing arguments or return values are rejected with `DF0043` / `DF0044`.
59
+
Handlers accept any serializable arguments. Declared `args` and `returns` schemas are enforced at runtime; extra object fields still reach the handler.
66
60
67
61
### Setup vs handler
68
62
@@ -198,15 +192,7 @@ The WS transport picks one of two encoders per function:
198
192
|`false` (default) |`structured-clone-es`|`s:`|`Map`, `Set`, `Date`, `BigInt`, cycles, class instances |
When every function is JSON-flagged, the wire stays plain JSON.
202
-
203
-
### Discovering shape errors during dev
204
-
205
-
When a `jsonSerializable: true` handler returns a value JSON cannot round-trip (`Map`, `Date`, …), the strict serializer throws [`DF0020`](/errors/DF0020).
206
-
207
-
### MCP requires JSON
208
-
209
-
`agent: {...}` requires `jsonSerializable: true`; one without the other throws [`DF0019`](/errors/DF0019).
195
+
When every function is JSON-flagged, the wire stays plain JSON. A `jsonSerializable: true` handler must return JSON-only values — `Map`, `Date`, and friends won't round-trip.
210
196
211
197
## Agent exposure
212
198
@@ -230,6 +216,8 @@ defineRpcFunction({
230
216
})
231
217
```
232
218
219
+
Exposing a function over MCP requires `jsonSerializable: true`.
220
+
233
221
## What's next
234
222
235
223
-[Shared State](/guide/shared-state) — state synced across clients
|`banner`| a small boxed console message | Called with `{ code, url }`; prints via `printBanner()`. |
32
+
|`onTrusted`|`undefined`| Called with `{ session, authToken }` once a code exchange succeeds, so a host rendering its own banner can retract it. |
32
33
|`serverUrl`|`context.host.resolveOrigin()`| Magic-link base URL. |
33
34
34
35
Returns a `DevframeAuthHandler`:
@@ -57,4 +58,6 @@ if (!auth.authorize(methodName, session))
57
58
auth.onConnect(peer, session)
58
59
```
59
60
61
+
An exchange rotates the code and prints the new one, and `onTrusted` fires after that, so a host retracting a sticky notice drops that follow-up too and calls `auth.printBanner()` when it next wants a code on screen.
62
+
60
63
Auth storage is internal, not `devframe/node/hub-internals`.
Copy file name to clipboardExpand all lines: docs/content/6.errors/DF0014.md
+2-6Lines changed: 2 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,9 +9,7 @@ description: 'RPC function "{name}" has an invalid agent field — description m
9
9
10
10
## Cause
11
11
12
-
An RPC function was defined with an `agent` field (opting it in for exposure to agents via the MCP adapter), but the required `description` property is missing or empty.
13
-
14
-
Agents rely on the description to decide when to invoke a tool. Empty or placeholder descriptions would produce unusable agent surface.
12
+
An RPC function opts into agent exposure with an `agent` field, but its required `description` is missing or empty. Agents rely on the description to decide when to invoke a tool.
15
13
16
14
## Example
17
15
@@ -28,7 +26,7 @@ defineRpcFunction({
28
26
29
27
## Fix
30
28
31
-
Provide a non-empty `description` (~1–3 sentences) explaining what the tool does and when agents should invoke it:
29
+
Provide a non-empty `description` (~1–3 sentences) explaining what the tool does and when agents should invoke it, or remove the `agent` field to keep it RPC-only.
32
30
33
31
```ts
34
32
defineRpcFunction({
@@ -41,8 +39,6 @@ defineRpcFunction({
41
39
})
42
40
```
43
41
44
-
If you didn't intend for this function to be agent-exposed, remove the `agent` field entirely (default-deny).
45
-
46
42
## Source
47
43
48
44
-[`packages/devframe/src/node/host-agent.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-agent.ts) — agent registration throws `DF0014` when a tool's `agent.description` is missing or empty.
Copy file name to clipboardExpand all lines: docs/content/6.errors/DF0017.md
+4-5Lines changed: 4 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,14 +11,13 @@ description: 'Failed to start MCP server ({transport}): {reason}'
11
11
12
12
The MCP server failed while initializing. Common reasons:
13
13
14
-
-`@modelcontextprotocol/server` is not installed. This is a peer dependency — add it to your devtool's dependencies.
15
-
- The stdio transport threw during `connect()` (e.g. stdin/stdout is not available).
16
-
- The route-based MCP server (`cli.mcp`) could not load its transport module — usually the missing SDK peer dependency.
14
+
- The `@modelcontextprotocol/server` peer dependency is missing (the stdio and route-based transports both need it).
15
+
- The stdio transport threw during `connect()` (e.g. stdin/stdout unavailable).
17
16
18
17
## Fix
19
18
20
-
-**Missing SDK**: `pnpm add @modelcontextprotocol/server`(or npm/yarn equivalent) in the package that imports `devframe/adapters/mcp` or enables `cli.mcp`.
21
-
-**Transport init failure**: check the underlying error (attached as `cause`) for specifics.
19
+
-**Missing SDK**: `pnpm add @modelcontextprotocol/server` in the package that imports `devframe/adapters/mcp` or enables `cli.mcp`.
20
+
-**Transport failure**: inspect the underlying error attached as `cause`.
Copy file name to clipboardExpand all lines: docs/content/6.errors/DF0019.md
+3-15Lines changed: 3 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,24 +9,21 @@ description: 'RPC function "{name}" has agent set but jsonSerializable is not tr
9
9
10
10
## Cause
11
11
12
-
The `agent` field exposes an RPC function as an MCP tool. MCP only consumes JSON-shaped data. Functions whose payloads can include `Map`, `Set`, `Date`, `BigInt`, circular references, or class instances cannot be safely advertised to agents.
13
-
14
-
A registered function is rejected when `agent` is present and `jsonSerializable` is not explicitly `true`.
12
+
The `agent` field exposes an RPC function as an MCP tool, and MCP only consumes JSON-shaped data. A function with `agent` set is rejected unless it also declares `jsonSerializable: true`.
Set `jsonSerializable: true` if the payload is JSON-safe, or remove `agent` to keep it RPC-only.
30
27
31
28
```ts
32
29
defineRpcFunction({
@@ -37,15 +34,6 @@ defineRpcFunction({
37
34
})
38
35
```
39
36
40
-
Or remove `agent` to keep the function as an internal RPC (no agent exposure):
41
-
42
-
```ts
43
-
defineRpcFunction({
44
-
name: 'my-plugin:summary',
45
-
handler: () =>newMap([['a', 1]]),
46
-
})
47
-
```
48
-
49
37
## Source
50
38
51
39
-[`packages/devframe/src/rpc/collector.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/rpc/collector.ts) — `RpcFunctionsCollectorBase.register()` throws `DF0019` when a definition has `agent` set but is not declared `jsonSerializable: true`.
Copy file name to clipboardExpand all lines: docs/content/6.errors/DF0020.md
+5-21Lines changed: 5 additions & 21 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,18 +9,14 @@ description: 'RPC function "{name}" declares jsonSerializable: true but the valu
9
9
10
10
## Cause
11
11
12
-
The function is declared `jsonSerializable: true`, which means its args and return value are encoded with strict `JSON.stringify` (both on the wire and in build dumps). The strict serializer rejects any value that JSON cannot round-trip losslessly:
12
+
A `jsonSerializable: true` function encodes its args and return value with strict `JSON.stringify` (on the wire and in build dumps). The serializer throws at the offending value rather than emit a corrupt payload when it hits anything JSON cannot round-trip losslessly:
13
13
14
14
-`Map`, `Set`, `WeakMap`, `WeakSet`
15
-
-`Date` (silently coerced to ISO string by JSON)
16
-
-`BigInt`
15
+
-`Date` (JSON coerces it to an ISO string)
16
+
-`BigInt`, `Symbol`, `Function`
17
17
- circular references
18
18
- non-plain class instances
19
19
-`undefined` leaves
20
-
-`Symbol`
21
-
-`Function`
22
-
23
-
When the strict serializer encounters one of these, it throws synchronously at the offending call rather than producing a corrupt payload.
24
20
25
21
## Example
26
22
@@ -29,26 +25,14 @@ defineRpcFunction({
29
25
name: 'my-plugin:graph',
30
26
jsonSerializable: true,
31
27
handler: () => ({
32
-
nodes: newMap([['a', 1]]), //← throws DF0020 with type=Map, path="nodes"
Either drop `jsonSerializable: true` so the function uses `structured-clone-es` (round-trips `Map`, `Set`, etc.):
40
-
41
-
```ts
42
-
defineRpcFunction({
43
-
name: 'my-plugin:graph',
44
-
// jsonSerializable: false (default) — Map/Set survive the wire and the dump
45
-
handler: () => ({
46
-
nodes: newMap([['a', 1]]),
47
-
}),
48
-
})
49
-
```
50
-
51
-
Or convert the payload to a JSON-safe shape (e.g. an array of entries, an ISO string, a plain object) before returning. Note: removing `jsonSerializable: true` also disables `agent` exposure; if you need MCP, you must use a JSON-safe shape.
35
+
Drop `jsonSerializable: true` to fall back to `structured-clone-es` (round-trips `Map`, `Set`, etc.), or convert the payload to a JSON-safe shape before returning. Keeping `agent` exposure requires the JSON-safe shape.
A streaming subscriber's queue grew past its `highWaterMark` because the consumer is slower than the producer. The oldest chunks were dropped to keep memory bounded.
13
-
14
-
This is a soft warning — the stream keeps running and remaining chunks still flow.
12
+
The consumer is slower than the producer, so the subscriber's queue grew past its `highWaterMark` and the oldest chunks were dropped to keep memory bounded. The stream keeps running and remaining chunks still flow.
15
13
16
14
## Fix
17
15
18
16
- Raise `highWaterMark` on `rpc.streaming.subscribe(channel, id, { highWaterMark })` if the consumer can occasionally catch up.
19
-
- Slow the producer so it doesn't outpace the wire (e.g. throttle, debounce, or batch chunks server-side).
20
-
-Switch to `sharedState` if you only need the latest value rather than every intermediate chunk.
17
+
- Slow the producer — throttle, debounce, or batch chunks server-side.
18
+
-Use `sharedState` if you only need the latest value rather than every chunk.
Copy file name to clipboardExpand all lines: docs/content/6.errors/DF0030.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,13 +9,13 @@ description: 'Stream "{channel}#{id}" is unknown — no producer has called chan
9
9
10
10
## Cause
11
11
12
-
A client subscribed to a stream id that the server-side channel doesn't know about. Either the producer never started a stream with that id, the producer already ended it and `replayWindow` is `0`, or the client passed the wrong id.
12
+
A client subscribed to a stream id no server-side producer has started. Either the producer never called `channel.start({ id })`, it already ended the stream and `replayWindow` is `0`, or the client passed the wrong id.
13
13
14
14
## Fix
15
15
16
-
-Make sure the action that returns the stream id runs **before** the client subscribes — typically by awaiting `rpc.call('your-action')` and using the returned id.
17
-
- Bump `replayWindow` on `ctx.rpc.streaming.create(name, { replayWindow })`if you need clients to resume after the producer has finished but kept the buffer warm.
18
-
-Check the id is propagated correctly across boundaries (action return value → component prop → subscribe call).
16
+
-Run the producer before clients subscribe — typically await `rpc.call('your-action')` and use the returned id.
17
+
- Bump `replayWindow` on `ctx.rpc.streaming.create(name, { replayWindow })`to let clients resume after the producer finishes.
18
+
-Verify the id propagates correctly (action return → component prop → subscribe call).
Copy file name to clipboardExpand all lines: docs/content/6.errors/DF0033.md
+3-10Lines changed: 3 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,19 +9,12 @@ description: 'Failed to start dev RPC bridge for "{id}": {reason}'
9
9
10
10
## Cause
11
11
12
-
`devframeViteBridge()` (from `@devframes/vite`) could not bring up the bridge dev server that pairs a host-served SPA (Vite, Nuxt, Astro, etc.) with devframe's RPC backend. Common reasons:
13
-
14
-
- The preferred port is in use and no fallback range was configured.
15
-
- Calling `def.setup(ctx)` threw — the devframe's own setup logic surfaced an error.
16
-
- A required peer (e.g. `get-port-please` or `h3`) is missing or mismatched.
17
-
18
-
This is a soft warning — the surrounding Vite dev server keeps running, but the host-served SPA will fail its `__connection.json` lookup until the bridge starts.
12
+
`devframeViteBridge()` (from `@devframes/vite`) could not bring up the bridge dev server that pairs a host-served SPA with devframe's RPC backend — usually because the preferred port is taken with no fallback range, or `def.setup(ctx)` threw. The surrounding Vite dev server keeps running, but the SPA's `__connection.json` lookup fails until the bridge starts.
19
13
20
14
## Fix
21
15
22
-
- Pin a port via `cli.port` / `cli.portRange` on the devframe definition, or via `port` on `devframeViteBridge`.
23
-
- Inspect the `reason` (or the attached `cause`) for the underlying error — fix the setup function or free the port.
24
-
- For Nuxt: pass `devMiddleware: { port: <free-port> }` to the `@devframes/nuxt` module.
16
+
- Pin a port via `cli.port` / `cli.portRange` on the definition, or `port` on `devframeViteBridge`.
17
+
- Inspect `reason` (or the attached `cause`) to fix the setup function or free the port.
description: 'Failed to persist storage file: {filepath}'
4
+
---
5
+
6
+
## Message
7
+
8
+
> Failed to persist storage file: `{filepath}`
9
+
10
+
## Cause
11
+
12
+
A shared-state store's debounced write to disk failed — the directory could not be created, or the temp-file write / atomic rename threw. Usually the storage directory is not writable or the disk is full.
13
+
14
+
## Fix
15
+
16
+
Check that the storage directory is writable and has free space.
17
+
18
+
## Source
19
+
20
+
-[`packages/devframe/src/node/storage.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/storage.ts) — the debounced `updated` handler reports this when writing the temp file or renaming it into place fails.
0 commit comments