Skip to main content

MCP server that scans package.json/pubspec.yaml/Cargo.toml/requirements.txt, flags risky upgrades via static semver/deprecation analysis, and surfaces pre-filtered breaking-change excerpts for LLM-driven migration planning

Project description

package-upgrade-mcp

An MCP server that helps an LLM plan dependency upgrades without drowning in changelog text. Point it at a manifest file and it tells you what's outdated, how risky each upgrade looks, and — only for the packages worth worrying about — pulls the actual breaking-change notes.

Supported manifests: package.json (npm), requirements.txt (PyPI), pubspec.yaml (pub.dev), Cargo.toml (crates.io).

No API keys required — every registry it talks to (npm, PyPI, pub.dev, crates.io, GitHub) is public.

Why it's split into stages

Fetching full release notes for every dependency in a real project would burn a huge amount of context for basically no benefit, since most upgrades are patch/minor bumps with nothing breaking in them. So the server does the boring, deterministic part itself and only reaches for text when it's actually warranted:

  1. Cheap and always calledscan_manifest / check_upgrades return current vs. latest version, the semver delta, and a risk level computed from version math plus registry metadata (deprecated/yanked flags). No changelog text, just one registry lookup per dependency.
  2. Targeted, only for what's flaggedget_breaking_change_excerpts fetches changelog or release-note text and runs it through a heuristic filter that keeps the parts that look like breaking-change announcements (## Breaking, BREAKING CHANGE:, removed/renamed/ deprecated notes, migration guides) and throws away routine feature/fix entries. get_raw_changelog is there too, for the rare case the filtered excerpt isn't enough.

The server never decides "this will take 3 days" or writes the upgrade plan itself — that synthesis is left to whichever LLM is calling it. Its job ends at handing over small, pre-digested findings instead of a wall of release notes.

Install

With Claude Code:

claude mcp add package-upgrade-mcp -- uvx package-upgrade-mcp

With OpenAI Codex CLI:

codex mcp add package-upgrade-mcp -- uvx package-upgrade-mcp

With Gemini CLI:

gemini mcp add package-upgrade-mcp -- uvx package-upgrade-mcp

Or add it to your MCP config by hand with uvx (no clone or install step — uvx fetches the package from PyPI on first run):

{
  "mcpServers": {
    "package-upgrade-mcp": {
      "command": "uvx",
      "args": ["package-upgrade-mcp"]
    }
  }
}

This package hasn't been published to PyPI yet, so uvx package-upgrade-mcp won't resolve until it is. Until then, run it from a clone — see Developing locally below, it takes about a minute.

Everything above is optional to configure — the server works with zero setup. If you want to raise GitHub's unauthenticated rate limit (60 requests/hour, which you can hit if you check a lot of packages in one session) or tune HTTP timeouts and cache TTL, copy .env.example to .env and fill in what you need; see the table in Configuration below.

Tools

Tool Stage What it does
scan_manifest(path) 1 Parse a manifest into a flat dependency list. Pure parsing, no network.
check_upgrades(path) 1 Scan + resolve latest versions + static risk analysis (semver delta, deprecated/yanked).
get_versions_between(ecosystem, package, from_version, to_version) 2 List every published version in a range.
get_breaking_change_excerpts(ecosystem, package, from_version, to_version) 2 Fetch changelogs and return only the excerpts that look breaking.
get_raw_changelog(ecosystem, package, from_version, to_version) 2 Full, unfiltered changelog text (opt-in fallback).

ecosystem is one of npm, pypi, pub, cargo.

Usage

You don't call these tools directly — you talk to your agent and it reaches for them based on what you ask. A few examples of what that looks like in practice:

"What's outdated in this project, and how risky does it look?" → triggers scan_manifest + check_upgrades against your package.json (or whichever manifest you point it at). A real run against a small project looked like this:

check_upgrades("package.json") →
  left-pad     1.3.0 → 1.3.0   delta=none   risk=medium  (deprecated)
  lodash       4.17.21 → 4.18.1 delta=minor  risk=medium
  jest         29.0.0 → 30.4.2  delta=major  risk=high
  my-local-pkg (file: dependency, skipped — not resolvable against a registry)

"What actually broke between jest 29 and 30 that could affect my tests?" → once a package is flagged risky, this triggers get_breaking_change_excerpts("npm", "jest", "29.0.0", "30.4.2"), which pulls GitHub release notes and hands back just the migration-relevant bits instead of every release's full changelog.

"Show me the raw changelog for X, the summary you gave me wasn't enough" → falls back to get_raw_changelog for the same version range.

A couple of things worth knowing:

  • check_upgrades never fetches changelog text — it's the cheap first pass. Point the agent at specific packages afterward if you want detail; asking it to explain every dependency up front will make it fan out to get_breaking_change_excerpts for all of them, which is slower and uses more context than necessary.
  • Dependencies pinned via file:, path:, git:, or workspace: specs show up in scan_manifest/check_upgrades but are marked unresolvable — there's no registry to check them against.
  • Results are cached in memory for the life of the server process, so asking about the same package twice in one session doesn't refetch it.

Configuration

All optional, all in .env.example:

Variable Default What it's for
REQUEST_TIMEOUT_SECONDS 15.0 HTTP timeout for registry/changelog requests
CACHE_TTL_SECONDS 3600 How long registry/changelog lookups stay cached
HTTP_USER_AGENT package-upgrade-mcp (contact.hasil@gmail.com) Sent on every request — crates.io in particular rejects requests without one
GITHUB_TOKEN unset Optional; raises GitHub's release-notes rate limit above the unauthenticated 60/hr

Developing locally

uv sync
cp .env.example .env   # optional
uv run package-upgrade-mcp

Point your MCP config at the local checkout instead of uvx:

{
  "mcpServers": {
    "package-upgrade-mcp": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/package-upgrade-mcp",
        "run",
        "package-upgrade-mcp"
      ]
    }
  }
}

Development

uv run pytest -v            # test suite (all HTTP mocked, no network needed)
uv run ruff check .          # lint
uv run ruff format .         # format
uv run mypy src              # type check

To poke at it against real registries instead of through an agent conversation, use the MCP Inspector:

npx @modelcontextprotocol/inspector uv run package-upgrade-mcp

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

package_upgrade_mcp-0.1.1.tar.gz (15.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

package_upgrade_mcp-0.1.1-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

File details

Details for the file package_upgrade_mcp-0.1.1.tar.gz.

File metadata

  • Download URL: package_upgrade_mcp-0.1.1.tar.gz
  • Upload date:
  • Size: 15.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for package_upgrade_mcp-0.1.1.tar.gz
Algorithm Hash digest
SHA256 8a58169ba92a2b02390008d5ad90e2a8a23f198b68cce8daf6540154f72d7668
MD5 e7f7e230cd15cea79990262fb651aeb5
BLAKE2b-256 1b0b02a6069c10cf5fcfecfd5444e222730b718288bd485c15f80f0dbebda02f

See more details on using hashes here.

Provenance

The following attestation bundles were made for package_upgrade_mcp-0.1.1.tar.gz:

Publisher: publish.yml on Hasilt/package-upgrade-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file package_upgrade_mcp-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for package_upgrade_mcp-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 11b51dddca1a6514d1ee07b79813166b5f4f5475b6f340cc47735938f670be77
MD5 a80b6c74f0de4044ffaab1fce7df0909
BLAKE2b-256 3602eceafa0cea732e46490e477e9785f2095c5c88872108a01b95b053a897ba

See more details on using hashes here.

Provenance

The following attestation bundles were made for package_upgrade_mcp-0.1.1-py3-none-any.whl:

Publisher: publish.yml on Hasilt/package-upgrade-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page