@aotter/mantle 0.2.0-alpha.8 → 0.2.0-alpha.9

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 (61) hide show
  1. package/README.md +17 -0
  2. package/dist/auth/adapter.js +9 -6
  3. package/dist/auth/routes.js +3 -2
  4. package/dist/build/compiler.js +1 -1
  5. package/dist/infrastructure/bundle.js +16 -3
  6. package/dist/infrastructure/generate.js +9 -1
  7. package/dist/infrastructure/generateAuth.js +51 -0
  8. package/dist/infrastructure/wrangler.js +19 -4
  9. package/dist/kernel/binds.d.ts +0 -3
  10. package/dist/kernel/contracts.d.ts +1 -1
  11. package/dist/kernel/contracts.js +4 -4
  12. package/dist/kernel/dialect/common.d.ts +0 -1
  13. package/dist/kernel/driver/postgres.js +3 -2
  14. package/dist/kernel/mcp-apps.js +1 -1
  15. package/dist/kernel/mcp-handler.js +1 -1
  16. package/dist/kernel/mcp.d.ts +5 -1
  17. package/dist/kernel/mcp.js +2 -1
  18. package/dist/kernel/scalars.js +3 -2
  19. package/dist/kernel/web-index.js +40 -0
  20. package/dist/kernel/web.js +3 -37
  21. package/dist/usecase/publishing.js +1 -1
  22. package/dist/usecase/site.js +0 -1
  23. package/dist/usecase/validation.js +8 -0
  24. package/docs/examples/README.md +1 -1
  25. package/docs/examples/app-shell/README.md +6 -2
  26. package/docs/examples/app-shell/package.json +3 -3
  27. package/docs/examples/app-shell/src/auth.ts +3 -2
  28. package/docs/examples/app-shell/src/service.ts +2 -2
  29. package/docs/examples/club-events/manifests/club-events.yaml +7 -0
  30. package/docs/examples/club-events/src/handlers.ts +1 -1
  31. package/docs/examples/purchasing/manifests/purchasing.yaml +16 -0
  32. package/docs/examples/purchasing/src/handlers.ts +1 -1
  33. package/docs/handbook/cloudflare/authentication.md +21 -4
  34. package/docs/handbook/cloudflare/chatgpt-sites.md +4 -2
  35. package/docs/handbook/cloudflare/deploy-and-operate.md +24 -1
  36. package/docs/handbook/cloudflare/media-r2.md +2 -3
  37. package/docs/handbook/cloudflare/service-entry.md +14 -4
  38. package/docs/handbook/concepts/runtime-and-adapters.md +1 -1
  39. package/docs/handbook/examples/hub.md +1 -1
  40. package/docs/handbook/guides/agent-setup.md +4 -4
  41. package/docs/handbook/guides/agent-workflow-acceptance.md +222 -0
  42. package/docs/handbook/guides/mcp-apps.md +78 -1
  43. package/docs/handbook/guides/native-admin.md +4 -0
  44. package/docs/handbook/guides/oauth.md +58 -0
  45. package/docs/handbook/guides/typed-queries.md +35 -0
  46. package/docs/handbook/reference/procedure.md +1 -1
  47. package/docs/handbook/reference/surface.md +6 -2
  48. package/docs/handbook/reference/trigger.md +9 -0
  49. package/docs/handbook/releases/index.md +3 -3
  50. package/docs/handbook/start/project-and-cli.md +9 -1
  51. package/docs/skills/develop/SKILL.md +43 -16
  52. package/docs/skills/integrate/SKILL.md +4 -9
  53. package/docs/skills/provision/SKILL.md +15 -12
  54. package/docs/skills/update/SKILL.md +19 -18
  55. package/package.json +2 -2
  56. package/skills/README.md +1 -1
  57. package/skills/mantle/SKILL.md +35 -13
  58. package/skills/mantle/references/local-testing.md +5 -0
  59. package/skills/mantle/references/website.md +6 -4
  60. package/docs/skills/develop/mcp-app.md +0 -39
  61. package/skills/.keep +0 -0
@@ -58,6 +58,15 @@ unchanged. A POST registration whose redirect URIs are all HTTP localhost,
58
58
  127.0.0.1 or [::1] becomes `application_type: "native"` when omitted; explicit
59
59
  types and mixed/external callbacks stay under the SDK's validation.
60
60
 
61
+ With the optional Admin UI selected, use `consentPage:
62
+ "/admin/oauth/consent"`, as in `docs/examples/app-shell/src/auth.ts`, and keep
63
+ `adminShell` mounted in the service entry. That page uses mantle-ui's `AuthCard`,
64
+ cards and sign-in buttons; it loads the verified request from
65
+ `/oauth/consent/data` and posts the original signed payload to `/oauth/consent`.
66
+ The UI displays the client, callback origin, requested scopes and background
67
+ access explanation. Keep Core's script-free `/oauth/consent` HTML as the
68
+ adapter-neutral fallback, not as the reference application's styled page.
69
+
61
70
  `/oauth/consent` and `/oauth/consents` serve escaped, script-free English or zh-TW
62
71
  HTML. Their `/data` routes serve the 0.2 UI JSON shapes. Consent approval and
63
72
  `POST /oauth/consents/revoke` require a live cookie session and same-origin
@@ -84,6 +93,55 @@ Grant lifetime follows Better Auth. Better Auth issues a refresh token only when
84
93
  the grant includes `offline_access`; a client registered for `authorization_code`
85
94
  may use `refresh_token` too, so registering only `authorization_code` does not
86
95
  prevent long-lived grants.
96
+ CIMD is the primary client-discovery path; dynamic client registration (DCR)
97
+ remains available for older MCP clients. Both paths require the client to request
98
+ `offline_access` and the user to consent before a renewable grant is issued.
99
+ The DCR registration defaults below apply only to that compatibility path.
100
+
101
+ For renewable MCP connections, the examples set both `scopes` and
102
+ `clientRegistrationDefaultScopes` to `["mcp", "offline_access"]`, so a dynamically
103
+ registered client that omits `scope` is registered for `mcp offline_access`. The
104
+ client must request those registered scopes during authorization and the user must
105
+ consent to background access. If the client explicitly requests only `mcp`, OAuth
106
+ must not add a scope the user did not consent to: the resulting grant is bound to
107
+ the browser session and has no refresh token. The client must request
108
+ `offline_access`, or the user must reconnect and authorize it, to gain renewable
109
+ access. Declaring the `refresh_token` grant type alone is insufficient.
110
+ `offline_access` belongs in authorization-server metadata, not in the protected
111
+ resource's required scope floor; resource metadata continues to advertise `mcp`.
112
+
113
+ For renewable connections, the app-shell's MCP handler also supplies an explicit
114
+ OAuth linking hint:
115
+
116
+ ```ts
117
+ const handleMcp = mcp({
118
+ db: (env: Env) => env.DB,
119
+ caller: resolveRequestCaller,
120
+ authorizationScopes: ["offline_access"],
121
+ });
122
+ ```
123
+
124
+ `authorizationScopes` adds scopes to the standard `WWW-Authenticate` challenge
125
+ when the caller is unauthenticated. With the default `requiredScopes`, that
126
+ challenge requests `mcp offline_access`. It does **not** change the API scope
127
+ floor, add scopes to a valid token, issue tokens or modify an already-signed
128
+ authorization request. An existing valid `mcp`-only token can still call a tool
129
+ that only requires `mcp`. The application must independently permit
130
+ `offline_access` in its provider and the user must explicitly consent.
131
+ Verify the actual requested scopes, refresh-token issuance and post-expiry
132
+ rotation in the intended client. Do not put `offline_access` in the resource's
133
+ required scope floor or silently add it after consent.
134
+
135
+ For an existing connection granted only `mcp`, deploy the allowed-scope change,
136
+ refresh the client's discovery/registration metadata, then reconnect or reauthorize
137
+ once with `offline_access` and obtain updated consent. Allowing a scope does not
138
+ add it to a stored consent. Do not edit consent rows, prolong access-token TTLs or
139
+ disable grant checks to avoid that step. Verify a refresh token was issued and
140
+ that the client can rotate it and resume MCP after access expiry; a successful
141
+ initial `tools/list` does not prove renewable authorization. The reference service's
142
+ local smoke exercises issuance, refresh after website sign-out, rotation retry
143
+ and revocation using real OAuth endpoints and a disposable D1.
144
+
87
145
  Without `offline_access` the grant is bound to the browser session: its tokens
88
146
  stop working when that session expires or the user signs out. A refresh that
89
147
  narrows the scope to drop `offline_access` after the session ends returns
@@ -90,3 +90,38 @@ Interactive `transaction(async tx => …)` exists on bun:sqlite and PostgreSQL,
90
90
  Ref handlers receive `{ caller, db, store }` bound to the same Caller and transaction. Implement the generated `Handlers` interface and pass it to `store(db, caller, { handlers })`. Direct Procedure modules expose SQL Procedures; ref Procedures are reached through the bound Store or selected surfaces.
91
91
 
92
92
  See [native execution](native-execution.md) for values and write outcomes and [migrations](../concepts/runtime-and-adapters.md#schema-installation-and-migrations) before changing a Schema.
93
+
94
+ ## Prefer Store-backed ref Procedures for simple writes
95
+
96
+ For a single-entity create or partial update, prefer a declared `ref` Procedure
97
+ whose handler awaits its caller-bound `ctx.store.collection(...).insert(...)`
98
+ or `.update(id, allowedPatch, { lock: expectedVersion })`. Keep the Procedure's
99
+ `requires` and generated validation at its invocation boundary: Store enforces
100
+ row policy, but does not substitute for operation authorization. Do not invoke
101
+ a handler directly from a route and accidentally bypass those checks.
102
+
103
+ Insert/update returns the actual written native row. Inside a transaction,
104
+ that result is provisional until the outer transaction commits; later failure
105
+ can still roll it back. Report committed success or emit external effects only
106
+ after that transaction succeeds. Return an explicit
107
+ allowlist of useful business fields, its backend `id` and returned `version`;
108
+ do not spread the entire row, echo the submitted version, calculate a new
109
+ version instead of reading the result, or add a generic receipt adapter. These
110
+ ref results are app-owned: declare their output schema and make any required
111
+ native-to-wire conversion explicit at the boundary.
112
+
113
+ Patch only permitted fields. Omitted fields remain unchanged; define whether
114
+ explicit null clears a nullable field or is invalid. Do not expand a partial
115
+ request into a full replacement with defaults for unseen fields. A stale lock
116
+ is a conflict; preserve intent and reconcile/review rather than silently
117
+ refreshing the lock and retrying.
118
+
119
+ Use SQL Procedures when conditions or related effects must commit together:
120
+ for example, creating an order while decrementing stock under a CHECK, a state
121
+ transition guarded by an atomic WHERE, or an additive restock upsert. A
122
+ pre-read in TypeScript followed by an unguarded write is not equivalent.
123
+ Store `batch` is atomic for known writes; a D1 batch step cannot consume a prior
124
+ step's returned ID. D1 has no interactive transaction callback, so retain the
125
+ supported SQL program or another existing native atomic boundary for that case.
126
+ The [MCP design audit](agent-workflow-acceptance.md) applies these choices before
127
+ implementation and separates them from independent end-user acceptance.
@@ -4,7 +4,7 @@ A Procedure requires `input`, `output` JSON Schemas and a `handler` containing S
4
4
 
5
5
  SQL uses declared Schemas, `input.<name>`, `auth.uid()` and native expressions accepted by the selected compiler dialect. Compiler policy supplies scope and system fields. Do not assign scope, version, status, published_at, timestamps or author columns yourself. Publishing writes use generated transitions. `restrict` narrows the rows the statements read and write, as on a [View](view.md#restrict); it is refused on ref handlers.
6
6
 
7
- A row-classified INSERT/UPDATE/DELETE (one that targets a single row, e.g. `WHERE id = input.id`) must affect one row. Use `WHERE id = input.id AND version = input.expectedVersion` for explicit OCC. Set operations, including UPDATE with a subquery and `INSERT ... SELECT`, may affect zero rows without an error: check the returned rows when zero means failure. A unique clash, a failed Schema `checks` constraint (the message starts with `A check of the Schema failed`; SQLite and D1 add the check expression, PostgreSQL the constraint name) or a failed row assertion is `CONFLICT` (409); statements run atomically. Results are raw `{ results }`, not runtime-validated authored outputs. RETURNING uses SQL output names; quote an explicit alias to preserve declared case (`RETURNING reqStatus AS "reqStatus"`). Generation warns about unaliased direct fields whose declared name differs from storage.
7
+ A row-classified INSERT/UPDATE/DELETE (one that targets a single row, e.g. `WHERE id = input.id`) must affect one row. Use `WHERE id = input.id AND version = input.expectedVersion` for explicit OCC. Set operations, including UPDATE with a subquery and `INSERT ... SELECT`, may affect zero rows without an error: check the returned rows when zero means failure. A unique clash, a failed Schema `checks` constraint or a failed row assertion is `CONFLICT` (409); statements run atomically. A check failure keeps that code and its existing `A check of the Schema failed` message, and sets `ContractError.fields` to `{ kind: "check", schema, check }`; `check` is the expression on SQLite/D1 and the generated constraint name on PostgreSQL. Results are raw `{ results }`, not runtime-validated authored outputs. RETURNING uses SQL output names; quote an explicit alias to preserve declared case (`RETURNING reqStatus AS "reqStatus"`). Generation warns about unaliased direct fields whose declared name differs from storage.
8
8
 
9
9
  A row-classified UPDATE/DELETE on an excluded or missing id affects zero rows and returns HTTP 409 (`CONFLICT`), just like a stale version; reads return not found. Native Admin's visible-row pre-read returns 404 for excluded/missing ids.
10
10
 
@@ -13,7 +13,7 @@
13
13
  snake-cased View names on each declared surface, and snake-cased Procedure
14
14
  names per MCP Trigger. Its SDK loads only through a request-time dynamic import.
15
15
  - HTTP/MCP Triggers declare exposure only. `mantle generate` refuses schedule
16
- and lifecycle Triggers; Cloud scheduling is a separate capability.
16
+ and lifecycle Triggers; host scheduling is a separate capability.
17
17
  - Native Admin retains selected 0.2 paths and the console contracts; see
18
18
  [Native Admin](../guides/native-admin.md) for app-mounted
19
19
  `adminBindings()` and `entries(bindings.schemas, bindings.adapter)`.
@@ -25,6 +25,8 @@ challenges, JSON-RPC, web rendering, keyset paging) lives in the package's
25
25
  `rest`, `mcp`, `web`, `paging` and `validation` kernel modules, so a runtime fix
26
26
  ships as a package update instead of a regeneration.
27
27
 
28
+ Procedure surface selection follows the [HTTP/MCP pairing default and deliberate asymmetry exception](trigger.md); no surface is implied.
29
+
28
30
  The generated `validation/views/<name>.ts` and
29
31
  `validation/procedures/<name>.ts` export `validate(unknown)`. View validators
30
32
  also export `query(URLSearchParams)` returning `{ input, limit?, cursor? }`.
@@ -219,7 +221,9 @@ must also mount Better Auth's `/.well-known/oauth-protected-resource/mcp`
219
221
  metadata route. Auth challenges point to that route. Presented bearer credentials
220
222
  need the `mcp` scope; cookie sessions use the same-origin mutation checks.
221
223
 
222
- Generation emits `mcp.ts` only for a non-internal View or MCP Trigger. These
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
223
227
  surfaces typecheck without the optional `@modelcontextprotocol/server` peer;
224
228
  install the exact version declared in the installed SDK's `peerDependencies`.
225
229
  The peer loads by dynamic import on the first matching `/mcp` request; if
@@ -24,12 +24,21 @@ metadata: { name: close-ticket-http }
24
24
  spec:
25
25
  source: { kind: http, method: POST, path: '/api/tickets/{id}/close' }
26
26
  target: { procedure: close-ticket }
27
+ ---
28
+ apiVersion: cms.mantle.aotter.net/v2
29
+ kind: Trigger
30
+ metadata: { name: close-ticket-mcp }
31
+ spec:
32
+ source: { kind: mcp, surface: admin }
33
+ target: { procedure: close-ticket }
27
34
  ```
28
35
 
29
36
  The target names a declared Procedure. `http` sources use POST/PUT/PATCH/DELETE and an `/api/` path. Path parameters bind to corresponding declared input fields and override JSON-body values. The generated REST module retains the Procedure's authorization and validates boundary input.
30
37
 
31
38
  An MCP source is `{ kind: mcp, surface: public }` or admin. The selected MCP module exposes the Procedure as a snake-case tool with its description, [tool hints](procedure.md#mcp-tool-hints) and requires checks.
32
39
 
40
+ By default, pair every HTTP Trigger with an MCP Trigger so the same operation is reachable by the user's AI. `mantle generate` warns when a Procedure has HTTP exposure but no MCP exposure, suggesting `surface: admin` for an admin-only Procedure and `public` otherwise. Triggers still decide every Procedure surface: intentional asymmetric exposure is allowed and generation succeeds.
41
+
33
42
  Lifecycle and schedule sources are rejected at generation with `GENERATE_UNSUPPORTED`, including disabled schedules. Use an app-owned handler or scheduler. For signed webhooks, verify the original raw Request in the app and call a generated Procedure with validated input; do not route it through a JSON boundary before signature verification.
34
43
 
35
44
  ## Input rules
@@ -17,10 +17,10 @@ Pin every package you use to the same exact version and upgrade them together.
17
17
 
18
18
  ## 0.2.0 — in preparation
19
19
 
20
- ### Upgrading to 0.2.0-alpha.8
20
+ ### Upgrading to 0.2.0-alpha.9
21
21
 
22
22
  Pin every selected `@aotter/mantle`, `@aotter/mantle-ui` and
23
- `@aotter/mantle-admin` dependency to `0.2.0-alpha.8`, refresh the lockfile,
23
+ `@aotter/mantle-admin` dependency to `0.2.0-alpha.9`, refresh the lockfile,
24
24
  then regenerate and check the application's owned artifacts. Keep existing
25
25
  production migrations application-owned; generated initial DDL is for an empty
26
26
  database. This alpha includes breaking Admin authoring and paging changes.
@@ -112,7 +112,7 @@ Existing alpha.6 applications require a source migration:
112
112
  application code. Review [authorization and Caller wiring](../concepts/runtime-and-adapters.md)
113
113
  and regenerate all owned artifacts before running `generate --check`.
114
114
 
115
- The [reference Worker](https://github.com/aotter/mantle/tree/v0.2.0-alpha.8/docs/examples/reference-service) and
115
+ The [reference Worker](https://github.com/aotter/mantle/tree/v0.2.0-alpha.9/docs/examples/reference-service) and
116
116
  [app shell](../../examples/app-shell/) demonstrate the current composition.
117
117
  The following earlier-alpha notes are historical; the current handbook and
118
118
  the native migration above supersede their runtime and CLI instructions.
@@ -12,6 +12,14 @@ description: Generation targets, CLI options and ownership of native application
12
12
  | `wrangler.jsonc` | App configuration; generator maintains selected split-build keys |
13
13
  | `dist/` | Disposable bundle output (`dist/worker`, `dist/client`); do not commit |
14
14
 
15
+ ## Accept user-facing work
16
+
17
+ Before reporting any user-facing deliverable as done, run the installed
18
+ version's [independent acceptance
19
+ flow](../guides/agent-workflow-acceptance.md) with a fresh agent on the surface
20
+ its user uses. For MCP work, also run that guide's design audit before choosing
21
+ tool names or writing manifests/handlers.
22
+
15
23
  ## Commands
16
24
 
17
25
  ```sh
@@ -37,7 +45,7 @@ Other handles may adapt to the same [structural driver contracts](../guides/adap
37
45
  | `--wrangler <path>` | Existing JSONC/JSON/TOML config; default finds one or creates `wrangler.jsonc`. TOML receives manual-edit instructions. |
38
46
  | `--entry <path>` | Worker source entry; default `src/worker.ts`. |
39
47
  | `--outdir <path>` | Bundle output; default `dist`. The Worker goes to `<outdir>/worker`. Keep separate from source/generated files. |
40
- | `--assets <path>` | With `--admin`: the static assets directory `mantle bundle` builds Admin into (`<assets>/admin`); default `<outdir>/client`. Written to Wrangler's `assets.directory`, and to the build command when not the default. |
48
+ | `--assets <path>` | With `--admin`: the static assets directory `mantle bundle` builds Admin into (`<assets>/admin`); default `<outdir>/client`. Written to Wrangler's `assets.directory`, and to the build command when not the default. New assets blocks default to `ASSETS`, Worker-first routing and no HTML rewriting; existing blocks keep their binding/routing. |
41
49
 
42
50
  ## Outputs
43
51
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: develop
3
- description: Work on an existing Mantle 0.2 project — manifests, SQL Views and Procedures, ref handlers, the service entry, surfaces, identity and MCP — using the installed package's contract.
3
+ description: Work on an existing Mantle 0.2 project — manifests, SQL Views and Procedures, ref handlers, the service entry, surfaces, identity, MCP tool design and independent acceptance — using the installed package's contract.
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: docs/skills/develop/SKILL.md
@@ -12,28 +12,48 @@ metadata:
12
12
 
13
13
  ## Workflow
14
14
 
15
- Read the installed SDK README and version-matched handbook before editing.
15
+ Read the installed SDK README, then follow the version-matched [project and CLI
16
+ workflow](../../handbook/start/project-and-cli.md) before editing.
16
17
 
17
- Follow the installed [native generation and service workflow](../../handbook/cloudflare/service-entry.md)
18
- with its [native contract](../../handbook/guides/native-execution.md) and [supported declarations](../../handbook/reference/features.md). Commit
19
- all generated output, run the same generation command with `--check`, and keep
20
- bundle `dist/` untracked. The app owns its verified Caller, handlers and native
21
- resources; Better Auth’s admin plugin `adminRoles` defines the admin layer (default `["admin"]`); `ctx.admin` takes no role list ([authorization](../../handbook/cloudflare/authentication.md)).
22
-
23
- Use the native Store/query/surface entries and app-owned fetch from the service composition guide.
18
+ Use the native Store/query/surface entries and app-owned fetch from the [service
19
+ composition guide](../../handbook/cloudflare/service-entry.md).
20
+ Pair every HTTP Trigger with an MCP Trigger by default; see [Trigger](../../handbook/reference/trigger.md)
21
+ for the generate warning and deliberate asymmetry exception.
24
22
  Keep optional modules out of unused entry imports and auth dynamically loaded.
25
23
  Run actual public/member/admin HTTP, MCP and UI scenarios; verify stored state,
26
24
  visibility, OCC and restart persistence, not just a 200 response.
25
+ Before reporting any user-facing work as done, follow the [independent
26
+ acceptance guide](../../handbook/guides/agent-workflow-acceptance.md): run it
27
+ with a fresh agent on the surface its user uses. This is not limited to MCP; if
28
+ it was not run, say so explicitly and report the work as UNVERIFIED rather than
29
+ done. For MCP workflows, also run the guide's design audit before implementing
30
+ tools.
27
31
 
28
- For native Admin, read the [current guide](../../handbook/guides/native-admin.md).
29
- Select `--admin` / `admin: true`; mount `entries(bindings.schemas, bindings.adapter)`
30
- from the optional Admin package. Keep one Core SDK, the Admin product and shared
31
- UI kit at matching versions. Generate → typecheck → implement listed handlers →
32
- typecheck. Ref handlers retain caller-bound `ctx.store`; generic Admin reads
33
- use shared generated View templates and writes use native Procedure fragments.
32
+ For native Admin, read the [current guide](../../handbook/guides/native-admin.md):
33
+ the app builds Admin with `mantle bundle`. To customise pages, slots, hooks or
34
+ MCP-host-safe components, follow [Customising Admin](../../handbook/guides/native-admin.md#customising-admin).
35
+ Keep matching SDK, Admin and UI versions. Ref handlers retain caller-bound
36
+ `ctx.store`; generic Admin reads use shared generated View templates and writes
37
+ use native Procedure fragments.
34
38
  Check actual member/Admin OTP, private scope/TTL/translation visibility, paging,
35
39
  statistics, safe/exact OCC and no automatic retry after unknown 503 outcomes.
36
40
 
41
+ For application-owned MCP App entries and host checks, follow the [MCP Apps
42
+ guide](../../handbook/guides/mcp-apps.md).
43
+
44
+ Build a custom frontend with `App` from `@modelcontextprotocol/ext-apps`: register
45
+ `ontoolinput`/`ontoolresult` before `connect()`, and use `callServerTool` for
46
+ host-proxied calls. Keep protocol negotiation and message routing in the official
47
+ client; importing its types while writing a second `postMessage` bridge is
48
+ insufficient.
49
+
50
+ For Admin UI upgrades, follow the installed handbook's
51
+ [retained-page recipe](../../handbook/guides/mcp-apps.md#keep-old-conversations-working-after-an-upgrade).
52
+ Keep deployed snapshots in source/artifact storage and pass `previous` to
53
+ `adminMcpApps`; verify old conversations after a changed UI build without host
54
+ tool refresh, reconnection or cache clearing. Keep tool contracts compatible
55
+ through the declared retention window.
56
+
37
57
 
38
58
  Place admin MCP Procedures in Admin deliberately: `spec.uiSchema.collectionAction:
39
59
  <schema-name>` adds a collection-header action; `spec.target: { schema, id,
@@ -51,10 +71,17 @@ The runtime never creates, alters or checks tables. Boot and requests run no DDL
51
71
 
52
72
  For the first install, apply generated `schema.sql`, including selected auth/site DDL, to an empty database. Apply selected seed SQL explicitly.
53
73
 
54
- For every later schema change, the application's coding agent diffs the regenerated `schema.sql`, writes a migration with the host's native tool (`wrangler d1 migrations` or the app's PostgreSQL migration tool), rehearses it on a copy, and applies it before deploying the new code. Cloud executes migrations at deploy time.
74
+ For every later schema change, the application's coding agent diffs the regenerated `schema.sql`, writes a migration with the host's native tool (`wrangler d1 migrations` or the app's PostgreSQL migration tool), rehearses it on a copy, and applies it before deploying the new code.
55
75
 
56
76
  Destructive changes and type changes need explicit data migration. Mantle drops nothing. Initial `schema.sql` is not an upgrade script.
57
77
 
58
78
  ## Data paths
59
79
 
60
80
  Writes use Store `insert` / `update` / `delete`, `batch`, `transaction`, or declared Procedures. Never use raw SQL writes: they bypass scope, TTL, publishing and system-column rules. For reads beyond an id lookup or simple AND filter (joins, aggregates, OR, reuse), declare an internal View so the SQL is reviewable and typed. Store `findById`, `find` and `first` remain suitable for handler lookups and embedding in an existing app. Ref handlers receive `ctx.store` bound to the same Caller and transaction.
81
+
82
+ For simple creates/partial updates, prefer Store-backed ref Procedures; keep
83
+ Procedure `requires`/generated input checks, use only allowed patch fields and
84
+ return safe business fields with the actual returned ID/version. Preserve
85
+ omitted fields and document null semantics. Retain SQL for atomic conditional
86
+ or related writes; D1 batches cannot consume an earlier result. Follow the
87
+ [Store write guidance](../../handbook/guides/typed-queries.md#prefer-store-backed-ref-procedures-for-simple-writes).
@@ -12,13 +12,8 @@ metadata:
12
12
 
13
13
  ## Workflow
14
14
 
15
- Read the installed SDK README and version-matched handbook before editing.
16
-
17
- Follow the installed [native generation and service workflow](../../handbook/cloudflare/service-entry.md)
18
- with its [native contract](../../handbook/guides/native-execution.md) and [supported declarations](../../handbook/reference/features.md). Commit
19
- all generated output, run the same generation command with `--check`, and keep
20
- bundle `dist/` untracked. The app owns its verified Caller, handlers and native
21
- resources; Better Auth’s admin plugin `adminRoles` defines the admin layer (default `["admin"]`); `ctx.admin` takes no role list ([authorization](../../handbook/cloudflare/authentication.md)).
15
+ Read the installed SDK README, then follow the version-matched [project and CLI
16
+ workflow](../../handbook/start/project-and-cli.md) before editing.
22
17
 
23
18
  Keep the application router, database/pool, identity and frontend. Replace
24
19
  Core consumers explicitly and compose only selected generated entries. Resolve
@@ -30,8 +25,8 @@ the configured OAuth resource row (see [OAuth](../../handbook/guides/oauth.md)).
30
25
  Verify cutover with actual app tests.
31
26
 
32
27
  Preserve the app router, verified identity and native resources. For selected
33
- Admin use the [native guide](../../handbook/guides/native-admin.md): three matching
34
- packages, generated checks and `entries(bindings.schemas, bindings.adapter)`.
28
+ Admin, follow the [native guide](../../handbook/guides/native-admin.md) for its
29
+ application-built bundle and service composition.
35
30
  Do not import old `/spec`, `/core` or runtime surface APIs. Review data/role
36
31
  migrations explicitly; full cross-target int64 parity and automated transfer
37
32
  are not this release's upgrade contract.
@@ -13,13 +13,8 @@ metadata:
13
13
 
14
14
  ## Workflow
15
15
 
16
- Read the installed SDK README and version-matched handbook before editing.
17
-
18
- Follow the installed [native generation and service workflow](../../handbook/cloudflare/service-entry.md)
19
- with its [native contract](../../handbook/guides/native-execution.md) and [supported declarations](../../handbook/reference/features.md). Commit
20
- all generated output, run the same generation command with `--check`, and keep
21
- bundle `dist/` untracked. The app owns its verified Caller, handlers and native
22
- resources; Better Auth’s admin plugin `adminRoles` defines the admin layer (default `["admin"]`); `ctx.admin` takes no role list ([authorization](../../handbook/cloudflare/authentication.md)).
16
+ Read the installed SDK README, then follow the version-matched [project and CLI
17
+ workflow](../../handbook/start/project-and-cli.md) before editing.
23
18
 
24
19
  Deploy/create resources only within the user's authorization; reuse configured
25
20
  host resources. Apply reviewed and rehearsed migrations before routing traffic;
@@ -30,12 +25,20 @@ as in [Workers assets and secrets](../../handbook/cloudflare/service-entry.md#wo
30
25
  Keep the Workers [public-fetch flags](../../handbook/cloudflare/deploy-and-operate.md#public-fetch-routing),
31
26
  including environment overrides; verify deployed MCP with a real OAuth bearer
32
27
  (`initialize` and `tools/list`), not only a browser session cookie.
28
+ For MCP Apps, also follow [deployed host acceptance](../../handbook/guides/mcp-apps.md#verify-a-deployed-app-in-chatgpt):
29
+ compare the current resource URI and visibly verify a newly invoked render tool.
30
+ Archive deployed Admin pages using the guide's `previous` recipe and carry them
31
+ through clean builds. Test the same old conversation after changing the UI build,
32
+ without reconnecting, refreshing host tools or clearing caches. A deliberate tool
33
+ contract change has its own migration/host rediscovery step. OAuth renewal, `tools/call` success and a
34
+ local official-host replay do not establish that the deployed App rendered.
33
35
  Bun uses native Database/SQL, an app server and scheduler. Verify admin sign-in
34
36
  (role admin), denied access, public reads, a persisted write, MCP and selected UI.
35
37
  Report artifact/resources/checks and keep rollback artifacts.
36
38
 
37
- Use the installed [Native Admin](../../handbook/guides/native-admin.md) asset
38
- contract and `entries(bindings.schemas, bindings.adapter)`. Admin SPA assets
39
- belong to `@aotter/mantle-admin`, while UI supplies the shared kit. Verify both
40
- member and admin OTP/UI flows. Deployment of an authorized application does
41
- not authorize SDK publication; a workspace preview remains a preview.
39
+ For ChatGPT Sites, follow [Mantle on ChatGPT Sites](../../handbook/cloudflare/chatgpt-sites.md).
40
+
41
+ The app builds Admin with `mantle bundle`; serve it as [Native
42
+ Admin](../../handbook/guides/native-admin.md) describes. Verify both member and
43
+ admin OTP/UI flows. Deployment of an authorized application does not authorize
44
+ SDK publication; a workspace preview remains a preview.
@@ -12,24 +12,25 @@ metadata:
12
12
 
13
13
  ## Workflow
14
14
 
15
- Read the installed SDK README and version-matched handbook before editing.
15
+ Read the installed SDK README, then follow the version-matched [project and CLI
16
+ workflow](../../handbook/start/project-and-cli.md) before editing.
16
17
 
17
- Follow the installed [native generation and service workflow](../../handbook/cloudflare/service-entry.md)
18
- with its [native contract](../../handbook/guides/native-execution.md) and [supported declarations](../../handbook/reference/features.md). Commit
19
- all generated output, run the same generation command with `--check`, and keep
20
- bundle `dist/` untracked. The app owns its verified Caller, handlers and native
21
- resources; Better Auth’s admin plugin `adminRoles` defines the admin layer (default `["admin"]`); `ctx.admin` takes no role list ([authorization](../../handbook/cloudflare/authentication.md)).
18
+ For 0.1.x, follow [Upgrade 0.1 to 0.2](../../upgrade-0.1-to-0.2.md): give the
19
+ 0.2 service a new database and move data by export and import; never boot 0.2
20
+ against a 0.1.x database. Between 0.2 versions, apply reviewed migrations
21
+ rehearsed on a copy.
22
22
 
23
- Pin exact target SDK/peer versions and read its release notes and Admin asset
24
- installation instructions. Core-to-native replaces app imports/wiring; it is not
25
- a package-only update. Preserve users, sessions, content and application code.
26
- Review emitted SQL/policy diffs and rehearse explicit data-preserving schema/auth/role
27
- migrations on a copy. Initial schema.sql never authorizes rebuilding production
28
- tables. Verify auth/Admin/MCP/REST, locks and persistence before authorized cutover.
23
+ Pin exact target SDK/peer versions and read its release notes and Admin build
24
+ instructions. Core-to-native replaces app imports/wiring; it is not
25
+ a package-only update. For 0.2-to-0.2 updates, preserve users, sessions, content
26
+ and application code; review emitted SQL/policy diffs and rehearse explicit
27
+ data-preserving schema/auth/role migrations on a copy. Initial schema.sql never
28
+ authorizes rebuilding production tables. Verify auth/Admin/MCP/REST, locks and
29
+ persistence before authorized cutover.
29
30
 
30
- Follow [Native Admin](../../handbook/guides/native-admin.md) for the three-package
31
- SDK/Admin/shared-kit split and asset/extension/MCP App subpath moves; old UI
32
- subpaths have no compatibility aliases. Pin only actually published versions,
33
- or explicitly use a matching workspace preview. Recheck member and Admin OTP,
34
- all affected native routes, private field visibility, OCC and unknown outcomes.
35
- Do not publish a package as part of an application dependency update.
31
+ Follow [Customising Admin](../../handbook/guides/native-admin.md#customising-admin)
32
+ for the current application-built Admin; old UI subpaths have no compatibility
33
+ aliases. Pin only actually published versions, or explicitly use a matching
34
+ workspace preview. Recheck member and Admin OTP, all affected native routes,
35
+ private field visibility, OCC and unknown outcomes. Do not publish a package as
36
+ part of an application dependency update.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aotter/mantle",
3
- "version": "0.2.0-alpha.8",
3
+ "version": "0.2.0-alpha.9",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "engines": {
@@ -92,7 +92,7 @@
92
92
  }
93
93
  },
94
94
  "devDependencies": {
95
- "@modelcontextprotocol/client": "2.1.0",
95
+ "@modelcontextprotocol/client": "2.2.0",
96
96
  "@modelcontextprotocol/server": "2.2.0",
97
97
  "@better-auth/core": "1.7.7",
98
98
  "@better-auth/oauth-provider": "1.7.7",
package/skills/README.md CHANGED
@@ -27,7 +27,7 @@ release tag as the application's SDK; alpha releases on `develop` can be newer
27
27
  than the default `main` branch. For a skill-only installation:
28
28
 
29
29
  ```sh
30
- npx skills add https://github.com/aotter/mantle/tree/v0.2.0-alpha.8 --skill mantle
30
+ npx skills add https://github.com/aotter/mantle/tree/v0.2.0-alpha.9 --skill mantle
31
31
  ```
32
32
 
33
33
  The explicit skill name excludes repository maintainer skills. The manifests
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mantle
3
- description: Create or continue a Mantle application, test it locally and deploy to a user-owned host using the installed SDK docs.
3
+ description: Create, continue, test or deploy a Mantle application using installed @aotter/mantle docs, including manifests, SQL Views and Procedures, MCP tools, app-built Admin, mantle generate and Mantle upgrades.
4
4
  metadata:
5
5
  source: "@aotter/mantle"
6
6
  sourcePath: skills/mantle/SKILL.md
@@ -21,10 +21,29 @@ checkout, or any online copy.
21
21
 
22
22
  Work in the user's application, outside the SDK checkout. Read package.json, the lockfile, installed SDK README and node_modules/@aotter/mantle/docs/. Those files define the exact version's contract. Preserve existing identity, handles, routes and resources.
23
23
 
24
+ ## Choose the installed package skill
25
+
26
+ - Read `node_modules/@aotter/mantle/docs/skills/develop/SKILL.md` to change an existing project's manifests, SQL, handlers, service entry or surfaces.
27
+ - Read `node_modules/@aotter/mantle/docs/skills/integrate/SKILL.md` to add Mantle to an application with existing code, users or data.
28
+ - Read `node_modules/@aotter/mantle/docs/skills/update/SKILL.md` to upgrade Mantle, including from 0.1.x.
29
+ - Read `node_modules/@aotter/mantle/docs/skills/provision/SKILL.md` only for an authorized production deployment.
30
+ - Read `node_modules/@aotter/mantle/docs/skills/media-gc/SKILL.md` only to audit or remove stale uncommitted media uploads.
31
+
24
32
  For a new project, install an exact published SDK version with matching selected Admin/UI packages. Node must be 22.22 or newer. Add TypeScript and target types; Workers also need Wrangler. `node_modules/@aotter/mantle/docs/examples/app-shell/package.json` lists the Wrangler, TypeScript and type-package versions this release is tested with. Optional auth/MCP peers use the installed SDK's package.json versions. Read the installed handbook's start/project-and-cli.md before choosing a target.
25
33
 
26
34
  For a new Cloudflare app, copy `node_modules/@aotter/mantle/docs/examples/app-shell/` first, then the closest example from `docs/examples/README.md` on top of it, and edit from there: manifests, `src/handlers.ts`, `src/routes.ts`, `src/schedule.ts`, `smoke.mjs`. Search for `AGENT:` to find what the app must fill in; the example pages are unstyled on purpose, so styling them is part of that work. Before reporting done, resolve each `AGENT:` or name the ones you left. Keep the two roles `admin` and `user` unless the user asks for more.
27
35
 
36
+ Before reporting any user-facing work as done, follow the installed
37
+ `node_modules/@aotter/mantle/docs/handbook/guides/agent-workflow-acceptance.md`:
38
+ run independent acceptance with a fresh agent on the surface its user uses.
39
+ This is not limited to MCP; if it was not run, say so explicitly and report the
40
+ work as UNVERIFIED rather than done. For MCP workflows, also run the guide's
41
+ design audit before implementing tools. This workflow practice applies to every
42
+ installed SDK version: if its guide is older or MCP-scoped, apply its fresh-agent
43
+ round loop to the user-facing surface actually built. If an older installed
44
+ version lacks this guide, record the missing guidance and preserve that version's
45
+ contract; do not silently upgrade or substitute checkout/online docs.
46
+
28
47
  For existing older applications, use their installed docs until the user requests an upgrade. For partial adoption read node_modules/@aotter/mantle/docs/skills/integrate/SKILL.md.
29
48
 
30
49
  ## Generated native applications
@@ -44,16 +63,16 @@ cookieCache `{enabled:true,maxAge:60,refreshCache:false,version:"read-60-v1"}` a
44
63
  no KV. Bump version when enabling/changing the policy; writes, Admin, consent,
45
64
  unknown/ref/media calls stay fresh. Read the installed auth docs before MCP caching.
46
65
 
47
- Use the workflow **generate → typecheck → implement listed handlers**. For example:
66
+ Use the workflow **generate → implement listed handlers → check**. In an app
67
+ copied from the installed app shell, its scripts are the canonical commands:
48
68
 
49
69
  ```sh
50
- pnpm exec mantle generate cloudflare-d1 generated manifests/*.yaml
51
- pnpm exec tsc --noEmit
52
- pnpm exec mantle generate cloudflare-d1 generated manifests/*.yaml --check
70
+ pnpm generate
71
+ pnpm check
53
72
  ```
54
- Implement every printed `Handlers` key
73
+ Implement every printed `Handlers` key
55
74
  and pass the implementations at `store(db, caller, { handlers })` (and selected
56
- surface options). Run `tsc --noEmit` again before local acceptance.
75
+ surface options).
57
76
  Generated Store metadata initializes on its first use, once per module. Bind
58
77
  `store(db, caller, options?)` for the current request; never reuse one caller's
59
78
  bound Store for another caller.
@@ -78,12 +97,12 @@ Resolve conflicts by merging manifests/options and regenerating. Add the printed
78
97
  `.gitattributes` line and run the same generate command with `--check` in CI.
79
98
  Compose the app-owned `fetch` using `docs/handbook/cloudflare/service-entry.md`: auth/consent, Admin,
80
99
  MCP, REST behind one request-local origin-checked resolver; keep heavy auth dynamic.
100
+ Pair every HTTP Trigger with an MCP Trigger by default; see `docs/handbook/reference/trigger.md` for the generate warning and deliberate asymmetry exception.
81
101
 
82
102
  For selected native Admin, follow the version-matched
83
- `docs/handbook/guides/native-admin.md`: select `--admin` (or `admin: true`),
84
- mount `entries(bindings.schemas, bindings.adapter)` and serve the Admin assets
85
- separately. The three-package preview is one Core SDK, the Admin product and the
86
- shared UI kit.
103
+ `docs/handbook/guides/native-admin.md`: select `--admin` (or `admin: true`), then
104
+ build and serve the application-owned Admin with `mantle bundle` as that guide
105
+ describes.
87
106
  Admin reads share generated whitelisted native View templates, writes use native
88
107
  Procedure fragments and generated checks. Ref operations retain typed `ctx.store`
89
108
  at request time. Verify member/frontend and admin/Admin OTP, tenant/privacy/TTL,
@@ -128,8 +147,11 @@ To add Mantle to an application that has its own code, users and data, read
128
147
  `node_modules/@aotter/mantle/docs/skills/integrate/SKILL.md` first.
129
148
 
130
149
  Report the project path, the exact Mantle version, what you changed, the checks
131
- you ran with their results, and anything left for the user.
150
+ you ran with their results, the independent acceptance verdict (CONVERGED / NOT
151
+ CONVERGED / BLOCKED / UNVERIFIED), and anything left for the user.
132
152
 
133
153
  ## Data paths
134
154
 
135
- Writes use Store `insert` / `update` / `delete`, atomic `batch` / supported `transaction`, or declared Procedures. Raw SQL writes bypass row rules. Use internal Views for joins, aggregates, OR and reusable reads; Store `findById` / `find` / `first` are for id lookups and simple AND filters. Ref handlers receive `ctx.store` with the same Caller and transaction.
155
+ Follow the installed `docs/handbook/guides/typed-queries.md` for Store,
156
+ Procedure and View data paths; the service composition guide owns request-local
157
+ Caller binding.
@@ -13,3 +13,8 @@ Better Auth limits each email-OTP path to 3 requests/minute per IP; `authHardeni
13
13
  - For selected remote MCP, use the installed `docs/examples/app-shell/smoke-kit.mjs` `assertMcpDiscovery(endpoint, { tools, headers })` in the application's smoke scenario. Derive `tools` from the generated `mcp.ts` exports, or the pure static `developerConsole.interfaces.callable` in generated `admin-descriptors.ts`; filter to the selected surface and explicitly select the expected permitted subset when application authorization narrows it. Include app-composed tools when present. Do not count `describe.targets` or reconstruct authorization rules in the harness. Assert initialized discovery and all `tools/list` pages; a 404, HTML 200, protocol error or missing expected tools must fail. Keep anonymous/member denial assertions separate. Discovery never needs a business-write `tools/call`.
14
14
  - Supply test credentials from the application's normal authorized sign-in/test setup (the smoke kit's console OTP sign-in is valid). The discovery assertion only receives credentials: it must not acquire credentials itself, scrape other sessions or manufacture authority. Report local cookie-session discovery, OAuth Bearer/client integration and browser WebMCP as separate evidence. Cookie or WebMCP success does not prove remote OAuth/ChatGPT wiring; missing credentials leave that case unverified, not passed. Do not enable remote routes or OAuth merely to make acceptance pass.
15
15
  - Record commands and passed/failed/blocked cases for the exact commit. Fix failures before source admission; rerun relevant checks after changing source. Packing does not run these checks and an agent's checklist is not server build attestation.
16
+
17
+ Independent acceptance for every user-facing workflow follows the installed
18
+ `docs/handbook/guides/agent-workflow-acceptance.md`. Scripted smoke is separate
19
+ evidence; browser or host rendering is the acceptance surface for UI and MCP App
20
+ deliverables.
@@ -7,8 +7,8 @@ selected host's frontend kit before choosing APIs. Native applications use
7
7
  their installed `docs/handbook/guides/native-admin.md` and the
8
8
  [web contract](web.md).
9
9
 
10
- - For self-hosted applications, mount the selected Admin API and serve the
11
- separate Admin package's assets with its shell/CSP contract. The application
10
+ - For self-hosted applications, build Admin with `mantle bundle` and serve it as
11
+ the installed `docs/handbook/guides/native-admin.md` describes. The application
12
12
  owns auth, OTP delivery and verified Caller wiring; use a console/test sender
13
13
  locally and configure production email for deployment. If a host already
14
14
  supplies identity/Admin, reuse its explicit contract and reserved routes.
@@ -38,8 +38,10 @@ Follow the [local HTTP acceptance](local-testing.md) before reporting local
38
38
  readiness: inspect HTML without JavaScript, check metadata/sitemap and anonymous
39
39
  draft/permission refusal, and exercise OTP and editing with isolated synthetic
40
40
  accounts. Local readiness does not require publication, a hosted tenant or a
41
- candidate preview. Report production email, public origin and real object-store
42
- upload/commit checks separately when local capabilities cannot exercise them.
41
+ candidate preview. Scripted local acceptance does not replace the installed
42
+ independent acceptance guide, which is required before reporting the pages done.
43
+ Report production email, public origin and real object-store upload/commit checks
44
+ separately when local capabilities cannot exercise them.
43
45
 
44
46
  For an explicitly requested deployment, use the selected host's native workflow
45
47
  and any preview it provides. Verify disabled preview capabilities on an already