Compatibility dates
The Zuplo MCP Gateway requires compatibilityDate >= 2026-03-01 in
zuplo.jsonc. Without it, the gateway can't reliably recover from expired
upstream credentials mid-request — calls that should succeed silently after an
upstream token refresh return errors instead.
For background on Zuplo's compatibility-date system in general, see Compatibility dates.
Pin the date in zuplo.jsonc
Code
Set the date once, treat it like a dependency version, and bump it deliberately when you want to opt into newer runtime behavior. New Zuplo projects created after March 1, 2026 default to this date or later, so the typical case for "do I need to set this?" is an existing project being upgraded to use the MCP Gateway.
Why the gateway needs this date
The mcp-token-exchange-inbound policy installs a response-sending hook
that watches the upstream response for a 401 Unauthorized status. When it sees
one, the hook:
- Reads the new
scope=value from the upstream'sWWW-Authenticateheader, if present. - Force-refreshes the user's upstream OAuth token (or runs the upstream auth flow again if refresh fails).
- Retries the upstream fetch once with the new credential.
For this to work, the response-hook system has to chain hooks correctly — each
hook needs to receive the previous hook's response so the upstream-401 retry can
replace the response that other hooks then see. That chaining behavior is the
feature that landed in the 2026-03-01 compatibility date.
What older compatibility dates break
Before 2026-03-01, every response-sending hook received the original
upstream response. Only the last hook's output was used. For the MCP Gateway
specifically:
- The upstream-401 retry hook silently loses. If another hook registers
after the retry hook — for example, the capability filter's list-projection
hook, or the
initializeicon-injection hook — that later hook's output overwrites the retried response. The client sees the original 401 even though the gateway successfully refreshed and retried. - Expired upstream tokens aren't auto-recovered. Users see a
connect-requirederror or a generic 401 on routine calls when their upstream provider rotates tokens, instead of the gateway recovering invisibly. tools/listfiltering may drop the retry result. When the capability filter is configured on a route, its response hook runs after the retry hook. Without chaining, the retry hook's refreshed response is discarded.
These aren't crashes — they're correctness issues that surface as user-visible errors that should have been recovered transparently.
How to verify the date is set correctly
After updating zuplo.jsonc, restart zuplo dev (or redeploy) and trigger a
forced upstream refresh:
- Connect an MCP client to a route protected by
mcp-token-exchange-inbound. - Make any successful call (e.g.,
tools/list). - From the upstream provider's admin UI, revoke the gateway's client/grant for your user. This invalidates the stored upstream access token.
- Make another
tools/listcall.
With compatibilityDate >= 2026-03-01:
- The upstream returns 401.
- The retry hook refreshes the token (or returns a
connect-requiredif refresh isn't possible). - The client either sees the successful response or a clear consent prompt, never a stale 401.
With an older compatibility date, the same scenario can leak the original 401 to the client depending on which other hooks are active on the route.
Upgrade path for existing projects
If an existing Zuplo project uses an older compatibility date, upgrading to use the MCP Gateway is one line:
Code
Compatibility dates are cumulative — moving to 2026-03-01 adopts every flag
from prior dates. Review the
Compatibility dates reference
for the full list of behaviors that change, and run your existing tests after
the bump to catch any unrelated runtime behaviors that the new flags adjust.
Related
- Compatibility dates — the general Zuplo compatibility-date system.
mcp-token-exchange-inbound— the policy whose retry hook needs the chained-hooks semantics.McpProxyHandler— the route handler that triggers the response-hook chain on every request.