Commit 6a54ff7
feat(mcp): #3504 — 0.3.0 coverage + login + scope (#20)
* feat(mcp,dxt,leadclaw): #3504 0.3.0 — coverage + login + scope (writes-on default, file-write login, --scope user)
Closes leadbay/product#3504 end-to-end. The 0.2.x default install shipped a
SERVER_INSTRUCTIONS that referenced tools the server didn't expose, the login
command leaked tokens to stdout, and `claude mcp add` registered project-local
so a fresh conversation couldn't see Leadbay. All three are fixed in 0.3.0.
Reviewed via /autoplan with dual voices (CEO + Eng + DX, Codex + Claude
subagent each phase) — 38 findings, 11 mechanical refinements applied,
2 user challenges surfaced at the gate (sub-tier split for risky writes
deferred to 0.4.0 once audit-log + undo land; --scope user docs nudge
bundled into this release).
Coverage — composite write tools default ON
* `LEADBAY_MCP_WRITE` defaults to "1" (ON). Composite write tools
(bulk_qualify_leads, enrich_titles, refine_prompt, report_outreach,
adjust_audience, answer_clarification, import_leads) are exposed by default.
Set LEADBAY_MCP_WRITE=0 (or `--no-write` on install) to disable.
* SERVER_INSTRUCTIONS is now built dynamically from the actual exposed tool
set (buildServerInstructions(exposedNames)). Read-only-mode agents get a
shorter prompt that drops the verification mandate and tells the agent to
ask the user to enable writes if needed. Tests parameterize the matrix
(default | read-only | advanced+writes) instead of asserting one static const.
* `parseWriteEnv()` accepts 1/true/yes/on as ON and 0/false/no/off as OFF;
unrecognized values default to ON with a one-shot stderr warning. Note: in
0.2.x only `=== "1"` was ON, so `=true|yes|on` are flipped to ON in 0.3.0.
Documented in MIGRATION.md.
* `--include-write` is now a no-op deprecation; warning prints BEFORE the
password prompt so it's visible.
Login — never leaks token
* `leadbay-mcp login` defaults to writing a 0600 credentials file at the
platform-correct path:
- $XDG_CONFIG_HOME/leadbay/credentials.json (if set)
- ~/Library/Application Support/leadbay/credentials.json (macOS)
- %APPDATA%\leadbay\credentials.json (Windows)
- ~/.config/leadbay/credentials.json (else)
Backward-compat: if 0.2.x's ~/.leadbay-mcp.json already exists, it's used
with a deprecation note pointing at the new path (no auto-migration).
* `--unsafe-print-token` restores stdout printing for legacy CI flows;
`--print-token` still works for one release with a deprecation warning.
* `--force` overrides the default refusal to overwrite a credentials file
that holds a different account's token/region.
* EACCES/EROFS/ENOENT print actionable remediation pointing at
--write-config /tmp/... or --unsafe-print-token.
Scope — visible from any project
* `leadbay-mcp install` now passes `--scope user` to `claude mcp add`. The
buildClaudeCodeAddArgs helper is exported and tested. README §1, §2 (Claude
Code), §4 (troubleshooting), and §5 (upgrade) all reference the new scope.
Tests
* Drop SERVER_INSTRUCTIONS const re-export — tests now exercise
buildServerInstructions(set) directly.
* New unit suites (43 new tests):
- parse-write-env.test.ts (17): tri-state parser
- login-default.test.ts (8): platform path resolution
- install-flags.test.ts (7): argv contract
* Existing server.test.ts updated for default-mode flips (20 tests).
* All 63 MCP + 145 core + 12 leadclaw tests pass; typecheck + build green.
Files
* packages/mcp/{src/bin.ts,src/server.ts,README.md,CHANGELOG.md,MIGRATION.md,package.json}
* packages/mcp/test/server.test.ts + 3 new unit test files
* packages/dxt/manifest.template.json (leadbay_mcp_write defaults true)
* packages/leadclaw/src/index.ts (stale comment update)
* CHANGELOG.md (root)
Backend follow-up filed as leadbay/product#3540 — processing-worker queue
hung today (US region); imports + qualify stuck at progress 0.0. Tools
correctly time out and return structured errors with importId for resume.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(mcp): pre-landing review fixes — Server version 0.3.0, dead code, vacuous test, partial-exposure tests
- server.ts:170 — Server({ version }) bumped 0.2.0 → 0.3.0 to match package.json (was misreporting in agent diagnostics).
- bin.ts: dead `void fresh;` lines removed; legacy-path branch now calls a new `computeFreshDefaultPath()` helper instead of duplicating resolveDefaultCredentialsPath's platform routing inline.
- server.test.ts:345 — replaced the vacuous "buildServer wires dynamic instructions" test (every assertion was wrapped in `if (typeof instructions === 'string')` which silently passed when getInstructions was undefined). Now asserts against `Server._instructions` (the SDK 1.29.0 internal accessor) for both read-only AND default-writes-on modes — fails loudly if the wiring breaks.
- New test/unit/build-instructions-partial.test.ts (5 tests) — covers buildServerInstructions partial-exposure branches (only bulk_qualify_leads exposed, only enrich_titles exposed, neither, partial composite list, report_outreach absent). Surfaced by the testing specialist; fills a real gap between FULL_EXPOSURE and READ_ONLY matrix coverage.
69/69 tests pass, typecheck + build green.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(mcp): adversarial review fixes — re-login UX, atomic write, shell quoting, identity-based collision
Adversarial dual voices (Claude subagent + Codex) converged on a real critical bug and three correctness issues. All fixed.
- **Critical: re-login on same account always failed.** loginAt() mints a fresh
token on every call; the old collision check compared tokens by equality and
always tripped, forcing users to pass --force on every re-login. Identity
rule rewritten: collision = different email OR different region. The new
config envelope stamps the email at the root so future re-logins can detect
the mismatch. 0.2.x configs (no email field) silently upgrade. New helper
`checkLoginCollision(existing, email, region)` is pure and unit-tested.
- **Atomic write + TOCTOU fix.** runLogin's credentials write was a direct
writeFileSync, so SIGINT mid-write left a half-written file that the next
login refused to touch. Replaced with tmp + chmod + rename — the same pattern
installInJsonConfig has used since 0.2.x. Also eliminates the writeFileSync→
chmod TOCTOU window where the token could briefly sit at the umask default.
- **Honest mode reporting.** statSync the final file and surface the actual
mode in the success message instead of unconditionally claiming "(mode 0600)".
On filesystems that don't honor POSIX modes (FAT32/exFAT/some NFS), the user
now sees the truth and can act on it.
- **Shell quoting in printed snippet.** Default macOS path is `~/Library/
Application Support/leadbay/credentials.json` (has a space). The printed
`claude mcp add … jq -r … ${targetPath}` was unquoted and broke on copy-paste.
Path is now single-quoted with proper escaping for embedded apostrophes.
Tests: new packages/mcp/test/unit/login-collision.test.ts (10 tests) covers
same-account re-login, different-email refusal, different-region refusal,
0.2.x compat, and degraded inputs (null/non-object/empty/non-string email).
Live verified on the test account: same-account re-login succeeds without
--force (was failing in the demo earlier today); different-email collision
refuses with a clear message; 0.2.x file upgrades silently.
79/79 tests pass, typecheck + build green.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(mcp): pin npx examples to 0.3 + clarify login default in LLM hint
The EACCES troubleshooting section claimed all examples use @leadbay/mcp@0.2,
but the §1 / §1.5 examples were already pinned to @0.3 — fix the description.
The LLM HINT in the header was also stale: it told agents that `login --write-config`
mints a token to a file, but in 0.3.0 file-write is the default; --write-config
now overrides the path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 3e347a9 commit 6a54ff7
15 files changed
Lines changed: 1100 additions & 165 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
3 | 7 | | |
4 | 8 | | |
5 | 9 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
| 46 | + | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | | - | |
62 | | - | |
| 61 | + | |
| 62 | + | |
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
17 | | - | |
18 | | - | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
19 | 22 | | |
20 | 23 | | |
21 | 24 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
3 | 35 | | |
4 | 36 | | |
5 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
1 | 73 | | |
2 | 74 | | |
3 | 75 | | |
| |||
0 commit comments