@agent-facets/protocol for reading the MCP server inventory a 0.4 lockfile records. They run on Node 22 or newer, need no Bun, and read no file, cache, archive, network, server, or adapter. Every expected outcome is a returned value, not a thrown error.
Parse first
Derivation takes a validated document, never raw bytes. Parse withparseLockfileDocument, which dispatches on the exact lockfileVersion and returns { ok: true, data } or { ok: false, failure }.
deriveLockedMcpInventory
DeriveLockedMcpInventoryResult
Describes the inventory the lockfile records, under the same naming, alias, omission, and composition rules as materialization. It takes no project-manifest overrides: the result is what the lockfile says, not what a changed
facets.json would produce.success
authored lists every recorded server, omitted ones included, ordered by facet then authored name. servers is the selected set in portable identity order. Both are empty for a 0.4 lockfile with no servers.The document is
0.2 or 0.3, which record no server inventory. requiredVersion is 0.4. This is returned even for an empty legacy document or one carrying an unrecognized servers field, so a legacy lockfile never reads as an empty inventory.conflict
Two or more different fingerprints claim one effective name.
groups names every conflicting claimant with its fingerprint, and authored is still complete. No selected set is returned, not even the servers outside the conflict.Authored and selected views
authored record
facet, source, version, facetIntegrity, authoredName, fingerprint, and the recorded materialization (all three arms). One per record in the lockfile.selected server
effectiveName, fingerprint, and origins, a non-empty list of every active claim. Each origin is { facet, source, version, facetIntegrity, authoredName, materialization }, ordered by facet then authored name.authored and never in origins.
Output is readonly and copied. It shares no array, disposition, or source object with the input, and it carries only the fields listed here: unrecognized lockfile fields never appear in it.
planLockedServerInventory
PlanLockedServerInventoryResult
Plans hypothetical intent over fingerprint-only inventories. Each contribution is
{ facet, servers: [{ name, fingerprint }], overrides? }, where overrides uses the facets.json materialization shape. Use it to ask what a manifest’s intent would select from recorded inventories.{ ok: true, planned, configurations, staleOverrides }:plannedlists every server with its resolved disposition, omitted ones included.configurationslists each selected identity with all its claimants.{ ok: false, reason: 'invalid-alias', problems }: an alias breaks the server name grammar.{ ok: false, reason: 'collision', groups, staleOverrides }: different fingerprints claim one effective name. Stale overrides are still reported.
Verify exported servers
An exporter that wrote portable MCP declarations can check them against a committed lockfile. Derive what the lockfile selects, fingerprint each declaration you actually exported, and compare the two complete sets. The comparison is yours to own: the package publishes derivation and fingerprinting, not a comparison function.verify-exports.ts
filesystem server is aliased to workspace-files, and whose docs server is authored:
Reordering environment assignments is not a change, because the canonical fingerprint sorts them.