feat(mcp): add hosted HTTP server and installer wizard - #69
Merged
milstan merged 37 commits intoMay 30, 2026
Conversation
Adds a Hono-based HTTP entry alongside the existing stdio server so Leadbay MCP can be deployed as a public endpoint (ChatGPT custom connectors, web clients) without changing the tool catalog or auth model. - New: packages/mcp/src/http-server.ts — Hono app exposing POST /mcp (Streamable HTTP, current MCP spec), GET /sse + POST /messages (legacy SSE), and GET /healthz for Fly liveness. - New: packages/mcp/src/auth-http.ts — per-request bearer-token resolver. Mirrors resolveClientFromEnv's probe + broken-client logic without touching the stdio code path; each session gets its own LeadbayClient. - New: Dockerfile (multi-stage, Node 22-slim, ~548 MB) + fly.toml (Paris primary, auto-stop, /healthz check). - Adds hono + @hono/node-server runtime deps and leadbay-mcp-http bin. Stdio path is unchanged. All 251 existing tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
Author
|
Installer status checkpoint:\n\n- Linux: command-only installer, no desktop artifact:\n\n |
…b.com-leadbay-product-issues-3651 # Conflicts: # README.md # packages/mcp/icon.png
- `installer --uninstall` opens the browser/Electron uninstall wizard (Linux only); only shows clients where Leadbay is already configured - electron-main.cjs routes --uninstall to startUninstallerGui so the Electron window title and flow are correct - Installer agent list now shows "install" (green) or "update" (amber) badge per client based on whether leadbay is already in their config - Pure uninstall helpers exported from bin.ts: stripShellExportBlock, stripCodexBlock, stripJsonMcpEntry + async uninstallFrom* functions - 12 unit tests covering all three strip functions - README and --help updated: uninstall via `installer --uninstall`, LEADBAY_API_BASE_URL typo fixed to LEADBAY_BASE_URL, leadbay_recall_ordered_titles added to tools table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Resolve bin.ts conflict: keep OAuth bootstrap code from main, remove now-redundant broken-client.ts import (makeBrokenClient/ResolvedClient are now inlined in bin.ts by main); import LeadbayClient as value and LeadbayError as type from @leadbay/core - Resolve packages/mcp/README.md conflict: keep installer GUI docs, add terminal-only install note (all platforms) and uninstall section - installer-gui.ts: keep process alive after startInstallerGui resolves so the HTTP server doesn't exit before the browser connects - README (root + mcp): clarify `install` works on all platforms, `installer --uninstall` is Linux-only for now Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…+ README dev testing notes electron-builder.cjs referenced dist/broken-client.js, dist/install-shared.js, and dist/env.js which tsup never emits as separate files (everything is bundled into bin.js / installer-gui.js). The missing files caused macOS to report the DMG app as "damaged and can't be opened". Now only the two files that actually exist in dist are listed. Also adds local testing instructions to README: use installer-electron.js (not installer-gui.js) to get the Electron window; installer-gui.js is the raw HTTP server / browser fallback only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
npx commands for install/uninstall are now shown together; local build instructions explain to use installer-electron.js (Electron window) not installer-gui.js (browser-only HTTP server fallback). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…b.com-leadbay-product-issues-3651 # Conflicts: # pnpm-lock.yaml
ArtyETH06
marked this pull request as ready for review
May 29, 2026 04:40
…anup Security: - Add Origin/Host validation to local installer GUI server (CSRF/DNS-rebinding) - Add 1 MB body limit to /mcp and /messages endpoints (OOM prevention) - Add 30-min TTL eviction for SSE sessions (memory leak on dropped connections) Correctness: - Fix CRLF regex in mergeShellExportBlock / stripShellExportBlock — token rotation and uninstall silently no-oped on Windows RC files with CRLF endings - Make shell RC file writes atomic (tmp + renameSync) in both appendShellExports and uninstallShellExports - Remove duplicate BrokenLeadbayClient / makeBrokenClient from bin.ts; import from broken-client.ts where they already lived Tests (+21): - test/unit/auth-http.test.ts — all 4 auth paths in resolveClientFromToken - test/unit/shell-crlf.test.ts — CRLF handling in both shell block functions - test/unit/installer-gui-unit.test.ts — sanitizeOutput token redaction + install negative paths Docs: - README: fix Linux installer note (command-only, no desktop window on Linux), remove duplicate installer section, split multi-command bash blocks 1-per-block - packages/mcp/README: fix section numbering (was 3→4→3a), collapse release-note walls into one-line upgrade callout, move LEADBAY_TIMEOUT_MS into its table, clarify leadbay_enrich_contacts is a granular (advanced) tool, split all multi-command bash blocks 1-per-block Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, SSE double-write, DXT removal
- Extract all install/uninstall logic from src/bin.ts into installer/:
install-claude-code.ts, install-json-config.ts, install-codex.ts,
install-dxt.ts, install-wizard.ts. bin.ts re-exports everything so
existing test imports are unaffected.
- Add configPath field to DetectedClient; drop detail.split(" ")[0]
pattern that truncated macOS paths with spaces.
- Fix /sse and /messages returning plain Response after SSEServerTransport
already wrote headers — use x-hono-already-sent sentinel like /mcp.
- Remove Leadbay DXT extension (dir + registry) when detected, then write
claude_desktop_config.json as the authoritative config source.
- Fix runBrowserFallback() to open uninstaller GUI when --uninstall is
passed, matching the Electron main process behavior.
- Pin all npx install targets to @latest instead of @0.16.
Co-Authored-By: Claude <noreply@anthropic.com>
… for 0.16.0 Co-Authored-By: Claude <noreply@anthropic.com>
This was referenced May 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
This PR makes the Leadbay MCP easy to install locally and usable from hosted-MCP clients like ChatGPT Desktop. The main install path now uses Leadbay OAuth instead of asking users for email/password or making them paste tokens.
Supported paths:
What Changed
OAuth installer flow
leadbay-mcp install --oauthfor terminal users.Installer wizard
claude_desktop_config.jsonwithnpx -y @leadbay/mcp@latest.OpenAI.ChatGPT-Desktop).@latestso installs always pick up the newest release.Installer logic moved into
installer/All installer logic has been extracted out of
src/bin.tsinto dedicated files underinstaller/:installer/install-shared.tsDetectedClienttypeinstaller/install-claude-code.tsrunClaudeMcpinstaller/install-json-config.tsstripJsonMcpEntryinstaller/install-codex.tsinstaller/install-dxt.tsinstaller/install-wizard.tsparseInstallSelection,chooseInstallClients, …)installer/installer-gui.tsinstaller/installer-electron.tssrc/bin.tsis now a thin MCP server entrypoint + CLI dispatcher. It re-exports all installer symbols from the new files so existing test imports (../../src/bin.js) continue to work without any test file changes.Config path fix (macOS spaces)
DetectedClientnow carries a dedicatedconfigPathfield (the real filesystem path) alongsidedetail(the display string). Previously all callers usedclient.detail.split(" ")[0]which truncated macOS paths containing spaces (~/Library/Application Support/Claude/...→~/Library/Application). All install and uninstall callsites now useclient.configPathdirectly.DXT extension removal on Claude Desktop
When Claude Desktop 2026 (with DXT support) is detected via its marker files (
Claude Extensions/,extensions-installations.json,config.jsonwithdxt:*keys), the installer now removes the Leadbay DXT extension and falls through to the normalclaude_desktop_config.jsonwrite:Claude Extensions/local.dxt.leadbay.leadbay/directory.local.dxt.leadbay.leadbayentry fromextensions-installations.json.mcpServers.leadbaytoclaude_desktop_config.jsonas usual.Browser fallback uninstall fix
runBrowserFallback()ininstaller-electron.tspreviously always launched the installer GUI, so--uninstall --browserand no-Electron uninstall flows opened the install wizard instead of removing config. Fixed to branch on--uninstalland callstartUninstallerGui().Legacy SSE double-write fix
/sseand/messagesnow return{ "x-hono-already-sent": "1" }(the same sentinel as/mcp) instead of a barenew Response(null, { status: 200 }). The previous response caused Hono's Node adapter to attempt a second header write afterSSEServerTransportandhandlePostMessagehad already written headers and body, producingCannot write headers after they are sent to the client.Hosted MCP server
POST /mcp.GET /sseandPOST /messages.GET /healthzfor Fly health checks.Authorization: Bearer <token>.X-Leadbay-Region: us|fr.How To Test Before Merge
Local installer app
macOS path smoke test
On macOS, after install, verify
~/Library/Application Support/Claude/claude_desktop_config.jsoncontainsmcpServers.leadbay(not a truncated path like~/Library/Application).DXT removal
With a Claude Desktop 2026 machine (DXT markers present):
Uninstall via browser fallback
node packages/mcp/dist/installer-electron.js --uninstall --browser # Expected: uninstaller GUI opens, not the installer wizardLegacy SSE
Validation
pnpm -r build && pnpm -r test— 355 tests pass, no test file modificationspnpm --filter @leadbay/mcp typechecknode --check packages/mcp/dist/bin.jsnode --check packages/mcp/dist/installer-electron.jsnode --check packages/mcp/dist/installer-gui.jsRefs #3651.
closes https://github.com/leadbay/product/issues/3651