Skip to main content
Glama
Tripletex

mcp-dependency-version

by Tripletex
README.md
<p align="center">
  <img src="./assets/logo.svg" alt="mcp-dependency-version Logo" width="200" height="200" />
</p>

# MCP Dependency Version

A Model Context Protocol (MCP) server for looking up package versions across
multiple package registries.

## Features

- **Multi-registry support**: npm, Maven Central, PyPI, crates.io, Go proxy,
  JSR, NuGet, Docker Hub, RubyGems, Packagist, pub.dev, Swift PM, GitHub Actions
- **Version lookup**: Get the latest stable (and optionally prerelease) versions
- **Version listing**: List all available versions with metadata
- **Vulnerability scanning**: Check packages against OSV and NVD databases with
  deduplicated, CVSS-scored results
- **License lookup**: Fetch the declared license for one or many packages in a
  single call, with explicit status for unsupported registries or undeclared
  licenses
- **Dependency analysis**: Analyze dependency files and check for updates
- **Docker support**: Look up image tags and analyze
  Dockerfile/docker-compose.yml dependencies

## Security: Use Exact Versions

**Always use exact versions instead of version ranges to prevent supply chain
attacks.**

| Bad (vulnerable) | Good (secure) |
| ---------------- | ------------- |
| `^1.2.3`         | `1.2.3`       |
| `~1.2.3`         | `1.2.3`       |
| `>=1.2.3`        | `1.2.3`       |
| `1.x`            | `1.2.3`       |

Version ranges (like `^1.2.3` or `~1.2.3`) allow automatic updates when new
minor or patch versions are published. If an attacker compromises a package and
publishes a malicious version, your project could automatically pull it in
without your knowledge.

Using exact versions ensures you control exactly which code runs in your
project. When you want to update, explicitly change the version and review the
changes.

### Docker: Use Digest-Pinned References

**Docker tags are NOT immutable.** Unlike package versions in npm/PyPI/etc., a
Docker tag can be moved to point to a completely different image at any time.

| Bad (vulnerable) | Good (secure)               |
| ---------------- | --------------------------- |
| `nginx:1.27.3`   | `nginx@sha256:1948e0c46...` |
| `postgres:16`    | `postgres@sha256:abc123...` |

When you use `nginx:1.27.3`, the image you pull today may be different from the
one you pull tomorrow if the tag is updated. This creates a supply chain attack
vector.

**Use digest-pinned references** (`image@sha256:...`) to ensure you always pull
the exact same image. The `lookup_version` and `list_versions` tools return the
`digest` and `secureReference` fields for Docker images to make this easy.

### GitHub Actions: Use Commit SHA-Pinned References

**GitHub Action tags are NOT immutable.** Tags like `v4` can be force-pushed to
point to a different commit at any time, creating a supply chain attack vector.

| Bad (vulnerable)        | Good (secure)                            |
| ----------------------- | ---------------------------------------- |
| `actions/checkout@v4`   | `actions/checkout@b4ffde65... # v4.2.0`  |
| `actions/setup-node@v4` | `actions/setup-node@1a4442c... # v4.0.1` |

**Use commit SHA-pinned references** (`owner/repo@sha`) to ensure you always use
the exact same action code. The `lookup_version` and `list_versions` tools
return the `digest` (commit SHA) and `secureReference` fields for GitHub Actions
to make this easy.

#### Monorepo actions

Actions that live as subdirectories of a shared repository are referenced with
their path (`uses: org/actions/deploy@...`) and are supported as
`owner/repo/path` package names. Two versioning conventions are recognized:

- **Per-action tags**: the repository tags each action's releases by prefixing
  the version with the action name — `deploy-v1.2.3` or `deploy@v1.2.3` (the
  action name may itself contain dashes). When such tags exist for the action,
  only they are considered, and repo-level tags (`v54`, `v1.2.3`) are ignored
  for that action.
- **Repo-level tags**: repositories like `github/codeql-action` version their
  subpath actions (`init`, `analyze`, ...) with plain repository-wide semver
  tags. When an action has no prefixed tags, these are used as a fallback.

Version lookups, listings, update analysis, and commit SHA pinning all work on
the per-action versions.

## Supported Registries

| Registry       | API Endpoint           | Package Format                   |
| -------------- | ---------------------- | -------------------------------- |
| npm            | registry.npmjs.org     | `package-name`, `@scope/package` |
| maven          | repo1.maven.org/maven2 | `groupId:artifactId`             |
| pypi           | pypi.org               | `package-name`                   |
| cargo          | crates.io              | `crate-name`                     |
| go             | proxy.golang.org       | `github.com/user/repo`           |
| jsr            | api.jsr.io             | `@scope/name`                    |
| nuget          | api.nuget.org          | `Package.Name`                   |
| docker         | hub.docker.com         | `image`, `user/image`            |
| rubygems       | rubygems.org           | `gem-name`                       |
| packagist      | packagist.org          | `vendor/package`                 |
| pub            | pub.dev                | `package_name`                   |
| swift          | api.github.com         | `owner/repo`                     |
| github-actions | api.github.com         | `owner/repo`                     |

## Installation

### Prerequisites

Install one of these runtimes, depending on how you want to start the server:

- [Node.js](https://nodejs.org/) v18.18 or later for `npx`
- [Bun](https://bun.sh/) v1.2 or later for `bunx` or direct Bun runs
- [Deno](https://deno.land/) v2.x or later for source-based Deno runs

### Run with npx or bunx

```bash
npx -y --package github:Tripletex/mcp-dependency-version mcp-dependency-version
```

```bash
bunx --bun github:Tripletex/mcp-dependency-version
```

These commands start an MCP stdio server, so direct terminal runs wait for MCP
JSON-RPC input and do not print a prompt.

### Setup with Claude Code CLI

Choose one of these commands.

Using npx:

```bash
claude mcp add mcp-dependency-version -- npx -y --package github:Tripletex/mcp-dependency-version mcp-dependency-version
```

Using Bun:

```bash
claude mcp add mcp-dependency-version -- bunx --bun github:Tripletex/mcp-dependency-version
```

Using Deno from a local checkout:

```bash
claude mcp add mcp-dependency-version -- deno run --allow-net --allow-env --allow-read /path/to/mcp-dependency-version/main.ts
```

### Setup with Codex CLI

Choose one of these commands.

Using npx:

```bash
codex mcp add mcp-dependency-version -- npx -y --package github:Tripletex/mcp-dependency-version mcp-dependency-version
```

Using Bun:

```bash
codex mcp add mcp-dependency-version -- bunx --bun github:Tripletex/mcp-dependency-version
```

Using Deno from a local checkout:

```bash
codex mcp add mcp-dependency-version -- deno run --allow-net --allow-env --allow-read /path/to/mcp-dependency-version/main.ts
```

### Setup with Codex config.toml

Codex stores MCP configuration in `~/.codex/config.toml`. You can also use a
project-scoped `.codex/config.toml` in trusted projects.

Using npx:

```toml
[mcp_servers."mcp-dependency-version"]
command = "npx"
args = ["-y", "--package", "github:Tripletex/mcp-dependency-version", "mcp-dependency-version"]
```

Using Bun:

```toml
[mcp_servers."mcp-dependency-version"]
command = "bunx"
args = ["--bun", "github:Tripletex/mcp-dependency-version"]
```

Using Deno from a local checkout:

```toml
[mcp_servers."mcp-dependency-version"]
command = "deno"
args = ["run", "--allow-net", "--allow-env", "--allow-read", "/path/to/mcp-dependency-version/main.ts"]
```

### Setup with Claude Desktop using npx

```json
{
  "mcpServers": {
    "mcp-dependency-version": {
      "command": "npx",
      "args": [
        "-y",
        "--package",
        "github:Tripletex/mcp-dependency-version",
        "mcp-dependency-version"
      ]
    }
  }
}
```

### Setup with Claude Desktop using bunx

```json
{
  "mcpServers": {
    "mcp-dependency-version": {
      "command": "bunx",
      "args": ["--bun", "github:Tripletex/mcp-dependency-version"]
    }
  }
}
```

### Setup with Claude Desktop using Deno

Add to your Claude Desktop configuration file
(`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS,
`~/.config/claude-desktop/claude_desktop_config.json` on Linux):

```json
{
  "mcpServers": {
    "mcp-dependency-version": {
      "command": "deno",
      "args": [
        "run",
        "--allow-net",
        "--allow-env",
        "--allow-read",
        "/path/to/mcp-dependency-version/main.ts"
      ]
    }
  }
}
```

### Setup with Docker

The service is available as a Docker image using stdio transport.

**Pull the image:**

```bash
docker pull ghcr.io/tripletex/mcp-dependency-version:latest
```

**Run directly:**

```bash
docker run --rm -i ghcr.io/tripletex/mcp-dependency-version:latest
```

**Claude Desktop configuration:**

```json
{
  "mcpServers": {
    "mcp-dependency-version": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "ghcr.io/tripletex/mcp-dependency-version:latest"
      ]
    }
  }
}
```

### Local Development

1. Clone the repository:
   ```bash
   git clone https://github.com/tripletex/mcp-dependency-version.git
   cd mcp-dependency-version
   ```

2. Run the server:
   ```bash
   deno task start
   ```

   Or run it with Bun:
   ```bash
   bun run main.ts
   ```

## Configuration

The server supports custom repository configurations for each registry type.
This allows you to use private registries, mirrors, or multiple repositories per
registry.

### Configuration File

Create a configuration file at `~/.config/mcp-dependency-version/config.json`:

```json
{
  "repositories": {
    "npm": {
      "npmjs": {
        "name": "npm",
        "url": "https://registry.npmjs.org",
        "default": true
      },
      "github": {
        "name": "GitHub Packages",
        "url": "https://npm.pkg.github.com",
        "auth": {
          "token": "ghp_xxxxxxxxxxxx"
        }
      }
    },
    "maven": {
      "central": {
        "name": "Maven Central",
        "url": "https://repo1.maven.org/maven2",
        "default": true
      },
      "atlassian": {
        "name": "Atlassian Maven",
        "url": "https://packages.atlassian.com/maven/public"
      },
      "jitpack": {
        "name": "JitPack",
        "url": "https://jitpack.io"
      }
    },
    "pypi": {
      "pypi": {
        "name": "PyPI",
        "url": "https://pypi.org/pypi",
        "default": true
      },
      "private": {
        "name": "Private PyPI",
        "url": "https://pypi.example.com/simple",
        "auth": {
          "username": "user",
          "password": "pass"
        }
      }
    }
  }
}
```

### Environment Variables

You can override the config file path using the `MCP_DEPENDENCY_VERSION_CONFIG`
environment variable:

```bash
export MCP_DEPENDENCY_VERSION_CONFIG=/path/to/config.json
```

Alternatively, pass the entire configuration as inline JSON via
`MCP_DEPENDENCY_VERSION_CONFIG_JSON`. When set, it takes precedence over the
config file. This is convenient in containerized setups (e.g. an MCP gateway
starting the server with `docker run`) where injecting an environment variable
is easier than mounting a file:

```bash
docker run --rm -i \
  -e MCP_DEPENDENCY_VERSION_CONFIG_JSON="$(cat config.json)" \
  ghcr.io/tripletex/mcp-dependency-version:<tag>
```

If the variable contains invalid JSON, a warning is printed to stderr and the
server falls back to the config file (then to the built-in defaults).

### Authentication

The configuration supports two authentication methods:

**Bearer Token:**

```json
{
  "auth": {
    "token": "your-token-here"
  }
}
```

**Basic Auth:**

```json
{
  "auth": {
    "username": "user",
    "password": "pass"
  }
}
```

Tokens are stored as plain text in the config file — environment variables are
not expanded inside config values. Restrict the file's permissions accordingly
(`chmod 600`).

### GitHub authentication (github-actions and swift)

The `github-actions` and `swift` registries are backed by the GitHub API. For
these registries, repository entries are configured with the **web URL** of the
organization or repository — not the API endpoint — and the entry whose URL is
the longest prefix of the package's GitHub URL supplies the authentication. This
lets different organizations use different tokens, which is how you give the
server access to actions in private repositories:

```json
{
  "repositories": {
    "github-actions": {
      "tripletex": {
        "name": "Tripletex (private)",
        "url": "https://github.com/Tripletex",
        "auth": { "token": "ghp_private_org_token" }
      },
      "github": {
        "name": "GitHub public (authenticated)",
        "url": "https://github.com/",
        "default": true,
        "auth": { "token": "ghp_public_token" }
      }
    }
  }
}
```

With this configuration, a lookup of `Tripletex/deploy-action` uses the private
org token, while `actions/checkout` matches the bare `https://github.com/` entry
and uses the public token (raising the GitHub API rate limit from 60 to 5,000
requests/hour). Omit the `auth` block on the default entry to keep public
lookups unauthenticated.

Matching rules:

- Longest URL prefix wins: `https://github.com/Tripletex/special-repo` beats
  `https://github.com/Tripletex`, which beats `https://github.com/`.
- Owner and repository comparison is case-insensitive, matching GitHub's
  behavior.
- Entries that do not match fall back to the `default: true` entry, then to the
  first configured entry.
- The API base URL is derived from the entry's host: `github.com` uses
  `https://api.github.com`, and any other host (GitHub Enterprise Server) uses
  `https://<host>/api/v3`. Legacy entries that point directly at
  `https://api.github.com` keep working as default/fallback entries but are
  never selected by URL matching.

For GitHub Enterprise, set the default entry to your GHES host (either
`https://ghe.example.com/` or the explicit `https://ghe.example.com/api/v3`).
Package names such as `owner/repo` carry no host, so unmatched lookups go to
whichever entry is the default.

A fine-grained personal access token with read-only `contents` and `metadata`
permissions on the relevant repositories is sufficient for version lookups.

### Default Repositories

If no configuration file exists, the server uses the official public registries:

| Registry       | Default URL                     |
| -------------- | ------------------------------- |
| npm            | https://registry.npmjs.org      |
| maven          | https://repo1.maven.org/maven2  |
| pypi           | https://pypi.org/pypi           |
| cargo          | https://crates.io/api/v1/crates |
| go             | https://proxy.golang.org        |
| jsr            | https://api.jsr.io              |
| nuget          | https://api.nuget.org/v3        |
| docker         | https://hub.docker.com          |
| rubygems       | https://rubygems.org            |
| packagist      | https://repo.packagist.org      |
| pub            | https://pub.dev/api             |
| swift          | https://api.github.com          |
| github-actions | https://api.github.com          |

## Tools

### lookup_version

Look up the latest version of a package.

**Parameters:**

- `registry` (required): Package registry (`npm`, `maven`, `pypi`, `cargo`,
  `go`, `jsr`, `nuget`, `docker`, `rubygems`, `packagist`, `pub`, `swift`,
  `github-actions`)
- `package` (required): Package name
- `includePrerelease` (optional): Include alpha/beta/rc versions
- `versionPrefix` (optional): Filter to a semver version line and return the
  newest match (e.g., `"2."` for 2.x). Mutually exclusive with
  `versionReference`.
- `versionReference` (optional): Resolve a floating, non-semver target to its
  current concrete pin -- a git branch/ref (github-actions: `"main"`), or a
  named dist-tag/channel (npm: `"next"`; docker: `"latest"`). Returns the
  resolved version and, where supported, a commit SHA / `sha256:` digest in
  `digest`, flagged `isMutable: true`. Supported for `github-actions`, `swift`,
  `npm`, and `docker`. Mutually exclusive with `versionPrefix`.
- `currentDigest` (optional): Reverse-resolve an existing digest pin to the
  version it corresponds to. For `github-actions` and `swift`, a commit SHA
  (full 40 characters or a prefix of at least 7); for `docker`, a `sha256:`
  digest. The response adds a `pin` object with the matching tags, the pinned
  version, and the update type relative to the latest version. Mutually
  exclusive with `versionPrefix` and `versionReference`.

**Example:**

```json
{
  "registry": "npm",
  "package": "lodash"
}
```

**Output:**

```json
{
  "packageName": "lodash",
  "registry": "npm",
  "latestStable": "4.17.21",
  "publishedAt": "2021-02-20T15:42:16.891Z"
}
```

**Docker Output (includes digest for secure pinning):**

```json
{
  "packageName": "nginx",
  "registry": "docker",
  "latestStable": "1.27.3",
  "publishedAt": "2024-12-04T18:51:59.819Z",
  "digest": "sha256:1948e0c46da16a3565a844aa65ab848e1546f85cf47e47d044a567906a3a497f",
  "secureReference": "nginx@sha256:1948e0c46da16a3565a844aa65ab848e1546f85cf47e47d044a567906a3a497f",
  "securityNotes": [
    "WARNING: Docker tags are NOT immutable. A tag can be moved to point to a different image at any time.",
    "Using the digest-pinned reference (image@sha256:...) provides protection against tag tampering.",
    "Digest-pinned references ensure you always pull the exact same image, preventing supply chain attacks.",
    "When updating, explicitly change the digest and verify the new image before deployment."
  ]
}
```

**GitHub Actions Output (includes commit SHA for secure pinning):**

```json
{
  "packageName": "actions/checkout",
  "registry": "github-actions",
  "latestStable": "4.2.0",
  "publishedAt": "2024-10-01T12:00:00.000Z",
  "digest": "b4ffde65f46336ab88eb53be808477a3936bae11",
  "secureReference": "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.2.0",
  "securityNotes": [
    "GitHub Action tags are NOT immutable. Tags can be force-pushed to point to different commits.",
    "The commit SHA for this version is returned in the 'digest' field.",
    "Use commit SHA-pinned references (owner/repo@<digest>) for supply chain security.",
    "Secure reference: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.2.0"
  ]
}
```

**Resolving a floating reference (`versionReference`):**

Resolve a GitHub Actions branch to its current commit SHA for pinning:

```json
{
  "registry": "github-actions",
  "package": "actions/checkout",
  "versionReference": "main"
}
```

```json
{
  "packageName": "actions/checkout",
  "registry": "github-actions",
  "latestStable": "main",
  "digest": "b4ffde65f46336ab88eb53be808477a3936bae11",
  "secureReference": "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # main",
  "isMutable": true,
  "resolvedReference": "main",
  "securityNotes": [
    "GitHub Action branches and tags are NOT immutable. Branches move and tags can be force-pushed to point to different commits.",
    "You are tracking the mutable branch 'main'. Its current commit SHA is returned in the 'digest' field.",
    "Pin to the commit SHA for supply chain security: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # main",
    "Updating means re-resolving branch 'main' to its latest commit, not moving to a release."
  ]
}
```

**Reverse pin lookup (`currentDigest`):**

Identify which version an existing SHA pin corresponds to, and how far behind it
is:

```json
{
  "registry": "github-actions",
  "package": "actions/checkout",
  "currentDigest": "b4ffde65f46336ab88eb53be808477a3936bae11"
}
```

```json
{
  "packageName": "actions/checkout",
  "registry": "github-actions",
  "latestStable": "7.0.0",
  "digest": "8edcb1bdb4e267140fa742c62e395cd74f332709",
  "secureReference": "actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709 # v7.0.0",
  "pin": {
    "digest": "b4ffde65f46336ab88eb53be808477a3936bae11",
    "matches": [
      { "reference": "v4.2.0", "version": "4.2.0" },
      { "reference": "v4", "version": "4.2.0" }
    ],
    "pinnedVersion": "4.2.0",
    "updateType": "major"
  }
}
```

For Docker, one digest commonly maps to several tags (`1.27.3`, `1.27`,
`latest`, ...); all matches are returned and per-architecture digest matches are
annotated in `detail`. When nothing matches -- an untagged commit, a
force-pushed tag's previous target, or a digest older than the fetched tag page
-- `pin.matches` is empty and `pin.notes` explains the possible causes.

### list_versions

List all available versions of a package.

**Parameters:**

- `registry` (required): Package registry
- `package` (required): Package name
- `limit` (optional): Maximum versions to return (default: 20)

**Example:**

```json
{
  "registry": "pypi",
  "package": "requests",
  "limit": 5
}
```

**Output:**

```json
{
  "packageName": "requests",
  "registry": "pypi",
  "versions": [
    {
      "version": "2.31.0",
      "publishedAt": "2023-05-22T15:12:44.000Z",
      "isPrerelease": false,
      "isDeprecated": false
    }
  ],
  "totalCount": 142,
  "showing": 5
}
```

### check_vulnerabilities

Check a package version for known security vulnerabilities.

Queries both OSV and NVD in parallel for comprehensive coverage. Results are
deduplicated by CVE ID — when a vulnerability appears in both databases, NVD's
CVSS v3.1 score is used as the authoritative severity rating.

Set the `NVD_API_KEY` environment variable for higher NVD rate limits (50 vs 5
requests per 30 seconds). Request a free key at
https://nvd.nist.gov/developers/request-an-api-key.

**Parameters:**

- `registry` (required): Package registry
- `package` (required): Package name
- `version` (required): Version to check
- `severityThreshold` (optional): Minimum severity (`LOW`, `MEDIUM`, `HIGH`,
  `CRITICAL`)

**Example:**

```json
{
  "registry": "npm",
  "package": "lodash",
  "version": "4.17.20"
}
```

**Output:**

```json
{
  "packageName": "lodash",
  "version": "4.17.20",
  "registry": "npm",
  "vulnerabilities": [
    {
      "id": "GHSA-29mw-wpgm-hmr9",
      "summary": "Prototype Pollution in lodash",
      "severity": "HIGH",
      "cvss": 7.2,
      "cveIds": ["CVE-2021-23337"],
      "cweIds": ["CWE-94"],
      "fixedVersions": ["4.17.21"],
      "source": "osv+nvd"
    }
  ],
  "totalCount": 1,
  "hasVulnerabilities": true,
  "summary": {
    "critical": 0,
    "high": 1,
    "medium": 0,
    "low": 0
  }
}
```

### analyze_dependencies

Analyze a dependency file and check for available updates.

**Parameters:**

- `content` (required): File content (package.json, pom.xml, build.gradle,
  build.gradle.kts, requirements.txt, Cargo.toml, go.mod, deno.json, _.csproj,
  Gemfile, composer.json, pubspec.yaml, Package.swift, .github/workflows/_.yml)
- `registry` (required): Package registry (use `maven` for Gradle files, use
  `github-actions` for workflow files)
- `checkVulnerabilities` (optional): Also scan for vulnerabilities (default:
  false)

**Supported Dependency Files:**

| Registry       | File Formats                                                    |
| -------------- | --------------------------------------------------------------- |
| npm            | `package.json`                                                  |
| maven          | `pom.xml`, `build.gradle` (Groovy), `build.gradle.kts` (Kotlin) |
| pypi           | `requirements.txt`                                              |
| cargo          | `Cargo.toml`                                                    |
| go             | `go.mod`                                                        |
| jsr            | `deno.json` (supports jsr: and npm: imports)                    |
| nuget          | `*.csproj` (PackageReference format)                            |
| docker         | `Dockerfile`, `docker-compose.yml`                              |
| rubygems       | `Gemfile`                                                       |
| packagist      | `composer.json`                                                 |
| pub            | `pubspec.yaml`                                                  |
| swift          | `Package.swift`                                                 |
| github-actions | `.github/workflows/*.yml`                                       |

**Note:** For GitHub Actions workflow files, SHA-pinned references are skipped
since they are already secure. For Gradle files, variable references
(`$version`, `${libs.xxx}`, version catalogs) are skipped since they can't be
resolved without evaluating the build.

**Example (npm):**

```json
{
  "content": "{\"dependencies\": {\"lodash\": \"^4.17.20\", \"express\": \"^4.18.0\"}}",
  "registry": "npm",
  "checkVulnerabilities": true
}
```

**Example (Gradle Kotlin DSL):**

```json
{
  "content": "dependencies {\n    implementation(\"org.springframework.boot:spring-boot-starter:3.2.0\")\n    testImplementation(\"org.junit.jupiter:junit-jupiter:5.10.0\")\n}",
  "registry": "maven"
}
```

**Output:**

```json
{
  "registry": "npm",
  "dependencies": [
    {
      "name": "lodash",
      "currentVersion": "4.17.20",
      "latestVersion": "4.17.21",
      "updateAvailable": true,
      "updateType": "patch",
      "vulnerabilities": [
        { "id": "GHSA-29mw-wpgm-hmr9", "summary": "Prototype Pollution" }
      ]
    },
    {
      "name": "express",
      "currentVersion": "4.18.0",
      "latestVersion": "4.18.2",
      "updateAvailable": true,
      "updateType": "patch",
      "vulnerabilities": []
    }
  ],
  "summary": {
    "total": 2,
    "outdated": 2,
    "vulnerable": 1,
    "deprecated": 0,
    "majorUpdates": 0,
    "minorUpdates": 0,
    "patchUpdates": 2
  }
}
```

### get_licenses

Look up the declared license for one or more packages in a single call. Results
are enriched with SPDX metadata and copyleft classification from a baked-in
dataset (see [License dataset](#license-dataset-datalicensesjson)).

**Parameters:**

- `packages` (required): Array of `{registry, package, version?}` entries.
  Results preserve input order. Pass a single-element array for one package.

**Example:**

```json
{
  "packages": [
    { "registry": "npm", "package": "lodash" },
    { "registry": "cargo", "package": "serde" },
    { "registry": "go", "package": "github.com/gin-gonic/gin" }
  ]
}
```

**Output (abbreviated):**

```json
{
  "results": [
    {
      "registry": "npm",
      "packageName": "lodash",
      "license": "MIT",
      "status": "found",
      "spdx": [
        {
          "licenseId": "MIT",
          "name": "MIT License",
          "isOsiApproved": true,
          "isDeprecated": false,
          "category": "Permissive",
          "isCopyleft": false,
          "reference": "https://spdx.org/licenses/MIT.html"
        }
      ],
      "isCopyleft": false
    },
    {
      "registry": "cargo",
      "packageName": "serde",
      "license": "MIT OR Apache-2.0",
      "status": "found",
      "spdx": [
        { "licenseId": "MIT", "category": "Permissive", "isCopyleft": false },
        {
          "licenseId": "Apache-2.0",
          "category": "Permissive",
          "isCopyleft": false
        }
      ],
      "isCopyleft": false
    },
    {
      "registry": "go",
      "packageName": "github.com/gin-gonic/gin",
      "license": null,
      "status": "registry-unsupported",
      "note": "The go registry does not expose license metadata through this tool. Check the package's source repository or documentation."
    }
  ],
  "summary": {
    "total": 3,
    "withLicense": 2,
    "notDeclared": 0,
    "registryUnsupported": 1,
    "errors": 0,
    "copyleft": 0
  },
  "sources": [
    {
      "name": "SPDX License List",
      "version": "3.28.0",
      "license": "CC0-1.0",
      "attribution": "..."
    },
    {
      "name": "ScanCode LicenseDB",
      "license": "CC-BY-4.0",
      "attribution": "..."
    }
  ]
}
```

**Status values:**

- `found` — license string returned in `license`; `spdx[]` and `isCopyleft`
  populated when the string resolves to one or more SPDX IDs
- `not-declared` — the package did not declare a license in its metadata
- `registry-unsupported` — the registry does not expose license metadata (`go`,
  `docker`)
- `error` — metadata fetch failed; see the `error` field

**Enrichment fields** (present on `found` results):

- `spdx[].licenseId` — canonical SPDX short identifier
- `spdx[].isOsiApproved` — OSI-approved open source license
- `spdx[].isDeprecated` — SPDX-deprecated identifier (use the replacement)
- `spdx[].category` — ScanCode LicenseDB category (`Permissive`, `Copyleft`,
  `Copyleft Limited`, `Public Domain`, `Proprietary Free`, etc.)
- `spdx[].isCopyleft` — shortcut for `category` ∈ {`Copyleft`,
  `Copyleft Limited`}
- `isCopyleft` (top-level) — true if _any_ resolved license is copyleft

A `not-declared` or `registry-unsupported` result does **not** mean the package
is unlicensed. Check the package's source repository or documentation instead.

Some registries return license strings that don't match a known SPDX ID
(free-form text, deprecated aliases, custom names). In that case `spdx` is an
empty array and `isCopyleft` is omitted — the raw `license` string is still
returned verbatim.

### get_package_docs

Get README documentation for a package.

**Parameters:**

- `registry` (required): Package registry (`npm`, `maven`, `pypi`, `cargo`,
  `go`, `jsr`, `nuget`, `docker`, `rubygems`, `packagist`, `pub`, `swift`,
  `github-actions`)
- `package` (required): Package name
- `version` (optional): Specific version to get documentation for

**Documentation Sources:**

| Registry       | README Source              | Repository URL Source       |
| -------------- | -------------------------- | --------------------------- |
| npm            | Registry API               | `repository` field          |
| pypi           | Registry API (description) | `project_urls` field        |
| cargo          | Registry API               | `repository` field          |
| maven          | GitHub (fallback)          | POM `<scm>` section         |
| go             | GitHub (fallback)          | Module path (if github.com) |
| jsr            | GitHub (fallback)          | `githubRepository` field    |
| nuget          | GitHub (fallback)          | Catalog entry               |
| docker         | GitHub (fallback)          | Docker Hub page             |
| rubygems       | Registry API (info)        | `source_code_uri` field     |
| packagist      | Registry API (description) | `repository` field          |
| pub            | Registry API (description) | `repository` field          |
| swift          | GitHub (fallback)          | GitHub repository URL       |
| github-actions | GitHub (fallback)          | GitHub repository URL       |

**Example:**

```json
{
  "registry": "npm",
  "package": "lodash"
}
```

**Output:**

```
# lodash Documentation
Registry: npm
Source: registry
Documentation: https://www.npmjs.com/package/lodash
Repository: https://github.com/lodash/lodash

---

# lodash

A modern JavaScript utility library delivering modularity, performance & extras.
...
```

## License dataset (`data/licenses.json`)

The `get_licenses` tool enriches each result with SPDX metadata and copyleft
classification from a baked-in dataset at `data/licenses.json`. Using a
committed snapshot keeps license lookups deterministic, offline-capable, and
auditable in the repo.

The dataset merges two upstream sources:

| Source                 | URL                                                 | License   | Use                                                                                              |
| ---------------------- | --------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------ |
| **SPDX License List**  | https://spdx.org/licenses/licenses.json             | CC0-1.0   | Canonical SPDX IDs, names, OSI approval status                                                   |
| **ScanCode LicenseDB** | https://scancode-licensedb.aboutcode.org/index.json | CC-BY-4.0 | License `category` (Permissive / Copyleft / Copyleft Limited / etc.) and copyleft classification |

Both sources — and their full attribution strings — are embedded in the
`sources[]` array at the root of `data/licenses.json` and echoed back on every
`get_licenses` response, so downstream consumers carry the attribution required
by ScanCode's CC-BY-4.0.

### Refreshing the dataset

```bash
deno task update-licenses
```

This runs `scripts/update-licenses.ts`, which fetches both upstream lists,
merges them, and rewrites `data/licenses.json`. Review the diff and commit — the
upstream lists change only a few times per year. The script needs
`--allow-net` + `--allow-write=data` (already set in the task).

## Development

### Commands

```bash
# Type check
deno task check

# Run tests
deno task test

# Start server
deno task start

# Start with file watching
deno task dev

# Lint
deno task lint

# Format
deno task fmt
```

### Project Structure

```
src/
├── config/       # Configuration loading
├── registries/   # Registry client implementations (npm, maven, pypi, etc.)
├── parsers/      # Dependency file parsers (package.json, pom.xml, etc.)
├── tools/        # MCP tool implementations
└── utils/        # Shared utilities (version parsing, caching, HTTP)
```

## API Reference

### Registry APIs

| Registry       | API Endpoint                                     | Documentation                                                            |
| -------------- | ------------------------------------------------ | ------------------------------------------------------------------------ |
| npm            | `registry.npmjs.org/{package}`                   | [docs](https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md) |
| Maven          | `repo1.maven.org/maven2`                         | [docs](https://central.sonatype.com/search)                              |
| PyPI           | `pypi.org/pypi/{package}/json`                   | [docs](https://warehouse.pypa.io/api-reference/json.html)                |
| Cargo          | `crates.io/api/v1/crates/{crate}`                | [docs](https://crates.io/data-access)                                    |
| Go             | `proxy.golang.org/{module}/@v/list`              | [docs](https://go.dev/ref/mod#goproxy-protocol)                          |
| JSR            | `api.jsr.io/scopes/{scope}/packages/{name}`      | [docs](https://jsr.io/docs/api)                                          |
| NuGet          | `api.nuget.org/v3-flatcontainer/{id}/index.json` | [docs](https://learn.microsoft.com/en-us/nuget/api/overview)             |
| Docker         | `hub.docker.com/v2/repositories/{image}/tags`    | [docs](https://docs.docker.com/docker-hub/api/latest/)                   |
| RubyGems       | `rubygems.org/api/v1/gems/{gem}.json`            | [docs](https://guides.rubygems.org/rubygems-org-api/)                    |
| Packagist      | `repo.packagist.org/p2/{vendor}/{package}.json`  | [docs](https://packagist.org/apidoc)                                     |
| Pub            | `pub.dev/api/packages/{package}`                 | [docs](https://pub.dev/help/api)                                         |
| Swift          | `api.github.com/repos/{owner}/{repo}/tags`       | [docs](https://docs.github.com/en/rest/repos/repos)                      |
| GitHub Actions | `api.github.com/repos/{owner}/{repo}/tags`       | [docs](https://docs.github.com/en/rest/repos/repos)                      |
| OSV            | `api.osv.dev/v1/query`                           | [docs](https://osv.dev/docs/)                                            |
| NVD            | `services.nvd.nist.gov/rest/json/cves/2.0`       | [docs](https://nvd.nist.gov/developers/vulnerabilities)                  |

## License

MIT

Maintenance

ActivitySlowing
ResponsivenessNo issues