# Strands Agents Integration

Connect agents built with [Strands Agents](https://strandsagents.com), AWS's open-source Python SDK, to Capsule Security for visibility into every prompt, tool call, and reply - with inline policy enforcement before the model call and before each tool runs, and an inventory of each agent's system prompt, model, and tools.

## Overview

This integration uses the `capsule-strands` Python package, a Strands plugin that runs in-process inside your agent. The plugin registers on the Strands hook system, forwards each event to your Capsule tenant over HTTPS, and applies the server's verdict on the hooks that support inline blocking.

Strands agents are operator-deployed - you run them in your own infrastructure, so there is no SaaS install. The only thing exchanged at install time is a signed token that scopes the plugin to your tenant and environment.

The following hooks are configured:

| Hook | Type | Description |
|  --- | --- | --- |
| **BeforeInvocationEvent** | Blocking | The user's prompt before the model is called - can cancel the invocation |
| **BeforeToolCallEvent** | Blocking | The tool name and input before the tool runs - can cancel the tool call |
| **AfterToolCallEvent** | Observation | The tool result |
| **AfterInvocationEvent** | Observation | The agent's reply |
| **Session start** | Observation | Sent on the first invocation of each agent session, see [Session Start Data](#session-start-data) |


Blocking hooks wait for Capsule's verdict. When Capsule blocks a prompt, the invocation is cancelled and the agent returns the policy's reason instead of calling the model. When Capsule blocks a tool call, the call is cancelled and the model receives an error tool result carrying the reason. The deprecated `Agent.structured_output()` method is gated as well; a blocked prompt raises `capsule_strands.CapsuleBlockedError` carrying the reason. Observation hooks are recorded for audit and never block. The session start is sent before the first prompt is judged; tool results and replies are posted from a background thread, in the order they happened, so they do not slow the agent down.

### Session Start Data

On the first invocation of each agent session, the plugin sends the agent's:

- ID and name
- Description
- System prompt
- Model ID
- Registered tools


Capsule uses this to build the agent's inventory entry.

### Sessions and Agent Identity

Capsule groups events into a session using the first of these that is available:

1. `trace_attributes["session.id"]`, if the agent sets it
2. The session ID of the agent's session manager
3. A random ID generated per `Agent` instance


Capsule identifies an agent by its `agent_id`. When `agent_id` is left at the Strands SDK default, the agent's `name` is used instead. Set an explicit `agent_id` on every agent so its identity does not change if you rename it.

## Prerequisites

Before you begin, ensure you have:

- A Python project that builds agents with **Strands Agents** (`strands-agents` 1.43 or later)
- A **Capsule Security** account with admin access
- Network access from the host running your agent to your Capsule agentsecurity endpoint (e.g. `https://agents.capsule.security`)


## Step 1: Generate a Plugin Token

1. Log in to the **Capsule Security** portal.
2. Navigate to **Integrations** and locate **Strands Agents**.
3. Click **Install** - Capsule generates a JWT scoped to your tenant and Strands environment. The token contains the tenant and environment claims required by the agentsecurity endpoint; treat it as a secret.
4. Copy the generated **endpoint** and **token**. You will set both in the next steps.


## Step 2: Install the Package

Install `capsule-strands` into the same Python environment that runs your agent:

```bash
pip install capsule-strands
```

The package requires `strands-agents` 1.43 or later.

## Step 3: Configure the Plugin

The plugin reads its configuration from environment variables. Set the endpoint and token in the environment where the agent runs:

```bash
export CAPSULE_STRANDS_ENDPOINT=https://agents.capsule.security/v1/strands/hooks/events
export CAPSULE_STRANDS_TOKEN=<token from Step 1>
```

The same settings can be passed as `CapsulePlugin` constructor arguments. A constructor argument takes precedence over the matching environment variable, which is used when the argument is omitted.

### Configuration Options

| Constructor Argument | Env Variable | Type | Default | Description |
|  --- | --- | --- | --- | --- |
| `endpoint` | `CAPSULE_STRANDS_ENDPOINT` | `string` | *(required)* | Capsule Strands hooks endpoint, e.g. `https://agents.capsule.security/v1/strands/hooks/events` |
| `token` | `CAPSULE_STRANDS_TOKEN` | `string` | *(required)* | JWT generated in Step 1, scoped to your tenant and environment |
| `block_on_risk` | `CAPSULE_STRANDS_BLOCK_ON_RISK` | `boolean` | `true` | Apply server `block` verdicts inline on prompts and tool calls. Set to `false` to run in observe-only mode |
| `fail_open` | `CAPSULE_STRANDS_FAIL_OPEN` | `boolean` | `true` | When Capsule is unreachable or returns an error, allow the agent to proceed. Set to `false` to fail closed |
| `timeout_ms` | `CAPSULE_STRANDS_TIMEOUT_MS` | `number` | `35000` | How long to wait for a verdict, in milliseconds |


### Token Storage

Never commit the JWT to version control. Recommended approaches:

- Keep the token in the `CAPSULE_STRANDS_TOKEN` environment variable of the process that runs the agent
- Source it from your secret manager (1Password, AWS Secrets Manager, HashiCorp Vault) at runtime
- Deliver it through the same channel that distributes the rest of your agent's deployment configuration


## Step 4: Attach the Plugin to Your Agent

Add `CapsulePlugin()` to the `plugins` list of each agent you want Capsule to govern:

```python
from strands import Agent
from capsule_strands import CapsulePlugin

agent = Agent(
    agent_id="support-agent",
    name="Acme Support Agent",
    system_prompt="You answer Acme billing questions.",
    tools=[...],
    plugins=[CapsulePlugin()],
)
```

To set the behavior options in code, pass them as constructor arguments. They override the matching environment variables:

```python
plugin = CapsulePlugin(fail_open=False, timeout_ms=10000)
```

Restart your agent so it loads the plugin and the environment variables.

## Step 5: Verify the Installation

1. Run your agent and send it a prompt that calls a tool to generate activity.
2. Log in to the **Capsule Security** portal.
3. Navigate to **Inventory > Agents** and confirm your Strands agent appears with its system prompt, model, and tools.
4. Click on the agent and review the audit logs to verify events are captured:
  - Session start event
  - The user prompt
  - Tool calls and their results
  - The agent's reply


### Troubleshooting

If events are not appearing:

1. **Verify the endpoint is reachable** from the host running your agent:

```bash
curl -sS -o /dev/null -w "%{http_code}\n" https://agents.capsule.security/v1/strands/hooks/events
```
2. **Verify the environment variables are set** - `CAPSULE_STRANDS_ENDPOINT` and `CAPSULE_STRANDS_TOKEN` must be present in the environment of the process that runs the agent, not only in your interactive shell. `CapsulePlugin()` raises a `ValueError` when either is missing.
3. **Verify the plugin is attached** - `plugins=[CapsulePlugin()]` must be on the `Agent` that handles the request. Run `pip show strands-agents` to confirm version 1.43 or later.
4. **Check the agent identity** - an agent appears under its `agent_id`, or under its `name` when `agent_id` is left at the SDK default. Set an explicit `agent_id` to make it easy to find.
5. **Check for timeouts** - if your network has high latency to the Capsule endpoint, increase `timeout_ms` or confirm `fail_open` is set appropriately.
6. **Inspect the agent logs** - the plugin logs every failed request to Capsule as a warning on the `capsule_strands` logger.
7. **Contact Capsule Security support** if issues persist.


## Not Supported Yet

- **MCP server attribution** - tools provided by an MCP server appear in Capsule as plain Strands tools, not attributed to their MCP server.
- **End-user identity** - events are not attributed to the end user of the agent.


## Security Considerations

The Capsule plugin runs in-process inside your agent and observes every prompt, tool call, tool result, and reply. Before deploying:

1. **Protect the JWT** - anyone with the token can post events on behalf of your tenant. Rotate it through the portal if it is exposed.
2. **Choose `fail_open` deliberately** - `true` (the default) prioritizes agent availability and lets actions through when Capsule is unreachable; `false` prioritizes policy enforcement and blocks prompts and tool calls when Capsule cannot be reached.
3. **Set `timeout_ms` for your latency budget** - blocking hooks wait up to `timeout_ms` for a verdict, so the default of 35 seconds can delay the agent when Capsule is slow to answer.
4. **Pin a plugin version** in production (for example `capsule-strands==<version>` in your requirements file) and review changelogs before upgrading.
5. **Use TLS-only endpoints**. The plugin sends a Bearer token on every request - never configure an `http://` endpoint outside local development.


## Support

For help with this integration:

- **Email**: support@capsule.security
- **Include**: Your organization ID, integration status, plugin version, and any error messages


## References

- [Strands Agents](https://strandsagents.com)
- [Capsule Strands Plugin on PyPI](https://pypi.org/project/capsule-strands/)