Vulnify MCP
Official@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.
Sign in with Vulnify (OAuth 2.1) at https://mcp.vulnify.io/mcp. That hosted server speaks stateless Streamable HTTP. The local path is npx -y @vulnify/mcp with VULNIFY_API_KEY.
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 source of truth is docs.vulnify.io/mcp. The same steps are in docs/clients.md.
Sign in with Vulnify
Connect to https://mcp.vulnify.io/mcp. Leave API-key and OAuth client fields empty. The client receives HTTP 401 and:
WWW-Authenticate: Bearer realm="https://mcp.vulnify.io/mcp", resource_metadata="https://mcp.vulnify.io/.well-known/oauth-protected-resource/mcp", scope="decisions:read decisions:write policies:read policies:test"The body is {"error":"unauthorized","message":"Sign in with Vulnify, or send an API key as Authorization: Bearer or X-Vulnify-Key:."}. Protected-resource metadata lists the authorization server https://api.vulnify.io.
You choose an organization and allow the scopes the client requests:
Scope | Tools |
|
|
|
|
|
|
|
|
After you allow access, the hosted server offers those five tools. plan_policy_changes and check_mcp_tool_call are not in that list. They need an API key. A missing scope comes back as a tool error that names the scope. That error is not an ALLOW. Revoke the connection under Settings > Connected apps: app.vulnify.io/settings/connected-apps.
Claude
In Claude, open Settings > Connectors > Add custom connector. Set the URL to https://mcp.vulnify.io/mcp. Do not paste an API key. Add Vulnify in Claude opens that dialog with the name and URL filled in. Claude then starts Sign in with Vulnify.
Cursor
Install in Cursor adds the hosted server and starts sign-in.
The same server in .cursor/mcp.json, or in ~/.cursor/mcp.json, is the URL with no header:
{
"mcpServers": {
"vulnify": {
"url": "https://mcp.vulnify.io/mcp"
}
}
}Grok
Open grok.com/connectors, then New Connector > Custom. Name it Vulnify. Set the URL to https://mcp.vulnify.io/mcp. Leave the OAuth fields empty. Grok starts Sign in with Vulnify.
Claude Code
claude mcp add --transport http vulnify https://mcp.vulnify.io/mcpClaude Code then signs in. Do not pass an API key on this command.
API key or local npx
Use an API key when you need plan_policy_changes or check_mcp_tool_call, when the client cannot open a browser, or when the key has an IP allowlist. The local process needs Node.js 20 or newer and reads VULNIFY_API_KEY. It does not open the OAuth consent page.
npx -y @vulnify/mcpVULNIFY_API_KEY=vln_live_... npx -y @vulnify/mcpThe key is vln_live_... or vln_test_.... A TEST key stores sandbox events. Those stay out of the main dashboards.
On the hosted server, both headers still work. Send the key on every request:
Authorization: Bearer <key>
X-Vulnify-Key: <key>An API-key session lists all seven tools.
Cursor, hosted:
{
"mcpServers": {
"vulnify": {
"url": "https://mcp.vulnify.io/mcp",
"headers": {
"Authorization": "Bearer vln_live_..."
}
}
}
}Cursor, local:
{
"mcpServers": {
"vulnify": {
"command": "npx",
"args": ["-y", "@vulnify/mcp"],
"env": {
"VULNIFY_API_KEY": "vln_live_..."
}
}
}
}X-Vulnify-Key works in place of Authorization on the hosted server.
Claude Code with an API key:
claude mcp add --transport http vulnify https://mcp.vulnify.io/mcp --header "Authorization: Bearer vln_test_your_key"Prefer an environment variable over a key written into the shell history. For sign-in, use the Claude Code command with no header.
Claude Desktop can run the local process. On macOS the file is ~/Library/Application Support/Claude/claude_desktop_config.json. On Windows it is %APPDATA%\Claude\claude_desktop_config.json. Restart the app after saving.
{
"mcpServers": {
"vulnify": {
"command": "npx",
"args": ["-y", "@vulnify/mcp"],
"env": {
"VULNIFY_API_KEY": "vln_live_..."
}
}
}
}VS Code, .vscode/mcp.json, hosted:
{
"servers": {
"vulnify": {
"type": "http",
"url": "https://mcp.vulnify.io/mcp",
"headers": {
"Authorization": "Bearer ${input:vulnify-api-key}"
}
}
}
}VS Code, local:
{
"servers": {
"vulnify": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@vulnify/mcp"],
"env": {
"VULNIFY_API_KEY": "${input:vulnify-api-key}"
}
}
}
}Related MCP server: hejdar-mcp
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.
Tool | API | Records? | readOnlyHint | Notes |
|
| No | true | Skips the PII scan and records nothing. No event id. |
|
| Yes | false | Writes a security event and an audit entry. |
|
| No | true | Reads a decision. Optional wait polls a |
|
| No | true | Policies as code, sorted by name. |
|
| No | true | Up to 200 cases. Skips the PII scan. Proposed |
|
| No | true |
|
|
| Yes | false | Maps a tool name to an action and records the decision. Does not run the tool. |
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:
Tool | Scope | OAuth route |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| API key and stdio only | Omitted from OAuth |
| API key and stdio only | Omitted from OAuth |
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
An API key can list allowed source IPs. Calls through https://mcp.vulnify.io/mcp with that key come from the hosted server's egress address, not from the machine where Cursor or VS Code is running. An allowlist that only contains an office or laptop address rejects the hosted server. Use npx -y @vulnify/mcp on a host whose address is on the list. Sign in with Vulnify does not send that API key.
Product
The screenshot is from a demo workspace. The agent is SupportBot.
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
Variable | Where | Required | Purpose |
| stdio process | yes | API key ( |
| both | no | API base URL. Default |
| HTTP | for user login | Bearer secret for |
| HTTP | no | When set, |
| HTTP | no | Listen port. Default |
| HTTP | no | Bind address. Default |
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
npx -y @vulnify/mcp --httpThe container runs the HTTP server as a non-root user:
docker build -t vulnify-mcp .
docker run --rm -p 8080:8080 -e PORT=8080 vulnify-mcpNo API key is required to start the container. Send the key on each request.
curl -sS localhost:8080/health
curl -sS -o /dev/null -w '%{http_code}\n' -X POST localhost:8080/mcp \
-H 'content-type: application/json' -d '{}'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.
curl -sS https://mcp.vulnify.io/.well-known/mcp/server-card.jsonErrors
Tool failures are MCP tool errors (isError). The API key is not included.
Status | Meaning |
401 | The API key or access token is missing, invalid, revoked, or expired. |
403 | The key is bound to another agent, the caller IP is not allowlisted, the access token lacks a scope, or |
413 | The JSON body is over the API's 200 KB limit. |
429 | The key is rate limited. Policy calls are 60 per minute. Retry a recorded decision with the same |
Security
Report a vulnerability to security@vulnify.io. 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 install
npm test
npm run lint
npm run buildnpm 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.
Available Tools
7 toolscheck_actionCheck actionARead-onlyIdempotentInspect
Use 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.
| Name | Required | Description | Default |
|---|---|---|---|
| agent | Yes | Agent name. The dry run does not accept agentId. | |
| action | Yes | Event action. Allowed values: READ_DATA, WRITE_DATA, DELETE_DATA, EXPORT_DATA, SEND_EMAIL. | |
| resource | Yes | Resource name. The dry run does not accept resourceId. | |
| destination | No | Where the data goes. Allowed values: INTERNAL, EXTERNAL_EMAIL, EXTERNAL_API. | |
| recordsAffected | No | How many records the action would touch. Optional. 0 to 100000000. | |
| containsSensitiveData | No | Hint for policy conditions. This call does not scan content. |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | One result per case. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnly/idempotent/non-destructive, and the description goes well beyond them: it discloses that this skips the PII scan, records no security event and no audit entry, performs no DLP scan, has no content field, and returns no event id or finalDecision, plus a concrete rate limit of 60 requests/minute/key. That is exactly the side-effect and constraint context an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence is front-loaded with actionable information and none is filler: usage condition, endpoint, side-effect profile, sibling routing, rate limit. Dense but each clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a dry-run tool with an output schema present, the description covers everything the agent needs: trigger condition, scope of the check, side effects, rate limit, and sibling alternatives. Return-value shape is intentionally signaled ('no event id and no finalDecision') rather than fully specified, which is appropriate given the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all six parameters (including both enums) are already fully documented in the schema, which sets the baseline at 3. The description adds only marginal parameter meaning by noting the absence of a content field and clarifying that no DLP scan occurs, which largely restates the schema's own note on containsSensitiveData.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb+resource ('Dry-run a proposed action against the organization policies') with the exact endpoint, and explicitly differentiates itself from siblings decide_action (records a decision) and test_policies (batch up to 200 cases). An agent can select this tool without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It opens with the triggering condition ('Use for what-if questions when you are not about to act') and names the alternative that does record ('decide_action is the call that records a decision'), plus routes batch needs to test_policies. When-to-use, when-not-to-use, and alternatives are all present.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_mcp_tool_callCheck MCP tool callAInspect
Records 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.
| Name | Required | Description | Default |
|---|---|---|---|
| tool | Yes | Downstream MCP tool name. Vulnify maps it to an action by substring. The tool is not executed. | |
| agent | No | Agent name. Use this or agentId. A registered MCP route can supply a default agent. | |
| agentId | No | Agent id (UUID). Use this or agent. | |
| content | No | Optional text scanned for sensitive data. Scanned in memory and not stored. This server does not log it. | |
| routeId | No | Registered MCP route. Supplies a default agent. | |
| resource | No | Resource name registered in Vulnify. Use this or resourceId. | |
| arguments | No | Tool arguments. Not executed by Vulnify. | |
| resourceId | No | Resource id (UUID). Use this or resource. | |
| destination | No | Where the data goes. Allowed values: INTERNAL, EXTERNAL_EMAIL, EXTERNAL_API. | |
| idempotencyKey | No | Retries with the same key return the stored decision for 24 hours instead of recording another event. | |
| recordsAffected | No | How many records the tool call would touch. Optional. 0 to 100000000. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Decision id. |
| finalDecision | Yes | Authoritative result. Allowed values: ALLOW, REVIEW, BLOCK. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only carry the readOnly/destructive/idempotent/openWorld hints, and the description adds substantial context beyond them: it writes a security event and an audit entry, the tool arguments are never executed, finalDecision is the authoritative output, REVIEW/BLOCK means the downstream tool was denied, the review cannot be approved here, and idempotencyKey governs retry behavior. This is exactly the side-effect and outcome disclosure a security-gate tool needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and delivered in short, information-dense sentences with no filler. It reads as a flat list of facts rather than organized prose, but each sentence carries distinct, useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be enumerated, and the description still flags the critical output field (finalDecision is authoritative) plus the meaning of REVIEW/BLOCK. For a write-bearing security gate with 11 parameters it covers the important behaviors, though it could be clearer about how a caller should act on an ALLOW versus a REVIEW outcome.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all 11 parameters (substring mapping, agent/agentId alternatives, content not stored, idempotencyKey 24h window, etc.). The description largely restates that material ('The tool arguments are not executed', the idempotencyKey behavior) rather than adding new parameter meaning, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Records a decision for a downstream MCP tool call') and pins it to an endpoint, so the agent knows exactly what operation it is. It names the sibling decide_action and explains the relationship ('returns the same recorded decision as decide_action'), which helps differentiate, though it never states plainly why one would pick this over decide_action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: the tool is scoped to downstream MCP tool calls that Vulnify maps by substring, which hints at the MCP-specific case versus the generic check_action/decide_action. There is one useful when-not signal ('This server cannot approve the review'), but no explicit 'use this instead of X when Y' routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
decide_actionDecide actionAInspect
Call 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.
| Name | Required | Description | Default |
|---|---|---|---|
| agent | No | Agent name. Omit only when the API key is bound to an agent. Use this or agentId. | |
| action | Yes | Event action. Allowed values: READ_DATA, WRITE_DATA, DELETE_DATA, EXPORT_DATA, SEND_EMAIL. | |
| agentId | No | Agent id. Use this or agent. | |
| content | No | Optional text scanned for sensitive data. Scanned in memory by Vulnify and not stored. This server does not log it. | |
| resource | No | Resource name. Use this or resourceId. | |
| resourceId | No | Resource id. Use this or resource. | |
| destination | No | Where the data goes. Allowed values: INTERNAL, EXTERNAL_EMAIL, EXTERNAL_API. | |
| idempotencyKey | No | Retries with the same key return the stored decision for 24 hours instead of recording another event. | |
| recordsAffected | No | How many records the action would touch. Optional. 0 to 100000000. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Decision id. |
| finalDecision | Yes | Authoritative result. Allowed values: ALLOW, REVIEW, BLOCK. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations only covering broad hints, the description carries the load and does so richly: it writes a security event and audit entry, consumes organization quota, may trigger human review, does not itself approve reviews, and content is scanned in memory and not stored. The idempotencyKey explanation (same key returns stored decision for 24h) refines rather than contradicts idempotentHint=false, which describes default behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the trigger condition and the outcome-to-action mapping, which is the highest-value content. It is dense and somewhat long, but nearly every sentence (finalDecision vs decision, REVIEW persistence, idempotency) adds decision-relevant information rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a consequential gating tool with an output schema present, the description still explains the return semantics (finalDecision is authoritative; decision does not change on resolution; REVIEW/BLOCK mean the action was not allowed). Combined with full schema coverage, nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all 9 parameters, including the enum values and idempotencyKey behavior. The description's mentions of content scanning and idempotency largely restate schema text, so this is correctly a baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific trigger ('Call this before a consequential action with any tool or connector') and states the resource is a recorded decision for a proposed action (POST /v1/events). An agent can distinguish it from siblings like get_decision and check_action without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly enumerates when to call it (sending email/messages, exporting/sharing data, deleting, production/financial writes, payments, external API writes) and prescribes per-outcome behavior for ALLOW, REVIEW, and BLOCK, including the follow-up call to get_decision. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_decisionGet decisionARead-onlyIdempotentInspect
Read 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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Decision id returned by decide_action or check_mcp_tool_call. | |
| waitMs | No | When finalDecision is REVIEW, the server polls until finalDecision is ALLOW or BLOCK, or until this many milliseconds pass. Maximum 30000. 0, or omitting the field, returns the current decision immediately. | |
| pollIntervalMs | No | Milliseconds between polls. Default 1000. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | Decision id. |
| waitTimedOut | Yes | True when the wait elapsed and finalDecision is still REVIEW. |
| finalDecision | Yes | Authoritative result. Allowed values: ALLOW, REVIEW, BLOCK. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safe-read profile (readOnly, idempotent, non-destructive), and the description adds genuine behavioral context: the state transition from REVIEW to ALLOW/BLOCK, that decision is immutable while finalDecision is authoritative, and the 30-second bounded wait. It omits error/not-found behavior, but the added semantics are substantial.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the verb, resource, and endpoint, then spends its remaining sentences on the semantics an agent actually needs (state transitions, polling bound, timeout flag). No filler or restated name/title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations carrying the safety profile, a 100%-covered schema, and an output schema, the description only needs to supply the non-obvious semantics, and it does: finalDecision authority, polling termination conditions, and the non-mutating boundary.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so id, waitMs, and pollIntervalMs are already fully documented with constraints. The description paraphrases waitMs polling behavior but adds no syntax or format detail beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read a recorded decision (GET /v1/events/{id})') and clarifies the scope includes a REVIEW, with the explicit boundary 'This does not approve or deny the review' separating it from decide_action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains clearly when to supply waitMs (while finalDecision is REVIEW, to poll until ALLOW/BLOCK or timeout) and what waitTimedOut means, plus when not to use it. It never names decide_action as the alternative for resolving a review, so the routing guidance is one step short of explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_policiesList policiesARead-onlyIdempotentInspect
List 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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| policies | Yes | Stored policies sorted by name. |
| apiVersion | Yes | Always vulnify.io/v1. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, idempotentHint, destructiveHint=false), yet the description adds genuinely new context: a 60 requests/minute/key rate limit, the auth requirement (any non-revoked API key), and the sort order of results. It does not describe error or pagination behavior, so it falls short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with purpose and endpoint, then behavior and constraints. Every sentence carries distinct information (endpoint, return shape, auth, rate limit) with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless read tool with an output schema and full annotation coverage, the definition supplies everything an agent needs: endpoint, authenticated-access rule, rate limit, and result ordering. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate; the schema is empty and fully covered. Baseline 4 applies for a no-parameter tool.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the organization policies as code') and pins the underlying endpoint (GET /v1/policies), which clearly separates it from mutating siblings like plan_policy_changes. It stops short of naming an alternative sibling or stating when this read is preferred over test_policies.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the read-only list semantics and the note that any non-revoked API key may call it, which tells the agent it needs no special scope. There is no explicit when-to-use/when-not-to-use statement or routing to a sibling such as test_policies or plan_policy_changes.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
plan_policy_changesPlan policy changesARead-onlyIdempotentInspect
Diff 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.
| Name | Required | Description | Default |
|---|---|---|---|
| prune | No | When true, the plan marks stored policies whose names are missing from policies for deletion. Nothing is deleted. | |
| policies | Yes | Policies to diff against the stored set. Each item needs name, action, and decision. Nothing is written. |
Output Schema
| Name | Required | Description |
|---|---|---|
| dryRun | Yes | Always true. The plan is not applied. |
| changes | Yes | Creates, updates, deletes, and unchanged names. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: dryRun is forced true and non-negotiable, nothing is stored, no audit entry is written, prune only marks deletions in the plan, and an org-wide LIVE key is required (TEST or agent-bound keys get a 403). This is exactly the operational context an agent needs before calling a policy tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Six short sentences, all front-loaded with the highest-value fact first (no writes) and none wasted. The endpoint, side-effect, parameter note, and auth constraint each earn their place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only preview tool with an output schema present, the description covers everything left unspecified: safety profile, side effects, parameter caveats, and authentication requirements. An agent has no unanswered question that would cause a wrong call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real value by clarifying prune's narrow effect and by stating that dryRun is forced and not a user-controllable input. It does not, however, add anything about the nested policy object beyond what the schema already documents.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (diff) and precise resource (policy creates, updates, and deletes), and even names the underlying endpoint POST /v1/policies/apply. The phrase 'without writing them' instantly separates it from a mutation tool and from siblings like test_policies or list_policies.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly frames the tool as a preview step ('This tool does not apply the plan'), which tells the agent when it is appropriate. It does not name the sibling that performs the actual apply, so the routing is implied rather than explicit; the auth conditions also function as usage preconditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
test_policiesTest policiesARead-onlyIdempotentInspect
Evaluate 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.
| Name | Required | Description | Default |
|---|---|---|---|
| cases | Yes | One to 200 cases. Each case has a name, an input, and an optional expected decision. | |
| policies | No | When set, these policies are evaluated instead of the stored ones. Nothing is written. |
Output Schema
| Name | Required | Description |
|---|---|---|
| results | Yes | One result per case. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, yet the description adds substantive behavior: nothing is written when 'policies' is set, the PII scan is skipped, 'pass' is null when 'expect' is omitted, and a concrete rate limit of 60 requests/minute/key. These are exactly the traits an agent cannot infer from structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Short sentences with the purpose and endpoint front-loaded and the rate limit sensibly at the end. Mild redundancy: 'up to 200 policy cases' in sentence one is repeated as 'One call covers up to 200 cases' later, and that limit is also in the schema (maxItems 200).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With rich annotations and an output schema present, the description still covers the pieces an agent needs: two operating modes, the no-write guarantee, the skipped scan, nullable results, and throughput/rate limits. Nothing required to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description restates the omitted-vs-set semantics of 'policies' that the schema already documents, adding little new meaning beyond what the structured field provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Evaluate ... policy cases') plus the endpoint and scope ('up to 200'), and the dry-run character ('without recording an event') sets it apart behaviorally from write-oriented siblings. It never names an alternative (check_action, decide_action) to anchor the distinction, so it stops short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear conditional context: omitting 'policies' uses stored policies, setting it evaluates proposed ones. It also discloses a usable limitation ('skips the PII scan: cases have no content field'). No explicit when-not or sibling routing, so 4 rather than 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v0.1.5- First observed
check_action - First observed
check_mcp_tool_call - First observed
decide_action - First observed
get_decision - First observed
list_policies - First observed
plan_policy_changes - First observed
test_policies
TDQS
Scored across 7 tools
Several tools share the same underlying endpoint and near-identical semantics: check_action and test_policies both hit POST /v1/policies/test as dry-runs, and check_mcp_tool_call vs decide_action both record decisions (one keyed to MCP tool names, one to general actions). The descriptions do a good job cross-referencing each other to steer selection, but the boundaries are genuinely blurry in places.
Names follow a mostly predictable verb_noun pattern (list_policies, test_policies, check_action, decide_action, get_decision). Minor deviations exist—plan_policy_changes uses a longer noun phrase and plural/singular usage is inconsistent (list_policies vs check_action)—but the convention is readable and consistent overall.
Seven tools is well-scoped for a policy governance and decision-recording server. Each tool maps to a distinct capability (list, test, plan, check, decide, get) without redundancy that would bloat the surface.
The read/test/decide/get lifecycle is covered, but there are notable gaps: plan_policy_changes only produces a plan with no corresponding apply tool, and no tool can approve or deny a review (explicitly noted as out of scope). Agents hitting a REVIEW have no resolution path within this server.
Maintenance
Related MCP Connectors
- EnokiOAuthai.enokilabs
Test your AI agents for security and safety weaknesses, and re-verify the fixes.
Deterministic allow/require_approval/deny verdicts for agent actions, before they happen.
Security gateway for AI agents: policy, approval, and audited execution, no secrets shared.
Pay-per-call cybersecurity for AI agents: vuln scans, threat intel, compliance, code security.
Related MCP Servers
- AlicenseAqualityBmaintenanceSecurity co-pilot for AI agents. Scans for vulnerabilities like prompt injection, infinite loops, and token bombing in AI Agents, audits MCP servers, verifies AGENTS.md governance, and generates EU AI Act compliance reports.1057 npm3Apache 2.0
- AlicenseAqualityCmaintenanceRuntime policy enforcement for AI agents. Evaluate every agent action against your organization's policies before execution, with observe and enforce modes.11MIT

Vaikora Guard MCPofficial
AlicenseNot gradedqualityDmaintenanceEnforces deterministic policies on AI agent tool calls, evaluating actions against compliance modules (SOC 2, HIPAA, GDPR, etc.) and returning ALLOW, BLOCK, or CONSTRAIN decisions with an audit trail.MIT- AlicenseAqualityFmaintenanceEnables agentic coding clients to enforce portable policy guardrails by evaluating proposed shell, file write, git, and network actions and returning allow, deny, or ask decisions.2MIT