@4da/mcp-server 6.0.2 → 6.2.0

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.
Files changed (119) hide show
  1. package/CHANGELOG.md +433 -0
  2. package/README.md +26 -18
  3. package/dist/db.d.ts +73 -8
  4. package/dist/db.js +180 -43
  5. package/dist/doctor.d.ts +1 -1
  6. package/dist/doctor.js +15 -9
  7. package/dist/embeddings.js +19 -7
  8. package/dist/gitignore.js +9 -3
  9. package/dist/index.js +43 -4
  10. package/dist/live/cache.d.ts +31 -2
  11. package/dist/live/cache.js +113 -1
  12. package/dist/live/changelog-classify.d.ts +9 -3
  13. package/dist/live/changelog-classify.js +125 -12
  14. package/dist/live/changelog.d.ts +22 -3
  15. package/dist/live/changelog.js +132 -38
  16. package/dist/live/dev-scope.d.ts +2 -2
  17. package/dist/live/fix-paths.d.ts +118 -0
  18. package/dist/live/fix-paths.js +498 -0
  19. package/dist/live/github-releases.d.ts +69 -0
  20. package/dist/live/github-releases.js +252 -0
  21. package/dist/live/go-registry.d.ts +5 -0
  22. package/dist/live/go-registry.js +1 -1
  23. package/dist/live/go-releases.d.ts +48 -0
  24. package/dist/live/go-releases.js +196 -0
  25. package/dist/live/index.d.ts +34 -8
  26. package/dist/live/index.js +66 -7
  27. package/dist/live/lockfile-parents.d.ts +61 -0
  28. package/dist/live/lockfile-parents.js +360 -0
  29. package/dist/live/lockfile-parsers-pygo.d.ts +31 -0
  30. package/dist/live/lockfile-parsers-pygo.js +178 -28
  31. package/dist/live/lockfile-parsers.d.ts +4 -1
  32. package/dist/live/lockfile-parsers.js +57 -24
  33. package/dist/live/lockfile-types.d.ts +12 -3
  34. package/dist/live/lockfile-types.js +2 -1
  35. package/dist/live/osv-ranges.d.ts +85 -0
  36. package/dist/live/osv-ranges.js +136 -0
  37. package/dist/live/osv-scanner.d.ts +1 -0
  38. package/dist/live/osv-scanner.js +85 -78
  39. package/dist/live/package-archive.d.ts +2 -0
  40. package/dist/live/package-archive.js +7 -4
  41. package/dist/live/pypi-releases.d.ts +44 -0
  42. package/dist/live/pypi-releases.js +103 -0
  43. package/dist/live/range-satisfies.d.ts +21 -0
  44. package/dist/live/range-satisfies.js +190 -0
  45. package/dist/live/rate-limiter.d.ts +6 -0
  46. package/dist/live/rate-limiter.js +5 -0
  47. package/dist/live/registry-health.js +2 -0
  48. package/dist/live/release-metadata.d.ts +17 -8
  49. package/dist/live/release-metadata.js +6 -3
  50. package/dist/live/resolution.d.ts +11 -1
  51. package/dist/live/resolution.js +16 -6
  52. package/dist/live/semver-utils.d.ts +8 -0
  53. package/dist/live/semver-utils.js +10 -2
  54. package/dist/live/types.d.ts +17 -1
  55. package/dist/live/upgrade-go.d.ts +62 -0
  56. package/dist/live/upgrade-go.js +130 -0
  57. package/dist/live/upgrade-pypi.d.ts +42 -0
  58. package/dist/live/upgrade-pypi.js +85 -0
  59. package/dist/live/upgrade-sources.d.ts +30 -11
  60. package/dist/live/upgrade-sources.js +93 -21
  61. package/dist/live/zip-archive.d.ts +22 -0
  62. package/dist/live/zip-archive.js +98 -0
  63. package/dist/memory-store.d.ts +19 -0
  64. package/dist/memory-store.js +8 -0
  65. package/dist/native-bindings.d.ts +7 -2
  66. package/dist/native-bindings.js +4 -0
  67. package/dist/project-tree.d.ts +50 -8
  68. package/dist/project-tree.js +121 -29
  69. package/dist/schema-registry.js +5 -5
  70. package/dist/server-instructions.d.ts +6 -0
  71. package/dist/server-instructions.js +20 -15
  72. package/dist/sqlite-driver.d.ts +134 -0
  73. package/dist/sqlite-driver.js +359 -0
  74. package/dist/tool-dispatch.d.ts +8 -1
  75. package/dist/tool-dispatch.js +17 -2
  76. package/dist/tools/agent-memory.js +10 -2
  77. package/dist/tools/app-plan.d.ts +11 -1
  78. package/dist/tools/app-plan.js +32 -6
  79. package/dist/tools/briefing-task-scope.d.ts +21 -2
  80. package/dist/tools/briefing-task-scope.js +97 -4
  81. package/dist/tools/decision-memory.js +9 -3
  82. package/dist/tools/dependency-check-signals.d.ts +23 -5
  83. package/dist/tools/dependency-check-signals.js +62 -19
  84. package/dist/tools/dependency-check-versions.d.ts +94 -0
  85. package/dist/tools/dependency-check-versions.js +213 -0
  86. package/dist/tools/dependency-check.d.ts +27 -11
  87. package/dist/tools/dependency-check.js +321 -74
  88. package/dist/tools/dependency-health.d.ts +1 -1
  89. package/dist/tools/dependency-health.js +5 -0
  90. package/dist/tools/knowledge-gap-ranges.d.ts +1 -1
  91. package/dist/tools/knowledge-gap-ranges.js +1 -1
  92. package/dist/tools/knowledge-gap-scope.d.ts +2 -2
  93. package/dist/tools/project-scope.d.ts +45 -0
  94. package/dist/tools/project-scope.js +119 -0
  95. package/dist/tools/record-feedback.js +1 -1
  96. package/dist/tools/upgrade-impact-callsites.d.ts +1 -0
  97. package/dist/tools/upgrade-impact-callsites.js +23 -5
  98. package/dist/tools/upgrade-impact-changelog.d.ts +41 -0
  99. package/dist/tools/upgrade-impact-changelog.js +79 -0
  100. package/dist/tools/upgrade-impact-imports.d.ts +42 -0
  101. package/dist/tools/upgrade-impact-imports.js +260 -0
  102. package/dist/tools/upgrade-impact-literals.js +1 -1
  103. package/dist/tools/upgrade-impact-report.d.ts +23 -0
  104. package/dist/tools/upgrade-impact-report.js +45 -6
  105. package/dist/tools/upgrade-impact.d.ts +13 -4
  106. package/dist/tools/upgrade-impact.js +236 -85
  107. package/dist/tools/upgrade-planner-fixpaths.d.ts +40 -0
  108. package/dist/tools/upgrade-planner-fixpaths.js +0 -0
  109. package/dist/tools/upgrade-planner.d.ts +31 -3
  110. package/dist/tools/upgrade-planner.js +74 -19
  111. package/dist/tools/vulnerability-scan-concise.d.ts +13 -1
  112. package/dist/tools/vulnerability-scan-concise.js +15 -3
  113. package/dist/tools/vulnerability-scan-format.d.ts +58 -1
  114. package/dist/tools/vulnerability-scan-format.js +107 -11
  115. package/dist/tools/vulnerability-scan.d.ts +104 -13
  116. package/dist/tools/vulnerability-scan.js +27 -3
  117. package/dist/tools/what-should-i-know.d.ts +22 -2
  118. package/dist/tools/what-should-i-know.js +122 -12
  119. package/package.json +10 -32
package/CHANGELOG.md CHANGED
@@ -1,5 +1,438 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.2.0 — 2026-10-11
4
+
5
+ ### `dependency_check`: the release-age hold no longer leaves only vulnerable choices
6
+
7
+ The 3-day hold on new releases is waived for a clean version when every safe
8
+ release on its line is inside the hold too and every older release on that line
9
+ has a known advisory: holding back would leave only vulnerable releases to
10
+ write. The `release_age` signal then says `proceed` with
11
+ `hold_waived: "no_settled_safe_release"`, names the line and the count of
12
+ vulnerable older releases, and asks the agent to tell the user the version's
13
+ age. A package whose only releases are new keeps the hold (that is the
14
+ supply-chain case it exists for). Found by G7 (golang.org/x/net on 2026-10-11:
15
+ the only safe releases were 1.8 and 2.8 days old, 59 of 61 v0 releases were
16
+ vulnerable, and every run left go.mod unchanged).
17
+
18
+ ### `dependency_check`: PyPI and Go, safe versions in a constraint, and instructions that make agents call it
19
+
20
+ The G7 agent evaluation (49 dependency tasks x 3 trials, 2026-10-11) found that
21
+ when an agent called a 4DA tool, 0 of 60 runs wrote a vulnerable version, and
22
+ when it did not, 41 of 78 did. The misses were runs that never called one:
23
+ PyPI and Go adds (`dependency_check` took npm and crates.io only), pin tasks
24
+ ("pin ws 7.x" wrote 7.5.10 from memory; body-parser 1.20.3), and agents told
25
+ they could not browse concluding they could not check versions at all.
26
+
27
+ - **PyPI.** Releases, upload times and yanks from the PyPI JSON API (a release
28
+ is yanked when every file is; one with no files is not installable), PEP 440
29
+ ordering, OSV.dev for the exact version.
30
+ - **Go.** Versions from proxy.golang.org, each judged version's date from its
31
+ `.info` (the list has none), `retract` directives in the latest go.mod
32
+ reported like a yank, `+incompatible` releases matched (`v2.0.0` finds
33
+ `v2.0.0+incompatible`), pseudo-versions dated and judged, and a major 2+
34
+ without its `/vN` path named as the different module it is.
35
+ - Signals the registry cannot supply (publisher trust, install scripts, new
36
+ transitive dependencies on PyPI and Go) say "not applicable" with the
37
+ reason, never a made-up value or an `unknown` that blocks the verdict.
38
+ - **Constraints.** `to` may be the constraint the agent was given: `"7.x"`,
39
+ `"^1.20"`, `"=0.17"`, `"~=3.1"`, `"3.1.*"`, `">=2,<3"`, `"v1"`. The item then
40
+ returns `range_check` (`smallest_safe_in_range`, `newest_safe_in_range`,
41
+ `safe_versions`, `smallest_safe_upgrade` above `from`), from every advisory
42
+ OSV.dev lists for the package evaluated against each release the constraint
43
+ admits, and the verdict is given for the version it picks: the smallest safe
44
+ upgrade above `from`, else the newest safe release past the 3-day hold. An
45
+ exact `to` an advisory affects gets the same `range_check` over its own line,
46
+ so "is ws 7.5.10 safe?" answers avoid and names 7.5.11. Withdrawn and
47
+ deprecated releases are never picked; maintenance notices do not make a
48
+ release unsafe. When nothing in the constraint is safe, the advisories that
49
+ affect all of it and the newest safe release outside it are named. A test
50
+ holds the local range evaluation to OSV.dev's own per-version answers on
51
+ recorded data (ws, Jinja2, golang.org/x/net).
52
+ - **Instructions and descriptions.** The server instructions now open with
53
+ the rule "before writing any dependency version into a manifest or
54
+ lockfile, call dependency_check on that exact version, or on the constraint
55
+ you were given", name add / pin / upgrade / bump / downgrade / choose a
56
+ version for all four ecosystems, and say the server reads the registries
57
+ for the agent (so "I cannot browse" is not a reason to skip it).
58
+ `vulnerability_scan` says it judges installed versions and points at
59
+ `dependency_check` for a version about to be written.
60
+ - The manifest-edit hook names a `dependency_check` call for PyPI and Go edits
61
+ too, and passes a range edit (`^7.0.0`) as a constraint instead of dropping it.
62
+ - `ecosystem` is matched case-insensitively (`pypi`, `go`).
63
+
64
+ ### `upgrade_impact` reads PyPI and Go
65
+
66
+ Until 6.1 it answered for npm and crates.io only, and pointed Python and Go
67
+ upgrades at the briefing.
68
+
69
+ - **PyPI.** Versions, publish dates and yanks from the PyPI JSON API, ordered
70
+ by PEP 440 (`2.0`, `3.0.0rc1` and `1.0.post1` are releases, not unreadable
71
+ semver); a two-part version such as pydantic `2.0` is that release, not a
72
+ line. The changelog is read from the target release's sdist on
73
+ files.pythonhosted.org (`CHANGES.rst`, `HISTORY.md`, ...); a release with
74
+ wheels only says so. Import sites are matched on the MODULE the
75
+ distribution installs, established from the project's virtualenv metadata
76
+ (`.venv` / `venv` / `env`: `top_level.txt`, else `RECORD`), else a table of
77
+ well-known differences (`PyYAML` -> `yaml`, `beautifulsoup4` -> `bs4`,
78
+ `Pillow` -> `PIL`), else the distribution name, which the answer labels an
79
+ assumption (`your_code.import_name_source`, `import_name_note`) instead of
80
+ guessing silently.
81
+ - **Go.** Versions from proxy.golang.org (`/@v/list`, dates from `.info`), the
82
+ changelog from the module zip. A major version 2+ is a different module
83
+ path: `github.com/jackc/pgx/v4` -> `v5.0.0` reads the `/v5` module, lists
84
+ the rest of the v4 line and the v5 releases crossed, and reports
85
+ `module_path_change` with the files that import the old path. An up-to-date
86
+ module whose next major exists says so (`newer_major_module`). Import sites
87
+ come from `import "module/..."` specs, never counting another major's path.
88
+ - The parser reads reStructuredText changelogs: titles underlined (or over-
89
+ and underlined) with any RST adornment, their `Released <date>` lines, and
90
+ non-release setext/RST titles as sub-headings. `CHANGELOG_PARSER_VERSION` is
91
+ 4 (with the classifier changes below), so changelogs cached by 6.1 are re-read.
92
+ - A zip reader (central directory only, stored and deflate entries, every
93
+ inflate capped) for Go module zips and zip sdists.
94
+
95
+ ### GitHub release notes, opt-in (`FOURDA_GITHUB_CHANGELOG`)
96
+
97
+ Off by default: the default contract is unchanged, and only the package's
98
+ own registry and OSV.dev are contacted. With `FOURDA_GITHUB_CHANGELOG=true`,
99
+ when the archive has no changelog for the range, `upgrade_impact` reads the
100
+ repository's GitHub releases (for PyPI, first a changelog file the project
101
+ links on GitHub in `project_urls`), with the same parser and classifier.
102
+
103
+ - The repository comes from registry metadata; for Go from the proxy's
104
+ recorded VCS origin, so vanity paths (`google.golang.org/grpc`) and
105
+ multi-module repositories (`service/s3/v1.50.0` tags) resolve. Monorepo tags
106
+ for other packages are skipped.
107
+ - Unauthenticated REST API, or `GITHUB_TOKEN` when set. A rate limit is
108
+ reported with its reset time and how to raise it, and is not cached.
109
+ Results are cached under a key that carries `CHANGELOG_PARSER_VERSION`.
110
+ - With the fallback off and no usable changelog, the answer says the
111
+ changelog is unavailable and gives `github_fallback.how_to_enable` (or says
112
+ enabling it would not help: no GitHub repository). `_meta.sources` and
113
+ `_meta.privacy` name GitHub whenever it was contacted.
114
+
115
+ ### Breaking-entry precision gate (G3)
116
+
117
+ A held-out panel, re-measured by the test suite on every run
118
+ (`src/__tests__/g3-breaking-precision.test.ts`, fixtures in
119
+ `src/__tests__/fixtures/g3/`): 40 real upgrade pairs, 10 each from npm,
120
+ crates.io, PyPI and Go, none of them a package the 6.1 rules were built on.
121
+ Each pair records the changelog source the tool read live (registry archive or
122
+ GitHub releases), trimmed to the range and checked to parse identically. Gold
123
+ for each of the 1,618 entries shown is the majority of three blind raters
124
+ working to a written rubric (`rubric.txt`), each label backed by a verbatim
125
+ quote from the entry (Fleiss kappa 0.984; the raters were model instances,
126
+ so agreement may be higher than independent human raters would reach). The
127
+ test fails below 90% precision, overall or on the held-out half, and on any
128
+ entry without a label.
129
+
130
+ | | flagged | precision | recall |
131
+ |---|---|---|---|
132
+ | 6.1 rules, all 40 pairs | 246 | 94.7% | 55.0% |
133
+ | 6.1 rules, 20 held-out pairs | 108 | 95.4% | 67.8% |
134
+ | 6.2 rules, all 40 pairs | 326 | 97.2% (95% CI 94.8-98.5) | 74.8% |
135
+ | 6.2 rules, 20 held-out pairs | 115 | 94.8% (95% CI 89.1-97.6) | 71.7% |
136
+
137
+ The 6.2 rule changes were made from the 20 dev pairs' errors only:
138
+
139
+ - An API change is read under any heading but a fix one. Covered: a leading
140
+ removal or rename of something API-shaped ("remove UMD build"), a changed
141
+ default, a type made private, a move or replacement, keyword-only
142
+ arguments, "now ... instead of", and "no longer comparable / usable /
143
+ subclasses".
144
+ - Dropped runtimes and raised minimums break whatever the reason given
145
+ ("drop node 10.x to upgrade dev dependencies", "Replace go 1.21 with go 1.22
146
+ in go mod").
147
+ - "Other Changes" / "Misc" headings are neutral, not additive: pgx 5 filed its
148
+ type removals there.
149
+ - Fix headings are their own context.
150
+ - "Previously-deprecated APIs have been removed" is a removal list.
151
+ - `*Backwards-incompatible*` is an explicit marker.
152
+ - Link targets, bare URLs and GitHub's "by @user in <PR>" are dropped before
153
+ classifying (a `go.dev/doc/...` link read "Drop support for Go 1.21" as a
154
+ docs change); RST ``literals`` read as code.
155
+ - No longer breaking: entries that say they still work or have no effect on
156
+ users, private modules and `_names`, removing a deprecation notice,
157
+ restoring an API, relaxing a bound, renaming the repository, swapping a
158
+ dependency crate, and changes scoped to an example, docs or CI.
159
+
160
+ Per ecosystem over all 40 pairs: npm 99.0% / 84.2%, crates.io 99.0% /
161
+ 81.3%, PyPI 95.7% / 72.5%, Go 92.0% / 54.1% (Go's GitHub release notes are
162
+ largely PR titles and prose). `CHANGELOG_PARSER_VERSION` is 4.
163
+
164
+ ### Hook and briefing
165
+
166
+ - The manifest-edit hook names the exact `upgrade_impact` call for
167
+ `pyproject.toml`, `requirements.txt` and `go.mod` edits (`ecosystem: "PyPI"`
168
+ / `"Go"`); `what_should_i_know`'s next step for a PyPI or Go package is
169
+ `upgrade_impact`.
170
+
171
+ ## 6.1.0 — 2026-10-10
172
+
173
+ Fixes from an agent eval over stdio JSON-RPC against real projects (2026-10-07)
174
+ and an engine-generalization test on 19 public repositories against
175
+ osv-scanner, pip-audit and a fix-path oracle (2026-10-10): precision 1.00 over
176
+ 19,115 findings and recall 19,115 of 19,116 on the lockfiles the server reads
177
+ (was 0.74 overall, with 44 langchain lockfiles dropped silently); the median
178
+ `vulnerability_scan` over the corpus went from 27 s to 8.6 s.
179
+
180
+ ### Install: runs under npm 12 with no install scripts
181
+
182
+ npm 12 blocks dependency install scripts unless allowed, so a default
183
+ `npx @4da/mcp-server` or `npm install` left better-sqlite3's native module
184
+ unbuilt and every tool failed (6.0.x documented an `allow-scripts` workaround).
185
+
186
+ - The server uses Node's built-in `node:sqlite` when the runtime has it
187
+ (Node 22.13.0 and later; npm 12 itself requires 22.22.2+). No install
188
+ script, no native download. better-sqlite3 moved to `optionalDependencies`
189
+ and is used only where `node:sqlite` is missing (Node 22.0-22.12), if its
190
+ native module opens a database.
191
+ - One driver interface (`src/sqlite-driver.ts`) covers what the server uses;
192
+ node:sqlite is adapted to match better-sqlite3: plain-object rows, BLOBs as
193
+ Buffers, error codes (`SQLITE_BUSY`, `SQLITE_NOTADB`, ...), the 5 s busy
194
+ timeout, savepoint-nested transactions, `readonly` / `fileMustExist`.
195
+ Read-only connections also set `PRAGMA query_only`.
196
+ - With no usable driver at all the server no longer exits at import: it
197
+ starts, `vulnerability_scan`, `dependency_health`, `dependency_check` and
198
+ `upgrade_impact` run without a database (in-memory cache), and the other
199
+ tools return the fix (upgrade Node, or allow better-sqlite3's build).
200
+ - `--doctor` reports the driver in use. `FOURDA_SQLITE_DRIVER=node|better|none`
201
+ forces one (tests, diagnostics). node:sqlite's one-time ExperimentalWarning
202
+ is filtered; other warnings pass.
203
+ - Tests: the suite passes on each driver (CI runs it on both, and on Node
204
+ 22.12, which has no node:sqlite); driver parity tests; the built server over
205
+ stdio with better-sqlite3's native module missing (fails on 6.0.2: every
206
+ tool answered "Could not locate the bindings file").
207
+
208
+ ### The server never changes the desktop app's schema
209
+
210
+ The server opened the 4DA app's live `4da.db` read-write, created a
211
+ `live_cache` table in it (2,774 rows on one machine) that the app's
212
+ migrations do not own, and added `embedding` columns to app tables when an
213
+ embedding provider was set.
214
+
215
+ - Reads go through a `{ readonly: true, fileMustExist: true }` connection.
216
+ Nothing is created or altered in the app's database: no tables, columns
217
+ or indexes.
218
+ - The response cache moved to `cache.db` in the server's own folder
219
+ (`%LOCALAPPDATA%\4da-mcp`, `~/Library/Application Support/4da-mcp`,
220
+ `~/.local/share/4da-mcp`; `FOURDA_MCP_HOME` moves it).
221
+ - Decisions, agent memory and feedback still go into the app's own
222
+ `developer_decisions`, `agent_memory`, `interactions` and `feedback`
223
+ tables, so the app's decision UI and calibration see them. They go through a
224
+ separate write connection (busy timeout 3 s, one short transaction per
225
+ record). If the app's table or a column a record needs is missing (schema
226
+ drift), the record goes to the server's `standalone.db` and the response
227
+ says so in `_meta`; the server never creates it.
228
+ - Semantic-recall embeddings are cached in the app's tables only when they
229
+ already have `embedding` / `embedding_model` columns; otherwise they are
230
+ computed per call.
231
+ - Tests: the app's `sqlite_master` is identical after every tool ran, and
232
+ only those four tables gain rows; caching alone leaves the file
233
+ byte-for-byte unchanged.
234
+ - The `live_cache` table already inside existing app databases is left
235
+ alone; the app owns that file and can drop it.
236
+
237
+ ### `upgrade_impact`
238
+
239
+ - Reads changelog headings with inline HTML before the version (stripe:
240
+ `## <a id="23-0-0"></a>23.0.0 - 2026-09-30`). stripe 22.3.0 -> 23.0.0 went
241
+ from "no changelog" to 8 releases with 11 entries flagged breaking.
242
+ - Bullets nested under an entry are that entry's `details`, not entries of
243
+ their own; a breaking detail makes the entry breaking. sqlx 0.8.6 -> 0.9.0
244
+ went from "83 entries flagged breaking" to 24 (its Breaking section's 23
245
+ bullets plus the MSRV statement).
246
+ - "`Cargo.lock` Removed from Tracking" is no longer read as a Removed
247
+ category.
248
+ - The parsed-changelog cache key carries the parser version
249
+ (`upgrade-impact:changelog:p2:...`). Parsed changelogs are cached for 7
250
+ days, so before this a parser fix in a release stayed invisible for a week
251
+ on every machine that had already read that package.
252
+
253
+ ### Breaking version ranges
254
+
255
+ - `dependency_health` and `upgrade_planner` label a 0.x minor bump (and a
256
+ 0.0.x patch bump) "major", as `upgrade_impact` and `dependency_check`
257
+ already did: under caret rules 0.12 -> 0.13 is breaking.
258
+ - `dependency_check` returns `review` for a breaking range (a major, a 0.x
259
+ minor) with "breaking version range, run upgrade_impact". It returned
260
+ `proceed` for vitest 3 -> 5, reqwest 0.12 -> 0.13 and stripe 22 -> 23.
261
+ - `what_should_i_know` counts a 0.x minor bump as a major one in
262
+ `majors_crossed` and the delegation verdict. It compared plain major
263
+ numbers, so "upgrade reqwest from 0.12 to 0.13" crossed 0 majors and came
264
+ back `safe_to_delegate`; it is now `review_needed`. Without a stated
265
+ "from", the furthest installed copy sets the distance.
266
+
267
+ ### Project scope
268
+
269
+ - `upgrade_planner` and `what_should_i_know` take `project_path`. Default:
270
+ the project the server was started in; `"*"` for every project. Called
271
+ from 4DA, the app's plan led with navcal, verax and 4da-ledger steps; it
272
+ is now narrowed to the project's steps, lines and sites, and says how
273
+ many steps only touch other projects.
274
+
275
+ ### `what_should_i_know`
276
+
277
+ - Expands the package families a task names ("all tauri plugins", "tauri
278
+ and its plugins", "@tauri-apps packages", "tauri-plugin-*") to the
279
+ project's direct dependencies in them. It matched only `tauri`.
280
+ - For a task asking for the latest release, each package gets `latest`
281
+ (newest stable, installed vs latest major line, `on_latest_major`) and the
282
+ summary says which are already on the latest major.
283
+
284
+ ### `vulnerability_scan`
285
+
286
+ - One recommendation per installed version, each advisory counted once.
287
+ "rsa 0.9.10 — 2 known vulnerabilities" was one advisory on two versions.
288
+
289
+ ### `vulnerability_scan` latency
290
+
291
+ A cold scan (empty cache) through the server's own modules, same machine,
292
+ same network, two runs each (2026-10-10):
293
+
294
+ | Project | Dependencies | Before | After |
295
+ |---|---|---|---|
296
+ | superset | 4,516 | 71 s, 143 s | 9.4 s, 9.2 s |
297
+ | proshop-mern | 1,545 | 34 s, 34 s | 6.5 s, 6.5 s |
298
+ | nushell | 531 | 20 s, 17 s | 4.5 s, 4.4 s |
299
+
300
+ - **Cache writes in one transaction.** A scan caches one row per dependency
301
+ and one per advisory, and each was its own autocommit: on superset the
302
+ network part of a cold scan ended at 12 s and the remaining ~40 s were
303
+ 4,516 WAL commits. They are now one transaction per batch, and the cache
304
+ file runs `synchronous = NORMAL` (a cache: in WAL mode a crash can lose
305
+ only the last writes, never corrupt the file).
306
+ - **Advisory details in a pool of 24, not rounds of 8.** OSV has no batch
307
+ endpoint for advisory records; rounds of 8 each waited for their slowest
308
+ request. A throttled (429), failed or timed-out detail request is retried
309
+ once instead of leaving the advisory without severity or summary.
310
+ - **querybatch chunks (1,000 dependencies each) are sent 4 at a time**
311
+ instead of one after another; every chunk must answer or the scan fails, so
312
+ an unanswered chunk can never read as clean.
313
+ - **No duplicate work beside the warm-up.** The startup warm-up scan and the
314
+ first tool call's scan ran side by side and both fetched every advisory
315
+ before either could cache it. An identical scan already running is now
316
+ awaited (a forced refresh still runs its own), and a detail request in
317
+ flight is shared by every scanner in the process.
318
+
319
+ ### What the standalone scan reads
320
+
321
+ From the same 19-repository corpus, checked against osv-scanner and
322
+ pip-audit.
323
+
324
+ - **The project-tree walk is wider, and never silent about its bounds.**
325
+ langchain keeps 108 lockfiles up to three levels down; the walk (depth 2,
326
+ 64 projects) read 64 and dropped 44 (4,852 advisory findings) without a
327
+ word in any answer. Bounds are now 4 levels and 256 projects (langchain:
328
+ about 2 s for the walk and every project scan). Past a bound the walk keeps
329
+ counting without scanning (up to 20,000 directories), and
330
+ `vulnerability_scan` returns a `coverage` block (concise and detailed:
331
+ `complete: false`, a note, `lockfiles_not_scanned`; detailed also lists
332
+ each skipped lockfile and why). `dependency_health` and `upgrade_planner`
333
+ say "Partial scan: ..." in their summaries.
334
+ - **Python reads every source in a directory, merged:** poetry.lock, uv.lock,
335
+ pdm.lock (new), Pipfile.lock and every requirements file:
336
+ `*requirements*.txt` (requirements-dev.txt, _test_minimum_requirements.txt)
337
+ and the `.txt` files of a `requirements/` folder (superset keeps its 100
338
+ runtime pins in requirements/base.txt, which nothing read), plus a
339
+ `.txt` that `-r`-includes a requirements file and pins packages itself
340
+ (CTFd's development.txt). It read one source per directory, so langchain's
341
+ libs/community poetry.lock hid the _test_minimum_requirements.txt beside
342
+ it. `-r` includes are followed; a `-c` constraints file only gives a
343
+ version to a name a requirements file lists (it installs nothing by
344
+ itself). Against pip-audit on the newly read files: superset 149/152 rows
345
+ agree (the 3 others are advisories OSV lists and pip-audit's data does not
346
+ yet) and 262/262 of pip-audit's advisories are found; CTFd 6/6; langchain
347
+ libs 38/38 rows, 64/65 advisories (the miss is a langchainjs CVE pip-audit
348
+ files under the Python package).
349
+ - A `dir/*/` .gitignore rule hid the files beside the ignored
350
+ subdirectories: `docs/api_reference/*/` made langchain's
351
+ docs/api_reference/requirements.txt look ignored. A directory-only rule now
352
+ ignores a file only through a directory above it.
353
+ - **Dev scope from a requirements file's name, and only from it:**
354
+ requirements-dev.txt, requirements/testing.txt, docs-requirements.txt and
355
+ the like mark their packages dev. A package any other file lists keeps an
356
+ unknown scope (never "dev"), so a runtime package is never graded down by
357
+ a dev file that includes the runtime one.
358
+ - **No lockfile: declared ranges are labelled, never scanned as installs.**
359
+ A package.json or Cargo.toml with no lockfile used to have its range
360
+ floors scanned as installed versions (nushell samples/wasm: 2 false
361
+ findings). Ranged dependencies now carry `declaredRange` ("^1.2.0") in
362
+ `dependency_health` and `upgrade_planner` and are not sent to OSV; exact
363
+ pins still are. `vulnerability_scan` lists each such manifest under
364
+ `coverage.unresolved_manifests` with the command that writes its lockfile.
365
+ - A Cargo.toml read without Cargo.lock took `[package]`'s own `version =
366
+ "0.1.0"` and `edition = "2021"` for crates named "version" and "edition";
367
+ only dependency tables are read now.
368
+
369
+ ### Fix paths (`upgrade_planner`)
370
+
371
+ From a fix-path oracle on 19 public repositories (2026-10-10): each
372
+ recommended step was applied to a copy, the lockfile re-resolved and the
373
+ project re-scanned with osv-scanner.
374
+
375
+ - **A target is checked against every advisory of the package, not only the
376
+ ones the installed version has.** openssl 0.10.38 was sent to 0.10.79,
377
+ which GHSA-phqj-4mhp-q6mq affects (0.10.50 up to 0.10.80): the upgrade
378
+ traded old advisories for a new one. The target is now the smallest
379
+ published version at or above every per-advisory fix that no advisory
380
+ affects (0.10.80). The package's advisories come from one OSV `/v1/query`
381
+ per vulnerable package; versions from its registry (npm, crates.io sparse
382
+ index, PyPI, Go proxy), else from the advisories' own fix events. An
383
+ advisory no release fixes (braces GHSA-vfj7-8cjw-p6xm, last affected
384
+ 3.0.3, the newest release) does not disqualify a target and is named in
385
+ the step. OSV `last_affected` events are read as closed ranges.
386
+ - **"Waiting on upstream" only when a parent really blocks the fix.** Every
387
+ transitive finding used to be `waiting_on_upstream`; for minimist 1.2.5
388
+ (npm), braces 3.0.2 (pnpm) and mio 0.8.0 (Cargo) that was wrong: each
389
+ parent's declared requirement already admitted the fix, and a lockfile
390
+ refresh fixed all three. The planner now reads the parents of the
391
+ installed copy (package-lock v1-v3, yarn v1 and berry and bun.lock record
392
+ the ranges; pnpm and Cargo.lock do not, so the parent's requirement comes
393
+ from registry.npmjs.org or the crates.io sparse index) and returns
394
+ `action: "lockfile_refresh"` with `refreshCommands` (`npm update
395
+ minimist`, `pnpm update braces`, `yarn up -R`, `cargo update -p
396
+ mio@0.8.0 --precise 0.8.11`) when every requirement admits a clean
397
+ version. A step still waiting on upstream names the requirement that
398
+ blocks it (`left-pad 1.0.0 requires "1.2.5"`). Summary and
399
+ `lockfileRefreshes` count them.
400
+ - A Cargo refresh names the locked crates `--precise` cannot move by itself.
401
+ `cargo update -p openssl@0.10.38 --precise 0.10.80` fails on nushell:
402
+ 0.10.80 pulls syn 2, which needs quote ^1.0.25, and quote 1.0.15 is held
403
+ by rstest. The step is now `cargo update -p bytemuck@1.8.0 -p
404
+ proc-macro2@1.0.36 -p quote@1.0.15 && cargo update -p openssl@0.10.38
405
+ --precise 0.10.80`, found by walking the target's dependency requirements
406
+ against the lockfile.
407
+ - Checked by applying every `lockfile_refresh` step of four plans to a copy
408
+ and re-scanning with osv-scanner: 104 of 104 steps cleared their
409
+ advisories (proshop-mern npm 45, taxonomy pnpm 21, chatgpt-tauri pnpm and
410
+ Cargo 26, nushell Cargo 12).
411
+ - **`vulnerability_scan` and `what_should_i_know` run the same check** on
412
+ the targets they recommend. "Upgrade openssl 0.10.38 → 0.10.79" is now
413
+ "→ 0.10.80 (... 0.10.79, the highest per-advisory fix, is itself affected
414
+ by GHSA-phqj-4mhp-q6mq)". Concise rows carry `fix_checked`; detailed
415
+ output adds `fix_targets` per vulnerable package version, while each
416
+ advisory row keeps its own `fixed_version`. The check is cached and
417
+ bounded (5 s per call, most severe first); what it could not reach in time
418
+ says `installed_advisories_only`. On nushell, cold: 33 of 39 package
419
+ versions checked in the first call.
420
+ - The test suite is offline by construction: `fetch` throws unless a test
421
+ stubs it (`FOURDA_TEST_NETWORK=1` lifts that), so no assertion can depend
422
+ on live OSV or registry data.
423
+ - Each vulnerable step says how its target was checked: `fixPathChecked`
424
+ is `all_advisories`, or `installed_advisories_only` when OSV or the
425
+ registry did not answer (offline, or past the 25 s budget), and the
426
+ summary counts those.
427
+
428
+ ### Build
429
+
430
+ - pnpm settings (overrides, onlyBuiltDependencies) moved from package.json
431
+ to pnpm-workspace.yaml: pnpm 11 ignores package.json's `pnpm` field, so
432
+ the CI audit ran without the security overrides. New override
433
+ source-map-js >= 1.2.2 (GHSA-68fv-2mgg-jv7q, high). Fixes the nightly CI
434
+ (#9).
435
+
3
436
  ## 6.0.2 — 2026-10-04
4
437
 
5
438
  ### `--setup` writes the files editors actually read
package/README.md CHANGED
@@ -27,9 +27,7 @@ One command to install. No API keys. No accounts. Your code never leaves your ma
27
27
 
28
28
  Requires Node.js 22 or later.
29
29
 
30
- > **npm 12+:** npm 12 blocks dependency install scripts unless you allow them, which leaves the SQLite module (`better-sqlite3`) unbuilt. Allow it once, then clear the npx cache:
31
- > `npm config set allow-scripts=better-sqlite3 --location=user` and `npx clear-npx-cache`.
32
- > `npx @4da/mcp-server --doctor` checks this by opening a database. npm 10 and 11, which every current Node release ships, are not affected.
30
+ The server uses Node's built-in `node:sqlite` (Node 22.13 and later), so it needs no install script and works with npm 12's default of blocking them. On Node 22.0-22.12 it uses the optional `better-sqlite3`, whose native module npm 10 and 11 build on install. `npx @4da/mcp-server --doctor` shows which one is in use.
33
31
 
34
32
  ```bash
35
33
  claude mcp add 4da -- npx @4da/mcp-server
@@ -108,25 +106,31 @@ Then ask your AI: **"What changes if I upgrade X to Y?"**, **"Scan for vulnerabi
108
106
 
109
107
  ## How It Works
110
108
 
111
- The server reads every lockfile of the project it is started in, including independently-locked projects below the root (a repo with `src-tauri/Cargo.lock` beside a root `pnpm-lock.yaml` is scanned whole) and skipping what `.gitignore` excludes: `package-lock.json` / `npm-shrinkwrap.json`, `pnpm-lock.yaml` (v5–v9), `yarn.lock` (v1 and berry), `bun.lock`, `Cargo.lock` (Cargo workspaces included), `poetry.lock`, `uv.lock`, `Pipfile.lock`, `requirements.txt` pins, and `go.mod` / `go.sum` (Go's build list). Every installed copy is scanned, not one version per name, with the lockfile's own dev flags. It re-reads them whenever a lockfile changes, and for npm it also checks what `node_modules` actually holds.
109
+ The server reads every lockfile of the project it is started in, including independently-locked projects below the root (a repo with `src-tauri/Cargo.lock` beside a root `pnpm-lock.yaml` is scanned whole) and skipping what `.gitignore` excludes: `package-lock.json` / `npm-shrinkwrap.json`, `pnpm-lock.yaml` (v5–v9), `yarn.lock` (v1 and berry), `bun.lock`, `Cargo.lock` (Cargo workspaces included), `poetry.lock`, `uv.lock`, `pdm.lock`, `Pipfile.lock`, requirements pins (`*requirements*.txt`, `requirements/*.txt`, with `-r` and `-c`), and `go.mod` / `go.sum` (Go's build list). Every Python source in a folder is read, not only the first. Every installed copy is scanned, not one version per name, with the lockfile's own dev flags. It re-reads them whenever a lockfile changes, and for npm it also checks what `node_modules` actually holds. The walk goes 4 levels down and up to 256 projects; whatever it leaves out, and every manifest with no lockfile (its declared ranges are not installs and are not scanned), is listed in the answer's `coverage` block, never dropped silently.
112
110
 
113
- Measured against osv-scanner on 12 projects (npm, pnpm, Cargo, Poetry, requirements.txt, Go): precision 1.00, recall 0.995; re-run before release from the installed package on those and 11 more never used to build the readers (yarn, pnpm, bun, uv, Pipfile, Go included), with every difference settled by OSV or Go's own build list.
111
+ Measured against osv-scanner on 19 public repositories (npm, pnpm v5-v9, yarn v1 and berry, bun, Cargo, Poetry, uv, Pipfile, requirements, Go; 2026-10-10), every difference settled by OSV's own API: precision 1.00 over 19,115 findings, recall 19,115 of 19,116 on the lockfiles it reads (Maven/Gradle are not read). Newly read requirement files were also checked against pip-audit.
114
112
 
115
113
  - **OSV.dev** for known vulnerabilities, matched to exact installed versions
116
114
  - **npm registry, crates.io, PyPI, Go module proxy** for versions, deprecations and yanks
117
- - **The package's own registry archive** (registry.npmjs.org, static.crates.io) for the changelog `upgrade_impact` reads; never GitHub
118
- - **npm full packument** and the **crates.io versions API** for `dependency_check` (publish times, publishers, install scripts, per-version dependencies). These requests carry the package name only; the version you have installed is never sent to a registry. Packuments are cached on disk and revalidated with `If-None-Match`; crates.io API reads are spaced one per second.
115
+ - **The package's own registry archive** (registry.npmjs.org, static.crates.io, files.pythonhosted.org, proxy.golang.org) for the changelog `upgrade_impact` reads; GitHub only if you opt in with `FOURDA_GITHUB_CHANGELOG=true` (below)
116
+ - **npm full packument**, the **crates.io versions API**, the **PyPI JSON API** and the **Go proxy's version list** for `dependency_check` (publish times, yanks and retractions; on npm and crates.io also publishers, install scripts, per-version dependencies). These requests carry the package name only; the version you have installed is never sent to a registry, except that the Go proxy's list has no dates, so it is asked for the `.info` of the versions being judged (the same request `go get` makes). OSV.dev's advisory list for the package lets `dependency_check` name the safe releases in a constraint. Packuments are cached on disk and revalidated with `If-None-Match`; crates.io API reads are spaced one per second.
119
117
  - **Hacker News Algolia API**, only when you call `ecosystem_pulse`
120
118
 
121
119
  Results are cached (24h for registry data, 1h for vulnerabilities, 30min for news) and rate-limited per source.
122
120
 
123
121
  **What's sent over the network:** package names and versions (the same data visible in your lockfile), and, only for `ecosystem_pulse`, the names of a few of your dependencies as search terms. No source code, no file paths, no personal data. The call-site scan of `upgrade_impact` runs locally. Set `FOURDA_OFFLINE=true` to disable all network calls.
124
122
 
123
+ **GitHub release notes (opt-in, off by default).** Many packages ship no changelog in their registry archive (vite, zod, cobra, most Go modules). With `FOURDA_GITHUB_CHANGELOG=true` in the server's environment, `upgrade_impact` reads the repository's GitHub releases when the archive has no changelog for the range (for PyPI, first a changelog file the project links on GitHub from its `project_urls`). GitHub then receives the repository name and the versions, nothing else. It uses the unauthenticated REST API (60 requests an hour) or `GITHUB_TOKEN` when set (5,000); a rate limit is reported with its reset time, never retried in a loop, and results are cached. With it off, an answer without a changelog says so and names the setting; nothing is guessed.
124
+
125
+ **What's written to disk, and where:** only files the server owns, in its folder in your user data directory (`%LOCALAPPDATA%\4da-mcp`, `~/Library/Application Support/4da-mcp` or `~/.local/share/4da-mcp`; `FOURDA_MCP_HOME` moves it): `cache.db` (registry, OSV and changelog responses; safe to delete) and `standalone.db` (without the desktop app: your project scan and the decisions, agent memory and feedback you record). Nothing is written inside your repository.
126
+
127
+ When the 4DA desktop app is installed, the server reads the app's database (`4da.db`) through a **read-only** connection and never creates or alters anything in it: no tables, no columns, no cache. The only writes are rows in the four app-owned tables the tools exist to fill, so the app sees them: decisions (`decision_memory` -> `developer_decisions`), agent memory (`agent_memory`), and feedback (`record_feedback` -> `interactions` and `feedback`, which the app's calibration reads). Those go through a separate write connection that waits briefly for the app's lock. If your app version lacks the table or a column a record needs, the record goes to `standalone.db` instead and the response says so in `_meta`. (Before 6.1 the server also created a `live_cache` table inside the app's database; it now keeps that cache in `cache.db`.)
128
+
125
129
  > The one exception: if you *explicitly* configure an OpenAI embedding provider (`FOURDA_EMBED_PROVIDER=openai`) for semantic recall, the decision/memory text you store is sent to OpenAI to be embedded. The default — no embedding provider, or a local Ollama one — keeps everything on your machine, and `FOURDA_OFFLINE=true` overrides it regardless.
126
130
 
127
- **Ecosystems supported:** npm, crates.io (Rust), PyPI (Python), Go. `upgrade_impact`: npm and crates.io.
131
+ **Ecosystems supported:** npm, crates.io (Rust), PyPI (Python), Go. `upgrade_impact` reads all four. PyPI: versions and dates from the PyPI JSON API, the changelog from the release's sdist, and import sites matched on the module the distribution installs: read from your virtualenv's metadata (`.venv`, `venv`, `env`: `top_level.txt` or `RECORD`), else a table of well-known differences (`PyYAML` -> `yaml`, `Pillow` -> `PIL`), else the distribution name, which the answer then labels an assumption. Go: versions and dates from proxy.golang.org, the changelog from the module zip, and a major version 2+ reported as the module path change it is (`github.com/jackc/pgx/v4` -> `/v5`), with the files that import the old path. `dependency_check` reads all four too; on PyPI and Go, where the registry exposes no publisher identity or install hooks, those signals say "not applicable".
128
132
 
129
- **Known limits, stated plainly:** a `requirements.txt` without a lockfile names only your direct pins, so their transitive dependencies are not scanned (use `uv lock`, `poetry lock` or `pip-compile`). Many packages ship no changelog in their registry archive (fastembed, vite, zod among them); `upgrade_impact` then says so and gives the release-notes URL instead of guessing. Breaking entries are flagged from the changelog's headings and wording, and every entry carries the heading it sits under (`under`). Measured on 47 upgrades never used to build the rules, against three blind raters who saw each entry's heading: 99% of entries flagged breaking were breaking (95% CI 95-100%) and about 65% of breaking entries were flagged. Earlier corpora, before the last parser fixes, measured 73-87%, so treat the flags as a pre-sort: read every entry of a major upgrade. Each answer says this in `_meta.classification`.
133
+ **Known limits, stated plainly:** a `requirements.txt` without a lockfile names only your direct pins, so their transitive dependencies are not scanned (use `uv lock`, `poetry lock` or `pip-compile`). Many packages ship no changelog in their registry archive (fastembed, vite, zod among them); `upgrade_impact` then says so and gives the release-notes URL instead of guessing. Breaking entries are flagged from the changelog's headings and wording, and every entry carries the heading it sits under (`under`). Measured on a panel of 40 real upgrades across npm, crates.io, PyPI and Go (1,618 entries, gold from three blind raters with verbatim evidence; the panel and the measurement are in the test suite): on the 20 upgrades held out from rule-making, 95% of entries flagged breaking were breaking (95% CI 89-98%) and about 72% of breaking entries were flagged; Go release notes written as prose are the weakest (about 54% recall). Treat the flags as a pre-sort: read every entry of a major upgrade. Each answer says this in `_meta.classification`.
130
134
 
131
135
  ## What You Can Ask
132
136
 
@@ -136,6 +140,7 @@ Results are cached (24h for registry data, 1h for vulnerabilities, 30min for new
136
140
  "Scan for vulnerabilities" -> vulnerability_scan
137
141
  "Which deps should I upgrade first?" -> upgrade_planner
138
142
  "Is it safe to bump axios to 1.14.1?" -> dependency_check
143
+ "Pin ws to a 7.x version" -> dependency_check (to: "7.x")
139
144
  "I'm about to bump fastembed 5 -> 7" -> what_should_i_know
140
145
  "What's happening in the ecosystem?" -> ecosystem_pulse
141
146
  "What's my tech stack?" -> get_context
@@ -150,23 +155,23 @@ Results are cached (24h for registry data, 1h for vulnerabilities, 30min for new
150
155
 
151
156
  | Tool | What it does |
152
157
  |------|-------------|
153
- | `upgrade_impact` | What changes between the installed and a target version of one dependency: releases in between, changelog entries classified breaking / deprecation / security, the breaking ones that touch your code (symbols you import, and route or pattern syntax in your string literals, e.g. axum 0.8's `/:id` -> `/{id}`), the files that import it, advisories fixed. |
154
- | `vulnerability_scan` | Every installed copy in every lockfile matched against OSV.dev. Scope-adjusted severity, the fix version on your release line, where each version is pinned. Concise by default (one row per vulnerable package version, the 40 most severe, about 4k tokens on a 290-advisory project); `package` for one dependency; `response_format: "detailed"` for every advisory. |
158
+ | `upgrade_impact` | What changes between the installed and a target version of one dependency (npm, crates.io, PyPI, Go): releases in between, changelog entries classified breaking / deprecation / security, the breaking ones that touch your code (symbols you import, and route or pattern syntax in your string literals, e.g. axum 0.8's `/:id` -> `/{id}`), the files that import it, advisories fixed. |
159
+ | `vulnerability_scan` | Every installed copy in every lockfile matched against OSV.dev. Scope-adjusted severity, an upgrade target checked against every advisory of the package (the highest per-advisory fix can itself be affected by another), where each version is pinned, and a `coverage` block whenever the scan is partial. Concise by default (one row per vulnerable package version, the 40 most severe, about 4k tokens on a 290-advisory project); `package` for one dependency; `response_format: "detailed"` for every advisory. |
155
160
  | `dependency_health` | Version freshness, deprecation (of the version you run) and vulnerability counts per dependency. |
156
- | `upgrade_planner` | The smallest version that fixes each vulnerability, majors flagged, transitive fixes waiting on upstream. `package` for a one-package plan. |
157
- | `dependency_check` | Call before adding a dependency or applying a bump. Verdict per item (`proceed` / `wait` / `review` / `avoid` / `unknown`) with evidence: advisories on the target, release age (holds releases under 3 days unless they fix an advisory you have), publish-trust drop, new install scripts, brand-new transitive dependencies, yanked or deprecated. npm and crates.io. |
161
+ | `upgrade_planner` | The smallest version no advisory affects for each vulnerable package, majors flagged (a 0.x minor counts as a major). Transitive fixes: `lockfile_refresh` with the exact command (`npm update`, `pnpm update`, `yarn up -R`, `cargo update -p ... --precise`) when every parent's declared requirement already admits a clean version, else `waiting_on_upstream` naming the requirement that blocks it. For one project: `project_path` (default: the project the server was started in; `"*"` for every project the desktop app tracks). `package` for a one-package plan. |
162
+ | `dependency_check` | Call before writing any dependency version into a manifest: adding, pinning, upgrading, bumping, downgrading. `to` is the exact version, or the constraint you were given (`"7.x"`, `"^1.20"`, `"~=3.1"`, `"v1"`): then `range_check` lists the smallest and newest release in it that no known advisory affects, and the verdict is for the one it picks. An exact version an advisory affects gets the safe releases on its line. Verdict per item (`proceed` / `wait` / `review` / `avoid` / `unknown`) with evidence: advisories on the target, release age (holds releases under 3 days unless they fix an advisory you have), publish-trust drop, new install scripts, brand-new transitive dependencies, yanked or deprecated, and a breaking version range (a major, or a 0.x minor) is `review` with a pointer to `upgrade_impact`. npm, crates.io, PyPI and Go (publisher trust, install scripts and new transitive dependencies are npm/crates.io signals and say "not applicable" elsewhere). |
158
163
 
159
164
  ### Intelligence
160
165
 
161
166
  | Tool | What it does |
162
167
  |------|-------------|
163
- | `what_should_i_know` | Pre-task briefing built from the task: the dependencies it names, their versions and confirmed vulnerabilities, majors crossed, your recorded decisions, and a delegation verdict only confirmed evidence can raise. |
168
+ | `what_should_i_know` | Pre-task briefing built from the task: the dependencies it names (and the families it names: "all tauri plugins" adds the project's tauri-*, tauri-plugin-* and @tauri-apps/* packages; for "latest", whether each is already on the newest major), their versions and confirmed vulnerabilities, majors crossed, your recorded decisions, and a delegation verdict only confirmed evidence can raise. |
164
169
  | `ecosystem_pulse` | Hacker News headlines that name your dependencies, then your languages (labelled as such). Fetched only when called. |
165
170
  | `get_context` | Your tech stack, resolved dependency versions, interests, detected topics. |
166
171
  | `get_relevant_content`* | Scored content feed that passed the desktop app's relevance judge. |
167
172
  | `get_actionable_signals`* | Judge-accepted feed items the app classified (advisories, breaking changes), plus your live vulnerabilities. |
168
173
  | `knowledge_gaps`* | Dependencies with judge-accepted advisories or releases you have not looked at. |
169
- | `record_feedback`* | Save or dismiss items so 4DA can record explicit interaction history. |
174
+ | `record_feedback`* | Save or dismiss items; recorded in the app's own feedback tables, which its calibration reads. |
170
175
 
171
176
  ### Decisions & Memory
172
177
 
@@ -204,7 +209,7 @@ The MCP server works without the desktop app. It keeps a small local database in
204
209
  | Tech stack detection + resolved versions | Yes | Yes |
205
210
  | Decision memory + alignment checking | Yes | Yes |
206
211
  | Agent memory (cross-session) | Yes | Yes |
207
- | Scored content feed (20+ sources) | -- | Yes |
212
+ | Developer news judged against your stack (the app's feed) | -- | Yes |
208
213
  | Actionable signals + knowledge gaps | -- | Yes |
209
214
  | The analysis layer (Signal Chains, Knowledge Gaps, temporal analysis) | -- | Yes |
210
215
 
@@ -253,6 +258,9 @@ npx @4da/mcp-server --version # Print version
253
258
  |----------|-------------|---------|
254
259
  | `FOURDA_DB_PATH` | Path to 4DA's SQLite database | Auto-detected |
255
260
  | `FOURDA_OFFLINE` | Disable all network calls | `false` |
261
+ | `FOURDA_GITHUB_CHANGELOG` | `upgrade_impact`: read GitHub release notes when the registry archive has no changelog for the range (`true` / `1` / `yes` / `on`) | Off |
262
+ | `GITHUB_TOKEN` | Used only with `FOURDA_GITHUB_CHANGELOG`: raises GitHub's rate limit from 60 to 5,000 requests an hour | Unset |
263
+ | `FOURDA_MCP_HOME` | Folder for the files the server writes (`cache.db`, `standalone.db`) | Your user data folder + `4da-mcp` |
256
264
  | `MCP_AUTH_SECRET` | Shared secret for verifying Bearer tokens on `--http` (HMAC-SHA256). Falls back to `JWT_SECRET`. Unset means no token is accepted. | Unset |
257
265
  | `MCP_AUTH_REQUIRED` | Require auth on a **loopback** `--http` bind. Always required on a non-loopback bind. | `false` |
258
266
  | `MCP_ALLOWED_HOSTS` | Extra comma-separated hostnames accepted in `Host`/`Origin` (needed when binding to `0.0.0.0`). | localhost only |
@@ -260,10 +268,10 @@ npx @4da/mcp-server --version # Print version
260
268
  ## FAQ
261
269
 
262
270
  **Does this send my code anywhere?**
263
- No. The server sends package names and versions to public APIs ([OSV.dev](https://osv.dev), npm registry, crates.io, PyPI, Go proxy), downloads the target version's archive from the package's own registry for `upgrade_impact`, and, only when you call `ecosystem_pulse`, sends a few dependency names as search terms to [HN Algolia](https://hn.algolia.com/api). No source code, no file paths, no personal data: the call-site scan runs locally. Set `FOURDA_OFFLINE=true` to disable all network calls. (The sole exception is opt-in OpenAI embeddings — see the network note above.)
271
+ No. The server sends package names and versions to public APIs ([OSV.dev](https://osv.dev), npm registry, crates.io, PyPI, Go proxy), downloads the target version's archive from the package's own registry for `upgrade_impact` (and reads GitHub release notes only if you set `FOURDA_GITHUB_CHANGELOG=true`), and, only when you call `ecosystem_pulse`, sends a few dependency names as search terms to [HN Algolia](https://hn.algolia.com/api). No source code, no file paths, no personal data: the call-site scan runs locally. Set `FOURDA_OFFLINE=true` to disable all network calls. (The sole exception is opt-in OpenAI embeddings — see the network note above.)
264
272
 
265
273
  **Do I need the 4DA desktop app?**
266
- No. 11 tools work standalone: upgrade impact, pre-install dependency checks, vulnerability scanning, dependency health, upgrade planning, ecosystem news, pre-task briefings, project context, decision memory, alignment checking, and agent memory. The desktop app adds a scored content feed from 20+ sources, judged against your actual stack.
274
+ No. 11 tools work standalone: upgrade impact, pre-install dependency checks, vulnerability scanning, dependency health, upgrade planning, ecosystem news, pre-task briefings, project context, decision memory, alignment checking, and agent memory. The desktop app is where the same engine runs day to day: one worklist across every project on the machine, a morning Brief of what changed in your stack, and an Upgrade Plan. With it installed, your agent also gets 5 more tools: signals, knowledge gaps, developer news judged against your stack, Developer DNA and feedback.
267
275
 
268
276
  **Which AI tools does this work with?**
269
277
  Any tool that supports [MCP](https://modelcontextprotocol.io): Claude Code, Claude Desktop, Cursor, Windsurf, VS Code (Copilot), and any custom MCP client.