Skip to content

Strict ingredient schema: refuse unknown keys, pass MCP server config through - #12

Merged
llima merged 13 commits into
mainfrom
feat/strict-ingredient-schema
Sep 25, 2026
Merged

llima merged 13 commits into
mainfrom
feat/strict-ingredient-schema

Conversation

@llima

@llima llima commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • Version bumped to 0.3.0 (minor: a Forge with an unknown ingredient key stops loading). Merging publishes craftar@0.3.0 to npm after approval in the npm environment.
  • Strict ingredient schema: an unknown key in ingredient.yaml (top level or origin) fails the Forge load, naming the file and every unknown key at once, instead of being stripped where forge unify could resolve and delete a variant that differed only there.
  • MCP server passes through: command/args/url/env/type are checked, and the server is returned as the very object the Forge holds, so undeclared keys (headers, timeout, disabled…), the source key order and __proto__ reach .mcp.json and .kiro/settings/mcp.json.
  • Validated fingerprints: both sides of every fingerprint hash zod-validated metadata through one fingerprintDir (the importer's second, unvalidated copy is gone).
  • Stricter import: an ingredient that would not load back (a non-string MCP env value, a name that is not slug-like) fails the whole import naming its source, with the Forge untouched; so does an existing Forge ingredient with an unknown key or a YAML syntax error.

Emitted bytes — which workspaces see a change

  • .mcp.json and .kiro/settings/mcp.json show update once for workspaces whose Forge MCP servers have keys beyond command/args/url/env/type, or keys stored out of that order.
  • An unlocked .mcp.json that read collision because of undeclared keys or type first now adopts.
  • Servers with only declared keys, in schema order, emit exactly the 0.2.4 bytes (pinned by literal-string tests for both targets). No other target file changes.
  • Saved forge unify plans go stale for hand-written ingredient.yaml that omits defaulted fields (their fingerprint moves); --plan refuses them and --save-plan regenerates. The golden unify plan was regenerated by its script: only its two fingerprint lines changed.

Upgrade note

See README § Upgrading → to 0.3.0: rename or remove unknown keys and commit the Forge; expect one update on the MCP files; re-save stale unify plans; import is stricter and may reuse where it used to create a variant.

Test plan

  • npm run typecheck — passes
  • npm run build — passes
  • npm test (Linux, --exclude test/ci.test.ts) — 19 files passed / 1 skipped; 350 passed / 5 skipped. With ci.test.ts included (pre-flight): 388 passed / 5 skipped
  • Oracle skipped — no fixture on this machine. This change alters emitted MCP bytes; the maintainer explicitly accepted shipping without the oracle. The byte claims rest on the emitter, status and import→sync tests
  • CI green on Linux and Windows (Windows not run locally)
  • node-cli-reviewer and docs-author: clean after one correction round (AC 1/AC 5 CLI tests, error path format, README cases, stage parity and import→sync round trip)

An unknown key in ingredient.yaml (top level or origin) now fails the Forge load,
naming every key, instead of being stripped where forge unify cannot see it.
The server is checked against its declared keys and returned as-is, so undeclared
keys, the source key order and __proto__ survive to .mcp.json; a type-first server now adopts.
fingerprintDir parses ingredient.yaml through IngredientSchema, so both sides of a
comparison carry the same defaults. Fingerprints move only for hand-written metadata that
omits defaulted fields; the golden unify plan is regenerated and only its two fingerprints change.
…gerprintDir

The stage now hands fingerprintDir an overlay reader instead of carrying its own copy,
so the second unvalidated ingredient.yaml parse is gone and both paths validate alike.
…ging it

0.2.4 wrote Forges no command could load (a numeric MCP env value, a name that is not
slug-like); the import now refuses them naming the source, with the Forge untouched.
…nify

Undeclared keys and the Forge's key order reach .mcp.json and the Kiro file, schema-ordered
servers keep their 0.2.4 bytes, and a server difference only in headers keeps the variant.
Unknown ingredient keys are refused, MCP servers pass through verbatim, and the upgrade
section lists the MCP updates, stale unify plans and the stricter import.
A cyclic ingredient.yaml was reported by absolute path while every other metadata error
is relative to the working directory; spec 07 §6.1 wants one form.
…nc --check

AC 1 was tested through forge variants only, and AC 5 through the update state only.
The import row cited a case the importer cannot hit and missed an existing Forge
ingredient that no longer loads; the upgrade note now says to commit the fixed Forge.
…c round trip

The parity check compares fingerprintDir through the stage with the flushed directory
directly, and an imported MCP server with undeclared keys adopts and syncs byte for byte.
Error messages name files with path.relative, which uses backslashes on Windows; the new
assertions expected POSIX separators and failed on windows-latest.
@llima
llima merged commit 5f04612 into main Sep 25, 2026
8 checks passed
@llima
llima deleted the feat/strict-ingredient-schema branch September 25, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant