Skip to content

mcp-data-platform-v1.61.4

Choose a tag to compare

@github-actions github-actions released this 14 May 18:08
· 120 commits to main since this release
ee27b44

Highlights

Fix: portal stopped attributing locally-decided revocations to the IdP (#405)

The connection status card previously rendered a single message for every revoked authorization_code connection:

"Previous session rejected by the upstream IdP. The token endpoint at <host> returned refresh_expired."

For two of the three revocation causes, this wording was wrong. The IdP was never called on the tick that produced the deletion — the verdict was reached locally:

  • IdP-disclosed deadline reached. A previous successful refresh response disclosed a hard refresh_expires_in value (e.g., Keycloak's SsoSessionMaxLifespan shrinks refresh_expires_in toward zero across rotations until it pins to an absolute ceiling). When local clock crossed that disclosed deadline, the platform stopped before contacting the IdP — but emitted an event row labeled refresh_failed_revoked with IDPErrorCode: "refresh_expired", then the banner read out that field as if the IdP had returned it.
  • No refresh token stored. When a row had no refresh token to exchange (e.g., scope without offline_access), the platform short-circuited before any network call but produced the same misleading event shape.
  • IdP-rejected invalid_grant. This is the only cause where the IdP actually rejected something — same event shape was used, but for this case the wording was correct.

The fix dispatches the leading event by cause:

Cause IdP called? Lead event
invalid_grant from IdP yes TypeRefreshFailedRevoked (unchanged)
Local deadline reached no TypeRefreshSkippedExpired
No refresh token stored no TypeRefreshSkippedNoToken

The two Skipped types were already defined in pkg/authevents and covered by writer tests, but had no production callers. They're now the canonical lead events for locally-decided verdicts.

The portal banner now branches on the persisted last_revocation.reason field and renders distinct headlines per cause — "Session reached the IdP-disclosed maximum lifetime", "Upstream IdP rejected the refresh token", or "No refresh token is stored for this connection". The History panel labels match. Legacy events still on disk from before this release render through a UI translation layer so (refresh_expired) becomes (IdP-disclosed deadline reached) etc.

Fix: deterministic ordering in the in-memory authevents store

MemoryStore.List returned events in undefined order when back-to-back inserts produced identical OccurredAt timestamps. On Apple Silicon, two consecutive Insert calls without -race slowing them down can both observe the same nanosecond timestamp; sort.Slice is not stable. TestMemoryStoreListFilters failed deterministically under go test and passed only because make verify uses -race. Added a monotonic insertion counter that breaks ties.

The Postgres backend has no comparable tie-breaker (UUID primary key, no secondary ORDER BY) and remains undefined under tied timestamps. The doc on MemoryStore calls this out explicitly: the only production consumer of either backend, admin.lastRevocationFor, tolerates either order because the lead and trail of a revocation pair both carry the same reason string.

Changelog

Bug Fixes

  • ee27b44: fix(connoauth): emit honest event types for locally-decided revocations (#405) (@cjimti)

Installation

Homebrew (macOS)

brew install txn2/tap/mcp-data-platform

Claude Code CLI

claude mcp add mcp-data-platform -- mcp-data-platform

Docker

docker pull ghcr.io/txn2/mcp-data-platform:v1.61.4

Verification

All release artifacts are signed with Cosign. Verify with:

cosign verify-blob --bundle mcp-data-platform_1.61.4_linux_amd64.tar.gz.sigstore.json \
  mcp-data-platform_1.61.4_linux_amd64.tar.gz