@frontmcp/skills 1.2.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +38 -29
  2. package/catalog/TEMPLATE.md +26 -0
  3. package/catalog/create-tool/SKILL.md +318 -0
  4. package/catalog/create-tool/examples/01-basic-class-tool.md +112 -0
  5. package/catalog/create-tool/examples/02-basic-function-tool.md +80 -0
  6. package/catalog/create-tool/examples/03-tool-with-zod-shape-output.md +78 -0
  7. package/catalog/create-tool/examples/04-tool-with-zod-schema-output.md +97 -0
  8. package/catalog/create-tool/examples/05-tool-with-primitive-output.md +93 -0
  9. package/catalog/create-tool/examples/06-tool-with-media-output.md +109 -0
  10. package/catalog/create-tool/examples/08-tool-with-provider-injection.md +110 -0
  11. package/catalog/create-tool/examples/09-tool-with-multiple-providers.md +107 -0
  12. package/catalog/create-tool/examples/11-tool-with-fetch.md +94 -0
  13. package/catalog/create-tool/examples/12-tool-with-fetch-and-retries.md +115 -0
  14. package/catalog/create-tool/examples/13-tool-with-single-auth-provider.md +85 -0
  15. package/catalog/create-tool/examples/14-tool-with-multiple-auth-providers.md +105 -0
  16. package/catalog/create-tool/examples/15-tool-with-credential-vault.md +115 -0
  17. package/catalog/create-tool/examples/16-tool-with-rate-limit.md +71 -0
  18. package/catalog/create-tool/examples/17-tool-with-concurrency-and-timeout.md +101 -0
  19. package/catalog/create-tool/examples/18-tool-with-progress-and-notify.md +96 -0
  20. package/catalog/create-tool/examples/19-tool-with-elicitation.md +102 -0
  21. package/catalog/create-tool/examples/20-tool-with-annotations.md +125 -0
  22. package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +107 -0
  23. package/catalog/create-tool/examples/22-tool-with-ui-html-template.md +93 -0
  24. package/catalog/create-tool/examples/23-tool-with-ui-filesource-tsx.md +112 -0
  25. package/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md +127 -0
  26. package/catalog/create-tool/examples/25-tool-handing-off-to-job.md +143 -0
  27. package/catalog/create-tool/examples/26-tool-with-resource-link-output.md +94 -0
  28. package/catalog/create-tool/examples/27-tool-with-examples-metadata.md +90 -0
  29. package/catalog/create-tool/references/annotations.md +96 -0
  30. package/catalog/create-tool/references/auth-providers.md +167 -0
  31. package/catalog/create-tool/references/availability.md +106 -0
  32. package/catalog/create-tool/references/decorator-options.md +95 -0
  33. package/catalog/create-tool/references/derived-types.md +102 -0
  34. package/catalog/create-tool/references/elicitation.md +128 -0
  35. package/catalog/create-tool/references/error-handling.md +128 -0
  36. package/catalog/create-tool/references/execution-context.md +158 -0
  37. package/catalog/create-tool/references/file-layout.md +96 -0
  38. package/catalog/create-tool/references/function-style-builder.md +118 -0
  39. package/catalog/create-tool/references/input-schema.md +141 -0
  40. package/catalog/create-tool/references/output-schema.md +175 -0
  41. package/catalog/create-tool/references/quick-start.md +124 -0
  42. package/catalog/create-tool/references/registration.md +132 -0
  43. package/catalog/create-tool/references/remote-and-esm.md +68 -0
  44. package/catalog/create-tool/references/testing.md +59 -0
  45. package/catalog/create-tool/references/throttling.md +109 -0
  46. package/catalog/create-tool/references/ui-widgets.md +198 -0
  47. package/catalog/create-tool/rules/always-define-output-schema.md +77 -0
  48. package/catalog/create-tool/rules/derive-execute-types.md +57 -0
  49. package/catalog/create-tool/rules/input-schema-is-raw-shape.md +76 -0
  50. package/catalog/create-tool/rules/no-toolcontext-generics.md +50 -0
  51. package/catalog/create-tool/rules/no-try-catch-around-execute.md +79 -0
  52. package/catalog/create-tool/rules/register-in-app.md +76 -0
  53. package/catalog/create-tool/rules/snake-case-tool-names.md +45 -0
  54. package/catalog/create-tool/rules/use-this-fail-for-business-errors.md +75 -0
  55. package/catalog/create-tool/rules/widget-paths-anchor-with-import-meta-url.md +76 -0
  56. package/catalog/create-tool/rules/widget-resource-mode-host-detect.md +61 -0
  57. package/catalog/frontmcp-auth-ui/SKILL.md +146 -0
  58. package/catalog/frontmcp-auth-ui/examples/custom-auth-ui/login-slot.md +97 -0
  59. package/catalog/frontmcp-auth-ui/examples/custom-auth-ui/multi-step-auth-extra.md +133 -0
  60. package/catalog/frontmcp-auth-ui/references/custom-auth-ui.md +162 -0
  61. package/catalog/frontmcp-authorities/SKILL.md +55 -18
  62. package/catalog/frontmcp-authorities/references/authority-profiles.md +25 -1
  63. package/catalog/frontmcp-authorities/references/custom-evaluators.md +1 -1
  64. package/catalog/frontmcp-authorities/references/rbac-abac-rebac.md +9 -0
  65. package/catalog/frontmcp-channels/SKILL.md +7 -1
  66. package/catalog/frontmcp-config/SKILL.md +14 -7
  67. package/catalog/frontmcp-config/examples/configure-auth/local-credential-vault.md +94 -0
  68. package/catalog/frontmcp-config/examples/configure-auth/local-secure-store.md +138 -0
  69. package/catalog/frontmcp-config/examples/configure-auth/remote-oauth-with-vault.md +45 -23
  70. package/catalog/frontmcp-config/examples/configure-auth-modes/local-behind-tunnel.md +73 -0
  71. package/catalog/frontmcp-config/examples/configure-auth-modes/local-consent-enforcement.md +87 -0
  72. package/catalog/frontmcp-config/examples/configure-auth-modes/local-dcr-control.md +67 -0
  73. package/catalog/frontmcp-config/examples/configure-auth-modes/local-minimal.md +62 -0
  74. package/catalog/frontmcp-config/examples/configure-auth-modes/local-multi-provider-orchestration.md +93 -0
  75. package/catalog/frontmcp-config/examples/configure-auth-modes/local-self-signed-tokens.md +18 -20
  76. package/catalog/frontmcp-config/examples/configure-auth-modes/local-single-operator.md +66 -0
  77. package/catalog/frontmcp-config/examples/configure-auth-modes/remote-enterprise-oauth.md +37 -23
  78. package/catalog/frontmcp-config/examples/configure-http/custom-http-routes.md +98 -0
  79. package/catalog/frontmcp-config/examples/configure-skills-http/audit-log-redis.md +17 -9
  80. package/catalog/frontmcp-config/references/configure-auth-modes.md +86 -23
  81. package/catalog/frontmcp-config/references/configure-auth.md +296 -50
  82. package/catalog/frontmcp-config/references/configure-deployment-targets.md +84 -1
  83. package/catalog/frontmcp-config/references/configure-http.md +203 -14
  84. package/catalog/frontmcp-config/references/configure-session.md +14 -7
  85. package/catalog/frontmcp-deployment/SKILL.md +17 -15
  86. package/catalog/frontmcp-deployment/references/build-for-mcpb.md +1 -1
  87. package/catalog/frontmcp-deployment/references/deploy-manifest-yaml.md +308 -0
  88. package/catalog/frontmcp-deployment/references/deploy-to-cloudflare-skills-only.md +174 -0
  89. package/catalog/frontmcp-deployment/references/mcp-client-integration.md +145 -2
  90. package/catalog/frontmcp-development/SKILL.md +36 -50
  91. package/catalog/frontmcp-development/examples/create-provider/basic-database-provider.md +14 -0
  92. package/catalog/frontmcp-development/examples/create-provider/config-and-api-providers.md +85 -9
  93. package/catalog/frontmcp-development/references/create-job.md +45 -11
  94. package/catalog/frontmcp-development/references/create-provider.md +80 -8
  95. package/catalog/frontmcp-development/references/create-skill-with-tools.md +31 -0
  96. package/catalog/frontmcp-development/references/create-skill.md +45 -0
  97. package/catalog/frontmcp-development/references/decorators-guide.md +15 -15
  98. package/catalog/frontmcp-extensibility/SKILL.md +1 -1
  99. package/catalog/frontmcp-extensibility/examples/skill-audit-log/verify-chain.md +8 -6
  100. package/catalog/frontmcp-extensibility/references/skill-audit-log.md +7 -2
  101. package/catalog/frontmcp-guides/SKILL.md +8 -8
  102. package/catalog/frontmcp-observability/SKILL.md +16 -8
  103. package/catalog/frontmcp-observability/examples/metrics-endpoint/enable-metrics-endpoint.md +77 -0
  104. package/catalog/frontmcp-observability/references/metrics-endpoint.md +161 -0
  105. package/catalog/frontmcp-production-readiness/SKILL.md +1 -1
  106. package/catalog/frontmcp-production-readiness/examples/common-checklist/security-hardening.md +3 -2
  107. package/catalog/frontmcp-setup/SKILL.md +12 -12
  108. package/catalog/frontmcp-setup/examples/frontmcp-skills-usage/install-and-search-skills.md +19 -1
  109. package/catalog/frontmcp-setup/examples/multi-app-composition/per-app-auth-and-isolation.md +7 -4
  110. package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +260 -19
  111. package/catalog/frontmcp-setup/references/multi-app-composition.md +6 -5
  112. package/catalog/frontmcp-setup/references/setup-project.md +29 -0
  113. package/catalog/frontmcp-setup/references/setup-sqlite.md +68 -9
  114. package/catalog/frontmcp-testing/SKILL.md +26 -18
  115. package/catalog/frontmcp-testing/references/test-auth.md +24 -0
  116. package/catalog/skills-manifest.json +676 -146
  117. package/package.json +1 -1
  118. package/src/manifest.d.ts +72 -1
  119. package/src/manifest.js +4 -1
  120. package/src/manifest.js.map +1 -1
  121. package/catalog/frontmcp-development/examples/create-tool/basic-class-tool.md +0 -61
  122. package/catalog/frontmcp-development/examples/create-tool/tool-with-di-and-errors.md +0 -84
  123. package/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md +0 -92
  124. package/catalog/frontmcp-development/examples/create-tool-annotations/destructive-delete-tool.md +0 -92
  125. package/catalog/frontmcp-development/examples/create-tool-annotations/readonly-query-tool.md +0 -59
  126. package/catalog/frontmcp-development/examples/create-tool-output-schema-types/primitive-and-media-outputs.md +0 -101
  127. package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-raw-shape-output.md +0 -62
  128. package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-schema-advanced-output.md +0 -101
  129. package/catalog/frontmcp-development/references/create-tool-annotations.md +0 -48
  130. package/catalog/frontmcp-development/references/create-tool-output-schema-types.md +0 -71
  131. package/catalog/frontmcp-development/references/create-tool.md +0 -728
@@ -0,0 +1,308 @@
1
+ ---
2
+ name: deploy-manifest-yaml
3
+ description: The frontmcp.deploy.yaml v1 schema — declarative manifest the GitHub Action consumes on every push to build, sign, and hot-reload the Cloudflare Worker
4
+ ---
5
+
6
+ # `frontmcp.deploy.yaml` — Schema Reference
7
+
8
+ This is the declarative manifest the GitHub Action ingests on every push. It is the source of truth for what your Cloudflare Worker serves. The schema is strict — unknown keys fail validation.
9
+
10
+ For the runtime that consumes the bundle, see `deploy-to-cloudflare-skills-only.md` in this same folder.
11
+ For the live docs version, see https://docs.agentfront.dev/frontmcp/deployment/deploy-manifest.
12
+
13
+ ## Minimum Manifest
14
+
15
+ ```yaml
16
+ $schema: https://schemas.agentfront.dev/frontmcp-deploy/v1.json
17
+ version: 1
18
+ name: acme-mcp
19
+
20
+ runtime:
21
+ target: cloudflare-worker
22
+ compatibilityDate: '2026-05-01'
23
+
24
+ server:
25
+ info: { name: acme-mcp, version: 1.0.0 }
26
+
27
+ specs: ./openapi/
28
+ skills: { source: ./skills/ }
29
+
30
+ bindings:
31
+ durableObjects:
32
+ - { binding: SESSIONS, className: SessionDO }
33
+ kvNamespaces:
34
+ - { binding: REPLAY_NONCE, id: '${env:KV_REPLAY_NONCE_ID}' }
35
+
36
+ signing:
37
+ algorithm: ed25519
38
+ trustRoots:
39
+ - { kid: prod-2026-05, publicKeySecret: TRUSTED_PUBKEY_PROD }
40
+ replay: { windowSeconds: 300, nonceKv: REPLAY_NONCE }
41
+
42
+ auth: { provider: none }
43
+
44
+ secrets:
45
+ - { name: TRUSTED_PUBKEY_PROD, required: true }
46
+ ```
47
+
48
+ ## Top-Level Keys
49
+
50
+ | Field | Required | Notes |
51
+ | ---------------- | -------- | ---------------------------------------------------------------- |
52
+ | `$schema` | optional | URL pointer for editor autocomplete |
53
+ | `version` | yes | Literal `1` only in v1.3 |
54
+ | `name` | yes | Identifier: `[A-Za-z][A-Za-z0-9_-]*` |
55
+ | `runtime` | yes | `target` + `compatibilityDate` + optional `compatibilityFlags[]` |
56
+ | `server` | yes | `info` + optional `instructions` (≤ 16 KB) |
57
+ | `specs` | yes | Directory string OR `{ id, spec, baseUrl?, bindingName? }[]` |
58
+ | `skills` | optional | Defaults to `{ source: './skills/' }` |
59
+ | `tags` | optional | `[{ name, description? }]` OpenAPI-shaped |
60
+ | `classification` | optional | Override rules over the auto-classifier |
61
+ | `bindings` | yes | CF DO / D1 / KV / R2 / vars (camelCase) |
62
+ | `signing` | yes | Algorithm + trust roots + replay-guard |
63
+ | `auth` | yes | `provider: none \| frontegg \| oauth \| apiKey` |
64
+ | `secrets` | optional | Names-only; cross-validator enforces references |
65
+ | `environments` | optional | Per-env overlay (deep-merge; bindings replace) |
66
+
67
+ ## `runtime`
68
+
69
+ ```yaml
70
+ runtime:
71
+ target: cloudflare-worker
72
+ compatibilityDate: '2026-05-01' # ISO-8601 YYYY-MM-DD
73
+ compatibilityFlags: [nodejs_compat] # optional
74
+ ```
75
+
76
+ ## `server`
77
+
78
+ ```yaml
79
+ server:
80
+ info:
81
+ name: acme-mcp
82
+ version: 1.0.0
83
+ title: ACME MCP # optional
84
+ instructions: | # optional, max 16 KB
85
+ Capabilities are organized as SKILLS...
86
+ ```
87
+
88
+ ## `specs`
89
+
90
+ ```yaml
91
+ specs: ./openapi/ # directory; specId = filename stem
92
+ ```
93
+
94
+ ```yaml
95
+ specs:
96
+ - ./openapi/acme.yaml
97
+ - id: billing
98
+ spec: ./openapi/billing.yaml
99
+ baseUrl: https://billing.acme.com
100
+ bindingName: billing # optional override for AgentScript namespace
101
+ ```
102
+
103
+ ## `skills`
104
+
105
+ ```yaml
106
+ skills:
107
+ source: ./skills/
108
+ alwaysLoad: # forced-load skill ids (kebab-case)
109
+ - auth-helpers
110
+ - observability-helpers
111
+ tags:
112
+ include: [public, billing]
113
+ exclude: [admin]
114
+ ```
115
+
116
+ A skill can also self-opt-in via its SKILL.md frontmatter:
117
+
118
+ ```yaml
119
+ ---
120
+ name: auth-helpers
121
+ description: Helpers every agent needs.
122
+ alwaysLoad: true
123
+ hideFromDiscovery: true # load without showing in search
124
+ ---
125
+ ```
126
+
127
+ ## `tags`
128
+
129
+ ```yaml
130
+ tags:
131
+ - { name: public, description: Always exposed }
132
+ - { name: billing, description: Billing flows }
133
+ - { name: admin, description: Admin only }
134
+ ```
135
+
136
+ ## `classification` Overrides
137
+
138
+ ```yaml
139
+ classification:
140
+ rules:
141
+ - { match: 'POST **/reset-password', emits: parent }
142
+ - { match: 'GET /metrics', expose: tool }
143
+ - { match: 'DELETE /users/{id}', emits: none }
144
+ ```
145
+
146
+ `match` = `METHOD path-glob`. Method may be `*`. `*` matches a single segment, `**` matches across `/`. First match wins.
147
+
148
+ ## `bindings`
149
+
150
+ Mirror wrangler shapes (camelCased). Strict — unknown keys reject.
151
+
152
+ ```yaml
153
+ bindings:
154
+ durableObjects:
155
+ - { binding: SESSIONS, className: SessionDO }
156
+ - { binding: EVENTS, className: EventStoreDO }
157
+ - { binding: BUNDLE, className: BundleDO }
158
+ d1Databases:
159
+ - { binding: AUDIT, databaseName: acme-audit, databaseId: '${env:D1_AUDIT_ID}' }
160
+ kvNamespaces:
161
+ - { binding: BUNDLE_CACHE, id: '${env:KV_BUNDLE_CACHE_ID}' }
162
+ - { binding: REPLAY_NONCE, id: '${env:KV_REPLAY_NONCE_ID}' }
163
+ r2Buckets:
164
+ - { binding: SKILL_DATA, bucketName: acme-skill-data }
165
+ vars:
166
+ LOG_LEVEL: info
167
+ ```
168
+
169
+ Binding names: SCREAMING_SNAKE_CASE.
170
+
171
+ ## `signing`
172
+
173
+ ```yaml
174
+ signing:
175
+ algorithm: ed25519 # or rs256
176
+ trustRoots:
177
+ - kid: prod-2026-05
178
+ publicKeySecret: TRUSTED_PUBKEY_PROD
179
+ replay:
180
+ windowSeconds: 300 # default 300; [10, 3600]
181
+ nonceKv: REPLAY_NONCE # MUST match a kvNamespaces[].binding
182
+ ```
183
+
184
+ Cross-validator enforces `replay.nonceKv` resolves to a KV binding name and every `publicKeySecret` appears in `secrets[]`.
185
+
186
+ ## `auth` (Discriminated Union)
187
+
188
+ ```yaml
189
+ auth: { provider: none }
190
+ ```
191
+
192
+ ```yaml
193
+ auth:
194
+ provider: frontegg
195
+ frontegg:
196
+ tenantResolver: subdomain # or 'header' / 'jwt-claim'
197
+ audience: acme-mcp
198
+ issuerSecret: FRONTEGG_ISSUER_URL
199
+ ```
200
+
201
+ ```yaml
202
+ auth:
203
+ provider: oauth
204
+ oauth:
205
+ issuer: https://issuer.example.com
206
+ audience: acme-mcp
207
+ credentialsSecret: M2M_CREDS # optional, M2M
208
+ ```
209
+
210
+ ```yaml
211
+ auth:
212
+ provider: apiKey
213
+ apiKey:
214
+ header: X-API-Key
215
+ allowlistSecret: ACME_API_KEYS
216
+ ```
217
+
218
+ ## `secrets`
219
+
220
+ ```yaml
221
+ secrets:
222
+ - { name: TRUSTED_PUBKEY_PROD, required: true, description: 'Signing trust root (PEM)' }
223
+ - { name: FRONTEGG_ISSUER_URL, required: true }
224
+ - { name: ACME_API_TOKEN, required: true, description: 'ACME API bearer' }
225
+ ```
226
+
227
+ SCREAMING_SNAKE_CASE names only. Inline values are forbidden by the schema.
228
+
229
+ ## `environments`
230
+
231
+ ```yaml
232
+ environments:
233
+ staging:
234
+ specs:
235
+ - id: acme
236
+ spec: ./openapi/acme.yaml
237
+ baseUrl: https://api.staging.acme.com
238
+ skills: { tags: { include: [public, billing, ops] } }
239
+ bindings: { vars: { LOG_LEVEL: debug } }
240
+ production:
241
+ skills: { tags: { include: [public, billing, ops], exclude: [admin, experimental] } }
242
+ bindings: { vars: { LOG_LEVEL: info } }
243
+ ```
244
+
245
+ Deep-merge for scalars + nested objects; `bindings` REPLACES (Wrangler non-inheritance semantics).
246
+
247
+ ## Cross-Field Validation
248
+
249
+ After per-field schema parse, run `crossValidateManifest(parsed)`:
250
+
251
+ | Check | Error shape |
252
+ | --------------------------------------------------------- | --------------------------------------------------------------------------------- |
253
+ | Secret referenced from auth/signing but not declared | `Secret "<NAME>" referenced at <path> is not declared in secrets[]` |
254
+ | `signing.replay.nonceKv` not in `bindings.kvNamespaces[]` | `signing.replay.nonceKv "<X>" does not match any bindings.kvNamespaces[].binding` |
255
+ | `skills.alwaysLoad[]` entry not kebab-case | `skills.alwaysLoad entry "<X>" is not a valid kebab-case skill id` |
256
+ | Tag filter references undeclared tag | `skills.tags references unknown tag "<X>" (not in tags[])` |
257
+
258
+ All errors aggregate in one report — not throw-on-first.
259
+
260
+ ```ts
261
+ import * as YAML from 'yaml';
262
+
263
+ import { crossValidateManifest, deployManifestSchema } from '@frontmcp/adapters/skills';
264
+
265
+ const parsed = deployManifestSchema.parse(YAML.parse(raw));
266
+ const cross = crossValidateManifest(parsed);
267
+ if (!cross.ok) cross.errors.forEach((e) => console.error('manifest:', e));
268
+ ```
269
+
270
+ ## Auto-Classification Table (HTTP → MCP)
271
+
272
+ | Method | Path | Matching GET? | Surface | Notify on success |
273
+ | ----------- | ---------------------- | ------------- | -------- | ---------------------------- |
274
+ | GET | `/users/{id}` | (self) | both | — |
275
+ | GET | `/users` | (self) | resource | — |
276
+ | POST | `/users` | yes | tool | `list_changed` on self |
277
+ | POST | `/users/{id}` | yes | tool | `updated` on self |
278
+ | POST | `/users/{id}/reset-pw` | no | tool | `updated` on parent |
279
+ | POST | any | no | tool | — |
280
+ | PUT / PATCH | any | yes | tool | `updated` on self |
281
+ | PUT / PATCH | any | no | tool | `updated` on parent (if any) |
282
+ | DELETE | singular | — | tool | `list_changed` on parent |
283
+ | DELETE | collection | yes | tool | `list_changed` on self |
284
+
285
+ The notification fires once per call regardless of which skill made it. Two skills calling the same PUT → still one event.
286
+
287
+ ## TypeScript Imports
288
+
289
+ ```ts
290
+ import {
291
+ applyClassificationOverrides,
292
+ buildResourceChangeNotification,
293
+ ClassificationRegistry,
294
+ classifyOperations,
295
+ crossValidateManifest,
296
+ deployManifestSchema,
297
+ extractOpReferences,
298
+ renderResourceUri,
299
+ validateOpReferences,
300
+ type DeployManifest,
301
+ } from '@frontmcp/adapters/skills';
302
+ ```
303
+
304
+ ## See Also
305
+
306
+ - `references/deploy-to-cloudflare-skills-only.md` — the runtime that consumes the manifest
307
+ - Docs: https://docs.agentfront.dev/frontmcp/deployment/deploy-manifest
308
+ - Docs: https://docs.agentfront.dev/frontmcp/features/skills-only-deployment
@@ -0,0 +1,174 @@
1
+ ---
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
4
+ ---
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.
9
+
10
+ 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).
12
+
13
+ ## When to Use This Skill
14
+
15
+ ### Must Use
16
+
17
+ - Deploying a FrontMCP server using the v1.3 skills-only model on Cloudflare
18
+ - Setting up GitHub-Action-driven hot-reload of skills + OpenAPI specs without a wrangler redeploy on every push
19
+ - Configuring AgentScript with namespaced OpenAPI bindings (`acme.getUser({...})`)
20
+
21
+ ### Recommended
22
+
23
+ - Standing up the first hosted FrontMCP runtime
24
+ - Migrating from the flat-tools model to skills-only
25
+
26
+ ### Skip When
27
+
28
+ - You want a long-running Node process — use `deploy-to-node`
29
+ - You need the full `@enclave-vm/core` CodeCall VM (pause / rerun) — host on Node; the Worker uses AST-preflight only
30
+ - You're shipping `scripts/` skill blobs (Anthropic Agent Skills spec) — that ships in v1.7
31
+
32
+ ## Worker Entry File
33
+
34
+ ```ts
35
+ // worker.ts (~10 lines)
36
+ import { createWorker } from '@frontmcp/worker';
37
+
38
+ import deployBundle from './frontmcp.deploy.bundle.js'; // emitted by the GH Action
39
+
40
+ const { handler, durableObjects } = createWorker({
41
+ bundle: deployBundle,
42
+ env: 'production',
43
+ });
44
+
45
+ export default handler;
46
+ export const { SessionDO, EventStoreDO, BundleDO } = durableObjects;
47
+ ```
48
+
49
+ `createWorker` parses the manifest, applies the `environments.production` overlay, verifies the signed envelope against `TRUSTED_KEYS`, and assembles the FrontMCP runtime.
50
+
51
+ ## Storage Layout (Opinionated Default)
52
+
53
+ | Binding | Class / kind | Purpose |
54
+ | -------------- | ------------------- | ----------------------------------------------------------- |
55
+ | `SESSIONS` | DO (`SessionDO`) | MCP session store; survives Worker restarts |
56
+ | `EVENTS` | DO (`EventStoreDO`) | Event store for Streamable HTTP + SSE resumability |
57
+ | `BUNDLE` | DO (`BundleDO`) | Last-good envelope + active classifications (hot-swappable) |
58
+ | `AUDIT` | D1 | Hash-chained skill action audit log (v1.2 audit spec) |
59
+ | `BUNDLE_CACHE` | KV | Fallback bundle reused if a resync push fails |
60
+ | `REPLAY_NONCE` | KV | Replay-guard nonce dedupe (`signing.replay.windowSeconds`) |
61
+ | `SKILL_DATA` | R2 | Large skill `data/` blobs |
62
+
63
+ ## Hot-Reload Cycle
64
+
65
+ ```text
66
+ GitHub push → frontmcp/deploy-action@v1
67
+ 1. Parse + cross-validate frontmcp.deploy.yaml
68
+ 2. Apply environments.<env> overlay
69
+ 3. Walk skills/; run the markdown op-reference harvester
70
+ 4. Run the OpenAPI → MCP classifier
71
+ 5. Inline & content-address artifacts
72
+ 6. Sign envelope (Ed25519 by default)
73
+ 7. POST → worker/_frontmcp/resync
74
+
75
+ Worker:
76
+ - Verify signature against signing.trustRoots[].publicKeySecret
77
+ - Replay-guard via REPLAY_NONCE KV
78
+ - Diff: structural change (DO classes, bindings) → require redeploy
79
+ - Otherwise: atomic swap of skill + classification registries
80
+ - Emit notifications/{skills,tools,resources,prompts}/list_changed
81
+ - Persist envelope into BUNDLE_CACHE KV
82
+ ```
83
+
84
+ Day-to-day skill / OpenAPI edits are pure hot-reload. `wrangler deploy` is only needed for structural changes.
85
+
86
+ ## Isolation Strategy
87
+
88
+ `codecall:execute` runs agent-authored AgentScript inside the Worker isolate. Two layers:
89
+
90
+ 1. **AST preflight on every call** via `@enclave-vm/ast` (pure-JS, Acorn-based, Worker-safe). Rejects `eval`, `Function`, `process`, `require`, dynamic `import`, raw `fetch`, prototype-walks, etc. before the script runs.
91
+ 2. **Frozen capability scope.** Script executes with `new Function('skills', 'ctx', code)` against a frozen object that holds only the active skills' generated namespaces + `callTool` / `getTool` / `mcpLog` / `mcpNotify`.
92
+
93
+ `@enclave-vm/core` (full VM) needs `node:vm` and is NOT Worker-safe — the Worker target uses AST-preflight + frozen scope only.
94
+
95
+ ## Auth at the Edge
96
+
97
+ ```yaml
98
+ auth:
99
+ provider: frontegg
100
+ frontegg:
101
+ tenantResolver: subdomain
102
+ audience: acme-mcp
103
+ issuerSecret: FRONTEGG_ISSUER_URL
104
+ ```
105
+
106
+ Frontegg JWT verification runs at the Worker edge — no upstream hop. Other providers (`oauth`, `apiKey`, `none`) share the same shape.
107
+
108
+ ## Secrets
109
+
110
+ Names-only in the manifest:
111
+
112
+ ```yaml
113
+ secrets:
114
+ - { name: TRUSTED_PUBKEY_PROD, required: true, description: 'Signing trust root (PEM)' }
115
+ - { name: FRONTEGG_ISSUER_URL, required: true }
116
+ - { name: ACME_API_TOKEN, required: true }
117
+ ```
118
+
119
+ Bind values out-of-band:
120
+
121
+ ```bash
122
+ wrangler secret put TRUSTED_PUBKEY_PROD
123
+ wrangler secret put FRONTEGG_ISSUER_URL
124
+ wrangler secret put ACME_API_TOKEN
125
+ ```
126
+
127
+ The cross-validator REJECTS any manifest that references a secret name not declared in `secrets[]`.
128
+
129
+ ## Sample `.github/workflows/deploy.yml`
130
+
131
+ ```yaml
132
+ name: Deploy
133
+ on:
134
+ push: { branches: [main] }
135
+ jobs:
136
+ deploy:
137
+ runs-on: ubuntu-latest
138
+ permissions: { contents: read }
139
+ steps:
140
+ - uses: actions/checkout@v6
141
+ - uses: frontmcp/deploy-action@v1
142
+ with:
143
+ environment: production
144
+ signingKey: ${{ secrets.FRONTMCP_SIGNING_KEY }}
145
+ secrets: |
146
+ TRUSTED_PUBKEY_PROD
147
+ FRONTEGG_ISSUER_URL
148
+ ACME_API_TOKEN
149
+ env:
150
+ TRUSTED_PUBKEY_PROD: ${{ secrets.TRUSTED_PUBKEY_PROD }}
151
+ FRONTEGG_ISSUER_URL: ${{ secrets.FRONTEGG_ISSUER_URL }}
152
+ ACME_API_TOKEN: ${{ secrets.ACME_API_TOKEN }}
153
+ ```
154
+
155
+ Action outputs:
156
+
157
+ - `deployment-url` — the Worker URL
158
+ - `bundle-sha256` — content-addressed envelope digest (pin in release notes)
159
+
160
+ ## Common Mistakes
161
+
162
+ - **Embedding secrets in `wrangler.toml` or the deploy YAML.** The cross-validator + schema both reject inline secret values. Always `wrangler secret put`.
163
+ - **Using `wrangler deploy` for every push.** Once the Worker has its DO + KV + D1 bindings, day-to-day skill changes flow through `POST /_frontmcp/resync`. Only structural changes need a redeploy.
164
+ - **Forgetting to declare `signing.replay.nonceKv`.** The cross-validator requires the value to match a binding in `bindings.kvNamespaces[]`. Without it the Worker can't dedupe replays.
165
+ - **Mixing `acme-api` as both a spec id AND an AgentScript namespace.** Dashes aren't valid JS identifiers — add `bindingName: acmeApi` on the spec entry, or rename the spec.
166
+ - **Assuming `notifications/resources/updated` carries no URI.** It does — `notifications/resources/list_changed` is the one with empty params.
167
+
168
+ ## See Also
169
+
170
+ - `references/deploy-manifest-yaml.md` — full `frontmcp.deploy.yaml` schema reference
171
+ - `references/deploy-to-cloudflare.md` — the older Express-to-Workers adapter path
172
+ - `references/wrangler-config.md` — wrangler.toml checklist
173
+ - Docs: https://docs.agentfront.dev/frontmcp/deployment/cloudflare-worker
174
+ - Docs: https://docs.agentfront.dev/frontmcp/features/skills-only-deployment
@@ -61,6 +61,16 @@ When building a FrontMCP server with a coding agent (Claude Code, Cursor, Windsu
61
61
  ```
62
62
  3. Code and iterate — every file save triggers a server reload
63
63
 
64
+ > **The endpoint path must match the `url`.** `frontmcp dev` serves the MCP
65
+ > endpoint at `transport.http.path` from `frontmcp.config.ts` (default `/`). The
66
+ > `url` above ends in `/mcp`, so set `transport: { default: 'http', http: { path: '/mcp' } }`
67
+ > in `frontmcp.config.ts` — otherwise the client hits `/mcp` while the server
68
+ > listens on `/` and every request 404s (issue #446). `dev` honors the configured
69
+ > path (it propagates it to the server via `FRONTMCP_HTTP_ENTRY_PATH`), and the
70
+ > generated `clients.*.url` derives from the same `transport.http.path`, so
71
+ > `frontmcp eject` and `dev` stay in sync. (A hard-coded
72
+ > `@FrontMcp({ http: { entryPath } })` in metadata wins over both — keep them aligned.)
73
+
64
74
  ### Why HTTP for development (not stdio)
65
75
 
66
76
  - **Hot reload:** Server restarts on file change (~200ms), client reconnects automatically
@@ -68,6 +78,37 @@ When building a FrontMCP server with a coding agent (Claude Code, Cursor, Windsu
68
78
  - **Debugging:** Logs visible in terminal, Inspector UI available via `npm run inspect`
69
79
  - **Persistence:** Server stays running across edits, no new process per connection
70
80
 
81
+ ### `frontmcp dev --stdio` bridge (issue #399)
82
+
83
+ If the client must speak stdio (MCPB-installed bundles, certain Claude Code configurations) and you still want the dev hot-reload loop, run the first-party watch-aware stdio bridge — replaces the third-party `mcp-remote` recipe:
84
+
85
+ ```bash
86
+ frontmcp dev --stdio # HTTP loopback under the hood
87
+ frontmcp dev --stdio --serve # stdio-over-pipe (no HTTP listener)
88
+ ```
89
+
90
+ Wire the client at the bridge:
91
+
92
+ ```json
93
+ {
94
+ "mcpServers": {
95
+ "my-server": {
96
+ "command": "npx",
97
+ "args": ["-y", "frontmcp", "dev", "--stdio"]
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ Bridge guarantees:
104
+
105
+ - Stdout is 100% JSON-RPC frames; diagnostics go to `./.frontmcp/dev.log` (override with `--log-file`).
106
+ - Session id survives reload (pinned via `FRONTMCP_DEV_FORCE_SESSION_ID`).
107
+ - Buffered RPCs during reload drain in FIFO once the child reports ready.
108
+ - Reload deadline + buffer overflow surface structured errors (`dev_server_unreachable` / `dev_buffer_full` / `dev_reload_deadline` — codes -32099 / -32098 / -32097) so the client spinner clears instead of hanging.
109
+
110
+ Flags: `--stdio`, `--serve`, `--log-file <path>`, `--buffer-size <n>` (default 8), `--reload-deadline-ms <ms>` (default 30000), `-p <port>` (HTTP-mode loopback, default 3000).
111
+
71
112
  ## Stdio Transport
72
113
 
73
114
  The most common transport. The MCP client spawns your server as a child process and communicates via stdin/stdout JSON-RPC.
@@ -78,6 +119,8 @@ The most common transport. The MCP client spawns your server as a child process
78
119
  - All logs are automatically redirected to stderr and `~/.frontmcp/logs/`
79
120
  - stdout contains ONLY MCP JSON-RPC protocol messages
80
121
  - One client per process (no multiplexing)
122
+ - **Stdio binds no TCP port** — the HTTP server is disabled, so multiple stdio
123
+ instances of the same server run without an `EADDRINUSE` conflict
81
124
 
82
125
  ### npx (published npm package)
83
126
 
@@ -108,19 +151,43 @@ The most common transport. The MCP client spawns your server as a child process
108
151
  }
109
152
  ```
110
153
 
111
- ### Node.js bundle
154
+ ### Node.js CLI bundle
155
+
156
+ Run the **CLI bundle** (`frontmcp build --target cli`) — it parses `--stdio`
157
+ itself before any framework initialization:
112
158
 
113
159
  ```json
114
160
  {
115
161
  "mcpServers": {
116
162
  "my-server": {
117
163
  "command": "node",
118
- "args": ["/path/to/my-server.bundle.js", "--stdio"]
164
+ "args": ["/path/to/my-server-cli.bundle.js", "--stdio"]
119
165
  }
120
166
  }
121
167
  }
122
168
  ```
123
169
 
170
+ ### Server runner (`--target node`)
171
+
172
+ `frontmcp build --target node` emits a runner at `dist/node/<name>`. Pass
173
+ `--stdio` to serve over stdin/stdout instead of HTTP — the runner sets
174
+ `FRONTMCP_STDIO=1`, so the server connects over stdio and binds no port:
175
+
176
+ ```json
177
+ {
178
+ "mcpServers": {
179
+ "my-server": {
180
+ "command": "/path/to/dist/node/my-server",
181
+ "args": ["--stdio"]
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ > Do not run the raw `--target node` bundle as `node dist/node/my-server.bundle.js --stdio`
188
+ > — that bundle is your `@FrontMcp` server module and starts the HTTP server on
189
+ > import. Use the runner above, or set `FRONTMCP_STDIO=1` before the bundle loads.
190
+
124
191
  ## HTTP Transport
125
192
 
126
193
  Connect to a running FrontMCP HTTP server. The server must be started separately.
@@ -190,6 +257,82 @@ The `url` hostname is ignored for Unix sockets; only the path (`/mcp`) is used f
190
257
  | VS Code | `.vscode/mcp.json` | Project root |
191
258
  | Windsurf | `mcp_config.json` | `~/.codeium/windsurf/` |
192
259
 
260
+ ## Claude Code plugin install (issue #411)
261
+
262
+ For Claude Code specifically, you can ship the whole server — MCP entry +
263
+ prompts (as slash commands) + `@Skill`s — as a single Claude Code plugin
264
+ folder, registered in one command. This is the recommended path when you
265
+ want users to enable/disable the server from `/plugins` rather than
266
+ hand-edit `.mcp.json`.
267
+
268
+ ### From a project source (dev tool)
269
+
270
+ ```bash
271
+ # At a FrontMCP project root — emits .claude/plugins/<name>/ in cwd
272
+ frontmcp plugin install --claude
273
+
274
+ # Inspect the plan without writing
275
+ frontmcp plugin install --claude --dry-run
276
+
277
+ # Also drop a Codex mcp_servers entry into ~/.codex/config.toml
278
+ frontmcp plugin install --claude --codex
279
+
280
+ # Report install state
281
+ frontmcp plugin status --claude
282
+
283
+ # Remove what install wrote (preserves user-added files)
284
+ frontmcp plugin uninstall --claude
285
+ ```
286
+
287
+ ### From a built bin (end-user)
288
+
289
+ Every CLI built with `frontmcp build --target cli` inherits the same
290
+ behavior under its `install` verb (no new top-level verb):
291
+
292
+ ```bash
293
+ my-bin install -p claude # write .claude/plugins/my-bin/
294
+ my-bin install -p claude --scope user # ~/.claude/plugins/my-bin/
295
+ my-bin install -p claude -p codex # both providers in one call
296
+ my-bin install --status # report state per provider
297
+ my-bin uninstall -p claude # remove only managed files
298
+ ```
299
+
300
+ ### What gets written
301
+
302
+ ```text
303
+ .claude/plugins/<bin>/
304
+ ├── .claude-plugin/plugin.json # name, version, mcpServers, skills, _meta.frontmcp.managedFiles
305
+ ├── commands/<prompt>.md # one per @Prompt the server advertises
306
+ └── skills/<name>/SKILL.md # one per @Skill, with references/examples/scripts/assets subdirs
307
+ ```
308
+
309
+ ### SKILL.md frontmatter synthesis
310
+
311
+ Claude Code's filesystem loader discovers skills via the YAML frontmatter at
312
+ the top of each `SKILL.md` (`name`, `description`, optional `tags`,
313
+ `license`). A `@Skill`-decorated entry typically points
314
+ `instructions.file` at a plain markdown body **without** frontmatter, so the
315
+ install flow composes the frontmatter from the decorator metadata before
316
+ writing the file:
317
+
318
+ - `name` and `description` come from `@Skill({ name, description })`.
319
+ - `tags` and `license` are forwarded when present.
320
+ - The instruction file body is copied verbatim AFTER the synthesized
321
+ frontmatter block.
322
+ - If the instruction file **already starts with `---`**, the existing
323
+ frontmatter is preserved as-is — the author is treated as authoritative.
324
+
325
+ Skill names are validated against the same allowlist as plugin names
326
+ (`^[a-zA-Z0-9][a-zA-Z0-9._-]*$`) before any filesystem write, so a malicious
327
+ `@Skill({ name: '../escape' })` cannot land outside the plugin tree.
328
+
329
+ ### Idempotency
330
+
331
+ Re-runs are idempotent: any file not listed in `_meta.frontmcp.managedFiles`
332
+ (including unknown top-level keys the user added to `plugin.json`, like
333
+ `hooks` or extra `mcpServers`) is preserved. Use `--dry-run` to inspect
334
+ the planned tree before writing.
335
+
193
336
  ## Common Patterns
194
337
 
195
338
  | Pattern | Correct | Incorrect | Why |