UNPKG

@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
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