Skip to content
Developer Preview

Compatibility

You will upgrade the host more often than you upgrade your client. This page says what keeps working when you do, one surface at a time, and — where a guarantee does not exist yet — says that plainly instead of leaving you to infer one.

The wire evolves by addition. A newer host may add methods, notifications, error kinds, and optional fields. A client written against an earlier version of the protocol keeps working against it.

Three rules follow from that, and they are the ones worth building into your client:

  • A schema fingerprint you do not recognise is a warning, never an error. The handshake result carries schema.fingerprint, and the TypeScript SDK compares it to the one it was built against and sets fingerprintWarning when they differ. Surface it if you like; do not refuse to run on it. Treating a mismatch as fatal turns every routine host upgrade into an outage for your users. (The Python SDK is the one deliberate exception, for its unbundled-host install story — see below.)
  • Treat every enumerated value as an open set. turn/completed carries a terminal that is "completed", "cancelled" or "failed" today, and the disposition on a turn/start ack is "started", "queued" or "steered" today. A future host may send a value neither this page nor your code knows. Handle the ones you act on and treat anything else as its category’s default — an unknown terminal is still terminal.
  • An unknown member is not an error. Read the fields you need by name and ignore the rest, rather than validating that a frame contains exactly what you expected.

Changes that addition alone does not cover are the exception, and each one is listed on the protocol changelog. That page is generated from the record such a change has to be entered in before it can reach the schema, so it cannot fall behind the protocol.

While the protocol is before its 1.0 release, no removal window is owed: a surface can change without first shipping a deprecation notice. From 1.0 onward, removal follows a notice in an earlier release, and the changelog lists each completed one.

The SDK pins the fingerprint it was generated against and compares it at handshake, which is where fingerprintWarning comes from. It does not refuse a host it does not recognise, and neither should you.

An older SDK against a newer host is the supported direction, and it is the direction that follows from additive evolution: fields the SDK does not know about are carried through untyped rather than rejected. The reverse — a newer SDK against an older host — is not a supported configuration. The SDK may call a method that host does not implement, and you will see the error for an unknown method rather than a graceful degradation.

The Python SDK (muse_code, distribution muse-code-sdk) carries the same fingerprint pin with the opposite posture, deliberately: it fails at initialize when the served fingerprint differs from the pin, with an exact MuseHostMismatchError naming the required host version and pointing at this page. The wheel does not bundle a host — you pair an installed muse with an installed SDK yourself — and the strict gate is what makes a wrong pairing loud at connect time instead of undefined behaviour later. Seeing that error means the pair is mismatched; update either side to a matching pair.

The row below is generated from the SDK source tree with every docs build, never written by hand:

SurfaceValue
Packagesmuse-code-msp (import muse_code_msp), muse-code-sdk (import muse_code)
Supported Python3.10 and newer (>=3.10)
Runtime dependencypydantic==2.13.5 (exact pin; the SDK's only one — the wire-types package has none)
Schema fingerprint (stable bundle)sha256:7469c9e352e67def4a59df7e439984d7194fa351e1c8b7abb34060fd977ced81
Schema version1
WheelHost versionSchema fingerprintPython
muse-code-msp 1.3.01.3.0sha256:ab69549a7ebb423fce94068762da0b5ff3cdec1f8fc263dcc17248eda117f852>=3.10
muse-code-sdk 1.3.01.3.0sha256:ab69549a7ebb423fce94068762da0b5ff3cdec1f8fc263dcc17248eda117f852>=3.10

The compatibility window for persisted sessions across host upgrades is a stated contract, and it is unbounded backward. Every session written by an earlier released host stays openable and servable by a newer one: session/resume must reconstruct the transcript and state its durable record supports. There is no vintage cutoff, no “N releases back”. The one recorded carve-out is a narrow dev-stage vintage that wrote an early flat form of the tool-search sidecar, before its current versioned form; it loses in-flight continuation — typed when it applies — but not its history. Any future narrowing of this window would be an explicit, stated contract change, never a silent behavior shift.

A stated window does not mean history omission can never happen — it means omission is typed, and your client has to handle the shape it arrives in:

  • session/resume returns a history whose mode must be read before items. For a short session it is { mode: "inline", items: [...] }. For a long or compacted one you get a snapshot with items: null.
  • mode: "none" is a state your client must be able to render, and it now says why. It means the host is not serving history for this session, and it carries a noneReason member naming which producer omitted the history — an open enum whose current values are "excluded", "cursorSuffix", "historyBudget", "projectionUnavailable", and "projectionReadLimit". Treat it as open: render unknown values generically. The values split by recovery: historyBudget and projectionReadLimit mean the history still exists — page it in via view/page; excluded and cursorSuffix honor your own request; only projectionUnavailable is unrecoverable by paging. A current host never serves a bare, unexplained mode: "none" — that would be a host defect — but an older host predating this member omits it; treat an absent noneReason the same as an unknown one.
  • The session itself still resumes. session/resume returning no history does not mean the session is unusable — resumed.session, resumed.viewCursor and resumed.pendingRequests are still yours to work with.

Build for that: when history is genuinely unavailable (projectionUnavailable), show the user a session whose earlier turns cannot be displayed — with the reason the host gave — rather than treating it as corrupt or hiding it; for the pageable reasons, fetch before concluding anything is missing. A client that assumes history is always available will show an empty conversation where there was a real one — and one built from the omission case alone will permanently hide history that a single view/page call recovers.

  • Protocol changelog — the non-additive changes, and the deprecations, in full.
  • Resume — the cursor and history contract in detail, including what each history.mode means for your UI.
  • MSP concepts — sessions, turns, approvals and exit classification as one model.