Vercel AI SDK
guardAiSdkTools wraps the tools you pass to generateText or streamText. It ships in @vulnify/sdk and does not depend on the ai package. This page matches @vulnify/sdk 0.3.3 and the Vercel AI SDK v7.
npm install @vulnify/sdk ai zodSet VULNIFY_API_KEY to an API key from the app. Pass that value to the client. The constructor does not read the environment itself.
const vulnify = new Vulnify({ apiKey: process.env.VULNIFY_API_KEY! });Client options, guard(), and the default fail-closed behavior are on Node.js. The same helper is listed with the other guards on Framework adapters.
Source: github.com/vulnify/vulnify-sdk. Package: npmjs.com/package/@vulnify/sdk.
Example
Section titled “Example”Only tools named in the third argument are checked. searchTickets is not listed, so it runs unchanged. deleteTicket is checked as DELETE_DATA on Support Tickets. On REVIEW, the call waits up to 2 minutes for a person to approve.
import { generateText, isStepCount, tool } from 'ai';import { guardAiSdkTools, Vulnify } from '@vulnify/sdk';import { z } from 'zod';
const vulnify = new Vulnify({ apiKey: process.env.VULNIFY_API_KEY! });
// Only tools listed in the third argument are checked; the rest run unchanged.const tools = guardAiSdkTools( vulnify, { searchTickets: tool({ description: 'Search support tickets', inputSchema: z.object({ query: z.string() }), execute: async ({ query }) => ({ results: [`Ticket about ${query}`] }), }), deleteTicket: tool({ description: 'Delete a support ticket', inputSchema: z.object({ ticketId: z.string() }), execute: async ({ ticketId }) => ({ deleted: ticketId }), }), }, { // Vulnify returns ALLOW, REVIEW or BLOCK before execute runs. deleteTicket: () => ({ agent: 'SupportBot', action: 'DELETE_DATA', resource: 'Support Tickets', recordsAffected: 1, }), }, // On REVIEW, wait up to 2 minutes for a person to approve. { wait: { timeoutMs: 120_000 } },);
const { text } = await generateText({ model: 'openai/gpt-5-mini', tools, stopWhen: isStepCount(5), prompt: 'Find the duplicate ticket about login errors and delete it.',});
console.log(text);isStepCount and stopWhen are the AI SDK v7 stop condition. inputSchema is the tool schema that version expects.
ALLOW, REVIEW, and BLOCK
Section titled “ALLOW, REVIEW, and BLOCK”Vulnify answers before execute runs. The answer is ALLOW, REVIEW, or BLOCK, with a 0-100 risk score and the reasons.
| Outcome | What happens |
|---|---|
ALLOW |
execute runs. Its return value is the tool output. |
REVIEW |
The call waits up to wait.timeoutMs for a person to approve in the Vulnify app or in Slack. execute runs only after that approval. |
BLOCK |
execute does not run. The model receives { blocked: true, decision, reasons, eventId, message }. |
On BLOCK, decision is "BLOCK". reasons are the strings Vulnify returned. eventId is the event id. message is the block text, in the form Vulnify BLOCK: …. The model can tell the user why the tool did not run, and generateText continues.
If a review is denied, expires, or the wait times out, execute does not run either. The model receives the same object. reasons then also include Review denied, Review expired, or Review timeout. Without wait, a REVIEW is not allowed and is returned the same way.
Approval is in the Vulnify app or from the Slack alert. Slack setup is on Slack.
onBlocked defaults to 'result', which is the object above. Pass 'throw' to rethrow VulnifyBlockedError instead.
Fail closed
Section titled “Fail closed”failMode defaults to 'closed'. Timeouts, network errors, 408, 429, and 5xx do not run execute. The model receives the same blocked object, with decision "BLOCK", eventId null, and a reason that Vulnify was unavailable. A rejected request, including a bad API key, still throws. See Node.js.

