INTERFACE=provenance-mcp:stdio/v1
TRANSPORT=stdio
REMOTE_TRANSPORT=NOT_IMPLEMENTED
CORE_SCHEMA_CHANGES=NONE
Phase 7 exposes the existing PROVENANCE core, local store, custody ledger, and verifier through MCP.
MCP is an interface layer. Deleting provenance_mcp/ does not change any existing evidence identity or verification rule.
The stdio server is dependency-free and supports both current MCP lifecycle eras used by the reference tests:
2026-07-28 modern/stateless era
2025-11-25 initialize-handshake era
Modern requests carry their protocol revision and client capabilities in params._meta. The connection is pinned to the era selected by its opening request.
The server emits one JSON-RPC message per UTF-8 line on stdout and writes no ordinary application output to stdout.
Ambiguous/non-JSON framing is rejected before MCP dispatch. Duplicate object keys and non-finite JSON numeric tokens are not silently normalized.
python3 -m provenance_mcp \
--store /path/to/provenance-store \
--custody /path/to/provenance-custodyBoth roots are explicit. Phase 7 does not start a network listener and does not infer a remote endpoint.
An MCP server is permanently bound to the filesystem identity of the store root it opened at construction. Current-state refreshes happen in-place through that bound store object. If the configured store pathname is renamed/replaced so it resolves to a different directory inode, the server rejects the operation rather than silently switching to the replacement store.
Refresh holds the originally bound root directory descriptor across authoritative HEAD reconstruction. HEAD is read descriptor-relatively from that bound root, and reconstructed state is staged from that descriptor-bound evidence only.
The staged state is committed before the final pathname continuity observation; if that observation fails, the previous accepted in-memory state is restored. No filesystem-derived state assignment occurs after the final continuity observation.
A non-cooperating process can still rename a POSIX pathname after the final observation has completed. That cannot redirect the just-completed refresh: its accepted state came entirely from the original bound inode. A subsequent store operation revalidates the configured root and rejects the replacement rather than using it.
Records caller-supplied JSON without upgrading it to observed truth.
Caller content is stored as:
provenance.mcp-declaration.v1
evidence_class = DECLARED
The MCP server separately creates a unique retained receipt:
provenance.mcp-receipt.v1
evidence_class = OBSERVED
actor = provenance-mcp:stdio/v1
This separation matters.
Two byte-identical declarations may share the same declaration identity, but two MCP calls receive different receipt artifacts/events. Therefore:
SAME DECLARATION
!=
SAME OCCURRENCE
A successful record call is also durably represented in the MCP operational working-state journal before the tool returns. The journal contains identities and working membership, not a second evidence schema, and is excluded from finalized evidence identity.
The working-state journal is multi-writer safe. All MCP server processes sharing a store serialize working-state recovery, record publication, journal replacement, and finalization through one POSIX advisory lock in the store root. After acquiring that lock, a server reloads the current store HEAD and replays the complete durable journal before performing its mutation. A writer therefore extends the merged working set instead of replacing it from stale process-local state.
Journal read, replacement, and removal use the same already-validated store-root descriptor held by that lock; they do not reopen the mutable configured pathname.
The server performs a final pathname continuity observation after the journal/evidence mutation is complete. If a replacement is visible at that observation, the request fails. If an external process renames the pathname only after that final observation has completed, the operation may return successfully, but its artifacts, events, and recovery journal are all already durably co-located on the original bound store inode. Nothing is redirected to the replacement root, and the next MCP operation rejects the configured-path identity change.
This is the enforceable POSIX guarantee: descriptor-bound evidence continuity, not prevention of an unrelated process renaming a pathname after the server's final syscall.
This rule covers both concurrent writes and stale long-lived server instances:
SERVER A ACKNOWLEDGES RECORD A
SERVER B ACKNOWLEDGES RECORD B
↓
DURABLE JOURNAL = A ∪ B
↓
ANY LATER FINALIZER = A ∪ B
The same lock also serializes shared custody-ledger initialization for concurrently starting MCP servers using the same roots.
After process restart, the server revalidates the journaled artifact records, retained bytes, and event identities against the object pool before reattaching them to the working snapshot. The journal's pending_verified_artifacts set must exactly equal the journaled artifact membership; recovery rejects extra, missing, or duplicate pending subjects before any VERIFIED custody can be appended.
If the store HEAD advanced because finalization committed immediately before a crash, restart requires the journaled members to be present in that verified HEAD and completes fresh VERIFIED custody before clearing the journal.
The caller cannot supply an evidenceClass override.
Resolves a PROVENANCE identity to its evidence kind/resource, or reports the current working/finalized store summary.
Delegates snapshot creation to LocalEvidenceStore.finalize(), requires its independent verification result, then appends VERIFIED custody for Phase 7 artifacts and the manifest.
Returns the existing independent bundle-verification report and independent custody-verification report.
The MCP layer does not implement a second verifier.
provenance.package
Copies the current immutable verified snapshot to a new destination directory, independently verifies the copy, and then appends EXPORTED custody for the manifest.
The destination must not already exist and must be outside the live evidence store, custody ledger, and source snapshot tree. This prevents an export from recursively copying into itself or contaminating the evidence roots it is meant to preserve.
Export publication is descriptor-bound. The destination parent is opened as a non-symlink directory descriptor, ancestry is checked against the protected evidence roots, and the snapshot is copied through that held descriptor rather than by reopening the checked pathname. The parent pathname/identity and protected-root ancestry are checked again before success is reported. A parent rename/symlink substitution during export therefore fails and the descriptor-bound partial copy is removed.
This Phase 7 export is a snapshot-copy interface only. It does not claim to complete the later Phase 11 portable forensic-package contract, which may additionally package custody, schemas, verification metadata, and declared gaps.
Phase 7 exposes:
provenance://event/<identity>
provenance://artifact/<identity>
provenance://manifest/<identity>
provenance://custody/<identity>
Event, manifest, and custody resources return canonical JSON.
Retained artifact resources return exact bytes through MCP blob content. DIGEST_ONLY artifacts return explicit retention metadata rather than fabricated content.
Artifact reads use the current working store binding, not merely the most recently finalized manifest binding. A monotonic DIGEST_ONLY → CONTENT_RETAINED upgrade is therefore visible immediately, before the next finalization.
Resource reads use identity-derived paths and descriptor-safe non-symlink file opens.
Before serving a resource, Phase 7 recomputes the identity bound by the URI:
event -> recompute event identity from canonical core
custody -> recompute custody identity from canonical core
manifest -> recompute manifest identity from canonical core
artifact -> recompute artifact-record identity
retained bytes -> recompute raw SHA-256 content identity
A canonical object placed under the wrong content-addressed filename is therefore not served as that identity. Resource corruption is reported as an internal evidence/read failure rather than silently relabeling the bytes.
Unknown resource URIs return MCP invalid-params/resource-not-found semantics instead of an internal error.
The central Phase 7 rule is:
CALLER ASSERTION
-> DECLARED
MCP SERVER RECEIPT OF CALL
-> OBSERVED
An MCP client saying:
"I called tool X because Y"
does not make X or Y independently observed facts.
PROVENANCE can observe that the MCP server received that declaration. It cannot silently promote the declaration's truth.
The Phase 7 integration test launches the real server as a child process and communicates over stdio.
The executed flow is:
server/discover
↓
tools/list
↓
provenance.record
↓
repeat identical provenance.record
↓
provenance.inspect
↓
provenance.finalize
↓
resources/read
↓
resources/list
↓
provenance.verify
↓
provenance.export
↓
independent verify_bundle(export)
The test requires:
caller declaration event = DECLARED
MCP receipt event = OBSERVED
identical declarations may deduplicate
identical calls must not collapse
finalized bundle verifies
custody verifies
exported copy independently verifies
legacy initialize era still lists tools
modern era remains connection-pinned
Phase 7 does not implement:
remote MCP transport
HTTP MCP transport
MCP authorization
sampling
elicitation
tasks extension
MCP Apps
provider authentication
generic provider adapters
Rust CLI/TUI
read-only web UI
full Phase 11 forensic-package export
Those capabilities must earn their place in later roadmap phases.
MCP IS AN INTERFACE.
CALLER CLAIMS STAY DECLARED.
OCCURRENCES MUST NOT COLLAPSE.
VERIFICATION REMAINS INDEPENDENT.
provenance.package takes:
{"destination":"/path/to/forensic-package"}and creates a finalized provenance.forensic-package.v1 archival package through the shared Phase 11 producer/verifier.
It is intentionally separate from provenance.export, whose Phase 7 contract remains a snapshot copy.
After successful package publication, MCP appends an EXPORTED custody record for the evidence manifest and binds its related_identity to the package identity.
See PACKAGE.md.
Phase 12 does not add private-key signing tools to the MCP interface.
Signing authority remains an explicit local operator capability exposed by the terminal CLI / Python producer API. Private keys are not accepted as MCP tool arguments and must not become ordinary evidence or custody payloads.
The detached records themselves remain independently verifiable through the Phase 12 verifier contract.
See TRUST.md.
The Phase 14 reference redaction producer is exposed through the local terminal/Python surface rather than as a new MCP tool.
This avoids turning raw sensitive source bytes or privacy-policy decisions into ordinary MCP arguments. Existing MCP evidence semantics remain unchanged.
Selective disclosures remain independently verifiable after creation.
See PRIVACY.md.
The Phase 15 reference transfer producer/receiver is exposed through the local CLI/Python API rather than new MCP tools.
Reasons:
sender/receiver private keys must not become ordinary MCP arguments
destination custody roots are operator-controlled local state
offline transfer must remain usable without an MCP host
MCP remains optional to independent verification
Existing MCP evidence semantics are unchanged.
See TRANSFER.md.