UNPKG

@receiz/mcp-server

Version:

MCP server for agents building and verifying Receiz proof-native applications through the canonical Receiz SDK rails.

59 lines • 4.2 kB
import { randomUUID } from "node:crypto"; import { createReceizInitialSubjectStateV122, prepareReceizInitialTemporalSubjectSourceV125, readReceizIdentityPublicBinding, readReceizTemporalSubjectSourceV125, receizBase64UrlEncode, verifyReceizArtifact, } from "@receiz/sdk"; import { createReceizNodeOfflineKaiProofFactory } from "@receiz/sdk/offline/kai/node"; async function openSealed(result) { const file = new File([new Uint8Array(result.artifactBytes)], result.filename, { type: result.mimeType }); const verification = await verifyReceizArtifact(file); if (verification.status !== "verified-artifact") throw new TypeError("RECEIZ_AI_SUBJECT_SEAL_VERIFICATION_REQUIRED"); const artifact = Object.freeze({ schema: "receiz.sealed-artifact-bytes.v124", filename: result.filename, mimeType: result.mimeType, exactBytesB64u: receizBase64UrlEncode(new Uint8Array(result.artifactBytes)), artifactSha256: verification.artifactDigest.value, payloadSha256: verification.payloadDigest.value, }); return { verification, artifact }; } /** Creates a distinct private AI subject beneath the admitted owner's identity. * No HTTP admission, public append, runtime enrollment, or file write occurs. * Persist both returned artifacts in private custody before accepting creation. * The initial source is a new owned document, never the owner's Identity Seal. */ export async function createReceizMcpDeviceAiSubject(input) { const { ownerIdentityAdmission, sealer } = input; const name = input.name ?? "Receiz AI"; if (typeof name !== "string" || !name.trim() || name.length > 200) throw new TypeError("RECEIZ_AI_SUBJECT_NAME_INVALID"); const owner = await readReceizIdentityPublicBinding(ownerIdentityAdmission); if (!owner.signatureVerified || !owner.artifactOwnerReceizId) throw new TypeError("RECEIZ_AI_SUBJECT_OWNER_REQUIRED"); // A new document has its own content and enclosing seal. Its UUID identifies // the document instance only; canonical subject identity derives from the seal. const creationId = randomUUID(); const seed = await sealer.seal({ bytes: new TextEncoder().encode(JSON.stringify({ name, subjectType: "agent", creationId, visibility: "private" })), filename: "ai-subject-source.json", mimeType: "application/json", }); const openedSeed = await openSealed(seed); const createCoordinate = await createReceizNodeOfflineKaiProofFactory(); const coordinate = await createCoordinate({ previousKaiSigilProofHead: null }); const state = await createReceizInitialSubjectStateV122({ documentVerification: openedSeed.verification, ownerIdentityAdmission, kksCoordinate: coordinate, idempotencyKey: `device-ai-subject:${creationId}` }); const candidate = await prepareReceizInitialTemporalSubjectSourceV125({ initialSource: true, subject: { subjectState: state, portable: { ownership: { ownerReceizId: owner.artifactOwnerReceizId, custody: "bearer", proofRef: "genesis" }, provenance: { root: openedSeed.artifact.artifactSha256, appends: [] }, settlement: { state: "none" }, } }, kksCoordinate: coordinate }); const sealed = await sealer.seal({ bytes: candidate.proofObject.payload.bytes, filename: "ai-subject.json", mimeType: "application/json" }); const opened = await openSealed(sealed); const temporal = await readReceizTemporalSubjectSourceV125(opened.artifact); if (temporal.source.subjectState.stateDigest !== state.stateDigest || temporal.source.subjectState.ownerReceizId !== owner.artifactOwnerReceizId || opened.artifact.artifactSha256 === owner.artifactSha256) throw new TypeError("RECEIZ_AI_SUBJECT_SOURCE_BINDING_MISMATCH"); const admittedCoordinate = temporal.coordinate; return Object.freeze({ subjectId: state.subjectId, ownerReceizId: owner.artifactOwnerReceizId, visibility: "private", sourceArtifact: openedSeed.artifact, temporalArtifact: opened.artifact, state: temporal.source.subjectState, temporalHead: temporal.head, coordinate: admittedCoordinate }); } //# sourceMappingURL=deviceAiSubject.js.map