A side-by-side look at the VPNDetection and Vulnify MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose VPNDetection for security work as a local process; choose Vulnify for security as a local process. You can also run both side by side in one mcpServers block.
VPNDETECTION_API_KEY, VPNDETECTION_BASE_URL.lookup_ip, lookup_ips, my_entitlement.VULNIFY_API_KEY.check_action, decide_action, get_decision.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Security | Security |
| Quality signal | 63/100 · Good | 63/100 · Good |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Local subprocess (stdio) |
| Auth | Not declared | Not declared |
| Pricing | Freemium | Not declared |
| Required env vars | VPNDETECTION_API_KEYVPNDETECTION_BASE_URL | VULNIFY_API_KEY |
| Verified owner | Community listing | Community listing |
| GitHub stars | 1 | 2 |
| Directory activity | 1 views 0 installs 0 upvotes | 1 views 0 installs 0 upvotes |
| Tools | 7 | 7 |
| Full listing | View VPNDetection | View Vulnify |
lookup_ipClassify a single IPv4 or IPv6 address: whether it belongs to a VPN, a residential, datacenter, mobile or public proxy, a Tor node, a privacy relay or web gateway, a hosting provider or a CDN, with the provider name where we have one. Private and reserved addresses are answered locally and cost nothing. Always read `coverage.note` before concluding anything from a field that is not in the result.lookup_ipsClassify a list of addresses in one call, returning a map keyed by address so duplicates collapse and the order you passed them stops mattering. Pass the whole list rather than splitting it yourself: a long one is batched for you. Prefer this over repeated `lookup_ip` calls when you already have the list, for example when triaging a log file. An address that fails carries its error in place of a result rather than failing the batch. Always read `coverage.note` before concluding anything from a field that is not in the result.my_entitlementWhat this API key is entitled to and how much of it has been used: the plan, the field tier that decides how much of a lookup answer comes back, the requests counted so far, the allowance, and when it resets. Use it to explain why a field is missing from a lookup, or before a large batch. Usage counts against the anniversary of the subscription rather than the calendar month, and can lag a few seconds behind. A null `hard_limit` means we never stop serving - it is NOT a limit of zero.list_databasesThe database catalog as this API key's organization may see it, one entry per database FAMILY, with `standing` saying where their license stands: `licensed` if the family is theirs today, `expired` if the term has ended, `unlicensed` if it is published but has never been bought. Each entry has a `base` id, which is what the license names, and a `versions` array whose `id` is what the other database tools take - pass `versions[].id` (`cdn_ip_v1`), never the `base` (`cdn_ip`). Ask again rather than holding on to this: it is answered per key and is not the same for everyone.database_metadataWhat is inside one database before you fetch it: the columns in each published format with their types, a few sample rows, the row count, the build date and the file sizes. Use this to answer questions about what a database contains without downloading it - the files reach several GB - and to budget a transfer before starting one.database_checksumThe published digests for one database file, for verifying a copy you already hold or deciding whether a build has changed since you last fetched it. It is licensed like the download itself, so a database this key cannot download is refused here too. Looking up a checksum is not a download and does not appear in `list_downloads`.list_downloadsThis organization's own recent download attempts, newest first, REFUSALS INCLUDED - a denial carries the `outcome` and `http_status` that answer "it stopped working", which nothing else here can. Use it to explain a failing fetch, to confirm a transfer ran, or to check whether a request was theirs. This is a bounded WINDOW of at most 200 rows, so a database missing from the answer means it is not in this window - never that it was never downloaded.check_actionUse for what-if questions when you are not about to act. Dry-run a proposed action against the organization policies (POST /v1/policies/test, one case, stored policies). This skips the PII scan and records nothing: no security event and no audit entry. There is no content field and no DLP scan. The result has no event id and no finalDecision. decide_action is the call that records a decision. Dry runs are limited to 60 requests per minute per key. test_policies covers up to 200 cases in one call.decide_actionCall this before a consequential action with any tool or connector: sending an email or message, exporting or sharing data, deleting data, writing to production or financial records, payments, or external API writes. ALLOW: proceed. REVIEW: hold the action, tell the user it waits for approval in Vulnify, and check again with get_decision. BLOCK: skip the action and explain the reasons. Records a decision for a proposed action (POST /v1/events). This writes a security event and an audit entry, counts toward the organization quota, and may open a human review. The finalDecision field is the authoritative result. decision is what was stored and does not change when a review is resolved. finalDecision stays REVIEW until a human approves, denies, or the review expires. This tool does not approve the review. A REVIEW or BLOCK finalDecision means the action was not allowed. Optional content is scanned in memory and is not stored. An idempotencyKey makes a retry return the same event.get_decisionRead a recorded decision (GET /v1/events/{id}), including a REVIEW. finalDecision is REVIEW while the review is pending, then ALLOW after approval or BLOCK after denial or expiry. decision itself does not change. The finalDecision field is the authoritative result. waitMs polls until finalDecision is ALLOW or BLOCK, or until the bounded wait elapses (30 seconds maximum). waitTimedOut is true when the wait ended and finalDecision is still REVIEW. This does not approve or deny the review.list_policiesList the organization policies as code (GET /v1/policies). Returns apiVersion vulnify.io/v1 and policies sorted by name. Read-only. Any non-revoked API key of the organization may call it. Limited to 60 requests per minute per key.test_policiesEvaluate up to 200 policy cases without recording an event (POST /v1/policies/test). When policies is omitted, the enabled stored policies are used. When policies is set, those proposed policies are evaluated instead and nothing is written. This skips the PII scan: cases have no content field. pass is null when a case omits expect. One call covers up to 200 cases. Limited to 60 requests per minute per key.plan_policy_changesDiff policy creates, updates, and deletes without writing them (POST /v1/policies/apply). dryRun is forced to true and cannot be turned off. Nothing is stored and no audit entry is written. prune only marks missing policies for deletion in the plan. Requires an org-wide LIVE API key; a TEST key or an agent-bound key is refused with 403. This tool does not apply the plan.check_mcp_tool_callRecords a decision for a downstream MCP tool call (POST /v1/gateway/mcp). Vulnify maps the tool name to an action by substring and returns the same recorded decision as decide_action, including an id. This writes a security event and an audit entry. The tool arguments are not executed. The finalDecision field is the authoritative result. A REVIEW or BLOCK finalDecision means the downstream tool was not allowed. This server cannot approve the review. An idempotencyKey makes a retry return the same event.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"vpndetection": {
"command": "npx",
"args": [
"-y",
"vpndetection-mcp"
],
"env": {
"VPNDETECTION_API_KEY": "YOUR_VPNDETECTION_API_KEY_HERE",
"VPNDETECTION_BASE_URL": "YOUR_VPNDETECTION_BASE_URL_HERE"
}
}
}
}
{
"mcpServers": {
"vulnify": {
"command": "npx",
"args": [
"-y",
"@vulnify/mcp"
],
"env": {
"VULNIFY_API_KEY": "YOUR_VULNIFY_API_KEY_HERE"
}
}
}
}