@abloatai/ablo 0.7.0 → 0.9.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.
- package/CHANGELOG.md +72 -1
- package/README.md +80 -66
- package/dist/BaseSyncedStore.d.ts +73 -0
- package/dist/BaseSyncedStore.js +179 -5
- package/dist/Model.d.ts +42 -0
- package/dist/Model.js +103 -44
- package/dist/SyncEngineContext.d.ts +2 -1
- package/dist/SyncEngineContext.js +5 -3
- package/dist/agent/session.js +6 -5
- package/dist/ai-sdk/coordination-context.js +4 -0
- package/dist/ai-sdk/index.d.ts +56 -47
- package/dist/ai-sdk/index.js +56 -47
- package/dist/ai-sdk/intent-broadcast.d.ts +5 -0
- package/dist/ai-sdk/intent-broadcast.js +11 -4
- package/dist/ai-sdk/wrap.d.ts +14 -11
- package/dist/ai-sdk/wrap.js +11 -13
- package/dist/auth/credentialSource.d.ts +34 -0
- package/dist/auth/credentialSource.js +63 -0
- package/dist/auth/index.d.ts +2 -22
- package/dist/auth/index.js +26 -36
- package/dist/auth/schemas.d.ts +35 -0
- package/dist/auth/schemas.js +53 -0
- package/dist/client/Ablo.d.ts +259 -33
- package/dist/client/Ablo.js +276 -73
- package/dist/client/ApiClient.d.ts +52 -4
- package/dist/client/ApiClient.js +236 -66
- package/dist/client/auth.d.ts +21 -2
- package/dist/client/auth.js +77 -5
- package/dist/client/createInternalComponents.d.ts +2 -0
- package/dist/client/createInternalComponents.js +8 -1
- package/dist/client/createModelProxy.d.ts +187 -79
- package/dist/client/createModelProxy.js +203 -68
- package/dist/client/httpClient.d.ts +71 -0
- package/dist/client/httpClient.js +69 -0
- package/dist/client/identity.d.ts +2 -6
- package/dist/client/identity.js +63 -11
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/registerDataSource.d.ts +19 -0
- package/dist/client/registerDataSource.js +59 -0
- package/dist/client/validateAbloOptions.d.ts +2 -1
- package/dist/client/validateAbloOptions.js +8 -7
- package/dist/core/DatabaseManager.js +30 -2
- package/dist/core/openIDBWithTimeout.d.ts +36 -0
- package/dist/core/openIDBWithTimeout.js +88 -1
- package/dist/errorCodes.d.ts +92 -1
- package/dist/errorCodes.js +139 -7
- package/dist/errors.d.ts +54 -3
- package/dist/errors.js +192 -44
- package/dist/index.d.ts +23 -10
- package/dist/index.js +21 -8
- package/dist/keys/index.d.ts +76 -0
- package/dist/keys/index.js +171 -0
- package/dist/mutators/UndoManager.d.ts +86 -50
- package/dist/mutators/UndoManager.js +129 -22
- package/dist/mutators/inverseOp.d.ts +129 -0
- package/dist/mutators/inverseOp.js +74 -0
- package/dist/mutators/readerActions.d.ts +1 -1
- package/dist/mutators/undoApply.d.ts +42 -0
- package/dist/mutators/undoApply.js +143 -0
- package/dist/query/client.d.ts +10 -9
- package/dist/query/client.js +22 -14
- package/dist/react/AbloProvider.d.ts +23 -101
- package/dist/react/AbloProvider.js +61 -103
- package/dist/react/ClientSideSuspense.d.ts +1 -1
- package/dist/react/DefaultFallback.d.ts +1 -1
- package/dist/react/SyncGroupProvider.d.ts +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +3 -2
- package/dist/react/useAblo.d.ts +4 -4
- package/dist/react/useAblo.js +10 -5
- package/dist/react/useCurrentUserId.d.ts +1 -1
- package/dist/react/useCurrentUserId.js +1 -1
- package/dist/react/useMutators.js +19 -12
- package/dist/react/useReactive.js +16 -3
- package/dist/schema/ddl.d.ts +26 -3
- package/dist/schema/ddl.js +152 -4
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.js +12 -0
- package/dist/schema/model.d.ts +11 -0
- package/dist/schema/model.js +2 -0
- package/dist/schema/openapi.d.ts +28 -0
- package/dist/schema/openapi.js +118 -0
- package/dist/schema/plane.d.ts +23 -0
- package/dist/schema/plane.js +19 -0
- package/dist/schema/relation.d.ts +20 -0
- package/dist/schema/serialize.d.ts +7 -3
- package/dist/schema/serialize.js +6 -2
- package/dist/schema/sync-delta-row.d.ts +157 -0
- package/dist/schema/sync-delta-row.js +102 -0
- package/dist/schema/sync-delta-wire.d.ts +180 -0
- package/dist/schema/sync-delta-wire.js +102 -0
- package/dist/server/adapter.d.ts +156 -0
- package/dist/server/adapter.js +19 -0
- package/dist/server/commit.d.ts +82 -0
- package/dist/server/commit.js +1 -0
- package/dist/server/index.d.ts +14 -0
- package/dist/server/index.js +1 -0
- package/dist/server/next.d.ts +51 -0
- package/dist/server/next.js +47 -0
- package/dist/server/read-config.d.ts +60 -0
- package/dist/server/read-config.js +8 -0
- package/dist/server/storage-mode.d.ts +17 -0
- package/dist/server/storage-mode.js +12 -0
- package/dist/source/adapter.d.ts +59 -0
- package/dist/source/adapter.js +19 -0
- package/dist/source/adapters/drizzle.d.ts +34 -0
- package/dist/source/adapters/drizzle.js +147 -0
- package/dist/source/adapters/memory.d.ts +12 -0
- package/dist/source/adapters/memory.js +114 -0
- package/dist/source/adapters/prisma.d.ts +57 -0
- package/dist/source/adapters/prisma.js +199 -0
- package/dist/source/conformance.d.ts +32 -0
- package/dist/source/conformance.js +134 -0
- package/dist/source/contract.d.ts +143 -0
- package/dist/source/contract.js +98 -0
- package/dist/source/index.d.ts +61 -10
- package/dist/source/index.js +98 -0
- package/dist/source/next.d.ts +33 -0
- package/dist/source/next.js +26 -0
- package/dist/sync/BootstrapHelper.d.ts +10 -0
- package/dist/sync/BootstrapHelper.js +56 -42
- package/dist/sync/ConnectionManager.d.ts +57 -1
- package/dist/sync/ConnectionManager.js +186 -11
- package/dist/sync/HydrationCoordinator.d.ts +93 -17
- package/dist/sync/HydrationCoordinator.js +241 -41
- package/dist/sync/NetworkProbe.d.ts +60 -18
- package/dist/sync/NetworkProbe.js +121 -23
- package/dist/sync/SyncWebSocket.d.ts +45 -70
- package/dist/sync/SyncWebSocket.js +113 -89
- package/dist/sync/createIntentStream.js +10 -1
- package/dist/sync/participants.js +5 -2
- package/dist/transactions/TransactionQueue.js +13 -1
- package/dist/types/streams.d.ts +9 -0
- package/dist/utils/mobx-setup.js +1 -0
- package/dist/webhooks/events.d.ts +38 -0
- package/dist/webhooks/events.js +40 -0
- package/dist/webhooks/index.d.ts +10 -0
- package/dist/webhooks/index.js +10 -0
- package/dist/wire/errorEnvelope.d.ts +34 -0
- package/dist/wire/errorEnvelope.js +86 -0
- package/dist/wire/frames.d.ts +119 -0
- package/dist/wire/frames.js +1 -0
- package/dist/wire/index.d.ts +24 -0
- package/dist/wire/index.js +21 -0
- package/dist/wire/listEnvelope.d.ts +45 -0
- package/dist/wire/listEnvelope.js +17 -0
- package/docs/api-keys.md +5 -5
- package/docs/api.md +125 -65
- package/docs/audit.md +16 -9
- package/docs/cli.md +57 -47
- package/docs/client-behavior.md +54 -40
- package/docs/coordination.md +66 -80
- package/docs/data-sources.md +56 -34
- package/docs/examples/agent-human.md +74 -28
- package/docs/examples/ai-sdk-tool.md +29 -22
- package/docs/examples/existing-python-backend.md +41 -26
- package/docs/examples/nextjs.md +32 -17
- package/docs/examples/scoped-agent.md +43 -28
- package/docs/examples/server-agent.md +40 -15
- package/docs/guarantees.md +38 -27
- package/docs/identity.md +65 -59
- package/docs/index.md +30 -19
- package/docs/integration-guide.md +78 -78
- package/docs/interaction-model.md +43 -35
- package/docs/mcp/claude-code.md +11 -19
- package/docs/mcp/cursor.md +7 -25
- package/docs/mcp/windsurf.md +7 -20
- package/docs/mcp.md +103 -26
- package/docs/quickstart.md +63 -61
- package/docs/react.md +24 -16
- package/docs/roadmap.md +13 -13
- package/docs/schema-contract.md +111 -0
- package/docs/the-loop.md +21 -0
- package/examples/README.md +8 -4
- package/examples/data-source/README.md +10 -7
- package/examples/data-source/customer-server.ts +27 -25
- package/examples/data-source/run.ts +4 -3
- package/examples/quickstart.ts +1 -1
- package/llms.txt +55 -21
- package/package.json +48 -3
package/docs/mcp/claude-code.md
CHANGED
|
@@ -3,22 +3,12 @@
|
|
|
3
3
|
## Install
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
claude mcp add --transport http ablo
|
|
6
|
+
claude mcp add --transport http ablo https://<your-app>/api/mcp
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
That's it
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
If your deployment requires a scoped bearer token (production setups should):
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
claude mcp add --transport http ablo-sync https://<your-app>/api/mcp \
|
|
17
|
-
--header "Authorization=Bearer $ABLO_MCP_TOKEN"
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Create a session-scoped bearer token from your server or dashboard — see
|
|
21
|
-
[MCP overview](/docs/mcp#auth).
|
|
9
|
+
That's it — no token or header needed. The endpoint is public and serves
|
|
10
|
+
only docs, schema lint, and scaffolds. The next `/help` in Claude Code will
|
|
11
|
+
list the Ablo Sync tools.
|
|
22
12
|
|
|
23
13
|
## Verify
|
|
24
14
|
|
|
@@ -28,16 +18,18 @@ In Claude Code, run:
|
|
|
28
18
|
/mcp list
|
|
29
19
|
```
|
|
30
20
|
|
|
31
|
-
You should see `ablo
|
|
21
|
+
You should see `ablo` with the integration tools enumerated:
|
|
22
|
+
`search_ablo_docs`, `get_recipe`, `get_api_surface`, `validate_schema`,
|
|
23
|
+
`scaffold_app`.
|
|
32
24
|
|
|
33
25
|
## Removing
|
|
34
26
|
|
|
35
27
|
```bash
|
|
36
|
-
claude mcp remove ablo
|
|
28
|
+
claude mcp remove ablo
|
|
37
29
|
```
|
|
38
30
|
|
|
39
31
|
## More
|
|
40
32
|
|
|
41
|
-
- [MCP overview](/docs/mcp) — how the transport works.
|
|
42
|
-
- [Cursor setup](/docs/mcp/cursor) — same
|
|
43
|
-
- [Windsurf setup](/docs/mcp/windsurf) — same
|
|
33
|
+
- [MCP overview](/docs/mcp) — what the server exposes and how the transport works.
|
|
34
|
+
- [Cursor setup](/docs/mcp/cursor) — same URL, different UI.
|
|
35
|
+
- [Windsurf setup](/docs/mcp/windsurf) — same URL, different UI.
|
package/docs/mcp/cursor.md
CHANGED
|
@@ -7,7 +7,7 @@ Add the Ablo Sync MCP server to Cursor's `mcp.json`:
|
|
|
7
7
|
```json
|
|
8
8
|
{
|
|
9
9
|
"mcpServers": {
|
|
10
|
-
"ablo
|
|
10
|
+
"ablo": {
|
|
11
11
|
"transport": "http",
|
|
12
12
|
"url": "https://<your-app>/api/mcp"
|
|
13
13
|
}
|
|
@@ -15,39 +15,21 @@ Add the Ablo Sync MCP server to Cursor's `mcp.json`:
|
|
|
15
15
|
}
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
The file lives at `~/.cursor/mcp.json` on macOS / Linux.
|
|
18
|
+
The file lives at `~/.cursor/mcp.json` on macOS / Linux. No auth header is
|
|
19
|
+
needed — the endpoint is public and serves only docs, schema lint, and
|
|
20
|
+
scaffolds.
|
|
19
21
|
|
|
20
22
|
Restart Cursor. The Ablo Sync tools appear under the MCP icon in the agent
|
|
21
23
|
panel.
|
|
22
24
|
|
|
23
|
-
## With auth
|
|
24
|
-
|
|
25
|
-
Add a `headers` block:
|
|
26
|
-
|
|
27
|
-
```json
|
|
28
|
-
{
|
|
29
|
-
"mcpServers": {
|
|
30
|
-
"ablo-sync": {
|
|
31
|
-
"transport": "http",
|
|
32
|
-
"url": "https://<your-app>/api/mcp",
|
|
33
|
-
"headers": {
|
|
34
|
-
"Authorization": "Bearer $ABLO_MCP_TOKEN"
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Cursor expands shell-style env vars in this block. Set `ABLO_MCP_TOKEN`
|
|
42
|
-
in your shell config.
|
|
43
|
-
|
|
44
25
|
## Verify
|
|
45
26
|
|
|
46
27
|
In Cursor's agent panel, open the MCP tools list. You should see the
|
|
47
|
-
Ablo Sync
|
|
28
|
+
Ablo Sync integration tools and their JSON schemas: `search_ablo_docs`,
|
|
29
|
+
`get_recipe`, `get_api_surface`, `validate_schema`, `scaffold_app`.
|
|
48
30
|
|
|
49
31
|
## More
|
|
50
32
|
|
|
51
|
-
- [MCP overview](/docs/mcp) — how the transport works.
|
|
33
|
+
- [MCP overview](/docs/mcp) — what the server exposes and how the transport works.
|
|
52
34
|
- [Claude Code setup](/docs/mcp/claude-code) — CLI install.
|
|
53
35
|
- [Windsurf setup](/docs/mcp/windsurf) — same JSON shape.
|
package/docs/mcp/windsurf.md
CHANGED
|
@@ -7,7 +7,7 @@ Add the Ablo Sync MCP server to Windsurf's MCP config:
|
|
|
7
7
|
```json
|
|
8
8
|
{
|
|
9
9
|
"mcpServers": {
|
|
10
|
-
"ablo
|
|
10
|
+
"ablo": {
|
|
11
11
|
"transport": "http",
|
|
12
12
|
"url": "https://<your-app>/api/mcp"
|
|
13
13
|
}
|
|
@@ -16,31 +16,18 @@ Add the Ablo Sync MCP server to Windsurf's MCP config:
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
The config path differs by platform — Windsurf surfaces it in Settings →
|
|
19
|
-
Cascade → MCP. Restart Windsurf after saving.
|
|
20
|
-
|
|
21
|
-
## With auth
|
|
22
|
-
|
|
23
|
-
```json
|
|
24
|
-
{
|
|
25
|
-
"mcpServers": {
|
|
26
|
-
"ablo-sync": {
|
|
27
|
-
"transport": "http",
|
|
28
|
-
"url": "https://<your-app>/api/mcp",
|
|
29
|
-
"headers": {
|
|
30
|
-
"Authorization": "Bearer $ABLO_MCP_TOKEN"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
```
|
|
19
|
+
Cascade → MCP. Restart Windsurf after saving. No auth header is needed —
|
|
20
|
+
the endpoint is public and serves only docs, schema lint, and scaffolds.
|
|
36
21
|
|
|
37
22
|
## Verify
|
|
38
23
|
|
|
39
24
|
Cascade's MCP panel lists every configured server with its tools. You
|
|
40
|
-
should see `ablo
|
|
25
|
+
should see `ablo` with the integration tools enumerated:
|
|
26
|
+
`search_ablo_docs`, `get_recipe`, `get_api_surface`, `validate_schema`,
|
|
27
|
+
`scaffold_app`.
|
|
41
28
|
|
|
42
29
|
## More
|
|
43
30
|
|
|
44
|
-
- [MCP overview](/docs/mcp) — how the transport works.
|
|
31
|
+
- [MCP overview](/docs/mcp) — what the server exposes and how the transport works.
|
|
45
32
|
- [Claude Code setup](/docs/mcp/claude-code) — CLI install.
|
|
46
33
|
- [Cursor setup](/docs/mcp/cursor) — same JSON shape.
|
package/docs/mcp.md
CHANGED
|
@@ -1,44 +1,121 @@
|
|
|
1
1
|
# Model Context Protocol
|
|
2
2
|
|
|
3
|
-
Ablo
|
|
4
|
-
assistant — Claude Code, Cursor, Windsurf — and your sync models become
|
|
5
|
-
typed, callable tools.
|
|
3
|
+
Ablo publishes **two** MCP servers for two different jobs. Don't confuse them:
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
| Server | Purpose | Auth | Tools |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| **Coordination** (`@ablo/mcp`) | Let an agent safely read & mutate your application data | API key (`sk_…` / `rk_…`) | per-model `get` / `list` / `create` / `update` / `delete` / `claim` / `release` |
|
|
8
|
+
| **Integration-helper** (hosted `/api/mcp`) | Help an AI coding assistant write SDK integration code that compiles | none (public docs) | doc search, export surface, schema lint, scaffold |
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
The coordination server **is the data plane** — it is how an agent changes
|
|
11
|
+
state. The integration-helper server only serves docs, schema lint, and
|
|
12
|
+
scaffolds; it does **not** read or write application data (there are no
|
|
13
|
+
per-model data tools on it). Pick by what you're doing: shipping an agent that
|
|
14
|
+
edits rows → coordination; teaching your IDE assistant the SDK → helper.
|
|
15
|
+
|
|
16
|
+
## Coordination server (`@ablo/mcp`)
|
|
17
|
+
|
|
18
|
+
The coordination server is the MCP projection of the model-scoped API
|
|
19
|
+
(`/v1/models/...`) — the same surface as `ablo.<model>.create/update/claim` and
|
|
20
|
+
the REST routes, rendered as tools. An agent connects with your API key and
|
|
21
|
+
gets one safe loop: **claim → read → commit → release.**
|
|
22
|
+
|
|
23
|
+
Install over stdio; set your key in the host's MCP env:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
claude mcp add ablo -- npx -y @ablo/mcp
|
|
27
|
+
# env: ABLO_API_KEY=sk_… (ABLO_API_URL optional; defaults to the hosted API)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Each tool mirrors an SDK verb, scoped to a model + id:
|
|
31
|
+
|
|
32
|
+
| Tool | Mirrors | Does |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `get_model` | `ablo.<model>.get(id)` | read latest state + active claims |
|
|
35
|
+
| `list_models` | `ablo.<model>.list({…})` | cursor-paginated list with filters |
|
|
36
|
+
| `create_model` | `ablo.<model>.create({ data })` | guarded create |
|
|
37
|
+
| `update_model` | `ablo.<model>.update({ id, … })` | guarded update |
|
|
38
|
+
| `delete_model` | `ablo.<model>.delete({ id })` | guarded delete |
|
|
39
|
+
| `claim_model` | `ablo.<model>.claim({ id })` | acquire / queue a coordination lease |
|
|
40
|
+
| `release_claim` | — | release the lease so others proceed |
|
|
41
|
+
|
|
42
|
+
The agent-facing contract — the safe loop, the "derive idempotency keys from
|
|
43
|
+
the business event" rule, and the error-code playbook — ships as a loadable
|
|
44
|
+
skill at `@ablo/mcp/skill.md`. Lives in `packages/mcp/`
|
|
45
|
+
(`createCoordinationMcpServer`, `src/tools.ts`).
|
|
46
|
+
|
|
47
|
+
## Integration-helper server
|
|
48
|
+
|
|
49
|
+
If you're integrating `@abloatai/ablo` with the help of an AI coding
|
|
50
|
+
assistant (Claude Code, Cursor, Windsurf, Codex), you don't want it guessing
|
|
51
|
+
at the API. This hosted server lets the assistant search the real docs,
|
|
52
|
+
inspect the actual export surface, lint your schema, and scaffold a starter —
|
|
53
|
+
so the code it writes uses APIs that exist. It serves docs only and returns
|
|
54
|
+
nothing org-specific; data access happens through the SDK or the coordination
|
|
55
|
+
server above, never here.
|
|
56
|
+
|
|
57
|
+
> The `@abloatai/ablo` npm package itself bundles neither server — it has
|
|
58
|
+
> no `@modelcontextprotocol/sdk` dependency. The helper is a feature of Ablo's
|
|
59
|
+
> hosted app, mounted at `/api/mcp`; the coordination server is the separate
|
|
60
|
+
> `@ablo/mcp` package.
|
|
61
|
+
|
|
62
|
+
### Install
|
|
63
|
+
|
|
64
|
+
Point your assistant at the hosted endpoint — no auth, no token:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
claude mcp add --transport http ablo https://<your-app>/api/mcp
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Per-client walkthroughs:
|
|
10
71
|
|
|
11
72
|
- [Claude Code](/docs/mcp/claude-code)
|
|
12
73
|
- [Cursor](/docs/mcp/cursor)
|
|
13
74
|
- [Windsurf](/docs/mcp/windsurf)
|
|
14
75
|
|
|
15
|
-
|
|
76
|
+
### What it exposes
|
|
16
77
|
|
|
17
|
-
|
|
78
|
+
#### Tools
|
|
18
79
|
|
|
19
|
-
|
|
|
20
|
-
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
|
|
|
80
|
+
| Tool | What it does |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `search_ablo_docs` | Keyword search across the docs corpus. Returns ranked matches with excerpts. Follow up with `get_recipe` on the top hit. |
|
|
83
|
+
| `get_recipe` | Returns the full markdown of one doc by name (e.g. `readme`, `quickstart`, `schema-contract`, `integration-guide`, `api`, `guarantees`). |
|
|
84
|
+
| `get_api_surface` | Returns the structured export list for an SDK subpath (`@abloatai/ablo`, `./react`, `./schema`, `./testing`, …). Call with no argument to list every subpath. |
|
|
85
|
+
| `validate_schema` | Lints `defineSchema` source against the DSL rules (camelCase fields, lowercase model keys, `scope`/`grants` sync groups, valid `load` strategies, no legacy builders) and returns a structured issue list. Runs no code. |
|
|
86
|
+
| `scaffold_app` | Emits a starter file tree for a schema-first integration — `next`, `node-agent`, or `plain`, with `managed` or `data-source` storage. |
|
|
87
|
+
|
|
88
|
+
#### Resources
|
|
89
|
+
|
|
90
|
+
Every doc file is addressable at `ablo://docs/{name}`, so a
|
|
91
|
+
client can list the corpus and fetch individual files on demand instead of
|
|
92
|
+
loading everything into context.
|
|
93
|
+
|
|
94
|
+
#### Prompts
|
|
25
95
|
|
|
26
|
-
|
|
27
|
-
rejections when it writes stale state. No invention, no hallucinated IDs.
|
|
96
|
+
Reusable, parameterised templates that drive an end-to-end flow:
|
|
28
97
|
|
|
29
|
-
|
|
98
|
+
- `integrate-sync-engine` — wire the SDK into an existing project.
|
|
99
|
+
- `add-agent` — add an agent worker that coordinates via intents and
|
|
100
|
+
conflict-safe writes.
|
|
101
|
+
- `define-schema` — design a Zod-first schema from a description, then run
|
|
102
|
+
`validate_schema` before committing.
|
|
30
103
|
|
|
31
|
-
|
|
32
|
-
token into the MCP client's auth header configuration. See your client's setup
|
|
33
|
-
guide for the exact mechanism.
|
|
104
|
+
### Transport and limits
|
|
34
105
|
|
|
35
|
-
|
|
106
|
+
The endpoint uses the stateless Streamable HTTP transport (`POST /api/mcp`;
|
|
107
|
+
`GET` returns 405 — SSE is not supported in stateless mode). A fresh
|
|
108
|
+
server is built per request, which suits serverless and horizontally-scaled
|
|
109
|
+
deployments.
|
|
36
110
|
|
|
37
|
-
|
|
38
|
-
|
|
111
|
+
There is **no authentication**: the server only serves docs, schema lint,
|
|
112
|
+
and scaffolds, so there's nothing org-scoped to protect. Abuse is bounded
|
|
113
|
+
by IP-based rate limiting — 120 requests per minute per IP. Rate-limit
|
|
114
|
+
headers are echoed on every response.
|
|
39
115
|
|
|
40
|
-
|
|
116
|
+
### Where it lives
|
|
41
117
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
118
|
+
- Route handler: `apps/sync-web/src/app/api/mcp/route.ts`
|
|
119
|
+
- Server setup: `apps/sync-web/src/lib/mcp-ablo/server.ts`
|
|
120
|
+
(`createSyncEngineMcpServer`)
|
|
121
|
+
- Tools: `apps/sync-web/src/lib/mcp-ablo/tools/`
|
package/docs/quickstart.md
CHANGED
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
# Quickstart
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Start building with Ablo in two steps: install, then declare one model and write
|
|
4
|
+
through its generated client.
|
|
4
5
|
|
|
5
6
|
If you already have a backend and database, still start here. The SDK call shape
|
|
6
7
|
is the same; [Integration Guide](./integration-guide.md) explains when to use
|
|
7
8
|
Ablo-managed state versus a Data Source that calls your existing API service.
|
|
8
9
|
|
|
9
|
-
## 1. Install
|
|
10
|
+
## 1. Install and set a sandbox key
|
|
10
11
|
|
|
11
12
|
```bash
|
|
12
13
|
npm install @abloatai/ablo
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## 2. Set a Sandbox Key
|
|
16
|
-
|
|
17
|
-
Use an Ablo sandbox key while integrating.
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
14
|
export ABLO_API_KEY=sk_test_...
|
|
21
15
|
```
|
|
22
16
|
|
|
23
17
|
`ABLO_API_KEY` is for trusted server runtimes. Browser apps should use the React
|
|
24
18
|
provider with a scoped session route, not a bundled API key.
|
|
25
19
|
|
|
26
|
-
##
|
|
20
|
+
## 2. Declare schema and write state
|
|
21
|
+
|
|
22
|
+
Your schema is the contract. It generates `ablo.<model>` methods for app code,
|
|
23
|
+
server actions, agents, React reads, and Data Source requests.
|
|
27
24
|
|
|
28
25
|
```ts
|
|
29
26
|
import Ablo from '@abloatai/ablo';
|
|
@@ -41,25 +38,21 @@ export const ablo = Ablo({
|
|
|
41
38
|
schema,
|
|
42
39
|
apiKey: process.env.ABLO_API_KEY,
|
|
43
40
|
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Customer apps should always pass `schema`. Treat it like Prisma's schema file:
|
|
47
|
-
it is the source of truth for typed model clients, realtime subscriptions,
|
|
48
|
-
agent writes, and Data Source requests.
|
|
49
|
-
|
|
50
|
-
## 4. Create and Update
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
41
|
await ablo.ready();
|
|
54
42
|
|
|
55
43
|
const created = await ablo.weatherReports.create({
|
|
56
|
-
|
|
57
|
-
|
|
44
|
+
data: {
|
|
45
|
+
location: 'Stockholm',
|
|
46
|
+
status: 'pending',
|
|
47
|
+
},
|
|
58
48
|
});
|
|
59
49
|
|
|
60
|
-
const updated = await ablo.weatherReports.update(
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
const updated = await ablo.weatherReports.update({
|
|
51
|
+
id: created.id,
|
|
52
|
+
data: {
|
|
53
|
+
status: 'ready',
|
|
54
|
+
forecast: 'Light rain, 13C',
|
|
55
|
+
},
|
|
63
56
|
});
|
|
64
57
|
|
|
65
58
|
console.log({ id: updated.id, status: updated.status });
|
|
@@ -71,77 +64,86 @@ Expected output:
|
|
|
71
64
|
{ id: '...', status: 'ready' }
|
|
72
65
|
```
|
|
73
66
|
|
|
74
|
-
##
|
|
67
|
+
## Run the example
|
|
75
68
|
|
|
76
69
|
```bash
|
|
77
|
-
cd
|
|
78
|
-
ABLO_API_KEY=sk_test_... npx tsx quickstart.ts
|
|
70
|
+
cd packages/sync-engine
|
|
71
|
+
ABLO_API_KEY=sk_test_... npx tsx examples/quickstart.ts
|
|
79
72
|
```
|
|
80
73
|
|
|
81
|
-
##
|
|
74
|
+
## Add coordination for slow work
|
|
82
75
|
|
|
83
76
|
When AI or background work will touch an existing row for more than a quick
|
|
84
|
-
write, coordinate through
|
|
85
|
-
claim
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
77
|
+
write, coordinate through `claim({ id })`. It claims the row and hands a handle
|
|
78
|
+
back; `claim.state({ id })` reads who is currently working on it without blocking;
|
|
79
|
+
and you write the usual way with `ablo.<model>.update({ id, data })`.
|
|
80
|
+
|
|
81
|
+
Claims don't lock. If another writer holds the row, `claim` waits for them,
|
|
82
|
+
re-reads the fresh row, then hands it to you — so two writers serialize instead
|
|
83
|
+
of clobbering. Normal reads still work while the claim is held. If a server read
|
|
84
|
+
should not return a row while someone else is mid-edit, pass `ifClaimed: 'wait'`
|
|
85
|
+
to wait for the claim to clear, or `ifClaimed: 'fail'` to error out instead.
|
|
86
|
+
Call `handle.release()` when your work is done.
|
|
91
87
|
|
|
92
88
|
```ts
|
|
93
89
|
// Claim the row so other participants serialize behind us while we work.
|
|
94
|
-
await ablo.weatherReports.claim(
|
|
95
|
-
'weather_stockholm',
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
90
|
+
const handle = await ablo.weatherReports.claim({
|
|
91
|
+
id: 'weather_stockholm',
|
|
92
|
+
action: 'checking_weather',
|
|
93
|
+
ttl: '2m',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Your existing weather tool or agent call. While this runs, other clients
|
|
97
|
+
// see that weather_stockholm is being checked.
|
|
98
|
+
const weather = await weatherAgent.getWeather(handle.data.location);
|
|
99
|
+
|
|
100
|
+
await ablo.weatherReports.update({
|
|
101
|
+
id: handle.data.id,
|
|
102
|
+
data: {
|
|
103
|
+
status: 'ready',
|
|
104
|
+
forecast: weather.summary,
|
|
105
105
|
},
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await handle.release();
|
|
108
109
|
```
|
|
109
110
|
|
|
110
|
-
Ablo does not fetch the weather.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
Ablo does not fetch the weather. If another participant already holds the row,
|
|
112
|
+
`claim` waits for them to finish, re-reads, and then hands you the fresh row.
|
|
113
|
+
While you hold the claim, `update({ id, data })` rejects with `AbloStaleContextError`
|
|
114
|
+
if someone else changed the row first — so you never overwrite work you didn't
|
|
115
|
+
see. Call `handle.release()` once your work is done.
|
|
115
116
|
|
|
116
|
-
##
|
|
117
|
+
## Multiplayer and claimed work
|
|
117
118
|
|
|
118
119
|
There is no separate multiplayer mode. Use the same schema client for human UI,
|
|
119
120
|
server actions, and agents; Ablo fans out confirmed writes and keeps active
|
|
120
121
|
claims visible on the same model row.
|
|
121
122
|
|
|
122
|
-
`
|
|
123
|
-
For schema clients, `claim(id
|
|
123
|
+
`claim.state({ id })` tells you when another human or agent is active on the same row.
|
|
124
|
+
For schema clients, `claim({ id })` waits fairly, re-reads, and then lets you
|
|
124
125
|
write through the model.
|
|
125
126
|
|
|
126
127
|
```ts
|
|
127
|
-
const active = ablo.weatherReports.
|
|
128
|
+
const active = ablo.weatherReports.claim.state({ id: 'weather_stockholm' });
|
|
128
129
|
if (active) {
|
|
129
130
|
console.log(`${active.heldBy} is ${active.action}`);
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
await ablo.weatherReports.claim('weather_stockholm'
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
const handle = await ablo.weatherReports.claim({ id: 'weather_stockholm' });
|
|
134
|
+
await ablo.weatherReports.update({ id: handle.data.id, data: { status: 'ready' } });
|
|
135
|
+
await handle.release();
|
|
135
136
|
```
|
|
136
137
|
|
|
137
138
|
Use `{ wait: false }` on `claim` when work should be skipped instead of queued
|
|
138
139
|
behind an active holder.
|
|
139
140
|
|
|
140
|
-
##
|
|
141
|
+
## Next steps
|
|
141
142
|
|
|
142
143
|
Keep using the schema client for app and agent writes.
|
|
143
144
|
|
|
144
145
|
- [Integration Guide](./integration-guide.md) explains the full app, React, Data Source, multiplayer, and agent path.
|
|
146
|
+
- [Schema Contract](./schema-contract.md) explains what the schema drives across SDK, React, agents, Data Source, and schema push.
|
|
145
147
|
- [Guarantees](./guarantees.md) explains what confirmed writes and stale checks mean.
|
|
146
148
|
- [Client Behavior](./client-behavior.md) covers errors, retries, and public imports.
|
|
147
149
|
- [Connect Your Database](./data-sources.md) covers the optional route for teams keeping rows in their own database.
|
package/docs/react.md
CHANGED
|
@@ -56,7 +56,7 @@ export function Providers({
|
|
|
56
56
|
| `url` | hosted endpoint | WebSocket URL of the sync server (`wss://…`). Hosted apps omit it. |
|
|
57
57
|
| `apiKey` | session/cookie | Bootstrap auth. Browser apps **omit this** — the key stays server-side. See Identity below. |
|
|
58
58
|
| `fallback` | neutral spinner | Rendered during the *first* bootstrap only. Pass a branded skeleton, `null`, or `'passthrough'`. |
|
|
59
|
-
| `bootstrapMode` | `'full'` | `'full'`
|
|
59
|
+
| `bootstrapMode` | `'full'` | `'full'` loads existing rows before the app renders, so reads are populated on first paint; `'none'` skips that initial load and only streams changes as they happen.|
|
|
60
60
|
| `persistence` | `'volatile'` | `'indexeddb'` opts into a durable browser cache that survives reloads. |
|
|
61
61
|
| `onSessionExpired` | — | Fired after the engine has already purged on a rejected session — use for redirect-to-sign-in. |
|
|
62
62
|
| `onError` | — | Engine / WebSocket / `postBootstrap` errors. Wire to Sentry / Datadog. |
|
|
@@ -74,8 +74,8 @@ reaches the browser, is the whole of
|
|
|
74
74
|
import { useAblo } from '@abloatai/ablo/react';
|
|
75
75
|
|
|
76
76
|
export function ReportView({ report: serverReport }: { report: { id: string; location: string } }) {
|
|
77
|
-
const report = useAblo((ablo) => ablo.weatherReports.
|
|
78
|
-
const active = useAblo((ablo) => ablo.weatherReports.
|
|
77
|
+
const report = useAblo((ablo) => ablo.weatherReports.get(serverReport.id)) ?? serverReport;
|
|
78
|
+
const active = useAblo((ablo) => ablo.weatherReports.claim.state({ id: serverReport.id }));
|
|
79
79
|
const claimed = Boolean(active);
|
|
80
80
|
|
|
81
81
|
return <article>{report.location}</article>;
|
|
@@ -84,12 +84,13 @@ export function ReportView({ report: serverReport }: { report: { id: string; loc
|
|
|
84
84
|
|
|
85
85
|
The hook:
|
|
86
86
|
|
|
87
|
-
1.
|
|
87
|
+
1. Uses the same `ablo.<model>.get(id)` / `.getAll()` methods you'd call anywhere
|
|
88
|
+
else in the SDK — the hook just makes them reactive.
|
|
88
89
|
2. Tracks the model fields read by the selector and re-renders when confirmed
|
|
89
90
|
deltas arrive.
|
|
90
91
|
3. Lets Server Component data stay outside the hook: use `?? serverReport` when a
|
|
91
92
|
parent already loaded the row.
|
|
92
|
-
4. Works for coordination state too, such as `ablo.weatherReports.
|
|
93
|
+
4. Works for coordination state too, such as `ablo.weatherReports.claim.state({ id })`.
|
|
93
94
|
|
|
94
95
|
Use the zero-argument form only when you need the full client for callbacks,
|
|
95
96
|
effects, or writes:
|
|
@@ -98,15 +99,14 @@ effects, or writes:
|
|
|
98
99
|
const abloClient = useAblo();
|
|
99
100
|
```
|
|
100
101
|
|
|
101
|
-
Prefer selector reads like `useAblo((ablo) => ablo.<model>.
|
|
102
|
-
|
|
103
|
-
should use the same model-client shape as the rest of the SDK.
|
|
102
|
+
Prefer selector reads like `useAblo((ablo) => ablo.<model>.get(id))`. Older hooks
|
|
103
|
+
also accept a string model name; prefer the selector form shown above.
|
|
104
104
|
|
|
105
105
|
For collections, keep the selector on the model client too:
|
|
106
106
|
|
|
107
107
|
```tsx
|
|
108
108
|
const reports = useAblo((ablo) =>
|
|
109
|
-
ablo.weatherReports.
|
|
109
|
+
ablo.weatherReports.getAll({
|
|
110
110
|
where: { projectId },
|
|
111
111
|
filter: (report) => report.status !== 'ready',
|
|
112
112
|
state: 'live',
|
|
@@ -117,10 +117,14 @@ const reports = useAblo((ablo) =>
|
|
|
117
117
|
## Server Load
|
|
118
118
|
|
|
119
119
|
```tsx
|
|
120
|
-
const
|
|
120
|
+
const report = await ablo.weatherReports.retrieve({ id });
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
-
Use `
|
|
123
|
+
Use `retrieve` in Server Components when the row may not be in the local pool
|
|
124
|
+
yet — it hydrates from the local store and the server, and returns a Promise, so
|
|
125
|
+
`await` it. (Server reads come in two shapes: `retrieve({ id })` for one row and
|
|
126
|
+
`list({ where })` for many; both are async. The synchronous local reads are
|
|
127
|
+
`get`/`getAll`/`getCount`, used in render below.)
|
|
124
128
|
|
|
125
129
|
## Writes
|
|
126
130
|
|
|
@@ -130,7 +134,9 @@ For Server Actions and route handlers, call the SDK directly:
|
|
|
130
134
|
import { ablo } from '@/lib/ablo';
|
|
131
135
|
|
|
132
136
|
const snap = ablo.snapshot({ weatherReports: id });
|
|
133
|
-
await ablo.weatherReports.update(
|
|
137
|
+
await ablo.weatherReports.update({
|
|
138
|
+
id,
|
|
139
|
+
data: patch,
|
|
134
140
|
readAt: snap.stamp,
|
|
135
141
|
onStale: 'reject',
|
|
136
142
|
wait: 'confirmed',
|
|
@@ -146,11 +152,13 @@ const ablo = useAblo();
|
|
|
146
152
|
async function markReady() {
|
|
147
153
|
if (!ablo) return;
|
|
148
154
|
const snap = ablo.snapshot({ weatherReports: id });
|
|
149
|
-
await ablo.weatherReports.update(
|
|
155
|
+
await ablo.weatherReports.update({
|
|
150
156
|
id,
|
|
151
|
-
{ status: 'ready' },
|
|
152
|
-
|
|
153
|
-
|
|
157
|
+
data: { status: 'ready' },
|
|
158
|
+
readAt: snap.stamp,
|
|
159
|
+
onStale: 'reject',
|
|
160
|
+
wait: 'confirmed',
|
|
161
|
+
});
|
|
154
162
|
}
|
|
155
163
|
```
|
|
156
164
|
|
package/docs/roadmap.md
CHANGED
|
@@ -5,22 +5,22 @@ What is shipped, what is next, and what we will not build.
|
|
|
5
5
|
## Shipped
|
|
6
6
|
|
|
7
7
|
- **Models, claims, commits** — the core API.
|
|
8
|
-
- **Audit log** — hash-chained
|
|
8
|
+
- **Audit log** — a tamper-evident, hash-chained record of who did what,
|
|
9
|
+
per principal.
|
|
9
10
|
- **MCP transport** — HTTP server at `/api/mcp`.
|
|
10
11
|
- **TypeScript SDK** — `@abloatai/ablo`, with React bindings.
|
|
11
12
|
- **Dashboard** — keys, audit, metrics, allowed origins.
|
|
12
|
-
- **Schema migrations** —
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
`ablo generate` emits typed clients from a pushed schema
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- **
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`intent(id)` handles for lease/await/commit coordination between agents.
|
|
13
|
+
- **Schema migrations** — change a model's shape after it already has data.
|
|
14
|
+
Ablo plans the migration, tells you which changes are safe to auto-apply,
|
|
15
|
+
and backfills the rest; the server applies and activates a migration only
|
|
16
|
+
if it succeeds. `ablo generate` emits typed clients from a pushed schema.
|
|
17
|
+
- **Structured errors** — every error has a stable code and a request id,
|
|
18
|
+
and the same codes work whether you reach Ablo over HTTP, WebSocket, or MCP.
|
|
19
|
+
- **Sync groups** — clients automatically receive only the records they have
|
|
20
|
+
access to, based on relationships you declare in the schema.
|
|
21
|
+
- **Agent coordination** — when several agents work on the same model, they
|
|
22
|
+
take turns instead of overwriting each other, via `intent(id)` handles
|
|
23
|
+
that claim, wait, and commit.
|
|
24
24
|
|
|
25
25
|
## In flight
|
|
26
26
|
|