@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.
- package/README.md +38 -29
- package/catalog/TEMPLATE.md +26 -0
- package/catalog/create-tool/SKILL.md +318 -0
- package/catalog/create-tool/examples/01-basic-class-tool.md +112 -0
- package/catalog/create-tool/examples/02-basic-function-tool.md +80 -0
- package/catalog/create-tool/examples/03-tool-with-zod-shape-output.md +78 -0
- package/catalog/create-tool/examples/04-tool-with-zod-schema-output.md +97 -0
- package/catalog/create-tool/examples/05-tool-with-primitive-output.md +93 -0
- package/catalog/create-tool/examples/06-tool-with-media-output.md +109 -0
- package/catalog/create-tool/examples/08-tool-with-provider-injection.md +110 -0
- package/catalog/create-tool/examples/09-tool-with-multiple-providers.md +107 -0
- package/catalog/create-tool/examples/11-tool-with-fetch.md +94 -0
- package/catalog/create-tool/examples/12-tool-with-fetch-and-retries.md +115 -0
- package/catalog/create-tool/examples/13-tool-with-single-auth-provider.md +85 -0
- package/catalog/create-tool/examples/14-tool-with-multiple-auth-providers.md +105 -0
- package/catalog/create-tool/examples/15-tool-with-credential-vault.md +115 -0
- package/catalog/create-tool/examples/16-tool-with-rate-limit.md +71 -0
- package/catalog/create-tool/examples/17-tool-with-concurrency-and-timeout.md +101 -0
- package/catalog/create-tool/examples/18-tool-with-progress-and-notify.md +96 -0
- package/catalog/create-tool/examples/19-tool-with-elicitation.md +102 -0
- package/catalog/create-tool/examples/20-tool-with-annotations.md +125 -0
- package/catalog/create-tool/examples/21-tool-with-availability-constraints.md +107 -0
- package/catalog/create-tool/examples/22-tool-with-ui-html-template.md +93 -0
- package/catalog/create-tool/examples/23-tool-with-ui-filesource-tsx.md +112 -0
- package/catalog/create-tool/examples/24-tool-with-ui-csp-and-bridge.md +127 -0
- package/catalog/create-tool/examples/25-tool-handing-off-to-job.md +143 -0
- package/catalog/create-tool/examples/26-tool-with-resource-link-output.md +94 -0
- package/catalog/create-tool/examples/27-tool-with-examples-metadata.md +90 -0
- package/catalog/create-tool/references/annotations.md +96 -0
- package/catalog/create-tool/references/auth-providers.md +167 -0
- package/catalog/create-tool/references/availability.md +106 -0
- package/catalog/create-tool/references/decorator-options.md +95 -0
- package/catalog/create-tool/references/derived-types.md +102 -0
- package/catalog/create-tool/references/elicitation.md +128 -0
- package/catalog/create-tool/references/error-handling.md +128 -0
- package/catalog/create-tool/references/execution-context.md +158 -0
- package/catalog/create-tool/references/file-layout.md +96 -0
- package/catalog/create-tool/references/function-style-builder.md +118 -0
- package/catalog/create-tool/references/input-schema.md +141 -0
- package/catalog/create-tool/references/output-schema.md +175 -0
- package/catalog/create-tool/references/quick-start.md +124 -0
- package/catalog/create-tool/references/registration.md +132 -0
- package/catalog/create-tool/references/remote-and-esm.md +68 -0
- package/catalog/create-tool/references/testing.md +59 -0
- package/catalog/create-tool/references/throttling.md +109 -0
- package/catalog/create-tool/references/ui-widgets.md +198 -0
- package/catalog/create-tool/rules/always-define-output-schema.md +77 -0
- package/catalog/create-tool/rules/derive-execute-types.md +57 -0
- package/catalog/create-tool/rules/input-schema-is-raw-shape.md +76 -0
- package/catalog/create-tool/rules/no-toolcontext-generics.md +50 -0
- package/catalog/create-tool/rules/no-try-catch-around-execute.md +79 -0
- package/catalog/create-tool/rules/register-in-app.md +76 -0
- package/catalog/create-tool/rules/snake-case-tool-names.md +45 -0
- package/catalog/create-tool/rules/use-this-fail-for-business-errors.md +75 -0
- package/catalog/create-tool/rules/widget-paths-anchor-with-import-meta-url.md +76 -0
- package/catalog/create-tool/rules/widget-resource-mode-host-detect.md +61 -0
- package/catalog/frontmcp-auth-ui/SKILL.md +146 -0
- package/catalog/frontmcp-auth-ui/examples/custom-auth-ui/login-slot.md +97 -0
- package/catalog/frontmcp-auth-ui/examples/custom-auth-ui/multi-step-auth-extra.md +133 -0
- package/catalog/frontmcp-auth-ui/references/custom-auth-ui.md +162 -0
- package/catalog/frontmcp-authorities/SKILL.md +55 -18
- package/catalog/frontmcp-authorities/references/authority-profiles.md +25 -1
- package/catalog/frontmcp-authorities/references/custom-evaluators.md +1 -1
- package/catalog/frontmcp-authorities/references/rbac-abac-rebac.md +9 -0
- package/catalog/frontmcp-channels/SKILL.md +7 -1
- package/catalog/frontmcp-config/SKILL.md +14 -7
- package/catalog/frontmcp-config/examples/configure-auth/local-credential-vault.md +94 -0
- package/catalog/frontmcp-config/examples/configure-auth/local-secure-store.md +138 -0
- package/catalog/frontmcp-config/examples/configure-auth/remote-oauth-with-vault.md +45 -23
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-behind-tunnel.md +73 -0
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-consent-enforcement.md +87 -0
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-dcr-control.md +67 -0
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-minimal.md +62 -0
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-multi-provider-orchestration.md +93 -0
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-self-signed-tokens.md +18 -20
- package/catalog/frontmcp-config/examples/configure-auth-modes/local-single-operator.md +66 -0
- package/catalog/frontmcp-config/examples/configure-auth-modes/remote-enterprise-oauth.md +37 -23
- package/catalog/frontmcp-config/examples/configure-http/custom-http-routes.md +98 -0
- package/catalog/frontmcp-config/examples/configure-skills-http/audit-log-redis.md +17 -9
- package/catalog/frontmcp-config/references/configure-auth-modes.md +86 -23
- package/catalog/frontmcp-config/references/configure-auth.md +296 -50
- package/catalog/frontmcp-config/references/configure-deployment-targets.md +84 -1
- package/catalog/frontmcp-config/references/configure-http.md +203 -14
- package/catalog/frontmcp-config/references/configure-session.md +14 -7
- package/catalog/frontmcp-deployment/SKILL.md +17 -15
- package/catalog/frontmcp-deployment/references/build-for-mcpb.md +1 -1
- package/catalog/frontmcp-deployment/references/deploy-manifest-yaml.md +308 -0
- package/catalog/frontmcp-deployment/references/deploy-to-cloudflare-skills-only.md +174 -0
- package/catalog/frontmcp-deployment/references/mcp-client-integration.md +145 -2
- package/catalog/frontmcp-development/SKILL.md +36 -50
- package/catalog/frontmcp-development/examples/create-provider/basic-database-provider.md +14 -0
- package/catalog/frontmcp-development/examples/create-provider/config-and-api-providers.md +85 -9
- package/catalog/frontmcp-development/references/create-job.md +45 -11
- package/catalog/frontmcp-development/references/create-provider.md +80 -8
- package/catalog/frontmcp-development/references/create-skill-with-tools.md +31 -0
- package/catalog/frontmcp-development/references/create-skill.md +45 -0
- package/catalog/frontmcp-development/references/decorators-guide.md +15 -15
- package/catalog/frontmcp-extensibility/SKILL.md +1 -1
- package/catalog/frontmcp-extensibility/examples/skill-audit-log/verify-chain.md +8 -6
- package/catalog/frontmcp-extensibility/references/skill-audit-log.md +7 -2
- package/catalog/frontmcp-guides/SKILL.md +8 -8
- package/catalog/frontmcp-observability/SKILL.md +16 -8
- package/catalog/frontmcp-observability/examples/metrics-endpoint/enable-metrics-endpoint.md +77 -0
- package/catalog/frontmcp-observability/references/metrics-endpoint.md +161 -0
- package/catalog/frontmcp-production-readiness/SKILL.md +1 -1
- package/catalog/frontmcp-production-readiness/examples/common-checklist/security-hardening.md +3 -2
- package/catalog/frontmcp-setup/SKILL.md +12 -12
- package/catalog/frontmcp-setup/examples/frontmcp-skills-usage/install-and-search-skills.md +19 -1
- package/catalog/frontmcp-setup/examples/multi-app-composition/per-app-auth-and-isolation.md +7 -4
- package/catalog/frontmcp-setup/references/frontmcp-skills-usage.md +260 -19
- package/catalog/frontmcp-setup/references/multi-app-composition.md +6 -5
- package/catalog/frontmcp-setup/references/setup-project.md +29 -0
- package/catalog/frontmcp-setup/references/setup-sqlite.md +68 -9
- package/catalog/frontmcp-testing/SKILL.md +26 -18
- package/catalog/frontmcp-testing/references/test-auth.md +24 -0
- package/catalog/skills-manifest.json +676 -146
- package/package.json +1 -1
- package/src/manifest.d.ts +72 -1
- package/src/manifest.js +4 -1
- package/src/manifest.js.map +1 -1
- package/catalog/frontmcp-development/examples/create-tool/basic-class-tool.md +0 -61
- package/catalog/frontmcp-development/examples/create-tool/tool-with-di-and-errors.md +0 -84
- package/catalog/frontmcp-development/examples/create-tool/tool-with-rate-limiting-and-progress.md +0 -92
- package/catalog/frontmcp-development/examples/create-tool-annotations/destructive-delete-tool.md +0 -92
- package/catalog/frontmcp-development/examples/create-tool-annotations/readonly-query-tool.md +0 -59
- package/catalog/frontmcp-development/examples/create-tool-output-schema-types/primitive-and-media-outputs.md +0 -101
- package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-raw-shape-output.md +0 -62
- package/catalog/frontmcp-development/examples/create-tool-output-schema-types/zod-schema-advanced-output.md +0 -101
- package/catalog/frontmcp-development/references/create-tool-annotations.md +0 -48
- package/catalog/frontmcp-development/references/create-tool-output-schema-types.md +0 -71
- 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 |
|