okengine 0.17.1 → 0.18.3

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 (197) hide show
  1. package/README.md +9 -5
  2. package/manifest.v1.schema.json +61 -2
  3. package/package.json +19 -4
  4. package/site/content/docs/elements/clock.mdx +1 -1
  5. package/site/content/docs/elements/flow.mdx +25 -1
  6. package/site/content/docs/elements/store.mdx +287 -341
  7. package/site/content/docs/elements/vault.mdx +5 -5
  8. package/site/content/docs/get-started/installation.mdx +1 -2
  9. package/site/content/docs/get-started/introduction.mdx +54 -110
  10. package/site/content/docs/get-started/meta.json +9 -1
  11. package/site/content/docs/get-started/testing.mdx +328 -0
  12. package/site/content/docs/get-started/why.mdx +94 -70
  13. package/site/content/docs/index.mdx +1 -1
  14. package/site/content/docs/plugins/apple.mdx +151 -0
  15. package/site/content/docs/plugins/discord.mdx +139 -0
  16. package/site/content/docs/plugins/facebook.mdx +134 -0
  17. package/site/content/docs/plugins/figma.mdx +138 -0
  18. package/site/content/docs/plugins/github.mdx +138 -0
  19. package/site/content/docs/plugins/google.mdx +153 -0
  20. package/site/content/docs/plugins/index.mdx +47 -1
  21. package/site/content/docs/plugins/meta.json +10 -0
  22. package/site/content/docs/plugins/microsoft.mdx +151 -0
  23. package/site/content/docs/plugins/oauth.mdx +188 -0
  24. package/site/content/docs/plugins/x.mdx +125 -0
  25. package/site/content/docs/reference/cli.md +3 -2
  26. package/site/content/docs/reference/client.mdx +58 -1
  27. package/site/content/docs/reference/configuration.mdx +2 -4
  28. package/site/content/docs/reference/fx.mdx +3 -1
  29. package/site/content/docs/reference/index.mdx +0 -5
  30. package/site/content/docs/reference/meta.json +2 -2
  31. package/site/content/docs/reference/okid.mdx +137 -0
  32. package/src/auth/bindings.ts +1 -1
  33. package/src/auth/config.ts +9 -0
  34. package/src/auth/identity-sql.ts +314 -0
  35. package/src/auth/identity.ts +140 -2
  36. package/src/auth/index.ts +17 -1
  37. package/src/auth/method-context.ts +3 -0
  38. package/src/auth/oauth-as/cimd.ts +132 -0
  39. package/src/auth/oauth-as/crypto.test.ts +101 -0
  40. package/src/auth/oauth-as/crypto.ts +393 -0
  41. package/src/auth/oauth-as/errors.ts +68 -0
  42. package/src/auth/oauth-as/http.test.ts +419 -0
  43. package/src/auth/oauth-as/http.ts +842 -0
  44. package/src/auth/oauth-as/stores.ts +61 -0
  45. package/src/auth/oauth-as/tables.ts +142 -0
  46. package/src/auth/tables.ts +0 -11
  47. package/src/bench/README.md +83 -0
  48. package/src/bench/REPORT.md +176 -0
  49. package/src/bench/g01-rls-stamp.bench.ts +194 -0
  50. package/src/bench/g02-clock-per-tenant.bench.ts +158 -0
  51. package/src/bench/g03-signal-once.bench.ts +157 -0
  52. package/src/bench/g03-signal-reconnect.bench.ts +254 -0
  53. package/src/bench/g03-signal-sse-memory.bench.ts +191 -0
  54. package/src/bench/g04-auth-vault-hotpath.bench.ts +170 -0
  55. package/src/bench/g05-sustained-full.bench.ts +265 -0
  56. package/src/bench/g06-mixed-load.bench.ts +260 -0
  57. package/src/bench/g07-vault-crypto.bench.ts +100 -0
  58. package/src/bench/g07-vault-rotate-under-read.bench.ts +285 -0
  59. package/src/bench/g08-conn-oversubscribe.bench.ts +194 -0
  60. package/src/bench/g08-store-kv-durable.bench.ts +133 -0
  61. package/src/bench/g08-store-sql.bench.ts +178 -0
  62. package/src/bench/g09-journal-sustained.bench.ts +203 -0
  63. package/src/bench/g10-observability-contention.bench.ts +246 -0
  64. package/src/bench/g11-cold-start-cycle.bench.ts +164 -0
  65. package/src/bench/g13-elements.bench.ts +427 -0
  66. package/src/bench/g14-graceful-shutdown.bench.ts +244 -0
  67. package/src/bench/g15-postgres-degradation.bench.ts +264 -0
  68. package/src/bench/g16-live-query-fanout.bench.ts +206 -0
  69. package/src/bench/lib/event-loop-lag.ts +26 -0
  70. package/src/bench/lib/infra.ts +60 -0
  71. package/src/bench/lib/report.ts +52 -0
  72. package/src/bench/lib/rss-sampler.ts +61 -0
  73. package/src/bench/lib/signal-pg.ts +88 -0
  74. package/src/bench/load-app.ts +337 -0
  75. package/src/bench/load-child.ts +108 -0
  76. package/src/bench/smoke.bench.ts +43 -0
  77. package/src/cli/competitor-mention-removal.test.ts +28 -0
  78. package/src/cli/doctor-fd.ts +117 -0
  79. package/src/cli/doctor.test.ts +192 -0
  80. package/src/cli/doctor.ts +129 -1
  81. package/src/client/create.ts +95 -1
  82. package/src/client/index.ts +9 -2
  83. package/src/client/transport.ts +11 -4
  84. package/src/client/use-live-query.ts +154 -0
  85. package/src/client-react/index.ts +15 -1
  86. package/src/client-react/live-resource.ts +246 -0
  87. package/src/client-react/use-live-query.test.ts +475 -0
  88. package/src/client-react/use-live-query.ts +530 -0
  89. package/src/compiler/extract.test.ts +518 -0
  90. package/src/compiler/extract.ts +386 -19
  91. package/src/console/server/invoke-user-flow.ts +2 -1
  92. package/src/console/ui-next/dist/assets/{access-page-9Wwx1g4Q.js → access-page-DY4N6nnk.js} +1 -1
  93. package/src/console/ui-next/dist/assets/{flows-page-DydRDi70.js → flows-page-CsPDMrVM.js} +1 -1
  94. package/src/console/ui-next/dist/assets/{index-Cg9nxd6m.js → index-CcTDXHuz.js} +3 -3
  95. package/src/console/ui-next/dist/assets/{observability-page-D-OzmFOI.js → observability-page-CKR595wP.js} +1 -1
  96. package/src/console/ui-next/dist/assets/{store-page-CS5-aETQ.js → store-page-02xOiqIK.js} +3 -3
  97. package/src/console/ui-next/dist/assets/{units-page-B_zeux2Y.js → units-page-CpPFFKyE.js} +1 -1
  98. package/src/console/ui-next/dist/assets/{vault-page-CsMg7XDW.js → vault-page-BsMf-9_W.js} +1 -1
  99. package/src/console/ui-next/dist/index.html +1 -1
  100. package/src/console/ui-next/src/features/store/lib/fields-from-table.ts +36 -2
  101. package/src/drivers/cdc-outbox.ts +389 -0
  102. package/src/drivers/memory.ts +20 -0
  103. package/src/drivers/oauth-apple.ts +156 -0
  104. package/src/drivers/oauth-discord.ts +79 -0
  105. package/src/drivers/oauth-facebook.ts +80 -0
  106. package/src/drivers/oauth-figma.ts +116 -0
  107. package/src/drivers/oauth-github.ts +92 -0
  108. package/src/drivers/oauth-google.ts +142 -0
  109. package/src/drivers/oauth-microsoft.ts +174 -0
  110. package/src/drivers/oauth-oidc.ts +293 -0
  111. package/src/drivers/oauth-shared.ts +326 -0
  112. package/src/drivers/oauth-types.ts +159 -0
  113. package/src/drivers/oauth-x.ts +77 -0
  114. package/src/drivers/oauth2-common.ts +95 -0
  115. package/src/drivers/oauth2-token.ts +61 -0
  116. package/src/drivers/pg-rls-row-passes.ts +251 -0
  117. package/src/drivers/pg-rls.ts +2 -0
  118. package/src/drivers/postgres.ts +45 -2
  119. package/src/drivers/signal-postgres.ts +2 -1
  120. package/src/elements/channel/runtime.ts +29 -2
  121. package/src/elements/channel.test.ts +52 -0
  122. package/src/elements/gate/boot.ts +29 -2
  123. package/src/elements/store/emit-drizzle.ts +147 -14
  124. package/src/elements/store/field-ddl.test.ts +118 -0
  125. package/src/elements/store/field-types.test.ts +455 -0
  126. package/src/elements/store/list-query.golden.json +777 -0
  127. package/src/elements/store/list-query.parity.test.ts +396 -0
  128. package/src/elements/store/list-query.ts +792 -0
  129. package/src/elements/store/live-default.test.ts +136 -0
  130. package/src/elements/store/live-http.test.ts +160 -0
  131. package/src/elements/store/live-isolation.test.ts +291 -0
  132. package/src/elements/store/live-query-runtime.test.ts +323 -0
  133. package/src/elements/store/live-query-runtime.ts +403 -0
  134. package/src/elements/store/live-query-server.test.ts +377 -0
  135. package/src/elements/store/live-query-server.ts +102 -0
  136. package/src/elements/store/live-query.ts +97 -0
  137. package/src/elements/store/resource.ts +189 -680
  138. package/src/elements/store/rls-row-passes-policies.parity.test.ts +665 -0
  139. package/src/elements/store/schema-decl.ts +539 -41
  140. package/src/elements/store/sql-rls-stamp.test.ts +27 -0
  141. package/src/elements/store/sql-session.ts +297 -35
  142. package/src/elements/store/table.ts +102 -21
  143. package/src/elements/store.test.ts +3 -1
  144. package/src/elements/store.ts +12 -1
  145. package/src/elements/vault/chaos-child.ts +74 -1
  146. package/src/elements/vault/chaos.test.ts +4 -2
  147. package/src/elements/vault/storage.ts +4 -2
  148. package/src/index.ts +4 -1
  149. package/src/kernel/app-auth.ts +1 -0
  150. package/src/kernel/app.ts +116 -2
  151. package/src/kernel/auth-sharing.test.ts +196 -0
  152. package/src/kernel/boot.test.ts +3 -3
  153. package/src/kernel/errors.ts +8 -0
  154. package/src/kernel/fx.test.ts +1 -0
  155. package/src/kernel/fx.ts +14 -2
  156. package/src/kernel/horizontal-child.ts +2 -1
  157. package/src/kernel/http-resource.ts +33 -7
  158. package/src/kernel/identity-host-persist.test.ts +119 -0
  159. package/src/kernel/instance-id.ts +4 -2
  160. package/src/kernel/journal.ts +2 -1
  161. package/src/kernel/mcp-tool.test.ts +95 -0
  162. package/src/kernel/on.ts +9 -0
  163. package/src/kernel/realtime-bind.ts +326 -0
  164. package/src/kernel/resource-live.ts +117 -0
  165. package/src/kernel/triggers.ts +86 -4
  166. package/src/manifest/diff.ts +37 -0
  167. package/src/manifest/types.ts +64 -2
  168. package/src/okid.bench.test.ts +64 -0
  169. package/src/okid.test.ts +338 -0
  170. package/src/okid.ts +245 -0
  171. package/src/plugins/anonymous.ts +19 -1
  172. package/src/plugins/auth/shared.ts +15 -0
  173. package/src/plugins/index.ts +2 -0
  174. package/src/plugins/magic-link.ts +10 -8
  175. package/src/plugins/mcp-oauth.ts +208 -0
  176. package/src/plugins/oauth/flow-store.ts +117 -0
  177. package/src/plugins/oauth/link.ts +69 -0
  178. package/src/plugins/oauth/shared.ts +108 -0
  179. package/src/plugins/oauth/token-vault.ts +100 -0
  180. package/src/plugins/oauth.security.test.ts +535 -0
  181. package/src/plugins/oauth.ts +532 -0
  182. package/src/plugins/otp.ts +48 -6
  183. package/src/plugins/passkey.ts +20 -1
  184. package/src/plugins/two-factor.ts +11 -0
  185. package/src/plugins/username.ts +40 -7
  186. package/src/release/build-lib.ts +7 -1
  187. package/src/release/measure.ts +1 -0
  188. package/src/release/official-plugins.ts +4 -1
  189. package/src/runs/collect.ts +2 -1
  190. package/src/runs/drivers/files.ts +2 -1
  191. package/src/test/create-test-app.ts +114 -5
  192. package/src/test/export-bundle.test.ts +33 -0
  193. package/src/test/live-signals.test.ts +83 -0
  194. package/src/test/tenant-isolation.test.ts +175 -0
  195. package/src/testing.ts +26 -0
  196. package/src/upgrade/codemods.ts +1 -1
  197. package/site/content/docs/reference/migrating-environments.mdx +0 -158
package/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
  <a href="https://www.npmjs.com/package/okengine"><img alt="npm" src="https://shieldcn.dev/npm/okengine.svg?size=sm&variant=outline" /></a>
17
17
  <a href="https://jsr.io/@omqkhafi/okengine"><img alt="JSR" src="https://shieldcn.dev/jsr/@omqkhafi/okengine.svg?size=sm&variant=outline" /></a>
18
18
  <a href="https://opensource.org/licenses/MIT"><img alt="MIT" src="https://shieldcn.dev/npm/license/okengine.svg?size=sm&variant=outline" /></a>
19
- <a href="https://bun.sh"><img alt="Bun >=1.3.14" src="https://shieldcn.dev/badge/Bun-%3E%3D1.3.14.svg?logo=bun&size=sm&variant=outline" /></a>
19
+ <a href="https://bun.sh"><img alt="Bun >=1.4.0" src="https://shieldcn.dev/badge/Bun-%3E%3D1.4.0.svg?logo=bun&size=sm&variant=outline" /></a>
20
20
  <a href="https://github.com/omqkhafi/okengine/actions"><img alt="CI" src="https://shieldcn.dev/github/ci/omqkhafi/okengine.svg?size=sm&variant=outline" /></a>
21
21
  <a href="https://github.com/omqkhafi/okengine/commits"><img alt="last commit" src="https://shieldcn.dev/github/last-commit/omqkhafi/okengine.svg?size=sm&variant=outline" /></a>
22
22
  </p>
@@ -39,7 +39,7 @@
39
39
 
40
40
  ## Install
41
41
 
42
- Requires [Bun](https://bun.sh) `>=1.3.14`.
42
+ Requires [Bun](https://bun.sh) `>=1.4.0`.
43
43
 
44
44
  ```bash
45
45
  bunx create-oke@latest my-app # scaffold (recommended)
@@ -69,12 +69,13 @@ Mnemonic: **O·K·E = 6·5·3**.
69
69
  The starter already exports a health Flow — change it and save; App and Console update from the same Manifest:
70
70
 
71
71
  ```typescript
72
- import { on, flow, http, gate } from "okengine";
72
+ // src/flows/main/health.ts folders are the URL: `main/health.ts` → GET /health
73
+ import { on, flow, http } from "okengine";
73
74
  import { z } from "zod";
74
75
 
75
76
  export const health = on(
76
- http.get("/health").gate(gate.public),
77
- flow("health.check", {
77
+ http.get().public(),
78
+ flow({
78
79
  out: z.object({ ok: z.literal(true) }),
79
80
  do: () => ({ ok: true as const }),
80
81
  }),
@@ -91,6 +92,8 @@ const api = createClient<App>("http://localhost:6530");
91
92
  const { data, error } = await api.main.health({});
92
93
  ```
93
94
 
95
+ Native ids: import `okid` from `okengine/okid` — compact, URL-safe, cryptographically random (`okid()`, `okid(16)`, `okid({ sortable: true })`).
96
+
94
97
  Full walkthrough: [Basic usage](https://oke.omqkhafi.dev/docs/get-started/basic-usage).
95
98
 
96
99
  ## One law
@@ -140,6 +143,7 @@ Store apps sync schema with `oke db push|generate|migrate` (local `oke dev` auto
140
143
  | Agents index | [/llms.txt](https://oke.omqkhafi.dev/llms.txt) |
141
144
  | Manifest | [`manifest.v1.schema.json`](manifest.v1.schema.json) |
142
145
  | Budgets | [`BUDGETS.md`](BUDGETS.md) |
146
+ | Security | [`SECURITY.md`](SECURITY.md) |
143
147
  | Contributing | [`CONTRIBUTING.md`](CONTRIBUTING.md) |
144
148
 
145
149
  ## License
@@ -219,7 +219,17 @@
219
219
  "signal": { "type": "string", "minLength": 1 },
220
220
  "cron": { "type": "string", "minLength": 1 },
221
221
  "every": { "type": "string", "minLength": 1 },
222
- "cdc": { "$ref": "#/$defs/CdcTrigger" }
222
+ "cdc": { "$ref": "#/$defs/CdcTrigger" },
223
+ "mcp": { "$ref": "#/$defs/McpTrigger" }
224
+ }
225
+ },
226
+ "McpTrigger": {
227
+ "description": "Explicit MCP tool exposure. Presence means the flow is listed over MCP for OAuth user-plane clients; absence means not an MCP tool (deny-by-default).",
228
+ "type": "object",
229
+ "required": ["name"],
230
+ "additionalProperties": false,
231
+ "properties": {
232
+ "name": { "type": "string", "minLength": 1 }
223
233
  }
224
234
  },
225
235
  "Slo": {
@@ -378,7 +388,38 @@
378
388
  "type": "object",
379
389
  "additionalProperties": false,
380
390
  "properties": {
381
- "type": { "type": "string", "enum": ["text", "integer"] },
391
+ "type": {
392
+ "type": "string",
393
+ "enum": [
394
+ "text",
395
+ "varchar",
396
+ "char",
397
+ "boolean",
398
+ "smallint",
399
+ "integer",
400
+ "bigint",
401
+ "serial",
402
+ "smallserial",
403
+ "bigserial",
404
+ "numeric",
405
+ "real",
406
+ "doublePrecision",
407
+ "json",
408
+ "jsonb",
409
+ "uuid",
410
+ "time",
411
+ "timestamp",
412
+ "date",
413
+ "interval",
414
+ "point",
415
+ "line",
416
+ "bytea",
417
+ "inet",
418
+ "cidr",
419
+ "macaddr",
420
+ "macaddr8"
421
+ ]
422
+ },
382
423
  "nullable": { "type": "boolean" },
383
424
  "primaryKey": { "type": "boolean" },
384
425
  "unique": { "type": "boolean" },
@@ -390,6 +431,24 @@
390
431
  { "type": "null" }
391
432
  ]
392
433
  },
434
+ "enumValues": {
435
+ "description": "Allowed string values for enum columns (text/varchar/char).",
436
+ "type": "array",
437
+ "items": { "type": "string" },
438
+ "minItems": 1
439
+ },
440
+ "length": { "description": "varchar/char max length.", "type": "integer", "minimum": 1 },
441
+ "precision": {
442
+ "description": "Fractional-second digits (time/timestamp, 0-6) or interval precision.",
443
+ "type": "integer"
444
+ },
445
+ "scale": { "description": "numeric(p, s) scale digits.", "type": "integer" },
446
+ "withTimezone": { "description": "time/timestamp timezone flavor.", "type": "boolean" },
447
+ "mode": {
448
+ "description": "JS mapping selector for temporal/numeric/point/line/bigint columns.",
449
+ "type": "string"
450
+ },
451
+ "fields": { "description": "interval field qualifier.", "type": "string" },
393
452
  "sqlName": { "type": "string", "minLength": 1 },
394
453
  "description": {
395
454
  "description": "Optional human description; consumers fall back to the column map key.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okengine",
3
- "version": "0.17.1",
3
+ "version": "0.18.3",
4
4
  "description": "One law. Eight elements. Ten exports. One package. One manifest. Every backend need is derived, never added.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -72,6 +72,12 @@
72
72
  "./client/auth": "./src/client/auth.ts",
73
73
  "./client-react": "./src/client-react/index.ts",
74
74
  "./test": "./src/test/index.ts",
75
+ "./testing": {
76
+ "types": "./src/testing.ts",
77
+ "bun": "./src/testing.ts",
78
+ "import": "./dist/testing.js",
79
+ "default": "./dist/testing.js"
80
+ },
75
81
  "./config": "./src/config/index.ts",
76
82
  "./auth": "./src/auth/index.ts",
77
83
  "./console": "./src/console/index.ts",
@@ -86,7 +92,13 @@
86
92
  "./ai": "./src/elements/ai.ts",
87
93
  "./runs": "./src/runs/index.ts",
88
94
  "./drivers": "./src/drivers/index.ts",
89
- "./drivers/*": "./src/drivers/*"
95
+ "./drivers/*": "./src/drivers/*",
96
+ "./okid": {
97
+ "types": "./src/okid.ts",
98
+ "bun": "./src/okid.ts",
99
+ "import": "./dist/okid.js",
100
+ "default": "./dist/okid.js"
101
+ }
90
102
  },
91
103
  "scripts": {
92
104
  "typecheck": "tsc --noEmit && tsc --noEmit -p tests/console/tsconfig.ui-next.json && tsc --noEmit -p src/console/ui-next && bun run --cwd packages/create-oke typecheck && bun run --cwd examples/keel typecheck",
@@ -112,15 +124,18 @@
112
124
  "test:keel:docker": "OKE_TEST_DOCKER=1 bun run --cwd examples/keel test",
113
125
  "build:lib": "bun src/release/build-lib.ts",
114
126
  "prepack": "bun run build && bun run build:lib",
115
- "bench": "bun test ./src/runtime/cold-start.bench.ts src/client/budget.test.ts src/console/budget.test.ts src/kernel/budget.test.ts src/kernel/routing-budget.test.ts src/runs/bench.test.ts",
127
+ "bench": "bun test ./src/runtime/cold-start.bench.ts src/client/budget.test.ts src/console/budget.test.ts src/kernel/budget.test.ts src/kernel/routing-budget.test.ts src/runs/bench.test.ts src/okid.bench.test.ts",
128
+ "bench:load": "OKE_BENCH=1 bun test ./src/bench/*.bench.ts --timeout 600000 --pass-with-no-tests",
116
129
  "budgets": "bun src/release/publish.ts",
117
130
  "gate": "PUBLISH_GATE=1 bun test src/cli/doc-staleness.test.ts src/cli/competitor-mention-removal.test.ts src/drivers/vault-driver-removal.test.ts src/kernel/errors.registry.test.ts src/upgrade/codemods.test.ts scripts/publish.gate.test.ts",
118
131
  "dev": "bun run --cwd site dev",
119
132
  "site:build": "bun run --cwd site build",
133
+ "verify": "bun run lint && bun run fmt:check && bun run typecheck && bun run test && bun run gate && bun run site:build",
120
134
  "ci": "bun scripts/ci.ts",
121
135
  "bump": "bun run scripts/bump-version.ts",
122
136
  "release": "bun run scripts/publish.ts",
123
- "g": "gflows"
137
+ "g": "gflows",
138
+ "postinstall": "bun run --cwd examples/keel prepare"
124
139
  },
125
140
  "dependencies": {
126
141
  "@clack/prompts": "^1.7.0",
@@ -141,7 +141,7 @@ reclaimed after its lease expires and continues at the next unfinished step.
141
141
  | Guarantee | What it means |
142
142
  | ------------------ | -------------------------------------------------------------------------------------------------- |
143
143
  | Leader election | Instances that share one CronStore fire a given tick once (lease TTL, default 30s) |
144
- | Process identity | Boot mints one `instanceId` (`inst-<uuid>`) for Clock, Journal, and the fleet registry |
144
+ | Process identity | Boot mints one `instanceId` (`inst-<okid>`) for Clock, Journal, and the fleet registry |
145
145
  | Catch-up `"one"` | Health counts every missed slot; the runtime still fires **once** when overdue — not once per miss |
146
146
  | Reconciled at boot | Named clocks land in `oke_crons`; the scheduler reads the Store, not the source |
147
147
  | DST detection | Ambiguous local times get a Console warning — schedules are not rewritten or blocked |
@@ -111,7 +111,7 @@ Built-in codes attach a localized `message` ([Errors](/docs/reference/errors));
111
111
  — that is the `as never` trap. See [fx](/docs/reference/fx).
112
112
  </Callout>
113
113
 
114
- ## The five triggers
114
+ ## The triggers
115
115
 
116
116
  <FlowTriggers />
117
117
 
@@ -201,6 +201,30 @@ export const getOrder = flow("orders.get", {
201
201
  const order = await fx.call(getOrder, { id: orderId }); // from any other flow
202
202
  ```
203
203
 
204
+ ### mcp — an agent calls a tool
205
+
206
+ `mcp.tool(name)` marks a flow for **OAuth-protected user-plane** exposure over MCP. Deny-by-default: a flow without this trigger is never listed over `tools/list`. A gate is required, exactly like a sensitive HTTP route:
207
+
208
+ ```typescript
209
+ export const createBooking = on(
210
+ mcp.tool("bookings.create").gate(member), // gates required — deny-by-default
211
+ flow("bookings.create", {
212
+ plane: "user",
213
+ in: BookingRef,
214
+ out: Booking,
215
+ do: async ({ id }, fx) => {
216
+ /* … */
217
+ },
218
+ }),
219
+ );
220
+ ```
221
+
222
+ - The tool name is namespaced (`bookings.create`) and appears in the Manifest under `trigger.mcp.name` with its gates.
223
+ - Binding the **same tool name** twice fails boot with **OKE1018** (`MCP tool "{tool}" is bound twice`).
224
+ - Ungated `mcp.tool(...)` fails gate posture at boot (`GateBootError`) — the trigger never exposes an unauthenticated tool.
225
+
226
+ This is one direction of [MCP](/docs/ai/mcp): exposing your flows as tools. The other is consuming external servers as `ai.mcpServer` tools via `fx.ask` / `ai.agent`.
227
+
204
228
  ## fx — the only door
205
229
 
206
230
  Everything a flow may touch, on one object: