Skip to main content
Glama
README.md
# Dependency Security MCP

Dependency Security MCP is a small, production-oriented Model Context Protocol server for OWASP Dependency-Check vulnerability scanning. It exposes three narrow tools:

- `start_dependency_scan`
- `get_scan_status`
- `get_scan_report`

It does not expose shell execution, arbitrary filesystem access, repository mutation, remediation, package manager commands, or unrestricted OWASP CLI arguments.

## Architecture

```mermaid
flowchart TD
    LangGraph --> Client[MCP Client]
    Client --> Server[Dependency Security MCP]
    Server --> ScanService
    Server --> ReportService
    ScanService --> Scanner[DependencyScanner]
    ReportService --> Scanner
    Scanner --> OWASP[OWASP Dependency-Check]
```

The MCP layer is intentionally thin. Tool functions validate and serialize request and response data, then call application services. Services depend on repository and scanner abstractions, so a later OSV scanner, dependency resolver, version provider, or remediation service can be added without changing the MCP tool contracts.

## Requirements

- Python 3.12+
- OWASP Dependency-Check CLI available as `dependency-check`
- The official MCP Python SDK v2

Install Python dependencies:

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

The project uses the current v2 server style:

```python
from mcp.server import MCPServer
```

## Installing OWASP Dependency-Check

Install Dependency-Check from the OWASP project and ensure the `dependency-check` command is on `PATH`. You can also point the server at a custom executable:

```bash
export OWASP_DEPENDENCY_CHECK_PATH=/opt/dependency-check/bin/dependency-check.sh
```

## Configuration

Copy `.env.example` values into your environment:

```bash
export MCP_SERVER_NAME=dependency-security-mcp
export OWASP_DEPENDENCY_CHECK_PATH=dependency-check
export SCAN_OUTPUT_ROOT=/tmp/dependency-security-mcp
export ALLOWED_REPOSITORY_ROOTS=/workspace/repos
export SCAN_TIMEOUT_SECONDS=1800
export LOG_LEVEL=INFO
```

`ALLOWED_REPOSITORY_ROOTS` is a comma-separated allowlist. Requested repositories are resolved with `Path.resolve()` and must exist, be directories, and live below one configured root.

## Running

Run with stdio for local development:

```bash
uv run mcp dev src/dependency_security_mcp/server.py
```

Run with Streamable HTTP for production-style serving:

```bash
uv run mcp run src/dependency_security_mcp/server.py --transport streamable-http
```

Open the MCP Inspector:

```bash
uv run mcp dev src/dependency_security_mcp/server.py
```

## MCP Tools

### `start_dependency_scan`

Starts an OWASP Dependency-Check scan asynchronously and returns immediately after scheduling.

Request:

```json
{
  "repository_path": "/workspace/repos/payment-service",
  "project_name": "payment-service"
}
```

Response:

```json
{
  "scan_id": "4f8b3dfd-e4f7-40ec-b31f-df3d1992c544",
  "scanner": "OWASP_DEPENDENCY_CHECK",
  "status": "RUNNING",
  "project_name": "payment-service",
  "started_at": "2026-10-03T07:00:00Z"
}
```

### `get_scan_status`

Request:

```json
{
  "scan_id": "4f8b3dfd-e4f7-40ec-b31f-df3d1992c544"
}
```

Response:

```json
{
  "scan_id": "4f8b3dfd-e4f7-40ec-b31f-df3d1992c544",
  "status": "COMPLETED",
  "scanner": "OWASP_DEPENDENCY_CHECK",
  "started_at": "2026-10-03T07:00:00Z",
  "completed_at": "2026-10-03T07:03:15Z",
  "error": null
}
```

### `get_scan_report`

Running response:

```json
{
  "scan_id": "4f8b3dfd-e4f7-40ec-b31f-df3d1992c544",
  "status": "RUNNING",
  "message": "Scan has not completed yet."
}
```

Completed response:

```json
{
  "scan_id": "4f8b3dfd-e4f7-40ec-b31f-df3d1992c544",
  "scanner": "OWASP_DEPENDENCY_CHECK",
  "project_name": "payment-service",
  "repository_path": "/workspace/repos/payment-service",
  "status": "COMPLETED",
  "dependency_count": 147,
  "vulnerable_dependency_count": 3,
  "vulnerability_count": 5,
  "findings": []
}
```

## Example Client Flow

```python
from mcp import Client


async with Client("http://localhost:8000/mcp") as client:
    start = await client.call_tool(
        "start_dependency_scan",
        {
            "repository_path": "/workspace/repos/payment-service",
            "project_name": "payment-service",
        },
    )
    scan_id = start.structured_content["scan_id"]

    while True:
        status = await client.call_tool("get_scan_status", {"scan_id": scan_id})
        if status.structured_content["status"] in {"COMPLETED", "FAILED", "CANCELLED"}:
            break

    report = await client.call_tool("get_scan_report", {"scan_id": scan_id})
    print(report.structured_content)
```

## Docker

Build:

```bash
docker build -t dependency-security-mcp .
```

Run with a read-only repository mount:

```bash
docker run --rm \
  -v /repos:/workspace/repos:ro \
  -e ALLOWED_REPOSITORY_ROOTS=/workspace/repos \
  dependency-security-mcp
```

The image runs as a non-root user and writes scan output under `SCAN_OUTPUT_ROOT`.

## Security Model

- Never executes arbitrary user commands.
- Never uses `shell=True`.
- Validates repository paths by resolving them before allowlist checks.
- Restricts scans to configured roots.
- Server controls output directories.
- Enforces scan timeouts and process termination.
- Validates scanner exit codes.
- Returns structured client-safe errors.
- Does not expose environment variables, raw shell access, arbitrary filesystem APIs, or arbitrary OWASP arguments.

The caller decides when and which allowed repository to scan. The MCP server decides whether the path is allowed, which executable and arguments are used, where reports are stored, how long the scan may run, and how reports are normalized.

## Tests

Run unit tests:

```bash
pytest
```

Run lint:

```bash
ruff check .
```

The real OWASP integration test is opt-in:

```bash
RUN_OWASP_INTEGRATION_TESTS=true pytest tests/integration
```

## Roadmap

The architecture is ready for additional narrow capabilities later:

- Maven, Gradle, and npm dependency resolution
- OSV scanning
- Dependency version lookup
- Suggested dependency fixes
- Controlled dependency updates
- Build/test verification

Those capabilities are intentionally not implemented in this first version.

TDQS

A3.5/5.0

Scored across 3 tools

Disambiguation5/5

Each tool targets a distinct phase of the scan lifecycle: start a scan, poll its status, and retrieve the completed report. There is no functional overlap, and the descriptions clearly differentiate status polling from report retrieval.

Naming Consistency5/5

All tool names follow a consistent snake_case verb_noun pattern (start_dependency_scan, get_scan_status, get_scan_report). The shared 'get_scan_' prefix for the two retrieval tools reinforces predictability.

Tool Count5/5

Three tools are well-scoped for a focused dependency scanning service: initiate, monitor, and fetch results. The set contains no redundant or filler tools, and the count sits comfortably in the typical 3-15 range.

Completeness4/5

The core lifecycle (start → status → report) is fully covered, but there is no way to list existing scans or cancel a running scan. These are minor gaps that agents can work around by retaining scan IDs, but they limit multi-scan management.

Maintenance

ActivityMaintained
ResponsivenessNo issues