# Safe Pkgs

> Package safety checks for AI agents before install via MCP

- **Type:** MCP server
- **Install:** `agentstack add mcp-math280h-safe-pkgs`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [math280h](https://agentstack.voostack.com/s/math280h)
- **Installs:** 0
- **Category:** [Developer Tools](https://agentstack.voostack.com/c/developer-tools)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [math280h](https://github.com/math280h)
- **Source:** https://github.com/math280h/safe-pkgs
- **Website:** https://math280h.github.io/safe-pkgs/

## Install

```sh
agentstack add mcp-math280h-safe-pkgs
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# safe-pkgs

  Package safety checks for AI agents before install.
  Rust MCP server + CLI with allow/deny decisions, risk scoring, and audit logs.

  Documentation &middot;
  GitHub Action

  
  
  
  

  
    Without safe-pkgs
    With safe-pkgs
  
  
    
      
    
    
      
    
  

## What It Is

`safe-pkgs` checks package risk before dependency actions and returns one machine-enforceable decision.

Decision payload includes:
- `allow`: `true` or `false`
- `risk`: `low | medium | high | critical`
- `reasons`: human-readable findings
- `evidence`: structured findings (`kind`, stable `id`, `severity`, `message`, `facts`)
- `metadata`: package context (latest, publish date, downloads, advisories)
- `fingerprints`: deterministic hashes (`config`, `policy`)

## Install + Run in 60 Seconds

Install once:

```bash
cargo install --path . --locked
```

Run MCP server:

```bash
safe-pkgs serve
```

Run a one-off audit:

```bash
safe-pkgs audit /path/to/project-or-lockfile
safe-pkgs audit /path/to/requirements.txt --registry pypi
```

Preview the decision without enforcing it (what-if):

```bash
safe-pkgs simulate /path/to/project-or-lockfile
```

Windows MCP hosts (Claude Desktop, etc.) should use:

```powershell
safe-pkgs-mcp.exe
```

## No Subscription Required

`safe-pkgs` does not require a paid plan, hosted account, or API key for built-in checks.

- Runs locally as a Rust binary (MCP server or CLI).
- Uses public package/advisory endpoints by default:
  - npm registry + npm downloads API + npms popularity index
  - crates.io API
  - PyPI JSON API + pypistats + top-pypi index
  - OSV advisory API
- Stores cache and audit logs locally on your machine.

## Registry and Check Support

Supported registries:
- `npm` (default)
- `cargo` (crates.io)
- `pypi` (Python packages)

View support map:
- Command: `safe-pkgs support-map`

### Private / Authenticated Registries

Point a registry client at a private mirror and authenticate with a bearer token via environment variables.

Base URL overrides (each defaults to the public endpoint when unset):

- npm:
  - `SAFE_PKGS_NPM_REGISTRY_API_BASE_URL` (package metadata; default `https://registry.npmjs.org`)
  - `SAFE_PKGS_NPM_DOWNLOADS_API_BASE_URL` (downloads; default `https://api.npmjs.org`)
  - `SAFE_PKGS_NPM_POPULAR_INDEX_API_BASE_URL` (popularity index; default `https://api.npms.io`)
- pypi:
  - `SAFE_PKGS_PYPI_PACKAGE_API_BASE_URL` (package metadata; default `https://pypi.org/pypi`)
  - `SAFE_PKGS_PYPI_DOWNLOADS_API_BASE_URL` (downloads; default `https://pypistats.org/api/packages`)
  - `SAFE_PKGS_PYPI_POPULAR_INDEX_URL` (popularity index; default top-pypi-packages JSON)
- cargo: no base-URL override env var yet; all calls use the crates.io API base (`https://crates.io/api/v1`).

Bearer-token auth — when set, requests send `Authorization: Bearer `:

- `SAFE_PKGS_NPM_REGISTRY_TOKEN`
- `SAFE_PKGS_PYPI_REGISTRY_TOKEN`
- `SAFE_PKGS_CARGO_REGISTRY_TOKEN`

The token is sent ONLY to the registry metadata/API host, to avoid leaking it to non-registry hosts:

- npm and pypi: the token is sent only to the package-metadata host. The downloads and popularity hosts (e.g. `api.npmjs.org`, `api.npms.io`, `pypistats.org`) receive no token, since they are separate third-party services.
- cargo: every request targets the same crates.io API base, so all of them (metadata, downloads, popular-crate listing) carry the token.

Empty or whitespace-only token values are treated as unset (no auth header is sent).

## Configuration

Global file:
- `~/.config/safe-pkgs/config.toml`

Project override:
- `.safe-pkgs.toml` (merged on top of global)

Minimal example:

```toml
min_version_age_days = 7
min_weekly_downloads = 50
max_risk = "medium"

[cache]
ttl_minutes = 30

[allowlist]
packages = ["my-internal-pkg"]

[denylist]
packages = ["event-stream@3.3.6"]
```

Full configuration schema:
- `docs/configuration-spec.md`

## MCP Config Example

macOS/Linux:

```json
{
  "servers": {
    "safe-pkgs": {
      "type": "stdio",
      "command": "/path/to/safe-pkgs",
      "args": ["serve"]
    }
  },
  "inputs": []
}
```

Windows (no console window):

```json
{
  "servers": {
    "safe-pkgs": {
      "type": "stdio",
      "command": "safe-pkgs-mcp.exe"
    }
  },
  "inputs": []
}
```

## Decision Output Example

```json
{
  "allow": true,
  "risk": "low",
  "reasons": [
    "lodash@3.10.1 is 1 major version behind latest (4.17.21)"
  ],
  "evidence": [
    {
      "kind": "check",
      "id": "staleness.behind_latest",
      "severity": "low",
      "message": "lodash@3.10.1 is 1 major version behind latest (4.17.21)",
      "facts": {
        "package_name": "lodash",
        "resolved_version": "3.10.1",
        "latest_version": "4.17.21",
        "major_gap": 1
      }
    }
  ],
  "fingerprints": {
    "config": "c7d9f5b8b9a8f2a9f6b1f42f0e8e8c8a63f2b2ef8fdde1f3cd9ea4f5a2c08a0b",
    "policy": "fca103ee4fd5b86595a6a6e933f8a5f87db0ce087f80744dc1ea9cdbf58f7a6f"
  },
  "metadata": {
    "latest": "4.17.21",
    "requested": "3.10.1",
    "published": "2015-08-31T00:00:00Z",
    "weekly_downloads": 45000000
  }
}
```

## Lockfile Audit Output Example (`dependency_ancestry`)

Input lockfile (`package-lock.json`) used for this example:

```json
{
  "name": "demo",
  "lockfileVersion": 2,
  "dependencies": {
    "react": {
      "version": "18.2.0",
      "dependencies": {
        "loose-envify": {
          "version": "1.4.0"
        }
      }
    }
  }
}
```

Audit output:

```json
{
  "allow": true,
  "risk": "low",
  "total": 2,
  "denied": 0,
  "packages": [
    {
      "name": "react",
      "requested": "18.2.0",
      "allow": true,
      "risk": "low",
      "reasons": [],
      "evidence": []
    },
    {
      "name": "loose-envify",
      "requested": "1.4.0",
      "allow": true,
      "risk": "low",
      "reasons": [],
      "evidence": [],
      "dependency_ancestry": {
        "paths": [
          { "ancestors": ["react"] }
        ]
      }
    }
  ],
  "fingerprints": {
    "config": "",
    "policy": ""
  }
}
```

`paths[].ancestors` lists only ancestors (root to immediate parent), excluding the package itself.
For direct dependencies, `dependency_ancestry` is omitted.

`evidence.id` is stable and machine-oriented:
- Built-in checks: `.` (example: `staleness.behind_latest`)
- Custom rules: `custom_rule.` (example: `custom_rule.low-downloads`)
- Policy/runtime items: explicit IDs (example: `denylist.package`, `risk.medium_pair_escalation`)

## Trust and Security Posture

- Fail-closed behavior: check/runtime failures are surfaced and do not silently allow installs.
- Local audit trail: append-only audit log for decision review.
- Deterministic policy context: responses include `policy_snapshot_version`, config and policy fingerprints, and enabled check set.
- Local cache: SQLite cache keyed by policy fingerprint + package tuple with TTL expiry.

## Disclaimer

`safe-pkgs` works as an MCP tool that AI agents can call before installing packages. However, **we cannot guarantee that an AI agent will always choose to call this tool** — agentic models that autonomously select tools may proceed with package installation without invoking `check_package` or `check_lockfile` first, depending on the model, prompt context, and system prompt configuration.

If your AI agent skipped `safe-pkgs` when it should have called it, please [open an issue](https://github.com/math280h/safe-pkgs/issues/new?template=ai_missed_tool.md) with the prompt and response so we can improve tool descriptions and usage guidance.

## Roadmap

Prioritized planned work:

### Now

- [x] Dependency confusion defenses for internal/private package names
- [x] Policy simulation mode (`what-if`) without enforcement
- [x] Metrics/log schema for latency, cache hit ratio, and registry error rates
- [ ] Support remote audit storage backends
- [x] Support remote config sources (GitHub repo, HTTP endpoint, etc.)
- [x] Support for private registries

### Next

- [ ] Policy waivers with expiry
- [ ] Package provenance checks (where ecosystem metadata supports it)
- [ ] Publisher trust signals (account age, maintainer churn, ownership changes)
- [x] Performance/scale improvements for large lockfiles:
  - [x] Configurable bounded concurrency (default: 5 parallel evaluations)
  - [x] Inter-batch delay to prevent API rate limiting (default: 100ms)
  - [x] Cache hit tracing for visibility
- [ ] Additional performance improvements (request coalescing for duplicate packages)

### Later

- [ ] NVD advisory enrichment
- [ ] Optional Snyk advisory provider
- [ ] Socket.dev integration
- [ ] GitHub Actions integration for CI auditing
- [ ] Registry-driven MCP schema and docs generation (single source of truth)
- [ ] HTTP Streamable MCP server option
- [ ] More validated editor config examples
- [ ] Git hook integration for pre-commit checks

## Development

```bash
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test
```

## Coverage

Install:

```bash
rustup component add llvm-tools-preview
cargo install cargo-llvm-cov
```

Summary:

```bash
cargo llvm-cov --workspace --all-features --summary-only
```

HTML report:

```bash
cargo llvm-cov --workspace --all-features --html
```

Report path:
- `target/llvm-cov/html/index.html`

## Local Docs

```bash
pip install zensical
zensical serve
```

> **Note:** This project now uses [Zensical](https://github.com/squidfunk/zensical) instead of MkDocs.
> MkDocs 2.0 drops the plugin system and breaks backward compatibility with no migration path ([announcement](https://squidfunk.github.io/mkdocs-material/blog/2026/02/18/mkdocs-2.0/)).

## Deterministic Evaluation Clock (Optional)

Set `SAFE_PKGS_EVALUATION_TIME` to an RFC3339 timestamp to force a fixed policy evaluation time (useful for replay/debug runs):

```bash
SAFE_PKGS_EVALUATION_TIME=2026-01-01T00:00:00Z safe-pkgs audit /path/to/project
```

```powershell
$env:SAFE_PKGS_EVALUATION_TIME = "2026-01-01T00:00:00Z"
safe-pkgs audit C:\path\to\project
```

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [math280h](https://github.com/math280h)
- **Source:** [math280h/safe-pkgs](https://github.com/math280h/safe-pkgs)
- **License:** MIT
- **Homepage:** https://math280h.github.io/safe-pkgs/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-math280h-safe-pkgs
- Seller: https://agentstack.voostack.com/s/math280h
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
