From 436b2c4c6c347dbb2e4352ce02cdcaabaa96994f Mon Sep 17 00:00:00 2001 From: Peter Rubenstein Date: Tue, 28 Jul 2026 17:26:04 -0400 Subject: [PATCH 1/2] Don't delete existing session before new login is verified and saved login_setup.py called secure_session.delete_token() unconditionally as the very first step of main(), before the new login attempt (cookies, password+MFA, or legacy token) was tried, tested against get_accounts(), or saved. Any failure after that point -- a bad cookie paste, a captcha, a 401 on the connection test, a save error -- left the user with no working session at all, having wiped a previously good one for nothing. save_authenticated_session() -> save_session_blob() already overwrites the stored keyring entry (or file-fallback) on success, and calls _cleanup_old_session_files() itself, so the upfront delete served no purpose on the success path. Removed it; the old session is now only ever replaced once a new one is confirmed working and saved. --- login_setup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/login_setup.py b/login_setup.py index c1d1bf7..6978879 100755 --- a/login_setup.py +++ b/login_setup.py @@ -125,9 +125,6 @@ async def main(): print(f"⚠️ Could not check version: {e}") try: - secure_session.delete_token() - print("🗑️ Cleared existing secure sessions") - print("\nHow do you sign in to Monarch Money?") print( " 1) Session cookies from browser " From 109ee8017938903dc914cf0d3318d6a240817322 Mon Sep 17 00:00:00 2001 From: Peter Rubenstein Date: Tue, 28 Jul 2026 17:51:37 -0400 Subject: [PATCH 2/2] Fix check_auth_status false negative on cookie-mode sessions check_auth_status() called secure_session.load_token(), which only ever inspects the top-level "token" key. A cookie-mode session (auth_mode: "cookie", saved by the recommended long-lived browser-cookie login path) stores its credentials under a "cookies" dict instead, with no top-level token -- so check_auth_status reported "No authentication token found" for a fully valid, working session. Confirmed live: get_accounts() returned real account data through the same session check_auth_status was reporting as absent. Now reads the full session via load_session() and treats either a non-empty token or a non-empty cookies dict as authenticated, reporting the actual auth_mode in the success message. --- src/monarch_mcp_server/tools/auth.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/monarch_mcp_server/tools/auth.py b/src/monarch_mcp_server/tools/auth.py index b26157e..0bc1ccb 100644 --- a/src/monarch_mcp_server/tools/auth.py +++ b/src/monarch_mcp_server/tools/auth.py @@ -66,9 +66,18 @@ async def monarch_logout() -> str: async def check_auth_status() -> str: """Check if already authenticated with Monarch Money.""" try: - token = secure_session.load_token() - if token: - status = "✅ Authentication token found in secure keyring storage\n" + # load_token() only ever inspects the "token" key, so it reports a + # false negative for cookie-mode sessions (auth_mode="cookie"), which + # carry a "cookies" dict and no top-level token. Check the full + # session so a valid cookie-only login — the recommended, long-lived + # auth path — is correctly recognized as authenticated. + session = secure_session.load_session() + has_token = bool(session and isinstance(session.get("token"), str) and session.get("token")) + has_cookies = bool(session and isinstance(session.get("cookies"), dict) and session.get("cookies")) + + if has_token or has_cookies: + auth_mode = session.get("auth_mode", "cookie" if has_cookies else "token") + status = f"✅ Authenticated session found in secure keyring storage (auth_mode={auth_mode})\n" else: status = "❌ No authentication token found in keyring\n"