Vulnify
io.vulnifyv0.1.2Updated Oct 10, 2026
Ask Vulnify before an AI agent acts. Check, record, and read policy decisions.
Overview
Lets an assistant ask Vulnify for an authorization decision before it reads, writes, deletes, exports, or sends data.
- What it does
- Vulnify is a runtime authorization layer for AI agents: the agent asks first, and the server returns a finalDecision of ALLOW, REVIEW, or BLOCK. Tools include check_action (a dry run that records nothing), decide_action (records a real security event and audit entry), get_decision (reads a decision, optionally waiting up to 30 seconds for a REVIEW to resolve), list_policies, test_policies (up to 200 cases), plan_policy_changes (always a dry run that returns a diff), and check_mcp_tool_call (maps a tool name to an action and records the decision without running the tool).
- When to use it
- Use it when an agent should be gated by policy before acting on data, and when you want decisions recorded for audit. It suits teams that already use Vulnify policies, or that want a review step where a person resolves REVIEW cases.
- Requirements
- Either the hosted endpoint at mcp.vulnify.io/mcp with Vulnify sign-in (OAuth) or an API key sent in a header, or the local npm package @vulnify/mcp run with npx on Node.js 20 or newer. The stdio process requires VULNIFY_API_KEY; VULNIFY_BASE_URL is optional and defaults to the Vulnify API. Network access to the Vulnify API is needed for every tool call.
Installation
In SourceWeft
- Open Vulnify in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.
Other MCP clients
Add this to your client's mcpServers config.
{
"mcpServers": {
"mcp": {
"type": "http",
"url": "https://mcp.vulnify.io/mcp"
}
}
}README
Vulnify MCP
Ask Vulnify before an AI agent reads, writes, deletes, exports, or sends data.
finalDecision is the authoritative result: ALLOW, REVIEW, or BLOCK.
[npm version] [npm downloads] [license] [CI]
Docs · MCP · Changelog · Node SDK
[Every agent action gets a decision. ALLOW, REVIEW, or BLOCK.]
@vulnify/mcp is the MCP server for Vulnify, a runtime authorization layer for AI agents. The agent asks first. This process proxies https://api.vulnify.io. It does not store events or API keys.
Two transports, one server:
- stdio for a local client:
npx -y @vulnify/mcp - stateless Streamable HTTP at
https://mcp.vulnify.io/mcp
check_action is a dry run. It skips the PII scan and records nothing. decide_action records a real decision. A REVIEW waits for a person. This server cannot approve or deny one.
Application code that should decide inside your own process uses the Node SDK (@vulnify/sdk). This package does not depend on the SDK. The SDK turns some transport failures into a fail-closed decision. This server returns those failures as MCP tool errors, so a model does not read them as ALLOW.
Quickstart
The hosted guide is docs.vulnify.io/mcp. Client snippets for Cursor, Claude Code, Claude Desktop, and VS Code are in docs/clients.md.
Hosted server
https://mcp.vulnify.io/mcp keeps no session. Sign in with Vulnify.
Claude custom connector: add https://mcp.vulnify.io/mcp. Claude follows the 401 challenge, opens Vulnify sign-in, and stops on the consent page until you allow access. The steps are in docs/clients.md.
Cursor: Install Vulnify.
An OAuth session lists check_action, decide_action, get_decision, list_policies, and test_policies.
API key
A manual config can still send an API key on every request. Use a dedicated TEST key (vln_test_...). This works for Cursor, Claude Code, VS Code, and any client that can set a header:
X-Vulnify-Key: <key> is accepted for vln_live_... and vln_test_... keys. A request with no credentials gets 401 and:
GET /health does not require a key. GET /.well-known/oauth-protected-resource and GET /.well-known/oauth-protected-resource/mcp return the protected-resource document (resource https://mcp.vulnify.io/mcp, authorization server https://api.vulnify.io, the four scopes above, bearer header). Responses are JSON.
A vln_oat_... access token is checked with POST /oauth/introspect, then sent to /v1/mcp/*. check_action and test_policies need policies:test. decide_action needs decisions:write. get_decision needs decisions:read. list_policies needs policies:read. plan_policy_changes and check_mcp_tool_call stay on API-key and stdio sessions: an OAuth tools/list does not include them, because the API has no /v1/mcp route for policy apply or the MCP gateway. There is no approve tool and no deny tool.
A vln_ort_... refresh token, a vln_oac_... credential, or any other unrecognized secret receives 401 and WWW-Authenticate with error="invalid_token". A vln_live_... or vln_test_... key is checked with GET /v1/policies before initialize and tools/list. A key the API rejects receives that same 401. A successful check is reused for at most 60 seconds. The cache key is a hash of the API key.
Local stdio
Node.js 20 or newer.
Cursor (.cursor/mcp.json):
How it works
The finalDecision field is the authoritative result. A tool error is not an ALLOW.
Tools
Every tool has a title and readOnlyHint, destructiveHint, idempotentHint, and openWorldHint. Hints tell the client how to present the tool. They do not change what the tool does.
destructiveHint is false on every tool. openWorldHint is false on every tool, because each call stays inside the caller's own Vulnify organization. idempotentHint is true for the read-only tools and false for decide_action and check_mcp_tool_call. An idempotencyKey makes a retry of a recorded decision return the same event.
Policy list, test, and plan calls are limited to 60 requests per minute per key. Batch dry runs with test_policies. POST /v1/events has its own limit. The OpenAPI text says only that the limit was exceeded.
Safety
This server cannot change a review and cannot save a policy.
- There is no approve tool and no deny tool. A human resolves a
REVIEWin Vulnify. - There is no apply tool.
plan_policy_changescallsPOST /v1/policies/applywithdryRun: trueon every request. The tool input has nodryRunfield. The server setsdryRunlast, so a caller cannot turn the dry run off. The tool returns the diff. POST /v1/gateway/httpis not a tool. OnALLOW, that API endpoint forwards the HTTP call. This server does not.
plan_policy_changes needs an org-wide LIVE key. A TEST key or an agent-bound key gets 403. An OAuth session does not list plan_policy_changes or check_mcp_tool_call.
OAuth scopes
User login is the hosted HTTP server. The local stdio process still uses an API key. The hosted server maps tools to scopes:
A token that is missing the scope gets a tool error that names the scope. That error is not an ALLOW. Active introspection results are reused for at most 60 seconds, and never past exp.
Distinct from the MCP gateway
https://mcp.vulnify.io/mcp (and the local stdio process) is the server an MCP client connects to. The client then calls the tools above.
check_mcp_tool_call is different. It sends one tool call to POST /v1/gateway/mcp on api.vulnify.io, records the decision, and stops. It does not execute the tool, and connecting a client to this server does not call that gateway.
IP allowlists
API-key IP allowlists are checked against the hosted server's egress IP when using https://mcp.vulnify.io/mcp, not the end user's IP. The hosted process calls api.vulnify.io from its own address. Users who need an IP allowlist should run the local npx/stdio mode (npx -y @vulnify/mcp). stdio runs on the user's machine, so the allowlist sees that machine.
Product
The screenshot is from a demo workspace. The agent is SupportBot.
[An allowed read: SupportBot, one record, internal destination, low risk.]
Audit exports in the Vulnify app are a JSON or CSV download that includes a SHA-256 checksum. SIEM export is JSON or CEF lines.
Configuration
Use an agent-bound LIVE key when a recorded decision should count for one agent. plan_policy_changes needs an org-wide LIVE key.
VULNIFY_TEST_API_KEY is only for npm run test:live. The server does not read it.
HTTP server
The container runs the HTTP server as a non-root user:
No API key is required to start the container. Send the key on each request.
The health check prints JSON. The POST without a key prints 401.
GET /.well-known/mcp/server-card.json does not require a key. It lists the tools the server registers (names, descriptions, and input schemas), states that you can sign in with OAuth or send a Vulnify API key in a header, and links to the docs and this repository.
Errors
Tool failures are MCP tool errors (isError). The API key is not included.
Security
Report a vulnerability to [email protected]. The disclosure policy is at vulnify.io/disclosure. See SECURITY.md.
Keys are not logged. Request logs are method, path, and status. content sent for a sensitive-data scan is forwarded to the API and is not written to this server's logs. The API scans that text in memory and does not store it.
This package does not run a shell and does not register hooks. Do not put a key in a tool argument or in a committed config file.
Network endpoints this process uses:
https://api.vulnify.io(orVULNIFY_BASE_URL) for every tool call, and forGET /v1/policieswhen an API key is checked. An API key is sent asAuthorization: Bearerto/v1/*. A user access token is sent asAuthorization: Bearerto/v1/mcp/*.POST /oauth/introspecton that same origin, withAuthorization: Bearerset toOAUTH_INTROSPECTION_SECRET, to validatevln_oat_...tokens. The secret is not sent to clients and is not logged.https://mcp.vulnify.io/mcpis the hosted HTTP transport. The server process does not call that URL.
Development
npm run test:live calls list_policies on https://api.vulnify.io when VULNIFY_TEST_API_KEY is set, and skips when it is not. It does not record an event.
Publishing is described in RELEASING.md.
License
MIT. Copyright 2026 Vulnify.
Source: README.md at commit cf0e0ac
Tools
0Version history
1- v0.1.2LatestOct 10, 2026
