@aotter/mantle 0.1.5-alpha.1 → 0.2.0-alpha.10

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 (495) hide show
  1. package/README.md +166 -298
  2. package/dist/auth/adapter.d.ts +32 -0
  3. package/dist/auth/adapter.js +288 -0
  4. package/dist/auth/admin.d.ts +3 -0
  5. package/dist/auth/admin.js +21 -0
  6. package/dist/auth/caller.d.ts +86 -0
  7. package/dist/auth/caller.js +155 -0
  8. package/dist/auth/hardening.d.ts +24 -0
  9. package/dist/auth/hardening.js +40 -0
  10. package/dist/auth/identity.d.ts +121 -0
  11. package/dist/auth/identity.js +203 -0
  12. package/dist/auth/oauth.d.ts +63 -0
  13. package/dist/auth/oauth.js +217 -0
  14. package/dist/auth/resource-seeds.js +10 -0
  15. package/dist/auth/routes.d.ts +32 -0
  16. package/dist/auth/routes.js +226 -0
  17. package/dist/build/compiler.d.ts +721 -0
  18. package/dist/build/compiler.js +7029 -0
  19. package/dist/build/spec/domain/model/ManifestGrammar.d.ts +439 -0
  20. package/dist/build/spec/domain/model/RuntimePlan.d.ts +168 -0
  21. package/dist/build/spec/domain/model/SqlIr.d.ts +129 -0
  22. package/dist/build/spec/domain/model/UserFields.d.ts +21 -0
  23. package/dist/build/spec/domain/service/PlanFingerprint.d.ts +7 -0
  24. package/dist/build/spec/kernel/diagnostic.d.ts +108 -0
  25. package/dist/bun/assets.d.ts +2 -0
  26. package/dist/bun/assets.js +24 -0
  27. package/dist/domain/adminOrder.js +24 -0
  28. package/dist/domain/columns.js +57 -0
  29. package/dist/domain/describe/chunk-ZVXBGP7Y.js +424 -0
  30. package/dist/domain/describe/index.d.ts +58 -0
  31. package/dist/domain/describe/index.js +115 -0
  32. package/dist/domain/describe/target-hash.d.ts +8 -0
  33. package/dist/domain/describe/target-hash.js +12 -0
  34. package/dist/domain/describe/targets.d.ts +40 -0
  35. package/dist/domain/entry.js +33 -0
  36. package/dist/domain/generated-tools.js +19 -0
  37. package/dist/domain/generation.d.ts +16 -0
  38. package/dist/domain/generation.js +1 -0
  39. package/dist/domain/input.js +17 -0
  40. package/dist/domain/model.js +1 -0
  41. package/dist/domain/paging.js +230 -0
  42. package/dist/domain/recipes.js +24 -0
  43. package/dist/domain/userReferences.js +5 -0
  44. package/dist/domain/wireScalars.js +47 -0
  45. package/dist/infrastructure/adminEntries.js +153 -0
  46. package/dist/infrastructure/adminPaging.js +72 -0
  47. package/dist/infrastructure/artifacts.js +71 -0
  48. package/dist/infrastructure/authSchema.js +83 -0
  49. package/dist/infrastructure/bundle.js +188 -0
  50. package/dist/infrastructure/cli.js +118 -0
  51. package/dist/infrastructure/generate.d.ts +12 -0
  52. package/dist/infrastructure/generate.js +189 -0
  53. package/dist/infrastructure/generateAuth.d.ts +8 -0
  54. package/dist/infrastructure/generateAuth.js +74 -0
  55. package/dist/infrastructure/help.js +60 -0
  56. package/dist/infrastructure/init.js +54 -0
  57. package/dist/infrastructure/inputs.js +41 -0
  58. package/dist/infrastructure/mounts.js +25 -0
  59. package/dist/infrastructure/wrangler.js +109 -0
  60. package/dist/kernel/authorization.d.ts +4 -0
  61. package/dist/kernel/authorization.js +16 -0
  62. package/dist/kernel/binds.d.ts +60 -0
  63. package/dist/kernel/binds.js +49 -0
  64. package/dist/kernel/caller-identity.d.ts +24 -0
  65. package/dist/kernel/caller-identity.js +22 -0
  66. package/dist/kernel/contract-error.d.ts +12 -0
  67. package/dist/kernel/contract-error.js +14 -0
  68. package/dist/kernel/contracts.d.ts +143 -0
  69. package/dist/kernel/contracts.js +97 -0
  70. package/dist/kernel/cron.d.ts +1 -0
  71. package/dist/kernel/cron.js +58 -0
  72. package/dist/kernel/dialect/common.d.ts +38 -0
  73. package/dist/kernel/dialect/common.js +6 -0
  74. package/dist/kernel/dialect/postgres.d.ts +16 -0
  75. package/dist/kernel/dialect/postgres.js +18 -0
  76. package/dist/kernel/dialect/sqlite.d.ts +20 -0
  77. package/dist/kernel/dialect/sqlite.js +34 -0
  78. package/dist/kernel/drain.d.ts +5 -0
  79. package/dist/kernel/drain.js +35 -0
  80. package/dist/kernel/driver/bun-sqlite.d.ts +19 -0
  81. package/dist/kernel/driver/bun-sqlite.js +77 -0
  82. package/dist/kernel/driver/d1.d.ts +24 -0
  83. package/dist/kernel/driver/d1.js +22 -0
  84. package/dist/kernel/driver/postgres.d.ts +25 -0
  85. package/dist/kernel/driver/postgres.js +99 -0
  86. package/dist/kernel/driver/transaction-context.d.ts +10 -0
  87. package/dist/kernel/driver/transaction-context.js +29 -0
  88. package/dist/kernel/email.d.ts +28 -0
  89. package/dist/kernel/email.js +38 -0
  90. package/dist/kernel/host-access.d.ts +10 -0
  91. package/dist/kernel/host-access.js +65 -0
  92. package/dist/kernel/host-caller.d.ts +39 -0
  93. package/dist/kernel/host-caller.js +56 -0
  94. package/dist/kernel/json-body.d.ts +4 -0
  95. package/dist/kernel/json-body.js +44 -0
  96. package/dist/kernel/mcp-apps.d.ts +69 -0
  97. package/dist/kernel/mcp-apps.js +40 -0
  98. package/dist/kernel/mcp-call.d.ts +5 -0
  99. package/dist/kernel/mcp-call.js +25 -0
  100. package/dist/kernel/mcp-handler.js +371 -0
  101. package/dist/kernel/mcp.d.ts +110 -0
  102. package/dist/kernel/mcp.js +50 -0
  103. package/dist/kernel/media.d.ts +27 -0
  104. package/dist/kernel/media.js +216 -0
  105. package/dist/kernel/paging.d.ts +55 -0
  106. package/dist/kernel/paging.js +95 -0
  107. package/dist/kernel/publishing.d.ts +4 -0
  108. package/dist/kernel/publishing.js +16 -0
  109. package/dist/kernel/r2-media.d.ts +42 -0
  110. package/dist/kernel/r2-media.js +78 -0
  111. package/dist/kernel/rest-dispatch.js +38 -0
  112. package/dist/kernel/rest-router.js +64 -0
  113. package/dist/kernel/rest.d.ts +58 -0
  114. package/dist/kernel/rest.js +41 -0
  115. package/dist/kernel/scalars.d.ts +6 -0
  116. package/dist/kernel/scalars.js +44 -0
  117. package/dist/kernel/search.js +5 -0
  118. package/dist/kernel/site-types.d.ts +146 -0
  119. package/dist/kernel/site-types.js +1 -0
  120. package/dist/kernel/site.d.ts +39 -0
  121. package/dist/kernel/site.js +32 -0
  122. package/dist/kernel/store.d.ts +175 -0
  123. package/dist/kernel/store.js +269 -0
  124. package/dist/kernel/surface.d.ts +10 -0
  125. package/dist/kernel/surface.js +19 -0
  126. package/dist/kernel/sweep.d.ts +17 -0
  127. package/dist/kernel/sweep.js +27 -0
  128. package/dist/kernel/translations.d.ts +1 -0
  129. package/dist/kernel/translations.js +13 -0
  130. package/dist/kernel/user-caller.d.ts +31 -0
  131. package/dist/kernel/user-caller.js +67 -0
  132. package/dist/kernel/user-field-name.js +3 -0
  133. package/dist/kernel/user-fields.d.ts +35 -0
  134. package/dist/kernel/user-fields.js +81 -0
  135. package/dist/kernel/validation.d.ts +15 -0
  136. package/dist/kernel/validation.js +54 -0
  137. package/dist/kernel/web-gateway.js +68 -0
  138. package/dist/kernel/web-head.js +15 -0
  139. package/dist/kernel/web-index.js +40 -0
  140. package/dist/kernel/web-response.js +8 -0
  141. package/dist/kernel/web.d.ts +77 -0
  142. package/dist/kernel/web.js +124 -0
  143. package/dist/kernel/where.js +27 -0
  144. package/dist/testing/index.d.ts +1 -0
  145. package/dist/testing/index.js +1 -0
  146. package/dist/usecase/adminBindings.js +143 -0
  147. package/dist/usecase/adminClient.js +87 -0
  148. package/dist/usecase/adminDescriptors.js +107 -0
  149. package/dist/usecase/generate.js +166 -0
  150. package/dist/usecase/handlers.js +21 -0
  151. package/dist/usecase/mcp-media.js +17 -0
  152. package/dist/usecase/mcp.js +100 -0
  153. package/dist/usecase/paging.js +71 -0
  154. package/dist/usecase/publishing.js +63 -0
  155. package/dist/usecase/refusals.js +27 -0
  156. package/dist/usecase/rest.js +54 -0
  157. package/dist/usecase/search.js +7 -0
  158. package/dist/usecase/site.js +39 -0
  159. package/dist/usecase/stores.js +64 -0
  160. package/dist/usecase/tool-version.js +9 -0
  161. package/dist/usecase/translations.js +78 -0
  162. package/dist/usecase/ttl.js +20 -0
  163. package/dist/usecase/validation.js +241 -0
  164. package/dist/usecase/web.js +23 -0
  165. package/docs/examples/README.md +39 -18
  166. package/docs/examples/app-shell/.dev.vars.example +4 -0
  167. package/docs/examples/app-shell/README.md +121 -0
  168. package/docs/examples/app-shell/accept.mjs +91 -0
  169. package/docs/examples/app-shell/gitignore +7 -0
  170. package/docs/examples/app-shell/package.json +43 -0
  171. package/docs/examples/app-shell/pnpm-workspace.yaml +8 -0
  172. package/docs/examples/app-shell/smoke-kit.mjs +168 -0
  173. package/docs/examples/app-shell/src/auth.ts +58 -0
  174. package/docs/examples/app-shell/src/handlers.ts +5 -0
  175. package/docs/examples/app-shell/src/index.ts +4 -0
  176. package/docs/examples/app-shell/src/routes.ts +14 -0
  177. package/docs/examples/app-shell/src/schedule.ts +8 -0
  178. package/docs/examples/app-shell/src/service.ts +70 -0
  179. package/docs/examples/app-shell/src/sign-in.ts +41 -0
  180. package/docs/examples/app-shell/src/signup.ts +8 -0
  181. package/docs/examples/app-shell/tsconfig.json +26 -0
  182. package/docs/examples/app-shell/worker-configuration.d.ts +22 -0
  183. package/docs/examples/app-shell/wrangler.jsonc +62 -0
  184. package/docs/examples/club-events/README.md +65 -0
  185. package/docs/examples/club-events/manifests/club-events.yaml +247 -0
  186. package/docs/examples/club-events/smoke.mjs +127 -0
  187. package/docs/examples/club-events/src/handlers.ts +25 -0
  188. package/docs/examples/club-events/src/signup.ts +9 -0
  189. package/docs/examples/generated-web/README.md +78 -0
  190. package/docs/examples/generated-web/posts.yaml +38 -0
  191. package/docs/examples/generated-web/seed.sql +6 -0
  192. package/docs/examples/generated-web/src/worker.ts +43 -0
  193. package/docs/examples/generated-web/tsconfig.json +8 -0
  194. package/docs/examples/generated-web/wrangler.jsonc +8 -0
  195. package/docs/examples/purchasing/.dev.vars.example +6 -0
  196. package/docs/examples/purchasing/README.md +99 -0
  197. package/docs/examples/purchasing/manifests/purchasing.yaml +374 -0
  198. package/docs/examples/purchasing/seed.sql +4 -0
  199. package/docs/examples/purchasing/smoke.mjs +191 -0
  200. package/docs/examples/purchasing/src/admin.tsx +13 -0
  201. package/docs/examples/purchasing/src/handlers.ts +33 -0
  202. package/docs/examples/purchasing/src/notify.ts +29 -0
  203. package/docs/examples/purchasing/src/requests-page.ts +57 -0
  204. package/docs/examples/purchasing/src/routes.ts +53 -0
  205. package/docs/examples/purchasing/src/schedule.ts +31 -0
  206. package/docs/examples/purchasing/src/signup.ts +5 -0
  207. package/docs/examples/purchasing/src/supplier.ts +32 -0
  208. package/docs/examples/purchasing/wrangler.jsonc +69 -0
  209. package/docs/examples/recipes/README.md +60 -0
  210. package/docs/examples/recipes/manifests/recipes.yaml +75 -0
  211. package/docs/examples/recipes/smoke.mjs +102 -0
  212. package/docs/examples/recipes/src/media.ts +13 -0
  213. package/docs/examples/recipes/src/pages.ts +99 -0
  214. package/docs/examples/recipes/src/routes.ts +71 -0
  215. package/docs/examples/recipes/src/site-config.ts +12 -0
  216. package/docs/examples/recipes/wrangler.jsonc +74 -0
  217. package/docs/handbook/cloudflare/authentication.md +180 -322
  218. package/docs/handbook/cloudflare/chatgpt-sites.md +5 -23
  219. package/docs/handbook/cloudflare/deploy-and-operate.md +55 -77
  220. package/docs/handbook/cloudflare/media-r2.md +61 -205
  221. package/docs/handbook/cloudflare/service-entry.md +239 -0
  222. package/docs/handbook/concepts/authorization.md +10 -107
  223. package/docs/handbook/concepts/four-atoms.md +9 -168
  224. package/docs/handbook/concepts/lifecycle-and-locales.md +9 -132
  225. package/docs/handbook/concepts/mcp-and-agents.md +24 -121
  226. package/docs/handbook/concepts/procedures-and-triggers.md +5 -217
  227. package/docs/handbook/concepts/runtime-and-adapters.md +15 -169
  228. package/docs/handbook/concepts/views.md +10 -185
  229. package/docs/handbook/examples/hub.md +3 -8
  230. package/docs/handbook/guides/adapter-guide.md +11 -0
  231. package/docs/handbook/guides/admin-ui.md +115 -133
  232. package/docs/handbook/guides/agent-setup.md +64 -74
  233. package/docs/handbook/guides/agent-workflow-acceptance.md +232 -0
  234. package/docs/handbook/guides/email.md +86 -0
  235. package/docs/handbook/guides/local-testing.md +40 -0
  236. package/docs/handbook/guides/mcp-apps.md +560 -0
  237. package/docs/handbook/guides/native-admin.md +566 -0
  238. package/docs/handbook/guides/native-execution.md +133 -0
  239. package/docs/handbook/guides/oauth.md +219 -0
  240. package/docs/handbook/guides/postgres-adapters.md +56 -0
  241. package/docs/handbook/guides/public-pages.md +87 -0
  242. package/docs/handbook/guides/size-and-startup.md +9 -0
  243. package/docs/handbook/guides/sql-patterns.md +266 -0
  244. package/docs/handbook/guides/typed-queries.md +211 -107
  245. package/docs/handbook/navigation.json +176 -55
  246. package/docs/handbook/reference/admin-descriptors.md +53 -0
  247. package/docs/handbook/reference/authorization.md +39 -266
  248. package/docs/handbook/reference/describe.md +56 -0
  249. package/docs/handbook/reference/diagnostics.md +19 -186
  250. package/docs/handbook/reference/features.md +45 -55
  251. package/docs/handbook/reference/manifest.md +6 -129
  252. package/docs/handbook/reference/procedure.md +11 -303
  253. package/docs/handbook/reference/schema.md +64 -240
  254. package/docs/handbook/reference/site-config.md +5 -218
  255. package/docs/handbook/reference/sql.md +64 -0
  256. package/docs/handbook/reference/surface.md +392 -201
  257. package/docs/handbook/reference/trigger.md +56 -192
  258. package/docs/handbook/reference/view.md +24 -243
  259. package/docs/handbook/releases/index.md +244 -77
  260. package/docs/handbook/start/agents.md +45 -0
  261. package/docs/handbook/start/overview.md +9 -48
  262. package/docs/handbook/start/project-and-cli.md +51 -104
  263. package/docs/handbook/start/quickstart-worker.md +43 -178
  264. package/docs/skills/develop/SKILL.md +79 -249
  265. package/docs/skills/integrate/SKILL.md +39 -50
  266. package/docs/skills/media-gc/SKILL.md +34 -10
  267. package/docs/skills/provision/SKILL.md +36 -186
  268. package/docs/skills/update/SKILL.md +23 -38
  269. package/docs/source/scripts/postgres-adapter.ts +101 -0
  270. package/docs/upgrade-0.1-to-0.2.md +227 -0
  271. package/package.json +120 -131
  272. package/skills/README.md +48 -108
  273. package/skills/mantle/SKILL.md +76 -0
  274. package/dist/admin-ui.d.ts +0 -2
  275. package/dist/admin-ui.d.ts.map +0 -1
  276. package/dist/admin-ui.js +0 -2
  277. package/dist/admin-ui.js.map +0 -1
  278. package/dist/admin.d.ts +0 -2
  279. package/dist/admin.d.ts.map +0 -1
  280. package/dist/admin.js +0 -2
  281. package/dist/admin.js.map +0 -1
  282. package/dist/auth.d.ts +0 -2
  283. package/dist/auth.d.ts.map +0 -1
  284. package/dist/auth.js +0 -2
  285. package/dist/auth.js.map +0 -1
  286. package/dist/bun.d.ts +0 -2
  287. package/dist/bun.d.ts.map +0 -1
  288. package/dist/bun.js +0 -2
  289. package/dist/bun.js.map +0 -1
  290. package/dist/cli/generate-cloudflare.d.ts +0 -7
  291. package/dist/cli/generate-cloudflare.d.ts.map +0 -1
  292. package/dist/cli/generate-cloudflare.js +0 -123
  293. package/dist/cli/generate-cloudflare.js.map +0 -1
  294. package/dist/cli/generate-project.d.ts +0 -25
  295. package/dist/cli/generate-project.d.ts.map +0 -1
  296. package/dist/cli/generate-project.js +0 -282
  297. package/dist/cli/generate-project.js.map +0 -1
  298. package/dist/cli/generate-sites.d.ts +0 -8
  299. package/dist/cli/generate-sites.d.ts.map +0 -1
  300. package/dist/cli/generate-sites.js +0 -273
  301. package/dist/cli/generate-sites.js.map +0 -1
  302. package/dist/cli/generate.d.ts +0 -20
  303. package/dist/cli/generate.d.ts.map +0 -1
  304. package/dist/cli/generate.js +0 -299
  305. package/dist/cli/generate.js.map +0 -1
  306. package/dist/cli/harness.d.ts +0 -3
  307. package/dist/cli/harness.d.ts.map +0 -1
  308. package/dist/cli/harness.js +0 -214
  309. package/dist/cli/harness.js.map +0 -1
  310. package/dist/cli/main.d.ts +0 -3
  311. package/dist/cli/main.d.ts.map +0 -1
  312. package/dist/cli/main.js +0 -77
  313. package/dist/cli/main.js.map +0 -1
  314. package/dist/cli/skills.d.ts +0 -3
  315. package/dist/cli/skills.d.ts.map +0 -1
  316. package/dist/cli/skills.js +0 -128
  317. package/dist/cli/skills.js.map +0 -1
  318. package/dist/cloudflare.d.ts +0 -2
  319. package/dist/cloudflare.d.ts.map +0 -1
  320. package/dist/cloudflare.js +0 -2
  321. package/dist/cloudflare.js.map +0 -1
  322. package/dist/codegen/emitMantleModule.d.ts +0 -22
  323. package/dist/codegen/emitMantleModule.d.ts.map +0 -1
  324. package/dist/codegen/emitMantleModule.js +0 -291
  325. package/dist/codegen/emitMantleModule.js.map +0 -1
  326. package/dist/codegen.d.ts +0 -2
  327. package/dist/codegen.d.ts.map +0 -1
  328. package/dist/codegen.js +0 -2
  329. package/dist/codegen.js.map +0 -1
  330. package/dist/runtime-testing.d.ts +0 -2
  331. package/dist/runtime-testing.d.ts.map +0 -1
  332. package/dist/runtime-testing.js +0 -2
  333. package/dist/runtime-testing.js.map +0 -1
  334. package/dist/runtime.d.ts +0 -2
  335. package/dist/runtime.d.ts.map +0 -1
  336. package/dist/runtime.js +0 -2
  337. package/dist/runtime.js.map +0 -1
  338. package/dist/spec.d.ts +0 -2
  339. package/dist/spec.d.ts.map +0 -1
  340. package/dist/spec.js +0 -2
  341. package/dist/spec.js.map +0 -1
  342. package/dist/vercel-libsql.d.ts +0 -2
  343. package/dist/vercel-libsql.d.ts.map +0 -1
  344. package/dist/vercel-libsql.js +0 -2
  345. package/dist/vercel-libsql.js.map +0 -1
  346. package/dist/vercel.d.ts +0 -2
  347. package/dist/vercel.d.ts.map +0 -1
  348. package/dist/vercel.js +0 -2
  349. package/dist/vercel.js.map +0 -1
  350. package/dist/web.d.ts +0 -2
  351. package/dist/web.d.ts.map +0 -1
  352. package/dist/web.js +0 -2
  353. package/dist/web.js.map +0 -1
  354. package/docs/adapter-guide.md +0 -222
  355. package/docs/adr/0001-four-atom-manifest-model.md +0 -440
  356. package/docs/adr/0002-closed-enums-for-bindings.md +0 -253
  357. package/docs/adr/0007-ai-as-primary-author.md +0 -473
  358. package/docs/adr/0008-structured-diagnostic-shape.md +0 -338
  359. package/docs/adr/0009-consumer-supplied-manifests.md +0 -149
  360. package/docs/adr/0010-locale-and-translates.md +0 -453
  361. package/docs/adr/0011-adapter-port-spec.md +0 -281
  362. package/docs/adr/0012-views-as-public-rest.md +0 -161
  363. package/docs/adr/0013-agent-provisioned-consumer-projects.md +0 -154
  364. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +0 -900
  365. package/docs/adr/0016-site-semantic-layer.md +0 -70
  366. package/docs/adr/0017-media-multi-variant-agent-side-optimization.md +0 -181
  367. package/docs/adr/0018-core-starters-repository-boundary.md +0 -179
  368. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +0 -240
  369. package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +0 -199
  370. package/docs/adr/0021-retire-starter-scaffolding.md +0 -30
  371. package/docs/adr/0022-caller-observed-version-occ.md +0 -212
  372. package/docs/adr/0023-port-failure-contract.md +0 -69
  373. package/docs/adr/0024-manifest-native-schema-tables.md +0 -264
  374. package/docs/adr/0025-reserved-native-columns-and-published-only-public-views.md +0 -105
  375. package/docs/adr/0026-progressive-generate.md +0 -36
  376. package/docs/adr/0027-scheduled-procedure-triggers.md +0 -31
  377. package/docs/adr/0028-schema-ttl-expiry.md +0 -33
  378. package/docs/adr/README.md +0 -81
  379. package/docs/adr/adr-lite-803-request-diagnostics.md +0 -38
  380. package/docs/adr/adr-lite-808-route-readiness.md +0 -47
  381. package/docs/adr/adr-lite-809-bounded-public-content.md +0 -71
  382. package/docs/adr/adr-lite-812-native-parity.md +0 -150
  383. package/docs/adr/adr-lite-823-home-statistics.md +0 -64
  384. package/docs/adr/adr-lite-842-mcp-authoring.md +0 -30
  385. package/docs/adr/adr-lite-845-frontend-client.md +0 -38
  386. package/docs/adr/adr-lite-861-admin-webmcp.md +0 -42
  387. package/docs/adr/adr-lite-909-admin-ui-kit.md +0 -37
  388. package/docs/agent-prompts.md +0 -64
  389. package/docs/api-mcp-authorization.md +0 -5
  390. package/docs/assets/mantle-admin-developer.png +0 -0
  391. package/docs/assets/mantle-admin-operations.png +0 -0
  392. package/docs/assets/mantle-hero.jpg +0 -0
  393. package/docs/auth-hosting-model.md +0 -166
  394. package/docs/cloudflare-low-level-composition.md +0 -5
  395. package/docs/consumer-onboarding-audit.md +0 -65
  396. package/docs/deferred-lifecycle-queues.md +0 -5
  397. package/docs/design-atoms.md +0 -16
  398. package/docs/design-references/editorial-blog-2026-05-05.md +0 -218
  399. package/docs/direct-authoring.md +0 -5
  400. package/docs/examples/builtin-commerce.md +0 -269
  401. package/docs/examples/builtin-intake.md +0 -143
  402. package/docs/examples/builtin-legal-documents.md +0 -189
  403. package/docs/examples/builtin-procurement.md +0 -241
  404. package/docs/examples/builtin-publication.md +0 -241
  405. package/docs/examples/builtin-reservation.md +0 -149
  406. package/docs/examples/cf-primitives-commerce-inventory.md +0 -809
  407. package/docs/examples/cf-primitives-guarded-api.md +0 -429
  408. package/docs/examples/cf-primitives-intake-hooks.md +0 -319
  409. package/docs/examples/host-chatgpt-sites/.openai/hosting.json +0 -1
  410. package/docs/examples/host-chatgpt-sites/README.md +0 -70
  411. package/docs/examples/host-chatgpt-sites/drizzle/0000_sites_users.sql +0 -8
  412. package/docs/examples/host-chatgpt-sites/drizzle/0001_mantle.sql +0 -294
  413. package/docs/examples/host-chatgpt-sites/drizzle/0002_article_cover.sql +0 -5
  414. package/docs/examples/host-chatgpt-sites/drizzle/0003_store_instance_id.sql +0 -1
  415. package/docs/examples/host-chatgpt-sites/drizzle/0004_mantle.sql +0 -10
  416. package/docs/examples/host-chatgpt-sites/drizzle/0005_mantle.sql +0 -24
  417. package/docs/examples/host-chatgpt-sites/drizzle/meta/_journal.json +0 -1
  418. package/docs/examples/host-chatgpt-sites/drizzle/meta/mantle-state.json +0 -60
  419. package/docs/examples/host-chatgpt-sites/manifests/site.yaml +0 -27
  420. package/docs/examples/host-chatgpt-sites/package.json +0 -1
  421. package/docs/examples/host-chatgpt-sites/public/site.css +0 -1
  422. package/docs/examples/host-chatgpt-sites/scripts/build.mjs +0 -13
  423. package/docs/examples/host-chatgpt-sites/scripts/check.mjs +0 -101
  424. package/docs/examples/host-chatgpt-sites/scripts/migration.mjs +0 -43
  425. package/docs/examples/host-chatgpt-sites/src/chatgpt-auth.ts +0 -66
  426. package/docs/examples/host-chatgpt-sites/src/index.ts +0 -46
  427. package/docs/examples/host-chatgpt-sites/src/mcp.ts +0 -49
  428. package/docs/examples/host-chatgpt-sites/src/media.ts +0 -109
  429. package/docs/examples/host-chatgpt-sites/src/r2-lab.ts +0 -38
  430. package/docs/examples/host-chatgpt-sites/src/storage-fingerprint.json +0 -1
  431. package/docs/examples/host-chatgpt-sites/src/web.ts +0 -35
  432. package/docs/examples/host-chatgpt-sites/tsconfig.json +0 -1
  433. package/docs/examples/host-chatgpt-sites/wrangler.jsonc +0 -10
  434. package/docs/examples/host-local-admin-otp/.dev.vars.example +0 -3
  435. package/docs/examples/host-local-admin-otp/README.md +0 -80
  436. package/docs/examples/host-local-admin-otp/ensure-dev-vars.mjs +0 -5
  437. package/docs/examples/host-local-admin-otp/manifests/site.yaml +0 -25
  438. package/docs/examples/host-local-admin-otp/package.json +0 -29
  439. package/docs/examples/host-local-admin-otp/public/.gitkeep +0 -1
  440. package/docs/examples/host-local-admin-otp/smoke.mjs +0 -141
  441. package/docs/examples/host-local-admin-otp/src/index.ts +0 -54
  442. package/docs/examples/host-local-admin-otp/tsconfig.json +0 -17
  443. package/docs/examples/host-local-admin-otp/wrangler.jsonc +0 -23
  444. package/docs/examples/host-minimal-worker/README.md +0 -41
  445. package/docs/examples/host-minimal-worker/manifests/site.yaml +0 -25
  446. package/docs/examples/host-minimal-worker/package.json +0 -26
  447. package/docs/examples/host-minimal-worker/smoke.mjs +0 -23
  448. package/docs/examples/host-minimal-worker/src/index.ts +0 -4
  449. package/docs/examples/host-minimal-worker/tsconfig.json +0 -17
  450. package/docs/examples/host-minimal-worker/wrangler.jsonc +0 -22
  451. package/docs/handbook/chatgpt-sites/equipment-checkout.md +0 -231
  452. package/docs/handbook/chatgpt-sites/generated-app.md +0 -84
  453. package/docs/handbook/chatgpt-sites/host-reference.md +0 -113
  454. package/docs/handbook/chatgpt-sites/index.md +0 -133
  455. package/docs/handbook/cloudflare/bindings.md +0 -291
  456. package/docs/handbook/cloudflare/conventional-worker.md +0 -166
  457. package/docs/handbook/cloudflare/deferred-hooks-queues.md +0 -170
  458. package/docs/handbook/cloudflare/low-level-composition.md +0 -169
  459. package/docs/handbook/cloudflare/public-web.md +0 -144
  460. package/docs/handbook/cloudflare/site-chrome.md +0 -75
  461. package/docs/handbook/examples/commerce-transaction.md +0 -11
  462. package/docs/handbook/examples/commerce.md +0 -11
  463. package/docs/handbook/examples/guarded-api.md +0 -10
  464. package/docs/handbook/examples/intake-form.md +0 -12
  465. package/docs/handbook/examples/intake-hooks.md +0 -11
  466. package/docs/handbook/examples/legal-documents.md +0 -10
  467. package/docs/handbook/examples/procurement-approvals.md +0 -10
  468. package/docs/handbook/examples/publication.md +0 -10
  469. package/docs/handbook/examples/reservation.md +0 -10
  470. package/docs/handbook/start/quickstart-admin.md +0 -52
  471. package/docs/labels.md +0 -71
  472. package/docs/media-uploads.md +0 -5
  473. package/docs/migration-0.1.2.md +0 -13
  474. package/docs/performance-harness.md +0 -228
  475. package/docs/release-process.md +0 -244
  476. package/docs/schema-indexes.md +0 -5
  477. package/docs/sealed-pipeline-ownership.md +0 -101
  478. package/docs/skills/plugin/SKILL.md +0 -132
  479. package/docs/skills/theme/SKILL.md +0 -77
  480. package/docs/spec-only-host-adoption.md +0 -157
  481. package/docs/transaction-patterns.md +0 -5
  482. package/skills/develop/SKILL.md +0 -258
  483. package/skills/install/SKILL.md +0 -112
  484. package/skills/integrate/SKILL.md +0 -60
  485. package/skills/media-gc/SKILL.md +0 -85
  486. package/skills/plugin/SKILL.md +0 -132
  487. package/skills/provision/SKILL.md +0 -194
  488. package/skills/theme/SKILL.md +0 -77
  489. package/skills/update/SKILL.md +0 -51
  490. package/templates/chatgpt-sites/auth.ts +0 -66
  491. package/templates/chatgpt-sites/build.mjs +0 -14
  492. package/templates/chatgpt-sites/env.ts +0 -1
  493. package/templates/chatgpt-sites/mcp.ts +0 -48
  494. package/templates/chatgpt-sites/public-mcp.ts +0 -29
  495. package/templates/chatgpt-sites/smoke-local.mjs +0 -20
@@ -1,205 +1,396 @@
1
- ---
2
- description: Every surface Mantle exposes — Worker HTTP routes and their cache class, the MCP tool catalog, CLI flags, the generated module, packages and versions.
3
- ---
4
- # HTTP, MCP, CLI and package surface
5
-
6
- What a Mantle deployment exposes, in one place: the routes the conventional Cloudflare Worker owns, the MCP tools it advertises, the commands the package installs, the module `mantle generate` writes, and which package holds what. Route behaviour follows [Conventional Worker](../cloudflare/conventional-worker.md); this page is the index.
7
-
8
- ## HTTP routes
9
-
10
- Every table below describes the conventional Cloudflare Worker assembled by `createMantleWorker`. The **cache** column is the class the final `applyCachePolicy` boundary assigns. Only an anonymous `200` `GET`/`HEAD` response that explicitly declares `public` plus a shared freshness lifetime stays cacheable, and it then varies on `Cookie` and `Authorization`; the request must carry neither header and the response must set no cookie. Everything else becomes `private, no-store` with any CDN cache override stripped.
11
-
12
- ### Manifest routes
13
-
14
- | Route | Response | Cache |
15
- |---|---|---|
16
- | `GET /api/views` | `{ ok: true, data: [ { name, title?, description, inputSchema } ] }` — one descriptor per public View. | `private, no-store` |
17
- | `GET /api/views/<name>?page=&show=` | `{ ok: true, data: { rows, page, show, hasMore } }`. One route per View declaring `surface: public`. Query values are coerced against `params`; a bad value is `400`. | `private, no-store`, or the View's `cache.sharedMaxAge` for an anonymous eligible response |
18
- | `<METHOD> <path>` | Every manifest HTTP Trigger, at its declared `POST`, `PUT`, `PATCH` or `DELETE` and path under `/api/`. The JSON body must be an object. Success is `{ ok: true, data: … }`. | `private, no-store` |
19
-
20
- Staff Views are not mounted here; they live under `/admin/api/views/<name>`.
21
-
22
- ### Admin
23
-
24
- All `/admin/api/*` routes require a staff session, carry a 1 MiB JSON body limit, and are `private, no-store`.
25
-
26
- | Route | Response |
27
- |---|---|
28
- | `GET /admin`, `/admin/`, `/admin/sign-in`, `/admin/c/:collection`, `/admin/c/:collection/:id`, `/admin/media`, `/admin/preferences`, `/admin/connected-apps`, `/admin/settings`, `/admin/staff`, `/admin/members`, `/admin/ops`, `/admin/dev`, `/admin/dev/model`, `/admin/dev/logic`, `/admin/dev/docs`, `/admin/views/:name` | The Admin SPA shell, read from `/_mantle/admin/index.html` through the assets binding. `503` with an explanatory body when the bundle is missing. |
29
- | `GET /admin/api/views/<name>` | Staff execution of any declared View, public or staff, with Admin search and filtering applied before pagination. |
30
- | `GET /admin/api/views/<name>/export` | The same query as CSV, covering every matching row rather than one page. |
31
- | `GET /admin/api/views-manifest` | `{ views: … }` — the View manifest projection the SPA renders from. |
32
- | `GET /admin/api/operations` | `{ operations: [ { name, title, description, input, uiSchema, triggers, rowBindings } ] }`, filtered per caller by re-evaluating each Procedure's `requires.auth.all`. |
33
- | `POST /admin/api/operations/:name` | Invokes a staff-operable Procedure through the same use case the staff MCP surface uses. `404` when the name is not staff-operable or when the caller's `requires.auth.all` predicates exclude it — the same filter the listing applies, so names cannot be probed. |
34
- | `GET /admin/api/me`, `/collections`, `/collections/:name/statistics`, `/entries`, `/entries/export`, `/entries/:id`, `/site` | Session, catalog and entry reads. Entry detail requires `?collection=<schema>`. |
35
- | `POST /admin/api/entries`, `PATCH /admin/api/entries/:id` | Create and edit. Entry mutation routes require `?collection=<schema>`; contributors are limited to drafts on publishing Schemas. |
36
- | `POST /admin/api/entries/:id/publish`, `/unpublish`, `DELETE /admin/api/entries/:id` | Lifecycle. Requires `?collection=<schema>` and editor or above. |
37
- | `POST /admin/api/media/uploads`, `POST /admin/api/media/uploads/:uploadGroupId/commit`, `GET /admin/api/media`, `GET`, `PATCH` and `DELETE /admin/api/media/:id` | Media lifecycle. Editor or above. |
38
- | `GET /admin/api/staff`, `PATCH /admin/api/staff/:id/role`, `POST /admin/api/staff/invitations`, `DELETE /admin/api/staff/invitations/:id`, `GET /admin/api/developer-console`, `GET` and `PATCH /admin/api/site-settings` | Owner only. |
39
- | `GET /admin/api/members` | Editor or above. |
40
-
41
- `/_mantle/*` holds the static Admin bundle. It is served by the Worker's static-assets layer from `public/_mantle/admin/`, which `mantle generate` syncs when `@aotter/mantle-admin-ui` is installed. The Worker registers no route there; the prefix is reserved so extensions cannot claim it.
42
-
43
- ### Auth, OAuth and MCP
44
-
45
- | Route | Response | Cache |
46
- |---|---|---|
47
- | `GET /api/auth/methods` | `{ methods }` — the registered sign-in method kinds, no secrets. Explicitly `no-store` so a method change cannot be served stale. | `private, no-store` |
48
- | `ALL /api/auth/*` | Better Auth: sign-in, callback, session, magic link, OTP. `/api/auth` is the default base path. | `private, no-store` |
49
- | `ALL /api/auth/oauth2/*` | The site's OAuth provider endpoints, including consent. | `private, no-store` |
50
- | `ALL /.well-known/oauth-authorization-server/*`, `/.well-known/oauth-protected-resource`, `/.well-known/oauth-protected-resource/*` | RFC 8414 and RFC 9728 discovery metadata, registered explicitly so no catch-all can swallow them. `/.well-known/oauth-protected-resource/mcp` is the resource metadata the MCP `WWW-Authenticate` challenge points at. | `private, no-store` |
51
- | `ALL /oauth/*` | Consent and connected-apps pages: `/oauth/consent`, `/oauth/consent/data`, `/oauth/consents`, `/oauth/consents/data`, `/oauth/consents/revoke`. | `private, no-store` |
52
- | `ALL /mcp` | Public MCP surface. JSON-RPC. | `private, no-store` |
53
- | `ALL /mcp/staff` | Staff MCP surface. Rejects a verified caller with no staff row using `403` and `insufficient_scope`. | `private, no-store` |
54
-
55
- Both MCP surfaces resolve the caller exactly as the HTTP routes do — consumer credential, then an OAuth access token against one canonical resource, `${PUBLIC_ORIGIN}/mcp`, and one scope, `mcp`, then a same-origin cookie session, then anonymous. The surface adds one rule: `/mcp/staff` requires a staff caller. An invalid token, or an anonymous caller on the staff surface, is `401` with a `Bearer` challenge naming the resource metadata URL; DPoP failures answer with a `DPoP` challenge; on `/mcp` an anonymous `tools/call` whose target requires identity answers the same `401` challenge. Misconfigured or partial auth environment variables keep public routes serving and return `503 setup_incomplete` from every Auth-owned route above — see [Authentication](../cloudflare/authentication.md).
56
-
57
- ### Public pages
58
-
59
- These are opt-in and application-declared. Public rendering needs three matching inputs: a `mountPublicRoutes(...)` call declaring the collection routes, a `TemplateRegistry` passed as `templates`, and a `publicPathResolver`. A headless deployment declares none of them and serves none of these routes; mounting every Schema automatically is deliberately not offered, because a collection can hold a slug and still be private. Bodies render from canonical database state.
60
-
61
- | Route | Response | Cache |
62
- |---|---|---|
63
- | `GET /` | `302` to `/{canonical locale}`. | `private, no-store` |
64
- | `GET /:locale` | Composed home page. | `public, max-age=0, s-maxage=300` + a deployment-scoped cache tag |
65
- | `GET /:locale.md` | Markdown mirror of the home page. | public |
66
- | `GET /:locale/:segment` | Collection list page. Opt-in per collection route. Paginates by `?cursor=` and adds `Link: <…>; rel="next"` plus an in-page next link. | public |
67
- | `GET /:locale/:segment.md` | Markdown mirror of the list. | public |
68
- | `GET /:locale/:segment/:slug` | Entry page. | public |
69
- | `GET /:locale/:segment/:slug.md` | Markdown mirror of one entry. Registered before the bare slug route, and the slug group stays single-segment. | public |
70
- | `GET /:locale/:segment/:slug?preview=1` | Live render of unpublished content. Gated on a staff session: `401` with no session, `403` for a non-staff user. | `private, no-store` |
71
- | `GET /llms.txt` | Composed agent index across every locale, with `?cursor=` continuation and `Link: rel="next"`. | public |
72
- | `GET /:locale/llms.txt` | The same, scoped to one locale. | public |
73
- | `GET /sitemap.xml` | Sitemap. `?part=1` returns a single part; otherwise a sitemap index is returned whenever a continuation exists, and `?cursor=` fetches the next part. Part size is `min(2000, floor(40000 / locale count))`, at least 1. | public |
74
- | `GET /robots.txt` | `User-agent: *`, `Allow: /`, and a `Sitemap:` pointer built from `siteDefaults.origin`. | public |
75
- | `GET /favicon.ico` | A convention, not a reserved path: an existing host route wins. Picks the first themeless PNG icon, else the first themeless icon, else the first icon; serves it from the assets binding when it resolves to `/favicon.ico` on this origin, and otherwise redirects to the icon `src`. | `private, no-store` |
76
-
77
- The first three rows register only when a home renderer is supplied; without one, `/` and `/{locale}` are not mounted at all. Unmatched paths fall through to the supplied not-found renderer. Setting `liveDev` switches entry and list HTML to `private, no-store`. `cacheScope` isolates each deployment's tag and optional KV key; without it the facade disables shared caching. Publishing-content and site-setting writes purge that scoped tag. Purge is best effort after the canonical write, and Cloudflare KV remains eventually consistent. Immutable assets and operational records stay outside that boundary. More in [Public web](../cloudflare/public-web.md).
78
-
79
- ### Reserved paths
80
-
81
- Extensions may add routes but may not replace Core surfaces. These are reserved:
82
-
83
- - `/admin` and `/admin/*`
84
- - `/_mantle` and `/_mantle/*`
85
- - `/api/auth` and `/api/auth/*`
86
- - `/api/views` and `/api/views/*`
87
- - `/oauth` and `/oauth/*`
88
- - `/mcp` and `/mcp/*`
89
- - `/.well-known/oauth*`
90
- - the global `*` and `/*` registrations
91
-
92
- A custom Auth factory's `basePath` and every manifest-owned `(method, path)` pair are reserved as well. Static literal conflicts fail the consumer's TypeScript build; computed paths cannot be proven statically, so the facade inspects the assembled route table and fails closed before serving. A manifest HTTP Trigger under one of these prefixes is `TRIGGER_PATH_INVALID` at boot.
93
-
94
- ## MCP tools
95
-
96
- Tool names are the mangled `metadata.name`: lower-cased, with `-` replaced by `_`. Discovery is filtered per caller, but discovery is never the enforcement boundary — every `tools/call` re-evaluates the target's `requires.auth.all` and its guard.
97
-
98
- | Tool | Surface | Registered when |
99
- |---|---|---|
100
- | `query_view_<segment>` | The View's own `surface` | One per public or staff View; internal Views have no tool. `annotations.readOnlyHint` is `true`; the input schema is the View's `params.properties` plus `page` and `show`. |
101
- | `<procedure_segment>` | The MCP Trigger's `surface` | One per `mcp` Trigger. `annotations` carry what Core can prove (`readOnlyHint: false` for every builtin handler, `destructiveHint: true` for `op: delete`, `idempotentHint: true` when an input carries `x-mcp-hint: idempotency-key`) plus whatever the Procedure declares under `spec.mcp`; `ref` handlers get nothing inferred beyond the idempotency key. Generic authoring, lifecycle and media tools are `readOnlyHint: false`; `delete_entry` is also `destructiveHint: true`. |
102
- | `<procedure segment>` | The Trigger's `surface` | One per `Trigger.source.kind: mcp`. A Procedure with no MCP Trigger is not exposed. |
103
- | `request_publish` | staff | Always. Rejected at call time for an operational Schema. |
104
- | `unpublish_entry` | staff | Always. Same restriction. |
105
- | `archive_entry` | staff | Always. Same restriction. |
106
- | `delete_entry` | staff | Always. |
107
- | `create_draft_<segment>`, `update_draft_<segment>` | staff | Per publishing Schema whose `schema.readOnly` is not `true`. |
108
- | `create_record_<segment>`, `update_record_<segment>` | staff | Per operational Schema whose `schema.readOnly` is not `true`. |
109
- | `create_media_upload`, `commit_media_upload` | staff | Only when a `mediaStorage` port is bound **and** at least one `media.purposes` entry is declared. |
110
-
111
- The public surface carries callable capabilities only — public Views and Procedures with a public MCP Trigger. No generic entry tool and no authoring tool is ever advertised there. Update tools add `id` and `expected_version` as required fields, and `x-mantle-bind` properties are stripped from authoring tool schemas because the server stamps them. Localized `title` and `description` collapse to their `en` value in tool schemas. Concepts are in [MCP and agents](../concepts/mcp-and-agents.md).
112
-
113
- ## CLI
114
-
115
- The `@aotter/mantle` package installs two binaries, `mantle` and `mantle-harness`. Exit codes are `0` for success, `1` for a diagnostic failure and `2` for an invocation problem.
116
-
117
- | Command | Flags |
118
- |---|---|
119
- | `mantle generate` | `--manifests <dir>` (default `./manifests`), `-o, --output <dir>` (default `.mantle/generated`), `--namespace <name>` (default `Mantle`), `--host cf\|chatgpt-sites`, `--features <list>`, `--review-unique-indexes` (managed Sites only), `--check`, `-h, --help` |
120
- | `mantle skills` | `--check`, `-h, --help` |
121
- | `mantle validate` | `--manifests <dir>` (default `./manifests`), `--source <dir>` (default `./src`), `--no-source`, `--phase preview\|deploy` (default `preview`), `--format json\|text` (default by TTY), `--json`, `-h, --help` |
122
- | `mantle emit-openapi` | `--manifests <dir>`, `--title <str>` (default `mantle`), `--version <str>` (default `0.1.0`), `--session-cookie-name <str>`, `-o, --output <file>`, `-h, --help` |
123
- | `mantle-harness indexes` | `--manifests <dir>`, `--rows <n>`, `--require <view>` (repeatable), `--require-public`, `--format json\|text`, `-h, --help` |
124
- | `mantle-harness http` | `--route <name=url>` (repeatable, required), `--base-url <url>`, `--rounds <n>`, `--warmup <n>`, `--format json\|text`, `-h, --help` |
125
-
126
- `generate` assembles new hosted projects with full Spec/Runtime/API/MCP/Admin/Web by default, or a smaller positive `--features` selection, then validates and compiles manifests into a typed module. Saved selections rerun safely; legacy authored projects keep compile mode. Selected Admin syncs its prebuilt SPA into `public/_mantle/admin/`. `--check` fails without writing on drift. `skills` copies project-scoped skills into `.agents/skills/mantle-*` and `.claude/skills/mantle-*`. `validate --phase deploy` adds pre-deploy checks; `emit-openapi` covers HTTP Triggers and View REST routes. `mantle-harness indexes` checks SQLite plans, while `http` samples a running Worker. [Project and CLI](../start/project-and-cli.md) has the install and ownership sequence.
127
-
128
- ## The generated module
129
-
130
- `mantle generate` writes one file, `.mantle/generated/mantle.ts`. It is generated code: do not edit it, and regenerate after any manifest change.
131
-
132
- | Export | Shape |
133
- |---|---|
134
- | `plan` | The sealed `RuntimePlan`, carrying a semantic fingerprint. |
135
- | `Mantle` (or `--namespace`) | Type namespace holding `Entry_*`, `ViewRow_*`, `ViewParams_*`, `ProcInput_*` and `ProcOutput_*` for every atom. |
136
- | `MantleViewOptions` | `{ page?, show?, ctx? }`. |
137
- | `MantleHandlers<Env>` | Typed map of every `handler.kind: ref` key the manifests declare. |
138
- | `CreateMantleOptions<Env>` | `BootMantleRuntimeArgs` without `plan` and `handlers`, plus the typed `handlers` map. |
139
- | `createMantle(options)` | Boots the runtime and returns the bound facade. Eager: it prepares once and neither caches nor retries. |
140
- | `bindMantle(runtime)` | Binds an already-booted runtime. Throws when `runtime.revision` does not equal the generated plan's fingerprint. |
141
-
142
- The bound object is deterministic lower-camel property names over the authored wire names:
1
+ # Generated surfaces
2
+
3
+ - The app mounts generated modules and passes `{ db, caller }`; `caller(request, env, { readOnly })`
4
+ is the app's own `resolveCaller(...)` call. There is no generated `service.ts`,
5
+ routing framework or Better Auth wrapper.
6
+ - REST retains public `GET /api/views/<name>` (and bodyless HEAD) with declared query input plus
7
+ `limit` and `cursor`, answering `{ rows, nextCursor? }`. HTTP Triggers retain
8
+ their own method/path; JSON bodies and path parameters form the Procedure
9
+ input and responses contain its result. Errors remain
10
+ `{ error: { code, message, path } }` with the 0.2 status. Cookie sessions cannot
11
+ mutate across origins.
12
+ - MCP retains public `POST /mcp`, admin `POST /mcp/admin`,
13
+ snake-cased View names on each declared surface, and snake-cased Procedure
14
+ names per MCP Trigger. Its SDK loads only through a request-time dynamic import.
15
+ - HTTP/MCP Triggers declare exposure only. `mantle generate` refuses schedule
16
+ and lifecycle Triggers; host scheduling is a separate capability.
17
+ - Native Admin retains selected 0.2 paths and the console contracts; see
18
+ [Native Admin](../guides/native-admin.md) for app-mounted
19
+ `adminBindings()` and `entries(bindings.schemas, bindings.adapter)`.
20
+
21
+ Generated `rest.ts`, `mcp.ts`, `web.ts`, `paged/views/*` and `validation/*` hold
22
+ only the app's tables: routes, tools, pages, operation bindings, compiled input
23
+ checks and types. Request handling (matching, body and query decoding, dispatch,
24
+ challenges, JSON-RPC, web rendering, keyset paging) lives in the package's
25
+ `rest`, `mcp`, `web`, `paging` and `validation` kernel modules, so a runtime fix
26
+ ships as a package update instead of a regeneration.
27
+
28
+ Procedure surface selection follows the [HTTP/MCP pairing default and deliberate asymmetry exception](trigger.md); no surface is implied.
29
+
30
+ The generated `validation/views/<name>.ts` and
31
+ `validation/procedures/<name>.ts` export `validate(unknown)`. View validators
32
+ also export `query(URLSearchParams)` returning `{ input, limit?, cursor? }`.
33
+ REST and MCP use the same generated checks, with no runtime schema interpreter.
34
+ Supported checks are types/unions and nullable, required/properties/additionalProperties,
35
+ arrays/items and length bounds, enum, `const`, local `$defs`/`$ref`, string length/pattern,
36
+ numeric bounds/`multipleOf`, and the formats `date`, `date-time`, `email` and `uri`. The deliberately permissive
37
+ email check requires exactly one `@`, non-empty local and domain parts, and no
38
+ whitespace; it does not require a dot, verify deliverability, or implement full
39
+ RFC mailbox syntax. `uri` means an absolute `http:` or `https:` URL, stricter than
40
+ JSON Schema's any-scheme URI, so `javascript:` and `data:` links fail. Integers must be safe; fractional
41
+ `multipleOf` uses a 1e-9 quotient tolerance. Defaults remain
42
+ annotations and are not inserted into request input.
43
+
44
+ View and Procedure input objects (not Schemas or outputs) also take three rules
45
+ between their declared properties, each failing with one `INPUT_VALIDATION_FAILED`
46
+ error whose message names the rule:
47
+
48
+ - `dependentRequired: { weight: [unit] }`: "'unit' is required when 'weight' is
49
+ present.", at the missing field.
50
+ - `anyOf` whose every branch is exactly `{ required: [...] }`: "At least one of
51
+ 'weight', 'heightCm' is required.", at the object.
52
+ - `if: { properties: { undo: { const: true } }, required: [undo] }` with `then`
53
+ and/or `else` each exactly `{ required: [...] }`: "'item' is required unless
54
+ 'undo' is true.", at the missing field. `if` must list its field in `required`:
55
+ otherwise an absent field would satisfy it.
56
+
57
+ Any other `anyOf`/`if`/`then`/`else`, `oneOf`, `uniqueItems`, object property-count
58
+ bounds and other keywords and formats are refused at generation. The rules only
59
+ validate: types, SQL binds and Admin forms keep the declared fields, and Admin
60
+ shows the server's error at its field. `describe` keeps them as written; MCP and
61
+ WebMCP tools advertise the schema without them and append each rule's sentence to
62
+ the tool description, since some model APIs refuse these keywords in a tool's
63
+ input schema. [Trigger](trigger.md#input-rules) has an example.
64
+
65
+ Native `paged/views/<name>.ts` has a default export, callable as
66
+ `page(db, caller, input, { limit?, cursor? })` after importing it as `page`,
67
+ answering `{ rows, nextCursor? }`.
68
+ Bare `views/<name>.ts` retains native row arrays and accepts app-validated input;
69
+ selecting neither boundary adds no validation or paging work to that query.
70
+ Selected REST/MCP/Admin responses convert declared integer columns on every target
71
+ to safe numbers or exact decimal strings. Their SQL reads integers as text before
72
+ driver conversion; integer cursor keys retain that precision without changing native
73
+ sort order. `views/<name>.ts` also exports `WireRow` for selected transport
74
+ boundaries; declared wire fields agree across targets. Declared SQLite/D1
75
+ JSON/boolean columns become JSON values and booleans. Aliases follow generated
76
+ source-column lineage; computed values and Procedure/ref results retain
77
+ their contracts. Native integer bigint cursor keys are serialized separately.
78
+ REST/MCP use separate `paged/wire/views/<name>.ts` SQL when declared integers
79
+ require exact transport projections. Web pages and internal paged Views keep
80
+ driver-native rows. Internal paging creates no public route/tool.
81
+ Authored SQL `LIMIT` bounds the complete ordered row set, including across cursor pages.
82
+ Use `limit` in the paging options to choose each page size (up to 500); it does not expand the View's declared result set.
83
+ An MCP View page keeps the `limit` of 1 to 500 but holds only as many of those rows
84
+ as fit 60,000 characters of JSON, so a result stays under host truncation limits. It
85
+ is cut before the first row that overflows and its next cursor resumes at that row.
86
+ A single row over the budget is returned alone with the cursor, and a View without an
87
+ order stays one whole page; a page still over the budget carries a notice that hosts
88
+ may truncate it.
89
+ A pageable Schema View has a complete NOT NULL scalar tuple ending in the
90
+ compiler's `id` tiebreak. Its ordered columns must match an index after fixed
91
+ equality prefixes; a user-field `IN` scope or restrict is not one (see
92
+ [index prefixes](schema.md#index-prefixes-for-paged-views)); multiple columns and mixed directions
93
+ retain the authored order. CTE projections preserve the proof; joins must have a
94
+ proved singleton side, or equality on the joined Schema's `id` or single-field
95
+ unique key, so the base id cannot fan out. LEFT JOIN accepts nullable or missing
96
+ base references while preserving the base order/key tuple; joined fields remain
97
+ nullable cursor keys. Refusals identify the nullable key, missing ORDER BY index,
98
+ or fanning join and state a remedy. Relation aliases and nested CTE
99
+ bindings keep their own identity; same-Schema aliases are not interchangeable.
100
+
101
+ Grouped Views use every GROUP BY key as the tiebreak, with known non-null scalar
102
+ keys; unfiltered, non-windowed `min`/`max` of a non-null column retain its native cursor type. Paging a
103
+ grouped result does not avoid the native grouping/source scan. Authors own source
104
+ predicate indexes and should inspect EXPLAIN for their workload. Nullable,
105
+ unknown and JSON order expressions and unproved fanout remain refused.
106
+ Trusted system reads retain their all-scope SQL: without an actual scope
107
+ predicate, a scope-first index is not a proved range scan and native sorting or
108
+ scanning may be needed. Caller equality-prefix proofs do not claim otherwise.
109
+
110
+ Proved singletons (scalar/aggregate queries, unique-id lookups and singleton
111
+ sources) run bounded first-page SQL, reject cursors and refuse overflow.
112
+ Internal Views receive directly imported paged modules only when the same proofs
113
+ hold; they gain no public route or tool. Their bare modules remain available for
114
+ other native SQL and return full authored-query arrays.
115
+
116
+ Cursors reuse the Store's percent-encoded JSON keyset codec. Selected View
117
+ cursors include operation provenance; malformed, foreign and
118
+ null-key cursors fail with `INPUT_VALIDATION_FAILED`. Each generated key has a
119
+ scalar check; temporal keys keep database microseconds. Existing one/two-key
120
+ formats and Store cursors are preserved; larger View tuples contain their full
121
+ key list followed by operation provenance. They are opaque navigation
122
+ values, not credentials; authorization and row predicates run on every page.
123
+
124
+ ## Mounting REST
125
+
126
+ `rest.ts` exports `rest({ db, caller })`, returning an async
127
+ `(request, env) => Response | undefined`. The app supplies its native handle and
128
+ verified Caller; requests outside `/api` fall through to the app. Public Views
129
+ use `/api/views/<name>`; HTTP Triggers keep their declared method and path.
130
+ Known paths with another method return 405 with an `Allow` header. All responses use
131
+ `cache-control: private, no-store`. JSON bodies and decoded path parameters
132
+ are validated together, with path parameters taking precedence. Bodies larger
133
+ than 1,000,000 bytes are refused; `content-length` is checked before reading.
134
+ A resolver's `ContractError` keeps its status (an expired token is 401 with its challenge); any other resolver exception returns a redacted 500. Cookie-origin refusals retain
135
+ `path: "request:origin"`; REST and MCP share the kernel check.
136
+
137
+ When the manifest declares ref Procedures, pass the generated `Handlers` as
138
+ `rest({ db, caller, handlers })`; the option is type-required. Handler dispatch
139
+ uses the generated Store, preserving the Procedure's authorization contract.
140
+ Publishing visibility belongs to generated SQL; the REST module adds no status
141
+ filter. The app retains ownership of authentication and its other routes.
142
+
143
+ ## Optional web output
144
+
145
+ `web.ts` exports `web({ db, caller, origin, title, description, locales?, pages,
146
+ cacheControl? })`. `db` is the application-owned native handle, including callable
147
+ PostgreSQL handles. `caller` is a verified Caller or the app's
148
+ `(request, env, { readOnly }) => Caller | Promise<Caller>` resolver, evaluated once per request.
149
+ Generated REST/MCP/web callbacks classify reads; forward the third argument to
150
+ `resolveCaller` to permit an enabled cookie cache, as documented in
151
+ [authentication](../cloudflare/authentication.md#explicit-read-only-cookie-snapshots).
152
+ The module dispatches public Views only; it never invokes Procedures or needs handlers.
143
153
 
144
154
  ```ts
145
- import { createMantle } from "../.mantle/generated/mantle.js";
146
-
147
- const mantle = await createMantle({ storage, handlers });
148
-
149
- await mantle.views.publishedNotes({ page: 1, show: 20 });
150
- await mantle.procedures.expireOrder({ orderId }, { user: null, staff: null, env });
151
- await mantle.entries.orders.createDraft({ data, authorId: user.id });
152
- mantle.triggers.expireOrderHttp; // { name, source, target }
153
- await mantle.runtime.archive.execute({ id, ctx });
155
+ import { web } from './generated/web.js';
156
+ const site = web({ db, caller, origin: 'https://example.com', title: 'Blog',
157
+ description: 'Articles', locales: ['en'],
158
+ pages: { '/posts/{slug}': { view: 'post-by-slug', list: 'posts', markdown: 'body' } }
159
+ });
160
+ const response = await site.fetch(request, env); // Response | undefined
161
+ const tags = await site.head(request, env); // HTML string, empty for unknown/missing pages
154
162
  ```
155
163
 
156
- `entries.<collection>` exposes `createDraft`, `get`, `list`, `delete`, and the indexed field reads `readBySlug`, `readByDataField`, `readByDataFieldIn` and `findManyByDataField`, supplying the required collection identity to Core. The field reads accept only declared Schema fields and their scalar types, and return entries whose `data` is the generated Schema shape, so an author never has to drop to an untyped `runtime.entries` call or scan `list` to find rows by an indexed field. Generic MCP entry tools require a `collection` argument. `runtime` is the underlying Core runtime, so the typed projection never hides it. A host that owns its own lifecycle can skip generation entirely and call `runtime.executeView({ view: "published-notes" })` directly.
157
-
158
- ## Packages
159
-
160
- The umbrella installs Spec and Runtime only. Web, Admin, Auth, Admin UI, Bun, Vercel and Cloudflare are optional peers; install one before importing its subpath. Every sub-package is also directly installable.
161
-
162
- | Package | Umbrella subpath | Holds |
163
- |---|---|---|
164
- | `@aotter/mantle` | root | Umbrella plus the `mantle` and `mantle-harness` binaries. |
165
- | `@aotter/mantle-spec` | `/spec` | Manifest grammar, parser, validators, JSON Schema to zod, site-config contract, diagnostic catalog. No environment, no IO. |
166
- | `@aotter/mantle-runtime` | `/runtime` | Hexagonal runtime: domain ports, use cases, MCP catalog, storage helpers. No adapter dependencies. |
167
- | — | `/runtime/testing` | Node-only crowded-SQLite planner and HTTP sampling helpers used by `mantle-harness`. |
168
- | — | `/codegen` | The pure linked-manifests or compiled-plan to typed-module emitter, with no IO. |
169
- | `@aotter/mantle-web` | `/web` | HTML, Markdown, `llms.txt`, sitemap, SEO and preview composition. No routes, no platform dependencies. |
170
- | `@aotter/mantle-admin` | `/admin` | Admin API, auth route mounting, OAuth pages, static-asset composition. |
171
- | `@aotter/mantle-auth` | `/auth` | Host-neutral Better Auth identity, staff roles, and OAuth 2.1 / MCP authorization. Adapters own IP headers and storage bindings. |
172
- | `@aotter/mantle-admin-ui` | `/admin-ui` | Pre-built React 19 Admin SPA bundle. |
173
- | `@aotter/mantle-bun` | `/bun` | Bun adapter over a caller-owned `bun:sqlite` database. |
174
- | `@aotter/mantle-vercel` | `/vercel` | Vercel Functions adapter with injected durable storage and platform `waitUntil`. |
175
- | — | `/vercel/libsql` | Optional application-owned Turso/libSQL driver. |
176
- | `@aotter/mantle-cloudflare` | `/cloudflare` | Cloudflare Workers adapter: D1, Workers Cache, R2, Queues, Better Auth 1.7 MCP and CIMD. |
177
- | `@aotter/mantle-indexeddb` | — | Browser IndexedDB adapter. Directly installable; no umbrella subpath. |
178
-
179
- ## Versions
180
-
181
- This handbook describes the snapshot in this source tree. Pin every `@aotter/mantle*` package to one exact version and keep them together. For a registry installation, use the documentation at the matching release tag. For a source-built package, record the source commit as well as the package version.
182
-
183
- The documentation site pins its handbook commit in `docs/handbook.json` and records its vendored SDK source in `vendor/mantle/SOURCE.txt`. Those commits may differ when the intervening changes are behavior-neutral; the site verifies that condition during the build.
184
-
185
- New projects can use `generate` to assemble a blank hosted app. Existing authored Workers can keep their own entry and use the same CLI for typed compilation. Pin all selected packages to one exact SDK version; prerelease APIs can change until stable publication.
186
-
187
- ## Source
188
-
189
- - [`packages/mantle/README.md`](../../../packages/mantle/README.md)
190
- - [`packages/mantle/src/cli/main.ts`](../../../packages/mantle/src/cli/main.ts)
191
- - [`packages/mantle/src/cli/generate.ts`](../../../packages/mantle/src/cli/generate.ts)
192
- - [`packages/mantle/src/cli/skills.ts`](../../../packages/mantle/src/cli/skills.ts)
193
- - [`packages/mantle/src/cli/harness.ts`](../../../packages/mantle/src/cli/harness.ts)
194
- - [`packages/mantle/src/codegen/emitMantleModule.ts`](../../../packages/mantle/src/codegen/emitMantleModule.ts)
195
- - [`packages/mantle-spec/src/infrastructure/cli/ValidateCommand.ts`](../../../packages/mantle-spec/src/infrastructure/cli/ValidateCommand.ts)
196
- - [`packages/mantle-spec/src/infrastructure/cli/EmitOpenapiCommand.ts`](../../../packages/mantle-spec/src/infrastructure/cli/EmitOpenapiCommand.ts)
197
- - [`packages/mantle-runtime/src/infrastructure/mcp/McpToolCatalog.ts`](../../../packages/mantle-runtime/src/infrastructure/mcp/McpToolCatalog.ts)
198
- - [`packages/mantle-admin/src/mountMantleAdmin.ts`](../../../packages/mantle-admin/src/mountMantleAdmin.ts)
199
- - [`packages/mantle-admin/src/mountMantleOAuth.ts`](../../../packages/mantle-admin/src/mountMantleOAuth.ts)
200
- - [`packages/adapters/cloudflare/README.md`](../../../packages/adapters/cloudflare/README.md)
201
- - [`packages/adapters/cloudflare/src/worker/createMantleWorker.ts`](../../../packages/adapters/cloudflare/src/worker/createMantleWorker.ts)
202
- - [`packages/adapters/cloudflare/src/mount/mountRuntimeEndpoints.ts`](../../../packages/adapters/cloudflare/src/mount/mountRuntimeEndpoints.ts)
203
- - [`packages/adapters/cloudflare/src/mount/mountPublicRoutes.ts`](../../../packages/adapters/cloudflare/src/mount/mountPublicRoutes.ts)
204
- - [`packages/adapters/cloudflare/src/mount/mountMcp.ts`](../../../packages/adapters/cloudflare/src/mount/mountMcp.ts)
205
- - [`packages/adapters/cloudflare/src/oauth/cachePolicy.ts`](../../../packages/adapters/cloudflare/src/oauth/cachePolicy.ts)
164
+ Page parameter names must exactly match the View's declared input properties.
165
+ Page and list Views must return the page parameters; list Views must accept empty input: no
166
+ `required`, no root `anyOf`, `enum` or `const`, no `else` rule, and a root `type`, when given, that includes `object`.
167
+ These conditions also apply to each schema a root `$ref` chain reaches, and an unresolved or cyclic chain fails them.
168
+ `web()` throws a `TypeError` for any other list View. Markdown
169
+ must name a textual output column. HTML remains application-owned.
170
+
171
+ `/llms.txt` and `/sitemap.xml` traverse each list's `nextCursor`; `.md` and head
172
+ read the current page View. SQL alone controls visibility, including publication.
173
+ GET/HEAD responses include content-derived SHA-256 ETags. Anonymous responses use
174
+ `cacheControl` or `public, max-age=60`; authenticated responses use
175
+ `private, no-store`. `If-None-Match` returns 304 only after current content is read.
176
+ Canonical URLs use the configured origin, independently of the request host.
177
+ The current implementation adds hreflang only for `locales[0]`; translation-aware
178
+ indexes and head tags are a follow-up below. Importing another generated entry
179
+ does not import web output.
180
+
181
+ Web indexes scan at most 50,000 rows for one sitemap and 5,000 for `llms.txt`,
182
+ across all configured lists. Skipped `.`/`..` path segments consume the scan
183
+ budget too. Output stops before exceeding 50 MiB of UTF-8 sitemap XML (including
184
+ its closing tag) or 1 MiB of `llms.txt`; a sitemap index can be added separately.
185
+ Each request also stops after 100 sitemap queries or 10 `llms.txt` queries,
186
+ across all templates. Small authored SQL LIMITs or many templates can truncate
187
+ output sooner. A sitemap index remains separate future work.
188
+
189
+ `head` normalizes URLs from the queried row's page parameters, matching sitemap
190
+ URLs; malformed paths, failed validation and unavailable rows return an empty
191
+ string. A NULL Markdown field answers 404. Errors and missing Markdown responses
192
+ use `cache-control: no-store`. A matching HTML page takes precedence over a `.md`
193
+ alternate, including slugs ending in `.md`; a Markdown alternate may read the
194
+ exact HTML candidate first and therefore performs at most two View queries.
195
+
196
+ ## Mounting MCP
197
+
198
+ Mount the generated `mcp({ db: env => env.DB, caller })` handler in the app's
199
+ Fetch entry. It returns `undefined` outside `/mcp` and `/mcp/admin`. Install the
200
+ optional official `@modelcontextprotocol/server` peer when selecting MCP; its
201
+ transport loads on the first matching request. Pass `handlers` when ref
202
+ Procedures are declared, as for REST. Admin tools are listed only on the admin
203
+ endpoint, and publishing visibility belongs to the generated SQL.
204
+
205
+ `apps.public` and `apps.admin` accept a `McpAppsSource` or an array mixing static
206
+ contributors with async `({ env, request }) => McpApps` factories. Static resources
207
+ are checked once at construction; factories resolve only after the endpoint's
208
+ origin and caller gates, and their resource identities and ownership are checked
209
+ on each request even when the tool names are unchanged.
210
+
211
+ Discovery is caller-independent by default (ADR-0014); listing a tool grants no
212
+ execution authority. Configure `authorize` to filter `tools/list` and the tool
213
+ names supplied to resource HTML loaders: that mode also excludes tools refused
214
+ by declared identity, scope and `check` requirements. Without `authorize`, the
215
+ complete endpoint catalogue is not a permission-filtered catalogue. Operation
216
+ validation, guards and credential checks still apply to calls.
217
+
218
+ The app resolves bearer tokens with `resolveCaller(request, auth, { audience:
219
+ new URL('/mcp', request.url).href })` (both surfaces use `<origin>/mcp`). The app
220
+ must also mount Better Auth's `/.well-known/oauth-protected-resource/mcp`
221
+ metadata route. Auth challenges point to that route. Presented bearer credentials
222
+ need the `mcp` scope; cookie sessions use the same-origin mutation checks.
223
+
224
+ Generation emits `mcp.ts` only for a non-internal View or MCP Trigger. Omitting
225
+ MCP remains a deliberate asymmetric choice; an HTTP-only Procedure still generates
226
+ successfully with the warning described in [Trigger](trigger.md). These
227
+ surfaces typecheck without the optional `@modelcontextprotocol/server` peer;
228
+ install the exact version declared in the installed SDK's `peerDependencies`.
229
+ The peer loads by dynamic import on the first matching `/mcp` request; if
230
+ missing, that request fails at runtime. Publishing alone does not select MCP;
231
+ on a selected
232
+ surface its generated `publish_<schema>`, `unpublish_<schema>` and
233
+ `archive_<schema>` tools use the same snake-case naming rule, are admin-only
234
+ and take `{ id, expectedVersion }` through the shared generated validation.
235
+
236
+ `/mcp/admin` admits only admins: `isAdmin(caller)` must hold. It lists only admin-surface tools; public Views and Procedures stay on `/mcp`, and generation never copies them across. An admin's AI that changes records also needs admin Views to find them and read back the committed row and its version (search, detail), declared by the app; the [acceptance design audit](../guides/agent-workflow-acceptance.md#audit-mcp-tool-design-before-implementation) checks this. Better Auth’s admin plugin `adminRoles` defines this layer (default `["admin"]`); the application supplies its verified Caller. MCP bodies are limited
237
+ to 1 MiB (1,048,576 bytes). Resource metadata challenges use the origin of
238
+ `request.url`; behind a proxy, the app must pass a Request with its public
239
+ origin so the audience and metadata URL point to the client-visible service.
240
+
241
+ Selected integer cursor keys may be exact canonical decimal strings on either dialect; SQLite cursor binds are cast to INTEGER so grouped expressions and native columns compare the same value; they are bounded to the declared integer range. This applies to cursor metadata only and does not change bare query or Store integer outputs, or recover integers already rounded by a native driver. `validCursor` is shared with selected boundary adapters.
242
+
243
+ Cursor casts target only proved AST bind nodes, preserving authored SQL literals. Paging proofs respect the compiler's lexical table/CTE identity. Explicit CTE or relation column renaming is currently refused for paging because uniqueness has not been proved through those aliases; internal bare queries remain available.
244
+
245
+ A paged View's output names must not collide with its actual hidden cursor keys (`_k0` through `_kN-1`); SQLite compares aliases without case sensitivity. Such internal Views keep their bare entry but receive no paged entry. Other output names remain available.
246
+
247
+ ## Selected Admin bindings
248
+
249
+ See [Native Admin](../guides/native-admin.md) for the single
250
+ current contract: three packages, explicit generation and app mounting, shared
251
+ whitelisted get/page/statistics templates, native Procedure fragments, generated
252
+ input checks, exact OCC and optional capabilities. These features are implemented
253
+ as an explicit option.
254
+
255
+
256
+ ### MCP catalogue and callbacks
257
+
258
+ `mcp({ db, caller, handlers?, locale?, instructions?, serverInfo?, requiredScopes?,
259
+ resourceMetadata?, allowedOrigins?, onObservation?, onRefusal? })` keeps the SDK behind its first
260
+ request. Locale defaults to `en`; localized titles/descriptions fall back to
261
+ English, then their first entry. Tools carry the [tool hints](procedure.md#mcp-tool-hints). `instructions` is returned by initialize.
262
+ The scope floor defaults to `["mcp"]` for every credential except cookie sessions;
263
+ `requiredScopes: []` disables that floor. An anonymous closed surface challenges
264
+ from initialize, and every call in a batch is checked before any call runs.
265
+ `resourceMetadata` overrides the challenge URL for reverse proxies.
266
+
267
+ Both endpoints answer a request without `Origin`, which is what server-side MCP
268
+ clients send; a bearer credential needs none. A cookie session is ambient authority, so
269
+ `checkOrigin` also guards it: on a request that is not GET, HEAD or OPTIONS, at least one of
270
+ `Origin` and `Sec-Fetch-Site` must be present and every present one must agree (`Origin` equal
271
+ to the request's own origin; `Sec-Fetch-Site` `same-origin` or `none`), else `AUTH_DENIED` at
272
+ `request:origin`. On MCP it runs only for a tool call that executes (not a read-only call from an
273
+ App host), not for initialize or `tools/list`; `allowedOrigins` does not lift it. A present `Origin` marks a browser: it must name a loopback host
274
+ (`localhost`, `127.0.0.1`, `[::1]`, any port) or an `allowedOrigins` entry, else
275
+ 403 before authentication on every credential; `Origin: null` is refused. The
276
+ site's own origin is not implied, because a DNS-rebinding page sends an `Origin`
277
+ equal to the `Host` it rebound, so a browser client served from your deployed
278
+ site lists that origin: `allowedOrigins: ["https://app.example"]`. Entries are
279
+ compared by hostname as the official SDK does (not scheme or port); each is a
280
+ full origin (`https://console.example`) or the SDK's bare host
281
+ (`console.example`, `localhost:8787`), and anything else, such as a path, throws
282
+ when `mcp(...)` is configured; so does a wildcard such as `*` or
283
+ `https://*.example.com`, since entries are matched by exact hostname. `Host` is
284
+ not checked. Cookie writes also need the full same-origin check.
285
+
286
+ `/mcp` sends no CORS headers. `allowedOrigins` therefore serves pages on the
287
+ same origin as the endpoint, or clients behind a proxy that forwards their
288
+ `Origin`; a browser client on another origin also needs the app to answer
289
+ preflight and add CORS headers itself. Under `wrangler dev` with `routes` or
290
+ `dev.host`, Miniflare rewrites an `Origin` equal to the request's own origin to
291
+ the route or `dev.host` origin, and a DNS-rebinding request always has that
292
+ shape. Listing that host would admit rebinding against the local server, so keep
293
+ the route and `dev.host` hosts out of `allowedOrigins` in local development.
294
+
295
+ `onObservation` receives request refusals and invocation attempt/completion
296
+ metadata, including duration and outcome. Its Caller and scopes are frozen copies.
297
+ `onRefusal` receives credential/origin refusals. Neither callback receives request
298
+ headers, bodies or tokens; delivery failures never change the business response
299
+ and promises are not awaited. The application owns callback delivery.
300
+
301
+ **Procedure output:** SQL Procedures retain the native `{ results }`
302
+ contract rather than 0.2's authored output projection/validation; MCP advertises
303
+ that object envelope, not the incompatible authored schema. Ref Procedures expose
304
+ the application's typed return value and advertise their authored object output
305
+ schema. The application owns ref output validation. MCP does not decode or
306
+ revalidate native database rows. Business failures carry diagnostics in text;
307
+ with an output schema they omit `structuredContent` to avoid validating an error
308
+ against a success schema. Without one they also expose diagnostic structured
309
+ content. MCP adds no prompts or non-UI resources.
310
+
311
+ ### Tool versions
312
+
313
+ Each generated tool (MCP, media, publishing and WebMCP) carries `version`, a
314
+ short hash computed by `mantle generate` of the definition as listed: name,
315
+ title, description without the marker, input schema (a View tool's with its
316
+ `limit` and `cursor` paging properties), output schema and annotations. A
317
+ localized title or description is hashed with all its locales. The description
318
+ ends with `(tool version <version>)`, so an unchanged tool keeps a
319
+ byte-identical description across deploys.
320
+
321
+ MCP results of a call that reaches the tool add a second text block,
322
+ `tool version <version>`; failures other than authorization refusals append
323
+ `; input schema <JSON>`, the input schema the tool is listed with.
324
+ `structuredContent` is unchanged. A `tools/call` naming a tool the endpoint
325
+ serves but the caller may not use is answered by the authentication or
326
+ authorization challenge (HTTP 401 or 403) before the SDK's transport and
327
+ request checks, even though that caller's `tools/list` omits the tool. A
328
+ `tools/call` naming a tool not in the endpoint's catalogue, in a batch or not
329
+ and from a host caller too, is answered with JSON-RPC error `-32602` and the
330
+ message
331
+ `No tool named "<name>" exists in the current catalogue (catalogue version <serverInfo.version>).`
332
+ For such a name the SDK's transport and request checks (headers, protocol
333
+ version, JSON-RPC shape) answer first, so a malformed request gets the same
334
+ refusal as one naming a served tool the caller may use.
335
+ Unless `serverInfo.version` is given, the kernel's `mcp()` sets it to
336
+ a hash of the `surface:name:version` of every tool the endpoint serves, before
337
+ per-caller filtering, so `/mcp` and `/mcp/admin` each report their own catalogue
338
+ version, and it changes when a generated tool's definition changes or a tool is
339
+ added or removed, however the tools are wired.
340
+
341
+ `POST /admin/api/webmcp/{tool}` returns `{ output, version }`. A refusal's
342
+ diagnostic adds `toolVersion`, and `inputSchema` unless it is an authorization
343
+ refusal; internal errors and refusals before the call carry neither. Admin's
344
+ WebMCP results repeat these as the same text block MCP adds. The navigation
345
+ tools `admin_get_context` and `admin_navigate` have no version. Tools an
346
+ application adds itself carry a version only if it sets one. Why: [MCP and
347
+ agents](../concepts/mcp-and-agents.md#when-a-host-shows-old-tools).
348
+
349
+ ### Declared View scalars on the wire
350
+
351
+ REST and MCP View pages expose declared JSON columns as JSON values and declared
352
+ boolean columns as booleans, including explicitly authored SQL aliases. SQLite
353
+ and D1 conversions are compiled into only the selected boundary pages;
354
+ PostgreSQL already-native JSON/boolean values pass through. SQL NULL stays null and absent
355
+ fields stay absent. A computed output that is a JSON document by construction
356
+ (`json_group_array`, `json_set`, `jsonb_agg`, `jsonb_build_object` and the like, a scalar
357
+ subquery of one, or a `COALESCE` of those) is converted like a declared JSON column, so it
358
+ is a JSON value on every target. Other expressions without declared column lineage stay native.
359
+ Malformed persisted JSON becomes a redacted internal failure. The conversion
360
+ adds no database operations and does not mutate native query rows.
361
+
362
+ Bare Views, Store rows, native SQL Procedure `{ results }` and application-owned
363
+ ref outputs retain their native contracts. Use the generated `rest()` / `mcp()`
364
+ module for this client wire contract; do not duplicate JSON/boolean decoding in
365
+ application route handlers or decode PostgreSQL JSON strings a second time.
366
+
367
+ Declared integer columns (including a selected system `version`) on every target
368
+ use the wire form: safe integers become JSON numbers; unsafe values remain exact
369
+ decimal text. Selected-boundary SQL casts integers to text before driver conversion,
370
+ preserving stored int64 values on D1 too. Values already rounded when written
371
+ (e.g. by a JavaScript number) cannot be recovered. Computed outputs and native
372
+ SQL Procedure/ref results retain their native values.
373
+ Unexpected surface failures log only a stable wire classification.
374
+
375
+ MCP locale fallback reads only own locale entries, then English, then the first
376
+ own entry; inherited property names never become titles or descriptions.
377
+
378
+ ### Admin media MCP tools
379
+
380
+ With generated site configuration, `mcp({ db, caller, media: env => storage })`
381
+ loads the media module only on a configured admin MCP request and
382
+ adds `get_media_upload_policy`, `create_media_upload`, `commit_media_upload`,
383
+ `list_media_assets`, `get_media_asset`, `update_media_asset` and
384
+ `delete_media_asset` to `/mcp/admin` only. `storage` is the same `MediaStorage`
385
+ used by the Admin media library. Omitting `media` omits all seven tools.
386
+ Admins can invoke them; cookie writes retain the origin check.
387
+ Generated boundary code checks each input and advertises the 0.2 schemas and
388
+ read-only/destructive/open-world hints. Image bytes are PUT directly to the
389
+ returned short-lived URLs with their required headers, then committed; bytes
390
+ never enter MCP. Store permanent asset ids, never upload URLs.
391
+
392
+ Generation always emits a `Handlers` type (empty when no Procedure uses `handler: { ref }`), so every app wires the
393
+ same way: `const handlers: Handlers = {...}`, then `store(db, caller, { handlers })`, `adminBindings({ handlers })`,
394
+ `rest({ ...options, handlers })`, `mcp({ ...options, handlers })` and `webmcpRoutes(catalogue, tools, handlers)`.
395
+ Type extra hand-written tools as `Tool<Handle, Handlers>` with the generated `Handlers` before combining them with
396
+ the generated `tools`. Handler-less apps may still omit `{ handlers }` at `store()` and `adminBindings()`.