@4da/mcp-server
OfficialDependency upgrade intelligence and security analysis for AI coding agents — scans local lockfiles and gives actionable vulnerability, upgrade, and project-memory insights.
Vulnerability scan: Find OSV.dev vulnerabilities in installed dependency versions across npm/pnpm/yarn/bun, Cargo, Python, Go, and more, including transitives, fix versions, and pin locations; filter by package, severity, dev deps, project path, and refresh cache.
Dependency health: Check version freshness, deprecation, and CVE counts across npm, Rust, Python, and Go; sort by risk/name/outdated and filter by ecosystem.
Upgrade planner: Generate a prioritized upgrade plan with smallest fixing versions, risk thresholds, dev-dependency handling, and recommendations for one project or package.
Upgrade impact: See what changes between installed and target versions: changelogs, breaking changes, deprecations, security fixes, and project files importing the dependency.
Pre-install dependency check: Vet exact versions or constraints before writing manifests; get verdicts like proceed/wait/review/avoid/unknown, safe releases, advisories, release age, yanks/deprecations, and breaking-range warnings.
Pre-task briefing: Before non-trivial work, get relevant dependencies/families named in the task, installed versions, confirmed vulnerabilities, releases since, recorded decisions, and a delegation verdict.
Ecosystem pulse: Fetch recent Hacker News discussions that mention dependencies or frameworks your project actually uses.
Project/user context: Retrieve role, tech stack, interests, exclusions, and detected project context.
Decision memory: Record, list, update, supersede, or check alignment of architectural/tech decisions.
Decision alignment check: Test whether a proposed technology or architecture pattern aligns with recorded decisions.
Agent memory: Store and recall cross-agent discoveries, decisions, context, warnings, and preferences.
Local/privacy-focused operation: Reads lockfiles locally; no source code sent; supports offline mode and private-package protections.
Integrates with the Hacker News Algolia API to fetch headlines that mention your dependencies and languages for the ecosystem_pulse tool.
Integrates with the npm registry to retrieve package versions, deprecations, yanks, publish times, publishers, install scripts, and per-version dependencies for npm-based projects, powering vulnerability scans, upgrade impact analysis, and dependency checks.
Can use a local Ollama embedding provider for semantic recall in decision and agent memory, keeping all data on the machine.
Optionally integrates with OpenAI's embedding API for semantic recall in decision and agent memory when explicitly configured.
Integrates with PyPI to fetch package versions, deprecations, and yanks for Python dependencies, supporting vulnerability scanning and upgrade planning.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@@4da/mcp-server what changes if I upgrade axum to 0.8.4?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@4da/mcp-server
Upgrade intelligence for AI coding agents. Before your agent bumps a dependency it learns what changes between the version you run and the one you want, which of your files that touches, and which vulnerabilities the move fixes, from your own lockfiles, on your machine. Plus vulnerability scanning at osv-scanner parity, ranked upgrade plans and decision memory. Zero config, no account.
You: "Upgrade axum to 0.8"
Agent → upgrade_impact { package: "axum", to_version: "0.8.4" }
axum 0.7.9 -> 0.8.4: 5 releases (0.8.2 yanked), changelog from the 0.8.4 crate.
12 breaking entries, 8 touch your code (Path, Query, Router, extract, serve):
breaking Remove OptionalFromRequestParts impl for `Query` (you use Query)
breaking Require `Sync` for all handlers added to `Router` (you use Router)
...
Your code: 10 files import axum.(Real output, abridged, run on this repository, 2026-10-02.)
One command to install. No API keys. No accounts. Your code never leaves your machine.
Install
Requires Node.js 22 or later.
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@6.2.7 --doctor shows which one is in use.
claude mcp add 4da -- npx @4da/mcp-server@6.2.7Every command and config on this page pins the version (@6.2.7), so what runs changes only when you change it. To update, replace the version with the newest one on npm (npm view @4da/mcp-server version); the changelog says what changed. Without a version, npx runs whatever it has cached or the newest release, which you did not review.
As a Claude Code plugin (the MCP server plus a hook):
claude plugin marketplace add 4DA-Systems/4da-mcp-server
claude plugin install 4da@4daThe hook: when your agent edits a dependency's version in package.json, Cargo.toml, pyproject.toml, requirements.txt or go.mod, it is told which packages moved and given the exact dependency_check and upgrade_impact calls to make before it installs and builds. The hook is plain Node, contacts nothing, and stays silent for every other edit.
Add to ~/.cursor/mcp.json (Cursor), ~/.codeium/windsurf/mcp_config.json (Windsurf), or ~/.config/devin/mcp_config.json (Devin Desktop and Devin CLI; %APPDATA%\devin\mcp_config.json on Windows):
{
"mcpServers": {
"4da": {
"command": "npx",
"args": ["@4da/mcp-server@6.2.7"]
}
}
}Run MCP: Open User Configuration and add the entry, or edit the file directly: %APPDATA%\Code\User\mcp.json (Windows), ~/Library/Application Support/Code/User/mcp.json (macOS), ~/.config/Code/User/mcp.json (Linux). VS Code uses servers, not mcpServers:
{
"servers": {
"4da": {
"type": "stdio",
"command": "npx",
"args": ["@4da/mcp-server@6.2.7"]
}
}
}Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"4da": {
"command": "npx",
"args": ["@4da/mcp-server@6.2.7"]
}
}
}npx @4da/mcp-server@6.2.7 --setup --dry-run # show each file and entry, write nothing
npx @4da/mcp-server@6.2.7 --setupWrites Cursor, VS Code (and Insiders), Windsurf, Devin Desktop and Claude Desktop configs it finds, merging into what is there: other servers, settings and comments are kept, a backup (.bak) is written first, and a file it cannot parse is left untouched with the entry to add by hand. For Claude Code it prints the claude mcp add command instead of editing ~/.claude.json.
Then ask your AI: "What changes if I upgrade X to Y?", "Scan for vulnerabilities" or "What should I upgrade first?"
Related MCP server: upgrade-pilot-mcp
How It Works
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): 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), go.mod / go.sum (Go's build list), Gemfile.lock (RubyGems; git and path gems are local code, not releases) and composer.lock (Packagist). 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. A lockfile is what is installed, so it is read whether or not .gitignore lists it, and a dot-directory (.github/actions/*) is a directory like any other. What is not scanned is said: a directory .gitignore excludes is left out (it is as often a scratch clone as the project's own code), and installed code, build output and caches (node_modules, target, dist, .venv, .git ...) are never entered, but every lockfile found in an ignored directory, or sitting directly in a build/-style one, is listed by path under coverage.not_scanned.lockfiles. The walk goes 4 levels down and up to 256 projects; whatever it leaves out is listed the same way. So is every manifest with no lockfile: its declared ranges are not installs and are not scanned, its exact pins are checked but the packages they pull in are resolved nowhere (coverage.unresolved_manifests, and coverage.manifests_without_lockfile for a manifest in a subdirectory that no lockfile above holds as a workspace member). The coverage block is present only when something was left out, and then always says complete: false; nothing is dropped silently. So are manifests of ecosystems it does not read (Maven pom.xml, Gradle, .NET *.csproj / packages.lock.json, Pub, Hex, SwiftPM, CocoaPods: coverage.not_covered), lockfiles that exist but cannot be read (merge-conflict markers, invalid JSON, Bun's binary bun.lockb: coverage.unreadable_lockfiles), lockfiles that are truncated or damaged and were read only up to the damage (coverage.partial_lockfiles), a second lockfile in a directory that disagrees with the one that was read (coverage.conflicting_lockfiles), requirements with no pinned version such as pydantic>=2.0.0 (coverage.unpinned_requirements: there is no installed version to check), dependencies installed from git or a URL (coverage.not_checkable: they are not the registry package of that name, so they are never matched against it), files of a supported ecosystem in a form that is not read (environment.yml, setup.py, pylock.toml), and lockfiles under test-fixture directories (tests/, test/, fixtures/, testdata/, __fixtures__/, conformance/ ...), which are not scanned as the project's own unless you pass include_test_fixtures: true (coverage.not_scanned).
An incomplete answer is never a clean one. When OSV.dev cannot answer for some dependencies (outage, HTTP error, timeout, the query budget), vulnerability_scan starts with summary: "NOT CHECKED: ... N of M dependencies have no vulnerability answer", counts them in coverage.unchecked_dependencies, and sets total_vulnerable_packages and by_severity to null (what the checked part found is under found_in_checked_dependencies); dependency_health withholds its score and vulnerable count, and upgrade_planner sets vulnerabilityDataAvailable: false. Large trees are queried in batches of 1,000 as OSV's budget allows, so a tree past 10,000 dependencies is answered in full, and a long answer is read to its last page. An advisory OSV matched whose record it then failed to return is listed as details: "NOT CHECKED": its severity is unknown, not low, no severity_filter drops it, and it is asked for again on the next scan instead of being cached. The scan the other tools reuse is at most one hour old; past that it is taken again.
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.
Measured with a coding agent (pre-registered, 49 dependency tasks x 3 trials, claude-sonnet-5-5, shell and web off in both arms): it wrote a vulnerable version in 82 of 138 well-posed runs without the server and 0 of 138 with it. Harness, tasks, pinned truth and results: bench/g7.
OSV.dev for known vulnerabilities, matched to exact installed versions
npm registry, crates.io, PyPI, Go module proxy (registry.npmjs.org, crates.io and index.crates.io, pypi.org, proxy.golang.org) for versions, deprecations and yanks
npm download counts (api.npmjs.org), for
dependency_health's weekly-downloads figure, and fordependency_checkwhen a new dependency's name is a near-miss of a popular package (that name and the popular one, nothing else)The package's own registry archive (registry.npmjs.org, static.crates.io, files.pythonhosted.org, proxy.golang.org) for the changelog
upgrade_impactreads; GitHub only if you opt in withFOURDA_GITHUB_CHANGELOG=true(below)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.infoand.modof the versions being judged (the same requestsgo getmakes;.modshows the module path the release declares). OSV.dev's advisory list for the package letsdependency_checkname the safe releases in a constraint. Packuments are cached on disk and revalidated withIf-None-Match; crates.io API reads are spaced one per second.Hacker News Algolia API (hn.algolia.com), only when you call
ecosystem_pulseGitHub (api.github.com, raw.githubusercontent.com), only with
FOURDA_GITHUB_CHANGELOG=trueYour embedding provider, only if you configure one for semantic recall (a local Ollama URL, or api.openai.com; see below)
Those are all the hosts the server contacts.
Results are cached (24h for registry data, 1h for vulnerabilities, 30min for news) and rate-limited per source.
What's sent over the network, and when: nothing when your editor starts the server and lists its tools; the project is read locally, and the first request goes out when a tool that needs it is called (vulnerability_scan, what_should_i_know, dependency_check ...). Then: 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. Never a private package's name (below). 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.
Private packages are never sent. A package is private, with nothing to configure, when your machine already says so:
npm: its scope is mapped to a registry other than the public one in the project's
.npmrc(or a parent directory's, or any member directory's the project walk finds, lockfile or not), your user.npmrc(orNPM_CONFIG_USERCONFIG),.yarnrc.ymlnpmScopesat any indentation or in flow style (@acme:registry=https://npm.acme.dev/), orbunfig.toml[install.scopes](a URL,{ url = ... }, or an[install.scopes.<scope>]table; the project's,~/.bunfig.tomlor$XDG_CONFIG_HOME/.bunfig.toml); or your lockfile installs it from another host: theresolvedlocation inpackage-lock.json/npm-shrinkwrap.jsonand yarn v1yarn.lock, a yarn berryresolutionURL (or__archiveUrl), thetarballinpnpm-lock.yaml(GitHub Packages included), the registry URL of abun.lockentry. Abunfig.toml[install] registryis read like an.npmrcregistry=(below). A globalregistry=setting, and a lockfile host that matches it, is read as a mirror of the public registry, not as making every package private; registry.npmjs.org, registry.yarnpkg.com and registry.npmmirror.com are public. That has a consequence you should know: withregistry=https://artifactory.example/..., an unscoped package published only on that server is still looked up on the public registry by its name. Every coverage note anddependency_checkanswer says so (registry_notice): list such packages inFOURDA_PRIVATE_PACKAGES, or publish them under a scope, to keep them private.Go: its module path matches
GOPRIVATE,GONOPROXYorGONOSUMDB, from the environment or fromgo env -w(theGOENVfile; as go does, an empty variable falls back to it).Cargo:
Cargo.lockinstalls it from a registry other than crates.io, or from git (path dependencies are local code and were never sent); orCargo.tomltakes it from another registry (registry = "acme"other thancrates-io, orregistry-index), in any dependency table, with or without a lockfile.PyPI:
uv.lock,poetry.lockorPipfile.lockinstalls it from an index other than pypi.org; or a requirements file pins it under--index-url/-inaming another index; orpyproject.tomltakes it from another index (uv[tool.uv.sources] name = { index = "acme" }naming a[[tool.uv.index]]that is not pypi.org; Poetryname = { version = ..., source = "acme" }naming a[[tool.poetry.source]]that is not pypi.org), with or without a lockfile; or pip is configured with another index (PIP_INDEX_URL, orindex-urlin any pip config file pip reads: site-wide%ProgramData%\pip\pip.ini,/etc/pip.conf,$XDG_CONFIG_DIRS/pip/pip.conf,/Library/Application Support/pip/pip.conf; per-user%USERPROFILE%\pip\pip.ini,%APPDATA%\pip\pip.ini,~/.pip/pip.conf,~/.config/pip/pip.conf,~/Library/Application Support/pip/pip.conf; the virtualenv's$VIRTUAL_ENV/pip.iniorpip.conf;PIP_CONFIG_FILE) and a requirements file installs it. A company mirror of PyPI named there counts as private too: list nothing, and its packages are reported as not checked. An--extra-index-url(in a requirements file,PIP_EXTRA_INDEX_URLor pip config) that is not pypi.org counts the same way: pip takes each package from whichever index has the newest release, so any name it installs may come from that index, and the packages a requirements file installs are private (not sent, reported as not checked); the answer names the index (registry_notice). A lockfile that records each package's index (uv.lock,poetry.lock,Pipfile.lock) lets the public ones be checked.Go: as go itself decides: only
GOPRIVATE,GONOPROXYandGONOSUMDBmake a module private. A module on your own host that none of them names is fetched by go through the public proxy too, and is looked up here the same way; name it inGOPRIVATE.RubyGems:
Gemfile.locklists it under aGEMsection whoseremote:is not rubygems.org.Packagist:
composer.lockinstalls it from a host other than Packagist and the public code hosts (a Satis or Private Packagist server), or from a repositorycomposer.jsondeclares that is not Packagist (vcs, even on GitHub;composer;path;artifact).
Names are compared the way each registry compares them: a crate is the same whatever its case and whether it is written with _ or - (acme_core, Acme-Core), PyPI names are PEP 503-normalised, npm, RubyGems and Packagist names are case-insensitive. Look-alike spellings are folded first (NFKC, so fullwidth @acme/ui is @acme/ui; every Unicode dash or hyphen is -), and acme/ui or github:acme/ui is read as a form of @acme/ui.
Add your own with FOURDA_PRIVATE_PACKAGES: comma- or space-separated globs, * matching anything (/ included), each optionally prefixed with its ecosystem: FOURDA_PRIVATE_PACKAGES="npm:@acme/*, crates.io:acme-*, PyPI:acme-*, Go:github.com/acme/*". A bare scope (@acme) covers the whole scope; PyPI names are compared PEP 503-normalised (Acme_Auth is acme-auth). The rules are read again whenever one of those files or variables changes. A name your project also installs from the public registry is public (asking about it reveals nothing). A private package is still read from your lockfiles, and is never looked up anywhere, as itself or as the parent of a public one: vulnerability_scan, dependency_health, upgrade_planner and what_should_i_know lead with NOT CHECKED (private) (the briefing also for a private package the task only names, installed or not, and never calls such a task safe_to_delegate) and list each one with the file or setting that made it private (coverage.private_dependency_list), so the answer is never a clean one for it; dependency_check answers it unknown with not_checked: "private"; upgrade_impact refuses it; upgrade_planner never looks up a private parent's releases, and a step that arrives through one leads with NOT CHECKED (private) and names it in privateParents (never an inferred refresh); ecosystem_pulse never uses its name as a search term (its terms come only from the project the server runs for, never from other projects in the same data folder, and a name another of those projects installs privately is never one). As a second line of defence, every OSV.dev, registry and search request passes one check that refuses it when its URL or body names a private package; a refused request reads as not checked, never clean. Registry URLs are shown without credentials.
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.
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, in every mode; safe to delete, it costs refetches only) and standalone.db (without the desktop app: your project scan and the decisions, agent memory and feedback you record; no cache). Nothing is written inside your repository. (Until 6.3.0 standalone mode kept the cache inside standalone.db; that table is dropped the first time 6.3.0 opens it.)
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.)
Where configuration and the database come from: FOURDA_DB_PATH if set (a relative path resolves against the server's working directory and must name an existing file; an absolute path that names no file is created as a new standalone database when its directory exists, and the first tool answer says so in _meta.database_note; otherwise it is refused with an error), else the desktop app's database in its data directory, else the server's own standalone.db. Embedding settings come from FOURDA_SETTINGS_PATH, else the settings.json beside FOURDA_DB_PATH, else the desktop app's data directory. Nothing is picked up from the directory the server is started in: until 6.2.5 a data/settings.json or data/4da.db inside (or beside) the working directory was adopted, so a cloned repository could redirect semantic recall to a host of its choosing or plant "decisions".
The one exception: if you explicitly configure an OpenAI embedding provider (
FOURDA_EMBED_PROVIDER=openai) for semantic recall, OpenAI receives exactly this, to be embedded: the text of the decisions and memories you stored (subject, decision or content, rationale, rejected alternatives), and the text being searched for: awhat_should_i_knowtask, anagent_memoryrecall query, or acheck_decision_alignmenttechnology and pattern. Never the file paths you pass infiles(they rank results locally), never source code. The default — no embedding provider, or a local Ollama one — keeps everything on your machine, andFOURDA_OFFLINE=trueoverrides it regardless.
Ecosystems supported: npm, crates.io (Rust), PyPI (Python), Go; vulnerability_scan also reads RubyGems (Gemfile.lock) and Packagist (composer.lock). Maven, Gradle and .NET are not read, and are listed as not covered. upgrade_impact reads npm, crates.io, PyPI and Go. 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".
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.
What You Can Ask
"What changes if I upgrade axum to 0.8?" -> upgrade_impact
"Check my dependency health" -> dependency_health
"Scan for vulnerabilities" -> vulnerability_scan
"Which deps should I upgrade first?" -> upgrade_planner
"Is it safe to bump axios to 1.14.1?" -> dependency_check
"Pin ws to a 7.x version" -> dependency_check (to: "7.x")
"I'm about to bump fastembed 5 -> 7" -> what_should_i_know
"What's happening in the ecosystem?" -> ecosystem_pulse
"What's my tech stack?" -> get_context
"Record a decision: we chose Postgres" -> decision_memory
"Does switching to MySQL align?" -> check_decision_alignment
"Remember: never use ORM for batch inserts" -> agent_memoryAll 16 Tools
Dependency Security
Tool | What it does |
| 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 |
| 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 |
| Version freshness, deprecation (of the version you run) and vulnerability counts per dependency. |
| The smallest version no advisory affects for each vulnerable package, majors flagged (a 0.x minor counts as a major). Direct steps ( |
| Call before writing any dependency version into a manifest: adding, pinning, upgrading, bumping, downgrading. |
Intelligence
Tool | What it does |
| 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. A version the task names ( |
| Hacker News headlines that name your dependencies, then your languages (labelled as such). Fetched only when called. |
| Your tech stack, resolved dependency versions, interests, detected topics. |
| Scored content feed that passed the desktop app's relevance judge. |
| Judge-accepted feed items the app classified (advisories, breaking changes), plus your live vulnerabilities. |
| Dependencies with judge-accepted advisories or releases you have not looked at. |
| Save or dismiss items; recorded in the app's own feedback tables, which its calibration reads. |
Decisions & Memory
Tool | What it does |
| Record, query, and manage architectural decisions across sessions. |
| Verify if a proposed technology change aligns with recorded decisions. |
| Persistent memory that survives across sessions, agents, and editors. |
Identity
Tool | What it does |
| Your tech identity: primary stack, top dependencies, blind spots. |
* Requires the 4DA desktop app for full data.
Prompt: deps
A user-invoked workflow (shown as a slash command by hosts that surface MCP prompts). It tells the agent to run upgrade_planner, check every proposed bump with dependency_check, apply only proceed items in small batches with the project's own tests after each batch, stop and report every review / avoid / wait / unknown item with its evidence, and re-run vulnerability_scan at the end. Optional argument scope (e.g. security only).
Standalone vs. Full Mode
The MCP server works without the desktop app. It keeps a small local database in your user data folder (%LOCALAPPDATA%\4da-mcp, ~/Library/Application Support/4da-mcp or ~/.local/share/4da-mcp, never inside your repository) and scans your project on every start:
Capability | Standalone | With 4DA Desktop |
Upgrade impact (changelog, breaking changes, your call sites) | Yes | Yes |
Vulnerability scanning (OSV.dev) | Yes | Yes |
Dependency health (4 registries) | Yes | Yes |
Upgrade planner | Yes | Yes |
Pre-install dependency check | Yes | Yes |
Ecosystem news (Hacker News, on request) | Yes | Yes |
Pre-task intelligence briefing | Yes | Yes |
Tech stack detection + resolved versions | Yes | Yes |
Decision memory + alignment checking | Yes | Yes |
Agent memory (cross-session) | Yes | Yes |
Developer news judged against your stack (the app's feed) | -- | Yes |
Actionable signals + knowledge gaps | -- | Yes |
The analysis layer (Signal Chains, Knowledge Gaps, temporal analysis) | -- | Yes |
Download 4DA for the full experience.
Transports
stdio (default) -- works with all MCP hosts:
npx @4da/mcp-server@6.2.7Streamable HTTP -- for remote or multi-client setups:
npx @4da/mcp-server@6.2.7 --http --port 4840The HTTP transport binds to 127.0.0.1 by default and applies a Host-header
DNS rebinding guard to every request. Exposing it beyond this machine requires
a shared secret:
MCP_AUTH_SECRET=<same value as the relay's JWT_SECRET> \
MCP_ALLOWED_HOSTS=mcp.internal \
npx @4da/mcp-server@6.2.7 --http --host 0.0.0.0Without MCP_AUTH_SECRET a non-loopback bind is refused at startup. With it,
every request must carry a Bearer token whose HMAC-SHA256 signature verifies
against that secret, and the token's role is enforced per tool (viewer is
read-only; member and admin may write). Put TLS in front of it.
HTTP mode is single-tenant. A token's team_id is verified but not used to
separate data: every authenticated client shares one database, one agent memory
and one set of decisions, and project_path lets any authenticated client scan
any directory on the host the server can read. Run one server per team (or per
trust boundary), and only behind clients you would give that access.
CLI Reference
npx @4da/mcp-server@6.2.7 # Start server (stdio)
npx @4da/mcp-server@6.2.7 --http # Start server (Streamable HTTP)
npx @4da/mcp-server@6.2.7 --setup # Auto-configure your editors
npx @4da/mcp-server@6.2.7 --doctor # Verify installation health
npx @4da/mcp-server@6.2.7 --version # Print versionEnvironment Variables
Variable | Description | Default |
| Path to 4DA's SQLite database. An existing file is used. A relative path (resolved against the working directory) that names no file is refused. An absolute path that names no file is created as a standalone database if its directory exists (one stderr line and | The desktop app's database, else the server's own |
|
| Beside |
| Disable all network calls |
|
| Package names never sent anywhere, in addition to what | Unset |
| The time one tool call may take, counted from the request. A call still running then is answered NOT CHECKED ( |
|
| The time the startup walk of the project tree may take. Past it the scan is partial and says so. |
|
| Every lookup goes through the proxy (NO_PROXY honoured); an unreachable proxy means NOT CHECKED, never a direct connection. See "Behind a proxy". | Unset |
|
| Off |
| Used only with | Unset |
| Folder for the files the server writes ( | Your user data folder + |
| Shared secret for verifying Bearer tokens on | Unset |
| Require auth on a loopback |
|
| Extra comma-separated hostnames accepted in | localhost only |
Limits
The server reads repositories it did not write, so everything it reads is bounded, and a bound that is hit is reported, never passed off as a clean result.
Time. A tool call has 20 s (
FOURDA_CALL_BUDGET_MS); the walk that finds a project's lockfiles has 10 s (FOURDA_WALK_BUDGET_MS), 20,000 directories, 4 levels and 256 projects. Past a walk bound the answer is a partial scan (coverage.complete: false).Size. A lockfile or manifest over 64 MB is not read and is reported as UNREADABLE. A
.gitignoreover 1 MB, its rules past 5,000, and a pattern over 1,024 characters are not applied; a negation that cannot be applied re-includes everything below its file, so a limit can only make the scan cover more.Arguments. A version or constraint is at most 256 characters, a package name 512.
Place. A drive or filesystem root and the operating system's own directories (
C:\Windowsand below,/usr,/etc,/Systemand the like) are not scanned: passproject_pathwith your project's root, or setFOURDA_PROJECT_DIR.
Behind a proxy
Set HTTPS_PROXY (and HTTP_PROXY, NO_PROXY, in upper or lower case) in the server's environment (the env block of the MCP server entry) and every OSV.dev, registry and search lookup goes through that proxy, on every supported Node; nothing else to set. Hosts NO_PROXY names are reached directly. A proxy that cannot be reached, or refuses a request, makes the lookup fail, and the answer says NOT CHECKED: nothing is ever retried around the proxy. The server uses undici's EnvHttpProxyAgent for this (undici, MIT, pinned). An embedding provider you configure is the exception: it uses Node's own fetch, which honours the proxy on Node 24.14+, or on 22.21+ started with NODE_USE_ENV_PROXY=1.
The first lines of the server's stderr, and npx @4da/mcp-server@6.2.7 --doctor, say so whenever a proxy variable is set.
FAQ
Does this send my code anywhere?
No. When a tool needs them (never at startup), the server sends public package names and versions to public APIs (OSV.dev, npm registry, crates.io, PyPI, Go proxy), never a private package's name, 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. 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.)
How does this compare with GitHub's dependency tool for agents?
GitHub's hosted MCP server has check_dependency_vulnerabilities (dependabot toolset). It needs a GitHub token and reports high and critical advisories and malware. On 216 identical cases (2026-11-11, ground truth from OSV.dev) it was faster (median 1.2 s against 2.5 s for this server's 6.2.7) and covers Maven and .NET, which this server does not read; it missed 102 of 178 known-bad versions, mostly medium and low advisories and RustSec / PyPA / Go-only records, which this server reports, and it does not judge nonexistent, yanked, deprecated or days-old releases. This server needs no account and runs on your machine.
Do I need the 4DA desktop app? 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.
Which AI tools does this work with? Any tool that supports MCP: Claude Code, Claude Desktop, Cursor, Windsurf, VS Code (Copilot), and any custom MCP client.
Build from Source
git clone https://github.com/4DA-Systems/4da-mcp-server.git
cd 4da-mcp-server
pnpm install
pnpm build
pnpm test # offlineLicense
Apache License 2.0 (Apache-2.0). See LICENSE.
Built by 4DA
Available Tools
11 toolsagent_memoryAgent memoryA
Cross-agent persistent memory: what one agent learns, any agent can recall. Call to store a discovery, decision, or warning, or to recall prior context before starting work.
Inputs: action (required), tags, limit, query, +9 more.
check_decision_alignmentDecision alignment checkARead-only
Check whether a technology or pattern aligns with the developer's recorded decisions. Call BEFORE suggesting a major tech change, new library, or architecture shift.
Inputs: technology (required), pattern.
decision_memoryDecision memoryA
Record, list, update, or supersede the developer's architectural and tech decisions. Call when the user makes, changes, or asks about a settled decision or convention.
Inputs: action (required), id, limit, new_id, +16 more.
dependency_checkDependency checkARead-onlyIdempotent
Is a dependency version safe to write? Verdict on an exact version, or safe releases in a constraint ("7.x", "~=3.1"); npm, crates.io, PyPI, Go. Call BEFORE writing any version to a manifest: add, pin, upgrade, bump, downgrade.
Inputs: items (required).
dependency_healthDependency healthARead-only
Dependency version freshness, deprecation and CVE counts across npm/Rust/Python/Go. Call when the user asks whether their dependencies are outdated, stale or need updating.
Inputs: limit, sort_by, include_dev, ecosystem_filter.
ecosystem_pulseEcosystem pulseARead-only
Recent Hacker News discussions that name a dependency or framework this project actually uses. Call when the user asks what is new or being discussed in their ecosystem.
Inputs: limit, min_points.
get_contextUser contextARead-only
What 4DA knows about the user: role, tech stack, interests, exclusions, and detected project context. Call FIRST when you need to know what the user works on before answering or recommending.
Inputs: include_ace, include_learned.
upgrade_impactUpgrade impactARead-only
What changes between the installed version of ONE dependency and a target: changelog per release, breaking changes, deprecations, security fixes, and this project's files that import it. Call before upgrading or bumping a dependency.
Inputs: package (required), ecosystem, to_version, from_version, +1 more.
upgrade_plannerUpgrade plannerARead-only
Prioritized upgrade plan for one project (project_path): the 4DA app's work order when computed (per-line targets, manifest vs lockfile fix), else the smallest version that fixes each vulnerability. Call when the user asks what to upgrade.
Inputs: package, include_dev, project_path, risk_threshold, +1 more.
vulnerability_scanVulnerability scanARead-only
Scan this project's lockfiles (npm/pnpm/yarn/bun, Cargo, Python, Go) for OSV.dev vulnerabilities in installed versions, transitives included, with fix versions and where each is pinned; package for one dep. Call when asked about security or CVEs. To vet a version first: dependency_check.
Inputs: package, include_dev, project_path, force_refresh, +3 more.
what_should_i_knowPre-task briefingARead-only
Pre-task briefing for a task in one project: the dependencies and package families it names, installed versions, confirmed vulnerabilities, releases since, your decisions, a delegation verdict. Call BEFORE a non-trivial task, especially one that changes dependencies.
Inputs: task (required), files, project_path.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 11 tools
Several tools target the same vulnerability/health space: dependency_health, vulnerability_scan, and dependency_check all report CVEs but on different scopes (aggregate counts, project lockfiles, single version), which can confuse an agent picking between them. Likewise agent_memory and decision_memory both store decisions/warnings, and get_context vs what_should_i_know both surface context, though the 'call when' cues in descriptions help disambiguate.
All names are snake_case so the casing is consistent, but the conventions are mixed: some are noun phrases (dependency_health, ecosystem_pulse, decision_memory), some are verb_noun (get_context, vulnerability_scan, check_decision_alignment), and one is a full question (what_should_i_know). It remains readable but there is no predictable verb_noun pattern throughout.
Eleven tools sit comfortably in the ideal 3-15 range and the set is coherently scoped to dependency intelligence plus developer context. A few dependency tools (health, scan, check, planner, impact) could arguably be consolidated, but none feels gratuitous.
The surface covers the domain well: health, upgrade planning, vulnerability scanning, pre-write version vetting, impact analysis, decision memory, agent memory, ecosystem pulse, and user context. Minor gaps exist (e.g., no explicit dependency listing/package-family enumeration, no advisory search), but agents can work around them.
Maintenance
Related MCP Connectors
CVE lookups (NVD) and dependency-manifest audits (OSV) for AI agents. No API keys.
CVE lookups (NVD) and dependency-manifest audits (OSV) for AI agents. No API keys.
Package intelligence for AI agents across npm, PyPI, crates.io and deps.dev. No API keys.
Package intelligence for AI agents across npm, PyPI, crates.io and deps.dev. No API keys.
Related MCP Servers
- AlicenseAqualityAmaintenanceDependency intelligence for AI agents. CVE scanning, health checks, upgrade planning.9590 npm2Apache 2.0
- AlicenseBqualityDmaintenanceEnables AI agents to safely upgrade JavaScript and TypeScript projects through dependency analysis, upgrade path detection, breaking change identification, codemod application, and PR summary generation.1415 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables autonomous AI agents and CI/CD runners to discover repository modules, scan outdated dependencies, fetch version histories, and apply verified dependency updates with major-version safety guardrails across polyglot monorepos.877 npmMIT
- AlicenseAqualityBmaintenanceEnables AI coding agents to ground dependency setup, compatibility checks, and major-version migration advice in a project's real runtime, lockfiles, and config files. It parses local project state and npm registry metadata deterministically, then returns tag-pinned install commands, breaking changes, and upgrade steps rather than generic documentation.553 npmMIT