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