Every tool, every field.
The same 6 tools are available two ways: as an MCP server your agent connects to once, and as a plain HTTP API. Both take the same parameters and return the same JSON.
Connect
MCP (Claude Code, Cursor, and any client that supports remote MCP servers over streamable HTTP):
claude mcp add --transport http wisco-labs https://api.wiscolabs.dev/mcp
Or in a client's JSON config:
{
"mcpServers": {
"wisco-labs": { "url": "https://api.wiscolabs.dev/mcp" }
}
}VS Code (Copilot agent mode), in .vscode/mcp.json:
{
"servers": {
"wisco-labs": { "type": "http", "url": "https://api.wiscolabs.dev/mcp" }
}
}HTTP: POST https://api.wiscolabs.dev/v1/<dataset>/<tool> with a JSON body, or GET with the same names as query parameters.
Make it automatic. Agents only call a tool when they think of it. Add this to your project's CLAUDE.md, AGENTS.md or Cursor rules:
## Dependencies Before adding, upgrading or pinning any Python or npm dependency, check it with the wisco-labs tools: upgrade_check for the whole requirements.txt or package-lock.json, what_changed for a single package. Never install a version they report as vulnerable or malicious; use the suggested version instead. Tell me about any breaking changes or removals before upgrading.
Basics
- No key needed during the beta. Please keep usage reasonable; heavy automated use may be rate limited.
- Responses are JSON. Package names are matched the way each registry does (e.g.
Flaskandflaskare the same). - Coverage: the 3,000 most-used PyPI packages and 1,000 most-used npm packages. Vulnerabilities are checked for any package, tracked or not.
- Freshness: data is collected continuously.
GET https://api.wiscolabs.dev/healthreturns when it was last published.
Errors
Errors come back as JSON with an error message that says what to fix.
| Status | Meaning |
|---|---|
400 | A required parameter is missing, or a value isn't valid (e.g. not a real version number). |
404 | Unknown tool, or a package we don't track (where the tool needs its history). |
503 | Data is being published for the first time; try again shortly. |
500 | Something went wrong on our side. It's logged; please try again. |
Check a whole project's dependencies
Give it a project's dependencies: a requirements.txt or pip freeze output for Python, or a package-lock.json, package.json, or name@version lines for npm. Flags malicious packages and versions with known vulnerabilities (with the smallest safe version to move to, preferring a low-risk patch), then reports what each upgrade breaks, removes, or deprecates, ranked into remove_now / fix_now / no_fix_available / upgrade_first / safe / review / needs_care.
- MCP tool
upgrade_check- HTTP
POST /v1/package_changes/upgrade_checkwith a JSON body, orGET /v1/package_changes/upgrade_check?…with query parameters
Parameters
| Parameter | What it is | |
|---|---|---|
requirements | required | Python: requirements.txt contents, one 'package==version' per line. npm: the contents of package-lock.json (best), package.json, or one 'name@version' per line. Lines without one exact version are listed but can't be assessed. |
ecosystem | optional, default PyPI | PyPI or npm. A package.json or package-lock.json is recognized as npm automatically. |
limit | optional, default 5 | Max items shown per category for each package |
What comes back
| Field | Meaning |
|---|---|
ecosystem | PyPI or npm: the ecosystem the dependencies were read as. |
input_notes | Present only when some input lines couldn't be read exactly (e.g. a version range in package.json); explains what was skipped. |
summary | Counts across the whole project: packages checked, outdated, up to date, not tracked, not assessed (no exact version), how many upgrades are high / medium / low risk, and total breaking changes, removals, deprecations and security fixes the upgrades would bring. |
security | Vulnerabilities in the versions you're on now: how many packages are affected, the number of known vulnerabilities, packages grouped by worst severity, and any packages reported as malicious. |
recommendation | Package names sorted into what to do, most urgent first. remove_now: reported as malicious. fix_now: has known vulnerabilities and a fixed version exists. no_fix_available: vulnerable with no fix released yet. upgrade_first: the upgrade gains security fixes without high risk. safe_to_upgrade: no notable changes. review_then_upgrade: removals, deprecations or new runtime requirements to check. needs_care: breaking changes or a major version jump. |
packages[] | One entry per outdated package: current and latest version, suggested_version (the smallest version without known vulnerabilities, when the current one has any), the vulnerabilities, upgrade_risk, and the same change lists as what_changed (breaking_changes, removals, deprecations, security_fixes_gained, major_version_bumps, runtime_requirement_changes, notes_coverage). |
up_to_date[] | Packages already on the latest version, with any known vulnerabilities that still affect it. |
not_tracked[] | Packages outside our coverage (the most-used packages of each ecosystem). Known vulnerabilities are still checked for them. |
not_assessed[] | Lines without one exact version (e.g. flask>=2.0), listed but not checked. |
Example request
curl -X POST https://api.wiscolabs.dev/v1/package_changes/upgrade_check \
-H 'Content-Type: application/json' \
-d '{"requirements": "requests==2.31.0\nurllib3==1.26.18", "limit": "2"}'Example response (shortened)
{
"ecosystem": "PyPI",
"summary": {
"packages_checked": 2,
"outdated": 2,
"up_to_date": 0,
"not_tracked": 0,
"not_assessed": 0,
"high_risk": 1,
"medium_risk": 1,
"low_risk": 0,
"total_breaking": 0,
"total_removed": 32,
"total_deprecated": 15,
"total_security_fixes_gained": 23
},
"security": {
"vulnerable_packages": 2,
"known_vulnerabilities": 3,
"vulnerable_packages_by_worst_severity": {
"critical": 0,
"high": 0,
"medium": 2,
"low": 0,
"unknown": 0
},
"malicious_packages": [],
"data": "OSV (osv.dev) and the GitHub Advisory Database, CC-BY 4.0"
},
"recommendation": {
"remove_now": [],
"fix_now": [
"urllib3",
"requests"
],
"no_fix_available": [],
"upgrade_first": [],
"safe_to_upgrade": [],
"review_then_upgrade": [],
"needs_care": []
},
"packages": [
{
"package": "urllib3",
"current": "1.26.18",
"latest": "2.8.0",
"suggested_version": "2.5.0",
"vulnerability_count": 2,
"max_severity": "medium",
"known_vulnerabilities": [
{
"id": "GHSA-34jh-p97f-mpxf",
"aliases": [
"CVE-2024-37891"
],
"summary": "urllib3's Proxy-Authorization request header isn't stripped during cross-origin redirects",
"severity": "medium",
"fixed_in": [
"1.26.19"
],
"malicious_package": false,
"url": "https://osv.dev/vulnerability/GHSA-34jh-p97f-mpxf"
},
{
"id": "GHSA-pq67-6m6q-mj2v",
"aliases": [
"CVE-2025-50181"
],
"summary": "urllib3 redirects are not disabled when retries are disabled on PoolManager instantiation",
"severity": "medium",
"fixed_in": [
"2.5.0"
],
"malicious_package": false,
"url": "https://osv.dev/vulnerability/GHSA-pq67-6m6q-mj2v"
}
],
"upgrade_risk": "high",
"summary": {
"releases_between": 24,
"releases_with_notes": 24,
"breaking": 0,
"removed": 27,
"deprecated": 14,
"security_fixes_gained": 18,
"other_changes": 156
},
"breaking_changes": [],
"removals": [
{
"version": "2.0.0",
"text": "Removed support for Python 2.7, 3.5, and 3.6 (#883, #2336).",
"source": "changelog",
"id": "51ebe54cd8a78119"
},
{
"version": "2.0.0",
"text": "Removed fallback on certificate `commonName` in `match_hostname()` function. This behavior was deprecated in May 2000 in RFC 2818. Instead only `subjectAltName` is used to verify the hostname by default. To enable verifying the hostname against `commonName` use `SSLContext.hostname_checks_common_name = True` (#2113).",
"source": "changelog",
"id": "876f65d187a11987"
}
],
"deprecations": [
{
"version": "2.0.0",
"text": "Deprecated `HTTPResponse.getheaders()` and `HTTPResponse.getheader()` which will be removed in urllib3 v2.1.0. Instead use `HTTPResponse.headers` and `HTTPResponse.headers.get(name, default)`. (#1543, #2814).",
"source": "changelog",
"id": "b25e165299973395"
},
{
"version": "2.0.0",
"text": "Deprecated `urllib3.contrib.pyopenssl` module which will be removed in urllib3 v2.1.0 (#2691).",
"source": "changelog",
"id": "f287b578471e6771"
}
],
"security_fixes_gained": [
{
"version": "2.5.0",
"text": "Fixed a security issue where restricting the maximum number of followed redirects at the `urllib3.PoolManager` level via the `retries` parameter did not work.",
"source": "changelog",
"id": "a611198286ef9ab9"
},
{
"version": "2.6.0",
"text": "Fixed a security issue where streaming API could improperly handle highly compressed HTTP content (\"decompression bombs\") leading to excessive resource consumption even when a small amount of data was requested. Reading small chunks of compressed data is safer and much more efficient now. (GHSA-2xpw-w6gg-jr37)",
"source": "changelog",
"id": "489b0b74658a81d6"
}
],
"major_version_bumps": [
{
"version": "2.0.0",
"released": "2023-04-26"
}
],
"runtime_requirement_changes": [
{
"version": "1.26.19",
"requires": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
},
{
"version": "2.0.0",
"requires": ">=3.7"
}
],
"notes_coverage": "complete",
"suggested_upgrade_risk": "high"
}
],
"up_to_date": [],
"not_tracked": [],
"not_assessed": []
}What changed between two versions of a package
What breaks, gets removed, is deprecated, or gains a security fix when upgrading one package between two versions, with an overall upgrade risk.
- MCP tool
what_changed- HTTP
POST /v1/package_changes/what_changedwith a JSON body, orGET /v1/package_changes/what_changed?…with query parameters
Parameters
| Parameter | What it is | |
|---|---|---|
name | required | Package name, e.g. flask (PyPI) or express or @babel/core (npm) |
from | required | The version you're on now |
to | required | The version you want to upgrade to (use latest_version from package_info if unsure) |
ecosystem | optional, default PyPI | PyPI or npm |
limit | optional, default 15 | Max items shown per category |
What comes back
| Field | Meaning |
|---|---|
upgrade_risk | high: the project lists breaking changes, or the upgrade crosses a major version. medium: removals, deprecations, new runtime requirements, or yanked versions in between. low: none of those. |
versioning | semantic or calendar. Major-version jumps only count as risky for semantic versioning. |
summary | Counts for every release between from and to: releases with notes, breaking changes, removals, deprecations, security fixes gained, and other changes. |
breaking_changes[] | Each change the project's own release notes call breaking: version, text (quoted from the notes), source (changelog or github_releases), and a stable id. |
removals[] | Removed features, functions and supported versions, in the same shape as breaking_changes. |
deprecations[] | Deprecated features that will be removed later, in the same shape. |
security_fixes_gained[] | Security fixes described in the release notes that the upgrade brings. |
major_version_bumps[] | Each major version crossed, with its release date. |
runtime_requirement_changes[] | Versions that changed the required language runtime (e.g. Python >=3.9, Node >=18). |
yanked_versions_in_range[] | Versions in between that were withdrawn by the maintainers. |
known_vulnerabilities | How many known vulnerabilities affect the from version, which ones the upgrade fixes, and which are still present in the to version. |
notes_coverage | complete: every release in between has release notes. partial: some do. none: only version signals (major bumps, runtime requirements) are available. |
Example request
curl -X POST https://api.wiscolabs.dev/v1/package_changes/what_changed \
-H 'Content-Type: application/json' \
-d '{"name": "flask", "from": "2.3.3", "to": "3.1.3", "limit": "3"}'Example response (shortened)
{
"ecosystem": "PyPI",
"package": "flask",
"repo": "pallets/flask",
"from": "2.3.3",
"to": "3.1.3",
"upgrade_risk": "high",
"versioning": "semantic",
"summary": {
"releases_between": 8,
"releases_with_notes": 8,
"breaking": 0,
"removed": 2,
"deprecated": 1,
"security_fixes_gained": 0,
"other_changes": 28
},
"breaking_changes": [],
"removals": [
{
"version": "3.0.0",
"text": "Remove previously deprecated code. #5223 (edited)",
"source": "changelog",
"id": "3cf6eee9d0aab566"
},
{
"version": "3.1.0",
"text": "Drop support for Python 3.8. #5623 (edited)",
"source": "changelog",
"id": "06741e103c17a6da"
}
],
"deprecations": [
{
"version": "3.0.0",
"text": "Deprecate the `__version__` attribute. Use feature detection, or `importlib.metadata.version(\"flask\")`, instead. #5230 (edited)",
"source": "changelog",
"id": "937b5578ec9cdedf"
}
],
"security_fixes_gained": [],
"major_version_bumps": [
{
"version": "3.0.0",
"released": "2023-09-30"
}
],
"runtime_requirement_changes": [
{
"version": "3.1.0",
"requires": ">=3.9"
}
],
"yanked_versions_in_range": [],
"known_vulnerabilities": {
"in_from_version": 0,
"fixed_by_upgrade": [],
"still_present_after_upgrade": []
},
"notes_coverage": "complete"
}Will my npm packages accept this upgrade? (peer dependencies)
Before upgrading a core npm package (react, typescript, eslint, vite...), checks every dependency in the project that declares it as a peer dependency: does the installed version accept the new version, and if not, the smallest release that does, with that upgrade's risk. Also checks the new version's own peer requirements against what's installed. Answers the questions behind npm's ERESOLVE errors without running npm.
- MCP tool
peer_check- HTTP
POST /v1/package_changes/peer_checkwith a JSON body, orGET /v1/package_changes/peer_check?…with query parameters
Parameters
| Parameter | What it is | |
|---|---|---|
upgrade | required | The package and version to upgrade to: react@19 (newest stable 19.x), [email protected] (exact), or react (latest stable) |
requirements | required | The project's dependencies: the contents of package-lock.json (best), package.json, or one 'name@version' per line |
limit | optional, default 5 | Max breaking changes shown for each suggested fix |
What comes back
| Field | Meaning |
|---|---|
upgrade | The package being upgraded: from (installed version, if any), to (the resolved version), and upgrade_risk of that upgrade itself. |
verdict | compatible: every dependency that declares it as a peer accepts the new version. blocked: at least one doesn't (npm would fail with ERESOLVE). check_by_hand: a peer range couldn't be read. |
summary | Counts: dependencies checked, compatible, blocking, warnings, unreadable. |
blocking[] | Each installed package that rejects the upgrade: installed version, its peer_range, fix_version (the smallest release that accepts it, or drops the requirement), fix_peer_range, fix_upgrade_risk and fix_breaking_changes. Also lists installed packages the new version itself requires a different version of. |
warnings[] | Optional peers that won't match, and required peers of the new version that aren't installed. |
unreadable[] | Peer ranges we couldn't interpret (e.g. workspace: or git URLs). |
compatible[] | Installed packages whose peer range already accepts the new version. |
target_peers[] | The new version's own peer dependencies, with the installed version of each and whether it satisfies the range. |
not_tracked[] | Dependencies outside our coverage, so their peer ranges weren't checked. |
Example request
curl -X POST https://api.wiscolabs.dev/v1/package_changes/peer_check \
-H 'Content-Type: application/json' \
-d '{"upgrade": "react@19", "requirements": "[email protected]\[email protected]\[email protected]"}'Example response (shortened)
{
"ecosystem": "npm",
"upgrade": {
"package": "react",
"from": "18.3.1",
"to": "19.2.0",
"upgrade_risk": "high"
},
"verdict": "blocked",
"summary": {
"dependencies_checked": 2,
"compatible": 0,
"blocking": 2,
"warnings": 0,
"unreadable": 0
},
"blocking": [
{
"package": "react-dom",
"installed": "18.3.1",
"peer_range": "^18.3.1",
"fix_version": "19.2.0",
"fix_peer_range": "^19.2.0",
"fix_upgrade_risk": "high",
"fix_breaking_changes": [
"..."
]
},
{
"package": "react-redux",
"installed": "8.1.3",
"peer_range": "^16.8 || ^17.0 || ^18.0",
"fix_version": "9.1.0",
"fix_peer_range": "^18.0 || ^19",
"fix_upgrade_risk": "high",
"fix_breaking_changes": [
"..."
]
}
],
"warnings": [],
"unreadable": [],
"compatible": [],
"target_peers": [],
"not_tracked": []
}Will pip find prebuilt wheels for this platform?
For each pinned Python package, whether a prebuilt wheel exists for a target Python version and platform (Windows ARM, Alpine/musl, Linux ARM, macOS, a new Python version). Without one, pip builds from source, which usually fails without a compiler for packages with C extensions. For each that's missing, gives the nearest newer version that has a wheel, with that upgrade's risk.
- MCP tool
wheel_check- HTTP
POST /v1/package_changes/wheel_checkwith a JSON body, orGET /v1/package_changes/wheel_check?…with query parameters
Parameters
| Parameter | What it is | |
|---|---|---|
requirements | required | requirements.txt contents or pip freeze output, one 'package==version' per line |
python | required | Target Python version, e.g. 3.13 |
platform | required | Target platform: manylinux_x86_64 or manylinux_aarch64 (glibc Linux, e.g. Debian/Ubuntu; assumes glibc 2.36, or give an exact tag like manylinux_2_28_x86_64), musllinux_x86_64 or musllinux_aarch64 (Alpine), win_amd64, win_arm64, win32, macosx_arm64, macosx_x86_64 |
limit | optional, default 5 | Max breaking changes shown for each suggested upgrade |
What comes back
| Field | Meaning |
|---|---|
target | The Python version and platform checked, and how many wheel tags count as compatible. |
summary | Counts: packages checked, with a wheel, source only (pip will build), not tracked. |
needs_build[] | Packages whose pinned version has no compatible wheel: likely_needs_compiler (the project ships binary wheels for other platforms, so it has C extensions), nearest_version_with_wheel and its upgrade_risk and breaking_changes (or a note that no release has one yet), and requires_python when the pinned version doesn't support the target Python at all. |
wheel_available[] | Packages with a compatible wheel, and which kind: pure (any platform) or binary (built for this platform). |
not_tracked[] | Packages outside our coverage (the 3,000 most-used PyPI packages). |
not_assessed[] | Lines without one exact version. |
Example request
curl -X POST https://api.wiscolabs.dev/v1/package_changes/wheel_check \
-H 'Content-Type: application/json' \
-d '{"python": "3.13", "platform": "win_arm64", "requirements": "numpy==1.26.4\nrequests==2.31.0"}'Example response (shortened)
{
"ecosystem": "PyPI",
"target": {
"python": "3.13",
"platform": "win_arm64",
"compatible_tags": 45
},
"summary": {
"checked": 2,
"wheel_available": 1,
"needs_build": 1,
"not_tracked": 0
},
"needs_build": [
{
"package": "numpy",
"version": "1.26.4",
"has_source_distribution": true,
"likely_needs_compiler": true,
"nearest_version_with_wheel": "2.3.0",
"upgrade_risk": "high",
"breaking_changes": [
"..."
]
}
],
"wheel_available": [
{
"package": "requests",
"version": "2.31.0",
"wheel": "pure"
}
],
"not_tracked": []
}Known vulnerabilities in a package version
Is this exact version of a package affected by known security advisories? Returns each vulnerability (with CVE ids and severity), the versions that fix it, and the smallest safe upgrade.
- MCP tool
vulnerabilities- HTTP
POST /v1/osv/vulnerabilitieswith a JSON body, orGET /v1/osv/vulnerabilities?…with query parameters
Parameters
| Parameter | What it is | |
|---|---|---|
name | required | Package name |
version | required | The exact version you're using |
ecosystem | optional, default PyPI | Package ecosystem (OSV naming) |
What comes back
| Field | Meaning |
|---|---|
affected | true if this exact version is affected by at least one known vulnerability. |
max_severity | The worst severity among them: critical, high, medium, low, or unknown. |
malicious_package | true if the package is reported as malicious: remove it rather than upgrade. |
vulnerabilities[] | Each advisory: id (GHSA, PYSEC, MAL...), aliases (CVE ids), summary, severity, cvss_score, the versions that fix it (fixed_in), the publish date, and a link. |
minimum_safe_version | The smallest newer version with no known vulnerabilities. |
minimum_safe_same_major | The smallest safe version without changing major version: often a low-risk patch. null if none exists. |
latest_version_affected | true if even the newest version is still affected. |
note | Present when the answer needs a caveat, e.g. for packages whose release history we don't track. |
source | Where the vulnerability data comes from, with its license. |
Example request
curl -X POST https://api.wiscolabs.dev/v1/osv/vulnerabilities \
-H 'Content-Type: application/json' \
-d '{"name": "urllib3", "version": "1.26.18"}'Example response (shortened)
{
"ecosystem": "PyPI",
"package": "urllib3",
"version": "1.26.18",
"affected": true,
"vulnerability_count": 2,
"max_severity": "medium",
"malicious_package": false,
"vulnerabilities": [
{
"id": "GHSA-34jh-p97f-mpxf",
"aliases": [
"CVE-2024-37891"
],
"summary": "urllib3's Proxy-Authorization request header isn't stripped during cross-origin redirects",
"severity": "medium",
"cvss_score": 4.4,
"malicious_package": false,
"fixed_in": [
"1.26.19"
],
"published": "2024-06-17",
"url": "https://osv.dev/vulnerability/GHSA-34jh-p97f-mpxf"
},
{
"id": "GHSA-pq67-6m6q-mj2v",
"aliases": [
"CVE-2025-50181"
],
"summary": "urllib3 redirects are not disabled when retries are disabled on PoolManager instantiation",
"severity": "medium",
"cvss_score": 5.3,
"malicious_package": false,
"fixed_in": [
"2.5.0"
],
"published": "2025-06-18",
"url": "https://osv.dev/vulnerability/GHSA-pq67-6m6q-mj2v"
}
],
"minimum_safe_version": "2.5.0",
"minimum_safe_same_major": null,
"latest_version_affected": false,
"source": "OSV (osv.dev) and the GitHub Advisory Database, CC-BY 4.0"
}Basic facts about a package
Latest and latest stable version, repository, release count, and where its release notes come from.
- MCP tool
package_info- HTTP
POST /v1/package_changes/package_infowith a JSON body, orGET /v1/package_changes/package_info?…with query parameters
Parameters
| Parameter | What it is | |
|---|---|---|
name | required | Package name |
ecosystem | optional, default PyPI | PyPI or npm |
What comes back
| Field | Meaning |
|---|---|
latest_version | The newest published version, including pre-releases. |
latest_stable_version | The newest version that isn't a pre-release or yanked. Use this as the upgrade target. |
repo | The source repository (owner/name) where release notes are read from. |
release_count | How many versions have been published. |
versions_with_notes | How many of those have release notes we could read. |
notes_sources | Where the notes come from: changelog (a file in the repository) and/or github_releases. |
first_release | Dates of the first and latest releases (first_release, latest_release). |
Example request
curl -X POST https://api.wiscolabs.dev/v1/package_changes/package_info \
-H 'Content-Type: application/json' \
-d '{"name": "flask"}'Example response (shortened)
{
"ecosystem": "PyPI",
"package": "flask",
"summary": "A simple framework for building complex web applications.",
"homepage": null,
"repo": "pallets/flask",
"latest_version": "3.1.3",
"latest_stable_version": "3.1.3",
"release_count": 64,
"versions_with_notes": 62,
"notes_sources": [
"changelog"
],
"first_release": "2010-04-16",
"latest_release": "2026-02-19"
}For machines
Everything on this page, in formats agents and code generators read directly:
https://api.wiscolabs.dev/llms.txt what's here, in plain text https://api.wiscolabs.dev/v1/catalog every dataset, tool and parameter https://api.wiscolabs.dev/openapi.json OpenAPI schema https://api.wiscolabs.dev/docs interactive (Swagger) explorer
Data and credit
- Vulnerabilities: OSV and the GitHub Advisory Database, licensed CC-BY 4.0; malicious-package reports from OpenSSF Malicious Packages.
- Releases: PyPI and the npm registry.
- Release notes: each project's changelog and release notes, quoted for reference; text belongs to its authors. The
sourcefield on every item says where it came from.
Changes are sorted into categories by software, which makes mistakes. See the terms.