mcp-data-platform-v1.61.3
Highlights
This release fixes a long-standing OAuth refresh-token loss bug in the gateway toolkits and adds a second authentication slot for upstream APIs that require it.
Fix: stale refresh-token cache caused invalid_grant revocation (#404)
The MCP and API gateway toolkits each held their own in-memory cache of the OAuth refresh token. The background refresher in connoauth.Refresher rotated the persisted refresh token every few minutes, but the in-toolkit cache never re-read it. When a tool call later forced a refresh through the in-toolkit path, it sent the stale refresh token to the IdP — which treats stale-RT use as token replay, revokes the entire RT family, returns invalid_grant, and forces the operator to re-Connect.
Both toolkits now build an ephemeral connoauth.Source per outbound request from the unified store. There is no in-memory RT cache to drift. The client_credentials path keeps a mutex-guarded in-memory access-token cache (cc has no rotation contract, so caching is correct there).
Net effect: roughly 5,000 lines of duplicated per-kind OAuth state-machine code deleted; one shared connoauth.Source implementation across both gateway kinds.
Related changes:
OAuthConfig.EndpointAuthStyle(basic/params) so operators can pick HTTP Basic vs form-body at the token endpoint. Defaulted to HTTP Basic broke IdPs that only accept form-body; this knob makes parity with apigateway'soauth2_endpoint_auth_style.ctxnow threaded through adminStatuscalls so client disconnect cancels the underlying DB read.- Migration
000041drops the now-orphanedgateway_oauth_tokensandapigateway_oauth_tokenstables;000039already moved every row toconnection_oauth_tokens.
Feature: static_headers for APIs that need a second auth header (#403)
Some REST upstreams require both an OAuth/Bearer Authorization header and a separate per-call header (e.g. a vendor-specific subscription key, Google's x-goog-user-project, and similar). The apigateway toolkit's auth_mode is a single value, so a connection could carry one or the other, not both. static_headers is the second slot.
Values in static_headers are operator-supplied, AES-256-GCM encrypted at rest with the same FieldEncryptor that protects credential and client_secret, applied to every outbound request, and unreachable to the model. Header precedence on the wire (later wins): model-supplied per-call headers -> static_headers -> auth_mode contribution. The model is blocked at request-validation time from supplying any header name that collides with static_headers.
Validation rejects:
Authorization(useauth_mode)- The API-key header when
auth_mode: api_keywithapi_key_placement: header - Hop-by-hop /
net/http-managed names (Host,Content-Length,Connection, etc.) - Header names that violate RFC 7230 token characters
- CRLF / NUL in values (header-smuggling defense)
The portal kind: api connection form has a new sensitive key/value editor that masks existing values and supports add/delete without inline edit — same pattern as credential / client_secret.
See docs/server/api-gateway.md for walkthroughs covering common upstream auth patterns.
Changelog
Features
- b60953a: feat(apigateway): add static_headers for APIs needing a second auth header (#403) (@cjimti)
Refactor
- e72de14: refactor(gateway): unify OAuth on connoauth.Source, fix stale-RT cache bug (#404) (@cjimti)
Installation
Homebrew (macOS)
brew install txn2/tap/mcp-data-platformClaude Code CLI
claude mcp add mcp-data-platform -- mcp-data-platformDocker
docker pull ghcr.io/txn2/mcp-data-platform:v1.61.3Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.61.3_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.61.3_linux_amd64.tar.gz