|
| 1 | +# Claude Agent SDK Multi-Agent Pattern |
| 2 | + |
| 3 | +This pattern integrates Anthropic's Claude Agent SDK with Amazon Bedrock AgentCore, providing Code Interpreter access via an in-process MCP server, subagent delegation via the Task tool, and Gateway tool integration. For a simpler single-agent version without subagents, see the `claude-agent-sdk-single-agent` pattern. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Claude Agent SDK**: Uses Anthropic's official agent SDK (`ClaudeSDKClient`) for agentic workflows on Bedrock |
| 8 | +- **Code Interpreter**: Execute Python code, bash commands, and file operations via an in-process MCP server |
| 9 | +- **Subagent Spawning**: Delegate focused subtasks to a specialized `code-analyst` subagent via the Task tool |
| 10 | +- **Gateway Integration**: Access Lambda-based tools through AgentCore Gateway (MCP protocol with OAuth2 auth) |
| 11 | +- **Session Management**: Resume conversations across requests via `claude_session_id` |
| 12 | +- **Secure Identity**: User identity extracted from validated JWT token (`RequestContext`), not from payload |
| 13 | + |
| 14 | +## Architecture |
| 15 | + |
| 16 | +``` |
| 17 | +User Request |
| 18 | + | |
| 19 | +BedrockAgentCoreApp (agent.py) |
| 20 | + | |
| 21 | +ClaudeSDKClient (Opus model via Bedrock) |
| 22 | + | |
| 23 | + +-- Code Interpreter MCP (in-process) |
| 24 | + | execute_code, execute_command, write_files, read_files |
| 25 | + | |
| 26 | + +-- Gateway MCP (HTTP, optional) |
| 27 | + | Lambda-based tools via AgentCore Gateway |
| 28 | + | |
| 29 | + +-- Task tool (subagent spawning) |
| 30 | + code-analyst (Sonnet) — analyze output, debug errors |
| 31 | +``` |
| 32 | + |
| 33 | +The main agent (Opus) orchestrates work and can delegate to a `code-analyst` subagent (Sonnet) that runs as a separate `claude-code` child process. |
| 34 | + |
| 35 | +## File Structure |
| 36 | + |
| 37 | +``` |
| 38 | +patterns/claude-agent-sdk-multi-agent/ |
| 39 | +├── agent.py # Main entrypoint (BedrockAgentCoreApp) |
| 40 | +├── agents/ |
| 41 | +│ └── subagents.py # Subagent definitions (code-analyst) |
| 42 | +├── code_int_mcp/ |
| 43 | +│ ├── server.py # MCP server with @tool definitions |
| 44 | +│ ├── client.py # boto3 wrapper for AgentCore Code Interpreter API |
| 45 | +│ └── models.py # Pydantic result model |
| 46 | +├── requirements.txt # Python dependencies |
| 47 | +├── Dockerfile # Container build (Python 3.11 + Node.js + claude-code CLI) |
| 48 | +└── README.md |
| 49 | +``` |
| 50 | + |
| 51 | +## Available Tools |
| 52 | + |
| 53 | +| Tool | MCP Prefix | Description | |
| 54 | +|------|-----------|-------------| |
| 55 | +| `execute_code` | `mcp__codeint__` | Execute Python code snippets | |
| 56 | +| `execute_command` | `mcp__codeint__` | Run bash/shell commands | |
| 57 | +| `write_files` | `mcp__codeint__` | Write files in the Code Interpreter session | |
| 58 | +| `read_files` | `mcp__codeint__` | Read files from the Code Interpreter session | |
| 59 | +| `Task` | — | Spawn a subagent for focused subtasks | |
| 60 | +| Gateway tools | `mcp__gateway__*` | Lambda-based tools via AgentCore Gateway | |
| 61 | + |
| 62 | +## Built-in Tool Configuration |
| 63 | + |
| 64 | +The Claude Agent SDK includes built-in tools from claude-code (Bash, Read, Write, etc.). This pattern disables most of them so the agent operates exclusively through Code Interpreter and Gateway MCP tools. |
| 65 | + |
| 66 | +**Disabled built-in tools** (`disallowed_tools` in `ClaudeAgentOptions`): |
| 67 | + |
| 68 | +| Tool | Why disabled | |
| 69 | +|------|-------------| |
| 70 | +| `Bash` | Use `mcp__codeint__execute_command` instead (sandboxed Code Interpreter) | |
| 71 | +| `Write` | Use `mcp__codeint__write_files` instead (sandboxed Code Interpreter) | |
| 72 | +| `Read` | Use `mcp__codeint__read_files` instead (sandboxed Code Interpreter) | |
| 73 | +| `Edit` | Use Code Interpreter file operations instead | |
| 74 | +| `NotebookEdit` | Use Code Interpreter for notebook-style execution | |
| 75 | +| `WebFetch` | Not needed for this pattern | |
| 76 | +| `Glob` | Use Code Interpreter for file discovery | |
| 77 | +| `Grep` | Use Code Interpreter for content searching | |
| 78 | +| `EnterWorktree` | Not applicable in this deployment context | |
| 79 | +| `Skill` | Not applicable in this deployment context | |
| 80 | +| `TodoWrite` | Not applicable in this deployment context | |
| 81 | +| `CronCreate` | Not applicable in this deployment context | |
| 82 | +| `CronDelete` | Not applicable in this deployment context | |
| 83 | +| `CronList` | Not applicable in this deployment context | |
| 84 | + |
| 85 | +**To re-enable a built-in tool**, remove it from the `disallowed_tools` list in the `_build_options()` function in `agent.py`: |
| 86 | + |
| 87 | +```python |
| 88 | +# Before: tool is disabled |
| 89 | +disallowed_tools=["Bash", "Write", "NotebookEdit", "Edit", "WebFetch", "Read", "Glob", "Grep", "EnterWorktree", "Skill", "TodoWrite", "CronCreate", "CronDelete", "CronList"], |
| 90 | + |
| 91 | +# After: Bash re-enabled |
| 92 | +disallowed_tools=["Write", "NotebookEdit", "Edit", "WebFetch", "Read", "Glob", "Grep", "EnterWorktree", "Skill", "TodoWrite", "CronCreate", "CronDelete", "CronList"], |
| 93 | +``` |
| 94 | + |
| 95 | +If you also want the agent to proactively use the re-enabled tool, add it to the `allowed_tools` list and mention it in the `system_prompt`. |
| 96 | + |
| 97 | +**Note**: Subagents inherit the parent's MCP server configuration but have their own `tools` list defined in `agents/subagents.py`. The `disallowed_tools` setting on the parent does not automatically apply to subagents — update their tool lists separately if needed. |
| 98 | + |
| 99 | +## Models |
| 100 | + |
| 101 | +- **Main agent**: `us.anthropic.claude-opus-4-6-v1` |
| 102 | +- **Subagents**: `sonnet` (cost-efficient for focused analysis tasks) |
| 103 | + |
| 104 | +## Streaming Events |
| 105 | + |
| 106 | +The agent yields three event types as SSE `data: {json}` lines: |
| 107 | + |
| 108 | +| Event | Format | Description | |
| 109 | +|-------|--------|-------------| |
| 110 | +| Text | `{"data": "text content"}` | Agent text response | |
| 111 | +| Tool use | `{"current_tool_use": {"name": "...", "input": {...}, "toolUseId": "..."}}` | Tool invocation | |
| 112 | +| Session ID | `{"claude_session_id": "..."}` | Session ID for conversation resumption | |
| 113 | + |
| 114 | +A dedicated frontend parser at `frontend/src/lib/agentcore-client/parsers/claude-agent-sdk.ts` handles these events. Both the single-agent and multi-agent patterns share the same parser. |
| 115 | + |
| 116 | +## Session Management |
| 117 | + |
| 118 | +This pattern uses `claude_session_id` for conversation continuity — **not** AgentCoreMemory. The flow: |
| 119 | + |
| 120 | +1. First request: no `claude_session_id` in payload — a fresh session is created |
| 121 | +2. Agent yields `{"claude_session_id": "..."}` at the end of the response |
| 122 | +3. Subsequent requests: include the returned `claude_session_id` in the payload |
| 123 | +4. The SDK resumes the conversation via the `resume` option in `ClaudeAgentOptions` |
| 124 | +5. If resumption fails (e.g., container replaced), the agent automatically starts a fresh session |
| 125 | + |
| 126 | +## Code Interpreter Session Handling |
| 127 | + |
| 128 | +Code Interpreter sessions are separate from Claude sessions: |
| 129 | + |
| 130 | +1. First call: pass `code_int_session_id: ""` (empty string) |
| 131 | +2. The tool returns a valid session ID in the response |
| 132 | +3. Use the returned session ID for all subsequent Code Interpreter calls |
| 133 | +4. Never generate or fabricate session IDs |
| 134 | + |
| 135 | +## Adding a Subagent |
| 136 | + |
| 137 | +Edit `agents/subagents.py` and add an entry to the dictionary returned by `get_subagent_definitions()`: |
| 138 | + |
| 139 | +```python |
| 140 | +"my-agent": AgentDefinition( |
| 141 | + description="When to use this agent (the main agent reads this to decide delegation)", |
| 142 | + prompt="System prompt defining the agent's role and behavior", |
| 143 | + tools=["mcp__codeint__execute_code", "mcp__gateway__*", "Read", "Grep", "Glob"], |
| 144 | + model="sonnet", |
| 145 | +), |
| 146 | +``` |
| 147 | + |
| 148 | +Constraints: |
| 149 | +- Subagents inherit MCP server configuration from the parent `ClaudeAgentOptions` |
| 150 | +- Subagents **cannot** spawn other subagents (don't include `Task` in their tools) |
| 151 | +- Keep descriptions clear — the main agent uses them to decide when to delegate |
| 152 | + |
| 153 | +## Deployment |
| 154 | + |
| 155 | +```bash |
| 156 | +cd infra-cdk |
| 157 | +# Set pattern in config.yaml: |
| 158 | +# backend: |
| 159 | +# pattern: claude-agent-sdk-multi-agent |
| 160 | +# deployment_type: docker |
| 161 | +cdk deploy |
| 162 | +``` |
| 163 | + |
| 164 | +**Note**: This pattern requires `deployment_type: docker` because it needs Node.js and the `@anthropic-ai/claude-code` npm package installed at build time. ZIP deployment is not supported. |
| 165 | + |
| 166 | +## Security |
| 167 | + |
| 168 | +- **User identity**: Extracted from the validated JWT token via `RequestContext`, not from the payload body |
| 169 | +- **STACK_NAME validation**: Validated for alphanumeric characters (plus `-` and `_`) before use in SSM parameter paths |
| 170 | +- **Payload validation**: Required fields (`prompt`, `runtimeSessionId`) are validated before processing |
| 171 | +- **Gateway auth**: OAuth2 client credentials flow via Cognito for machine-to-machine authentication |
| 172 | +- **Gateway resilience**: If Gateway is unavailable, the agent continues without Gateway tools |
| 173 | + |
| 174 | +## Differences from Strands / LangGraph Patterns |
| 175 | + |
| 176 | +| Feature | Claude Agent SDK (Multi-Agent) | Strands | LangGraph | |
| 177 | +|---------|-------------------------------|---------|-----------| |
| 178 | +| Framework | Anthropic Claude Agent SDK | Strands Agents | LangGraph + LangChain | |
| 179 | +| Model provider | Bedrock (via `CLAUDE_CODE_USE_BEDROCK`) | Bedrock (`BedrockModel`) | Bedrock (`ChatBedrock`) | |
| 180 | +| Memory | `claude_session_id` (SDK-managed) | AgentCoreMemory | AgentCoreMemory | |
| 181 | +| Token streaming | No (complete message blocks) | Yes | Yes | |
| 182 | +| Subagents | Yes (Task tool + `AgentDefinition`) | No (single agent) | No (single agent) | |
| 183 | +| Code Interpreter | In-process MCP server | `StrandsCodeInterpreterTools` | LangGraph tool wrapper | |
| 184 | +| Requires Node.js | Yes (claude-code CLI) | No | No | |
| 185 | +| ZIP deployment | Not supported | Supported | Supported | |
0 commit comments