Repository navigation
fix(mcp): make paired settings/credential writes atomic - #259
Conversation
Restore the settings half of an MCP server entry when OAuth credential cleanup fails after the config mutation, instead of leaving a silent half-paired write. If the rollback itself also fails, raise loudly with both residual states (C1 unverified semantics).
1884394 to
8ed9edb
Compare
|
Merged into main on 2026-10-21 as 73d7846 ("Merge PR #259: keep an MCP server's settings and credential in step"). Thank you, and sorry this waited a week for a reply after your rebase. The rollback is a real improvement for One follow-up on main in e40ac1d: the The change is listed in the README news for 2026-10-21. |
Summary
An MCP upsert/remove is a paired write: the config
settingsentry and its OAuth credentials must land together or not at all. Today, if the credential half fails after the settings mutation, the config is left silently half-paired (settings entry updated/orphaned while credentials disagree).This PR gives paired writes compensating-transaction semantics:
unverifiedsemantics) — never leave a silent half-pair behind.Changes
core/application/mcp_service.py: new_restore_server_entry()helper;upsert/removewrap credential cleanup so a failure triggers a compensating settings rollback, and a rollback failure raisesApplicationErrorwithdetailscarrying bothcredential_errorandrollback_error.tests/application/test_management_services.py: 3 regression tests — rollback on cleanup failure (upsert + remove), happy-path pair lands together and cleans the credential, and rollback failure raises a loud error naming both residual states.Tests
main(5cd55efb):tests/application/test_management_services.py tests/test_mcp_oauth.py tests/test_mcp_server.py tests/test_mcp_cli.py→ 30 passedruff check+ruff formatclean (pre-commit hooks pass)Dependency note
No new dependencies. Only existing symbols (
ApplicationError,ConfigStore.mutate,McpOAuthCredentialStore.delete_endpoint) are used.