Your AI agent checks a package before it installs it.
xyz MCP gives Claude Code, Cursor, Codex, Gemini CLI, Devin Desktop and VS Code the same verdict as the install proxy. The agent asks before it adds a dependency, and an install hook denies a blocked install before the shell runs it.
- 13tools
- 4ecosystems
- 2ways to run it
Claude Code
Cursor
Codex
Gemini CLI
Devin Desktop
VS Code
- known malicious release · use axios@1.20.0
- faker@6.6.6 blocked · 3 alerts · express allowed
- 4 advisories · move to 9.0.0
- 1 moderate advisory · 2.33.1 clears it
- pytorch_model.bin in all-MiniLM-L6-v2 · safe to load
- 6 packages · axios blocked · jsonwebtoken alert
each answer is the live tool's reply on 20 October 2026, shortened
Six agents, one setup each.
One command points each agent at the CyberXYZ tools, and --rules adds the dependency policy to the agent's own rules file. Every file is merged, never overwritten, and the previous version is kept as .bak.
-
Claude Code
--client claude-code- server
- ~/.claude.json · .mcp.json
- rules
- CLAUDE.md
the install hook:
--hooks -
Cursor
--client cursor- server
- ~/.cursor/mcp.json · .cursor/mcp.json
- rules
- .cursor/rules/cyberxyz.mdc
-
Codex
--client codex- server
- ~/.codex/config.toml
- rules
- AGENTS.md
-
Gemini CLI
--client gemini- server
- ~/.gemini/settings.json · .gemini/settings.json
- rules
- GEMINI.md
-
Devin Desktop
--client windsurf- server
- ~/.codeium/windsurf/mcp_config.json
- rules
- .windsurf/rules/cyberxyz.md
formerly Windsurf; it kept the
~/.codeiumpaths -
VS Code
--client vscode- server
- .vscode/mcp.json, or
code --add-mcp - rules
- .github/copilot-instructions.md
Copilot agent mode
Three commands, two minutes.
Copy each command into a terminal. The installer merges its entry into the agent's own config, keeps the old file as .bak, and with --dry-run shows the diff before anything changes.
-
Install xyz Scanner
From PyPI. The MCP server needs Python 3.10 or newer.
$pipx install cyberxyz-scanner -
Sign in
Opens your browser. On a server or in CI, set
XYZ_API_KEYinstead.$xyz login -
Connect your agent
$xyz mcp install --client claude-code --hooksAdds the server to
~/.claude.jsonand the install hook to~/.claude/settings.json.
the installer's real output (xyz 1.4.75 on a Mac, home shown as ~); run it again and each line says "already configured"
Four checkpoints, one verdict.
The agent can ask first. If it doesn't, the hook reads the command. A lockfile install goes to the proxy, which checks every download, and CyberXYZ Gate reads the lockfile in CI. A bad package stops at the first checkpoint it reaches.
check_package axios 1.14.1npm install axios@1.14.1npm cigit pushthe behaviour each product documents; the lockfile in rows 3 and 4 pins axios 1.14.1, and any one checkpoint also works alone
A blocked install never reaches the shell.
Before Claude Code runs a command, the hook reads it the way the shell does and checks every package in it in one call. A blocked or quarantined package is denied with the reason and a safe version, and the agent moves on to that version. The hook never approves a command on its own.
Add an HTTP client and load the product list.
I'll add axios for the HTTP calls.
npm axios 1.20.0ALLOWOK to install axios@1.20.0.1.14.1 is a known malicious release. Installing 1.20.0, the version CyberXYZ suggests, instead.
drawn as a generic agent chat; the denial is the hook's output for this command and the check is check_package's answer, both on 20 October 2026. The last step is the agent's own prompt: you approve it.
npm, pnpm, yarn and bun installs that name a package
pip and uv, including the exact pins inside
-rfiles, plus poetry add and pipx installgo get and go install
dotnet add package
The hook lets the command run and prints a warning. Install it with --hook-fail-closed to deny instead, and with --hook-strict to ask you about alerts and unverified packages.
{
"hooks": { "PreToolUse": [{
"matcher": "Bash",
"hooks": [{ "type": "command",
"command": "/opt/homebrew/bin/xyz hook check-install",
"timeout": 30 }]
}]}
}
// lockfile installs
npm ci, a bare npm install and the unpinned lines of a requirements file name no package, so they pass to the proxy, which checks each download as it happens.
What the agent can ask.
Thirteen tools and the cyberxyz-dependency-policy prompt, which tells the agent to check before it installs and never to install a package whose verdict is block or quarantine. Pick a tool to see a real call and its answer. Lookups identify themselves, so asking about a package is never counted as installing it.
- before installing
- fixing
- investigating
- your organization
- AI models
check_package
The install-time verdict for one version: the behaviour signals, every advisory with CVSS, EPSS, KEV and the fixed version, and a version to use instead.
check_package(ecosystem="npm", name="axios", version="1.14.1"){
"decision": "block",
"malicious": true,
"signals": [
{
"label": "known malicious release",
"severity": "CRITICAL",
"description": "Confirmed supply chain attack: RAT Dropper via Compromised Maintainer."
},
{
"label": "abnormal version jump",
"severity": "MEDIUM",
"description": "axios jumped from 1.10.0 to 1.14.1 (skipped 4 minor versions overnight)"
},
// 13 "known vulnerability" signals trimmed
],
"advisory_count": 35,
"suggested_version": "1.20.0",
"recommendation": "Do not install axios@1.14.1 (block: known malicious release; abnormal version jump; 13 known vulnerabilities). Install axios@1.20.0 instead, after checking it with check_package."
}check_packages
The same verdict for up to 500 packages of one ecosystem: counts and an overall verdict first, detail only for what is flagged.
check_packages(ecosystem="npm", packages=["express@4.21.2", "faker@6.6.6", "lodash@4.17.20", "jsonwebtoken@8.5.1", "react@19.0.0"]){
"summary": {
"checked": 5,
"block": 1,
"quarantine": 0,
"alert": 3,
"allow": 1
},
"verdict": "do_not_install",
"flagged": [
{
"package": "faker",
"version": "6.6.6",
"decision": "block",
"reason": "known malicious release; 1 known vulnerability",
"safe_version": "5.5.3"
},
{
"package": "jsonwebtoken",
"version": "8.5.1",
"decision": "alert",
"reason": "2 known vulnerabilities",
"safe_version": "9.0.0"
},
// lodash 4.17.20 and react 19.0.0: alert, trimmed
],
"allowed": ["express@4.21.2"]
}package_dependencies
What adding a package pulls in: its direct dependencies, how far it reaches, and the findings inside that closure. It reads the latest release.
package_dependencies(ecosystem="npm", name="express"){
"graph_version": "5.2.1",
"direct_dependency_count": 44,
"reachable_total": 48,
"max_depth": 4,
"findings": [
{
"package_name": "proxy-addr",
"version": "2.0.7",
"depth": 1,
"worst_severity": "CRITICAL",
"path": ["express", "proxy-addr"]
},
{
"package_name": "qs",
"version": "6.15.3",
"depth": 1,
"worst_severity": "MEDIUM",
"path": ["express", "qs"]
}
],
// the 44 direct dependencies trimmed
}scan_project
Reads the project's manifests and lockfiles, checks every package, and says what to change: the version to move to, or the direct dependency to bump.
scan_project(path="."){
"summary": {
"files": ["package-lock.json", "package.json", "requirements.txt"],
"packages_checked": 6,
"block": 1,
"alert": 1,
"verdict": "do_not_ship"
},
"flagged": [
{
"package": "axios",
"version": "1.14.1",
"decision": "block",
"direct": true,
"action": "upgrade to 1.20.0",
"why": "nearest clean release above 1.14.1, clears 35 advisories (also latest clean)"
},
{
"package": "jsonwebtoken",
"version": "8.5.1",
"decision": "alert",
"direct": true,
"action": "upgrade to 9.0.0",
"why": "nearest clean release above 8.5.1, clears 4 advisories"
}
]
}upgrade_plan
Whether an installed version is affected, and the nearest clean release above it: the smallest safe bump.
upgrade_plan(ecosystem="npm", name="jsonwebtoken", current_version="8.5.1"){
"affected": true,
"advisories": [
{
"id": "GHSA-27h2-hvpr-p74q",
"severity": "HIGH",
"fixed_version": "9.0.0"
},
{
"id": "GHSA-8cf7-32gw-wr33",
"severity": "HIGH",
"fixed_version": "9.0.0"
},
// 2 MEDIUM advisories trimmed
],
"advisory_count": 4,
"upgrade_to": "9.0.0",
"latest_clean": "9.0.3",
"recommendation": "Upgrade jsonwebtoken from 8.5.1 to 9.0.0 (nearest clean release above it)."
}dependency_paths
Why a transitive package is installed: the shortest path from one of your direct dependencies, which is the one to bump.
dependency_paths(ecosystem="npm", roots=["express", "jsonwebtoken"], targets=["qs", "ms"]){
"paths": [
{
"target": "qs",
"root": "express",
"depth": 1,
"path": ["express", "qs"],
"bump": "express"
},
{
"target": "ms",
"root": "jsonwebtoken",
"depth": 1,
"path": ["jsonwebtoken", "ms"],
"bump": "jsonwebtoken"
}
],
"unresolved": [],
"unwalked_roots": []
}get_vulnerability
One advisory in full: the affected package and ranges, the fixed version, CVSS, EPSS and KEV, and the references.
get_vulnerability(id="GHSA-jqcg-44mw-7w3h"){
"aliases": {
"cve": "CVE-2026-90711",
"ghsa": "GHSA-jqcg-44mw-7w3h"
},
"package": "proxy-addr",
"ecosystem": "npm",
"severity": "CRITICAL",
"cwe": {
"id": "CWE-290",
"name": "Authentication bypass by spoofing"
},
"affected_ranges": [">= 1.1.0, < 2.0.8"],
"fixed_version": "2.0.8",
"kev": false,
"scores": {
"cvss": 9.1,
"epss": 0.00332
},
"published": "2026-10-05",
// description and 7 references trimmed
}search_vulnerabilities
Search the advisory corpus (GHSA, OSV, NVD) by package, id or description, worst first.
search_vulnerabilities(ecosystem="npm", package="lodash", limit=5){
"total": 10,
"results": [
{
"cve": "CVE-2019-10744",
"severity": "CRITICAL",
"affected_ranges": "['< 4.17.12']",
"fixed_version": "4.17.12",
"epss": 0.05006
},
{
"cve": "CVE-2026-4800",
"severity": "HIGH",
"affected_ranges": "['>= 4.0.0, <= 4.17.23']",
"fixed_version": "4.18.0",
"epss": 0.02566
},
// 3 more HIGH rows trimmed
]
}blast_radius
Who is exposed when a version goes bad: the packages that depend on it, the range each declares and whether it resolves into the bad version.
blast_radius(ecosystem="npm", name="axios", version="1.14.1"){
"direct_dependent_count": 1348,
"direct_dependent_dev_count": 385,
"reachable_from_required": 1582,
"direct_dependents": [
{
"package_name": "@base44/sdk",
"constraints": ["^1.6.2"],
"resolves_into_affected": true
},
{
"package_name": "@sendgrid/client",
"constraints": ["^1.12.0"],
"resolves_into_affected": true
},
{
"package_name": "@datadog/datadog-ci-plugin-deployment",
"constraints": ["1.13.5"],
"resolves_into_affected": false
},
// more rows trimmed
]
}machines_with_package
Which machines in your organization installed it in the last 90 days, at which versions, and what CyberXYZ decided.
machines_with_package(ecosystem="npm", name="axios"){
"window": "last 90 days of your organization's CyberXYZ install log",
"total_machines": 192,
"other_versions": [
{
"version": "1.20.0",
"machine_count": 180,
"machines": [
{
"machine_name": "ci-runner-17",
"decision": "allow"
},
// 9 rows trimmed
]
},
{
"version": "1.14.1",
"machine_count": 13,
"machines": [
{
"machine_name": "laptop-02",
"decision": "block",
"last_seen": "2026-10-07T18:07:04Z"
},
// 9 rows trimmed
]
}
]
}model_risk
The supply-chain risk of a Hugging Face model's install: the Python packages it pulls, declared and transitive, and what is flagged among them.
model_risk(repo_id="sentence-transformers/all-MiniLM-L6-v2"){
"verdict": "LOW",
"summary": {
"risk_level": "LOW",
"artifact_kind": "model"
},
"attack_vectors": [
{
"vector": "weak_chokepoint",
"score": 4.1,
"evidence_count": 35
},
{
"vector": "pin_excluded_unknown",
"score": 2.2,
"evidence_count": 15
}
],
"package_count": 126,
"flagged_package_count": 0
}model_load_safety
Whether a model's weight files are safe to load, file by file, before from_pretrained or torch.load. Only CLEAN is safe to load.
model_load_safety(repo_id="sentence-transformers/all-MiniLM-L6-v2"){
"verdict": "UNSCANNED",
"safe_to_load": false,
"guidance": "Some weight files are not scanned yet: treat as unknown and ask the user.",
"file_count": 14,
"files": [
{
"path": "tf_model.h5",
"verdict": "UNSCANNED",
"basis": "pickle-format file not yet scanned by CyberXYZ; Hugging Face status: queued"
},
{
"path": "model.safetensors",
"verdict": "CLEAN",
"basis": "safetensors: tensor-only format, nothing executes on load"
},
{
"path": "pytorch_model.bin",
"verdict": "CLEAN",
"basis": "cyberxyz_pickle_scan: no code-executing opcodes"
},
// 11 files trimmed
]
}check_model_file
Look up a downloaded model file by its sha256: which watched model ships it, and that file’s load-safety verdict.
check_model_file(sha256="c3a85f238711653950f6a79ece63eb0ea93d76f6a6284be04019c53733baf256"){
"verdict": "CLEAN",
"safe_to_load": true,
"matches": [
{
"repo_id": "sentence-transformers/all-MiniLM-L6-v2",
"path": "pytorch_model.bin",
"current": true,
"verdict": "CLEAN",
"basis": "cyberxyz_pickle_scan: no code-executing opcodes"
}
]
}real answers on 20 October 2026, trimmed where marked; the project scan ran on a demo project, and machine names in machines_with_package are replaced
Nothing to install, if you prefer.
https://mcp.cyberxyz.io/mcp speaks MCP over HTTP. Without a token it offers the public tier: the public verdict, advisories, package search and model reports. With a dashboard API key or your xyz login token it offers the signed-in tools; scan_project becomes check_dependencies, because a hosted server cannot read your files.
Watch it block a real attack. Then run it on your own installs.
Book 15 minutes. We run real packages through CyberXYZ while you watch, including axios 1.14.1, a hijacked release that dropped remote-control malware in March 2026, and show you exactly what your team would see. Then we connect one laptop or one pipeline for a 30-day proof of value.
What happens next
- 01A 15-minute callReal packages, real verdicts, and your questions.
- 02Connect one laptop or one pipelineOne setup command, or one line in CI. Under an hour.
- 03A 30-day proof of value on your own installsEvery signal, no card.
Not ready for a call?
Check any package, free, and see the verdict we would give it.
Thanks! We'll be in touch.
Check your inbox. We'll reach out within 24 hours.