---
name: devupdate-dependency-upgrades
description: Plan and apply dependency upgrades safely using the DevUpdate.io MCP server. Use when the user asks to update/upgrade dependencies, work through available upgrades, address security updates in pinned packages, or act on DevUpdate.io findings. Requires the DevUpdate.io MCP server (https://api.devupdate.io/mcp) to be connected.
---

# Safe dependency upgrades with DevUpdate.io

DevUpdate.io analyzes every release of your monitored dependencies — diffs,
changelogs, security fixes, and undocumented behavior changes — and computes
which upgrades are available for each lockfile. Your job is to combine that
intelligence with the project's own tooling and tests. The MCP tools tell you
*what's in an upgrade*; only the repository tells you *whether this project
survives it*.

## Workflow

### 1. Triage

Call `get_lockfile_upgrade_summary` (no arguments) to see every lockfile's
counts: upgradeable / major / security / high-risk / not monitored. Pick the
lockfiles you'll work on.

### 2. Pull the full picture — unfiltered

Call `get_available_upgrades(lockfile_id=…)` **without a `risk_level`
filter**. Risk levels mean:

| Level | Meaning | Action |
|---|---|---|
| `critical` | A security fix ships in the (pinned, latest] range — the pin is on the vulnerable side of it | Take first, today |
| `high` | Major jump or documented breaking changes in the range | Read details before taking |
| `medium` | Undocumented changes detected via diff, or a release scoring 40+ | Batch, verify with tests |
| `low` | Routine catch-up | Batch freely |

Never conclude "up to date" from a *filtered* query that returns nothing —
re-query without the filter.

### 3. Partition into waves

- **Wave 1 — security (`critical`).** Apply each one, even when the range
  also contains breaking changes; in that case read the details first
  (step 4) and do the migration as part of the same wave.
- **Wave 2 — routine (`low` + `medium`).** Apply as one batch with the
  ecosystem's native bulk command.
- **Wave 3 — careful (`high`).** Decide per package using step 4. Never
  bundle a major upgrade into a batch you can't bisect.

### 4. Read the range before risky upgrades

For every `high` upgrade (and `critical` ones that also flag `breaking` or
`major`), call `get_upgrade_details(upgrade_ids=[…])` (max 5 per call). It
returns each release in the exact (pinned, latest] range with breaking
changes, security fixes, migration notes, and undocumented changes detected
from the diff. Decide from that — not from the version number alone.

**Green tests are not enough when the dependency is mocked.** If the test
suite stubs the integration (an HTTP SDK, a cloud client, an LLM API),
passing tests prove nothing about a major upgrade of it. Check how the
dependency is exercised before trusting the suite; if it's mocked, weigh the
migration notes more heavily and say so in your report.

### 5. Apply with native tooling

Use the ecosystem's own tools so resolution stays correct; DevUpdate.io never
edits your project.

- **Python (uv):** `uv lock --upgrade-package <name> …` then `uv sync`
- **Python (pip-tools/poetry):** bump in `pyproject.toml`, re-lock
- **npm:** `npm update` for in-range; `npm install <name>@<version>` for
  targeted bumps. (`npm outdated` shows "Wanted" vs "Latest" — semver-range
  ceilings, not safety.)
- **Cargo / Composer / Go:** `cargo update -p`, `composer update <name>`,
  `go get <module>@<version>`

Respect **coupled pins**: some packages exact-pin siblings (e.g.
`aiobotocore` pins `botocore`/`boto3`; framework families like
`next`/`eslint-config-next` move together). Upgrade coupled sets together or
defer them together — check the resolver error or the package's constraints
before fighting it.

### 6. Verify

Install, then run the project's test suite, lint, and typecheck. If a wave
fails, bisect within the wave rather than reverting everything.

### 7. Close the loop

- **Don't dismiss what you upgraded.** The next lockfile sync auto-resolves
  taken upgrades.
- **Do dismiss what you deferred**: `dismiss_upgrades(upgrade_ids=[…])` for
  each upgrade you deliberately decided not to take, so the next session
  starts from a clean list. A dismissal auto-clears when a newer version
  appears.
- **Cover the gap**: if the summary showed *not monitored* dependencies,
  DevUpdate.io has no data for them — run the registry audit
  (`npm audit`, `pip-audit`, `cargo audit`) and handle its findings
  separately. Beware `npm audit fix --force`: it may apply breaking
  *downgrades*; never accept one without checking what it changes.

### 8. Report

Summarize per wave: what was taken (call out the security fixes by package
and CVE where known), what was deferred and *why* (with the dismissed ids),
and what's unmonitored or otherwise out of scope. Flag any upgrade you took
on weak evidence (mocked integration, sparse tests).

## Pitfalls

- A filtered `get_available_upgrades` that returns nothing is **not** "up to
  date".
- Don't treat the risk level as the whole story — the flags
  (`major`/`security`/`breaking`) and `get_upgrade_details` are what justify
  a decision.
- Upgrade ids change as upgrades resolve; don't reuse ids across sessions.
- DevUpdate.io's view lags the registry by up to one sync cycle; for a
  just-published fix, cross-check the registry directly.
