Skip to main content
Glama
josh-thurston

Vanta MCP Server

README.md
# Vanta MCP Server

An [MCP](https://modelcontextprotocol.io) server for the [Vanta](https://www.vanta.com) compliance platform. Exposes **16 tools** for managing controls, vulnerabilities, vendors, tests, risk scenarios, policies, monitored computers, documents, and findings.

## Transports

| Transport | Endpoint | Use case |
|-----------|----------|----------|
| Streamable HTTP | `http://<host>:8010/mcp` | Claude Desktop, most MCP clients |
| SSE | `http://<host>:8010/sse` | Legacy clients (n8n, etc.) |
| Health | `http://<host>:8010/health` | Container health checks |

## Quick Start — Docker

### 1. Get Credentials

1. Log in to the [Vanta Developer Console](https://developer.vanta.com).
2. Go to **Settings → API Access**.
3. Create an OAuth application — copy the **Client ID** and **Client Secret**.
4. Grant the scopes your tools require (typically `vanta.read` and `vanta.write`).

### 2. Configure

```bash
cp .env.example .env
# Edit .env — set OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET at minimum
```

`.env` example:
```env
OAUTH_CLIENT_ID=your_client_id
OAUTH_CLIENT_SECRET=your_client_secret
AUTH_TOKEN=          # optional — requires Bearer auth on /mcp and /sse
PORT=8010
```

### 3. Run

```bash
docker compose up -d
# Server: http://localhost:8010
# Health: http://localhost:8010/health
```

### 4. Connect Claude Desktop

Add to `claude_desktop_config.json`:
```json
{
  "mcpServers": {
    "vanta": {
      "type": "http",
      "url": "http://localhost:8010/mcp"
    }
  }
}
```

If `AUTH_TOKEN` is set:
```json
{
  "mcpServers": {
    "vanta": {
      "type": "http",
      "url": "http://localhost:8010/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_AUTH_TOKEN"
      }
    }
  }
}
```

---

## Quick Start — Local (no Docker)

```bash
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
export OAUTH_CLIENT_ID=your_client_id
export OAUTH_CLIENT_SECRET=your_client_secret
python -m src.server
```

---

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `TRANSPORT_MODE` | `streamable-http` | Informational — both `/mcp` and `/sse` are always active |
| `MCP_HOST` | `0.0.0.0` | Bind address |
| `MCP_PORT` | `8010` | HTTP port (also accepts legacy `PORT`) |
| `AUTH_TOKEN` | _(disabled)_ | If set, clients must send `Authorization: Bearer <AUTH_TOKEN>` |
| `LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
| `TIMEOUT_SECONDS` | `60` | HTTP request timeout in seconds |
| `API_BASE_URL` | `https://api.vanta.com` | Vanta API base URL |
| `OAUTH_CLIENT_ID` | — | Default tenant: Vanta OAuth2 client ID (`vci_...`) |
| `OAUTH_CLIENT_SECRET` | — | Default tenant: Vanta OAuth2 client secret (`vcs_...`) |

`OAUTH_CLIENT_ID` / `OAUTH_CLIENT_SECRET` are only used to auto-create a `"default"` tenant entry. For multi-tenant deployments use `config/tenants.json` instead.

---

## Multi-Tenant

For multiple Vanta organizations, populate `config/tenants.json` (copy from `config/tenants.json.example`):

```json
{
  "tenants": [
    {
      "id": "acme-corp",
      "vanta_base_url": "https://api.vanta.com",
      "vanta_client_id": "vci_CLIENT_ID_A",
      "vanta_client_secret": "vcs_CLIENT_SECRET_A"
    },
    {
      "id": "contoso",
      "vanta_base_url": "https://api.vanta.com",
      "vanta_api_token": "vat_PERSONAL_ACCESS_TOKEN_B"
    }
  ]
}
```

The `id` value is what your agent/client sends in the `X-Tenant-ID` request header:

```
X-Tenant-ID: acme-corp
Authorization: Bearer <AUTH_TOKEN>   ← only if AUTH_TOKEN is set in .env
```

**Credential fields:**

| Field | Required | Description |
|-------|----------|-------------|
| `id` | ✅ | Must match `X-Tenant-ID` header exactly (case-sensitive) |
| `vanta_base_url` | Optional | Defaults to `https://api.vanta.com` |
| `vanta_client_id` | ✅* | OAuth client ID (prefix: `vci_`) |
| `vanta_client_secret` | ✅* | OAuth client secret (prefix: `vcs_`) |
| `vanta_api_token` | ✅* | Personal access token — alternative to OAuth (prefix: `vat_`) |

*One auth method required per tenant. OAuth is recommended for production.

If `"default"` exists in `tenants.json`, requests without an `X-Tenant-ID` header will automatically use those credentials. See `../MCP-Multi-Tenant-Guide.md` for full architecture and client usage examples.

`config/tenants.json` is gitignored and never baked into the Docker image — it is mounted at runtime via the `./config:/app/config:ro` volume.

---

## Tools

### Controls

| Tool | Description |
|------|-------------|
| `list_controls` | List all controls with optional pagination |
| `get_control` | Get full details for a specific control by ID |
| `list_control_tests` | List all tests linked to a specific control |

### Vulnerabilities

| Tool | Description |
|------|-------------|
| `list_vulnerabilities` | List vulnerabilities with optional pagination |
| `remediate_vulnerability` | Update a vulnerability's remediation status and notes |
| `list_vulnerability_findings` | List vulnerability findings with optional pagination |
| `deactivate_vulnerability` | Deactivate a vulnerability by ID |

### Vendors

| Tool | Description |
|------|-------------|
| `list_vendors` | List approved vendors with optional pagination |
| `get_vendor` | Get full details for a specific vendor by ID |
| `list_discovered_vendors` | List auto-discovered vendors (from integrations) |

### Tests

| Tool | Description |
|------|-------------|
| `list_tests` | List all compliance tests with optional pagination |
| `list_test_entities` | List entities associated with a specific test |

### Risk & Policies

| Tool | Description |
|------|-------------|
| `list_risk_scenarios` | List risk scenarios with optional pagination |
| `list_policies` | List security policies with optional pagination |

### Assets & Documents

| Tool | Description |
|------|-------------|
| `list_monitored_computers` | List computers monitored by Vanta agents |
| `list_documents` | List documents (policies, procedures, evidence) |

---

## Auth Flow

```
Claude Desktop → POST /mcp
  → TenantAuthMiddleware (validates AUTH_TOKEN if set, reads X-Tenant-ID)
  → _require_client() → tenants.json lookup or env vars
  → OAuth2 token exchange (POST https://api.vanta.com/oauth/token)
  → Vanta API call with Bearer token
  → Response
```

Tokens are cached per tenant instance and refreshed automatically on 401.

---

## Project Layout

```
Vanta-MCP/
├── src/
│   ├── server.py              # Starlette app; /mcp, /sse, /health endpoints
│   ├── middleware.py          # TenantAuthMiddleware (AUTH_TOKEN + X-Tenant-ID)
│   ├── context.py             # Request-scoped tenant ID storage
│   ├── tools.py               # All 16 MCP tool functions
│   └── vanta_client.py        # Vanta API client (OAuth2, all endpoints)
├── config/
│   ├── tenants.json           # Gitignored — your multi-tenant config
│   └── tenants.json.example   # Template — safe to commit
├── Dockerfile
├── docker-compose.yml
├── docker-compose.override.yml.example
├── requirements.txt
├── .env.example
└── README.md
```

---

## Troubleshooting

**`401 Unauthorized`**
- Verify `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are correct.
- Ensure the OAuth app has the required scopes in the Vanta Developer Console.
- If using `AUTH_TOKEN`, confirm the client sends `Authorization: Bearer <token>`.

**`403 Forbidden`**
- The OAuth app scope is insufficient. Update it in the Vanta Developer Console.

**`404 Not Found`**
- The resource ID does not exist in your Vanta organization.

**Tools not showing in Claude Desktop**
- Restart Claude Desktop after changing `claude_desktop_config.json`.
- Confirm the server is running: `curl http://localhost:8010/health`.

**`No Vanta credentials found`**
- Ensure `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are set in `.env` (or environment).
- Or confirm `config/tenants.json` is mounted and contains a valid entry for the requested tenant.

**Docker code changes not reflected**
- `docker compose restart` does NOT rebuild the image. Run:
  ```bash
  docker compose build --no-cache && docker compose up -d
  ```

---

## License

MIT