> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool Defense

> Configure StackOne Defender per-toolset from the Node.js SDK: scan tool call responses, block on risk, or defer to your project dashboard

<Info>
  New to tool defense? Start with [Tool Defense 101](/agents/typescript/tool-defense-101) for an overview of why, how, and when to use it.
</Info>

## Configuration Modes

The `defender` option on `StackOneToolSet` accepts four shapes. Each maps to a distinct behavior for the RPC calls that toolset makes.

### Default (defer to project dashboard)

Omitting `defender` leaves the project's dashboard setting in charge. The SDK adds no `defender_config` field to the RPC payload, so whatever you configured in the StackOne dashboard takes effect.

```typescript theme={null}
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet();
// defenderMode === 'project'
```

The explicit form is equivalent:

```typescript theme={null}
const toolset = new StackOneToolSet({
  defender: { useProjectSettings: true },
});
// defenderMode === 'project'
```

Use the explicit form when you want the call site to document that deferring is intentional rather than an oversight. `useProjectSettings: true` cannot be combined with other defender fields; passing both throws a `ToolSetConfigError` at construction time.

### Explicit SDK-level config

Pass an object to override your project's dashboard setting with explicit per-toolset values. Missing fields fall back to `DEFAULT_DEFENDER_CONFIG`, which has all scanning on and blocking off.

```typescript theme={null}
import { DEFAULT_DEFENDER_CONFIG, StackOneToolSet } from '@stackone/ai';

// Opt in with safe defaults, but block on HIGH or CRITICAL risk
const toolset = new StackOneToolSet({
  defender: { ...DEFAULT_DEFENDER_CONFIG, blockHighRisk: true },
});
// defenderMode === 'explicit'

// Fully specified
const fullyExplicit = new StackOneToolSet({
  defender: {
    enabled: true,
    blockHighRisk: false,
    useTier1Classification: true,
    useTier2Classification: false, // skip ML scoring on this toolset
  },
});
```

| Field                    | Type      | Fallback | Description                                          |
| ------------------------ | --------- | -------- | ---------------------------------------------------- |
| `enabled`                | `boolean` | `true`   | Master switch for this toolset                       |
| `blockHighRisk`          | `boolean` | `false`  | Throw on HIGH or CRITICAL risk instead of annotating |
| `useTier1Classification` | `boolean` | `true`   | Pattern-based detection (regex, role markers)        |
| `useTier2Classification` | `boolean` | `true`   | ML-based detection (local ONNX model)                |

### Force disabled

Pass `null` to forcibly disable defender for this toolset. The SDK sends `defender_config` with every field set to `false`, overriding whatever the dashboard says.

```typescript theme={null}
const toolset = new StackOneToolSet({ defender: null });
// defenderMode === 'disabled'
```

Use this for trusted internal flows where you've already vetted the data source and want to avoid scanning latency.

## Inspecting the Resolved Mode

Every toolset exposes a `defenderMode` getter that returns one of three strings:

```typescript theme={null}
const toolset = new StackOneToolSet({
  defender: { ...DEFAULT_DEFENDER_CONFIG, blockHighRisk: true },
});

toolset.defenderMode; // 'project' | 'disabled' | 'explicit'
```

| Value        | Meaning                                                                        |
| ------------ | ------------------------------------------------------------------------------ |
| `'project'`  | SDK adds no `defender_config`; the project dashboard setting controls behavior |
| `'disabled'` | SDK forces defender off, overriding the dashboard                              |
| `'explicit'` | SDK sends an explicit `defender_config`, overriding the dashboard              |

The getter is useful in tests, custom observability, and conditional logging without poking at private internals.

### Override warning

When the SDK overrides the project dashboard (modes `disabled` or `explicit`), it emits a `console.warn` once per process per distinct override shape. Repeat constructions with the same configuration stay quiet, so wrapping the SDK in a per-request factory will not flood your logs.

```
Defender forcibly disabled via SDK config; project dashboard setting will be ignored.
Defender configured via SDK (enabled=true, blockHighRisk=true, useTier1Classification=true, useTier2Classification=true); project dashboard setting will be ignored.
```

## Reading the Response

When defender runs (modes `explicit` or, on the backend side, when `project` mode resolves to enabled), the RPC response includes a `defenderMetadata` sibling next to `data`:

```typescript theme={null}
const tools = await toolset.fetchTools();
const tool = tools.toArray().find((t) => t.name === 'gmail_list_messages');

const result = await tool.execute({ body: { max_results: 5 } });

const metadata = (result as { defenderMetadata?: DefenderMetadata }).defenderMetadata;

interface DefenderMetadata {
  applied: boolean;
  result: {
    allowed: boolean;
    riskLevel: 'low' | 'medium' | 'high' | 'critical';
    fieldsSanitized: string[];
    patternsByField: Record<string, string[]>;
    detections: unknown[];
    tier2SkipReason?: string;
    latencyMs: number;
  };
}
```

| Field                    | Description                                                                                                       |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `applied`                | `false` when defender was configured but skipped (e.g. all classification disabled), `true` when it ran           |
| `result.allowed`         | `false` only when `blockHighRisk` was on and content classified as HIGH or CRITICAL caused the call to be blocked |
| `result.riskLevel`       | Final risk classification for the response                                                                        |
| `result.fieldsSanitized` | JSON paths to fields whose content was annotated or stripped                                                      |
| `result.patternsByField` | Per-field map of detection pattern IDs that fired                                                                 |
| `result.detections`      | Raw detection records from Tier 1 / Tier 2                                                                        |
| `result.tier2SkipReason` | Present only when Tier 2 was skipped (e.g. `"No strings extracted from tool result"`)                             |
| `result.latencyMs`       | Time defender spent classifying this response                                                                     |

A typed accessor is not yet exposed on the SDK response surface, so casting through the response object is the current ergonomic.

## Runtime Validation

The constructor validates `useProjectSettings: true` is not combined with other defender fields. JavaScript callers that bypass TypeScript get a runtime error:

```typescript theme={null}
new StackOneToolSet({
  defender: { useProjectSettings: true, enabled: true },
});
// throws ToolSetConfigError:
// "Cannot combine useProjectSettings: true with explicit defender options. Use one or the other."
```

TypeScript catches this at compile time via the discriminated union; the runtime guard exists for JS consumers and runtime-built configs.

## Next Steps

* [Full runnable example](https://github.com/StackOneHQ/stackone-ai-node/blob/main/examples/defender-config.ts) covering all four modes, the `defenderMode` getter, and the override warning
* [Defender (platform guide)](/guides/defender) for dashboard configuration, detection pipeline, and risk thresholds
* [Tool Filtering](/agents/typescript/tool-filtering) for scoping toolsets by provider, action, or account
* [Basic Usage](/agents/typescript/basic-usage) for fetching and executing tools
