Configuration Change Receipts: Binding the Server Manifest
Asqav requires every protectmcp:lifecycle:configuration_change receipt to carry config_manifest_digest, a SHA-256 over the canonical-JSON MCP server manifest. The cross-field guard rejects a configuration_change receipt without the digest at the wire layer so a silent capability change cannot be smuggled into the audit log.
What is the config_manifest_digest field?
config_manifest_digest is an OPTIONAL string on most sign requests and REQUIRED on receipt_type=protectmcp:lifecycle:configuration_change. The wire format is sha256:<64 lowercase hex> matching the other digest fields on the signing surface.
Compute the value with the public canonicaliser: "sha256:" + hashlib.sha256(asqav.canonical_json(manifest_dict)).hexdigest(). Because asqav.canonical_json serialises under JCS, two auditors who receive the same manifest dict produce identical digests regardless of key insertion order.
How does Asqav address silent capability drift?
The NSA CSI describes the configuration-change risk in the security concerns chapter:
Poor approval workflows for the deployment, integration, and consumption of MCP servers in organizations - especially when controls are insufficient and rely on individual users to authorize, integrate, and use them - can lead to security vulnerabilities. These insufficient controls can manifest themselves in many ways. Examples include unrestricted system change permissions, lack of an approval process for AI tools, poor approval processes (or even pencil-whipped processes), and improper user training. NSA CSI U/OO/6030316-26, p.5
Asqav reads this as: every change to the MCP server's advertised capability surface must be a signed, auditable event. The config_manifest_digest field is the binding. When the MCP server reloads its config (new tool registered, schema updated, environment changed), the SDK or the orchestrator emits a protectmcp:lifecycle:configuration_change receipt with the digest of the new manifest. Two consecutive configuration_change receipts with different digests prove the surface changed. An auditor can rebuild the chain by following the digest values.
The cross-field guard message that fires when the digest is missing is verbatim:
configuration_change_missing_config_manifest_digest:
protectmcp:lifecycle:configuration_change receipts require
config_manifest_digest (sha256:<64 hex>).
The SDK, the IETF conformance vector, and the cloud Pydantic validator all assert this exact byte sequence so a third-party verifier can pattern-match on it.
How do I emit a configuration_change receipt?
The manifest dict can be any JSON-serialisable shape. The Asqav recommendation is to mirror the MCP server's tools/list plus resources/list plus prompts/list responses since those define the agent-visible surface.
JSON wire example
{
"action_type": "mcp:server_reload",
"receipt_type": "protectmcp:lifecycle:configuration_change",
"config_manifest_digest": "sha256:5b41362bc82b7f3d56edc5a306db22105707d01ff4819e26faef9724a2d406c9"
}
Python SDK
import hashlib
import asqav
asqav.init()
agent = asqav.Agent.create("mcp-server-supervisor")
new_manifest = {
"tools": [
{"name": "users.list", "schema": {"type": "object", "properties": {}}},
{"name": "users.create", "schema": {"type": "object", "properties": {"email": {"type": "string"}}}},
],
"resources": [],
"prompts": [],
}
digest = "sha256:" + hashlib.sha256(asqav.canonical_json(new_manifest)).hexdigest()
receipt = agent.sign(
"mcp:server_reload",
{"trigger": "hot_reload", "previous_digest": "sha256:0000..."},
receipt_type="protectmcp:lifecycle:configuration_change",
config_manifest_digest=digest,
)
curl
curl -X POST https://api.asqav.com/api/v1/agents/agt_super_001/sign \
-H "X-API-Key: $ASQAV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action_type": "mcp:server_reload",
"receipt_type": "protectmcp:lifecycle:configuration_change",
"config_manifest_digest": "sha256:5b41362bc82b7f3d56edc5a306db22105707d01ff4819e26faef9724a2d406c9"
}'
How does an auditor follow the digest chain?
Two consecutive configuration_change receipts for the same agent give an auditor a Merkle-style chain of capability snapshots. The producer SHOULD include the previous receipt's digest in the request context so the chain is explicit even when receipts are filtered or partial. The auditor queries SELECT signed_at, config_manifest_digest FROM signature_records WHERE agent_id=? AND receipt_type='protectmcp:lifecycle:configuration_change' ORDER BY signed_at and gets the full configuration history of the agent over its lifetime.
What happens if I miss the digest?
HTTP 422 with the exact guard message above. The receipt is not minted and the wire layer returns the error to the caller. The same guard runs in the SDK pre-flight (when compliance_mode=True, the default) so the producer sees the failure before the network roundtrip.