@leadbay/mcp 0.21.1 → 0.21.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/bin.js +463 -77
- package/dist/http-server.js +146 -27
- package/dist/installer-electron.js +351 -144
- package/dist/installer-gui.js +349 -142
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog — @leadbay/mcp
|
|
2
2
|
|
|
3
|
+
## 0.21.3 — 2026-06-19
|
|
4
|
+
|
|
5
|
+
Kills the 401 startup "reconnect Leadbay" hallucination — the assistant told users to re-authenticate on a connection that actually worked (product#3761). Fixed at every layer that produced or surfaced the spurious 401:
|
|
6
|
+
|
|
7
|
+
- **`leadbay_account_status` withholds an unreadable quota from the payload entirely** (the actual source the user hit): `account_status` fans out `/users/me` (identity, succeeds) + `/organizations/{id}/quota_status` (quota), and for an org with no billing plan (`plan: null`) the backend's `quota_status` returns **401** on the very token that just succeeded. The old code surfaced that as `quota_error: {code: AUTH_EXPIRED, http_status: 401}`, and both the tool description and the `quota_error` schema told the agent *"on 401/403 tell the user to reconnect"* — so a perfectly-authenticated user was told to reconnect, every time, on a plan-less org. A 401/403 quota failure is now dropped from the response before the agent can see it (only logged); a genuine non-auth failure (500 / network) still surfaces as `quota_error` so the agent can honestly say quota is unreadable. Guidance alone was leaky — an agent still hedged *"quota had a hiccup"* — so withholding it is the only thing the agent literally cannot parrot. Locked by `account-status-quota-401.test.ts`.
|
|
8
|
+
- **`leadbay_account_status` gates the active lens on the trigger text, and uses the name not the raw id**: the agent was volunteering the active lens unprompted and surfacing the bare numeric id (e.g. `40005`). The lens (id and resolved name) is now withheld from the payload unless the user's message mentions lens / audience / targeting / segment / filter — the verbatim `_triggered_by` slice is plumbed through to `ToolContext` so the composite gates on what was actually asked (prompt guidance alone leaked the lens unprompted in ~1/3 of live runs). When asked, it resolves `last_requested_lens` → `last_requested_lens_name` (best-effort via `/lenses`, ids string-normalized so the match never drifts string-vs-number), and the agent answers with the name, never the number. (Core 0.8.3.)
|
|
9
|
+
- **Hosted HTTP MCP — empty the 401 OAuth-challenge body**: the Fly connector answers an unauthenticated / expired `POST /mcp` with `401` + `WWW-Authenticate` — the RFC 9728 OAuth challenge that drives host sign-in / silent refresh (added in 0.21.0), correct and unchanged. The bug was the challenge's *JSON body* ("Sign in with Leadbay again."), which a spec-compliant client never reads but Claude's host surfaces to the LLM, which then told the user to reconnect even though the immediate retry succeeded. `sendChallenge()` now returns an empty 401 body; the status and the `WWW-Authenticate` header (incl. `error="invalid_token"` for expired) are preserved byte-for-byte, so no protocol signal is lost. Test: `http-auth-challenge-body.test.ts`.
|
|
10
|
+
- **Local (stdio) MCP — `TRANSIENT_401` server-instruction**: Leadbay bearer tokens don't expire on a timer, so the client already treats a GET 401 as a transient blip and auto-retries once; only a *persistent* 401 surfaces (as `AUTH_EXPIRED`). But the agent still occasionally read a lone 401 as a broken login and told the user to reconnect even though the next call worked. A new always-on server-instruction paragraph tells the agent a one-off 401 is a brief Leadbay-side hiccup the client already retried — retry silently, never turn it into a "reconnect" message, and surface it only if calls keep failing (offered to `leadbay_report_friction`). Retry count and the `AUTH_EXPIRED` code are unchanged, so a real logout/revocation still surfaces. Test: `server-instructions-transient-401.test.ts`.
|
|
11
|
+
|
|
12
|
+
## 0.21.2 — 2026-06-17
|
|
13
|
+
|
|
14
|
+
- **Early host shutdown no longer kills the OAuth flow mid-registration** (review P1): Claude Desktop's probe→teardown can close stdin within ~100ms — while the background bootstrap is still in region-probe/discovery/registration, before any browser-open exists. `shutdown()` now waits (bounded ~4s) on the whole bootstrap task, not just the browser-open promise, so the flow reaches the authorize-URL mint + open dispatch instead of dying early. Verified: stdin closed at 1s still produced `spawn OK` at ~2.7s.
|
|
15
|
+
- **Terminal bootstrap failures surface `AUTH_FAILED`, not a forever-"pending"** (review P2): a non-browser failure (region probe / discovery / registration / token exchange) with no sign-in URL used to leave tools reporting "a browser window should have opened…" indefinitely. The failure reason is now recorded and the gate returns `AUTH_FAILED` with the real error + restart guidance; it takes priority over a stale sign-in link.
|
|
16
|
+
|
|
17
|
+
- **Browser auto-open now reconstructs a missing `DISPLAY`/`WAYLAND_DISPLAY` on Linux**: a real Claude Desktop install log showed the spawned server's env had `DISPLAY=<unset> WAYLAND=<unset>` (the host strips them inconsistently — present on some launches, absent on others). Without a display var, `xdg-open` spawns "successfully" but can't reach the display server, so no tab opens — the silent install failure. `openInBrowser` now backfills the missing vars from `XDG_RUNTIME_DIR` (the `wayland-N` socket) and `/tmp/.X11-unix` (defaulting to `:0`) before spawning the launcher, and passes that env to the child. Already-set vars are left untouched; non-Linux is unaffected.
|
|
18
|
+
- **OAuth client registration is cached & reused** (the actual "nothing opens" root cause): bootstrap was registering a fresh Dynamic-Client-Registration client on every launch, and Claude Desktop's probe-restarts (several launches per install) blew past the backend's ~10-registrations/IP/hour cap — so bootstrap 429'd *before* building a sign-in URL. The registered `client_id` is now persisted per auth server in `~/.leadbay/oauth-client.json` and reused (loopback clients accept any 127.0.0.1 port per RFC 8252), so registration happens at most once and the 429 never recurs.
|
|
19
|
+
|
|
20
|
+
- **OAuth-on-install for the Claude Desktop `.dxt` no longer fails with "Unable to connect to extension server"**: the bundled stdio server ran the full interactive browser OAuth flow (up to 5 minutes) at startup, *before* answering the MCP `initialize` handshake — so Claude Desktop, which gives a launched extension only a few seconds to respond, timed out the connection and marked the server unreachable. The OAuth bootstrap is now **non-blocking**: the server answers `initialize` immediately with a real (tokenless) client, runs the browser sign-in in the background, and the first tool call returns a transient `AUTH_PENDING` envelope ("Signing you in to Leadbay — a browser window should have opened…") until the token lands. The moment the loopback callback completes, the token is set on the live client and the next tool call executes authenticated — no server rebuild, no restart.
|
|
21
|
+
- **Browser auto-open now survives Claude Desktop's install-time probe race**: a freshly-installed extension is probed with rapid connect→shutdown cycles (the first spawned process can live <100ms — confirmed in the install logs), which killed the process before the background OAuth flow reached the browser-launch step, so no tab opened on install even though it works on a stable session. The bootstrap now fires the browser-open the moment the authorize URL is known (tracked in an in-flight handle), and the shutdown path waits up to 1.5s for that spawn to dispatch before exiting — the detached launcher then survives our exit. Net: the browser opens on install. The clickable sign-in link remains as the backstop.
|
|
22
|
+
- **The sign-in link is also surfaced to the user instead of relying solely on auto-opening a browser**: the spawned `.dxt` stdio process frequently can't open a GUI browser at all — Claude Desktop strips `PATH` *and* `DISPLAY`/`WAYLAND_DISPLAY` from the child env, so `xdg-open`/`open` either `ENOENT`s or (worse) silently exits 0 without launching anything, leaving the user with no link and no error. The bootstrap now captures the live OAuth authorize URL and the gate returns it as a **clickable sign-in link** in the tool envelope ("Open this link to authorize Leadbay…"); the loopback listener stays alive in the background, so clicking it completes the flow and the next tool call is authenticated. The browser auto-open is still attempted (best-effort, now via absolute launcher paths `/usr/bin/open` / `%SystemRoot%\System32\cmd.exe` / `/usr/bin/xdg-open`) for environments where it works — but the surfaced link is the reliable path and no longer depends on it.
|
|
23
|
+
|
|
3
24
|
## 0.21.1 — 2026-06-16
|
|
4
25
|
|
|
5
26
|
- **CSV import no longer 400s on a lead status the agent didn't uppercase** (product#3745): `leadbay_import_leads` / `leadbay_import_and_qualify` forwarded `default_status` / `statuses` values verbatim to `POST /imports/{id}/update_mappings`, whose backend `MappingsPayload` decodes them as the strict, case-sensitive `LeadStatus` enum (`DEFAULT, INBOUND, UNWANTED, WANTED, LOST, WON`). A value like "Won" failed deserialization and the whole call 400'd with an opaque "JSON deserialization error" before any record committed — JM hit this trying to tag 179 companies as Won. The MCP now owns the canonical set and enforces it before sending: status values are matched case-insensitively to their enum member ("Won" → "WON"), an empty default means no default, and a genuinely unknown status returns a clear `IMPORT_INVALID_STATUS` error naming the valid values instead of an opaque backend 400. The two tools' input schemas now declare the enum.
|