# bindHarness
Source: https://docs.verifiedx.me/api-reference/bind-harness
Bind the VerifiedX runtime to your TypeScript harness.
Use `vx.bindHarness(...)` after `await initVerifiedX()` to promote your own methods into named VerifiedX surfaces.
## Basic usage
```typescript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
const harness = {
async callModel(messages) {
return { messages };
},
async lookupInternalWorkflow({ workflowId }) {
return { workflowId, status: "legal_complete" };
},
async setWorkflowStatus({ workflowId, status }) {
return { ok: true, workflowId, status };
},
};
const vx = await initVerifiedX();
vx.bindHarness(harness, {
llm: {
callModel: "gpt-5.4-mini",
},
retrievals: {
lookupInternalWorkflow: "internal workflow evidence",
},
actions: {
setWorkflowStatus: "set_workflow_status",
},
});
```
This is the raw TypeScript path. You keep your existing harness and declare which methods should be treated as LLM calls, retrievals, protected actions, durable memory writes, or general tools.
## Parameters
The object whose methods you want VerifiedX to wrap in place.
Optional LLM method bindings.
You can use either:
* string shorthand: `{ callModel: "gpt-5.4-mini" }`
* object form: `{ callModel: { modelName: "gpt-5.4-mini" } }`
Optional retrieval bindings for reads and lookups that should contribute decision context.
You can use:
* string shorthand: `{ lookupInternalWorkflow: "internal workflow evidence" }`
* tuple-style shorthand: `{ searchPublicWeb: ["public web context", "external_retrieval"] }`
* object form with `query` and optional `objectType`
Optional high-impact business method bindings such as record mutations, workflow updates, or message sends.
You can use either:
* string shorthand: `{ setWorkflowStatus: "set_workflow_status" }`
* object form: `{ setWorkflowStatus: { toolName: "set_workflow_status" } }`
You can also attach optional `schema` and `docstring` metadata.
Optional durable memory-write bindings.
You can use either:
* string shorthand: `{ rememberCustomerPreference: "remember_customer_preference" }`
* object form: `{ rememberCustomerPreference: { toolName: "remember_customer_preference" } }`
You can also attach optional `schema` and `docstring` metadata.
Optional general tool bindings for helper methods and tool history.
If a method is definitely a durable memory write or another high-impact effect, prefer `memories` or `actions`.
## What it does
`bindHarness(...)` wraps the methods you name and turns them into first-class VerifiedX surfaces.
That means you can give your own business methods clearer names and better receipts, instead of relying only on lower-seam runtime capture.
High-impact methods bound under `actions` are protected before they execute. Durable writes bound under `memories` are protected as memory writes.
`initVerifiedX()` already installs lower-seam runtime fallbacks by default. `bindHarness(...)` is how you add cleaner business-level boundaries on top of that runtime capture.
## Return value
Returns the same VerifiedX runtime object, so you can keep using it after binding.
## Example
```typescript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
const harness = {
async callModel(messages) {
return { messages };
},
async lookupInternalWorkflow({ workflowId }) {
return { workflowId, status: "legal_complete" };
},
async setWorkflowStatus({ workflowId, status }) {
return { ok: true, workflowId, status };
},
async rememberCustomerPreference({ customerId, value }) {
return { ok: true, customerId, value };
},
};
const vx = await initVerifiedX();
vx.bindHarness(harness, {
llm: {
callModel: "gpt-5.4-mini",
},
retrievals: {
lookupInternalWorkflow: "internal workflow evidence",
},
actions: {
setWorkflowStatus: {
toolName: "set_workflow_status",
docstring: "Update an internal workflow record.",
},
},
memories: {
rememberCustomerPreference: {
toolName: "remember_customer_preference",
},
},
});
```
In this example, VerifiedX will:
* observe `callModel` as an LLM call
* record `lookupInternalWorkflow` as retrieval context
* protect `setWorkflowStatus` as a high-impact boundary
* protect `rememberCustomerPreference` as a durable memory write
# Initialization
Source: https://docs.verifiedx.me/api-reference/init
Initialize the VerifiedX runtime in Python or TypeScript.
Most builders initialize VerifiedX once, then use the returned runtime object with the raw runtime or a native adapter.
## Basic usage
```python Python theme={null}
from verifiedx import init_verifiedx
vx = init_verifiedx()
```
```typescript TypeScript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
const vx = await initVerifiedX();
```
## What you do next
After initialization, use the returned runtime object with the integration surface you chose.
```python Python theme={null}
vx.install_runtime(node, ...)
```
```typescript TypeScript theme={null}
vx.bindHarness(harness, ...)
```
If you are using a native adapter such as OpenAI, Anthropic, Claude Agent SDK, LangGraph, Vercel AI SDK, or MCP, initialize VerifiedX once and then pass it into that adapter.
## Required configuration
For normal SDK usage, **`VERIFIEDX_API_KEY` is the only required environment variable**.
```ini .env theme={null}
VERIFIEDX_API_KEY=your_runtime_key
```
Both SDKs read `VERIFIEDX_API_KEY` automatically. Most builders do not need any additional initialization config.
## Python
Use `init_verifiedx()` when you are working in Python.
* Returns a VerifiedX runtime object
* Synchronous
* Usually followed by `install_runtime(...)` or a native Python adapter
```python theme={null}
from verifiedx import init_verifiedx
vx = init_verifiedx()
```
## TypeScript
Use `initVerifiedX()` when you are working in TypeScript.
* Returns a Promise resolving to a VerifiedX runtime object
* Async
* Usually followed by `bindHarness(...)` or a native TypeScript adapter
```typescript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
const vx = await initVerifiedX();
```
`initVerifiedX()` is async. Always `await` it before using the returned runtime object.
## Advanced usage
Both SDKs also support advanced initialization in code for custom transports and specialized setups, but most builders should ignore that and use the default environment-variable path.
The normal public integration pattern is:
1. Set `VERIFIEDX_API_KEY`
2. Initialize VerifiedX once
3. Bind the raw runtime or attach the native adapter
# install_runtime
Source: https://docs.verifiedx.me/api-reference/install-runtime
Install the Python runtime and optionally bind your harness in one call.
Use `vx.install_runtime(...)` for the one-call raw Python path.
It does two things:
1. Installs the Python runtime capture layer
2. If you pass a target object, binds your named methods as VerifiedX surfaces in the same call
## Basic usage
```python theme={null}
from verifiedx import init_verifiedx
vx = init_verifiedx()
vx.install_runtime(
node,
llm={"call_model": "gpt-5.4-mini"},
retrievals={"lookup_internal_workflow": "internal workflow evidence"},
actions={"set_workflow_status": "set_workflow_status"},
memories={"remember_customer_preference": "remember_customer_preference"},
)
```
This is the simplest raw Python integration. You keep your existing harness and declare which methods should be treated as LLM calls, retrievals, protected actions, or durable memory writes.
## Install now, bind later
If you want to install runtime capture first and bind a harness later, call `install_runtime()` with no target:
```python theme={null}
vx.install_runtime()
vx.bind_harness(node, ...)
```
## Parameters
Optional target object to bind in place. If provided, VerifiedX wraps the named methods on that object during the same call.
Optional LLM method bindings.
You can use either:
* string shorthand: `{"call_model": "gpt-5.4-mini"}`
* object form: `{"call_model": {"model_name": "gpt-5.4-mini"}}`
Optional retrieval bindings for reads and lookups that should contribute decision context.
You can use:
* string shorthand: `{"lookup_internal_workflow": "internal workflow evidence"}`
* tuple shorthand: `{"search_public_web": ("public web context", "external_retrieval")}`
* object form with `query` and optional `object_type`
Optional high-impact business method bindings such as record mutations, workflow updates, or message sends.
You can use either:
* string shorthand: `{"set_workflow_status": "set_workflow_status"}`
* object form: `{"set_workflow_status": {"tool_name": "set_workflow_status"}}`
Optional durable memory-write bindings.
You can use either:
* string shorthand: `{"remember_customer_preference": "remember_customer_preference"}`
* object form: `{"remember_customer_preference": {"tool_name": "remember_customer_preference"}}`
Optional general tool bindings for helper methods and tool history.
If a method is definitely a durable memory write or another high-impact effect, prefer `memories` or `actions`.
## What it enables
`install_runtime(...)` turns on the Python runtime lane and lower-seam capture automatically.
That means you get runtime visibility for real side effects even before you bind every business method manually. Explicit bindings are how you promote your own harness methods into named VerifiedX surfaces with cleaner receipts and better business-level boundaries.
## Return value
Returns an activation report for the installed Python runtime.
Most builders do not need to use the return value directly.
## Example
```python theme={null}
from verifiedx import init_verifiedx
class WorkflowNode:
def call_model(self, messages):
...
def lookup_internal_workflow(self, workflow_id):
...
def set_workflow_status(self, workflow_id, status):
...
def remember_customer_preference(self, customer_id, preference):
...
vx = init_verifiedx()
node = WorkflowNode()
vx.install_runtime(
node,
llm={"call_model": "gpt-5.4-mini"},
retrievals={"lookup_internal_workflow": "internal workflow evidence"},
actions={"set_workflow_status": "set_workflow_status"},
memories={"remember_customer_preference": "remember_customer_preference"},
)
```
In this example, VerifiedX will:
* observe `call_model` as an LLM call
* record `lookup_internal_workflow` as retrieval context
* protect `set_workflow_status` as a high-impact boundary
* protect `remember_customer_preference` as a durable memory write
# Logs
Source: https://docs.verifiedx.me/api-reference/logs
Access and use your VerifiedX decision logs.
Decision logs let you inspect how VerifiedX is behaving on real protected boundaries.
Use them to understand:
* which boundaries are firing
* what outcomes VerifiedX is returning
* why a workflow continued, replanned, or stopped
* what would need to change before a blocked step should be retried
## Where to view logs
Decision logs are available in the builder app at [api.verifiedx.me](https://api.verifiedx.me).
They are grouped around real protected-boundary decisions, not just raw event streams.
## What you see in the logs view
In the main logs list, VerifiedX surfaces summary fields such as:
* outcome
* receipt mode
* action class
* created time
* primary reason
When you open a decision, the detail view includes:
* the full `decision_receipt`
* `safe_next_steps`
* `viewer_guidance`
* `what_would_change_this`
That gives you both the high-level result and the exact recovery contract for the workflow.
## Log retention
| Plan | Retention |
| ------------ | --------- |
| Free Sandbox | 7 days |
| Startup | 30 days |
| Enterprise | Custom |
## What to look for first
When reviewing a log entry, the fastest path is usually:
1. Check the outcome.
2. Check the receipt mode.
3. Read the primary reason.
4. Inspect `safe_next_steps`.
5. Open the full `decision_receipt` if you need the exact routing and resume semantics.
## Practical use
Logs are most useful for questions like:
* Are we protecting the right boundaries?
* Are agents hitting `replan_required` for the reasons we expect?
* Is recovery staying local or routing upstream?
* Which actions need better grounding, retrieval support, or cleaner approvals?
* Are we seeing the same blocked action retried without meaningful new support?
## Key idea
Decision logs are not just for auditing.
They are how you understand how VerifiedX expects the workflow to continue:
* downstream when execution is allowed
* locally when the current node should replan
* upstream when a parent stage needs to gather missing support
* truthfully when a path has to stop
After your first integration, read a handful of real decision logs before changing prompts or tool definitions. The logs usually show whether the issue is weak grounding, missing support, or a workflow that should recover upstream instead of locally.
# API Overview
Source: https://docs.verifiedx.me/api-reference/overview
The VerifiedX SDK and API surfaces.
VerifiedX is designed to be used through the Python and TypeScript SDKs.
Most builders should use an SDK or native adapter. The direct HTTP API is available for advanced custom integrations, but the SDKs are the standard path.
## Base URL
VerifiedX requests go to:
```text theme={null}
https://api.verifiedx.me
```
## Authentication
For direct HTTP requests, pass your API key in the `Authorization` header:
```text theme={null}
Authorization: Bearer your_runtime_key
```
If you are using an SDK, set `VERIFIEDX_API_KEY` and the SDK will read it automatically.
## Integration surfaces
VerifiedX supports three public integration surfaces:
* **Python SDK**
* `pip install verifiedx`
* **TypeScript SDK**
* `npm install @verifiedx-core/sdk`
* **Direct HTTP API**
* for advanced custom integrations
The SDKs are the recommended path. They handle runtime context capture, protected-boundary checks, receipts, and execution reporting for you.
## Versioning
VerifiedX versions its API at the HTTP layer. If you use an SDK, you normally do not need to think about this directly.
## Recommended path
Use the SDK or native adapter that matches your stack:
* Python SDK
* TypeScript SDK
* OpenAI direct
* OpenAI Agents SDK
* Anthropic direct
* Claude Agent SDK
* LangGraph
* LangChain
* Vercel AI SDK
* MCP
## Reference
Use the raw runtime or a native Python adapter.
Use `bindHarness(...)`, native adapters, or lower-seam fallbacks.
Configure VerifiedX with the minimal required settings.
Understand the structured receipt returned by protected checks.
# Preflight
Source: https://docs.verifiedx.me/api-reference/preflight
How VerifiedX checks a protected boundary before it executes.
Preflight is the core VerifiedX check.
When a protected boundary is about to run, VerifiedX evaluates whether that exact side effect is justified by the current run context before it executes.
In product terms, preflight usually applies to:
* high-impact actions
* durable memory writes
If you are using the Python or TypeScript SDKs, or any native adapter, you do not call preflight manually. VerifiedX runs it automatically at the right boundary.
## What gets preflighted
Preflight is not just for methods named `actions`.
Depending on the integration, VerifiedX can preflight:
* **Protected actions**
* record mutations
* system changes
* external messages
* webhook-like effects
* **Durable memory writes**
* memory or long-lived state that should only change when justified
## What preflight considers
Preflight evaluates the pending side effect in context.
That can include:
* what the agent is about to do
* what the agent has already done in this run
* the evidence the agent gathered before the action
* upstream context when the run is part of a multi-agent or agent+human system
This is why the same action can be allowed in one run and require replanning in another.
## What the SDK does automatically
When a protected boundary is reached, the SDK or native adapter automatically:
1. builds the current decision context
2. runs the preflight check
3. receives the decision and decision receipt
4. either lets the side effect proceed or returns a structured blocked loopback result
If the boundary is allowed, VerifiedX can also observe whether the effect actually happened after execution.
## Possible outcomes
A preflight check returns one of these outcomes:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured [Decision Receipt](/concepts/decision-receipts).
## What happens after preflight
### If allowed
If the outcome is `allow` or `allow_with_warning`:
* the side effect may execute
* the workflow can continue
* VerifiedX can report the observed execution afterward
### If replanning is required
If the outcome is `replan_required`:
* the requested side effect does not execute
* the workflow gets a structured blocked result and decision receipt
* the system should keep moving toward the same goal through a safer or better-grounded next step
That replan can stay:
* **local** to the current node or agent
* **upstream** to a parent orchestrator when the missing support has to be gathered there
### If terminally blocked
If the outcome is `goal_fail_terminal`:
* the requested path must not execute
* the workflow should fail truthfully or hand off cleanly to a human or upstream system
## Why this matters
Preflight is not only about stopping obviously malicious behavior.
It also catches cases where the agent is:
* weakly grounded
* acting on stale or partial context
* retrying the wrong side effect
* trying to mutate state without enough trustworthy support
The goal is to stop the wrong side effect while keeping the system moving safely toward the same goal state whenever that is still possible.
## Direct API usage
Advanced custom integrations can use the VerifiedX API directly, but most builders should use an SDK or native adapter instead.
The SDK path is the recommended path because it already handles:
* context accumulation
* protected-boundary checks
* decision receipts
* execution reporting
* runtime loopbacks
# Receipts
Source: https://docs.verifiedx.me/api-reference/receipts
The decision receipt returned by every protected boundary check.
A decision receipt is the structured contract VerifiedX returns for every protected boundary check.
Your system should usually branch on:
* `outcome`
* `safe_to_execute`
* `disposition.mode`
For deeper explanation, see [Decision Receipts](/concepts/decision-receipts).
## Fields
Always `verifiedx_decision_receipt`.
Stable identifier for the decision.
One of:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
The protected class involved, such as `memory_write`, `record_mutation`, `system_change`, or `external_message_send`.
Whether the requested boundary is safe to execute now.
Indicates whether this path should terminate into human review.
Indicates whether the same blocked action should not be retried unchanged.
Structured reasons for the decision.
Safe next steps the workflow can take without pretending the blocked effect happened.
Extra guidance for reviewer-facing or operator-facing views.
Evidence, approvals, or context that would change the decision.
How the workflow should continue.
One of:
* `continue_downstream`
* `local_replan`
* `upstream_replan`
* `human_review`
* `terminal_block`
Whether the workflow can continue downstream normally.
Whether the current node should keep working locally toward the same goal.
Whether the receipt should be routed upstream before retrying this node.
Routing hints for the next stage of the workflow.
High-level routing direction such as `downstream`, `local`, `upstream`, `human`, or `stop`.
Optional hint for the next node, reviewer, or upstream stage.
Optional node identifier to resume or return to.
Optional workflow identifier when the decision is part of a larger workflow.
Optional current node identifier.
Resume and retry semantics for the blocked or warned boundary.
Whether this node should be retried later.
Whether the receipt should be passed upstream.
Whether new upstream context is expected before retry.
Conditions or evidence that should be true before retry.
Runtime loopback information when the workflow should replan locally or upstream.
Optional runtime or adapter metadata attached by the SDK.
## Example receipt
```json theme={null}
{
"receipt_type": "verifiedx_decision_receipt",
"decision_id": "dec_composed_replan_1",
"outcome": "replan_required",
"action_class": "system_change",
"safe_to_execute": false,
"human_review_terminal": false,
"must_not_retry_same_action": false,
"reasons": [
{
"code": "missing_upstream_support",
"message": "The workflow update is missing required upstream approval."
}
],
"safe_next_steps": [
{
"code": "GET_LEGAL_REVIEW",
"message": "Obtain legal review approval for WF-2302."
}
],
"viewer_guidance": [],
"what_would_change_this": [
"A legal review event confirming WF-2302 is approved_with_follow_up."
],
"disposition": {
"mode": "upstream_replan",
"downstream_allowed": false,
"local_replan_recommended": false,
"upstream_replan_required": true
},
"routing": {
"direction": "upstream",
"target_hint": "workflow_supervisor",
"return_to_node": "workflow_supervisor",
"workflow_id": "REQ-2302",
"node_id": "handoff_note_node"
},
"resume_contract": {
"retry_this_node": true,
"pass_receipt_upstream": true,
"pass_new_upstream_context_on_retry": true,
"resume_when": [
"A legal review event confirming WF-2302 is approved_with_follow_up."
]
},
"runtime_loopback": {
"same_goal_state": true
},
"metadata": {}
}
```
## Outcome values
| Outcome | Meaning |
| -------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `allow` | The boundary can execute normally. |
| `allow_with_warning` | The boundary can execute, but the receipt still carries warnings and guidance. |
| `replan_required` | This exact side effect should not happen now. The workflow should continue through a safer or better-grounded path. |
| `goal_fail_terminal` | The requested path is terminally blocked for the current goal state. |
When a blocked boundary is returned through a runtime or native adapter, the tool result usually also includes the same `decision_receipt` so the agent can replan immediately.
# Authentication
Source: https://docs.verifiedx.me/authentication
Get your API key and configure your runtime.
## Get your API key
Sign in to the [VerifiedX dashboard](https://api.verifiedx.me) and open **API keys**. Create a new runtime key for your workspace.
All VerifiedX API keys start with `vxpk_`.
## Configure your environment
For the hosted service, `VERIFIEDX_API_KEY` is the only required variable.
Never hardcode your API key in source code. Always load it from an environment variable.
```bash .env theme={null}
VERIFIEDX_API_KEY=vxpk_your_runtime_key
```
The SDK reads this automatically on initialization.
This is enough for the normal hosted setup. Most builders do not need any other auth or runtime configuration to get started.
## Optional environment variables
Use these only if you need to override the default hosted setup or attach extra runtime metadata.
| Variable | Required | Default | Description |
| ------------------------- | -------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `VERIFIEDX_BASE_URL` | No | `https://api.verifiedx.me` | Override the VerifiedX API base URL. |
| `VERIFIEDX_ORG_ID` | No | `org_1` | Runtime org identifier. For hosted requests, the API key scope is authoritative. |
| `VERIFIEDX_PROJECT_ID` | No | `project_1` | Runtime project identifier. For hosted requests, the API key scope is authoritative. |
| `VERIFIEDX_ENVIRONMENT` | No | `prod` | Runtime environment label, for example `prod`, `staging`, or `dev`. For hosted requests, the API key scope is authoritative. |
| `VERIFIEDX_AGENT_ID` | No | Auto-generated | Agent identifier shown in runtime context and logs. |
| `VERIFIEDX_SOURCE_SYSTEM` | No | SDK default | Source-system label for the runtime. |
For the hosted API, org, project, and environment are aligned to the API key scope server-side.
## Missing or invalid keys
Missing and invalid keys do not behave exactly the same across SDKs.
* In Python, `init_verifiedx()` fails fast if `VERIFIEDX_API_KEY` is missing from env.
* Invalid, unrecognized, or revoked keys are rejected by the VerifiedX API on the first real request.
* In TypeScript, missing auth usually surfaces when the first VerifiedX request is made rather than at `initVerifiedX()` time.
* If you rotate a key in the dashboard, update the environment variable and restart your process.
Before sending live traffic, run:
```bash Python theme={null}
verifiedx doctor
```
```bash TypeScript theme={null}
npx verifiedx doctor
```
That reports your loaded config and, when a key is present, checks connectivity to the VerifiedX API.
# Decision Receipts
Source: https://docs.verifiedx.me/concepts/decision-receipts
What VerifiedX returns for every protected boundary check.
A decision receipt is the structured result VerifiedX returns for every protected boundary check.
It is not just a yes or no. It tells your system:
* what outcome VerifiedX reached
* whether the side effect is safe to execute
* how the workflow should continue
* where recovery should happen
* what would need to change before retrying
Receipts are designed so your agent or orchestrator can keep working toward the same goal state safely instead of guessing what to do next.
## What a receipt contains
Every receipt includes the decision in a machine-readable form.
In practice, a receipt gives your system:
* a stable identifier for the decision
* the outcome of the check
* whether the requested side effect is safe to execute now
* structured reasons explaining the decision
* safe next steps the workflow can take
* guidance on where the workflow should continue
* retry or resume guidance when the boundary should be attempted again
* optional metadata from the runtime or adapter
## Decision outcomes
VerifiedX uses the same core outcome model across runtimes and adapters:
| Outcome | What it means |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `allow` | The boundary can execute normally |
| `allow_with_warning` | The boundary can execute, but the receipt still carries warnings and guidance |
| `replan_required` | The requested boundary must not execute as requested; the workflow should continue through a safer or better-grounded path |
| `goal_fail_terminal` | The requested boundary path is terminally blocked for the current goal state |
## Disposition modes
The most important part of the receipt is usually the disposition.
| Mode | What it means |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `continue_downstream` | The boundary is allowed and the workflow can continue normally |
| `local_replan` | The current node should keep working locally toward the same goal without executing the denied effect |
| `upstream_replan` | The current node should stop local work and return the receipt upstream so missing support or approvals can be gathered |
| `human_review` | The workflow should pause for human review |
| `terminal_block` | The current path is terminally blocked |
## Example receipt
This is a representative `replan_required` receipt for a workflow that needs safer recovery instead of immediate execution:
```json theme={null}
{
"decision_id": "dec_example_1",
"outcome": "replan_required",
"safe_to_execute": false,
"reasons": [
{
"code": "missing_required_support",
"message": "The requested action is missing required support."
}
],
"safe_next_steps": [
{
"code": "GATHER_MISSING_SUPPORT",
"message": "Gather the missing approval, evidence, or corroboration before retrying."
}
],
"disposition": {
"mode": "upstream_replan"
}
}
```
## How to use receipts
Receipts are synchronous outputs of the protected-boundary flow. Your system should branch on the receipt, not guess from prose.
Typical handling looks like this:
* **`continue_downstream`**
* The boundary is allowed.
* The side effect may execute.
* The workflow can continue normally.
* **`local_replan`**
* Do not execute the denied side effect.
* Keep the receipt local.
* Replan inside the same node or agent using the reasons and safe next steps in the receipt.
* **`upstream_replan`**
* Do not keep pushing locally.
* Return the receipt upstream.
* Let the parent orchestrator or supervisor gather the missing evidence, approval, or context before retrying this node.
* **`human_review`**
* Surface the receipt to a reviewer.
* Use the receipt to guide the review step and the eventual retry path.
* **`terminal_block`**
* Do not claim success.
* Fail truthfully.
* Hand off to a human or upstream workflow if appropriate.
## Receipts and blocked results
When a protected boundary is blocked at runtime, VerifiedX usually returns a runtime-friendly blocked result as well as the receipt.
The receipt is the routing and recovery contract. The blocked result is the immediate payload your agent or workflow can act on right away.
## Key idea
A receipt is not just an audit artifact. It is the machine-readable contract that lets your system continue safely:
* downstream when execution is allowed
* locally when replanning should stay on the current node
* upstream when the blocker must be resolved by a parent stage
* truthfully when the workflow has to pause or stop
# How It Works
Source: https://docs.verifiedx.me/concepts/how-it-works
How VerifiedX checks high-impact agent actions before they execute.
VerifiedX is an additive action-boundary layer.
It does not replace your orchestrator, agent loop, tools, or memory system. It attaches to the surfaces you already use, builds context as the run unfolds, checks high-impact actions before they execute, and returns a decision your system can act on.
## What VerifiedX uses to make a decision
VerifiedX does not judge an action in isolation.
It looks at:
* what the agent is about to do
* what the agent has already done in this run
* what the agent has ingested into its context from external retrievals and user-controlled inputs
* the evidence the agent gathered before the action
* upstream context when the run is part of a multi-agent or agent+human system
## The execution flow
VerifiedX connects through the adapter or runtime surface that already matches your stack. You keep your current orchestrator, tools, prompts, and business logic.
As the run continues, VerifiedX observes the relevant context around the agent's work so it can evaluate high-impact actions with the full picture available at that point.
Before the agent writes durable memory, updates records, changes workflow state, sends messages, calls webhooks, or takes another high-impact action, VerifiedX checks whether that action is justified.
VerifiedX returns an outcome, reasons, and structured guidance for how the run should continue.
If the action is justified, it can execute. If it is not, the action does not execute and the agent or upstream system gets a replan path instead.
## Decision outcomes
VerifiedX uses the same core outcome model across runtimes and adapters:
| Outcome | What it means |
| -------------------- | --------------------------------------------------------------------------------------------------------- |
| `allow` | The action can execute normally |
| `allow_with_warning` | The action can execute, but with warnings and guidance |
| `replan_required` | The action must not execute as requested; the run should continue through a safer or better-grounded path |
| `goal_fail_terminal` | The requested action path is terminally blocked for the current goal state |
Every decision also includes a structured decision receipt your system can keep local, route downstream, or pass upstream.
## Single-agent and composed systems
VerifiedX works in both direct and composed systems.
In a direct or single-agent flow, a blocked action usually becomes a local replan. The side effect does not execute, and the same agent can keep working toward the goal through a safer next step.
In a multi-agent or agent+human system, the same blocked action can be routed upstream instead. That lets a parent workflow or supervisor gather the missing approval, evidence, or context before retrying.
## What VerifiedX does not do
* It does not replace your orchestrator, framework, or tool loop.
* It does not own your database, memory store, queue, or message bus.
* It does not require you to redesign your system around a special VerifiedX workflow.
* It does not force one integration path. You can use the native adapter that matches your stack or the raw runtime.
VerifiedX is designed to be the thinnest truthful layer between an agent and a high-impact action.
# Protected Actions
Source: https://docs.verifiedx.me/concepts/protected-actions
What counts as a protected action and how checks work.
A protected action check is the billable name for a real high-impact boundary check.
VerifiedX runs that check before the side effect executes, returns a decision receipt, and then helps the system either execute safely, recover locally, or replan upstream without losing the goal state.
Context from user-controlled inputs, external retrieval-like inputs, event ingest, execution reports, and decision reads are all included in that check.
## What counts as a protected action
In product terms, protected actions include durable memory writes and other high-impact side effects.
Common examples:
* **Durable memory writes** such as saving agent memory, long-lived state, or knowledge-store entries
* **Record updates** such as CRM changes, ticket changes, ledger writes, or workflow-state changes
* **Outbound messaging** such as email, Slack, SMS, notifications, and similar message sends
* **External callbacks and submissions** such as webhooks, browser submissions, queue publishes, and uploads
* **Internal system changes** such as file writes, config changes, and other internal mutations
## What does not count as a protected action
These do not usually create a billable protected action check on their own:
* **LLM calls**
* **Retrievals and read-like lookups**
* **Memory reads**
* **Other support inputs** that help build decision context but do not themselves create a protected side effect
They still matter. They help VerifiedX evaluate the next high-impact boundary with better context.
## How protected actions are discovered
VerifiedX can discover protected actions through more than one surface:
* **Raw runtimes**
* You explicitly bind `actions` and `memories`
* **Native adapters**
* VerifiedX uses your existing tool names, descriptions, schemas, approval hints, and framework-native execution surfaces
* **Supported lower-level surfaces**
* When supported by the integration, VerifiedX can also protect lower-level side effects without requiring you to redesign your system
A write is not only protectable when you explicitly declare it under `actions`. Native adapters and supported lower-level surfaces can also expose real protected boundaries.
## Declaring protected actions in the raw runtimes
If you are using the raw Python or TypeScript runtime, declare durable memory writes under `memories` and other high-impact effects under `actions`.
```python Python theme={null}
from verifiedx import init_verifiedx
vx = init_verifiedx()
vx.install_runtime(
node,
memories={
"write_memory": {"tool_name": "write_memory"},
},
actions={
"send_email": {"tool_name": "send_email"},
"set_workflow_status": {"tool_name": "set_workflow_status"},
},
)
```
```typescript TypeScript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
const vx = await initVerifiedX();
vx.bindHarness(node, {
memories: {
writeMemory: { toolName: "write_memory" },
},
actions: {
sendEmail: { toolName: "send_email" },
setWorkflowStatus: { toolName: "set_workflow_status" },
},
});
```
The method name is the method on your harness or node. The `tool_name` or `toolName` is the stable boundary name that appears in decision logs and receipts.
## Native adapters
On native adapter surfaces, you usually do not declare separate `actions` and `memories` dictionaries.
Instead, your existing native tool definitions are the source of truth. VerifiedX uses the tool name, schema, description, approval hints, and adapter-specific runtime signals to determine whether a tool should be treated as:
* a protected action
* a durable memory write
* or only a support input
That is why native integrations can stay few-LOC while still protecting high-impact tools.
## What happens during and after a protected action check
VerifiedX sees that the agent is about to take a durable memory write or another high-impact action.
VerifiedX evaluates the pending step using the surrounding run context, including what the agent has already done, what it has gathered, and any relevant upstream context.
VerifiedX evaluates whether the pending action is justified before execution.
The result can be `allow`, `allow_with_warning`, `replan_required`, or `goal_fail_terminal`, along with reasons, safe next steps, and structured receipt data.
If allowed, the side effect may execute. If the boundary is blocked or needs replanning, the side effect does not execute and the workflow gets a structured recovery path instead.
## Replan is the default recovery shape
VerifiedX is optimized to stop the wrong action, not stop the agent.
If the current side effect is wrong but the goal is still valid, the usual outcome is `replan_required`. That means:
* the requested side effect does not execute
* the system gets a structured decision receipt
* the workflow gets reasons and safer next steps
* the run should continue toward the same goal state through a safer or better-grounded path
## Local and upstream replan
VerifiedX supports both local and upstream recovery.
### Local replan
In a direct or single-node flow, a denied protected action usually becomes a local replan first.
That means:
* the side effect stays unapplied
* the current agent or node gets the blocked result
* the same node can continue with a safer next step
### Upstream replan
In a composed workflow, the same `replan_required` outcome can route upstream when the missing support, approvals, or corroboration has to come from a parent stage.
That means:
* the local node stops trying to complete this stage on its own
* it returns the decision receipt upstream
* the orchestrator or parent node gathers what is missing
* the blocked node is retried later with better upstream context
This is how VerifiedX helps complex systems keep working toward the same goal state without pretending the blocked step succeeded.
## A successful check is not the end of the story
A protected action check covers the preflight. After an allowed boundary runs, VerifiedX can also observe and report whether the side effect actually happened.
If the action throws, fails, or does not confirm execution, VerifiedX keeps the workflow honest instead of silently counting that step as complete.
That is why execution reports and runtime loopbacks are included in the same protected action check.
## Pricing
Each protected action check counts as one billable unit. The following are included at no additional cost per check:
* Context from user-controlled and retrieval-like inputs
* Event ingest
* Execution reports
* Decision reads from your decision log
Start by protecting the boundary most likely to cause irreversible damage if the agent is hijacked, misgrounded, or hallucinating.
# Environment Variables
Source: https://docs.verifiedx.me/configuration/environment
Configure VerifiedX with the one required environment variable and a few optional overrides.
For normal SDK usage, **`VERIFIEDX_API_KEY` is the only environment variable you need**.
Both SDKs read it automatically:
* Python: `init_verifiedx()`
* TypeScript: `initVerifiedX()`
## Required
| Variable | Required | Default | Description |
| ------------------- | -------- | ------- | ------------------------------ |
| `VERIFIEDX_API_KEY` | Yes | — | Your VerifiedX runtime API key |
## Minimal `.env`
```ini .env theme={null}
VERIFIEDX_API_KEY=your_runtime_key
```
That is enough for a normal integration.
You do not need extra config code just to use `VERIFIEDX_API_KEY`. The SDKs pick it up automatically from `os.environ` in Python and `process.env` in TypeScript.
## Optional
Most builders can ignore this section. These are the only optional variables most teams are likely to care about:
| Variable | Default | When to use it |
| ----------------------- | -------------------------- | ----------------------------------------------------- |
| `VERIFIEDX_ENVIRONMENT` | `prod` | Separate production from staging or dev traffic |
| `VERIFIEDX_PROJECT_ID` | SDK default | Split logs across multiple projects or services |
| `VERIFIEDX_BASE_URL` | `https://api.verifiedx.me` | Point the SDK at a non-default VerifiedX API endpoint |
## Practical example
```ini .env theme={null}
VERIFIEDX_API_KEY=your_runtime_key
VERIFIEDX_ENVIRONMENT=staging
VERIFIEDX_PROJECT_ID=workflow-ops
```
## Notes
* Set `VERIFIEDX_ENVIRONMENT` to something like `staging` or `dev` outside production so your logs stay clearly separated.
* Use `VERIFIEDX_PROJECT_ID` if multiple apps or workflows share the same VerifiedX account.
* Use `VERIFIEDX_BASE_URL` only if you have been told to point at a different VerifiedX API endpoint.
The SDKs also support additional advanced runtime and debug environment variables, but most builders never need them. This page intentionally keeps the main setup surface small.
# Decision Logs
Source: https://docs.verifiedx.me/configuration/logging
Inspect the decisions VerifiedX records for protected boundaries.
VerifiedX records a decision every time it preflights a protected boundary.
In practice, decision logs are where you inspect:
* what boundary was checked
* what outcome VerifiedX returned
* why it returned that outcome
* how the workflow was expected to continue
This is useful both for production monitoring and for tightening an integration after the first rollout.
## What a decision log entry contains
A decision log entry is centered on the protected boundary decision itself.
Typical fields include:
* The `decision_id`
* The outcome, such as:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
* The protected class involved, such as:
* `memory_write`
* `record_mutation`
* `system_change`
* `external_message_send`
* The primary reasons for the decision
* Safe next steps
* Viewer guidance
* What would change the decision
* The exact machine-readable `decision_receipt`
In the builder app, decision entries are shown with outcome, receipt mode, action class, created time, primary reason, and the full decision receipt.
## What to look for first
When you open a decision log, the fastest way to understand it is usually:
1. Check the outcome.
2. Check the action class.
3. Read the reasons.
4. Read the safe next steps.
5. Inspect the `decision_receipt`.
That tells you whether the workflow should have:
* continued downstream
* replanned locally
* routed upstream
* paused for review
* or stopped
## Why logs are useful
Decision logs help answer questions like:
* Which protected boundaries are actually firing in production?
* Are agents being blocked for the reasons we expect?
* Are we seeing local replans or upstream replans?
* Which actions are high-friction and need better grounding or better upstream support?
* Are we incorrectly treating a retrieval, helper, or tool as the wrong kind of boundary?
They are especially useful right after the first integration, because they show the real runtime behavior instead of what the prompts or code were supposed to do.
## What the builder app surfaces
The builder app has a decision logs view for browsing recent decisions.
From the current app surface, log entries expose:
* `outcome`
* receipt mode
* `action_class`
* `created_at`
* primary reason
* full `decision_receipt`
* `viewer_guidance`
* `what_would_change_this`
* `safe_next_steps`
That makes the decision log the main place to inspect how VerifiedX wants the workflow to recover.
## What logs tell you about recovery
Decision logs are especially important for replans.
If a boundary returns `replan_required`, the receipt will tell you whether the system should:
* recover locally on the same node
* route the receipt upstream
* or pause for human review
That is usually more useful than just knowing something was “blocked,” because VerifiedX is optimized to keep the workflow moving safely toward the same goal state.
## Local debug diagnostics
If you need lower-level runtime diagnostics during development, both SDKs can also write a local JSONL debug file when debug logging is enabled.
That file is separate from the builder app decision logs and can include records such as:
* `verifiedx_boundary_request`
* `verifiedx_boundary_diagnostic`
* `verifiedx_ingress_diagnostic`
* `verifiedx_runtime_loopback`
This is mainly for debugging integration behavior, request shape, and runtime loopback handling.
## Practical usage
Use decision logs to:
* Review unexpected `replan_required` outcomes
* Confirm that high-impact actions are being classified correctly
* Inspect whether a blocked step should have recovered locally or upstream
* Validate that your protected boundaries are the right ones
* Spot workflows that need stronger grounding, better retrieval support, or cleaner upstream approvals
After the first integration, read a handful of real decision logs before changing prompts or tool definitions. The logs usually show whether the problem is weak grounding, missing support, the wrong boundary shape, or a workflow that should recover upstream instead of locally.
# Introduction
Source: https://docs.verifiedx.me/introduction
What VerifiedX does and why builders use it.
VerifiedX gives you peace of mind that your autonomous agents can keep doing useful work without taking the kind of high-impact bad action that can hurt your business, your users, or your operations.
It checks whether a meaningful agent action or durable memory write is justified by trustworthy evidence before it happens.
The core question is not just “is this suspicious?” It is: why is the agent about to do this exact thing with these exact parameters, and should that side effect happen?
It sits at the action boundary. Your runtime, orchestrator, prompts, tools, and memory stay the same. VerifiedX observes the run, preflights protected side effects before they land, and returns a machine-readable decision plus a decision receipt your system can act on.
## What it does
Once integrated, VerifiedX automatically observes the runtime context it needs and preflights protected side effects such as:
* Memory writes
* Record mutations
* System changes
* External messages
* Internal notifications, handoff notes, and workflow updates
* Webhook calls
* File writes
* Database mutations
* Queue publishes
* Delegation and handoffs
This is about both adversarial and non-adversarial failures. Prompt injection is one cause, but so are weak grounding, stale context, drift, overeagerness, and retrying the wrong action without a meaningful new basis.
## What to expect from a decision
When a protected side effect is about to happen, VerifiedX returns one of four outcomes:
* `allow` — the action is sufficiently justified and can proceed.
* `allow_with_warning` — the action can proceed, but VerifiedX attaches a meaningful caution and a receipt.
* `replan_required` — this exact action should not happen now, but the agent should keep working toward the same goal through a safer or better-grounded next step.
* `goal_fail_terminal` — last resort. VerifiedX uses this only when the goal itself depends on a clearly forbidden, unsafe, malicious, or illegitimate action and there is no safe path forward.
Every outcome still comes with a structured decision receipt, reasons, and next-step guidance.
## What happens when an action is wrong
VerifiedX is optimized to stop the bad action, not stop the agent.
If the action itself is wrong but the goal is still valid, VerifiedX returns a replan decision with reasons, what not to do, and safe next steps so the system can keep moving toward the same goal state.
In single-agent systems, that replan stays local first: the runtime loopback gives the same agent the decision, the reasons, and the next-step guidance so it can continue without executing the bad action.
In composed systems, VerifiedX first gives the current node a chance to recover locally. If the blocker is still unresolved, the decision receipt can route that replan upstream so your orchestrator knows this stage needs an upstream fix before retry.
`goal_fail_terminal` is the complete last resort. Even then, VerifiedX still returns a structured decision receipt with exact reasons, routing, and resume semantics so your system can fail truthfully and hand the situation back to a human or upstream workflow cleanly.
## Why builders use it
* Few lines of code
* Machine-readable decision receipts
* Clear reasons and next steps
* Decision logs, usage, keys, and billing in the builder app
* Strong bias toward continuing the workflow safely instead of dead-ending it
## What VerifiedX is not
* Not an orchestrator
* Not a replacement for your runtime
* Not a memory system or workflow engine
* Not just post-hoc logging
* Not a generic AI safety, observability, or framework-rewrite product
VerifiedX adds a preflight boundary around the moments that can actually change the world.
## Supported runtimes and adapters
| Surface | Languages |
| -------------------------------- | ------------------ |
| Raw runtime | Python, TypeScript |
| OpenAI direct | Python, TypeScript |
| OpenAI Agents SDK | Python, TypeScript |
| Anthropic direct | Python, TypeScript |
| Claude Agent SDK / query surface | Python, TypeScript |
| LangGraph | Python, TypeScript |
| LangChain | Python, TypeScript |
| Vercel AI SDK | TypeScript |
| MCP tools | Python, TypeScript |
## Free Sandbox
The Free Sandbox includes **250 protected action checks per month**.
## Next steps
Protect your first boundary in a few minutes.
Use the raw runtime or a native Python adapter.
Use `bindHarness(...)`, native adapters, or lower-seam fallbacks.
See taint probe, preflight, receipts, execution reporting, and logs end to end.
# Pricing
Source: https://docs.verifiedx.me/pricing
Plans and pricing for VerifiedX.
VerifiedX charges per protected action check.
One protected action check equals one real boundary preflight. Taint marking, event ingest, execution reports, receipts, and decision reads are included in that unit.
## Plans
**\$0**
* 250 protected action checks per month
* 7 day decision logs
* All languages, providers, and adapters
* Ideal for first integration
**\$99/mo for first 3 months**
* Then \$149/mo
* Includes 3,000 protected action checks
* 30 day decision logs
* All SDKs and adapters
* \$50 per extra 1,000 protected checks
**Custom**
* Annual contracts
* Security review support
* Custom procurement flow
* Longer retention and support
## What counts as a check
One protected action check equals one preflight boundary call.
That includes:
* Taint marking
* Event ingest
* Execution reports
* Decision receipts
* Decision reads
## What does not count as a check
These do not create a protected action check on their own:
* LLM calls
* Retrieval calls
* Context accumulation
* Support-input history capture
The Free Sandbox includes every language, provider, framework, and adapter. The difference is the monthly check limit and log retention, not a reduced product surface.
Start on the Free Sandbox, protect one risky boundary, inspect real decisions, and only then widen the rollout.
# Quick Start
Source: https://docs.verifiedx.me/quickstart
Protect your first agent node in a few minutes.
Start with the Free Sandbox to protect one risky node, see real decision logs, and only then widen the rollout.
Start with the highest-level surface your repo already uses. Only fall back to raw Python or raw TypeScript if no native adapter already owns the tool loop, graph, or runtime.
Run doctor first:
* TypeScript / OpenAI / OpenAI Agents SDK / Anthropic / Claude / Claude Agent SDK / Vercel AI SDK / MCP (TypeScript): `npx @verifiedx-core/sdk doctor`
* Python / LangGraph / MCP (Python): `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.quickstart)
## Pick your stack first
Most common starting points:
* [OpenAI](https://docs.verifiedx.me/sdks/openai)
* [OpenAI Agents SDK](https://docs.verifiedx.me/sdks/openai-agents-sdk)
* [LangGraph](https://docs.verifiedx.me/sdks/langgraph)
* [Vercel AI SDK](https://docs.verifiedx.me/sdks/vercel-ai)
* [Anthropic / Claude](https://docs.verifiedx.me/sdks/anthropic)
* [Claude Agent SDK](https://docs.verifiedx.me/sdks/claude-agent-sdk)
* [Python SDK](https://docs.verifiedx.me/sdks/python)
* [TypeScript SDK](https://docs.verifiedx.me/sdks/typescript)
Other supported surfaces:
* [LangGraph (OpenAI)](https://docs.verifiedx.me/sdks/langgraph-openai)
* [LangGraph (Anthropic / Claude)](https://docs.verifiedx.me/sdks/langgraph-anthropic)
* [MCP (Python)](https://docs.verifiedx.me/sdks/mcp-python)
* [MCP (TypeScript)](https://docs.verifiedx.me/sdks/mcp-typescript)
Add the VerifiedX package for your runtime.
```bash Python theme={null}
pip install verifiedx
```
```bash TypeScript theme={null}
npm install @verifiedx-core/sdk
```
This page shows the raw runtime path because it is the fastest universal install. If your app already uses OpenAI direct, OpenAI Agents SDK, Anthropic direct, Claude Agent SDK, LangGraph, LangChain, Vercel AI SDK, or MCP, prefer the native adapter page for that stack.
For the hosted service, `VERIFIEDX_API_KEY` is the only required variable.
```bash .env theme={null}
VERIFIEDX_API_KEY=vxpk_your_runtime_key
# Optional
VERIFIEDX_BASE_URL=https://api.verifiedx.me
VERIFIEDX_ORG_ID=your_org_id
VERIFIEDX_PROJECT_ID=your_project_id
VERIFIEDX_ENVIRONMENT=prod
```
`VERIFIEDX_BASE_URL` already defaults to `https://api.verifiedx.me`. Org, project, and environment also have SDK defaults, so they are optional for a first install.
Bind the methods VerifiedX should observe and preflight. For the raw runtime, that usually means your LLM call, retrieval method, and the first risky action or durable memory write you want to protect.
```python Python theme={null}
from verifiedx import init_verifiedx
class WorkflowNode:
def call_model(self, messages):
...
def lookup_internal_workflow(self, workflow_id):
...
def set_workflow_status(self, workflow_id, status):
...
vx = init_verifiedx()
node = WorkflowNode()
vx.install_runtime(
node,
llm={"call_model": {"model_name": "gpt-5.4-mini"}},
retrievals={"lookup_internal_workflow": {"query": "internal workflow evidence"}},
actions={"set_workflow_status": {"tool_name": "set_workflow_status"}},
)
```
```typescript TypeScript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
const node = {
async callModel(messages) {
return { messages };
},
async lookupInternalWorkflow(workflowId) {
return { workflowId, status: "legal_complete" };
},
async setWorkflowStatus({ workflowId, status }) {
return { workflowId, status };
},
};
const vx = await initVerifiedX();
vx.bindHarness(node, {
llm: { callModel: { modelName: "gpt-5.4-mini" } },
retrievals: {
lookupInternalWorkflow: { query: "internal workflow evidence" },
},
actions: {
setWorkflowStatus: { toolName: "set_workflow_status" },
},
});
```
If the first risky node you want to protect is a durable memory write instead of an action, bind it under `memories` instead of `actions`.
If the node you are protecting is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so that node has better system and situational awareness before it takes a high-impact action. VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
Run your agent as normal. VerifiedX observes the run, preflights the protected side effect before it lands, and records a decision receipt you can inspect.
Open the [VerifiedX dashboard](https://verifiedx.me/?vx_source=docs.quickstart) to inspect your decision logs. Each protected action check includes:
* The action that was intercepted
* The preflight outcome: `allow`, `allow_with_warning`, `replan_required`, or `goal_fail_terminal`
* Structured reasons explaining the decision
* A machine-readable decision receipt with routing and resume semantics
If a replan happens, the side effect does not execute. The agent gets loopback guidance and can keep working toward the same goal through a safer next step.
## Tell your coding agent
Use this prompt to have a coding agent integrate VerifiedX into an existing codebase for you.
```text theme={null}
You are integrating VerifiedX into an existing codebase.
Goal
Install VerifiedX with the smallest truthful integration. Keep the existing runtime, tools, prompts, orchestration, graph, business logic, and deployment shape. Use VERIFIEDX_API_KEY from env. Prefer the native VerifiedX adapter that exactly matches the runtime or framework already used by this codebase.
Before editing, you may run read-only commands to orient yourself:
- Python: `verifiedx doctor`
- TypeScript: `npx @verifiedx-core/sdk doctor`
- Optional repo scan: `npx -y @verifiedx-core/sdk@latest verify --json --cwd .`
Use these only for reconnaissance. They do not replace reading the code.
First step
Read the codebase and identify which VerifiedX integration surface matches it. Before editing, open the matching docs page:
- Python raw: https://docs.verifiedx.me/sdks/python
- TypeScript raw: https://docs.verifiedx.me/sdks/typescript
- OpenAI Agents SDK: https://docs.verifiedx.me/sdks/openai-agents-sdk
- Claude Agent SDK: https://docs.verifiedx.me/sdks/claude-agent-sdk
- Vercel AI SDK: https://docs.verifiedx.me/sdks/vercel-ai
- LangGraph: https://docs.verifiedx.me/sdks/langgraph
- OpenAI direct: https://docs.verifiedx.me/sdks/openai
- Anthropic direct: https://docs.verifiedx.me/sdks/anthropic
- LangGraph (OpenAI): https://docs.verifiedx.me/sdks/langgraph-openai
- LangGraph (Anthropic): https://docs.verifiedx.me/sdks/langgraph-anthropic
- MCP (Python): https://docs.verifiedx.me/sdks/mcp-python
- MCP (TypeScript): https://docs.verifiedx.me/sdks/mcp-typescript
Concept references
If you need the runtime model or blocked-result semantics while integrating, use these:
- How it works: https://docs.verifiedx.me/concepts/how-it-works
- Protected actions: https://docs.verifiedx.me/concepts/protected-actions
- Decision receipts: https://docs.verifiedx.me/concepts/decision-receipts
Use concept pages to understand what VerifiedX does. Do not use concept pages to invent API names. Use the matching SDK page for exact installation and code.
Selection rules
- Use the highest-level native adapter already owning the tool loop, graph, runtime, or MCP boundary.
- If the codebase uses LangGraph, use the LangGraph adapter, not direct OpenAI or Anthropic.
- If the codebase uses OpenAI Agents SDK, use the OpenAI Agents SDK adapter, not OpenAI direct.
- If the codebase uses Claude Agent SDK, use the Claude Agent SDK adapter, not Anthropic direct.
- If the codebase uses Vercel AI SDK, use the Vercel AI SDK adapter.
- If the codebase exposes standalone MCP tool handlers, use MCP Python or MCP TypeScript.
- Only use raw Python or raw TypeScript if no supported native adapter owns the loop.
Context
Start with one risky boundary only: a real action or durable memory write that can change business state, send a message, update a record, issue a refund, modify a subscription, change workflow status, write memory, run code, or affect an external system.
VerifiedX checks the pending action using what the agent is about to do, what it already did in the run, compact evidence from prior tool calls, and upstream context when the node is part of a multi-agent or agent+human workflow.
Composed systems
If the protected node is part of a composed system, pass upstream context already available in the codebase into VerifiedX. Do not invent a new schema. Upstream context may include parent workflow state, supervisor instruction, human approval, prior agent summary, task owner, workflow id, or routing context.
In Python, use `with_upstream_context(...)` or `set_upstream_context(...)` only when the matching SDK page supports it.
In TypeScript, use `withUpstreamContext(...)` or `setUpstreamContext(...)` only when the matching SDK page supports it.
Success criteria
- VerifiedX is installed through the exact supported surface already used by this codebase.
- The runtime key is loaded from env, not hardcoded.
- Existing app behavior stays the same except for VerifiedX action protection, blocked-result behavior, loopback, and decision receipts.
- One real risky boundary is protected first.
- A justified action still executes.
- An unjustified action does not execute and returns a decision receipt/replan/terminal result as the docs describe.
Implementation rules
- Read the existing code first, then edit.
- Apply the change in the real codebase. Do not stop at prose or TODO comments.
- Use exact VerifiedX API names from the matching SDK page.
- Do not add custom policy code.
- Do not redesign the workflow.
- Do not add fake fallback lanes.
- Do not rename the builder’s tools just to help VerifiedX.
- Do not send giant prompt/history dumps into VerifiedX.
- Do not protect read-only lookups as actions. Lookups are evidence/support input; high-impact side effects are the boundary.
- Do not invent old semantic config such as `userInstructions`, `pendingAction`, or `pendingMemoryWrite`.
When complete, report:
- detected integration surface,
- docs page used,
- files changed,
- first protected action or memory write,
- whether the system is single-agent or composed,
- how upstream context is passed, if applicable,
- validation run and result.
```
# Anthropic / Claude
Source: https://docs.verifiedx.me/sdks/anthropic
Protect Claude Messages API tool use in Python or TypeScript.
**Best for:** teams using Claude through the Anthropic SDK directly.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
* Python: `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.anthropic)
If doctor shows a supported native adapter already owns the tool loop, use that page instead of the raw provider path.
## Install
```bash Python theme={null}
pip install verifiedx
```
```bash TypeScript theme={null}
npm install @verifiedx-core/sdk
```
This page assumes your app already uses `anthropic` in Python or `@anthropic-ai/sdk` in TypeScript.
## Net-new VerifiedX code
This is the actual VerifiedX delta in an existing Claude Messages tool loop.
```python Python theme={null}
from verifiedx import (
attach_anthropic,
create_anthropic_tool_dispatcher,
init_verifiedx,
)
from verifiedx.anthropic_direct import (
assistant_message_param as anthropic_assistant_message_param,
)
verifiedx = init_verifiedx()
client = attach_anthropic(client, verifiedx=verifiedx)
dispatch_tool_uses = create_anthropic_tool_dispatcher(
verifiedx=verifiedx,
tools=TOOLS,
tool_handlers=HANDLERS,
)
# Inside your existing loop:
messages.append(anthropic_assistant_message_param(message))
tool_results = dispatch_tool_uses(message)
if tool_results:
messages.append({"role": "user", "content": tool_results})
```
```typescript TypeScript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
import {
assistantMessageParam as anthropicAssistantMessageParam,
attach as attachAnthropicDirect,
createToolDispatcher as createAnthropicToolDispatcher,
} from "@verifiedx-core/sdk/anthropic-direct";
const verifiedx = await initVerifiedX();
const client = await attachAnthropicDirect(client, { verifiedx });
const dispatchToolUses = await createAnthropicToolDispatcher({
verifiedx,
tools: TOOLS,
handlers: HANDLERS,
});
// Inside your existing loop:
messages = [...messages, anthropicAssistantMessageParam(message)];
const toolResults = await dispatchToolUses(message);
if (toolResults.length > 0) {
messages = [...messages, { role: "user", content: toolResults }];
}
```
That is the important part. The rest of the full example below is your normal Anthropic client, tool-definition, tool-handler, and message-loop code that you likely already have.
Your native tool surface is the config. VerifiedX uses your existing tool names, descriptions, schemas, and native Claude Messages tool loop as the source of truth for what to preflight. If you want explicit `actions` and `memories` dictionaries, use the raw runtime instead.
## Full example
```python Python theme={null}
import os
from anthropic import Anthropic
from verifiedx import (
attach_anthropic,
create_anthropic_tool_dispatcher,
init_verifiedx,
)
from verifiedx.anthropic_direct import (
assistant_message_param as anthropic_assistant_message_param,
)
TOOLS = [
{
"name": "lookup_workflow",
"description": "Look up an internal workflow before changing state.",
"input_schema": {
"type": "object",
"properties": {"workflow_id": {"type": "string"}},
"required": ["workflow_id"],
},
},
{
"name": "set_workflow_status",
"description": "Update internal workflow status.",
"input_schema": {
"type": "object",
"properties": {
"workflow_id": {"type": "string"},
"status": {"type": "string"},
"reason": {"type": "string"},
},
"required": ["workflow_id", "status", "reason"],
},
},
]
def lookup_workflow(args):
return {
"ok": True,
"workflow": {
"workflow_id": args["workflow_id"],
"status": "pending_review",
},
}
def set_workflow_status(args):
return {
"ok": True,
"workflow_updated": {
"workflow_id": args["workflow_id"],
"status": args["status"],
"reason": args["reason"],
},
}
verifiedx = init_verifiedx()
client = attach_anthropic(
Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]),
verifiedx=verifiedx,
)
dispatch_tool_uses = create_anthropic_tool_dispatcher(
verifiedx=verifiedx,
tools=TOOLS,
tool_handlers={
"lookup_workflow": lookup_workflow,
"set_workflow_status": set_workflow_status,
},
)
messages = [
{
"role": "user",
"content": "Update workflow WF-1002 to awaiting_human because billing verification is missing.",
}
]
while True:
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1200,
system="Use tools instead of prose for operational work.",
messages=messages,
tools=TOOLS,
)
messages.append(anthropic_assistant_message_param(message))
tool_results = dispatch_tool_uses(message)
if not tool_results:
break
messages.append({"role": "user", "content": tool_results})
```
```typescript TypeScript theme={null}
import Anthropic from "@anthropic-ai/sdk";
import { initVerifiedX } from "@verifiedx-core/sdk";
import {
assistantMessageParam as anthropicAssistantMessageParam,
attach as attachAnthropicDirect,
createToolDispatcher as createAnthropicToolDispatcher,
} from "@verifiedx-core/sdk/anthropic-direct";
const TOOLS = [
{
name: "lookup_workflow",
description: "Look up an internal workflow before changing state.",
input_schema: {
type: "object",
properties: { workflow_id: { type: "string" } },
required: ["workflow_id"],
},
},
{
name: "set_workflow_status",
description: "Update internal workflow status.",
input_schema: {
type: "object",
properties: {
workflow_id: { type: "string" },
status: { type: "string" },
reason: { type: "string" },
},
required: ["workflow_id", "status", "reason"],
},
},
];
const HANDLERS = {
async lookup_workflow(args) {
return {
ok: true,
workflow: {
workflow_id: args.workflow_id,
status: "pending_review",
},
};
},
async set_workflow_status(args) {
return {
ok: true,
workflow_updated: {
workflow_id: args.workflow_id,
status: args.status,
reason: args.reason,
},
};
},
};
const verifiedx = await initVerifiedX();
const client = await attachAnthropicDirect(
new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }),
{ verifiedx },
);
const dispatchToolUses = await createAnthropicToolDispatcher({
verifiedx,
tools: TOOLS,
handlers: HANDLERS,
});
let messages = [
{
role: "user",
content: "Update workflow WF-1002 to awaiting_human because billing verification is missing.",
},
];
while (true) {
const message = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1200,
system: "Use tools instead of prose for operational work.",
messages,
tools: TOOLS,
});
messages = [...messages, anthropicAssistantMessageParam(message)];
const toolResults = await dispatchToolUses(message);
if (toolResults.length === 0) {
break;
}
messages = [...messages, { role: "user", content: toolResults }];
}
```
Do not use raw `install_runtime(...)` or `bindHarness(...)` for this path. Keep the native Anthropic Messages API surface and use the Anthropic direct adapter.
## Composed systems
If this Claude Messages loop is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current run has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current run should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```python Python theme={null}
upstream = {
"source": "workflow_supervisor",
"workflow_id": "WF-2203",
"approval_status": "approved_with_follow_up",
"human_review": {
"reviewer": "ops_lead",
"result": "approved",
},
"prior_agent_output": {
"summary": "Billing verification is complete.",
},
}
with verifiedx.with_upstream_context(upstream):
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1200,
system="Use tools instead of prose for operational work.",
messages=messages,
tools=TOOLS,
)
tool_results = dispatch_tool_uses(message)
```
```typescript TypeScript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete.",
},
};
await verifiedx.withUpstreamContext(upstream, async () => {
const message = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1200,
system: "Use tools instead of prose for operational work.",
messages,
tools: TOOLS,
});
const toolResults = await dispatchToolUses(message);
});
```
Upstream context is supporting workflow context from outside the current run. It is not proof that this run already executed any local action.
## Async tool handlers
If your tool handlers are async, use the async dispatcher in Python:
```python theme={null}
tool_results = await dispatch_tool_uses.async_dispatch(message)
```
In TypeScript, the dispatcher is already async:
```typescript theme={null}
const toolResults = await dispatchToolUses(message);
```
## Streaming
The adapter works with the normal Anthropic streaming surfaces too:
* `client.messages.create(...)`
* `client.messages.stream(...)`
* `client.messages.create(..., stream=True)` in Python
* `client.messages.create({ ..., stream: true })` in TypeScript
That includes native tool-history capture from streamed `tool_use` output while preserving the native stream shape.
## What the adapter already captures
Once attached, VerifiedX already captures and protects the native Anthropic Messages surface, including:
* Native `tool_use` history
* Native `server_tool_use` history
* Client-executed custom tools through the Anthropic tool dispatcher
* Built-in mutating memory, text-editor, and bash tool uses when present
* Prompt context from the Messages window across turns
* Normal continuation turns in direct tool loops
* Managed and raw streaming tool selections
Provider-executed server tools such as native web fetch, web search, code execution, and tool search stay in run history as native support inputs. You do not dispatch those yourself.
## What to expect at runtime
Protected boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a client-side tool is replanned, the side effect does not execute. The dispatcher returns Anthropic `tool_result` blocks containing the blocked result shape, including `ok: false`, `blocked: true`, `boundary_outcome`, `safe_next_steps`, and `decision_receipt`, so Claude can keep moving toward the same goal safely.
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your orchestrator or human workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
***
For the raw runtime reference, see the [Python SDK](/sdks/python) and [TypeScript SDK](/sdks/typescript). For the Claude Agent SDK surface, see the Claude Agent SDK page.
# Claude Agent SDK
Source: https://docs.verifiedx.me/sdks/claude-agent-sdk
Attach VerifiedX to the Claude Agent SDK query surface in Python or TypeScript.
**Best for:** teams already using the Claude Agent SDK and wanting native protection without changing runtimes.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
* Python: `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.claude-agent-sdk)
If doctor shows a different native adapter already owns the tool loop, switch to that page before integrating.
## Install
```bash Python theme={null}
pip install verifiedx
```
```bash TypeScript theme={null}
npm install @verifiedx-core/sdk
```
This page assumes your app already uses `claude_agent_sdk` in Python or `@anthropic-ai/claude-agent-sdk` in TypeScript.
## Net-new VerifiedX code
This is the actual VerifiedX delta in an existing Claude Agent SDK query setup.
```python Python theme={null}
from verifiedx import claude_query, init_verifiedx
verifiedx = init_verifiedx()
async for message in claude_query(
prompt=prompt,
verifiedx=verifiedx,
options=options,
):
...
```
```typescript TypeScript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
import { withVerifiedXClaudeQuery } from "@verifiedx-core/sdk/claude-agent";
const verifiedx = await initVerifiedX();
const verifiedxQuery = await withVerifiedXClaudeQuery(claudeQuery, { verifiedx });
for await (const message of verifiedxQuery({ prompt, options })) {
console.log(message);
}
```
That is the important part. Your existing prompt, tools, MCP servers, system prompt, permission mode, and max-turn configuration stay the same.
In Python, if you prefer to wrap an existing callable instead of swapping imports, use `attach_claude_query(...)`. In TypeScript, `withVerifiedXClaudeQuery(...)` is the wrapper surface.
Your native tool surface is the config. VerifiedX uses your existing tool names, descriptions, schemas, MCP servers, built-ins, and approval flow as the source of truth for what to preflight.
## Full example
```python Python theme={null}
import os
from claude_agent_sdk import ClaudeAgentOptions, create_sdk_mcp_server, tool
from verifiedx import claude_query, init_verifiedx
@tool(
"lookup_workflow",
"Look up an internal workflow before changing state.",
{"workflow_id": str},
)
async def lookup_workflow(args: dict[str, str]) -> dict:
return {
"content": [
{
"type": "text",
"text": '{"ok": true, "workflow": {"workflow_id": "%s", "status": "pending_review"}}'
% args["workflow_id"],
}
]
}
@tool(
"set_workflow_status",
"Update internal workflow status.",
{"workflow_id": str, "status": str, "reason": str},
)
async def set_workflow_status(args: dict[str, str]) -> dict:
return {
"content": [
{
"type": "text",
"text": (
'{"ok": true, "workflow_updated": {"workflow_id": "%s", "status": "%s", "reason": "%s"}}'
% (args["workflow_id"], args["status"], args["reason"])
),
}
]
}
support_ops = create_sdk_mcp_server(
"support_ops",
tools=[lookup_workflow, set_workflow_status],
)
verifiedx = init_verifiedx()
options = ClaudeAgentOptions(
tools=[],
mcp_servers={"support_ops": support_ops},
system_prompt="Use tools instead of prose for operational work.",
permission_mode="bypassPermissions",
model=os.environ.get("ANTHROPIC_MODEL", "claude-sonnet-4-6"),
max_turns=20,
)
async for message in claude_query(
prompt="CASE-7003: For workflow WF-1002, inspect it and then update it to awaiting_human because billing verification is missing.",
verifiedx=verifiedx,
options=options,
):
print(message)
```
```typescript TypeScript theme={null}
import { createSdkMcpServer, query as claudeQuery, tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
import { initVerifiedX } from "@verifiedx-core/sdk";
import { withVerifiedXClaudeQuery } from "@verifiedx-core/sdk/claude-agent";
const lookupWorkflow = tool(
"lookup_workflow",
"Look up an internal workflow before changing state.",
{
workflow_id: z.string(),
},
async ({ workflow_id }) => ({
content: [
{
type: "text",
text: JSON.stringify({
ok: true,
workflow: {
workflow_id,
status: "pending_review",
},
}),
},
],
}),
);
const setWorkflowStatus = tool(
"set_workflow_status",
"Update internal workflow status.",
{
workflow_id: z.string(),
status: z.string(),
reason: z.string(),
},
async ({ workflow_id, status, reason }) => ({
content: [
{
type: "text",
text: JSON.stringify({
ok: true,
workflow_updated: { workflow_id, status, reason },
}),
},
],
}),
);
const supportOps = createSdkMcpServer({
name: "support_ops",
tools: [lookupWorkflow, setWorkflowStatus],
});
const verifiedx = await initVerifiedX();
const verifiedxQuery = await withVerifiedXClaudeQuery(claudeQuery, { verifiedx });
const options = {
tools: [],
mcpServers: { support_ops: supportOps },
systemPrompt: "Use tools instead of prose for operational work.",
permissionMode: "bypassPermissions",
model: process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6",
maxTurns: 20,
};
for await (const message of verifiedxQuery({
prompt: "CASE-8003: For workflow WF-1002, inspect it and then update it to awaiting_human because billing verification is missing.",
options,
})) {
console.log(message);
}
```
Do not use raw `install_runtime(...)` or `bindHarness(...)` for this path. Keep the native Claude Agent SDK query surface and wrap that directly.
## Composed systems
If this Claude run is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current run has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current Claude run should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```python Python theme={null}
upstream = {
"source": "workflow_supervisor",
"workflow_id": "WF-2203",
"approval_status": "approved_with_follow_up",
"human_review": {
"reviewer": "ops_lead",
"result": "approved",
},
"prior_agent_output": {
"summary": "Billing verification is complete.",
},
}
with verifiedx.with_upstream_context(upstream):
async for message in claude_query(
prompt=prompt,
verifiedx=verifiedx,
options=options,
):
...
```
```typescript TypeScript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete",
},
};
await verifiedx.withUpstreamContext(upstream, async () => {
for await (const message of verifiedxQuery({ prompt, options })) {
console.log(message);
}
});
```
Upstream context is supporting workflow context from outside the current run. It is not proof that this run already executed any local action.
## What the adapter already captures
Once attached, VerifiedX appends native Claude Agent SDK hooks into your existing query options and protects the SDK surface you already use.
That includes:
* Prompt submission through `UserPromptSubmit`
* Tool boundaries through `PreToolUse`, `PostToolUse`, and `PostToolUseFailure`
* Permission flow through `PermissionRequest` and `PermissionDenied`
* Stop events through `Stop`
* Lifecycle summaries through `Notification`, `PreCompact`, `SessionStart`, `SessionEnd`, `StopFailure`, `SubagentStart`, and `SubagentStop`
* Custom Claude Agent SDK tools and MCP-backed tools
* Mutating built-ins such as `Bash`, `Write`, `Edit`, `MultiEdit`, and `NotebookEdit` as direct protected boundaries
* Read-like built-ins such as `Read`, `Glob`, `Grep`, `WebFetch`, and `WebSearch` as support inputs in run history
`Read` stays a read-only support input here. It is captured truthfully as `file_read`, not misclassified as a memory write.
## What to expect at runtime
Protected boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a step is blocked during `PreToolUse`, VerifiedX returns native Claude hook output with a block decision and deny signal instead of throwing. The side effect does not execute, and Claude gets the reason plus safe-next-step guidance so it can keep moving toward the same goal when possible.
## Production-style validation coverage
The Claude Agent SDK validation paths in this repo cover real workflows including:
* Clean durable memory writes
* Clean record mutations
* Clean internal workflow updates
* Multi-step internal runs across retrieval, mutation, memory, and internal notification
* External email attempts that replan into safer internal Slack fallbacks
* Repeated adversarial external-email attempts that should not keep pushing the same unsafe action
* Built-in `Bash` local journal updates
* Built-in `Write` local note creation
* Built-in `Edit` local note mutation after a `Read` prerequisite step
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your orchestrator or human workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
***
For the raw runtime reference, see the [Python SDK](/sdks/python) and [TypeScript SDK](/sdks/typescript). For the direct Claude / Anthropic adapter outside the Claude Agent SDK, see the Anthropic pages.
# LangGraph
Source: https://docs.verifiedx.me/sdks/langgraph
Protect LangGraph graphs, state updates, stores, and tools without changing your graph design.
**Best for:** teams using LangGraph directly with `StateGraph`, `ToolNode`, stores, checkpointers, or direct state updates.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
* Python: `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.langgraph)
If doctor shows a different native adapter already owns the tool loop, switch to that page before integrating.
## Install
```bash Python theme={null}
pip install verifiedx
```
```bash TypeScript theme={null}
npm install @verifiedx-core/sdk
```
## Net-new VerifiedX code
This is usually the only VerifiedX delta in an existing pure LangGraph setup.
```python Python theme={null}
from verifiedx import init_verifiedx, install_langgraph
vx = init_verifiedx()
install_langgraph(verifiedx=vx)
graph = builder.compile(
checkpointer=InMemorySaver(),
store=InMemoryStore(),
)
```
```typescript TypeScript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
import { install as installLanggraph } from "@verifiedx-core/sdk/langgraph";
const verifiedx = await initVerifiedX();
await installLanggraph({ verifiedx });
const graph = builder.compile({
checkpointer: new MemorySaver(),
store: new InMemoryStore(),
});
```
That one install call is the few-LOC path. Your existing graph nodes, `Command.update`, `ToolNode`, tools, stores, and checkpointer stay the same.
Start with the graph that owns the highest-impact tool call, store write, or state update. LangGraph protection is broad once installed, but rollout can still be incremental.
In Python, `install_langgraph(...)` patches `StateGraph.compile` and LangChain `BaseTool.invoke`. In TypeScript, `installLanggraph(...)` patches `StateGraph.compile`, `StructuredTool.call`, and `ToolNode.runTool`.
Your LangGraph surface is the config. VerifiedX uses your existing node names, tool names, descriptions, schemas, store namespaces, and state-update surfaces as the source of truth for what to preflight.
## If you want explicit wrapping instead of `install`
Most builders do not need this, but it is available.
```python Python theme={null}
from verifiedx import compile as compile_langgraph, init_verifiedx
vx = init_verifiedx()
graph = compile_langgraph(
builder,
verifiedx=vx,
checkpointer=InMemorySaver(),
store=InMemoryStore(),
)
```
```typescript TypeScript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
import {
compile as compileLanggraph,
wrapStore as wrapLanggraphStore,
wrapTools as wrapLanggraphTools,
} from "@verifiedx-core/sdk/langgraph";
const verifiedx = await initVerifiedX();
const store = await wrapLanggraphStore(new InMemoryStore(), { verifiedx });
const tools = await wrapLanggraphTools(rawTools, { verifiedx });
const graph = await compileLanggraph(builder, {
verifiedx,
store,
checkpointer: new MemorySaver(),
});
```
You normally do not need to instantiate `VerifiedXCheckpointer` manually. `install` and `compile` handle LangGraph checkpointers for you.
## Composed systems
If this LangGraph run is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current graph run has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current graph run should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```python Python theme={null}
upstream = {
"source": "workflow_supervisor",
"workflow_id": "WF-2203",
"approval_status": "approved_with_follow_up",
"human_review": {
"reviewer": "ops_lead",
"result": "approved",
},
"prior_agent_output": {
"summary": "Billing verification is complete.",
},
}
with vx.with_upstream_context(upstream):
result = graph.invoke(
{
"messages": [
{
"role": "user",
"content": "Inspect workflow WF-2203 and then update it to awaiting_human.",
}
]
}
)
```
```typescript TypeScript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete.",
},
};
const result = await verifiedx.withUpstreamContext(upstream, async () => {
return await graph.invoke({
messages: [
{
role: "user",
content: "Inspect workflow WF-2203 and then update it to awaiting_human.",
},
],
});
});
```
Upstream context is supporting workflow context from outside the current run. It is not proof that this run already executed any local action.
## What the adapter already captures
Once attached, VerifiedX captures native LangGraph surfaces across both SDKs, including:
* `StateGraph.compile`
* Node registration through `add_node` or `addNode`
* Graph execution through `invoke`, `ainvoke`, `stream`, and `astream` where available
* Selected tool history from `AIMessage.tool_calls` and `ToolMessage.tool_call_id`
* Tool execution through LangChain tool surfaces
* Direct state mutations through `Command.update`
* Direct compiled state mutations through `graph.update_state` or `graph.updateState`
* Store reads through `store.get` and `store.search`
* Store writes through `store.put` and `store.delete`
* Checkpoint events through `checkpointer.put` and `put_writes` or `putWrites`
## What gets preflighted
VerifiedX preflights the high-impact LangGraph boundaries directly.
That includes:
* Wrapped tool executions
* `store.put`
* `store.delete`
* `Command.update`
* Compiled `graph.update_state` or `graph.updateState`
The following surfaces are still captured, but as support history rather than direct guarded boundaries:
* `store.get`
* `store.search`
* `graph.get_state` or `graph.getState`
Checkpoint writes are also captured as native checkpoint events for runtime truth and ingest compatibility, but in the current pure LangGraph path they are not treated as a standalone clean-run guarded boundary.
## Framework and provider attribution
The framework stays `langgraph` on this adapter path, even when your underlying model or tool-call shape comes from another provider.
When LangGraph carries provider hints, VerifiedX still records them truthfully. For example:
* OpenAI-shaped tool call IDs can infer `provider: "openai"`
* Anthropic-shaped tool call IDs can infer `provider: "anthropic"`
That means the framework remains `langgraph`, while provider attribution stays accurate when LangGraph exposes it.
## What to expect at runtime
Protected LangGraph boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a tool, store write, or state update is replanned, the side effect does not execute. VerifiedX returns the blocked result or loopback guidance so the graph can keep moving toward the same goal safely.
If LangGraph already surfaced the selected tool through `AIMessage.tool_calls`, VerifiedX reuses that preflight at execution time instead of sending a duplicate boundary request.
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your orchestrator or workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
***
If your graph is specifically built around provider-native model loops, see the LangGraph OpenAI and LangGraph Anthropic pages for provider-specific examples on top of this same LangGraph boundary layer.
# LangGraph (Anthropic / Claude)
Source: https://docs.verifiedx.me/sdks/langgraph-anthropic
Protect Claude-backed LangGraph graphs, tools, and state updates.
**Best for:** teams using LangGraph with Claude models through `langchain_anthropic` or `@langchain/anthropic`.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
* Python: `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.langgraph-anthropic)
If doctor shows a supported native adapter already owns the graph or tool loop, use that page instead of this provider-specific path.
## Install
```bash Python theme={null}
pip install verifiedx
```
```bash TypeScript theme={null}
npm install @verifiedx-core/sdk
```
## Net-new VerifiedX code
This is usually the only VerifiedX delta in an existing Claude-backed LangGraph setup.
```python Python theme={null}
from langchain_anthropic import ChatAnthropic
from verifiedx import init_verifiedx, install_langgraph
vx = init_verifiedx()
install_langgraph(verifiedx=vx)
model = ChatAnthropic(model="claude-sonnet-4-6", temperature=0).bind_tools(TOOLS)
graph = builder.compile()
```
```typescript TypeScript theme={null}
import { ChatAnthropic } from "@langchain/anthropic";
import { initVerifiedX } from "@verifiedx-core/sdk";
import { install as installLanggraph } from "@verifiedx-core/sdk/langgraph";
const verifiedx = await initVerifiedX();
await installLanggraph({ verifiedx });
const model = new ChatAnthropic({
model: "claude-sonnet-4-5",
temperature: 0,
}).bindTools(tools);
const graph = builder.compile();
```
That is the important part. The rest of your LangGraph graph stays the same: your nodes, edges, `ToolNode` or manual tool loop, store, checkpointer, and state schema do not need to be redesigned.
Do not use the Anthropic direct adapter here. If LangGraph owns the graph and tool loop, use the LangGraph adapter.
Start with the graph that owns the highest-impact tool call or state update. Claude-backed LangGraph protection is broad once installed, but rollout can still be incremental.
Your native tool surface is the config. VerifiedX uses your existing tool names, descriptions, schemas, and LangGraph state/store surfaces as the source of truth for what to preflight.
## Typical graph shape
A normal Claude-backed LangGraph graph still looks like a normal LangGraph graph.
```python Python theme={null}
from langchain_core.messages import SystemMessage
from langgraph.graph import END, START, MessagesState, StateGraph
def assistant(state: MessagesState):
response = model.invoke([SystemMessage(content=SYSTEM_PROMPT), *state["messages"]])
return {"messages": [response]}
def route_after_assistant(state: MessagesState):
last = state["messages"][-1]
return "run_tools" if getattr(last, "tool_calls", None) else END
builder = StateGraph(MessagesState)
builder.add_node("assistant", assistant)
builder.add_node("run_tools", run_tools)
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", route_after_assistant, {"run_tools": "run_tools", END: END})
builder.add_edge("run_tools", "assistant")
graph = builder.compile()
```
```typescript TypeScript theme={null}
import { END, START, MessagesAnnotation, StateGraph } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
const toolNode = new ToolNode(tools);
const builder = new StateGraph(MessagesAnnotation)
.addNode("assistant", async (state) => {
const response = await model.invoke([
{ role: "system", content: SYSTEM_PROMPT },
...state.messages,
]);
return { messages: [response] };
})
.addNode("tools", toolNode)
.addEdge(START, "assistant")
.addConditionalEdges("assistant", (state) =>
state.messages.at(-1)?.tool_calls?.length ? "tools" : END
)
.addEdge("tools", "assistant");
const graph = builder.compile();
```
## If you manually dispatch tools in Python
If your Python graph runs tools manually instead of using `ToolNode`, catch `BoundaryDeniedError` and replay `exc.tool_result()` back into the graph as a normal tool result.
```python theme={null}
from verifiedx import BoundaryDeniedError
try:
result = TOOL_MAP[tool_name].invoke(tool_args)
except BoundaryDeniedError as exc:
result = exc.tool_result()
```
That keeps the Claude tool loop moving safely instead of crashing the graph.
## Composed systems
If this Claude-backed LangGraph run is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current graph run has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current graph run should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```python Python theme={null}
upstream = {
"source": "workflow_supervisor",
"workflow_id": "WF-2203",
"approval_status": "approved_with_follow_up",
"human_review": {
"reviewer": "ops_lead",
"result": "approved",
},
"prior_agent_output": {
"summary": "Billing verification is complete.",
},
}
with vx.with_upstream_context(upstream):
result = graph.invoke(
{
"messages": [
{
"role": "user",
"content": "Inspect workflow WF-2203 and then update it to awaiting_human.",
}
]
}
)
```
```typescript TypeScript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete.",
},
};
const result = await verifiedx.withUpstreamContext(upstream, async () => {
return await graph.invoke({
messages: [
{
role: "user",
content: "Inspect workflow WF-2203 and then update it to awaiting_human.",
},
],
});
});
```
Upstream context is supporting workflow context from outside the current run. It is not proof that this run already executed any local action.
## What the adapter already captures
On this path, VerifiedX captures the native LangGraph surfaces and keeps provider attribution truthful for Claude-backed runs.
That includes:
* `StateGraph.compile`
* Graph execution through `invoke`, `ainvoke`, `stream`, and `astream` where available
* Selected tool history from `AIMessage.tool_calls`
* Tool execution through LangChain tool surfaces and `ToolNode`
* Direct state mutations through `Command.update`
* Direct compiled state mutations through `graph.update_state` or `graph.updateState`
* Store reads through `store.get` and `store.search`
* Store writes through `store.put` and `store.delete`
When Anthropic-style tool call IDs are present, VerifiedX records `provider: "anthropic"` while keeping `framework: "langgraph"`.
## What gets preflighted
VerifiedX preflights the high-impact LangGraph boundaries directly.
That includes:
* Wrapped tool executions
* `store.put`
* `store.delete`
* `Command.update`
* Compiled `graph.update_state` or `graph.updateState`
Claude-selected tools seen first in `AIMessage.tool_calls` are reused at execution time, so VerifiedX does not send duplicate preflight requests for the same boundary.
## What to expect at runtime
Protected LangGraph boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a tool, store write, or state update is replanned, the side effect does not execute. VerifiedX returns the blocked result or loopback guidance so the graph can keep moving toward the same goal safely.
## Production-style validation coverage
The Claude-backed LangGraph validation paths in this repo cover real graph flows including:
* Clean memory writes
* Clean record mutations
* Clean internal workflow-state changes
* Multi-step internal runs across retrieval, memory, mutation, and internal notification
* External email attempts that replan into safer internal Slack updates
* Repeated adversarial external-email attempts that should not keep pushing the same unsafe action
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your orchestrator or workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
***
For the provider-agnostic graph adapter, see [LangGraph](/frameworks/langgraph). For the direct Anthropic adapter outside LangGraph, see the Anthropic SDK pages.
# LangGraph (OpenAI)
Source: https://docs.verifiedx.me/sdks/langgraph-openai
Protect OpenAI-backed LangGraph graphs, tools, and state updates.
**Best for:** teams using LangGraph with OpenAI models through `langchain_openai` or `@langchain/openai`.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
* Python: `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.langgraph-openai)
If doctor shows a supported native adapter already owns the graph or tool loop, use that page instead of this provider-specific path.
## Install
```bash Python theme={null}
pip install verifiedx
```
```bash TypeScript theme={null}
npm install @verifiedx-core/sdk
```
## Net-new VerifiedX code
This is usually the only VerifiedX delta in an existing OpenAI-backed LangGraph setup.
```python Python theme={null}
from langchain_openai import ChatOpenAI
from verifiedx import init_verifiedx, install_langgraph
vx = init_verifiedx()
install_langgraph(verifiedx=vx)
model = ChatOpenAI(model="gpt-5.4-mini", temperature=0).bind_tools(TOOLS)
graph = builder.compile()
```
```typescript TypeScript theme={null}
import { ChatOpenAI } from "@langchain/openai";
import { initVerifiedX } from "@verifiedx-core/sdk";
import { install as installLanggraph } from "@verifiedx-core/sdk/langgraph";
const verifiedx = await initVerifiedX();
await installLanggraph({ verifiedx });
const model = new ChatOpenAI({
model: "gpt-5.4-mini",
temperature: 0,
}).bindTools(tools);
const graph = builder.compile();
```
That is the important part. The rest of your LangGraph graph stays the same: your nodes, edges, `ToolNode` or manual tool loop, store, checkpointer, and state schema do not need to be redesigned.
Do not use the OpenAI direct adapter here. If LangGraph owns the graph and tool loop, use the LangGraph adapter.
Start with the graph that owns the highest-impact tool call or state update. OpenAI-backed LangGraph protection is broad once installed, but rollout can still be incremental.
Your native tool surface is the config. VerifiedX uses your existing tool names, descriptions, schemas, and LangGraph state/store surfaces as the source of truth for what to preflight.
## Typical graph shape
A normal OpenAI-backed LangGraph graph still looks like a normal LangGraph graph.
```python Python theme={null}
from langchain_core.messages import SystemMessage
from langgraph.graph import END, START, MessagesState, StateGraph
def assistant(state: MessagesState):
response = model.invoke([SystemMessage(content=SYSTEM_PROMPT), *state["messages"]])
return {"messages": [response]}
def route_after_assistant(state: MessagesState):
last = state["messages"][-1]
return "run_tools" if getattr(last, "tool_calls", None) else END
builder = StateGraph(MessagesState)
builder.add_node("assistant", assistant)
builder.add_node("run_tools", run_tools)
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", route_after_assistant, {"run_tools": "run_tools", END: END})
builder.add_edge("run_tools", "assistant")
graph = builder.compile()
```
```typescript TypeScript theme={null}
import { END, START, MessagesAnnotation, StateGraph } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
const toolNode = new ToolNode(tools);
const builder = new StateGraph(MessagesAnnotation)
.addNode("assistant", async (state) => {
const response = await model.invoke([
{ role: "system", content: SYSTEM_PROMPT },
...state.messages,
]);
return { messages: [response] };
})
.addNode("tools", toolNode)
.addEdge(START, "assistant")
.addConditionalEdges("assistant", (state) =>
state.messages.at(-1)?.tool_calls?.length ? "tools" : END
)
.addEdge("tools", "assistant");
const graph = builder.compile();
```
## If you manually dispatch tools in Python
If your Python graph runs tools manually instead of using `ToolNode`, catch `BoundaryDeniedError` and replay `exc.tool_result()` back into the graph as a normal tool result.
```python theme={null}
from verifiedx import BoundaryDeniedError
try:
result = TOOL_MAP[tool_name].invoke(tool_args)
except BoundaryDeniedError as exc:
result = exc.tool_result()
```
That keeps the OpenAI tool loop moving safely instead of crashing the graph.
## Composed systems
If this OpenAI-backed LangGraph run is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current graph run has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current graph run should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```python Python theme={null}
upstream = {
"source": "workflow_supervisor",
"workflow_id": "WF-2203",
"approval_status": "approved_with_follow_up",
"human_review": {
"reviewer": "ops_lead",
"result": "approved",
},
"prior_agent_output": {
"summary": "Billing verification is complete.",
},
}
with vx.with_upstream_context(upstream):
result = graph.invoke(
{
"messages": [
{
"role": "user",
"content": "Inspect workflow WF-2203 and then update it to awaiting_human.",
}
]
}
)
```
```typescript TypeScript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete.",
},
};
const result = await verifiedx.withUpstreamContext(upstream, async () => {
return await graph.invoke({
messages: [
{
role: "user",
content: "Inspect workflow WF-2203 and then update it to awaiting_human.",
},
],
});
});
```
Upstream context is supporting workflow context from outside the current run. It is not proof that this run already executed any local action.
## What the adapter already captures
On this path, VerifiedX captures the native LangGraph surfaces and keeps provider attribution truthful for OpenAI-backed runs.
That includes:
* `StateGraph.compile`
* Graph execution through `invoke`, `ainvoke`, `stream`, and `astream` where available
* Selected tool history from `AIMessage.tool_calls`
* Tool execution through LangChain tool surfaces and `ToolNode`
* Direct state mutations through `Command.update`
* Direct compiled state mutations through `graph.update_state` or `graph.updateState`
* Store reads through `store.get` and `store.search`
* Store writes through `store.put` and `store.delete`
When OpenAI-style tool call IDs are present, VerifiedX records `provider: "openai"` while keeping `framework: "langgraph"`.
## What gets preflighted
VerifiedX preflights the high-impact LangGraph boundaries directly.
That includes:
* Wrapped tool executions
* `store.put`
* `store.delete`
* `Command.update`
* Compiled `graph.update_state` or `graph.updateState`
OpenAI-selected tools seen first in `AIMessage.tool_calls` are reused at execution time, so VerifiedX does not send duplicate preflight requests for the same boundary.
## What to expect at runtime
Protected LangGraph boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a tool, store write, or state update is replanned, the side effect does not execute. VerifiedX returns the blocked result or loopback guidance so the graph can keep moving toward the same goal safely.
## Production-style validation coverage
The OpenAI-backed LangGraph validation paths in this repo cover real graph flows including:
* Clean memory writes
* Clean record mutations
* Clean internal workflow-state changes
* Multi-step internal runs across retrieval, memory, mutation, and internal notification
* External email attempts that replan into safer internal Slack updates
* Repeated adversarial external-email attempts that should not keep pushing the same unsafe action
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your orchestrator or workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
***
For the provider-agnostic graph adapter, see [LangGraph](/frameworks/langgraph). For the direct OpenAI adapter outside LangGraph, see the OpenAI SDK pages.
# MCP (Python)
Source: https://docs.verifiedx.me/sdks/mcp-python
Wrap standalone MCP tool handlers with VerifiedX in Python.
**Best for:** teams implementing MCP servers or MCP tool handlers directly in Python and wanting tool-level boundary protection without changing their server design.
Run doctor in the repo before you wire anything:
* Python: `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.mcp-python)
If doctor shows a supported native adapter already owns the tool loop, use that page instead of the raw MCP handler path.
## Install
```bash theme={null}
pip install verifiedx
```
This page is for standalone MCP tool handlers you own directly. If MCP is being surfaced through OpenAI Agents or another higher-level SDK, use that native adapter page instead.
## Net-new VerifiedX code
This is the actual VerifiedX delta in an existing MCP server.
```python theme={null}
from verifiedx import init_verifiedx, wrap_tool_handler
verifiedx = init_verifiedx()
handlers = {
"search_memories": wrap_tool_handler(
"search_memories",
search_memories,
verifiedx=verifiedx,
),
"add_memory": wrap_tool_handler(
"add_memory",
add_memory,
verifiedx=verifiedx,
policy_scope="memory_write",
),
}
# In your existing callTool path:
return handlers[tool_name](params)
```
That is the important part. The rest of your MCP server stays the same: your `listTools()` output, transport, server wiring, and request routing do not need to be redesigned.
If a tool is definitely a durable memory write, pass `policy_scope="memory_write"` instead of relying only on name heuristics.
`wrap_tool_handler(...)` is a thin adapter over VerifiedX core boundary wrappers. It uses the same preflight, decision-receipt, execution-report, and runtime-loopback path as the rest of the product.
Your MCP tool surface is the config here. VerifiedX uses your existing tool names, descriptions, schemas, and params shape as the source of truth for what to preflight.
## Optional: wrap tool definitions too
If your server returns tool definitions directly, you can also wrap them before returning them from `listTools()`:
```python theme={null}
from verifiedx import wrap_tool_definition
wrapped_definitions = [
wrap_tool_definition(definition, verifiedx=verifiedx)
for definition in tool_definitions
]
```
Boundary protection lives in `wrap_tool_handler(...)`. `wrap_tool_definition(...)` just enriches the tool definitions you expose from your MCP server.
## Full example
```python theme={null}
from verifiedx import (
init_verifiedx,
wrap_tool_definition,
wrap_tool_handler,
)
tool_definitions = [
{
"name": "search_memories",
"description": "Search customer memory entries.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"},
},
"required": ["query"],
},
},
{
"name": "add_memory",
"description": "Persist a customer memory entry.",
"input_schema": {
"type": "object",
"properties": {
"namespace": {"type": "string"},
"key": {"type": "string"},
"value": {},
},
"required": ["namespace", "key", "value"],
},
},
{
"name": "set_workflow_status",
"description": "Update internal workflow status.",
"input_schema": {
"type": "object",
"properties": {
"workflow_id": {"type": "string"},
"status": {"type": "string"},
"reason": {"type": "string"},
},
"required": ["workflow_id", "status", "reason"],
},
},
]
definitions_by_name = {definition["name"]: definition for definition in tool_definitions}
verifiedx = init_verifiedx()
wrapped_tool_definitions = [
wrap_tool_definition(definition, verifiedx=verifiedx)
for definition in tool_definitions
]
def search_memories(params):
return {
"ok": True,
"hits": [
{"key": "cust_123.preference", "query": params["query"]},
],
}
def add_memory(params):
return {
"ok": True,
"saved": {
"namespace": params["namespace"],
"key": params["key"],
"value": params["value"],
},
}
def set_workflow_status(params):
return {
"ok": True,
"workflow_updated": {
"workflow_id": params["workflow_id"],
"status": params["status"],
"reason": params["reason"],
},
}
handlers = {
"search_memories": wrap_tool_handler(
"search_memories",
search_memories,
verifiedx=verifiedx,
definition=definitions_by_name["search_memories"],
),
"add_memory": wrap_tool_handler(
"add_memory",
add_memory,
verifiedx=verifiedx,
policy_scope="memory_write",
definition=definitions_by_name["add_memory"],
),
"set_workflow_status": wrap_tool_handler(
"set_workflow_status",
set_workflow_status,
verifiedx=verifiedx,
definition=definitions_by_name["set_workflow_status"],
),
}
def list_tools():
return wrapped_tool_definitions
def call_tool(tool_name, params):
handler = handlers.get(tool_name)
if handler is None:
return {"ok": False, "error": f"Unknown tool: {tool_name}"}
return handler(params)
```
Do not use raw `install_runtime(...)` for this path. The Python MCP surface is `wrap_tool_handler(...)`, with optional `wrap_tool_definition(...)` for `listTools()` metadata.
## Composed systems
If this MCP tool call is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current tool invocation has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current MCP tool should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```python theme={null}
upstream = {
"source": "workflow_supervisor",
"workflow_id": "WF-2203",
"approval_status": "approved_with_follow_up",
"human_review": {
"reviewer": "ops_lead",
"result": "approved",
},
"prior_agent_output": {
"summary": "Billing verification is complete.",
},
}
with verifiedx.with_upstream_context(upstream):
result = handlers["set_workflow_status"](
{
"workflow_id": "WF-2203",
"status": "awaiting_human",
"reason": "billing verification is missing",
}
)
```
Upstream context is supporting workflow context from outside the current tool invocation. It is not proof that this tool already executed any local action.
## Async handlers
If your MCP handler is async, `wrap_tool_handler(...)` preserves that shape:
```python theme={null}
async def add_memory(params):
return {"ok": True, "saved": params}
guarded_add_memory = wrap_tool_handler(
"add_memory",
add_memory,
verifiedx=verifiedx,
policy_scope="memory_write",
)
result = await guarded_add_memory(
{"namespace": "crm", "key": "cust_123.preference", "value": "sms"}
)
```
## What the wrapper already does
Once wrapped, VerifiedX handles the MCP tool boundary directly.
That includes:
* Injecting `_meta.verifiedx` into request params and result payloads
* Recording retrieval-like tools into run history as support inputs
* Preflighting high-impact tools before the handler runs
* Observing tool-result ingress with `sourceUri: "mcp://"`
* Emitting MCP tool-result events with `sourceLineage: ["mcp_tool"]`
## What gets preflighted
VerifiedX infers the protected boundary from the MCP tool name and params.
That includes:
* `memory_write`
* `external_message_send`
* `record_mutation`
* `system_change`
Retrieval-like tools stay in run history as support inputs instead of being treated as mutations.
Examples from the actual Python MCP wrapper behavior:
* `search_memories` records retrieval history
* `add_memory` with `namespace`, `key`, and `value` preflights as `memory_write`
* `send_email` infers `external_message_send`
* `update_customer_record` infers `record_mutation`
* `set_workflow_status` infers `system_change`
The explicit override today is mainly `policy_scope="memory_write"` for durable memory writes. Other action classes are inferred from the MCP tool name and params shape.
## What to expect at runtime
Protected MCP boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a tool is replanned, the side effect does not execute. The wrapped handler returns the normal VerifiedX blocked result shape, including:
* `ok: false`
* `blocked: true`
* `boundary_outcome`
* `safe_next_steps`
* `decision_receipt`
In a standalone MCP server, you will usually consume that blocked result locally and let the caller replan. When the same tool is composed inside a larger orchestration layer, the same receipt can signal either local replan or upstream replan.
## Validation coverage
The standalone Python MCP wrapper is directly covered in this repo.
That includes:
* Retrieval history carrying into a later memory-write preflight
* Durable memory writes preflighting as `memory_write`
* Internal workflow updates preflighting as `system_change`
* Blocked external sends returning the normal loopback result and suppressing the side effect
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your MCP server or orchestration. It returns receipts your system can keep local, route downstream, or pass upstream.
***
For the full raw runtime reference, see the [Python SDK](/sdks/python). If your MCP tools are being used through OpenAI Agents, see the OpenAI Agents SDK page.
# MCP (TypeScript)
Source: https://docs.verifiedx.me/sdks/mcp-typescript
Wrap standalone MCP tool handlers with VerifiedX in TypeScript.
**Best for:** teams implementing MCP servers or MCP tool handlers directly in TypeScript and wanting tool-level boundary protection without changing their server design.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.mcp-typescript)
If doctor shows a supported native adapter already owns the tool loop, use that page instead of the raw MCP handler path.
## Install
```bash theme={null}
npm install @verifiedx-core/sdk
```
This page is for standalone MCP tool handlers you own directly. If MCP is being surfaced through OpenAI Agents or another higher-level SDK, use that native adapter page instead.
## Net-new VerifiedX code
This is the actual VerifiedX delta in an existing MCP server.
```typescript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
import { wrapMcpTool } from "@verifiedx-core/sdk/mcp";
const verifiedx = await initVerifiedX();
const searchMemories = wrapMcpTool(
"search_memories",
async (params) => ({ ok: true, hits: [] }),
{ verifiedx },
);
const addMemory = wrapMcpTool(
"add_memory",
async (params) => ({ ok: true, saved: params }),
{ verifiedx, policyScope: "memory_write" },
);
// In your existing callTool path:
const handlers = {
search_memories: searchMemories,
add_memory: addMemory,
};
return handlers[toolName](params);
```
That is the important part. The rest of your MCP server stays the same: your `listTools()` output, transport, server wiring, and request routing do not need to be redesigned.
If a tool is definitely a durable memory write, pass `policyScope: "memory_write"` instead of relying only on name heuristics.
`wrapMcpTool(...)` is a thin adapter over VerifiedX core boundary wrappers. It uses the same preflight, decision-receipt, execution-report, and runtime-loopback path as the rest of the product.
## Full example
```typescript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
import { wrapMcpTool } from "@verifiedx-core/sdk/mcp";
const toolDefinitions = [
{
name: "search_memories",
description: "Search customer memory entries.",
inputSchema: {
type: "object",
properties: {
query: { type: "string" },
},
required: ["query"],
},
},
{
name: "add_memory",
description: "Persist a customer memory entry.",
inputSchema: {
type: "object",
properties: {
namespace: { type: "string" },
key: { type: "string" },
value: {},
},
required: ["namespace", "key", "value"],
},
},
{
name: "set_workflow_status",
description: "Update internal workflow status.",
inputSchema: {
type: "object",
properties: {
workflow_id: { type: "string" },
status: { type: "string" },
reason: { type: "string" },
},
required: ["workflow_id", "status", "reason"],
},
},
];
const verifiedx = await initVerifiedX();
const handlers = {
search_memories: wrapMcpTool(
"search_memories",
async ({ query }) => ({
ok: true,
hits: [{ key: "cust_123.preference", query }],
}),
{ verifiedx },
),
add_memory: wrapMcpTool(
"add_memory",
async ({ namespace, key, value }) => ({
ok: true,
saved: { namespace, key, value },
}),
{ verifiedx, policyScope: "memory_write" },
),
set_workflow_status: wrapMcpTool(
"set_workflow_status",
async ({ workflow_id, status, reason }) => ({
ok: true,
workflow_updated: { workflow_id, status, reason },
}),
{ verifiedx },
),
};
export async function listTools() {
return toolDefinitions;
}
export async function callTool(toolName, params) {
const handler = handlers[toolName];
if (!handler) {
return { ok: false, error: `Unknown tool: ${toolName}` };
}
return handler(params);
}
```
Do not use raw `bindHarness(...)` for this path. The TypeScript MCP surface is `wrapMcpTool(...)`.
## Composed systems
If this MCP tool call is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current tool invocation has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current MCP tool should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```typescript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete.",
},
};
const result = await verifiedx.withUpstreamContext(upstream, async () => {
return await handlers.set_workflow_status({
workflow_id: "WF-2203",
status: "awaiting_human",
reason: "billing verification is missing",
});
});
```
Upstream context is supporting workflow context from outside the current tool invocation. It is not proof that this tool already executed any local action.
## What the wrapper already does
Once wrapped, VerifiedX handles the MCP tool boundary directly.
That includes:
* Injecting `_meta.verifiedx` into request params and result payloads
* Recording retrieval-like tools into run history as support inputs
* Preflighting high-impact tools before the handler runs
* Observing tool-result ingress with `sourceUri: "mcp://"`
* Emitting MCP tool-result events with `sourceLineage: ["mcp_tool"]`
There is no separate TypeScript tool-definition wrapper. Keep your existing MCP tool definitions as they are and wrap the handlers you register behind `callTool(...)`.
## What gets preflighted
VerifiedX infers the protected boundary from the MCP tool name and params.
That includes:
* `memory_write`
* `external_message_send`
* `record_mutation`
* `system_change`
Retrieval-like tools stay in run history as support inputs instead of being treated as mutations.
Examples from the actual MCP wrapper logic:
* `search_memories` records retrieval history
* `add_memory` with `namespace`, `key`, and `value` preflights as `memory_write`
* message-like tools such as `send_email` infer `external_message_send`
* record-like tools such as `update_customer_record` infer `record_mutation`
* internal update tools such as `set_workflow_status` infer `system_change`
The explicit `policyScope` override is mainly for durable memory writes today. Other action classes are inferred from the MCP tool name and params shape.
## What to expect at runtime
Protected MCP boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a tool is replanned, the side effect does not execute. The wrapped handler returns the normal VerifiedX blocked result shape, including:
* `ok: false`
* `blocked: true`
* `boundary_outcome`
* `safe_next_steps`
* `decision_receipt`
In a standalone MCP server, you will usually consume that blocked result locally and let the caller replan. When the same tool is composed inside a larger orchestration layer, the same receipt can signal either local replan or upstream replan.
## Validation coverage
The standalone TypeScript MCP wrapper is directly covered in this repo.
That includes:
* A dedicated MCP wrapper test where `search_memories` is recorded as retrieval history and `add_memory` is preflighted as `memory_write`
* A fixture app that wraps an MCP tool with `wrapMcpTool(...)`
* Additional MCP `listTools()` and `callTool(...)` coverage inside the OpenAI Agents TypeScript integration, including hosted MCP approval requests
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your MCP server or orchestration. It returns receipts your system can keep local, route downstream, or pass upstream.
***
For the full raw runtime reference, see the [TypeScript SDK](/sdks/typescript). If your MCP tools are being used through OpenAI Agents, see the OpenAI Agents SDK page.
# OpenAI
Source: https://docs.verifiedx.me/sdks/openai
Protect OpenAI Responses and Chat Completions tool calls in Python or TypeScript.
**Best for:** teams using the OpenAI SDK directly with native tool loops.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
* Python: `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.openai)
If doctor shows a supported native adapter already owns the tool loop, use that page instead of the raw provider path.
## Install
```bash Python theme={null}
pip install verifiedx
```
```bash TypeScript theme={null}
npm install @verifiedx-core/sdk
```
This page assumes your app already uses `openai` in Python or `openai` in TypeScript.
## Net-new VerifiedX code
This is the actual VerifiedX delta in an existing OpenAI tool loop.
```python Python theme={null}
from verifiedx import attach_openai, create_openai_tool_dispatcher, init_verifiedx
verifiedx = init_verifiedx()
client = attach_openai(client, verifiedx=verifiedx)
dispatcher = create_openai_tool_dispatcher(
verifiedx=verifiedx,
tools=TOOL_DEFINITIONS,
tool_handlers=TOOL_HANDLERS,
)
# Inside your existing loop:
tool_outputs = dispatcher(result, surface="responses") # or surface="chat"
```
```typescript TypeScript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
import {
attach as attachOpenAIDirect,
createToolDispatcher,
} from "@verifiedx-core/sdk/openai-direct";
const verifiedx = await initVerifiedX();
const client = await attachOpenAIDirect(client, { verifiedx });
const dispatch = await createToolDispatcher({
verifiedx,
tools: TOOL_DEFINITIONS,
handlers: TOOL_HANDLERS,
});
// Inside your existing loop:
const toolOutputs = await dispatch(result, { surface: "responses" }); // or "chat"
```
That is the important part. The rest of the example is your normal OpenAI client, native `tools` definitions, tool handlers, and loop code that you likely already have.
Your native tool surface is the config. VerifiedX uses your existing tool names, descriptions, schemas, and native OpenAI tool loop as the source of truth for what to preflight. If you want explicit `actions` and `memories` dictionaries, use the raw runtime instead.
## Typical setup
Keep your existing OpenAI client and native OpenAI tool definitions.
```python Python theme={null}
from openai import OpenAI
from verifiedx import attach_openai, create_openai_tool_dispatcher, init_verifiedx
TOOL_DEFINITIONS = [
{
"type": "function",
"function": {
"name": "lookup_workflow",
"description": "Look up an internal workflow before changing state.",
"parameters": {
"type": "object",
"properties": {"workflow_id": {"type": "string"}},
"required": ["workflow_id"],
},
},
},
{
"type": "function",
"function": {
"name": "set_workflow_status",
"description": "Update internal workflow status.",
"parameters": {
"type": "object",
"properties": {
"workflow_id": {"type": "string"},
"status": {"type": "string"},
"reason": {"type": "string"},
},
"required": ["workflow_id", "status", "reason"],
},
},
},
]
TOOL_HANDLERS = {
"lookup_workflow": lambda payload: {"ok": True, "workflow": payload},
"set_workflow_status": lambda payload: {"ok": True, "workflow_updated": payload},
}
verifiedx = init_verifiedx()
client = attach_openai(OpenAI(), verifiedx=verifiedx)
dispatcher = create_openai_tool_dispatcher(
verifiedx=verifiedx,
tools=TOOL_DEFINITIONS,
tool_handlers=TOOL_HANDLERS,
)
```
```typescript TypeScript theme={null}
import OpenAI from "openai";
import { initVerifiedX } from "@verifiedx-core/sdk";
import {
attach as attachOpenAIDirect,
createToolDispatcher,
} from "@verifiedx-core/sdk/openai-direct";
const TOOL_DEFINITIONS = [
{
type: "function",
function: {
name: "lookup_workflow",
description: "Look up an internal workflow before changing state.",
parameters: {
type: "object",
properties: { workflow_id: { type: "string" } },
required: ["workflow_id"],
},
},
},
{
type: "function",
function: {
name: "set_workflow_status",
description: "Update internal workflow status.",
parameters: {
type: "object",
properties: {
workflow_id: { type: "string" },
status: { type: "string" },
reason: { type: "string" },
},
required: ["workflow_id", "status", "reason"],
},
},
},
];
const TOOL_HANDLERS = {
async lookup_workflow(payload) {
return { ok: true, workflow: payload };
},
async set_workflow_status(payload) {
return { ok: true, workflow_updated: payload };
},
};
const verifiedx = await initVerifiedX();
const client = await attachOpenAIDirect(
new OpenAI({ apiKey: process.env.OPENAI_API_KEY }),
{ verifiedx },
);
const dispatch = await createToolDispatcher({
verifiedx,
tools: TOOL_DEFINITIONS,
handlers: TOOL_HANDLERS,
});
```
Do not use raw `install_runtime(...)` or `bindHarness(...)` for this path. Keep the native OpenAI client and native `tools` payload you already have.
## Composed systems
If this OpenAI tool loop is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current run has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current run should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```python Python theme={null}
upstream = {
"source": "workflow_supervisor",
"workflow_id": "WF-2203",
"approval_status": "approved_with_follow_up",
"human_review": {
"reviewer": "ops_lead",
"result": "approved",
},
"prior_agent_output": {
"summary": "Billing verification is complete.",
},
}
with verifiedx.with_upstream_context(upstream):
response = client.responses.create(
model="gpt-5.4-mini",
instructions="Use tools instead of prose for operational work.",
input=[{"role": "user", "content": "Update WF-2203 to awaiting_human."}],
tools=TOOL_DEFINITIONS,
)
tool_outputs = dispatcher(response, surface="responses")
```
```typescript TypeScript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete.",
},
};
await verifiedx.withUpstreamContext(upstream, async () => {
const response = await client.responses.create({
model: "gpt-5.4-mini",
instructions: "Use tools instead of prose for operational work.",
input: [{ role: "user", content: "Update WF-2203 to awaiting_human." }],
tools: TOOL_DEFINITIONS,
});
const toolOutputs = await dispatch(response, { surface: "responses" });
});
```
Upstream context is supporting workflow context from outside the current run. It is not proof that this run already executed any local action.
## Responses API
Use the same tool definitions and dispatch the selected tool calls back into the Responses loop.
```python Python theme={null}
response = client.responses.create(
model="gpt-5.4-mini",
instructions="Use tools instead of prose for operational work.",
input=[{"role": "user", "content": "Update WF-1002 to awaiting_human."}],
tools=TOOL_DEFINITIONS,
)
tool_outputs = dispatcher(response, surface="responses")
if tool_outputs:
response = client.responses.create(
model="gpt-5.4-mini",
previous_response_id=response.id,
input=tool_outputs,
tools=TOOL_DEFINITIONS,
)
```
```typescript TypeScript theme={null}
let response = await client.responses.create({
model: "gpt-5.4-mini",
instructions: "Use tools instead of prose for operational work.",
input: [{ role: "user", content: "Update WF-1002 to awaiting_human." }],
tools: TOOL_DEFINITIONS,
});
const toolOutputs = await dispatch(response, { surface: "responses" });
if (toolOutputs.length) {
response = await client.responses.create({
model: "gpt-5.4-mini",
previous_response_id: response.id,
input: toolOutputs,
tools: TOOL_DEFINITIONS,
});
}
```
## Chat Completions
The same dispatcher also works for Chat Completions.
```python Python theme={null}
completion = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "developer", "content": "Use tools instead of prose for operational work."},
{"role": "user", "content": "Update WF-1002 to awaiting_human."},
],
tools=TOOL_DEFINITIONS,
tool_choice="auto",
)
tool_outputs = dispatcher(completion, surface="chat")
```
```typescript TypeScript theme={null}
const completion = await client.chat.completions.create({
model: "gpt-5.4-mini",
messages: [
{ role: "developer", content: "Use tools instead of prose for operational work." },
{ role: "user", content: "Update WF-1002 to awaiting_human." },
],
tools: TOOL_DEFINITIONS,
tool_choice: "auto",
});
const toolOutputs = await dispatch(completion, { surface: "chat" });
```
## Async handlers and streaming
If your Python tool handlers are async, use:
```python theme={null}
tool_outputs = await dispatcher.async_dispatch(response, surface="responses")
```
In TypeScript, the dispatcher is already async.
TypeScript also patches the native stream surfaces:
* `client.responses.stream(...)`
* `client.chat.completions.stream(...)`
That keeps prompt context, tool selections, and final outputs in VerifiedX while preserving the native stream shape.
## What the adapter already captures
VerifiedX keeps the native OpenAI surface intact and uses your existing tool definitions as the source of truth.
On both SDKs it captures:
* `client.responses.create(...)`
* `client.chat.completions.create(...)`
* Lookup and read tools as support inputs in run history
* High-impact tool boundaries before the handler runs
* Durable memory writes, record mutations, system changes, and external messages inferred from tool name, schema, and description
* Native OpenAI Responses items such as `function_call`, `custom_tool_call`, `shell_call`, `local_shell_call`, `mcp_call`, `mcp_list_tools`, and MCP approval items
TypeScript also handles additional native Responses command items such as:
* `apply_patch_call`
* `computer_call`
* `file_search_call`
* `web_search_call`
The direct adapter also runs with the normal runtime lane underneath, so lower-seam coverage stays on as well.
## What to expect at runtime
Tool boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a tool is replanned, the side effect does not execute. The dispatcher returns the blocked result in the native OpenAI tool-output shape so the loop can continue safely.
## Production-style validation coverage
The direct OpenAI validation paths in this repo cover real workflows including:
* Clean memory writes
* Clean system changes
* Multi-step internal operations
* External email attempts that replan into safer internal Slack updates
* Adversarial external-email attempts that should not keep pushing the same unsafe action
* Chat Completions record-mutation flows
* Responses continuation turns that preserve prior tool history
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your orchestrator or human workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
***
For the raw runtime reference, see the [Python SDK](/sdks/python) and [TypeScript SDK](/sdks/typescript). For the OpenAI Agents SDK surface, see the OpenAI Agents SDK page.
# OpenAI Agents SDK
Source: https://docs.verifiedx.me/sdks/openai-agents-sdk
Attach VerifiedX to the OpenAI Agents SDK in Python or TypeScript.
**Best for:** teams already using the OpenAI Agents SDK and wanting native protection without changing runtimes.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
* Python: `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.openai-agents-sdk)
If doctor shows a different native adapter already owns the tool loop, switch to that page before integrating.
## Install
```bash Python theme={null}
pip install verifiedx
```
```bash TypeScript theme={null}
npm install @verifiedx-core/sdk
```
This page assumes your app already uses `agents` in Python or `@openai/agents` in TypeScript.
## Net-new VerifiedX code
This is the smallest truthful integration in an existing OpenAI Agents setup.
```python Python theme={null}
from verifiedx import init_verifiedx, run_openai_agents_sync
verifiedx = init_verifiedx()
result = run_openai_agents_sync(agent, input, verifiedx=verifiedx)
```
```typescript TypeScript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
import { run as verifiedxRun } from "@verifiedx-core/sdk/openai-agents";
const verifiedx = await initVerifiedX();
const result = await verifiedxRun(agent, input, { verifiedx });
```
That is the important part. Your existing agent, tools, instructions, handoffs, and orchestration stay the same.
Start with the wrapper-first path above. If you later want to keep calling the SDK's own runner directly, attach VerifiedX once to the agent or runner instead.
Your native tool surface is the config. VerifiedX uses your existing tool names, descriptions, schemas, approval hints, MCP servers, and built-in tool types as the source of truth for what to preflight.
## Attach once instead of wrapping `run`
If you prefer to attach VerifiedX to the agent and keep calling the SDK runner directly, use the native attach surface.
```python Python theme={null}
from agents import Runner
from verifiedx import attach_openai_agents_agent, init_verifiedx
verifiedx = init_verifiedx()
attach_openai_agents_agent(agent, verifiedx=verifiedx)
result = Runner.run_sync(agent, input)
```
```typescript TypeScript theme={null}
import { run as openaiRun } from "@openai/agents";
import { initVerifiedX } from "@verifiedx-core/sdk";
import { withVerifiedXAgent } from "@verifiedx-core/sdk/openai-agents";
const verifiedx = await initVerifiedX();
await withVerifiedXAgent(agent, { verifiedx });
const result = await openaiRun(agent, input);
```
## Linked runtime / runner setups
If you already manage a runner or trace installer yourself, attach VerifiedX there instead of changing the rest of your code.
```python Python theme={null}
from verifiedx import attach_openai_agents_runner, init_verifiedx
verifiedx = init_verifiedx()
attach_openai_agents_runner(runner, verifiedx=verifiedx)
```
```typescript TypeScript theme={null}
import { withVerifiedXRunner } from "@verifiedx-core/sdk/openai-agents";
await withVerifiedXRunner(runtime, {
verifiedx,
agent,
input,
});
```
## Full example
```python Python theme={null}
from agents import Agent, function_tool
from verifiedx import init_verifiedx, run_openai_agents_sync
@function_tool
def set_workflow_status(workflow_id: str, status: str, reason: str) -> dict:
return {
"ok": True,
"workflow_id": workflow_id,
"status": status,
"reason": reason,
}
agent = Agent(
name="WorkflowOps",
model="gpt-5.4-mini",
instructions="Update internal workflows safely.",
tools=[set_workflow_status],
)
verifiedx = init_verifiedx()
result = run_openai_agents_sync(
agent,
"Set workflow WF-1002 to awaiting_human because billing verification is missing.",
verifiedx=verifiedx,
)
```
```typescript TypeScript theme={null}
import { Agent, tool } from "@openai/agents";
import { z } from "zod";
import { initVerifiedX } from "@verifiedx-core/sdk";
import { run as verifiedxRun } from "@verifiedx-core/sdk/openai-agents";
const setWorkflowStatus = tool({
name: "set_workflow_status",
description: "Update internal workflow status.",
parameters: z.object({
workflow_id: z.string(),
status: z.string(),
reason: z.string(),
}),
async execute({ workflow_id, status, reason }) {
return {
ok: true,
workflow_updated: { workflow_id, status, reason },
};
},
});
const agent = new Agent({
name: "WorkflowOps",
model: "gpt-5.4-mini",
instructions: "Update internal workflows safely.",
tools: [setWorkflowStatus],
});
const verifiedx = await initVerifiedX();
const result = await verifiedxRun(
agent,
"Set workflow WF-1002 to awaiting_human because billing verification is missing.",
{ verifiedx },
);
```
Do not use raw `install_runtime(...)` or `bindHarness(...)` for this path. The OpenAI Agents adapter is native to the SDK and wraps the agent or runner directly.
## Composed systems
If this OpenAI Agents run is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current run has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current run should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```python Python theme={null}
upstream = {
"source": "workflow_supervisor",
"workflow_id": "WF-2203",
"approval_status": "approved_with_follow_up",
"human_review": {
"reviewer": "ops_lead",
"result": "approved",
},
"prior_agent_output": {
"summary": "Billing verification is complete.",
},
}
with verifiedx.with_upstream_context(upstream):
result = run_openai_agents_sync(agent, input, verifiedx=verifiedx)
```
```typescript TypeScript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete.",
},
};
const result = await verifiedx.withUpstreamContext(upstream, async () => {
return await verifiedxRun(agent, input, { verifiedx });
});
```
Upstream context is supporting workflow context from outside the current run. It is not proof that this run already executed any local action.
## What the adapter already captures
Once attached, VerifiedX already captures and protects the native OpenAI Agents surface, including:
* Native tool-call history through tracing
* Tool execution through both `tool.execute(...)` and SDK-style `tool.invoke(...)`
* Durable memory writes inferred from tool name, schema, and description
* High-impact tools such as record mutations, system changes, and external messages
* Handoffs and delegated or subagent traces when present
* MCP server `listTools()` and `callTool(...)`
* Hosted MCP approval requests and MCP calls from response spans
* Native built-in tool types such as shell, apply-patch, computer, file search, web search, tool search, image generation, and MCP-style tools when present
If a trace processor cannot be installed in the current setup, VerifiedX falls back to wrapper-only mode instead of failing the integration. Wrapped tools and MCP boundaries still stay protected.
## What to expect at runtime
Protected boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a tool or memory write is replanned, the side effect does not execute. The wrapped tool returns the normal VerifiedX blocked result shape, including `ok: false`, `blocked: true`, `boundary_outcome`, `safe_next_steps`, and `decision_receipt`, so the agent can keep moving toward the same goal safely or route the receipt upstream when needed.
## Production-style validation coverage
The OpenAI Agents validation paths in this repo cover real workflows including:
* Clean durable memory writes
* Clean record mutations
* Clean internal workflow updates
* Multi-step internal runs across retrieval, mutation, memory, and internal notification
* External email attempts that replan into safer internal Slack fallbacks
* Repeated adversarial external-email attempts that should not keep pushing the same unsafe action
* MCP filesystem list, read, and write flows
* Hosted MCP approval requests captured into history and boundary preflight
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your orchestrator or human workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
***
For the raw runtime reference, see the [Python SDK](/sdks/python) and [TypeScript SDK](/sdks/typescript). For direct OpenAI tool loops outside the Agents SDK, see the OpenAI pages.
# Python SDK
Source: https://docs.verifiedx.me/sdks/python
Use the raw Python runtime to protect a harness you already own.
**Best for:** when you own the harness and want the smallest truthful Python integration.
Run doctor in the repo before you wire anything:
* Python: `verifiedx doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.python)
If doctor shows a supported native adapter already owns the tool loop, use that page instead of the raw runtime path.
If your app already uses OpenAI direct, OpenAI Agents SDK, Anthropic direct, Claude Agent SDK, LangGraph, LangChain, or MCP, prefer that native adapter page instead of the raw runtime.
## Install
```bash theme={null}
pip install verifiedx
```
## Net-new VerifiedX code
This is the real VerifiedX delta in a custom Python harness.
```python theme={null}
from verifiedx import init_verifiedx
class WorkflowNode:
def call_model(self, messages):
...
def lookup_internal_workflow(self, workflow_id):
...
def set_workflow_status(self, workflow_id, status):
...
vx = init_verifiedx()
node = WorkflowNode()
vx.install_runtime(
node,
llm={"call_model": "gpt-5.4-mini"},
retrievals={"lookup_internal_workflow": "internal workflow evidence"},
actions={"set_workflow_status": "set_workflow_status"},
)
```
That is the important part. You keep your existing harness and declare which business methods should be treated as LLM calls, retrievals, memories, tools, or actions.
## Two-step binding
If you want to install the Python runtime once and bind harnesses later, use the two-step path:
```python theme={null}
from verifiedx import init_verifiedx
vx = init_verifiedx()
vx.install_runtime()
vx.bind_harness(
node,
llm={"call_model": "gpt-5.4-mini"},
retrievals={"lookup_internal_workflow": "internal workflow evidence"},
actions={"set_workflow_status": "set_workflow_status"},
)
```
Use `install_runtime(node, ...)` for the fewest lines. Use `install_runtime()` plus `bind_harness(...)` when your process owns multiple nodes or you want runtime patching to happen earlier in startup.
## What the runtime already captures by default
When you call `install_runtime()`, VerifiedX already turns on lower-seam capture for common real-world side effects, including:
* File writes through `builtins.open`
* Async file writes through `aiofiles`
* HTTP and network calls through `urllib`, `requests`, and `httpx`
* Provider and runtime capture for `OpenAI`, `Anthropic`, and `LiteLLM`
* AWS runtime client calls through `botocore`
* Database mutations through `sqlite3`, `psycopg`, `psycopg2`, and `SQLAlchemy`
* Queue publishes through `kombu`
* Browser client actions through Playwright
That means you do not need to enumerate every low-level side effect for VerifiedX to start working. Use explicit method binding when you want your own business methods to become the named boundary.
## When to bind methods explicitly
Explicit binding is how you promote your own harness methods into first-class VerifiedX boundaries.
That lets you choose exactly which methods you want to name and preflight under:
* `llm`
* `retrievals`
* `actions`
* `memories`
* `tools`
This is useful when you want:
* A business-level boundary like `set_workflow_status` instead of only the lower-seam `requests.patch`
* A memory boundary like `remember_customer_preference` instead of only the underlying storage write
* Better receipts with your own `tool_name`
* Optional `schema` and `docstring` metadata on the boundary
* Protection for custom in-process methods that might not otherwise hit an auto-patched lower seam
## Binding categories
Use the smallest surface that matches what the method really does.
### `llm`
Declare model-call methods here. You can use a dict or the string shorthand.
```python theme={null}
llm={"call_model": {"model_name": "gpt-5.4-mini"}}
```
```python theme={null}
llm={"call_model": "gpt-5.4-mini"}
```
### `retrievals`
Declare reads and lookups that provide context to the model here.
Internal reads can use the string shorthand:
```python theme={null}
retrievals={"lookup_internal_workflow": "internal workflow evidence"}
```
For public-web or weaker outside context, set `object_type` to `external_retrieval`:
```python theme={null}
retrievals={
"search_public_web": {
"query": "search public web",
"object_type": "external_retrieval",
},
}
```
You can also use the tuple shorthand:
```python theme={null}
retrievals={
"search_public_web": ("search public web", "external_retrieval"),
}
```
### `actions`
Declare high-impact side effects here, such as record mutations, system changes, internal or external messages, webhooks, and other writes.
```python theme={null}
actions={
"set_workflow_status": {
"tool_name": "set_workflow_status",
},
"send_external_email": {
"tool_name": "send_external_email",
},
}
```
String shorthand also works:
```python theme={null}
actions={
"set_workflow_status": "set_workflow_status",
"send_external_email": "send_external_email",
}
```
Each declared action gets an `action_execute` preflight before it runs.
### `memories`
Declare durable memory writes here.
```python theme={null}
memories={
"remember_customer_preference": {
"tool_name": "remember_customer_preference",
}
}
```
String shorthand also works:
```python theme={null}
memories={"remember_customer_preference": "remember_customer_preference"}
```
Each declared memory method gets a `memory_write` preflight before it runs.
### `tools`
`tools` is still supported for general tool wrapping and tool history.
```python theme={null}
tools={"sync_customer_crm": {"tool_name": "sync_customer_crm"}}
```
String shorthand also works:
```python theme={null}
tools={"sync_customer_crm": "sync_customer_crm"}
```
If a method is definitely a durable memory write or a high-impact side effect, prefer `memories` or `actions`. Use `tools` for general helper wrapping when the real protected boundary may still be caught lower in the runtime.
## Optional metadata
You can attach `schema` and `docstring` metadata to `actions`, `memories`, and `tools` bindings for better boundary context and better receipts.
```python theme={null}
actions={
"set_workflow_status": {
"tool_name": "set_workflow_status",
"docstring": "Update an internal workflow record.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {"type": "string"},
"status": {"type": "string"},
},
"required": ["workflow_id", "status"],
},
}
}
```
## Composed systems
If this node is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current node has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current node should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```python theme={null}
upstream = {
"source": "workflow_supervisor",
"workflow_id": "WF-2203",
"approval_status": "approved_with_follow_up",
"human_review": {
"reviewer": "ops_lead",
"result": "approved",
},
"prior_agent_output": {
"summary": "Billing verification is complete.",
},
}
with vx.with_upstream_context(upstream):
node.set_workflow_status("WF-2203", "awaiting_human")
```
Upstream context is supporting workflow context from outside the current node. It is not proof that this node already executed any local action.
## What to expect at runtime
Protected boundaries in the raw Python runtime can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
In the production-style raw Python scenarios, VerifiedX exercises:
* `allow` for grounded memory writes and internal handoff writes
* `allow_with_warning` when public-web context is supplementary but still relevant to a safe internal next step
* `replan_required` when an unsafe external email is blocked and the same goal continues through a safer internal Slack update
If a boundary is replanned, the bad side effect does not execute. The agent gets loopback guidance and a decision receipt so it can continue safely or route the receipt upstream when needed.
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes the raw runtime and every native adapter.
VerifiedX does not replace your orchestrator or human workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
# TypeScript SDK
Source: https://docs.verifiedx.me/sdks/typescript
Use the raw TypeScript runtime to protect a harness you already own.
**Best for:** when you own the harness and want the smallest truthful TypeScript integration.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.typescript)
If doctor shows a supported native adapter already owns the tool loop, use that page instead of the raw runtime path.
If your app already uses OpenAI direct, OpenAI Agents SDK, Anthropic direct, Claude Agent SDK, LangGraph, LangChain, Vercel AI SDK, or MCP, prefer that native adapter page instead of the raw runtime.
## Install
```bash theme={null}
npm install @verifiedx-core/sdk
```
The raw TypeScript SDK is ESM and targets Node.js 18+.
## Net-new VerifiedX code
This is the real VerifiedX delta in a custom TypeScript harness.
```typescript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
const harness = {
async callModel(messages) {
return { messages };
},
async lookupInternalWorkflow({ workflowId }) {
return { workflowId, status: "legal_complete" };
},
async setWorkflowStatus({ workflowId, status }) {
return { ok: true, workflowId, status };
},
};
const verifiedx = await initVerifiedX();
verifiedx.bindHarness(harness, {
llm: {
callModel: "gpt-5.4-mini",
},
retrievals: {
lookupInternalWorkflow: "internal workflow evidence",
},
actions: {
setWorkflowStatus: "setWorkflowStatus",
},
});
```
That is the important part. You keep your existing harness and declare which business methods should be treated as LLM calls, retrievals, memories, tools, or actions.
## What the runtime already captures by default
When you call `initVerifiedX()`, VerifiedX already installs lower-seam fallbacks for common Node.js side effects, including:
* `globalThis.fetch` and `undici.fetch`
* `node:fs/promises.writeFile` and `appendFile`
* `fs.writeFileSync` and `appendFileSync`
* `pg.Client.query` and `pg.Pool.query` mutation calls
* `amqplib.Channel.publish` and `sendToQueue`
So even without `bindHarness()`, the raw runtime can still preflight real fetch, file, database, and queue side effects. `bindHarness()` is how you get cleaner business-level boundaries, better names, and richer receipts.
## When to bind methods explicitly
Explicit binding is how you promote your own harness methods into first-class VerifiedX boundaries.
That means you can choose exactly which methods you want to name and preflight under:
* `llm`
* `retrievals`
* `actions`
* `memories`
* `tools`
This is useful when you want:
* A business-level boundary like `setWorkflowStatus` instead of only the lower-seam `fetch` or `pg.query`
* A memory boundary like `rememberCustomerPreference` instead of only the underlying storage write
* Better receipts with your own `toolName`
* Optional `schema` and `docstring` metadata on the boundary
* Protection for custom in-process methods that might not otherwise hit an auto-patched lower seam
## Binding categories
Use the smallest surface that matches what the method really does.
### `llm`
Declare model-call methods here. You can use a dict or the string shorthand.
```typescript theme={null}
llm: {
callModel: { modelName: "gpt-5.4-mini" },
}
```
```typescript theme={null}
llm: {
callModel: "gpt-5.4-mini",
}
```
### `retrievals`
Declare reads and lookups that provide context to the model here.
Internal reads can use the string shorthand:
```typescript theme={null}
retrievals: {
lookupInternalWorkflow: "internal workflow evidence",
}
```
For public-web or weaker outside context, set `objectType` to `external_retrieval`:
```typescript theme={null}
retrievals: {
searchPublicWeb: { query: "search public web", objectType: "external_retrieval" },
}
```
You can also use the tuple-style shorthand:
```typescript theme={null}
retrievals: {
searchPublicWeb: ["search public web", "external_retrieval"],
}
```
### `actions`
Declare high-impact business methods here, such as record mutations, system changes, internal or external messages, webhooks, and other writes.
```typescript theme={null}
actions: {
setWorkflowStatus: { toolName: "setWorkflowStatus" },
sendExternalEmail: { toolName: "sendExternalEmail" },
}
```
String shorthand also works:
```typescript theme={null}
actions: {
setWorkflowStatus: "setWorkflowStatus",
sendExternalEmail: "sendExternalEmail",
}
```
VerifiedX then infers whether the protected boundary should be treated as:
* `memory_write`
* `record_mutation`
* `system_change`
* `external_message_send`
If the method does not resolve to one of those high-impact classes, it still gets wrapped as a normal tool boundary with tool history.
### `memories`
Declare durable memory writes here.
```typescript theme={null}
memories: {
rememberCustomerPreference: { toolName: "rememberCustomerPreference" },
}
```
String shorthand also works:
```typescript theme={null}
memories: {
rememberCustomerPreference: "rememberCustomerPreference",
}
```
Each declared memory method gets a `memory_write` preflight before it runs.
### `tools`
`tools` is still supported for general helper wrapping and tool history.
```typescript theme={null}
tools: {
syncCustomerCrm: { toolName: "syncCustomerCrm" },
}
```
String shorthand also works:
```typescript theme={null}
tools: {
syncCustomerCrm: "syncCustomerCrm",
}
```
If a method is definitely a durable memory write or a high-impact side effect, prefer `memories` or `actions`. Use `tools` for general helper wrapping when the real protected boundary may still be caught lower in the runtime.
## Optional metadata
You can attach `schema` and `docstring` metadata to `actions`, `memories`, and `tools` bindings for better boundary context and better receipts.
```typescript theme={null}
actions: {
setWorkflowStatus: {
toolName: "setWorkflowStatus",
docstring: "Update an internal workflow record.",
schema: {
type: "object",
properties: {
workflow_id: { type: "string" },
status: { type: "string" },
},
required: ["workflow_id", "status"],
},
},
}
```
## Composed systems
If this node is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current node has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current node should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```typescript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete.",
},
};
await verifiedx.withUpstreamContext(upstream, async () => {
await harness.setWorkflowStatus({ workflowId: "WF-2203", status: "awaiting_human" });
});
```
Upstream context is supporting workflow context from outside the current node. It is not proof that this node already executed any local action.
## What to expect at runtime
Protected boundaries in the raw TypeScript runtime can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
In the production-style raw TypeScript scenarios, VerifiedX exercises:
* `allow` for grounded memory writes and internal handoff writes
* `allow_with_warning` when public-web context is supplementary but a safe internal next step is still grounded
* `replan_required` when an unsafe external email is blocked and the same goal continues through a safer internal Slack update
If a boundary is replanned, the side effect does not execute. Wrapped methods return a structured blocked result with `ok: false`, `blocked: true`, the boundary outcome, and a decision receipt your system can keep local or route upstream.
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes the raw runtime and every native adapter.
VerifiedX does not replace your orchestrator or human workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
# Vercel AI SDK
Source: https://docs.verifiedx.me/sdks/vercel-ai
Protect Vercel AI SDK tool calls across providers in TypeScript.
**Best for:** teams already using the Vercel AI SDK and wanting one native VerifiedX adapter surface regardless of model provider.
Run doctor in the repo before you wire anything:
* TypeScript: `npx @verifiedx-core/sdk doctor`
Protect one action free: [verifiedx.me](https://verifiedx.me/?vx_source=docs.vercel-ai)
If doctor shows a different native adapter already owns the tool loop, switch to that page before integrating.
## Install
```bash theme={null}
npm install @verifiedx-core/sdk
```
This page assumes your app already uses `ai`. The VerifiedX wrapper surface is the same across supported Vercel model providers.
## Net-new VerifiedX code
The Vercel AI SDK integration surface is the same regardless of whether your model is OpenAI, Anthropic, or another provider.
```typescript theme={null}
import { initVerifiedX } from "@verifiedx-core/sdk";
import { withVerifiedXGenerateText } from "@verifiedx-core/sdk/vercel-ai";
const verifiedx = await initVerifiedX();
const generate = await withVerifiedXGenerateText(generateText, { verifiedx });
const result = await generate({
model,
system,
messages,
tools,
});
```
If you already use `streamText(...)`, wrap that instead:
```typescript theme={null}
import { withVerifiedXStreamText } from "@verifiedx-core/sdk/vercel-ai";
const stream = await withVerifiedXStreamText(streamText, { verifiedx });
const result = await stream({ model, system, messages, tools });
```
If you already use `ToolLoopAgent`, wrap the agent once:
```typescript theme={null}
import { withVerifiedXToolLoopAgent } from "@verifiedx-core/sdk/vercel-ai";
await withVerifiedXToolLoopAgent(agent, { verifiedx });
```
That is the important part. Your existing Vercel AI SDK tool definitions, `onStepFinish`, `experimental_onToolCallStart`, `experimental_onToolCallFinish`, messages, and provider model wiring stay the same.
Your native tool surface is the config. VerifiedX uses your existing tool names, descriptions, schemas, and approval hints as the source of truth for what to preflight.
## Provider examples
The VerifiedX wrapper stays the same. Only the provider model import and model call change.
```typescript OpenAI theme={null}
import { openai } from "@ai-sdk/openai";
const result = await generate({
model: openai("gpt-5.4-mini"),
system,
messages,
tools,
});
```
```typescript Anthropic theme={null}
import { anthropic } from "@ai-sdk/anthropic";
const result = await generate({
model: anthropic("claude-sonnet-4-5"),
system,
messages,
tools,
});
```
## Full example
```typescript theme={null}
import { anthropic } from "@ai-sdk/anthropic";
import { openai } from "@ai-sdk/openai";
import { generateText, tool } from "ai";
import { z } from "zod";
import { initVerifiedX } from "@verifiedx-core/sdk";
import { withVerifiedXGenerateText } from "@verifiedx-core/sdk/vercel-ai";
const PROVIDER = process.env.VERCEL_AI_PROVIDER || "openai";
const MODEL = process.env.VERCEL_AI_MODEL || (PROVIDER === "anthropic" ? "claude-sonnet-4-5" : "gpt-5.4-mini");
function providerModel() {
return PROVIDER === "anthropic" ? anthropic(MODEL) : openai(MODEL);
}
const tools = {
lookup_workflow: tool({
description: "Look up an internal workflow before changing state.",
inputSchema: z.object({
workflow_id: z.string(),
}),
execute: async ({ workflow_id }) => ({
ok: true,
workflow: {
workflow_id,
status: "pending_review",
},
}),
}),
set_workflow_status: tool({
description: "Update internal workflow status.",
inputSchema: z.object({
workflow_id: z.string(),
status: z.string(),
reason: z.string(),
}),
execute: async ({ workflow_id, status, reason }) => ({
ok: true,
workflow_updated: { workflow_id, status, reason },
}),
}),
};
const verifiedx = await initVerifiedX();
const generate = await withVerifiedXGenerateText(generateText, { verifiedx });
const result = await generate({
model: providerModel(),
system: "Use tools instead of prose for operational work.",
messages: [
{
role: "user",
content: "Inspect workflow WF-1002 and then update it to awaiting_human because billing verification is missing.",
},
],
tools,
});
console.log(result.text);
```
Do not use raw `bindHarness(...)` for this path. Keep the native Vercel AI SDK surface and wrap `generateText(...)`, `streamText(...)`, or `ToolLoopAgent` directly.
## Composed systems
If this Vercel AI SDK run is part of a larger multi-agent or agent+human workflow, pass upstream context into VerifiedX so the current run has better system and situational awareness before it takes a high-impact action.
This is useful when a supervisor agent, parent workflow, or human reviewer already has context that the current run should use before taking action.
VerifiedX does not require a fixed schema for this. Pass the upstream context you already have in any JSON-serializable shape.
```typescript theme={null}
const upstream = {
source: "workflow_supervisor",
workflow_id: "WF-2203",
approval_status: "approved_with_follow_up",
human_review: {
reviewer: "ops_lead",
result: "approved",
},
prior_agent_output: {
summary: "Billing verification is complete.",
},
};
const result = await verifiedx.withUpstreamContext(upstream, async () => {
return await generate({
model: providerModel(),
system: "Use tools instead of prose for operational work.",
messages: [
{
role: "user",
content: "Inspect workflow WF-2203 and then update it to awaiting_human.",
},
],
tools,
});
});
```
The same pattern works for `streamText(...)` and `ToolLoopAgent` too.
Upstream context is supporting workflow context from outside the current run. It is not proof that this run already executed any local action.
## Supported surfaces
The VerifiedX Vercel adapter currently wraps:
* `generateText(...)`
* `streamText(...)`
* `ToolLoopAgent.generate(...)`
* `ToolLoopAgent.stream(...)`
It also captures native Vercel lifecycle hooks and payloads around those calls, including:
* `experimental_onToolCallStart`
* `experimental_onToolCallFinish`
* `onStepFinish`
* `toolCalls`
* `toolResults`
* UI tool parts from `messages[].parts`
* Approval-requested and approval-response tool parts
## Provider handling
The adapter surface is provider-agnostic. You use the same VerifiedX wrapper no matter which Vercel model you pass in.
VerifiedX currently infers provider attribution from model metadata such as:
* OpenAI provider values like `openai.responses`
* Anthropic or Claude provider values like `anthropic` or `claude`
* Bedrock-flavored provider values containing `bedrock`
If your model wrapper does not expose provider metadata, VerifiedX still protects the tool loop. The provider label in decision context may just be less specific.
## What the adapter already captures
Once attached, VerifiedX keeps the Vercel AI SDK surface intact and captures the native lifecycle around your tool loop.
That includes:
* Prompt and message context for each top-level invocation
* Fresh VerifiedX run context per top-level invocation
* Native tool-start history from `experimental_onToolCallStart`
* Native tool-finish and tool-result capture from `experimental_onToolCallFinish`
* Native step summaries and tool history from `onStepFinish`
* Vercel UI tool parts in `messages[].parts`
* Approval-requested and approval-response tool parts
* Approval-capable tools declared with `needsApproval`
If the same native tool selection appears through both `experimental_onToolCallStart` and `onStepFinish`, VerifiedX deduplicates that history instead of double-counting it.
## What gets preflighted
VerifiedX wraps your tool `execute(...)` functions directly and infers the protected boundary from the tool name, description, schema shape, and approval hints.
That includes:
* Memory writes such as tools with `namespace`, `key`, and `value`
* Record mutations such as customer, CRM, ticket, account, or workflow updates
* Internal updates such as routing, status, config, flags, metadata, and workflow-state changes
* Message sends such as Slack, email, replies, notifications, and other communication tools
Lookup-style tools stay in run history as support inputs instead of being treated as mutations. In the Vercel tests, tools like `lookup_workflow` are recorded as `internal_retrieval`.
If a tool is otherwise ambiguous but marked `needsApproval`, VerifiedX still treats it as a guarded internal update and records its approval capability in tool metadata.
## What to expect at runtime
Protected boundaries can return:
* `allow`
* `allow_with_warning`
* `replan_required`
* `goal_fail_terminal`
Every outcome includes a structured decision receipt.
If a tool is replanned, the side effect does not execute. The wrapped tool returns the normal VerifiedX blocked result shape, including `ok: false`, `blocked: true`, `boundary_outcome`, `safe_next_steps`, and `decision_receipt`, so your agent can keep moving toward the same goal safely.
Across the tested Vercel AI SDK paths, VerifiedX is exercised to:
* infer OpenAI and Anthropic providers correctly from model configuration
* wrap `generateText(...)`, `streamText(...)`, and `ToolLoopAgent`
* carry native tool-start history into the next boundary preflight
* preserve clean per-invocation run context instead of leaking tool history across separate top-level calls
* record UI approval-request tool parts truthfully
## Pricing note
One protected action check equals one real boundary preflight. Taint, event ingest, execution reports, and decision reads are all included at that price. The Free Sandbox includes every language, provider, framework, and adapter.
VerifiedX does not replace your orchestrator or human workflow. It returns receipts your system can keep local, route downstream, or pass upstream.
***
For the full raw runtime reference, see the [TypeScript SDK](/sdks/typescript).