Skip to content

mcp-data-platform-v1.96.0

Choose a tag to compare

@github-actions github-actions released this 04 Jul 00:05
9602064

v1.96.0 is a security and hardening release: CSRF protection for every cookie-authenticated mutation, OAuth 2.1 fixes with behavior changes operators should read before upgrading, strict configuration parsing that surfaces silent config drift, a fix that makes memory's recall-first dedup actually converge, and the frontend test suites joining CI.

Upgrade notes (read before upgrading)

Three behavior changes from the OAuth hardening (#777) and one from configuration parsing (#779):

  1. Dynamic Client Registration is now deny-by-default (#750). With oauth.dcr.enabled: true and no allowed_redirect_patterns, registration used to accept any redirect URI; it is now denied with a clear error and a startup warning. Configure allowed_redirect_patterns, or set the new explicit oauth.dcr.allow_all_redirect_uris: true to keep the old behavior.
  2. Access tokens now mint aud as the issuer URL per RFC 9068, with the requesting client in a client_id claim (#751). Tokens minted by earlier versions fail validation after upgrade; clients receive a 401 and silently refresh into a valid token. Rolling multi-replica upgrades self-heal the same way once the rollout completes.
  3. Refresh-token rotation is fail-closed (#748). Rotation now atomically consumes the old token and fails the grant if it cannot, instead of discarding the delete error. Storage outages return 500 server_error so spec-compliant clients keep their refresh token and retry.
  4. Unknown configuration keys now log a prominent WARN at startup (#757). This release is the warn phase: loading continues, so existing configs keep working. Set config.strict: true to reject unknown keys with a hard startup error (recommended). A future release flips the default.

Security

CSRF protection for cookie-authenticated mutations (#749, PR #778)

Every POST/PUT/PATCH/DELETE route reachable by browser-session cookie auth (portal, admin, managed resources) now requires a CSRF token. Previously the only cross-site defense was the cookie SameSite mode, which is operator-configurable.

  • Stateless token: HMAC-SHA256 over the session subject with the session signing key, domain-separated from the session HMAC, verified with a constant-time compare. No server-side store.
  • Cookie-only enforcement: API-key and Bearer requests are exempt (browsers do not attach those credentials automatically). Safe methods (GET, HEAD, OPTIONS, TRACE) always pass.
  • Recoverable failure: a missing or invalid token on a valid session returns 403, not 401, so the SPA shows a recoverable error instead of logging the user out. The SPA obtains the token from GET /me.
  • Session-cookie hardening: auth.browser_session.same_site is now configurable, with a startup warning when a cross-site mode weakens the cookie defense.

OAuth 2.1 server fixes (PR #777)

Beyond the three behavior changes above: SQL and redirect allowlist tightening, plus the remaining Phase 2 review-findings fixes across the memory store, enrichment, the Trino toolkit, platform lifecycle, and URN handling (#765, #751, #748, #747, #753, #750, #752, #759, #763, #754, #755, #760). URN construction is consolidated in one place (pkg/urnbuild).

Memory

Near-duplicate captures now consolidate (#762, PR #782)

memory_capture promises recall-first behavior: a restatement of something already known supersedes it instead of duplicating. A live deployment showed the promise not holding: two active, nearly identical memories on one entity, captured seconds apart.

Root cause: the recall similarity search could match records that were already superseded. A dead predecessor would absorb the new capture's supersede while the active duplicate survived. Fixed, plus the full set of consolidation behaviors from the issue:

  • Recall matches the right records. Superseded rows are excluded; stale rows remain matchable, since a restatement is exactly how a stale record gets corrected.
  • A capture supersedes every restatement it finds (cosine >= 0.9), not just the best one, so capturing over an already-duplicated set heals the whole set. The response's superseded field keeps its single-id shape (best match); new superseded_ids lists all of them.
  • Update-vs-create candidates. Matches in the 0.75 to 0.9 band come back as similar_existing (id + score) so the agent can consolidate instead of appending a near-duplicate.
  • New memory_manage commands: review_duplicates lists the caller's own active high-similarity pairs (pgvector self-join, per-user by construction), and consolidate supersedes a chosen duplicate by the record kept, with ownership checks and a guard that the kept record is active. This is the backstop for duplicates created before this release.

Proven against real Postgres + pgvector in integration tests: three rapid restatements converge to a single active record.

Configuration

Strict YAML parsing and a docs round-trip gate (#757, #770, #775, #769, PR #779)

Closes a class of silent configuration drift: documented keys that never took effect and toolkit examples that would load zero toolkits if copied verbatim.

  • config.strict option with warn-phase default (see upgrade notes). Detection covers every typed struct, top-level and nested; free-form maps (the toolkits tree, per-toolkit config: maps, persona names) stay exempt by design.
  • A CI test now extracts every fenced YAML block from README.md and docs/**, strict-loads each config-shaped block, and validates toolkit shape, so documented examples cannot drift from the parser again.
  • Toolkit YAML examples corrected to the real enabled/instances nesting; the shipped configs/platform.yaml is verified strict-clean.

CI and verification

  • Frontend suites now run in CI (#765, PR #777): the vitest suite (264 tests) and the interactive Playwright e2e suite (43 tests), both MSW-backed and mirrored in make verify for CI/local parity. Eight e2e specs that had drifted against UI changes were repaired.
  • Docs orphan gate (#772, PR #781): a repo-level test ensures every docs/**/*.md page is reachable from the MkDocs nav, intentionally unlinked, or excluded from the build. Pages in none of those sets were published but unreachable by browsing; mkdocs build --strict does not catch this.

Documentation

  • README restructured into a curated front door (#774, PR #780): 521 lines down to 229, grouped feature tables with every row linking to its docs page, current portal screenshots throughout (all 112 regenerated, light and dark), and docs/llms.txt rewritten to spec-length entries.
  • Config/docs correctness sweep (#768, #769, #767, #770, #772, #773, PR #776): removed the broken definitions: wrapper from persona examples (30 occurrences across 15 files; it silently produced no working personas), swept ~20 documented keys/flags that do not exist in code, fixed the built-in persona examples to match the fail-closed defaults, merged the orphaned observability page into the canonical one, and regenerated CLAUDE.md's project map from the real tree with a test so it cannot drift.

Changelog

Full diff: v1.95.0...v1.96.0

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.96.0

Verification

All release artifacts are signed with Cosign. Verify with:

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