Skip to content

fix(clients): revoke the client credential on disconnect - #1518

Merged
Dumbris merged 7 commits into
mainfrom
fix/issues-w2b1-backend-internal-httpapi-inter
Oct 6, 2026
Merged

Dumbris merged 7 commits into
mainfrom
fix/issues-w2b1-backend-internal-httpapi-inter

Conversation

@Dumbris

@Dumbris Dumbris commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Summary

Disconnecting a client now revokes its client credential, per the maintainer-approved option B. The change covers DELETE /api/v1/connect/{client}, daemon-backed mcpproxy disconnect and offline mcpproxy disconnect. A revoke failure stays HTTP 200 and is reported in credential_revoke_error.

Items

  • 1435-5 (disconnect and the client credential). Approved decision: revoke the client credential on disconnect. Change: REST, CLI daemon path and CLI offline path revoke after removing the entry. Disconnect skips already-revoked tombstones. The Web UI confirmation states the revoke and shows credential_revoke_error when the revoke fails. Docs, CLI help, swagger and CHANGELOG are updated. Tests: cmd/mcpproxy/connect_disconnect_revoke_test.go, internal/httpapi/connect_client_credential_test.go, frontend/tests/unit/connect-modal-display-path-reload.spec.ts.
  • 1451 follow-up (runtime). When a rotating connect fails to finalize, the runtime now writes a compensating profile_change audit record, emits client.binding_changed, and logs a restore failure. Test: internal/runtime/clients_service_restore_audit_test.go.

Skipped or declined

  • Preserving a profile pin across a revoke was first declined, then fixed conservatively after review found the widening (see "Reconnect keeps the profile pin" below).
  • CLI disconnect fails with exit 1 when a stale socket file is left by a crashed daemon, or when another process holds config.db. This was declined. It needs a socket-liveness probe, the trigger is narrow, and failing loudly beats silently skipping the revoke.
  • The other wave-2 items were not part of this batch.

Review Status

Clean after review, unresolved findings: [].

  • glm:1.1 (medium): a profileless reconnect after a disconnect re-minted the client bound to All servers (widening). Fixed, see below.
  • glm:2.2 (low): CLI disconnect hard-fails on a stale socket file or a held config.db. Declined: it needs a socket-liveness probe, the trigger is narrow, and failing loudly beats silently skipping the revoke.

Reconnect keeps the profile pin

Review found that disconnect plus a profileless reconnect silently widened a profile-pinned client to All servers, because a revoked credential was treated as a fresh grant.

Conservative fix:

  • The revoked tombstone preserves the client's previous binding.
  • On reconnect, an explicitly requested profile still wins.
  • Without one, the preserved binding is reused, so the pin survives.
  • Old tombstones that carry no preserved binding fall back to the defaults, as before.

Tests cover the preserved binding, explicit profile override, and old-tombstone fallback, alongside the existing "revoked starts from the defaults" contract case. Latest main merged in; go build ./... and internal/httpapi, internal/profile tests pass.

Closes #1435
Refs #1451

DELETE /api/v1/connect/{client} and mcpproxy disconnect (daemon and
offline) now revoke the client credential after removing the entry, as
approved by the maintainer. A revoke failure stays HTTP 200 and is
reported in credential_revoke_error. The Web UI confirmation says so;
docs, swagger and CHANGELOG updated.

fix(runtime): audit and announce the binding restore when a rotating
connect fails to finalize (compensating profile_change record plus
client.binding_changed), and log a restore failure.

Closes #1435
Refs #1451
…revoked tombstones on disconnect

The Connect list now reports credential_revoke_error instead of a plain
success, disconnect no longer re-forgets an already revoked credential,
and the POST /connect swagger text no longer claims a connect revokes.
@Dumbris
Dumbris enabled auto-merge (squash) October 5, 2026 17:48
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 3c38e93
Status: ✅  Deploy successful!
Preview URL: https://e1f85be5.mcpproxy-docs.pages.dev
Branch Preview URL: https://fix-issues-w2b1-backend-inte.mcpproxy-docs.pages.dev

View logs

@Dumbris
Dumbris disabled auto-merge October 5, 2026 18:00
@codecov-commenter

codecov-commenter commented Oct 5, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 74.07407% with 35 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cmd/mcpproxy/connect_credential.go 65.85% 9 Missing and 5 partials ⚠️
cmd/mcpproxy/connect_cmd.go 75.00% 5 Missing and 1 partial ⚠️
internal/httpapi/connect.go 75.00% 4 Missing and 2 partials ⚠️
internal/runtime/clients_service_connect.go 77.27% 4 Missing and 1 partial ⚠️
internal/storage/client_credentials.go 60.00% 3 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

📦 Build Artifacts

Workflow Run: View Run
Branch: fix/issues-w2b1-backend-internal-httpapi-inter

Available Artifacts

  • archive-darwin-amd64 (31 MB)
  • archive-darwin-arm64 (28 MB)
  • archive-linux-amd64 (19 MB)
  • archive-linux-arm64 (17 MB)
  • archive-windows-amd64 (31 MB)
  • archive-windows-arm64 (27 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (27 MB)
  • installer-dmg-darwin-arm64 (24 MB)
  • smart-mcp-proxymcpproxy-goPP03VE.dockerbuild (0 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 37417800555 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

…nnect

A profileless reconnect of a disconnected (revoked) client re-minted it bound
to All servers, silently widening a profile-locked client. The revoked
tombstone already keeps the prior pin and mode; resolveBindingLocked now keeps
it for a revoked record too. An explicit profile still wins, and a tombstone
with no binding starts from the defaults. The reconnect preview shares the
rule. Old tombstones decode unchanged.

refs #1435
… connect minted over it

A fresh mint over a revoked/expired record now snapshots the replaced binding on the new record. Abort and undo revoke through ForgetClientCredentialRestoringPrior, which puts it back, so a later profileless reconnect cannot re-mint with the binding of a connect that never took effect. Also corrects the stale REST docs and notes the require_mcp_auth=off guard refusal.
…ange; keep the held binding in the undo record
@Dumbris
Dumbris merged commit c5593ca into main Oct 6, 2026
48 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow-ups from Spec 108-c2 client credentials review (#1430)

2 participants