Skip to content
Developer Preview

Handle a fingerprint mismatch

One day your app will connect to a host that is newer than the SDK you built with. This page shows you how to notice that at the handshake — and why the right response is a warning in your logs, not an error in your user’s face.

The one idea to take away: a schema fingerprint mismatch is advisory. The protocol evolves by adding optional things, so an older SDK keeps working against a newer host. The SDK already follows this posture: it compares fingerprints at the handshake and hands you a warning value instead of throwing. Your only job is to not turn that warning back into an error.

Every connection opens the same way — your client sends initialize:

{"dir":"client","raw":"{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"clientInfo\":{\"name\":\"conformance\",\"version\":\"0.0.0\"}}}"}

The host’s reply describes itself, and one member of that reply is the schema object: its version is the served protocol-schema version and its fingerprint is the value this whole page turns on.

This page shows you no copy of that reply — a copy is exactly the kind of content that drifts. The tested recipe reads the real thing instead: it takes schema and schema.fingerprint from a live release-built host’s initialize result, by those member names, so if either member ever moves or is renamed, the cookbook-journeys job goes red — this page cannot quietly disagree with the wire.

The SDK pins the fingerprint it was written against as EXPECTED_SCHEMA_FINGERPRINT and compares the served value against that pin during initialize.

After the handshake, check one field:

const msp = await handshake.initialize({
clientInfo: { name: "my-app", version: "1.0.0" },
});
if (msp.fingerprintWarning !== undefined) {
// The host serves a schema this SDK was not built against. Surface it
// where a developer will see it — then carry on. Everything your SDK
// version knows how to do still works.
console.warn(msp.fingerprintWarning.message);
}

That is the whole recipe. The warning value tells you both sides of the comparison (pinned is what your SDK expected, served is what the host advertised) and carries a message written for a human reading logs.

If you want the same comparison outside a live connection — say, in a diagnostic screen — call it directly:

import { EXPECTED_SCHEMA_FINGERPRINT, checkServedFingerprint } from "@muse-code/sdk";
const warning = checkServedFingerprint(servedFingerprint);
// undefined on a match; a descriptive warning value on a mismatch.
// It never throws.

Do not refuse to run when the fingerprints differ. The three-line mistake looks responsible and is not:

// WRONG: this turns every host upgrade into an outage for your users.
if (msp.fingerprintWarning !== undefined) {
throw new Error("schema mismatch");
}

A mismatch means the host moved ahead of your SDK, which is the normal state of the world between your releases. Log it, surface it in diagnostics, and plan an SDK update — while your app keeps working.

You need Node 20 or newer, an installed muse binary, and the SDK your own application depends on — npm install @muse-code/sdk. The recipe programs live in the SDK repository:

Terminal window
git clone https://github.com/meta-models/muse-code-sdk
cd muse-code-sdk
npm ci && npm run build --workspace @muse-code/sdk
MUSE_BIN=$(command -v muse) \
npm run recipes --workspace @muse-code/sdk-cookbook -- --only fingerprint-mismatch

The runnable source for this page is published in full, comments and all, as the fingerprint-mismatch example. It is the source that runs, not a retelling of it: it is executed end to end on every change to this area, and every docs build checks the wire exchange above against the committed transcript, so this page cannot drift from what actually runs.

The harness spawns its hosts with MUSE_EXPERIMENTAL_SDK_ENABLED set explicitly. muse serve now runs by default; the variable survives as an off-switch, and the harness pins the run open so an operator’s off in the environment cannot silently change what this page proves. That is not advice for your application — build against muse serve as a supported command.