frontmcp.deploy.yaml is the declarative manifest the GitHub Action ingests on every push and the source of truth for what your Cloudflare Worker serves. This page is the full v1 schema reference.
For the conceptual picture, see Skills-Only Deployment; for the runtime that consumes the bundle, see Cloudflare Worker.
Minimum viable manifest
Top-level shape
| Field | Required | Notes |
|---|---|---|
$schema | optional | URL pointer for editor autocomplete. Conventionally https://schemas.agentfront.dev/frontmcp-deploy/v1.json. |
version | yes | Must equal the literal 1 in v1.3. |
name | yes | Project name. Identifier: starts with a letter, then [A-Za-z0-9_-]*. |
runtime | yes | Target runtime + compatibility date. |
server | yes | Server identity (info) + optional instructions injected at MCP initialize. |
specs | yes | OpenAPI specs to ingest. Either a directory string or an array of { id, spec, baseUrl?, bindingName? }. |
skills | optional (defaults to { source: './skills/' }) | Where to find skills + optional alwaysLoad list + tag filter. |
tags | optional | Declared tag dictionary (OpenAPI-style [{ name, description? }]). |
classification | optional | Override rules on top of the OpenAPI → MCP classifier. |
bindings | yes | Cloudflare bindings — DO / D1 / KV / R2 / vars. |
signing | yes | Algorithm + trust roots + replay-guard config. |
auth | yes | Discriminated union: none / frontegg / oauth / apiKey. |
secrets | optional | Names-only list. Cross-validator enforces every referenced secret appears here. |
environments | optional | Per-env overlay map (deep-merged at deploy time). |
runtime
compatibilityDate follows Cloudflare’s own date format. Future versions of the schema will add vercel-edge and deno-deploy to the target discriminator.
server
server.instructions is what the MCP client receives at initialize. The existing skillsConfig.injectInstructions machinery (v1.2) decides how a per-skill summary follows (append by default).
specs — OpenAPI inventory
Two shapes:
bindingName lets you keep a URI-safe spec id (e.g. acme-api) while exposing a JS-identifier namespace (acmeApi) to AgentScript.
skills
alwaysLoad IDs must be kebab-case (the cross-validator catches typos at deploy time). When the manifest also declares tags[], every name in tags.include / tags.exclude must appear in that list.
A skill in ./skills/<id>/ can also opt itself in via SKILL.md frontmatter:
tags
OpenAPI-shaped tag dictionary, used for the scope filter:
billing in the spec contributes that tag to any skill that references it.
classification (overrides)
By default the classifier follows HTTP semantics (see the classification table). Override per-pattern:
match is a METHOD path-glob. Method may be * to match any. expose overrides the MCP surface (tool / resource / both); emits overrides the resource-change notification target (self / parent / none). First match wins.
bindings
Mirror wrangler.toml field shapes verbatim (camelCased in YAML). Strict — unknown keys reject.
BINDING_NAME_RE — uppercase, digits, underscores, must start with a letter. The Action can emit a wrangler.toml from this section so you can keep both files in sync without duplicating fields.
signing — bundle envelope signature
replay.nonceKv matches a KV binding name in bindings.kvNamespaces[]. publicKeySecret must appear in secrets[]. The Worker verifies every resync envelope against the trust roots and rejects bundles older than windowSeconds.
Key rotation is supported by listing multiple trust roots; the GH Action signs with the newest, the Worker accepts any matching kid for an overlap window.
auth
Discriminated by provider:
*Secret name MUST appear in the top-level secrets[] list; the cross-validator enforces it.
secrets
[A-Z][A-Z0-9_]* (SCREAMING_SNAKE_CASE). Inline values are not permitted by the schema.
environments
Per-environment overlay. Scalars and nested objects deep-merge; bindings REPLACES (mirrors Cloudflare’s [env.X] non-inheritance rule).
frontmcp deploy build --env production or via the GH Action’s environment: input.
Cross-field validation
Beyond the per-field schema, the parser runs a final cross-validation pass that catches:| Check | Error |
|---|---|
auth.*Secret referenced but not declared | Secret "<NAME>" referenced at <path> is not declared in secrets[] |
signing.trustRoots[].publicKeySecret not declared | Same shape |
signing.replay.nonceKv not in bindings.kvNamespaces[] | signing.replay.nonceKv "<X>" does not match any bindings.kvNamespaces[].binding |
skills.alwaysLoad[] entry not kebab-case | skills.alwaysLoad entry "<X>" is not a valid kebab-case skill id |
| Tag filter references an undeclared tag | skills.tags references unknown tag "<X>" (not in tags[]) |
{ ok: false, errors: string[] } rather than throwing on the first.
Resource-change classification (auto-derived)
The classifier output is implicit in the manifest — every OpenAPI operation it discovers is classified by HTTP semantics. The full ruleset:| Method | Path shape | Matching GET? | Surface | Notify on success |
|---|---|---|---|---|
GET | path-param (/users/{id}) | (the op) | both | — |
GET | no path-param (/users) | (the op) | resource | — |
POST | collection (/users) | yes | tool | list_changed on self |
POST | singular (/users/{id}) | yes | tool | updated on self |
POST | action (/users/{id}/reset-pw) | no | tool | updated on parent |
POST | any | no | tool | — |
PUT / PATCH | any | yes | tool | updated on self |
PUT / PATCH | any | no | tool | updated on parent (if any) |
DELETE | singular | — | tool | list_changed on parent |
DELETE | collection | yes | tool | list_changed on self |
classification.rules.
The runtime emits the notification once per call regardless of which skill’s binding made the call — the notification is a property of the operation, not the skill. Two skills both calling the same
PUT /users/{id} → still one event.Public surface (TypeScript)
For host code that wants to load + validate manifests programmatically:Skills-Only Deployment
The model behind the manifest.
Cloudflare Worker target
How the Worker consumes a signed bundle.