@frontmcp/skills 1.4.0 → 1.5.0-rc.1

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.
@@ -47,12 +47,15 @@ export default MyServer;
47
47
  ```
48
48
 
49
49
  ```toml
50
- # wrangler.toml — the Cloudflare adapter overwrites this on every build
51
- # with exactly these three lines (configure name/compatibility_date via
52
- # frontmcp.config.deployments[].wrangler).
50
+ # wrangler.toml — the Cloudflare adapter overwrites these top-level keys on
51
+ # every build (configure name/compatibility_date/compatibility_flags via
52
+ # frontmcp.config.deployments[].wrangler). nodejs_compat is always emitted —
53
+ # the worker entry require()s @frontmcp/sdk + Express, so without it the
54
+ # Worker fails to boot.
53
55
  name = "frontmcp-worker"
54
56
  main = "dist/cloudflare/index.js"
55
- compatibility_date = "2024-01-01"
57
+ compatibility_date = "2024-09-23"
58
+ compatibility_flags = ["nodejs_compat"]
56
59
  ```
57
60
 
58
61
  ```bash
@@ -55,12 +55,15 @@ export default TranslateServer;
55
55
  ```
56
56
 
57
57
  ```toml
58
- # wrangler.toml — name/compatibility_date are managed by frontmcp.config;
59
- # bindings appended below are re-applied after each build (the adapter
60
- # overwrites the top-level keys on every `frontmcp build --target cloudflare`).
58
+ # wrangler.toml — name/compatibility_date/compatibility_flags are managed by
59
+ # frontmcp.config; bindings appended below are re-applied after each build (the
60
+ # adapter overwrites the top-level keys on every `frontmcp build --target
61
+ # cloudflare`). nodejs_compat is always emitted — the worker entry needs Node
62
+ # builtins or it won't boot.
61
63
  name = "translate-worker"
62
64
  main = "dist/cloudflare/index.js"
63
- compatibility_date = "2024-01-01"
65
+ compatibility_date = "2024-09-23"
66
+ compatibility_flags = ["nodejs_compat"]
64
67
 
65
68
  [[kv_namespaces]]
66
69
  binding = "FRONTMCP_KV"
@@ -49,11 +49,14 @@ export default MyServer;
49
49
  ```
50
50
 
51
51
  ```toml
52
- # wrangler.toml — name/main/compatibility_date are rewritten by the build.
53
- # Bindings below need to be re-applied (or appended) after each build.
52
+ # wrangler.toml — name/main/compatibility_date/compatibility_flags are
53
+ # rewritten by the build (nodejs_compat is always emitted; the worker entry
54
+ # needs Node builtins or it won't boot). Bindings below need to be re-applied
55
+ # (or appended) after each build.
54
56
  name = "frontmcp-worker"
55
57
  main = "dist/cloudflare/index.js"
56
- compatibility_date = "2024-01-01"
58
+ compatibility_date = "2024-09-23"
59
+ compatibility_flags = ["nodejs_compat"]
57
60
 
58
61
  [[kv_namespaces]]
59
62
  binding = "FRONTMCP_KV"
@@ -1,14 +1,34 @@
1
1
  ---
2
2
  name: deploy-to-cloudflare-skills-only
3
- description: Deploy a FrontMCP server to Cloudflare Workers using the v1.3 skills-only model OpenAPI as capability inventory, AgentScript with namespaced bindings, four meta-tools, hot-reload via GitHub Action and a signed-bundle webhook
3
+ description: Deploy an auto-updating FrontMCP server to Cloudflare Workers with @frontmcp/edge createEdgeMcp managed skilled-OpenAPI bundle pulled from a SaaS endpoint, cached in KV, refreshed on a Cron Trigger
4
4
  ---
5
5
 
6
- # Deploy to Cloudflare Workers (Skills-Only Model)
7
-
8
- The v1.3 Cloudflare Worker target hosts FrontMCP as a control plane where the MCP surface is just **four meta-tools** (`searchSkills`, `searchKnowledge`, `describe`, `execute`) and every capability is reached through a skill. OpenAPI specs ship to the project but are NEVER directly exposed — they are the capability inventory, classified by HTTP semantics into resources / tools, with auto-derived `notifications/resources/*` events.
6
+ # Deploy to Cloudflare Workers (Managed / Skills-Only Model)
7
+
8
+ > **⚠️ Status experimental.** `@frontmcp/edge` `createEdgeMcp` **deploys and
9
+ > serves on real Cloudflare** (verified live), as long as you (1) keep
10
+ > `serve: false` (now the createEdgeMcp default) and (2) stub three Node-only
11
+ > transports your bundler statically includes but the edge never uses —
12
+ > `express`, `raw-body`, `cross-spawn`. NOTE: **miniflare local-dev is stricter
13
+ > than production** and rejects `node:http2`/`node:fs` that real Cloudflare's
14
+ > `nodejs_compat` provides, so the local managed e2e is skipped even though the
15
+ > package runs in production. Managed mode (this page) additionally needs a SaaS
16
+ > bundle endpoint + the optional peer `@frontmcp/plugin-skilled-openapi`. The
17
+ > worker-conditioned SDK build (roadmap) removes the manual stubs. For the
18
+ > simplest production path, the decorator build in
19
+ > [`deploy-to-cloudflare.md`](./deploy-to-cloudflare.md) needs none of this.
20
+ > The GitHub Action / signed-resync-webhook / Durable Object stores / Frontegg
21
+ > edge auth described below remain ROADMAP — not yet implemented.
22
+
23
+ The managed model hosts FrontMCP where the MCP surface is a small set of
24
+ meta-tools (`search_skill`, `load_skill`, `run_workflow`) and every capability
25
+ is reached through a skill compiled from an OpenAPI spec. `run_workflow` runs a
26
+ short AgentScript program in the Worker isolate, where each `callTool(actionId,
27
+ input)` invokes a loaded skill's operation. The bundle is pulled from a SaaS
28
+ endpoint, cached in KV, and refreshed on a Cron Trigger.
9
29
 
10
30
  For the conceptual picture, see [Skills-Only Deployment](https://docs.agentfront.dev/frontmcp/features/skills-only-deployment).
11
- For the older Express-to-Workers adapter, see [`deploy-to-cloudflare.md`](./deploy-to-cloudflare.md).
31
+ For the production-ready decorator build, see [`deploy-to-cloudflare.md`](./deploy-to-cloudflare.md).
12
32
 
13
33
  ## When to Use This Skill
14
34
 
@@ -32,21 +52,47 @@ For the older Express-to-Workers adapter, see [`deploy-to-cloudflare.md`](./depl
32
52
  ## Worker Entry File
33
53
 
34
54
  ```ts
35
- // worker.ts (~10 lines)
36
- import { createWorker } from '@frontmcp/worker';
55
+ // worker.ts — the real API is createEdgeMcp (not createWorker)
56
+ import { createEdgeMcp, kvBundleCacheFromEnv } from '@frontmcp/edge';
57
+
58
+ export default createEdgeMcp({
59
+ info: { name: 'my-worker', version: '1.0.0' },
60
+ apps: [],
61
+ tasks: { enabled: false },
62
+ managed: {
63
+ endpoint: 'https://cloud.example.com/v1/bundles/acme',
64
+ authToken: 'pinned-pull-token',
65
+ expectedAudience: 'acme-mcp',
66
+ jwksUrl: 'https://cloud.example.com/.well-known/jwks.json',
67
+ expectedIssuer: 'https://cloud.example.com',
68
+ // KV-backed last-good cache, resolved from the per-request `env`.
69
+ cache: kvBundleCacheFromEnv('BUNDLE_CACHE'),
70
+ },
71
+ });
72
+ ```
37
73
 
38
- import deployBundle from './frontmcp.deploy.bundle.js'; // emitted by the GH Action
74
+ `createEdgeMcp` returns `{ fetch, scheduled }`: `fetch` serves MCP; `scheduled`
75
+ is the **Cron Trigger** entrypoint that pulls a fresh bundle and hot-swaps it.
76
+ Managed mode requires the optional peer `@frontmcp/plugin-skilled-openapi`.
39
77
 
40
- const { handler, durableObjects } = createWorker({
41
- bundle: deployBundle,
42
- env: 'production',
43
- });
78
+ This path is bundled by **wrangler** (not `frontmcp build`), so you maintain
79
+ `wrangler.toml` yourself — it needs a `[[kv_namespaces]] binding = "BUNDLE_CACHE"`
80
+ and a `[triggers] crontabs = [...]` (the `managed.pollIntervalMs` option is
81
+ ignored on edge — Workers have no background timers; the Cron drives refresh):
44
82
 
45
- export default handler;
46
- export const { SessionDO, EventStoreDO, BundleDO } = durableObjects;
47
- ```
83
+ ```toml
84
+ name = "my-worker"
85
+ main = "worker.ts"
86
+ compatibility_date = "2024-09-23"
87
+ compatibility_flags = ["nodejs_compat"]
48
88
 
49
- `createWorker` parses the manifest, applies the `environments.production` overlay, verifies the signed envelope against `TRUSTED_KEYS`, and assembles the FrontMCP runtime.
89
+ [[kv_namespaces]]
90
+ binding = "BUNDLE_CACHE"
91
+ id = "<your-kv-namespace-id>"
92
+
93
+ [triggers]
94
+ crontabs = ["*/5 * * * *"]
95
+ ```
50
96
 
51
97
  ## Storage Layout (Opinionated Default)
52
98
 
@@ -92,7 +138,11 @@ Day-to-day skill / OpenAPI edits are pure hot-reload. `wrangler deploy` is only
92
138
 
93
139
  `@enclave-vm/core` (full VM) needs `node:vm` and is NOT Worker-safe — the Worker target uses AST-preflight + frozen scope only.
94
140
 
95
- ## Auth at the Edge
141
+ ## Auth at the Edge 🚧 Roadmap — not yet implemented
142
+
143
+ > The Frontegg **edge** JWT verification below is a ROADMAP shape, not a shipped
144
+ > feature. Don't wire it expecting edge-native verification today; use the
145
+ > standard auth providers via the decorator build until this lands.
96
146
 
97
147
  ```yaml
98
148
  auth:
@@ -126,7 +176,11 @@ wrangler secret put ACME_API_TOKEN
126
176
 
127
177
  The cross-validator REJECTS any manifest that references a secret name not declared in `secrets[]`.
128
178
 
129
- ## Sample `.github/workflows/deploy.yml`
179
+ ## Sample `.github/workflows/deploy.yml` 🚧 Roadmap — not yet implemented
180
+
181
+ > The packaged GitHub Action and the signed-resync webhook are ROADMAP. The
182
+ > workflow below is an illustrative target, not a copy-paste-ready pipeline —
183
+ > deploy manually with `wrangler deploy` until the Action ships.
130
184
 
131
185
  ```yaml
132
186
  name: Deploy
@@ -64,19 +64,20 @@ wrangler.toml # Wrangler configuration (overwritten on every build)
64
64
 
65
65
  Cloudflare Workers use CommonJS (not ESM). The build command sets `--module commonjs` automatically.
66
66
 
67
- > **Important:** The Cloudflare adapter sets `alwaysWriteConfig: true` and overwrites the entire `wrangler.toml` on every build with the three-line template below. Hand-edited bindings (`[[kv_namespaces]]`, `[vars]`, `[[d1_databases]]`, etc.) WILL be erased the next time you run `frontmcp build --target cloudflare`. Configure `name` and `compatibility_date` via your `frontmcp.config` file's `deployments[].wrangler` section, and keep bindings in a separate config file referenced from your toolchain (or re-add them after each build).
67
+ > **Important:** The Cloudflare adapter sets `alwaysWriteConfig: true` and overwrites the entire `wrangler.toml` on every build with the template below. Hand-edited bindings (`[[kv_namespaces]]`, `[vars]`, `[[d1_databases]]`, etc.) WILL be erased the next time you run `frontmcp build --target cloudflare`. Configure `name`, `compatibility_date`, and extra `compatibility_flags` via your `frontmcp.config` file's `deployments[].wrangler` section, and keep bindings in a separate config file referenced from your toolchain (or re-add them after each build).
68
68
 
69
69
  ## Step 3: Configure wrangler.toml
70
70
 
71
- The build always writes exactly this:
71
+ The build always writes this. `compatibility_flags = ["nodejs_compat"]` is **always** emitted — the worker entry is an ES Module that imports `@frontmcp/sdk`'s web-fetch handler, which still transitively pulls in Node builtins (no Express on the Worker), so without the flag the deployed Worker fails to load. The default `compatibility_date` is `2024-09-23` (the date that enables full `nodejs_compat`). `main` is `dist/cloudflare/index.js`.
72
72
 
73
73
  ```toml
74
74
  name = "frontmcp-worker"
75
75
  main = "dist/cloudflare/index.js"
76
- compatibility_date = "2024-01-01"
76
+ compatibility_date = "2024-09-23"
77
+ compatibility_flags = ["nodejs_compat"]
77
78
  ```
78
79
 
79
- `name` and `compatibility_date` come from `frontmcp.config.{ts,js}`'s `deployments` array. Example:
80
+ `name`, `compatibility_date`, and any extra `compatibilityFlags` come from `frontmcp.config.{ts,js}`'s `deployments` array (`nodejs_compat` is merged in automatically). Example:
80
81
 
81
82
  ```ts
82
83
  // frontmcp.config.ts
@@ -87,6 +88,8 @@ export default {
87
88
  wrangler: {
88
89
  name: 'my-worker',
89
90
  compatibilityDate: '2025-01-15',
91
+ // Optional — nodejs_compat is always added for you.
92
+ compatibilityFlags: ['nodejs_compat_populate_process_env'],
90
93
  },
91
94
  },
92
95
  ],
@@ -99,6 +102,7 @@ To add KV storage or other bindings, append them AFTER each build (or use a wrap
99
102
  name = "my-worker"
100
103
  main = "dist/cloudflare/index.js"
101
104
  compatibility_date = "2025-01-15"
105
+ compatibility_flags = ["nodejs_compat"]
102
106
 
103
107
  [[kv_namespaces]]
104
108
  binding = "FRONTMCP_KV"
@@ -170,12 +174,57 @@ curl -X POST https://frontmcp-worker.your-subdomain.workers.dev/mcp \
170
174
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
171
175
  ```
172
176
 
177
+ ## Endpoint path, CORS & SSE — config-driven
178
+
179
+ The worker's transport is driven by the standard `http` + `transport` config (the same fields the Express host reads), so behaviour is identical on both adapters. The worker serves MCP at **exactly one path** — `http.entryPath` (the worker root `/` when unset) — not a guessed `/` + `/mcp` set. Cloudflare never strips the path before it reaches the worker:
180
+
181
+ | Clients use | `http.entryPath` | `wrangler.toml` route | Worker serves |
182
+ | --- | --- | --- | --- |
183
+ | `https://mcp.example.com` (subdomain) | omit (`/`) | `routes = [{ pattern = "mcp.example.com", custom_domain = true }]` | `/` |
184
+ | `https://example.com/mcp` (path) | `'/mcp'` | `routes = [{ pattern = "example.com/mcp*", zone_name = "example.com" }]` | `/mcp` |
185
+
186
+ ```ts
187
+ createEdgeMcp({
188
+ /* …info, apps… */
189
+ http: {
190
+ entryPath: '/mcp', // the ONE path MCP is served at (omit → root '/')
191
+ cors: { origin: true }, // browser MCP clients (e.g. Inspector "Direct" mode); { origin, credentials, maxAge }
192
+ },
193
+ // transport: 'legacy'/'modern' → SSE streaming on POST; 'stateless-api' → buffered JSON.
194
+ });
195
+ ```
196
+
197
+ - **CORS** ← `http.cors` (`false` disables; a function `origin` is unsupported on the worker — use `true` / string / `string[]`).
198
+ - **SSE** ← derived from the transport protocol (streaming under `legacy`/`modern`, buffered JSON under `stateless-api`); server→client `GET` streams are always honored.
199
+ - A trailing slash is normalized (`/mcp/` matches `/mcp`); `/healthz` + `/readyz` always answer a liveness 200 regardless of `entryPath`.
200
+
173
201
  ## Workers Limitations
174
202
 
175
203
  - **Bundle size**: Workers have a 1 MB compressed / 10 MB uncompressed limit (paid plan: 10 MB / 30 MB). Review dependencies and remove unused packages to reduce bundle size.
176
204
  - **CPU time**: 10 ms CPU time on free plan, 30 seconds on paid. Long-running operations must be optimized or use Durable Objects.
177
205
  - **No native modules**: `better-sqlite3` and other native Node.js modules are not available. Use KV, D1, or Upstash Redis for storage.
178
- - **Streaming**: SSE streaming may have limitations through the Workers adapter. Test thoroughly.
206
+ - **Streaming**: Streamable HTTP works, including SSE responses (`POST` with `Accept: text/event-stream`) and the server→client SSE `GET` stream. The worker uses the SDK's `WebStandardStreamableHTTPServerTransport` — which **is** the standard Streamable HTTP transport (the Node `StreamableHTTPServerTransport` is a thin `req`/`res` wrapper over it, so there's one engine). **Server→client notifications** on the standalone `GET` stream require **stateful sessions** — set `sessions: {}` and bind the `SessionDurableObject` (Durable Object) per `Mcp-Session-Id`. Without it the worker is stateless and the `GET` stream can't deliver pushed notifications.
207
+
208
+ ### Stateful sessions (Durable Object)
209
+
210
+ ```ts
211
+ const mcp = createEdgeMcp({ /* …info, apps… */ http: { entryPath: '/mcp' }, sessions: {} });
212
+ export default mcp;
213
+ export const FrontMcpSession = mcp.SessionDurableObject;
214
+ ```
215
+
216
+ ```toml
217
+ [[durable_objects.bindings]]
218
+ name = "FRONTMCP_SESSIONS"
219
+ class_name = "FrontMcpSession"
220
+ [[migrations]]
221
+ tag = "v1"
222
+ new_classes = ["FrontMcpSession"]
223
+ [vars]
224
+ MCP_SESSION_SECRET = "..." # required on production isolates; bridged into process.env
225
+ ```
226
+
227
+ One DO per session holds a persistent transport so the `GET` notification stream stays open and `tools/call` notifications reach it. It runs the **same `http:request` flow** (auth/session:verify/router/audit/metrics + hooks) as the stateless path — so transparent auth returns `401` + `WWW-Authenticate` on the worker too.
179
228
 
180
229
  ## Storage Options
181
230
 
@@ -190,16 +239,16 @@ curl -X POST https://frontmcp-worker.your-subdomain.workers.dev/mcp \
190
239
  | Problem | Cause | Solution |
191
240
  | ----------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------- |
192
241
  | Worker exceeds size limit | Too many bundled dependencies | Review dependencies and remove unused packages to reduce bundle size |
193
- | Module format errors | `wrangler.toml` sets `type = "module"` | Remove the `type` field; FrontMCP Cloudflare builds use CommonJS |
242
+ | Module format errors | Worker bundled as a Service Worker | FrontMCP Cloudflare builds emit an **ES Module Worker** (`export default { fetch }`); `nodejs_compat` requires it. Don't force `type`/CommonJS |
194
243
  | KV binding errors | Namespace not created or binding name mismatch | Run `wrangler kv:namespace create` and copy the `id` into `wrangler.toml` |
195
244
  | Timeout errors | CPU time exceeds plan limit | Upgrade plan or offload heavy computation to Durable Objects |
196
- | CORS failures on MCP endpoint | Missing CORS headers in Worker response | Add CORS middleware or headers in your FrontMCP server configuration |
245
+ | CORS failures on MCP endpoint | Missing CORS headers in Worker response | `@frontmcp/edge`: pass `cors: { origin: true }` to `createEdgeMcp({...})` (transport-level CORS) |
197
246
 
198
247
  ## Common Patterns
199
248
 
200
249
  | Pattern | Correct | Incorrect | Why |
201
250
  | ------------------ | -------------------------------------------------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
202
- | Module format | CommonJS (`main = "dist/cloudflare/index.js"`) | ESM (`type = "module"`) | FrontMCP Cloudflare builds emit CommonJS at this exact path; the build overwrites `wrangler.toml` to enforce it |
251
+ | Module format | ES Module Worker (`main = "dist/cloudflare/index.js"`, `export default { fetch }`) | Service Worker / forced CommonJS | FrontMCP Cloudflare builds emit an ES Module Worker at this exact path; the build overwrites `wrangler.toml`. `nodejs_compat` requires the Module shape |
203
252
  | Transport key | `transport: { protocol: 'modern' }` (or `{ sse: true, streamable: true }`) | `transport: { type: 'sse' }` | The schema field is `protocol`; valid presets are `'legacy' \| 'modern' \| 'stateless-api' \| 'full'`, or pass a `ProtocolConfig` object |
204
253
  | Storage binding | `[[kv_namespaces]]` with matching `binding` | Hardcoded KV namespace ID in code | Bindings are injected at runtime by Workers |
205
254
  | Compatibility date | Set via `frontmcp.config.deployments[].wrangler.compatibilityDate` | Hand-editing `wrangler.toml` | The build overwrites `wrangler.toml`; config-driven values survive |
@@ -215,7 +264,7 @@ curl -X POST https://frontmcp-worker.your-subdomain.workers.dev/mcp \
215
264
 
216
265
  **Configuration**
217
266
 
218
- - [ ] `wrangler.toml` has correct `name`, `main`, and `compatibility_date`
267
+ - [ ] `wrangler.toml` has correct `name`, `main`, `compatibility_date`, and `compatibility_flags = ["nodejs_compat"]`
219
268
  - [ ] KV namespace IDs match between dashboard and `wrangler.toml`
220
269
  - [ ] Secrets are stored via `wrangler secret put`, not in `[vars]`
221
270
 
@@ -66,6 +66,38 @@ These are the flow names with pre-built hook decorator exports in `@frontmcp/sdk
66
66
  | `channels:send-notification` | Channel notification send | `ChannelSendHook` |
67
67
  | `channels:list` | Channel listing | `ChannelListHook` |
68
68
 
69
+ ## Strict architecture: flows are the only path — never bypass them
70
+
71
+ This is the load-bearing invariant behind every hook above: in FrontMCP **every
72
+ request runs through a flow**, and because flows are made of `@Stage` steps that
73
+ `FlowHooksOf` exposes for interception, hooks work *everywhere* automatically.
74
+ The hookability is only guaranteed because nothing handles a request outside a
75
+ flow.
76
+
77
+ Therefore:
78
+
79
+ - **Never bypass the flow pipeline to make something work.** Add or extend a flow
80
+ + its stages; do not hand-roll request logic (auth, transport, routing) in a
81
+ transport/adapter that skips the flow. A bypass silently deletes every hook on
82
+ that path.
83
+ - **Adapters only translate.** A transport adapter (Express, the Web-fetch/worker
84
+ handler, stdio) converts its native request/response to the flow's normalized
85
+ `ServerRequest` + `httpRespond` output and then runs the **same** flows. Two
86
+ adapters must never diverge in behavior (e.g. one enforcing auth, another not).
87
+ - **Fix runtime gaps in the flow, not around it.** If a flow stage can't run in a
88
+ target runtime (e.g. a stage needs a Node `ServerResponse` but a Worker only has
89
+ Web `Request`/`Response`), make the stage runtime-agnostic (emit normalized
90
+ output each adapter renders) — do not write a runtime-specific shortcut that
91
+ skips the flow.
92
+ - **Cross-cutting concerns are stages, not inlined code.** Auth, quota, audit,
93
+ metrics belong to flow stages (so they're hookable), never re-implemented inside
94
+ an adapter.
95
+ - **Use `FlowInputOf` / `FlowOutputOf`**, never ad-hoc `as { … }` casts on flow
96
+ results — a cast is a sign you're working around the flow instead of with it.
97
+
98
+ If a change handles a request without going through a flow, or inlines a
99
+ cross-cutting concern, it's wrong — rework it through a hookable flow.
100
+
69
101
  ## Server Lifecycle Hooks
70
102
 
71
103
  In addition to flow-based hooks, the framework exposes a single `scope.onServerStarted(callback)` API for post-startup work. Callbacks register against the active `ScopeEntry` and run after `server.start()` completes.
@@ -6,7 +6,7 @@ tags: [extensibility, audit, skills, tamper-evident, signature, chain]
6
6
 
7
7
  # Skill Audit Log
8
8
 
9
- The `@frontmcp/adapters/skills` module provides a tamper-evident, hash-chained audit log for skill action executions. Every authority pass / authority fail / HTTP success / HTTP failure phase emitted by `execute-action.tool.ts` is captured, signed, and chained so any later mutation breaks signature verification.
9
+ The `@frontmcp/adapters/skills` module provides a tamper-evident, hash-chained audit log for skill action executions. Every authority pass / authority fail / HTTP success / HTTP failure phase emitted by the skill-action executor (`run_workflow`'s `callTool`) is captured, signed, and chained so any later mutation breaks signature verification.
10
10
 
11
11
  ## Architecture
12
12
 
@@ -15,7 +15,7 @@ phase method assembles its payload internally and routes through a shared
15
15
  chain pipeline:
16
16
 
17
17
  ```text
18
- ExecuteActionTool.execute()
18
+ run_workflow → callTool(action)
19
19
  ├── writer.writeAuthorityPass(ctx) // authority-check-pass
20
20
  ├── writer.writeAuthorityFail(ctx, { reason }) // authority-check-fail
21
21
  ├── writer.writeHttpCallSuccess(ctx, { status, output }) // http-call-success
@@ -19,10 +19,13 @@ Shows how to use Cloudflare Durable Objects for stateful coordination alongside
19
19
 
20
20
  ```toml
21
21
  # wrangler.toml — with Durable Objects and R2
22
- # NOTE: `main` is written by `frontmcp build --target cloudflare`. Do not hand-edit.
22
+ # NOTE: `main`, `compatibility_date`, and `compatibility_flags` are written by
23
+ # `frontmcp build --target cloudflare`. Do not hand-edit. nodejs_compat is
24
+ # required — the worker entry needs Node builtins or it won't boot.
23
25
  name = "stateful-mcp-worker"
24
26
  main = "dist/cloudflare/index.js"
25
- compatibility_date = "2024-01-01"
27
+ compatibility_date = "2024-09-23"
28
+ compatibility_flags = ["nodejs_compat"]
26
29
 
27
30
  # KV for cache
28
31
  [[kv_namespaces]]
@@ -46,7 +46,8 @@ Checklist for verifying the `wrangler.toml` produced by `frontmcp build --target
46
46
 
47
47
  - [ ] No secrets in `wrangler.toml` — all set via `wrangler secret put <NAME>`
48
48
  - [ ] `[vars]` block contains only non-sensitive config (region names, feature flags)
49
- - [ ] `compatibility_date` set to a recent date and locked
49
+ - [ ] `compatibility_date` set to a recent date and locked (the build defaults to `2024-09-23`, the date that enables full `nodejs_compat`)
50
+ - [ ] `compatibility_flags` includes `nodejs_compat` — the build emits it automatically; without it the deployed Worker fails to boot (`require()`/`node:*` are unavailable)
50
51
 
51
52
  ## Deploy & observability
52
53
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/skills",
3
- "version": "1.4.0",
3
+ "version": "1.5.0-rc.1",
4
4
  "description": "Curated skills catalog for FrontMCP projects",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",