# 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](../../programmable-api/compatibility-dates.mdx).

## Pin the date in `zuplo.jsonc`

```jsonc
// zuplo.jsonc
{
  "version": 1,
  "compatibilityDate": "2026-03-01",
}
```

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`](../../policies/mcp-token-exchange-inbound.mdx)
policy installs a **response-sending hook** that watches the upstream response
for a `401 Unauthorized` status. When it sees one, the hook:

1. Reads the new `scope=` value from the upstream's `WWW-Authenticate` header,
   if present.
2. Force-refreshes the user's upstream OAuth token (or runs the upstream auth
   flow again if refresh fails).
3. 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 `initialize` icon-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-required` error or a generic 401 on routine calls when their upstream
  provider rotates tokens, instead of the gateway recovering invisibly.
- **`tools/list` filtering 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:

1. Connect an MCP client to a route protected by `mcp-token-exchange-inbound`.
2. Make any successful call (e.g., `tools/list`).
3. From the upstream provider's admin UI, revoke the gateway's client/grant for
   your user. This invalidates the stored upstream access token.
4. Make another `tools/list` call.

With `compatibilityDate >= 2026-03-01`:

- The upstream returns 401.
- The retry hook refreshes the token (or returns a `connect-required` if 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:

```diff
 {
   "version": 1,
-  "compatibilityDate": "2024-04-26"
+  "compatibilityDate": "2026-03-01"
 }
```

Compatibility dates are cumulative — moving to `2026-03-01` adopts every flag
from prior dates. Review the
[Compatibility dates](../../programmable-api/compatibility-dates.mdx) 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](../../programmable-api/compatibility-dates.mdx) — the
  general Zuplo compatibility-date system.
- [`mcp-token-exchange-inbound`](../../policies/mcp-token-exchange-inbound.mdx)
  — the policy whose retry hook needs the chained-hooks semantics.
- [`McpProxyHandler`](./mcp-proxy-handler.mdx) — the route handler that triggers
  the response-hook chain on every request.
