@receiz/mcp-server
Version:
MCP server for agents building and verifying Receiz proof-native applications through the canonical Receiz SDK rails.
69 lines • 3.91 kB
JavaScript
import { createHash, randomBytes } from "node:crypto";
import { digestReceizV124McpMaterial, resolveReceizV124McpMaterial, } from "./v124Operations.js";
/**
* Host-private execution consent, following the v112 consumed-attempt pattern.
* No object returned here constitutes SDK plan admission or proof authority.
* The canonical dispatcher must supply and execute its original SDK-held plan.
*/
export function createReceizV125TrustAttemptCustody(input) {
if (!input.applicationAudience || input.applicationAudience !== input.applicationAudience.trim()) {
throw new TypeError("V125_TRUST_MCP_APPLICATION_REQUIRED");
}
const options = { ...input.materialOptions, applicationId: input.applicationAudience };
// A fresh runtime never regenerates a previously consumed confirmation.
const runtimeNonce = randomBytes(32).toString("hex");
const attempts = new Map();
const activeByBasis = new Map();
let sequence = 0;
const requestDigest = (request) => digestReceizV124McpMaterial({
applicationAudience: input.applicationAudience, operation: request.operation,
proofReference: request.proofReference, selectedKai: request.selectedKai,
idempotencyKey: request.idempotencyKey,
});
const resolve = async (request) => structuredClone(await resolveReceizV124McpMaterial(input.client, options, request.proofReference, "operation-plan"));
return Object.freeze({
async prepare(request, plan) {
const material = await resolve(request);
const [requestHash, materialHash, planHash] = await Promise.all([
requestDigest(request), digestReceizV124McpMaterial(material), digestReceizV124McpMaterial(plan),
]);
const basis = `${requestHash}:${materialHash}:${planHash}`;
const active = activeByBasis.get(basis);
if (active) {
const held = attempts.get(active);
if (!held.consumed && held.plan === plan)
return Object.freeze({ confirmationDigest: active });
}
const confirmationDigest = createHash("sha256").update(JSON.stringify({
schema: "receiz.mcp.trust-attempt.v125", runtimeNonce, sequence: ++sequence, basis,
})).digest("hex");
attempts.set(confirmationDigest, { requestDigest: requestHash, materialDigest: materialHash,
planDigest: planHash, plan, consumed: false });
activeByBasis.set(basis, confirmationDigest);
return Object.freeze({ confirmationDigest });
},
async reserve(request, confirmationDigest) {
const held = attempts.get(confirmationDigest);
if (!held)
throw new TypeError("V125_TRUST_MCP_CONFIRMATION_INVALID");
if (held.consumed)
throw new TypeError("V125_TRUST_MCP_CONFIRMATION_CONSUMED");
if (await requestDigest(request) !== held.requestDigest)
throw new TypeError("V125_TRUST_MCP_CONFIRMATION_INVALID");
// Recheck after asynchronous hashing, then synchronously reserve before any
// resolver or SDK await. An exception or unknown outcome stays consumed.
if (held.consumed)
throw new TypeError("V125_TRUST_MCP_CONFIRMATION_CONSUMED");
held.consumed = true;
const material = await resolve(request);
const [materialHash, planHash] = await Promise.all([
digestReceizV124McpMaterial(material), digestReceizV124McpMaterial(held.plan),
]);
if (materialHash !== held.materialDigest || planHash !== held.planDigest) {
throw new TypeError("V125_TRUST_MCP_MATERIAL_CHANGED");
}
return Object.freeze({ material, plan: held.plan });
},
});
}
//# sourceMappingURL=v125TrustCustody.js.map