* signin: switch to Auth0 Device Authorization Flow
Backend pivoted away from the github-oauth Lambda (and its hacky
db-connection user creation) to Auth0 Device Flow + the existing
token-go Lambda. The matching MCP-side change:
* Drop GitHub Device Flow entirely (lib/github-device-flow.ts),
drop the gh-cli zero-click probe (lib/gh-cli.ts), drop the
GitHub-token-based exchange call to the deleted /api/v1/auth/github
endpoint.
* Add lib/auth0-device-flow.ts: RFC 8628 client against Auth0's
/oauth/device/code and /oauth/token. Auth0 client_id is hardcoded
to the prod public Native client (overridable via
LOG10X_AUTH0_CLIENT_ID for staging/forks). Domain hardcoded to
auth.log10x.com (overridable via LOG10X_AUTH0_DOMAIN). Scopes:
openid profile email offline_access.
* lib/auth-api.ts now exposes exchangeAuth0TokenForApiKey(token)
which POSTs to /api/v1/auth/token (the existing token-go Lambda).
The response shape is simpler: {api_key, username}; we no longer
carry github_login or is_new_account because Auth0 Device Flow
isn't GitHub-specific (the user picks GitHub OR Google at Auth0's
universal login page).
* tools/signin.ts: mode rename "github" -> "browser" since the flow
isn't GitHub-specific. Default mode is browser. The api_key paste
branch is unchanged. Updated message text in signout, doctor,
login-status, rotate-api-key, environments.ts, and index.ts to
match the new mode name.
* lib/credentials.ts: drop the cosmetic githubLogin field.
* Bump 1.6.7 -> 1.7.0. This is a backwards-incompatible change in
the user-facing mode arg and removes the gh-cli zero-click path.
Per "no existing users yet" the breakage is acceptable.
User experience after this change:
log10x_signin
-> opens https://auth.log10x.com/activate?user_code=XXXX-YYYY
-> universal login page shows "Sign in with GitHub" and
"Sign in with Google" buttons (both connections enabled on the
mcp_backend Auth0 client)
-> user picks one, completes IdP OAuth in the browser, confirms
the device authorization
-> MCP polls Auth0 /oauth/token, gets access_token
-> MCP exchanges access_token at /api/v1/auth/token, gets api_key
-> stored in ~/.log10x/credentials, envs hot-reloaded
Build passes. Test suite: 381 pass, 5 skipped (integration), 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* signin: clean up stale GitHub references missed in the Auth0 cutover
Independent code review caught one BLOCKER plus several stale strings
that the prior commit missed:
* default-manifest.json log10x_signin description still said `mode:
"github"` is the default and described the GitHub Device Flow. The
manifest description gets applied to the registered tool at boot,
overriding the in-code description, so the model would call
log10x_signin({ mode: "github" }), Zod's z.enum(['browser',
'api_key']) would reject it, and the user would see a cryptic
invalid-argument error. The browser-flow signin path was effectively
unusable end-to-end on the previous commit. Rewrote both the signin
and login_status manifest descriptions to match the actual code.
* src/lib/tool-errors.ts: 401/403 hint mentioned "via GitHub" as the
signin method. Now mentions Auth0 Device Flow with GitHub or Google.
* src/lib/environments.ts: file-header block comment described path 2
as "GitHub-device-flow signup or signin" and demo-fallback as
pointing the user at "the one-click GitHub flow". Updated to
reference the Auth0 Device Flow.
Build passes. No leftover references to mode "github", GitHub Device
Flow, GitHub-device-flow, gh-cli, LOG10X_GITHUB_CLIENT_ID, or any of
the deleted symbols anywhere in the source tree or manifest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* signin: surface device user_code via MCP notifications/message
The Auth0 device authorization confirmation page asks the user to
verify the user_code matches what's "displayed on your Log10x MCP" —
but the MCP never showed it. The user clicked Confirm in the browser
on faith, defeating the device-binding security check (RFC 8628 step
where the user verifies the same code is on both surfaces).
Tool calls in MCP are RPC-style and the existing signin tool
synchronously polls /oauth/token until the user finishes. By the time
the tool returns, the user has already approved or denied. There is
no path for the tool's return value to surface the user_code BEFORE
polling.
The MCP protocol's mid-tool channel is `notifications/message`,
exposed via RequestHandlerExtra.sendNotification on the SDK's tool
handler. Hosts that implement it (Cursor, Claude Desktop) render the
message inline as the tool runs.
* src/index.ts: thread `extra` through the registerLog10xTool wrapper
so handlers can opt in to it. Other tools' handlers are unchanged
(they ignore the new arg).
* src/index.ts: signin registration now passes `extra` through to
`executeSignin`.
* src/tools/signin.ts: define a minimal SigninExtra type covering
just `sendNotification`. Before the polling loop starts, fire a
notifications/message at info level with the user_code, the
verification URL, and a "verify the code matches" instruction.
Failure is non-fatal: hosts that don't implement the channel get
silently skipped.
* src/tools/signin.ts: also include the verified user_code in the
final success markdown ("Path: Auth0 Device Flow (verified code
XXXX-YYYY)") so even hosts that didn't render the notification
surface the code in the post-hoc record. Belt-and-braces.
After this, on Cursor: the user runs log10x_signin, sees a message
with the code (e.g. VLSW-JMWP), opens the Auth0 page that shows the
same code, confirms they match, then approves. The browser
verification step actually verifies something now.
On hosts that don't render notifications/message inline, behavior
falls back to what we shipped before: the user has to trust the
binding (suboptimal, but no worse than before).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* signin: split into log10x_signin_start + log10x_signin_complete
The single log10x_signin tool relied on extra.sendNotification
('notifications/message', ...) to surface the Auth0 device user_code
mid-tool, but the SDK's assertNotificationCapability throws
synchronously when the server has no `logging` capability declared,
and even with the capability declared Cursor and most other MCP hosts
do not render mid-tool notifications today. The user never saw the
code and the tool just hung until the device_code expired.
Splitting the flow sidesteps the host limitation: log10x_signin_start
returns the user_code + device_code immediately as plain markdown so
the model can surface them, and log10x_signin_complete polls Auth0
with the device_code (or validates an api_key for the pasted-key
path). Works in every host without any notification API.
The McpServer constructor now declares { logging: {} } as correct
hygiene for any future tool that wants the channel on hosts that DO
support it, but the sign-in tools no longer rely on it.
Other changes in this commit:
- bump package version to 1.8.0 (user-visible tool shape change)
- update default-manifest.json with two new entries replacing
log10x_signin, with model-facing chain instructions on the _start
description
- update every reference to log10x_signin across signout, doctor,
rotate-api-key, login-status, environments, credentials,
auth0-device-flow, tool-errors, and the manifest test
- update the routing-rules comment in index.ts instructions and
the CLI --help text
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Auto-recover from demo-fallback when credentials become valid mid-session
If the MCP boots while the user's saved key is temporarily invalid (a
rotated key whose 5-minute authorizer cache had not yet expired, a
typo'd key the user fixed in the host config but not the file, or a
transient backend issue), every account-scoped EnvConfig has the demo
key baked into it for the lifetime of the process. Tools keep hitting
the gateway with the demo key and returning demo data even after the
real key starts working. Until now the only recovery was to fully quit
and reopen the MCP host. This commit makes recovery automatic.
Three coordinated changes:
1. **wrap() retry**: in src/index.ts, the tool wrapper that already
handles error formatting now also catches HTTP 401/403 errors when
the MCP is in demo-fallback mode (envs.isDemoMode &&
envs.demoFallbackReason set). On match it calls
revalidateEnvironments() once and re-runs the tool. Pure demo mode
(no key configured) does NOT trigger retry; only the fallback case
where the user intended to use a real account. Tools that don't hit
the gateway never produce 401/403 so this is a no-op for them.
2. **login_status revalidates on every call**: src/tools/login-status.ts
now calls revalidateEnvironments() at entry. The user-visible "am
I signed in" tool must reflect ground truth, not a snapshot from
MCP boot. Best-effort: a reload failure does not break the status
render.
3. **revalidateEnvironments() helper + clearOverridingEnvVar() export**:
src/lib/environments.ts exposes both. revalidateEnvironments() does
clearOverridingEnvVar() then reloadEnvironmentsInPlace() in one
call, so every revalidation entry point gets the same priority-fix
behavior signin / signout / rotate already had.
clearOverridingEnvVar() was previously a private function in
signin.ts and inline duplicates in signout.ts and rotate-api-key.ts;
centralized it. signin / signout / rotate now import from
environments.ts; the local copies are gone.
Why the precedence flip via clearOverridingEnvVar instead of changing
the priority order in loadEnvironments: a stale LOG10X_API_KEY in the
host config (priority 1) would still beat the freshly-validated
~/.log10x/credentials (priority 2) on revalidation. Calling
clearOverridingEnvVar() first removes the in-process env var so the
file wins on reload. Per-call decision; we keep env-var-first as the
default ordering for users who deliberately set LOG10X_API_KEY.
Build passes. Test suite 381 pass / 5 skipped / 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Dor Levi <11015255+dor-levi@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>