mirror of
https://github.com/anthropics/skills
synced 2026-04-22 20:33:25 +00:00
Update claude-api skill with Managed Agents guidance (#891)
* Update claude-api skill with Managed Agents guidance * Replace OPUS_ID placeholder with concrete model string in claude-api skill * Replace remaining model placeholders with concrete model names and IDs
This commit is contained in:
@@ -1,13 +1,22 @@
|
|||||||
---
|
|
||||||
name: claude-api
|
|
||||||
description: "Build apps with the Claude API or Anthropic SDK. TRIGGER when: code imports `anthropic`/`@anthropic-ai/sdk`/`claude_agent_sdk`, or user asks to use Claude API, Anthropic SDKs, or Agent SDK. DO NOT TRIGGER when: code imports `openai`/other AI SDK, general programming, or ML/data-science tasks."
|
|
||||||
license: Complete terms in LICENSE.txt
|
|
||||||
---
|
|
||||||
|
|
||||||
# Building LLM-Powered Applications with Claude
|
# Building LLM-Powered Applications with Claude
|
||||||
|
|
||||||
This skill helps you build LLM-powered applications with Claude. Choose the right surface based on your needs, detect the project language, then read the relevant language-specific documentation.
|
This skill helps you build LLM-powered applications with Claude. Choose the right surface based on your needs, detect the project language, then read the relevant language-specific documentation.
|
||||||
|
|
||||||
|
## Before You Start
|
||||||
|
|
||||||
|
Scan the target file (or, if no target file, the prompt and project) for non-Anthropic provider markers — `import openai`, `from openai`, `langchain_openai`, `OpenAI(`, `gpt-4`, `gpt-5`, file names like `agent-openai.py` or `*-generic.py`, or any explicit instruction to keep the code provider-neutral. If you find any, stop and tell the user that this skill produces Claude/Anthropic SDK code; ask whether they want to switch the file to Claude or want a non-Claude implementation. Do not edit a non-Anthropic file with Anthropic SDK calls.
|
||||||
|
|
||||||
|
## Output Requirement
|
||||||
|
|
||||||
|
When the user asks you to add, modify, or implement a Claude feature, your code must call Claude through one of:
|
||||||
|
|
||||||
|
1. **The official Anthropic SDK** for the project's language (`anthropic`, `@anthropic-ai/sdk`, `com.anthropic.*`, etc.). This is the default whenever a supported SDK exists for the project.
|
||||||
|
2. **Raw HTTP** (`curl`, `requests`, `fetch`, `httpx`, etc.) — only when the user explicitly asks for cURL/REST/raw HTTP, the project is a shell/cURL project, or the language has no official SDK.
|
||||||
|
|
||||||
|
Never mix the two — don't reach for `requests`/`fetch` in a Python or TypeScript project just because it feels lighter. Never fall back to OpenAI-compatible shims.
|
||||||
|
|
||||||
|
**Never guess SDK usage.** Function names, class names, namespaces, method signatures, and import paths must come from explicit documentation — either the `{lang}/` files in this skill or the official SDK repositories or documentation links listed in `shared/live-sources.md`. If the binding you need is not explicitly documented in the skill files, WebFetch the relevant SDK repo from `shared/live-sources.md` before writing code. Do not infer Ruby/Java/Go/PHP/C# APIs from cURL shapes or from another language's SDK.
|
||||||
|
|
||||||
## Defaults
|
## Defaults
|
||||||
|
|
||||||
Unless the user requests otherwise:
|
Unless the user requests otherwise:
|
||||||
@@ -16,6 +25,14 @@ For the Claude model version, please use Claude Opus 4.6, which you can access v
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Subcommands
|
||||||
|
|
||||||
|
If the User Request at the bottom of this prompt is a bare subcommand string (no prose), search every **Subcommands** table in this document — including any in sections appended below — and follow the matching Action column directly. This lets users invoke specific flows via `/claude-api <subcommand>`. If no table in the document matches, treat the request as normal prose.
|
||||||
|
|
||||||
|
<!-- Subcommand tables are defined per-section below; this header block contains only the dispatch rule so that feature-gated sections can add their own tables without leaking strings into ungated builds. -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Language Detection
|
## Language Detection
|
||||||
|
|
||||||
Before reading code examples, determine which language the user is working in:
|
Before reading code examples, determine which language the user is working in:
|
||||||
@@ -52,16 +69,18 @@ Before reading code examples, determine which language the user is working in:
|
|||||||
|
|
||||||
### Language-Specific Feature Support
|
### Language-Specific Feature Support
|
||||||
|
|
||||||
| Language | Tool Runner | Agent SDK | Notes |
|
| Language | Tool Runner | Managed Agents | Notes |
|
||||||
| ---------- | ----------- | --------- | ------------------------------------- |
|
| ---------- | ----------- | -------------- | ------------------------------------- |
|
||||||
| Python | Yes (beta) | Yes | Full support — `@beta_tool` decorator |
|
| Python | Yes (beta) | Yes (beta) | Full support — `@beta_tool` decorator |
|
||||||
| TypeScript | Yes (beta) | Yes | Full support — `betaZodTool` + Zod |
|
| TypeScript | Yes (beta) | Yes (beta) | Full support — `betaZodTool` + Zod |
|
||||||
| Java | Yes (beta) | No | Beta tool use with annotated classes |
|
| Java | Yes (beta) | Yes (beta) | Beta tool use with annotated classes |
|
||||||
| Go | Yes (beta) | No | `BetaToolRunner` in `toolrunner` pkg |
|
| Go | Yes (beta) | Yes (beta) | `BetaToolRunner` in `toolrunner` pkg |
|
||||||
| Ruby | Yes (beta) | No | `BaseTool` + `tool_runner` in beta |
|
| Ruby | Yes (beta) | Yes (beta) | `BaseTool` + `tool_runner` in beta |
|
||||||
| cURL | N/A | N/A | Raw HTTP, no SDK features |
|
| C# | No | No | Official SDK |
|
||||||
| C# | No | No | Official SDK |
|
| PHP | Yes (beta) | Yes (beta) | `BetaRunnableTool` + `toolRunner()` |
|
||||||
| PHP | Yes (beta) | No | `BetaRunnableTool` + `toolRunner()` |
|
| cURL | N/A | Yes (beta) | Raw HTTP, no SDK features |
|
||||||
|
|
||||||
|
> **Managed Agents code examples**: dedicated language-specific READMEs are provided for Python, TypeScript, Go, Ruby, PHP, Java, and cURL (`{lang}/managed-agents/README.md`, `curl/managed-agents.md`). Read your language's README plus the language-agnostic `shared/managed-agents-*.md` concept files. **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.create` and pass it to every subsequent `sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI is one convenient way to create agents and environments from version-controlled YAML — its URL is in `shared/live-sources.md`. If a binding you need isn't shown in the README, WebFetch the relevant entry from `shared/live-sources.md` rather than guess. C# does not currently have Managed Agents support; use cURL-style raw HTTP requests against the API.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -69,37 +88,44 @@ Before reading code examples, determine which language the user is working in:
|
|||||||
|
|
||||||
> **Start simple.** Default to the simplest tier that meets your needs. Single API calls and workflows handle most use cases — only reach for agents when the task genuinely requires open-ended, model-driven exploration.
|
> **Start simple.** Default to the simplest tier that meets your needs. Single API calls and workflows handle most use cases — only reach for agents when the task genuinely requires open-ended, model-driven exploration.
|
||||||
|
|
||||||
| Use Case | Tier | Recommended Surface | Why |
|
| Use Case | Tier | Recommended Surface | Why |
|
||||||
| ----------------------------------------------- | --------------- | ------------------------- | --------------------------------------- |
|
| ----------------------------------------------- | --------------- | ------------------------- | ------------------------------------------------------------ |
|
||||||
| Classification, summarization, extraction, Q&A | Single LLM call | **Claude API** | One request, one response |
|
| Classification, summarization, extraction, Q&A | Single LLM call | **Claude API** | One request, one response |
|
||||||
| Batch processing or embeddings | Single LLM call | **Claude API** | Specialized endpoints |
|
| Batch processing or embeddings | Single LLM call | **Claude API** | Specialized endpoints |
|
||||||
| Multi-step pipelines with code-controlled logic | Workflow | **Claude API + tool use** | You orchestrate the loop |
|
| Multi-step pipelines with code-controlled logic | Workflow | **Claude API + tool use** | You orchestrate the loop |
|
||||||
| Custom agent with your own tools | Agent | **Claude API + tool use** | Maximum flexibility |
|
| Custom agent with your own tools | Agent | **Claude API + tool use** | Maximum flexibility |
|
||||||
| AI agent with file/web/terminal access | Agent | **Agent SDK** | Built-in tools, safety, and MCP support |
|
| Server-managed stateful agent with workspace | Agent | **Managed Agents** | Anthropic runs the loop and hosts the tool-execution sandbox |
|
||||||
| Agentic coding assistant | Agent | **Agent SDK** | Designed for this use case |
|
| Persisted, versioned agent configs | Agent | **Managed Agents** | Agents are stored objects; sessions pin to a version |
|
||||||
| Want built-in permissions and guardrails | Agent | **Agent SDK** | Safety features included |
|
| Long-running multi-turn agent with file mounts | Agent | **Managed Agents** | Per-session containers, SSE event stream, Skills + MCP |
|
||||||
|
|
||||||
> **Note:** The Agent SDK is for when you want built-in file/web/terminal tools, permissions, and MCP out of the box. If you want to build an agent with your own tools, Claude API is the right choice — use the tool runner for automatic loop handling, or the manual loop for fine-grained control (approval gates, custom logging, conditional execution).
|
> **Note:** Managed Agents is the right choice when you want Anthropic to run the agent loop *and* host the container where tools execute — file ops, bash, code execution all run in the per-session workspace. If you want to host the compute yourself or run your own custom tool runtime, Claude API + tool use is the right choice — use the tool runner for automatic loop handling, or the manual loop for fine-grained control (approval gates, custom logging, conditional execution).
|
||||||
|
|
||||||
|
> **Third-party providers (Amazon Bedrock, Google Vertex AI, Microsoft Foundry):** Managed Agents is **not available** on Bedrock, Vertex, or Foundry. If you are deploying through any third-party provider, use **Claude API + tool use** for all use cases — including ones where Managed Agents would otherwise be the recommended surface.
|
||||||
|
|
||||||
### Decision Tree
|
### Decision Tree
|
||||||
|
|
||||||
```
|
```
|
||||||
What does your application need?
|
What does your application need?
|
||||||
|
|
||||||
|
0. Are you deploying through Amazon Bedrock, Google Vertex AI, or Microsoft Foundry?
|
||||||
|
└── Yes → Claude API (+ tool use for agents) — Managed Agents is 1P only.
|
||||||
|
No → continue.
|
||||||
|
|
||||||
1. Single LLM call (classification, summarization, extraction, Q&A)
|
1. Single LLM call (classification, summarization, extraction, Q&A)
|
||||||
└── Claude API — one request, one response
|
└── Claude API — one request, one response
|
||||||
|
|
||||||
2. Does Claude need to read/write files, browse the web, or run shell commands
|
2. Do you want Anthropic to run the agent loop and host a per-session
|
||||||
as part of its work? (Not: does your app read a file and hand it to Claude —
|
container where Claude executes tools (bash, file ops, code)?
|
||||||
does Claude itself need to discover and access files/web/shell?)
|
└── Yes → Managed Agents — server-managed sessions, persisted agent configs,
|
||||||
└── Yes → Agent SDK — built-in tools, don't reimplement them
|
SSE event stream, Skills + MCP, file mounts.
|
||||||
Examples: "scan a codebase for bugs", "summarize every file in a directory",
|
Examples: "stateful coding agent with a workspace per task",
|
||||||
"find bugs using subagents", "research a topic via web search"
|
"long-running research agent that streams events to a UI",
|
||||||
|
"agent with persisted, versioned config used across many sessions"
|
||||||
|
|
||||||
3. Workflow (multi-step, code-orchestrated, with your own tools)
|
3. Workflow (multi-step, code-orchestrated, with your own tools)
|
||||||
└── Claude API with tool use — you control the loop
|
└── Claude API with tool use — you control the loop
|
||||||
|
|
||||||
4. Open-ended agent (model decides its own trajectory, your own tools)
|
4. Open-ended agent (model decides its own trajectory, your own tools, you host the compute)
|
||||||
└── Claude API agentic loop (maximum flexibility)
|
└── Claude API agentic loop (maximum flexibility)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -152,7 +178,7 @@ A note: if any of the model strings above look unfamiliar to you, that's to be e
|
|||||||
|
|
||||||
**Opus 4.6 — Adaptive thinking (recommended):** Use `thinking: {type: "adaptive"}`. Claude dynamically decides when and how much to think. No `budget_tokens` needed — `budget_tokens` is deprecated on Opus 4.6 and Sonnet 4.6 and must not be used. Adaptive thinking also automatically enables interleaved thinking (no beta header needed). **When the user asks for "extended thinking", a "thinking budget", or `budget_tokens`: always use Opus 4.6 with `thinking: {type: "adaptive"}`. The concept of a fixed token budget for thinking is deprecated — adaptive thinking replaces it. Do NOT use `budget_tokens` and do NOT switch to an older model.**
|
**Opus 4.6 — Adaptive thinking (recommended):** Use `thinking: {type: "adaptive"}`. Claude dynamically decides when and how much to think. No `budget_tokens` needed — `budget_tokens` is deprecated on Opus 4.6 and Sonnet 4.6 and must not be used. Adaptive thinking also automatically enables interleaved thinking (no beta header needed). **When the user asks for "extended thinking", a "thinking budget", or `budget_tokens`: always use Opus 4.6 with `thinking: {type: "adaptive"}`. The concept of a fixed token budget for thinking is deprecated — adaptive thinking replaces it. Do NOT use `budget_tokens` and do NOT switch to an older model.**
|
||||||
|
|
||||||
**Effort parameter (GA, no beta header):** Controls thinking depth and overall token spend via `output_config: {effort: "low"|"medium"|"high"|"max"}` (inside `output_config`, not top-level). Default is `high` (equivalent to omitting it). `max` is Opus 4.6 only. Works on Opus 4.5, Opus 4.6, and Sonnet 4.6. Will error on Sonnet 4.5 / Haiku 4.5. Combine with adaptive thinking for the best cost-quality tradeoffs. Use `low` for subagents or simple tasks; `max` for the deepest reasoning.
|
**Effort parameter (GA, no beta header):** Controls thinking depth and overall token spend via `output_config: {effort: "low"|"medium"|"high"|"max"}` (inside `output_config`, not top-level). Default is `high` (equivalent to omitting it). `max` is Opus 4.6 only. Works on Opus 4.5, Opus 4.6, and Sonnet 4.6. Will error on Sonnet 4.5 / Haiku 4.5. Combine with adaptive thinking for the best cost-quality tradeoffs. Lower effort means fewer and more-consolidated tool calls, less preamble, and terser confirmations — `medium` is often a favorable balance; use `max` when correctness matters more than cost; use `low` for subagents or simple tasks.
|
||||||
|
|
||||||
**Sonnet 4.6:** Supports adaptive thinking (`thinking: {type: "adaptive"}`). `budget_tokens` is deprecated on Sonnet 4.6 — use adaptive thinking instead.
|
**Sonnet 4.6:** Supports adaptive thinking (`thinking: {type: "adaptive"}`). `budget_tokens` is deprecated on Sonnet 4.6 — use adaptive thinking instead.
|
||||||
|
|
||||||
@@ -182,6 +208,30 @@ For placement patterns, architectural guidance, and the silent-invalidator audit
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Managed Agents (Beta)
|
||||||
|
|
||||||
|
**Managed Agents** is a third surface: server-managed stateful agents with Anthropic-hosted tool execution. You create a persisted, versioned Agent config (`POST /v1/agents`), then start Sessions that reference it. Each session provisions a container as the agent's workspace — bash, file ops, and code execution run there; the agent loop itself runs on Anthropic's orchestration layer and acts on the container via tools. The session streams events; you send messages and tool results back.
|
||||||
|
|
||||||
|
**Managed Agents is first-party only.** It is not available on Amazon Bedrock, Google Vertex AI, or Microsoft Foundry. For agents on third-party providers, use Claude API + tool use.
|
||||||
|
|
||||||
|
**Mandatory flow:** Agent (once) → Session (every run). `model`/`system`/`tools` live on the agent, never the session. See `shared/managed-agents-overview.md` for the full reading guide, beta headers, and pitfalls.
|
||||||
|
|
||||||
|
**Beta headers:** `managed-agents-2026-04-01` — the SDK sets this automatically for all `client.beta.{agents,environments,sessions,vaults}.*` calls. Skills API uses `skills-2025-10-02` and Files API uses `files-api-2025-04-14`, but you don't need to explicitly pass those in for endpoints other than `/v1/skills` and `/v1/files`.
|
||||||
|
|
||||||
|
**Subcommands** — invoke directly with `/claude-api <subcommand>`:
|
||||||
|
|
||||||
|
| Subcommand | Action |
|
||||||
|
|---|---|
|
||||||
|
| `managed-agents-onboard` | Walk the user through setting up a Managed Agent from scratch. **Read `shared/managed-agents-onboarding.md` immediately** and follow its interview script: mental model → know-or-explore branch → template config → session setup → emit code. Do not summarize — run the interview. |
|
||||||
|
|
||||||
|
**Reading guide:** Start with `shared/managed-agents-overview.md`, then the topical `shared/managed-agents-*.md` files (core, environments, tools, events, client-patterns, onboarding, api-reference). For Python, TypeScript, Go, Ruby, PHP, and Java, read `{lang}/managed-agents/README.md` for code examples. For cURL, read `curl/managed-agents.md`. **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.create` and pass it to every subsequent `sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI is one convenient way to create agents and environments from version-controlled YAML (URL in `shared/live-sources.md`). If a binding you need isn't shown in the language README, WebFetch the relevant entry from `shared/live-sources.md` rather than guess. C# does not currently have Managed Agents support; use raw HTTP from `curl/managed-agents.md` as a reference.
|
||||||
|
|
||||||
|
**When the user wants to set up a Managed Agent from scratch** (e.g. "how do I get started", "walk me through creating one", "set up a new agent"): read `shared/managed-agents-onboarding.md` and run its interview — same flow as the `managed-agents-onboard` subcommand.
|
||||||
|
|
||||||
|
**When the user asks "how do I write the client code for X":** reach for `shared/managed-agents-client-patterns.md` — covers lossless stream reconnect, `processed_at` queued/processed gate, interrupt, `tool_confirmation` round-trip, the correct idle/terminated break gate, post-idle status race, stream-first ordering, file-mount gotchas, keeping credentials host-side via custom tools, etc.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Reading Guide
|
## Reading Guide
|
||||||
|
|
||||||
After detecting the language, read the relevant files based on what the user needs:
|
After detecting the language, read the relevant files based on what the user needs:
|
||||||
@@ -203,14 +253,17 @@ After detecting the language, read the relevant files based on what the user nee
|
|||||||
**Function calling / tool use / agents:**
|
**Function calling / tool use / agents:**
|
||||||
→ Read `{lang}/claude-api/README.md` + `shared/tool-use-concepts.md` + `{lang}/claude-api/tool-use.md`
|
→ Read `{lang}/claude-api/README.md` + `shared/tool-use-concepts.md` + `{lang}/claude-api/tool-use.md`
|
||||||
|
|
||||||
|
**Agent design (tool surface, context management, caching strategy):**
|
||||||
|
→ Read `shared/agent-design.md`
|
||||||
|
|
||||||
**Batch processing (non-latency-sensitive):**
|
**Batch processing (non-latency-sensitive):**
|
||||||
→ Read `{lang}/claude-api/README.md` + `{lang}/claude-api/batches.md`
|
→ Read `{lang}/claude-api/README.md` + `{lang}/claude-api/batches.md`
|
||||||
|
|
||||||
**File uploads across multiple requests:**
|
**File uploads across multiple requests:**
|
||||||
→ Read `{lang}/claude-api/README.md` + `{lang}/claude-api/files-api.md`
|
→ Read `{lang}/claude-api/README.md` + `{lang}/claude-api/files-api.md`
|
||||||
|
|
||||||
**Agent with built-in tools (file/web/terminal):**
|
**Managed Agents (server-managed stateful agents with workspace):**
|
||||||
→ Read `{lang}/agent-sdk/README.md` + `{lang}/agent-sdk/patterns.md`
|
→ Read `shared/managed-agents-overview.md` + the rest of the `shared/managed-agents-*.md` files. For Python, TypeScript, Go, Ruby, PHP, and Java, read `{lang}/managed-agents/README.md` for code examples. For cURL, read `curl/managed-agents.md`. **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.create` and pass it to every subsequent `sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI is one convenient way to create agents and environments from version-controlled YAML (URL in `shared/live-sources.md`). If a binding you need isn't shown in the language README, WebFetch the relevant entry from `shared/live-sources.md` rather than guess. C# does not currently support Managed Agents — use raw HTTP from `curl/managed-agents.md` as a reference.
|
||||||
|
|
||||||
### Claude API (Full File Reference)
|
### Claude API (Full File Reference)
|
||||||
|
|
||||||
@@ -218,23 +271,18 @@ Read the **language-specific Claude API folder** (`{language}/claude-api/`):
|
|||||||
|
|
||||||
1. **`{language}/claude-api/README.md`** — **Read this first.** Installation, quick start, common patterns, error handling.
|
1. **`{language}/claude-api/README.md`** — **Read this first.** Installation, quick start, common patterns, error handling.
|
||||||
2. **`shared/tool-use-concepts.md`** — Read when the user needs function calling, code execution, memory, or structured outputs. Covers conceptual foundations.
|
2. **`shared/tool-use-concepts.md`** — Read when the user needs function calling, code execution, memory, or structured outputs. Covers conceptual foundations.
|
||||||
3. **`{language}/claude-api/tool-use.md`** — Read for language-specific tool use code examples (tool runner, manual loop, code execution, memory, structured outputs).
|
3. **`shared/agent-design.md`** — Read when designing an agent: bash vs. dedicated tools, programmatic tool calling, tool search/skills, context editing vs. compaction vs. memory, caching principles.
|
||||||
4. **`{language}/claude-api/streaming.md`** — Read when building chat UIs or interfaces that display responses incrementally.
|
4. **`{language}/claude-api/tool-use.md`** — Read for language-specific tool use code examples (tool runner, manual loop, code execution, memory, structured outputs).
|
||||||
5. **`{language}/claude-api/batches.md`** — Read when processing many requests offline (not latency-sensitive). Runs asynchronously at 50% cost.
|
5. **`{language}/claude-api/streaming.md`** — Read when building chat UIs or interfaces that display responses incrementally.
|
||||||
6. **`{language}/claude-api/files-api.md`** — Read when sending the same file across multiple requests without re-uploading.
|
6. **`{language}/claude-api/batches.md`** — Read when processing many requests offline (not latency-sensitive). Runs asynchronously at 50% cost.
|
||||||
7. **`shared/prompt-caching.md`** — Read when adding or optimizing prompt caching. Covers prefix-stability design, breakpoint placement, and anti-patterns that silently invalidate cache.
|
7. **`{language}/claude-api/files-api.md`** — Read when sending the same file across multiple requests without re-uploading.
|
||||||
8. **`shared/error-codes.md`** — Read when debugging HTTP errors or implementing error handling.
|
8. **`shared/prompt-caching.md`** — Read when adding or optimizing prompt caching. Covers prefix-stability design, breakpoint placement, and anti-patterns that silently invalidate cache.
|
||||||
9. **`shared/live-sources.md`** — WebFetch URLs for fetching the latest official documentation.
|
9. **`shared/error-codes.md`** — Read when debugging HTTP errors or implementing error handling.
|
||||||
|
10. **`shared/live-sources.md`** — WebFetch URLs for fetching the latest official documentation.
|
||||||
|
|
||||||
> **Note:** For Java, Go, Ruby, C#, PHP, and cURL — these have a single file each covering all basics. Read that file plus `shared/tool-use-concepts.md` and `shared/error-codes.md` as needed.
|
> **Note:** For Java, Go, Ruby, C#, PHP, and cURL — these have a single file each covering all basics. Read that file plus `shared/tool-use-concepts.md` and `shared/error-codes.md` as needed.
|
||||||
|
|
||||||
### Agent SDK
|
> **Note:** For the Managed Agents file reference, see the `## Managed Agents (Beta)` section above — it lists every `shared/managed-agents-*.md` file and the language-specific READMEs.
|
||||||
|
|
||||||
Read the **language-specific Agent SDK folder** (`{language}/agent-sdk/`). Agent SDK is available for **Python and TypeScript only**.
|
|
||||||
|
|
||||||
1. **`{language}/agent-sdk/README.md`** — Installation, quick start, built-in tools, permissions, MCP, hooks.
|
|
||||||
2. **`{language}/agent-sdk/patterns.md`** — Custom tools, hooks, subagents, MCP integration, session resumption.
|
|
||||||
3. **`shared/live-sources.md`** — WebFetch URLs for current Agent SDK docs.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
333
skills/claude-api/curl/managed-agents.md
Normal file
333
skills/claude-api/curl/managed-agents.md
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
# Managed Agents — cURL / Raw HTTP
|
||||||
|
|
||||||
|
Use these examples when the user needs raw HTTP requests or is working without an SDK.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export ANTHROPIC_API_KEY="your-api-key"
|
||||||
|
|
||||||
|
# Common headers
|
||||||
|
HEADERS=(
|
||||||
|
-H "Content-Type: application/json"
|
||||||
|
-H "x-api-key: $ANTHROPIC_API_KEY"
|
||||||
|
-H "anthropic-version: 2023-06-01"
|
||||||
|
-H "anthropic-beta: managed-agents-2026-04-01"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Environment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://api.anthropic.com/v1/environments \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"name": "my-dev-env",
|
||||||
|
"config": {
|
||||||
|
"type": "cloud",
|
||||||
|
"networking": { "type": "unrestricted" }
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### With restricted networking
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://api.anthropic.com/v1/environments \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"name": "restricted-env",
|
||||||
|
"config": {
|
||||||
|
"type": "cloud",
|
||||||
|
"networking": {
|
||||||
|
"type": "package_managers_and_custom",
|
||||||
|
"allowed_hosts": ["api.example.com"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Agent (required first step)
|
||||||
|
|
||||||
|
> ⚠️ **There is no inline agent config.** Under `managed-agents-2026-04-01`, `model`/`system`/`tools` are top-level fields on `POST /v1/agents`, not on the session. Always create the agent first — the session only takes `"agent": {"type": "agent", "id": "..."}`.
|
||||||
|
|
||||||
|
### Minimal
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Create the agent
|
||||||
|
curl -X POST https://api.anthropic.com/v1/agents \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"name": "Coding Assistant",
|
||||||
|
"model": "claude-opus-4-6",
|
||||||
|
"tools": [{ "type": "agent_toolset_20260401" }]
|
||||||
|
}'
|
||||||
|
# → { "id": "agent_abc123", ... }
|
||||||
|
|
||||||
|
# 2. Start a session
|
||||||
|
curl -X POST https://api.anthropic.com/v1/sessions \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"agent": { "type": "agent", "id": "agent_abc123", "version": "1772585501101368014" },
|
||||||
|
"environment_id": "env_abc123"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### With system prompt, custom tools, and GitHub repo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Create the agent
|
||||||
|
curl -X POST https://api.anthropic.com/v1/agents \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"name": "Code Reviewer",
|
||||||
|
"model": "claude-opus-4-6",
|
||||||
|
"system": "You are a senior code reviewer. Be thorough and constructive.",
|
||||||
|
"tools": [
|
||||||
|
{ "type": "agent_toolset_20260401" },
|
||||||
|
{
|
||||||
|
"type": "custom",
|
||||||
|
"name": "run_linter",
|
||||||
|
"description": "Run the project linter on a file",
|
||||||
|
"input_schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file_path": { "type": "string", "description": "Path to lint" }
|
||||||
|
},
|
||||||
|
"required": ["file_path"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
# 2. Start a session with the repo mounted
|
||||||
|
curl -X POST https://api.anthropic.com/v1/sessions \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"agent": { "type": "agent", "id": "agent_abc123", "version": "1772585501101368014" },
|
||||||
|
"environment_id": "env_abc123",
|
||||||
|
"title": "Code review session",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"type": "github_repository",
|
||||||
|
"url": "https://github.com/owner/repo",
|
||||||
|
"mount_path": "/workspace/repo",
|
||||||
|
"authorization_token": "ghp_...",
|
||||||
|
"branch": "feature-branch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Send a User Message
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://api.anthropic.com/v1/sessions/$SESSION_ID/events \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"type": "user.message",
|
||||||
|
"content": [{ "type": "text", "text": "Review the auth module for security issues" }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stream Events (SSE)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -N https://api.anthropic.com/v1/sessions/$SESSION_ID/events/stream \
|
||||||
|
"${HEADERS[@]}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Response format:
|
||||||
|
|
||||||
|
```
|
||||||
|
event: session.status_running
|
||||||
|
data: {"type":"session.status_running","id":"sevt_...","processed_at":"..."}
|
||||||
|
|
||||||
|
event: agent.message
|
||||||
|
data: {"type":"agent.message","id":"sevt_...","content":[{"type":"text","text":"I'll review..."}],"processed_at":"..."}
|
||||||
|
|
||||||
|
event: session.status_idle
|
||||||
|
data: {"type":"session.status_idle","id":"sevt_...","processed_at":"..."}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Poll Events
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get all events
|
||||||
|
curl https://api.anthropic.com/v1/sessions/$SESSION_ID/events \
|
||||||
|
"${HEADERS[@]}"
|
||||||
|
|
||||||
|
# Paginated — get next page of events
|
||||||
|
curl "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?page=page_abc123" \
|
||||||
|
"${HEADERS[@]}"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Provide Custom Tool Result
|
||||||
|
|
||||||
|
When the agent calls a custom tool, send the result back:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://api.anthropic.com/v1/sessions/$SESSION_ID/events \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"type": "user.custom_tool_result",
|
||||||
|
"custom_tool_use_id": "sevt_abc123",
|
||||||
|
"content": [{ "type": "text", "text": "No linting errors found." }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interrupt a Running Session
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://api.anthropic.com/v1/sessions/$SESSION_ID/events \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"type": "interrupt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Get Session Details
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://api.anthropic.com/v1/sessions/$SESSION_ID \
|
||||||
|
"${HEADERS[@]}"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List Sessions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://api.anthropic.com/v1/sessions \
|
||||||
|
"${HEADERS[@]}"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Delete a Session
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X DELETE https://api.anthropic.com/v1/sessions/$SESSION_ID \
|
||||||
|
"${HEADERS[@]}"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upload a File
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://api.anthropic.com/v1/files \
|
||||||
|
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
||||||
|
-H "anthropic-version: 2023-06-01" \
|
||||||
|
-H "anthropic-beta: files-api-2025-04-14" \
|
||||||
|
-F "file=@path/to/file.txt" \
|
||||||
|
-F "purpose=agent"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List and Download Session Files
|
||||||
|
|
||||||
|
List files the agent wrote to `/mnt/session/outputs/` during a session, then download them.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List files associated with a session
|
||||||
|
curl "https://api.anthropic.com/v1/files?scope=$SESSION_ID" \
|
||||||
|
"${HEADERS[@]}"
|
||||||
|
|
||||||
|
# Download a specific file
|
||||||
|
curl "https://api.anthropic.com/v1/files/$FILE_ID/content" \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-o downloaded_file.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List Agents
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://api.anthropic.com/v1/agents \
|
||||||
|
"${HEADERS[@]}"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MCP Server Integration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Agent declares MCP server (no auth here — auth goes in a vault)
|
||||||
|
curl -X POST https://api.anthropic.com/v1/agents \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"name": "MCP Agent",
|
||||||
|
"model": "claude-opus-4-6",
|
||||||
|
"mcp_servers": [
|
||||||
|
{ "type": "url", "name": "my-tools", "url": "https://my-mcp-server.example.com/sse" }
|
||||||
|
],
|
||||||
|
"tools": [
|
||||||
|
{ "type": "agent_toolset_20260401" },
|
||||||
|
{ "type": "mcp_toolset", "mcp_server_name": "my-tools" }
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
# 2. Session attaches vault containing credentials for that MCP server URL
|
||||||
|
curl -X POST https://api.anthropic.com/v1/sessions \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"agent": "agent_abc123",
|
||||||
|
"environment_id": "env_abc123",
|
||||||
|
"vault_ids": ["vlt_abc123"]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
See `shared/managed-agents-tools.md` §Vaults for creating vaults and adding credentials.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tool Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST https://api.anthropic.com/v1/agents \
|
||||||
|
"${HEADERS[@]}" \
|
||||||
|
-d '{
|
||||||
|
"name": "Restricted Agent",
|
||||||
|
"model": "claude-opus-4-6",
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"type": "agent_toolset_20260401",
|
||||||
|
"default_config": { "enabled": true },
|
||||||
|
"configs": [
|
||||||
|
{ "name": "bash", "enabled": false }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
561
skills/claude-api/go/managed-agents/README.md
Normal file
561
skills/claude-api/go/managed-agents/README.md
Normal file
@@ -0,0 +1,561 @@
|
|||||||
|
# Managed Agents — Go
|
||||||
|
|
||||||
|
> **Bindings not shown here:** This README covers the most common managed-agents flows for Go. If you need a class, method, namespace, field, or behavior that isn't shown, WebFetch the Go SDK repo **or the relevant docs page** from `shared/live-sources.md` rather than guess. Do not extrapolate from cURL shapes or another language's SDK.
|
||||||
|
|
||||||
|
> **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.New` and pass it to every subsequent `sessions.New`; do not call `agents.New` in the request path. The Anthropic CLI is one convenient way to create agents and environments from version-controlled YAML — its URL is in `shared/live-sources.md`. The examples below show in-code creation for completeness; in production the create call belongs in setup, not in the request path.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/anthropics/anthropic-sdk-go
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client Initialization
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/anthropics/anthropic-sdk-go"
|
||||||
|
"github.com/anthropics/anthropic-sdk-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default (uses ANTHROPIC_API_KEY env var)
|
||||||
|
client := anthropic.NewClient()
|
||||||
|
|
||||||
|
// Explicit API key
|
||||||
|
client := anthropic.NewClient(
|
||||||
|
option.WithAPIKey("your-api-key"),
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Environment
|
||||||
|
|
||||||
|
```go
|
||||||
|
environment, err := client.Beta.Environments.New(ctx, anthropic.BetaEnvironmentNewParams{
|
||||||
|
Name: "my-dev-env",
|
||||||
|
Config: anthropic.BetaCloudConfigParams{
|
||||||
|
Networking: anthropic.BetaCloudConfigParamsNetworkingUnion{
|
||||||
|
OfUnrestricted: &anthropic.UnrestrictedNetworkParam{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(environment.ID) // env_...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Agent (required first step)
|
||||||
|
|
||||||
|
> ⚠️ **There is no inline agent config.** `Model`/`System`/`Tools` live on the agent object, not the session. Always start with `Beta.Agents.New()` — the session only takes `Agent: anthropic.BetaSessionNewParamsAgentUnion{OfString: anthropic.String(agent.ID)}` (or the typed `OfBetaManagedAgentsAgents` variant when you need a specific version).
|
||||||
|
|
||||||
|
### Minimal
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 1. Create the agent (reusable, versioned)
|
||||||
|
agent, err := client.Beta.Agents.New(ctx, anthropic.BetaAgentNewParams{
|
||||||
|
Name: "Coding Assistant",
|
||||||
|
Model: anthropic.BetaManagedAgentsModelConfigParams{
|
||||||
|
ID: "claude-opus-4-6",
|
||||||
|
Type: anthropic.BetaManagedAgentsModelConfigParamsTypeModelConfig,
|
||||||
|
},
|
||||||
|
System: anthropic.String("You are a helpful coding assistant."),
|
||||||
|
Tools: []anthropic.BetaAgentNewParamsToolUnion{{
|
||||||
|
OfAgentToolset20260401: &anthropic.BetaManagedAgentsAgentToolset20260401Params{
|
||||||
|
Type: anthropic.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Start a session
|
||||||
|
session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
|
||||||
|
Agent: anthropic.BetaSessionNewParamsAgentUnion{
|
||||||
|
OfBetaManagedAgentsAgents: &anthropic.BetaManagedAgentsAgentParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsAgentParamsTypeAgent,
|
||||||
|
ID: agent.ID,
|
||||||
|
Version: anthropic.Int(agent.Version),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EnvironmentID: environment.ID,
|
||||||
|
Title: anthropic.String("Quickstart session"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Session ID: %s, status: %s\n", session.ID, session.Status)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updating an Agent
|
||||||
|
|
||||||
|
Updates create new versions; the agent object is immutable per version.
|
||||||
|
|
||||||
|
```go
|
||||||
|
updatedAgent, err := client.Beta.Agents.Update(ctx, agent.ID, anthropic.BetaAgentUpdateParams{
|
||||||
|
Version: agent.Version,
|
||||||
|
System: anthropic.String("You are a helpful coding agent. Always write tests."),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("New version: %d\n", updatedAgent.Version)
|
||||||
|
|
||||||
|
// List all versions
|
||||||
|
iter := client.Beta.Agents.Versions.ListAutoPaging(ctx, agent.ID, anthropic.BetaAgentVersionListParams{})
|
||||||
|
for iter.Next() {
|
||||||
|
version := iter.Current()
|
||||||
|
fmt.Printf("Version %d: %s\n", version.Version, version.UpdatedAt.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
if err := iter.Err(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Archive the agent
|
||||||
|
_, err = client.Beta.Agents.Archive(ctx, agent.ID, anthropic.BetaAgentArchiveParams{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Send a User Message
|
||||||
|
|
||||||
|
```go
|
||||||
|
_, err = client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
|
||||||
|
Events: []anthropic.SendEventsParamsUnion{{
|
||||||
|
OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
|
||||||
|
Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
|
||||||
|
OfText: &anthropic.BetaManagedAgentsTextBlockParam{
|
||||||
|
Type: anthropic.BetaManagedAgentsTextBlockTypeText,
|
||||||
|
Text: "Review the auth module",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Stream-first:** Open the stream *before* (or concurrently with) sending the message. The stream only delivers events that occur after it opens — stream-after-send means early events arrive buffered in one batch. See [Steering Patterns](../../shared/managed-agents-events.md#steering-patterns).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stream Events (SSE)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Open the stream first, then send the user message
|
||||||
|
stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, anthropic.BetaSessionEventStreamParams{})
|
||||||
|
defer stream.Close()
|
||||||
|
|
||||||
|
if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
|
||||||
|
Events: []anthropic.SendEventsParamsUnion{{
|
||||||
|
OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
|
||||||
|
Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
|
||||||
|
OfText: &anthropic.BetaManagedAgentsTextBlockParam{
|
||||||
|
Type: anthropic.BetaManagedAgentsTextBlockTypeText,
|
||||||
|
Text: "Summarize the repo README",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
events:
|
||||||
|
for stream.Next() {
|
||||||
|
switch event := stream.Current().AsAny().(type) {
|
||||||
|
case anthropic.BetaManagedAgentsAgentMessageEvent:
|
||||||
|
for _, block := range event.Content {
|
||||||
|
fmt.Print(block.Text)
|
||||||
|
}
|
||||||
|
case anthropic.BetaManagedAgentsAgentToolUseEvent:
|
||||||
|
fmt.Printf("\n[Using tool: %s]\n", event.Name)
|
||||||
|
case anthropic.BetaManagedAgentsSessionStatusIdleEvent:
|
||||||
|
break events
|
||||||
|
case anthropic.BetaManagedAgentsSessionErrorEvent:
|
||||||
|
fmt.Printf("\n[Error: %s]\n", event.Error.Message)
|
||||||
|
break events
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := stream.Err(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reconnecting and Tailing
|
||||||
|
|
||||||
|
When reconnecting mid-session, list past events first to dedupe, then tail live events:
|
||||||
|
|
||||||
|
```go
|
||||||
|
stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, anthropic.BetaSessionEventStreamParams{})
|
||||||
|
defer stream.Close()
|
||||||
|
|
||||||
|
// Stream is open and buffering. List history before tailing live.
|
||||||
|
seenEventIDs := map[string]struct{}{}
|
||||||
|
history := client.Beta.Sessions.Events.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionEventListParams{})
|
||||||
|
for history.Next() {
|
||||||
|
seenEventIDs[history.Current().ID] = struct{}{}
|
||||||
|
}
|
||||||
|
if err := history.Err(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail live events, skipping anything already seen
|
||||||
|
tail:
|
||||||
|
for stream.Next() {
|
||||||
|
event := stream.Current()
|
||||||
|
if _, seen := seenEventIDs[event.ID]; seen {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seenEventIDs[event.ID] = struct{}{}
|
||||||
|
switch event := event.AsAny().(type) {
|
||||||
|
case anthropic.BetaManagedAgentsAgentMessageEvent:
|
||||||
|
for _, block := range event.Content {
|
||||||
|
fmt.Print(block.Text)
|
||||||
|
}
|
||||||
|
case anthropic.BetaManagedAgentsSessionStatusIdleEvent:
|
||||||
|
break tail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := stream.Err(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Provide Custom Tool Result
|
||||||
|
|
||||||
|
> ℹ️ The Go managed-agents bindings for `user.custom_tool_result` are not yet documented in this skill or in the apps source examples. Refer to `shared/managed-agents-events.md` for the wire format and the `github.com/anthropics/anthropic-sdk-go` repository for the corresponding Go params types.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Poll Events
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Auto-paginating iterator
|
||||||
|
iter := client.Beta.Sessions.Events.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionEventListParams{})
|
||||||
|
for iter.Next() {
|
||||||
|
event := iter.Current()
|
||||||
|
fmt.Printf("%s: %s\n", event.Type, event.ID)
|
||||||
|
}
|
||||||
|
if err := iter.Err(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upload a File
|
||||||
|
|
||||||
|
```go
|
||||||
|
csvFile, err := os.Open("data.csv")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer csvFile.Close()
|
||||||
|
|
||||||
|
file, err := client.Beta.Files.Upload(ctx, anthropic.BetaFileUploadParams{
|
||||||
|
File: csvFile,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("File ID: %s\n", file.ID)
|
||||||
|
|
||||||
|
// Mount in a session
|
||||||
|
session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
|
||||||
|
Agent: anthropic.BetaSessionNewParamsAgentUnion{
|
||||||
|
OfString: anthropic.String(agent.ID),
|
||||||
|
},
|
||||||
|
EnvironmentID: environment.ID,
|
||||||
|
Resources: []anthropic.BetaSessionNewParamsResourceUnion{{
|
||||||
|
OfFile: &anthropic.BetaManagedAgentsFileResourceParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsFileResourceParamsTypeFile,
|
||||||
|
FileID: file.ID,
|
||||||
|
MountPath: anthropic.String("/workspace/data.csv"),
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add and Manage Resources on an Existing Session
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Attach an additional file to an open session
|
||||||
|
resource, err := client.Beta.Sessions.Resources.Add(ctx, session.ID, anthropic.BetaSessionResourceAddParams{
|
||||||
|
BetaManagedAgentsFileResourceParams: anthropic.BetaManagedAgentsFileResourceParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsFileResourceParamsTypeFile,
|
||||||
|
FileID: file.ID,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(resource.ID) // "sesrsc_01ABC..."
|
||||||
|
|
||||||
|
// List resources on the session
|
||||||
|
listed, err := client.Beta.Sessions.Resources.List(ctx, session.ID, anthropic.BetaSessionResourceListParams{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, entry := range listed.Data {
|
||||||
|
fmt.Println(entry.ID, entry.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach a resource
|
||||||
|
if _, err := client.Beta.Sessions.Resources.Delete(ctx, resource.ID, anthropic.BetaSessionResourceDeleteParams{
|
||||||
|
SessionID: session.ID,
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List and Download Session Files
|
||||||
|
|
||||||
|
> ℹ️ Listing and downloading files an agent wrote during a session is not yet documented for Go in this skill or in the apps source examples. See `shared/managed-agents-events.md` and the `github.com/anthropics/anthropic-sdk-go` repository for the `Beta.Files.List` and `Beta.Files.Download` Go params types.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Management
|
||||||
|
|
||||||
|
```go
|
||||||
|
// List environments
|
||||||
|
environments, err := client.Beta.Environments.List(ctx, anthropic.BetaEnvironmentListParams{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve a specific environment
|
||||||
|
env, err := client.Beta.Environments.Get(ctx, environment.ID, anthropic.BetaEnvironmentGetParams{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Archive an environment (read-only, existing sessions continue)
|
||||||
|
_, err = client.Beta.Environments.Archive(ctx, environment.ID, anthropic.BetaEnvironmentArchiveParams{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete an environment (only if no sessions reference it)
|
||||||
|
_, err = client.Beta.Environments.Delete(ctx, environment.ID, anthropic.BetaEnvironmentDeleteParams{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a session
|
||||||
|
_, err = client.Beta.Sessions.Delete(ctx, session.ID, anthropic.BetaSessionDeleteParams{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MCP Server Integration
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Agent declares MCP server (no auth here — auth goes in a vault)
|
||||||
|
agent, err := client.Beta.Agents.New(ctx, anthropic.BetaAgentNewParams{
|
||||||
|
Name: "GitHub Assistant",
|
||||||
|
Model: anthropic.BetaManagedAgentsModelConfigParams{
|
||||||
|
ID: "claude-opus-4-6",
|
||||||
|
Type: anthropic.BetaManagedAgentsModelConfigParamsTypeModelConfig,
|
||||||
|
},
|
||||||
|
MCPServers: []anthropic.BetaManagedAgentsUrlmcpServerParams{{
|
||||||
|
Type: anthropic.BetaManagedAgentsUrlmcpServerParamsTypeURL,
|
||||||
|
Name: "github",
|
||||||
|
URL: "https://api.githubcopilot.com/mcp/",
|
||||||
|
}},
|
||||||
|
Tools: []anthropic.BetaAgentNewParamsToolUnion{
|
||||||
|
{
|
||||||
|
OfAgentToolset20260401: &anthropic.BetaManagedAgentsAgentToolset20260401Params{
|
||||||
|
Type: anthropic.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OfMCPToolset: &anthropic.BetaManagedAgentsMCPToolsetParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsMCPToolsetParamsTypeMCPToolset,
|
||||||
|
MCPServerName: "github",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session attaches vault(s) containing credentials for those MCP server URLs
|
||||||
|
session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
|
||||||
|
Agent: anthropic.BetaSessionNewParamsAgentUnion{
|
||||||
|
OfBetaManagedAgentsAgents: &anthropic.BetaManagedAgentsAgentParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsAgentParamsTypeAgent,
|
||||||
|
ID: agent.ID,
|
||||||
|
Version: anthropic.Int(agent.Version),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EnvironmentID: environment.ID,
|
||||||
|
VaultIDs: []string{vault.ID},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See `shared/managed-agents-tools.md` §Vaults for creating vaults and adding credentials.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vaults
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a vault
|
||||||
|
vault, err := client.Beta.Vaults.New(ctx, anthropic.BetaVaultNewParams{
|
||||||
|
DisplayName: "Alice",
|
||||||
|
Metadata: map[string]string{"external_user_id": "usr_abc123"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an OAuth credential
|
||||||
|
credential, err := client.Beta.Vaults.Credentials.New(ctx, vault.ID, anthropic.BetaVaultCredentialNewParams{
|
||||||
|
DisplayName: anthropic.String("Alice's Slack"),
|
||||||
|
Auth: anthropic.BetaVaultCredentialNewParamsAuthUnion{
|
||||||
|
OfMCPOAuth: &anthropic.BetaManagedAgentsMCPOAuthCreateParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsMCPOAuthCreateParamsTypeMCPOAuth,
|
||||||
|
MCPServerURL: "https://mcp.slack.com/mcp",
|
||||||
|
AccessToken: "xoxp-...",
|
||||||
|
ExpiresAt: anthropic.Time(time.Date(2026, time.April, 15, 0, 0, 0, 0, time.UTC)),
|
||||||
|
Refresh: anthropic.BetaManagedAgentsMCPOAuthRefreshParams{
|
||||||
|
TokenEndpoint: "https://slack.com/api/oauth.v2.access",
|
||||||
|
ClientID: "1234567890.0987654321",
|
||||||
|
Scope: anthropic.String("channels:read chat:write"),
|
||||||
|
RefreshToken: "xoxe-1-...",
|
||||||
|
TokenEndpointAuth: anthropic.BetaManagedAgentsMCPOAuthRefreshParamsTokenEndpointAuthUnion{
|
||||||
|
OfClientSecretPost: &anthropic.BetaManagedAgentsTokenEndpointAuthPostParam{
|
||||||
|
Type: anthropic.BetaManagedAgentsTokenEndpointAuthPostParamTypeClientSecretPost,
|
||||||
|
ClientSecret: "abc123...",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate the credential (e.g., after a token refresh)
|
||||||
|
_, err = client.Beta.Vaults.Credentials.Update(ctx, credential.ID, anthropic.BetaVaultCredentialUpdateParams{
|
||||||
|
VaultID: vault.ID,
|
||||||
|
Auth: anthropic.BetaVaultCredentialUpdateParamsAuthUnion{
|
||||||
|
OfMCPOAuth: &anthropic.BetaManagedAgentsMCPOAuthUpdateParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsMCPOAuthUpdateParamsTypeMCPOAuth,
|
||||||
|
AccessToken: anthropic.String("xoxp-new-..."),
|
||||||
|
ExpiresAt: anthropic.Time(time.Date(2026, time.May, 15, 0, 0, 0, 0, time.UTC)),
|
||||||
|
Refresh: anthropic.BetaManagedAgentsMCPOAuthRefreshUpdateParams{
|
||||||
|
RefreshToken: anthropic.String("xoxe-1-new-..."),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Archive a vault
|
||||||
|
_, err = client.Beta.Vaults.Archive(ctx, vault.ID, anthropic.BetaVaultArchiveParams{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GitHub Repository Integration
|
||||||
|
|
||||||
|
Mount a GitHub repository as a session resource (a vault holds the GitHub MCP credential):
|
||||||
|
|
||||||
|
```go
|
||||||
|
session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
|
||||||
|
Agent: anthropic.BetaSessionNewParamsAgentUnion{OfString: anthropic.String(agent.ID)},
|
||||||
|
EnvironmentID: environment.ID,
|
||||||
|
VaultIDs: []string{vault.ID},
|
||||||
|
Resources: []anthropic.BetaSessionNewParamsResourceUnion{
|
||||||
|
{
|
||||||
|
OfGitHubRepository: &anthropic.BetaManagedAgentsGitHubRepositoryResourceParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsGitHubRepositoryResourceParamsTypeGitHubRepository,
|
||||||
|
URL: "https://github.com/org/repo",
|
||||||
|
MountPath: anthropic.String("/workspace/repo"),
|
||||||
|
AuthorizationToken: "ghp_your_github_token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple repositories on the same session:
|
||||||
|
|
||||||
|
```go
|
||||||
|
resources := []anthropic.BetaSessionNewParamsResourceUnion{
|
||||||
|
{
|
||||||
|
OfGitHubRepository: &anthropic.BetaManagedAgentsGitHubRepositoryResourceParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsGitHubRepositoryResourceParamsTypeGitHubRepository,
|
||||||
|
URL: "https://github.com/org/frontend",
|
||||||
|
MountPath: anthropic.String("/workspace/frontend"),
|
||||||
|
AuthorizationToken: "ghp_your_github_token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OfGitHubRepository: &anthropic.BetaManagedAgentsGitHubRepositoryResourceParams{
|
||||||
|
Type: anthropic.BetaManagedAgentsGitHubRepositoryResourceParamsTypeGitHubRepository,
|
||||||
|
URL: "https://github.com/org/backend",
|
||||||
|
MountPath: anthropic.String("/workspace/backend"),
|
||||||
|
AuthorizationToken: "ghp_your_github_token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Rotating a repository's authorization token:
|
||||||
|
|
||||||
|
```go
|
||||||
|
listed, err := client.Beta.Sessions.Resources.List(ctx, session.ID, anthropic.BetaSessionResourceListParams{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
repoResourceID := listed.Data[0].ID
|
||||||
|
|
||||||
|
_, err = client.Beta.Sessions.Resources.Update(ctx, repoResourceID, anthropic.BetaSessionResourceUpdateParams{
|
||||||
|
SessionID: session.ID,
|
||||||
|
AuthorizationToken: "ghp_your_new_github_token",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
```
|
||||||
442
skills/claude-api/java/managed-agents/README.md
Normal file
442
skills/claude-api/java/managed-agents/README.md
Normal file
@@ -0,0 +1,442 @@
|
|||||||
|
# Managed Agents — Java
|
||||||
|
|
||||||
|
> **Bindings not shown here:** This README covers the most common managed-agents flows for Java. If you need a class, method, namespace, field, or behavior that isn't shown, WebFetch the Java SDK repo **or the relevant docs page** from `shared/live-sources.md` rather than guess. Do not extrapolate from cURL shapes or another language's SDK.
|
||||||
|
|
||||||
|
> **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `client.beta().agents().create` and pass it to every subsequent `client.beta().sessions().create`; do not call `agents().create` in the request path. The Anthropic CLI is one convenient way to create agents and environments from version-controlled YAML — its URL is in `shared/live-sources.md`. The examples below show in-code creation for completeness; in production the create call belongs in setup, not in the request path.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.anthropic</groupId>
|
||||||
|
<artifactId>anthropic-java</artifactId>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client Initialization
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
|
||||||
|
|
||||||
|
// Default (uses ANTHROPIC_API_KEY env var)
|
||||||
|
var client = AnthropicOkHttpClient.fromEnv();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Environment
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.environments.BetaCloudConfigParams;
|
||||||
|
import com.anthropic.models.beta.environments.EnvironmentCreateParams;
|
||||||
|
import com.anthropic.models.beta.environments.UnrestrictedNetwork;
|
||||||
|
|
||||||
|
var environment = client.beta().environments().create(EnvironmentCreateParams.builder()
|
||||||
|
.name("my-dev-env")
|
||||||
|
.config(BetaCloudConfigParams.builder()
|
||||||
|
.networking(UnrestrictedNetwork.builder().build())
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
System.out.println("Environment ID: " + environment.id()); // env_...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Agent (required first step)
|
||||||
|
|
||||||
|
> ⚠️ **There is no inline agent config.** Model, system, and tools live on the agent object, not the session. Always start with `client.beta().agents().create()` — the session takes either `.agent(agent.id())` or the typed `BetaManagedAgentsAgentParams.builder()...build()`.
|
||||||
|
|
||||||
|
### Minimal
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.agents.AgentCreateParams;
|
||||||
|
import com.anthropic.models.beta.agents.BetaManagedAgentsAgentToolset20260401Params;
|
||||||
|
import com.anthropic.models.beta.sessions.BetaManagedAgentsAgentParams;
|
||||||
|
import com.anthropic.models.beta.sessions.SessionCreateParams;
|
||||||
|
|
||||||
|
// 1. Create the agent (reusable, versioned)
|
||||||
|
var agent = client.beta().agents().create(AgentCreateParams.builder()
|
||||||
|
.name("Coding Assistant")
|
||||||
|
.model("claude-opus-4-6")
|
||||||
|
.system("You are a helpful coding assistant.")
|
||||||
|
.addTool(BetaManagedAgentsAgentToolset20260401Params.builder()
|
||||||
|
.type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// 2. Start a session
|
||||||
|
var session = client.beta().sessions().create(SessionCreateParams.builder()
|
||||||
|
.agent(BetaManagedAgentsAgentParams.builder()
|
||||||
|
.type(BetaManagedAgentsAgentParams.Type.AGENT)
|
||||||
|
.id(agent.id())
|
||||||
|
.version(agent.version())
|
||||||
|
.build())
|
||||||
|
.environmentId(environment.id())
|
||||||
|
.title("Quickstart session")
|
||||||
|
.build());
|
||||||
|
System.out.println("Session ID: " + session.id());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updating an Agent
|
||||||
|
|
||||||
|
Updates create new versions; the agent object is immutable per version.
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.agents.AgentUpdateParams;
|
||||||
|
|
||||||
|
var updatedAgent = client.beta().agents().update(agent.id(), AgentUpdateParams.builder()
|
||||||
|
.version(agent.version())
|
||||||
|
.system("You are a helpful coding agent. Always write tests.")
|
||||||
|
.build());
|
||||||
|
System.out.println("New version: " + updatedAgent.version());
|
||||||
|
|
||||||
|
// List all versions
|
||||||
|
for (var version : client.beta().agents().versions().list(agent.id()).autoPager()) {
|
||||||
|
System.out.println("Version " + version.version() + ": " + version.updatedAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Archive the agent
|
||||||
|
var archived = client.beta().agents().archive(agent.id());
|
||||||
|
System.out.println("Archived at: " + archived.archivedAt().orElseThrow());
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Send a User Message
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.sessions.events.BetaManagedAgentsUserMessageEventParams;
|
||||||
|
import com.anthropic.models.beta.sessions.events.EventSendParams;
|
||||||
|
|
||||||
|
client.beta().sessions().events().send(session.id(), EventSendParams.builder()
|
||||||
|
.addEvent(BetaManagedAgentsUserMessageEventParams.builder()
|
||||||
|
.type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
|
||||||
|
.addTextContent("Review the auth module")
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Stream-first:** Open the stream *before* (or concurrently with) sending the message. The stream only delivers events that occur after it opens — stream-after-send means early events arrive buffered in one batch. See [Steering Patterns](../../shared/managed-agents-events.md#steering-patterns).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stream Events (SSE)
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.sessions.events.StreamEvents;
|
||||||
|
|
||||||
|
// Open the stream first, then send the user message
|
||||||
|
try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
|
||||||
|
client.beta().sessions().events().send(session.id(), EventSendParams.builder()
|
||||||
|
.addEvent(BetaManagedAgentsUserMessageEventParams.builder()
|
||||||
|
.type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
|
||||||
|
.addTextContent("Summarize the repo README")
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
for (var event : (Iterable<StreamEvents>) stream.stream()::iterator) {
|
||||||
|
if (event.isAgentMessage()) {
|
||||||
|
event.asAgentMessage().content().forEach(block -> System.out.print(block.text()));
|
||||||
|
} else if (event.isAgentToolUse()) {
|
||||||
|
System.out.println("\n[Using tool: " + event.asAgentToolUse().name() + "]");
|
||||||
|
} else if (event.isSessionStatusIdle()) {
|
||||||
|
break;
|
||||||
|
} else if (event.isSessionError()) {
|
||||||
|
System.out.println("\n[Error]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reconnecting and Tailing
|
||||||
|
|
||||||
|
When reconnecting mid-session, list past events first to dedupe, then tail live events. The cross-variant `id` field is read from the raw `_json()` value:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.core.JsonValue;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
|
||||||
|
// Stream is open and buffering. List history before tailing live.
|
||||||
|
var seenEventIds = new HashSet<String>();
|
||||||
|
for (var past : client.beta().sessions().events().list(session.id()).autoPager()) {
|
||||||
|
Optional<Map<String, JsonValue>> obj = past._json().orElseThrow().asObject();
|
||||||
|
seenEventIds.add(obj.orElseThrow().get("id").asStringOrThrow());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail live events, skipping anything already seen
|
||||||
|
for (var event : (Iterable<StreamEvents>) stream.stream()::iterator) {
|
||||||
|
Optional<Map<String, JsonValue>> obj = event._json().orElseThrow().asObject();
|
||||||
|
if (!seenEventIds.add(obj.orElseThrow().get("id").asStringOrThrow())) continue;
|
||||||
|
if (event.isAgentMessage()) {
|
||||||
|
event.asAgentMessage().content().forEach(block -> System.out.print(block.text()));
|
||||||
|
} else if (event.isSessionStatusIdle()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Provide Custom Tool Result
|
||||||
|
|
||||||
|
> ℹ️ The Java managed-agents bindings for `user.custom_tool_result` are not yet documented in this skill or in the apps source examples. Refer to `shared/managed-agents-events.md` for the wire format and the `anthropic-java` repository for the corresponding params types.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Poll Events
|
||||||
|
|
||||||
|
```java
|
||||||
|
for (var event : client.beta().sessions().events().list(session.id()).autoPager()) {
|
||||||
|
System.out.println(event.type() + ": " + event);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upload a File
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.files.FileUploadParams;
|
||||||
|
import com.anthropic.models.beta.sessions.BetaManagedAgentsFileResourceParams;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
var dataCsv = Path.of("data.csv");
|
||||||
|
|
||||||
|
var file = client.beta().files().upload(FileUploadParams.builder()
|
||||||
|
.file(dataCsv)
|
||||||
|
.build());
|
||||||
|
System.out.println("File ID: " + file.id());
|
||||||
|
|
||||||
|
// Mount in a session
|
||||||
|
var session = client.beta().sessions().create(SessionCreateParams.builder()
|
||||||
|
.agent(agent.id())
|
||||||
|
.environmentId(environment.id())
|
||||||
|
.addResource(BetaManagedAgentsFileResourceParams.builder()
|
||||||
|
.type(BetaManagedAgentsFileResourceParams.Type.FILE)
|
||||||
|
.fileId(file.id())
|
||||||
|
.mountPath("/workspace/data.csv")
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add and Manage Resources on an Existing Session
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.sessions.resources.ResourceAddParams;
|
||||||
|
import com.anthropic.models.beta.sessions.resources.ResourceDeleteParams;
|
||||||
|
|
||||||
|
// Attach an additional file to an open session
|
||||||
|
var resource = client.beta().sessions().resources().add(session.id(), ResourceAddParams.builder()
|
||||||
|
.betaManagedAgentsFileResourceParams(BetaManagedAgentsFileResourceParams.builder()
|
||||||
|
.type(BetaManagedAgentsFileResourceParams.Type.FILE)
|
||||||
|
.fileId(file.id())
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
System.out.println(resource.id()); // "sesrsc_01ABC..."
|
||||||
|
|
||||||
|
// List resources on the session — entries are a discriminated union
|
||||||
|
var listed = client.beta().sessions().resources().list(session.id());
|
||||||
|
for (var entry : listed.data()) {
|
||||||
|
if (entry.isFile()) {
|
||||||
|
var fileResource = entry.asFile();
|
||||||
|
System.out.println(fileResource.id() + " " + fileResource.type());
|
||||||
|
} else if (entry.isGitHubRepository()) {
|
||||||
|
var repoResource = entry.asGitHubRepository();
|
||||||
|
System.out.println(repoResource.id() + " " + repoResource.type());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach a resource
|
||||||
|
client.beta().sessions().resources().delete(resource.id(), ResourceDeleteParams.builder()
|
||||||
|
.sessionId(session.id())
|
||||||
|
.build());
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List and Download Session Files
|
||||||
|
|
||||||
|
> ℹ️ Listing and downloading files an agent wrote during a session is not yet documented for Java in this skill or in the apps source examples. See `shared/managed-agents-events.md` and the `anthropic-java` repository for the file list/download bindings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Management
|
||||||
|
|
||||||
|
```java
|
||||||
|
// List environments
|
||||||
|
var environments = client.beta().environments().list();
|
||||||
|
|
||||||
|
// Retrieve a specific environment
|
||||||
|
var env = client.beta().environments().retrieve(environment.id());
|
||||||
|
|
||||||
|
// Archive an environment (read-only, existing sessions continue)
|
||||||
|
client.beta().environments().archive(environment.id());
|
||||||
|
|
||||||
|
// Delete an environment (only if no sessions reference it)
|
||||||
|
client.beta().environments().delete(environment.id());
|
||||||
|
|
||||||
|
// Delete a session
|
||||||
|
client.beta().sessions().delete(session.id());
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MCP Server Integration
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.agents.BetaManagedAgentsMcpToolsetParams;
|
||||||
|
import com.anthropic.models.beta.agents.BetaManagedAgentsUrlmcpServerParams;
|
||||||
|
|
||||||
|
// Agent declares MCP server (no auth here — auth goes in a vault)
|
||||||
|
var agent = client.beta().agents().create(AgentCreateParams.builder()
|
||||||
|
.name("GitHub Assistant")
|
||||||
|
.model("claude-opus-4-6")
|
||||||
|
.addMcpServer(BetaManagedAgentsUrlmcpServerParams.builder()
|
||||||
|
.type(BetaManagedAgentsUrlmcpServerParams.Type.URL)
|
||||||
|
.name("github")
|
||||||
|
.url("https://api.githubcopilot.com/mcp/")
|
||||||
|
.build())
|
||||||
|
.addTool(BetaManagedAgentsAgentToolset20260401Params.builder()
|
||||||
|
.type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
|
||||||
|
.build())
|
||||||
|
.addTool(BetaManagedAgentsMcpToolsetParams.builder()
|
||||||
|
.type(BetaManagedAgentsMcpToolsetParams.Type.MCP_TOOLSET)
|
||||||
|
.mcpServerName("github")
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Session attaches vault(s) containing credentials for those MCP server URLs
|
||||||
|
var session = client.beta().sessions().create(SessionCreateParams.builder()
|
||||||
|
.agent(BetaManagedAgentsAgentParams.builder()
|
||||||
|
.type(BetaManagedAgentsAgentParams.Type.AGENT)
|
||||||
|
.id(agent.id())
|
||||||
|
.version(agent.version())
|
||||||
|
.build())
|
||||||
|
.environmentId(environment.id())
|
||||||
|
.addVaultId(vault.id())
|
||||||
|
.build());
|
||||||
|
```
|
||||||
|
|
||||||
|
See `shared/managed-agents-tools.md` §Vaults for creating vaults and adding credentials.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vaults
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.core.JsonValue;
|
||||||
|
import com.anthropic.models.beta.vaults.VaultCreateParams;
|
||||||
|
import com.anthropic.models.beta.vaults.credentials.BetaManagedAgentsMcpOAuthCreateParams;
|
||||||
|
import com.anthropic.models.beta.vaults.credentials.BetaManagedAgentsMcpOAuthRefreshParams;
|
||||||
|
import com.anthropic.models.beta.vaults.credentials.BetaManagedAgentsMcpOAuthRefreshUpdateParams;
|
||||||
|
import com.anthropic.models.beta.vaults.credentials.BetaManagedAgentsMcpOAuthUpdateParams;
|
||||||
|
import com.anthropic.models.beta.vaults.credentials.CredentialCreateParams;
|
||||||
|
import com.anthropic.models.beta.vaults.credentials.CredentialUpdateParams;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
// Create a vault
|
||||||
|
var vault = client.beta().vaults().create(VaultCreateParams.builder()
|
||||||
|
.displayName("Alice")
|
||||||
|
.metadata(VaultCreateParams.Metadata.builder()
|
||||||
|
.putAdditionalProperty("external_user_id", JsonValue.from("usr_abc123"))
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
System.out.println(vault.id()); // "vlt_01ABC..."
|
||||||
|
|
||||||
|
// Add an OAuth credential
|
||||||
|
var credential = client.beta().vaults().credentials().create(vault.id(),
|
||||||
|
CredentialCreateParams.builder()
|
||||||
|
.displayName("Alice's Slack")
|
||||||
|
.auth(BetaManagedAgentsMcpOAuthCreateParams.builder()
|
||||||
|
.type(BetaManagedAgentsMcpOAuthCreateParams.Type.MCP_OAUTH)
|
||||||
|
.mcpServerUrl("https://mcp.slack.com/mcp")
|
||||||
|
.accessToken("xoxp-...")
|
||||||
|
.expiresAt(OffsetDateTime.parse("2026-04-15T00:00:00Z"))
|
||||||
|
.refresh(BetaManagedAgentsMcpOAuthRefreshParams.builder()
|
||||||
|
.tokenEndpoint("https://slack.com/api/oauth.v2.access")
|
||||||
|
.clientId("1234567890.0987654321")
|
||||||
|
.scope("channels:read chat:write")
|
||||||
|
.refreshToken("xoxe-1-...")
|
||||||
|
.clientSecretPostTokenEndpointAuth("abc123...")
|
||||||
|
.build())
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Rotate the credential (e.g., after a token refresh)
|
||||||
|
client.beta().vaults().credentials().update(credential.id(),
|
||||||
|
CredentialUpdateParams.builder()
|
||||||
|
.vaultId(vault.id())
|
||||||
|
.auth(BetaManagedAgentsMcpOAuthUpdateParams.builder()
|
||||||
|
.type(BetaManagedAgentsMcpOAuthUpdateParams.Type.MCP_OAUTH)
|
||||||
|
.accessToken("xoxp-new-...")
|
||||||
|
.expiresAt(OffsetDateTime.parse("2026-05-15T00:00:00Z"))
|
||||||
|
.refresh(BetaManagedAgentsMcpOAuthRefreshUpdateParams.builder()
|
||||||
|
.refreshToken("xoxe-1-new-...")
|
||||||
|
.build())
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// Archive a vault
|
||||||
|
client.beta().vaults().archive(vault.id());
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GitHub Repository Integration
|
||||||
|
|
||||||
|
Mount a GitHub repository as a session resource (a vault holds the GitHub MCP credential):
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.sessions.BetaManagedAgentsGitHubRepositoryResourceParams;
|
||||||
|
|
||||||
|
var session = client.beta().sessions().create(SessionCreateParams.builder()
|
||||||
|
.agent(agent.id())
|
||||||
|
.environmentId(environment.id())
|
||||||
|
.addVaultId(vault.id())
|
||||||
|
.addResource(BetaManagedAgentsGitHubRepositoryResourceParams.builder()
|
||||||
|
.type(BetaManagedAgentsGitHubRepositoryResourceParams.Type.GITHUB_REPOSITORY)
|
||||||
|
.url("https://github.com/org/repo")
|
||||||
|
.mountPath("/workspace/repo")
|
||||||
|
.authorizationToken("ghp_your_github_token")
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple repositories on the same session:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
var resources = List.of(
|
||||||
|
BetaManagedAgentsGitHubRepositoryResourceParams.builder()
|
||||||
|
.type(BetaManagedAgentsGitHubRepositoryResourceParams.Type.GITHUB_REPOSITORY)
|
||||||
|
.url("https://github.com/org/frontend")
|
||||||
|
.mountPath("/workspace/frontend")
|
||||||
|
.authorizationToken("ghp_your_github_token")
|
||||||
|
.build(),
|
||||||
|
BetaManagedAgentsGitHubRepositoryResourceParams.builder()
|
||||||
|
.type(BetaManagedAgentsGitHubRepositoryResourceParams.Type.GITHUB_REPOSITORY)
|
||||||
|
.url("https://github.com/org/backend")
|
||||||
|
.mountPath("/workspace/backend")
|
||||||
|
.authorizationToken("ghp_your_github_token")
|
||||||
|
.build());
|
||||||
|
```
|
||||||
|
|
||||||
|
Rotating a repository's authorization token:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.anthropic.models.beta.sessions.resources.ResourceUpdateParams;
|
||||||
|
|
||||||
|
var listed = client.beta().sessions().resources().list(session.id());
|
||||||
|
var repoResourceId = listed.data().get(0).asGitHubRepository().id();
|
||||||
|
|
||||||
|
client.beta().sessions().resources().update(repoResourceId, ResourceUpdateParams.builder()
|
||||||
|
.sessionId(session.id())
|
||||||
|
.authorizationToken("ghp_your_new_github_token")
|
||||||
|
.build());
|
||||||
|
```
|
||||||
435
skills/claude-api/php/managed-agents/README.md
Normal file
435
skills/claude-api/php/managed-agents/README.md
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
# Managed Agents — PHP
|
||||||
|
|
||||||
|
> **Bindings not shown here:** This README covers the most common managed-agents flows for PHP. If you need a class, method, namespace, field, or behavior that isn't shown, WebFetch the PHP SDK repo **or the relevant docs page** from `shared/live-sources.md` rather than guess. Do not extrapolate from cURL shapes or another language's SDK.
|
||||||
|
|
||||||
|
> **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `$client->beta->agents->create` and pass it to every subsequent `->sessions->create`; do not call `agents->create` in the request path. The Anthropic CLI is one convenient way to create agents and environments from version-controlled YAML — its URL is in `shared/live-sources.md`. The examples below show in-code creation for completeness; in production the create call belongs in setup, not in the request path.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require "anthropic-ai/sdk"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client Initialization
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Anthropic\Client;
|
||||||
|
|
||||||
|
// Default (uses ANTHROPIC_API_KEY env var)
|
||||||
|
$client = new Client();
|
||||||
|
|
||||||
|
// Explicit API key
|
||||||
|
$client = new Client(apiKey: 'your-api-key');
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Environment
|
||||||
|
|
||||||
|
```php
|
||||||
|
$environment = $client->beta->environments->create(
|
||||||
|
name: 'my-dev-env',
|
||||||
|
config: ['type' => 'cloud', 'networking' => ['type' => 'unrestricted']],
|
||||||
|
);
|
||||||
|
echo "Environment ID: {$environment->id}\n"; // env_...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Agent (required first step)
|
||||||
|
|
||||||
|
> ⚠️ **There is no inline agent config.** `model`/`system`/`tools` live on the agent object, not the session. Always start with `$client->beta->agents->create()` — the session takes either `agent: $agent->id` or the typed `BetaManagedAgentsAgentParams::with(type: 'agent', id: $agent->id, version: $agent->version)`.
|
||||||
|
|
||||||
|
### Minimal
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Anthropic\Beta\Agents\BetaManagedAgentsAgentToolset20260401Params;
|
||||||
|
|
||||||
|
// 1. Create the agent (reusable, versioned)
|
||||||
|
$agent = $client->beta->agents->create(
|
||||||
|
name: 'Coding Assistant',
|
||||||
|
model: 'claude-opus-4-6',
|
||||||
|
system: 'You are a helpful coding assistant.',
|
||||||
|
tools: [
|
||||||
|
BetaManagedAgentsAgentToolset20260401Params::with(
|
||||||
|
type: 'agent_toolset_20260401',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Start a session
|
||||||
|
$session = $client->beta->sessions->create(
|
||||||
|
agent: ['type' => 'agent', 'id' => $agent->id, 'version' => $agent->version],
|
||||||
|
environmentID: $environment->id,
|
||||||
|
title: 'Quickstart session',
|
||||||
|
);
|
||||||
|
echo "Session ID: {$session->id}\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updating an Agent
|
||||||
|
|
||||||
|
Updates create new versions; the agent object is immutable per version.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$updatedAgent = $client->beta->agents->update(
|
||||||
|
$agent->id,
|
||||||
|
version: $agent->version,
|
||||||
|
system: 'You are a helpful coding agent. Always write tests.',
|
||||||
|
);
|
||||||
|
echo "New version: {$updatedAgent->version}\n";
|
||||||
|
|
||||||
|
// List all versions
|
||||||
|
foreach ($client->beta->agents->versions->list($agent->id)->pagingEachItem() as $version) {
|
||||||
|
echo "Version {$version->version}: {$version->updatedAt->format(DateTimeInterface::ATOM)}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Archive the agent
|
||||||
|
$archived = $client->beta->agents->archive($agent->id);
|
||||||
|
echo "Archived at: {$archived->archivedAt->format(DateTimeInterface::ATOM)}\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Send a User Message
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client->beta->sessions->events->send(
|
||||||
|
$session->id,
|
||||||
|
events: [
|
||||||
|
[
|
||||||
|
'type' => 'user.message',
|
||||||
|
'content' => [['type' => 'text', 'text' => 'Review the auth module']],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Stream-first:** Open the stream *before* (or concurrently with) sending the message. The stream only delivers events that occur after it opens — stream-after-send means early events arrive buffered in one batch. See [Steering Patterns](../../shared/managed-agents-events.md#steering-patterns).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stream Events (SSE)
|
||||||
|
|
||||||
|
> ℹ️ **Streaming transporter:** PHP's default buffered PSR-18 client never returns for the open-ended session event stream. Use a streaming Guzzle transporter for `streamStream()` calls — other calls keep the default client.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$streamingClient = new GuzzleHttp\Client(['stream' => true]);
|
||||||
|
|
||||||
|
// Open the stream first, then send the user message
|
||||||
|
$stream = $client->beta->sessions->events->streamStream(
|
||||||
|
$session->id,
|
||||||
|
requestOptions: ['transporter' => $streamingClient],
|
||||||
|
);
|
||||||
|
$client->beta->sessions->events->send(
|
||||||
|
$session->id,
|
||||||
|
events: [
|
||||||
|
[
|
||||||
|
'type' => 'user.message',
|
||||||
|
'content' => [['type' => 'text', 'text' => 'Summarize the repo README']],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($stream as $event) {
|
||||||
|
match ($event->type) {
|
||||||
|
'agent.message' => array_walk(
|
||||||
|
$event->content,
|
||||||
|
static fn($block) => $block->type === 'text' ? print($block->text) : null,
|
||||||
|
),
|
||||||
|
'agent.tool_use' => print("\n[Using tool: {$event->name}]\n"),
|
||||||
|
'session.error' => printf("\n[Error: %s]", $event->error?->message ?? 'unknown'),
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
if ($event->type === 'session.status_idle' || $event->type === 'session.error') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$stream->close();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reconnecting and Tailing
|
||||||
|
|
||||||
|
When reconnecting mid-session, list past events first to dedupe, then tail live events:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$stream = $client->beta->sessions->events->streamStream(
|
||||||
|
$session->id,
|
||||||
|
requestOptions: ['transporter' => $streamingClient],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Stream is open and buffering. List history before tailing live.
|
||||||
|
$seenEventIds = [];
|
||||||
|
foreach ($client->beta->sessions->events->list($session->id)->pagingEachItem() as $event) {
|
||||||
|
$seenEventIds[$event->id] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail live events, skipping anything already seen
|
||||||
|
foreach ($stream as $event) {
|
||||||
|
if (isset($seenEventIds[$event->id])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$seenEventIds[$event->id] = true;
|
||||||
|
match ($event->type) {
|
||||||
|
'agent.message' => array_walk(
|
||||||
|
$event->content,
|
||||||
|
static fn($block) => $block->type === 'text' ? print($block->text) : null,
|
||||||
|
),
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
if ($event->type === 'session.status_idle') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$stream->close();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Provide Custom Tool Result
|
||||||
|
|
||||||
|
> ℹ️ The PHP managed-agents bindings for `user.custom_tool_result` are not yet documented in this skill or in the apps source examples. Refer to `shared/managed-agents-events.md` for the wire format and the `anthropic-ai/sdk` PHP repository for the corresponding params.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Poll Events
|
||||||
|
|
||||||
|
```php
|
||||||
|
foreach ($client->beta->sessions->events->list($session->id)->pagingEachItem() as $event) {
|
||||||
|
echo "{$event->type}: {$event->id}\n";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upload a File
|
||||||
|
|
||||||
|
> ℹ️ **PHP file upload:** The PHP SDK's beta managed-agents file upload binding is not shown in the apps source examples; the canonical PHP example uses raw cURL against `POST /v1/files`. If your codebase prefers the SDK, WebFetch the `anthropic-ai/sdk` PHP repository for the latest binding before writing code.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Anthropic\Beta\Sessions\BetaManagedAgentsFileResourceParams;
|
||||||
|
|
||||||
|
// Raw cURL upload (canonical example from the apps source)
|
||||||
|
$csvPath = 'data.csv';
|
||||||
|
$ch = curl_init('https://api.anthropic.com/v1/files');
|
||||||
|
curl_setopt_array($ch, [
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_POST => true,
|
||||||
|
CURLOPT_HTTPHEADER => [
|
||||||
|
'x-api-key: ' . getenv('ANTHROPIC_API_KEY'),
|
||||||
|
'anthropic-version: 2023-06-01',
|
||||||
|
'anthropic-beta: files-api-2025-04-14',
|
||||||
|
],
|
||||||
|
CURLOPT_POSTFIELDS => ['file' => new CURLFile($csvPath, 'text/csv', 'data.csv')],
|
||||||
|
]);
|
||||||
|
$file = json_decode(curl_exec($ch));
|
||||||
|
echo "File ID: {$file->id}\n";
|
||||||
|
|
||||||
|
// Mount in a session
|
||||||
|
$session = $client->beta->sessions->create(
|
||||||
|
agent: $agent->id,
|
||||||
|
environmentID: $environment->id,
|
||||||
|
resources: [
|
||||||
|
BetaManagedAgentsFileResourceParams::with(
|
||||||
|
type: 'file',
|
||||||
|
fileID: $file->id,
|
||||||
|
mountPath: '/workspace/data.csv',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add and Manage Resources on an Existing Session
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Attach an additional file to an open session
|
||||||
|
$resource = $client->beta->sessions->resources->add(
|
||||||
|
$session->id,
|
||||||
|
type: 'file',
|
||||||
|
fileID: $file->id,
|
||||||
|
);
|
||||||
|
echo "{$resource->id}\n"; // "sesrsc_01ABC..."
|
||||||
|
|
||||||
|
// List resources on the session
|
||||||
|
$listed = $client->beta->sessions->resources->list($session->id);
|
||||||
|
foreach ($listed->data as $entry) {
|
||||||
|
echo "{$entry->id} {$entry->type}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach a resource
|
||||||
|
$client->beta->sessions->resources->delete($resource->id, sessionID: $session->id);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List and Download Session Files
|
||||||
|
|
||||||
|
> ℹ️ Listing and downloading files an agent wrote during a session is not yet documented for PHP in this skill or in the apps source examples. See `shared/managed-agents-events.md` and the `anthropic-ai/sdk` PHP repository for the file list/download bindings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Management
|
||||||
|
|
||||||
|
```php
|
||||||
|
// List environments
|
||||||
|
$environments = $client->beta->environments->list();
|
||||||
|
|
||||||
|
// Retrieve a specific environment
|
||||||
|
$env = $client->beta->environments->retrieve($environment->id);
|
||||||
|
|
||||||
|
// Archive an environment (read-only, existing sessions continue)
|
||||||
|
$client->beta->environments->archive($environment->id);
|
||||||
|
|
||||||
|
// Delete an environment (only if no sessions reference it)
|
||||||
|
$client->beta->environments->delete($environment->id);
|
||||||
|
|
||||||
|
// Delete a session
|
||||||
|
$client->beta->sessions->delete($session->id);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MCP Server Integration
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Anthropic\Beta\Agents\BetaManagedAgentsAgentToolset20260401Params;
|
||||||
|
use Anthropic\Beta\Agents\BetaManagedAgentsMCPToolsetParams;
|
||||||
|
use Anthropic\Beta\Agents\BetaManagedAgentsUrlmcpServerParams;
|
||||||
|
use Anthropic\Beta\Sessions\BetaManagedAgentsAgentParams;
|
||||||
|
|
||||||
|
// Agent declares MCP server (no auth here — auth goes in a vault)
|
||||||
|
$agent = $client->beta->agents->create(
|
||||||
|
name: 'GitHub Assistant',
|
||||||
|
model: 'claude-opus-4-6',
|
||||||
|
mcpServers: [
|
||||||
|
BetaManagedAgentsUrlmcpServerParams::with(
|
||||||
|
type: 'url',
|
||||||
|
name: 'github',
|
||||||
|
url: 'https://api.githubcopilot.com/mcp/',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tools: [
|
||||||
|
BetaManagedAgentsAgentToolset20260401Params::with(type: 'agent_toolset_20260401'),
|
||||||
|
BetaManagedAgentsMCPToolsetParams::with(
|
||||||
|
type: 'mcp_toolset',
|
||||||
|
mcpServerName: 'github',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Session attaches vault(s) containing credentials for those MCP server URLs
|
||||||
|
$session = $client->beta->sessions->create(
|
||||||
|
agent: BetaManagedAgentsAgentParams::with(
|
||||||
|
type: 'agent',
|
||||||
|
id: $agent->id,
|
||||||
|
version: $agent->version,
|
||||||
|
),
|
||||||
|
environmentID: $environment->id,
|
||||||
|
vaultIDs: [$vault->id],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
See `shared/managed-agents-tools.md` §Vaults for creating vaults and adding credentials.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vaults
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Create a vault
|
||||||
|
$vault = $client->beta->vaults->create(
|
||||||
|
displayName: 'Alice',
|
||||||
|
metadata: ['external_user_id' => 'usr_abc123'],
|
||||||
|
);
|
||||||
|
echo $vault->id . "\n"; // "vlt_01ABC..."
|
||||||
|
|
||||||
|
// Add an OAuth credential
|
||||||
|
$credential = $client->beta->vaults->credentials->create(
|
||||||
|
vaultID: $vault->id,
|
||||||
|
displayName: "Alice's Slack",
|
||||||
|
auth: [
|
||||||
|
'type' => 'mcp_oauth',
|
||||||
|
'mcp_server_url' => 'https://mcp.slack.com/mcp',
|
||||||
|
'access_token' => 'xoxp-...',
|
||||||
|
'expires_at' => '2026-04-15T00:00:00Z',
|
||||||
|
'refresh' => [
|
||||||
|
'token_endpoint' => 'https://slack.com/api/oauth.v2.access',
|
||||||
|
'client_id' => '1234567890.0987654321',
|
||||||
|
'scope' => 'channels:read chat:write',
|
||||||
|
'refresh_token' => 'xoxe-1-...',
|
||||||
|
'token_endpoint_auth' => [
|
||||||
|
'type' => 'client_secret_post',
|
||||||
|
'client_secret' => 'abc123...',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rotate the credential (e.g., after a token refresh)
|
||||||
|
$client->beta->vaults->credentials->update(
|
||||||
|
$credential->id,
|
||||||
|
vaultID: $vault->id,
|
||||||
|
auth: [
|
||||||
|
'type' => 'mcp_oauth',
|
||||||
|
'access_token' => 'xoxp-new-...',
|
||||||
|
'expires_at' => '2026-05-15T00:00:00Z',
|
||||||
|
'refresh' => ['refresh_token' => 'xoxe-1-new-...'],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Archive a vault
|
||||||
|
$client->beta->vaults->archive($vault->id);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GitHub Repository Integration
|
||||||
|
|
||||||
|
Mount a GitHub repository as a session resource (a vault holds the GitHub MCP credential):
|
||||||
|
|
||||||
|
```php
|
||||||
|
$session = $client->beta->sessions->create(
|
||||||
|
agent: $agent->id,
|
||||||
|
environmentID: $environment->id,
|
||||||
|
vaultIDs: [$vault->id],
|
||||||
|
resources: [
|
||||||
|
[
|
||||||
|
'type' => 'github_repository',
|
||||||
|
'url' => 'https://github.com/org/repo',
|
||||||
|
'mountPath' => '/workspace/repo',
|
||||||
|
'authorizationToken' => 'ghp_your_github_token',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple repositories on the same session:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$resources = [
|
||||||
|
[
|
||||||
|
'type' => 'github_repository',
|
||||||
|
'url' => 'https://github.com/org/frontend',
|
||||||
|
'mountPath' => '/workspace/frontend',
|
||||||
|
'authorizationToken' => 'ghp_your_github_token',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'type' => 'github_repository',
|
||||||
|
'url' => 'https://github.com/org/backend',
|
||||||
|
'mountPath' => '/workspace/backend',
|
||||||
|
'authorizationToken' => 'ghp_your_github_token',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
Rotating a repository's authorization token:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$listed = $client->beta->sessions->resources->list($session->id);
|
||||||
|
$repoResourceId = $listed->data[0]->id;
|
||||||
|
|
||||||
|
$client->beta->sessions->resources->update(
|
||||||
|
$repoResourceId,
|
||||||
|
sessionID: $session->id,
|
||||||
|
authorizationToken: 'ghp_your_new_github_token',
|
||||||
|
);
|
||||||
|
```
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
# Agent SDK — Python
|
|
||||||
|
|
||||||
The Claude Agent SDK provides a higher-level interface for building AI agents with built-in tools, safety features, and agentic capabilities.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install claude-agent-sdk
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async for message in query(
|
|
||||||
prompt="Explain this codebase",
|
|
||||||
options=ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"])
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Built-in Tools
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
| --------- | ------------------------------------ |
|
|
||||||
| Read | Read files in the workspace |
|
|
||||||
| Write | Create new files |
|
|
||||||
| Edit | Make precise edits to existing files |
|
|
||||||
| Bash | Execute shell commands |
|
|
||||||
| Glob | Find files by pattern |
|
|
||||||
| Grep | Search files by content |
|
|
||||||
| WebSearch | Search the web for information |
|
|
||||||
| WebFetch | Fetch and analyze web pages |
|
|
||||||
| AskUserQuestion | Ask user clarifying questions |
|
|
||||||
| Agent | Spawn subagents |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Primary Interfaces
|
|
||||||
|
|
||||||
### `query()` — Simple One-Shot Usage
|
|
||||||
|
|
||||||
The `query()` function is the simplest way to run an agent. It returns an async iterator of messages.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
|
|
||||||
|
|
||||||
async for message in query(
|
|
||||||
prompt="Explain this codebase",
|
|
||||||
options=ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"])
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
```
|
|
||||||
|
|
||||||
### `ClaudeSDKClient` — Full Control
|
|
||||||
|
|
||||||
`ClaudeSDKClient` provides full control over the agent lifecycle. Use it when you need custom tools, hooks, streaming, or the ability to interrupt execution.
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, TextBlock
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
options = ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"])
|
|
||||||
async with ClaudeSDKClient(options=options) as client:
|
|
||||||
await client.query("Explain this codebase")
|
|
||||||
async for message in client.receive_response():
|
|
||||||
if isinstance(message, AssistantMessage):
|
|
||||||
for block in message.content:
|
|
||||||
if isinstance(block, TextBlock):
|
|
||||||
print(block.text)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
`ClaudeSDKClient` supports:
|
|
||||||
|
|
||||||
- **Context manager** (`async with`) for automatic resource cleanup
|
|
||||||
- **`client.query(prompt)`** to send a prompt to the agent
|
|
||||||
- **`receive_response()`** for streaming messages until completion
|
|
||||||
- **`interrupt()`** to stop agent execution mid-task
|
|
||||||
- **Required for custom tools** (via SDK MCP servers)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Permission System
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
|
|
||||||
|
|
||||||
async for message in query(
|
|
||||||
prompt="Refactor the authentication module",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Read", "Edit", "Write"],
|
|
||||||
permission_mode="acceptEdits" # Auto-accept file edits
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
```
|
|
||||||
|
|
||||||
Permission modes:
|
|
||||||
|
|
||||||
- `"default"`: Prompt for dangerous operations
|
|
||||||
- `"plan"`: Planning only, no execution
|
|
||||||
- `"acceptEdits"`: Auto-accept file edits
|
|
||||||
- `"bypassPermissions"`: Skip all prompts (use with caution)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## MCP (Model Context Protocol) Support
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
|
|
||||||
|
|
||||||
async for message in query(
|
|
||||||
prompt="Open example.com and describe what you see",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
mcp_servers={
|
|
||||||
"playwright": {"command": "npx", "args": ["@playwright/mcp@latest"]}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Hooks
|
|
||||||
|
|
||||||
Customize agent behavior with hooks using callback functions:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, ResultMessage
|
|
||||||
|
|
||||||
async def log_file_change(input_data, tool_use_id, context):
|
|
||||||
file_path = input_data.get('tool_input', {}).get('file_path', 'unknown')
|
|
||||||
print(f"Modified: {file_path}")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
async for message in query(
|
|
||||||
prompt="Refactor utils.py",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
permission_mode="acceptEdits",
|
|
||||||
hooks={
|
|
||||||
"PostToolUse": [HookMatcher(matcher="Edit|Write", hooks=[log_file_change])]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
```
|
|
||||||
|
|
||||||
Hook callback inputs for tool-lifecycle events (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`) include `agent_id` and `agent_type` fields, allowing hooks to identify which agent (main or subagent) triggered the tool call.
|
|
||||||
|
|
||||||
Available hook events: `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `UserPromptSubmit`, `Stop`, `SubagentStop`, `PreCompact`, `Notification`, `SubagentStart`, `PermissionRequest`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Common Options
|
|
||||||
|
|
||||||
`query()` takes a top-level `prompt` (string) and an `options` object (`ClaudeAgentOptions`):
|
|
||||||
|
|
||||||
```python
|
|
||||||
async for message in query(prompt="...", options=ClaudeAgentOptions(...)):
|
|
||||||
```
|
|
||||||
|
|
||||||
| Option | Type | Description |
|
|
||||||
| ----------------------------------- | ------ | -------------------------------------------------------------------------- |
|
|
||||||
| `cwd` | string | Working directory for file operations |
|
|
||||||
| `allowed_tools` | list | Tools the agent can use (e.g., `["Read", "Edit", "Bash"]`) |
|
|
||||||
| `tools` | list | Built-in tools to make available (restricts the default set) |
|
|
||||||
| `disallowed_tools` | list | Tools to explicitly disallow |
|
|
||||||
| `permission_mode` | string | How to handle permission prompts |
|
|
||||||
| `mcp_servers` | dict | MCP servers to connect to |
|
|
||||||
| `hooks` | dict | Hooks for customizing behavior |
|
|
||||||
| `system_prompt` | string | Custom system prompt |
|
|
||||||
| `max_turns` | int | Maximum agent turns before stopping |
|
|
||||||
| `max_budget_usd` | float | Maximum budget in USD for the query |
|
|
||||||
| `model` | string | Model ID (default: determined by CLI) |
|
|
||||||
| `agents` | dict | Subagent definitions (`dict[str, AgentDefinition]`) |
|
|
||||||
| `output_format` | dict | Structured output schema |
|
|
||||||
| `thinking` | dict | Thinking/reasoning control |
|
|
||||||
| `betas` | list | Beta features to enable (e.g., `["context-1m-2025-08-07"]`) |
|
|
||||||
| `setting_sources` | list | Settings to load (e.g., `["project"]`). Default: none (no CLAUDE.md files) |
|
|
||||||
| `env` | dict | Environment variables to set for the session |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Message Types
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage, SystemMessage
|
|
||||||
|
|
||||||
async for message in query(
|
|
||||||
prompt="Find TODO comments",
|
|
||||||
options=ClaudeAgentOptions(allowed_tools=["Read", "Glob", "Grep"])
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
print(f"Stop reason: {message.stop_reason}") # e.g., "end_turn", "max_turns"
|
|
||||||
elif isinstance(message, SystemMessage) and message.subtype == "init":
|
|
||||||
session_id = message.data.get("session_id") # Capture for resuming later
|
|
||||||
```
|
|
||||||
|
|
||||||
`AssistantMessage` includes per-turn `usage` data (a dict matching the Anthropic API usage shape) for tracking costs:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage
|
|
||||||
|
|
||||||
async for message in query(prompt="...", options=ClaudeAgentOptions()):
|
|
||||||
if isinstance(message, AssistantMessage) and message.usage:
|
|
||||||
print(f"Input: {message.usage['input_tokens']}, Output: {message.usage['output_tokens']}")
|
|
||||||
```
|
|
||||||
|
|
||||||
Typed task message subclasses are available for better type safety when handling subagent task events:
|
|
||||||
- `TaskStartedMessage` — emitted when a subagent task is registered
|
|
||||||
- `TaskProgressMessage` — real-time progress updates with cumulative usage metrics
|
|
||||||
- `TaskNotificationMessage` — task completion notifications
|
|
||||||
|
|
||||||
`RateLimitEvent` is emitted when the rate limit status transitions (e.g., from `allowed` to `allowed_warning` or `rejected`). Use it to warn users or back off gracefully:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, RateLimitEvent
|
|
||||||
|
|
||||||
async for message in query(prompt="...", options=ClaudeAgentOptions()):
|
|
||||||
if isinstance(message, RateLimitEvent):
|
|
||||||
print(f"Rate limit status: {message.rate_limit_info.status}")
|
|
||||||
if message.rate_limit_info.resets_at:
|
|
||||||
print(f"Resets at: {message.rate_limit_info.resets_at}")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Subagents
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition, ResultMessage
|
|
||||||
|
|
||||||
async for message in query(
|
|
||||||
prompt="Use the code-reviewer agent to review this codebase",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Read", "Glob", "Grep", "Agent"],
|
|
||||||
agents={
|
|
||||||
"code-reviewer": AgentDefinition(
|
|
||||||
description="Expert code reviewer for quality and security reviews.",
|
|
||||||
prompt="Analyze code quality and suggest improvements.",
|
|
||||||
tools=["Read", "Glob", "Grep"]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, CLINotFoundError, CLIConnectionError, ResultMessage
|
|
||||||
|
|
||||||
try:
|
|
||||||
async for message in query(
|
|
||||||
prompt="...",
|
|
||||||
options=ClaudeAgentOptions(allowed_tools=["Read"])
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
except CLINotFoundError:
|
|
||||||
print("Claude Code CLI not found. Install with: pip install claude-agent-sdk")
|
|
||||||
except CLIConnectionError as e:
|
|
||||||
print(f"Connection error: {e}")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session History
|
|
||||||
|
|
||||||
Retrieve past session data with top-level functions:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import list_sessions, get_session_messages
|
|
||||||
|
|
||||||
# List all past sessions (sync function — no await)
|
|
||||||
sessions = list_sessions()
|
|
||||||
for session in sessions:
|
|
||||||
print(f"{session.session_id}: {session.cwd}")
|
|
||||||
|
|
||||||
# Get messages from a specific session (sync function — no await)
|
|
||||||
messages = get_session_messages(session_id="...")
|
|
||||||
for msg in messages:
|
|
||||||
print(msg)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Session Mutations
|
|
||||||
|
|
||||||
Rename or tag sessions (sync functions — no await):
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import rename_session, tag_session
|
|
||||||
|
|
||||||
# Rename a session
|
|
||||||
rename_session(session_id="...", title="My refactoring session")
|
|
||||||
|
|
||||||
# Tag a session (tags are Unicode-sanitized automatically)
|
|
||||||
tag_session(session_id="...", tag="experiment")
|
|
||||||
|
|
||||||
# Clear a tag
|
|
||||||
tag_session(session_id="...", tag=None)
|
|
||||||
|
|
||||||
# Optionally scope to a specific project directory
|
|
||||||
rename_session(session_id="...", title="New title", directory="/path/to/project")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## MCP Server Management
|
|
||||||
|
|
||||||
Manage MCP servers at runtime using `ClaudeSDKClient`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
async with ClaudeSDKClient(options=options) as client:
|
|
||||||
# Reconnect a disconnected MCP server
|
|
||||||
await client.reconnect_mcp_server("my-server")
|
|
||||||
|
|
||||||
# Toggle an MCP server on/off
|
|
||||||
await client.toggle_mcp_server("my-server", enabled=False)
|
|
||||||
|
|
||||||
# Get status of all MCP servers
|
|
||||||
status = await client.get_mcp_status() # returns McpStatusResponse
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Always specify allowed_tools** — Explicitly list which tools the agent can use
|
|
||||||
2. **Set working directory** — Always specify `cwd` for file operations
|
|
||||||
3. **Use appropriate permission modes** — Start with `"default"` and only escalate when needed
|
|
||||||
4. **Handle all message types** — Check for `ResultMessage` to get agent output
|
|
||||||
5. **Limit max_turns** — Prevent runaway agents with reasonable limits
|
|
||||||
@@ -1,359 +0,0 @@
|
|||||||
# Agent SDK Patterns — Python
|
|
||||||
|
|
||||||
## Basic Agent
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async for message in query(
|
|
||||||
prompt="Explain what this repository does",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
cwd="/path/to/project",
|
|
||||||
allowed_tools=["Read", "Glob", "Grep"]
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Custom Tools
|
|
||||||
|
|
||||||
Custom tools require an MCP server. Use `ClaudeSDKClient` for full control (custom SDK MCP tools require `ClaudeSDKClient` — `query()` only supports external stdio/http MCP servers).
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import (
|
|
||||||
tool,
|
|
||||||
create_sdk_mcp_server,
|
|
||||||
ClaudeSDKClient,
|
|
||||||
ClaudeAgentOptions,
|
|
||||||
AssistantMessage,
|
|
||||||
TextBlock,
|
|
||||||
)
|
|
||||||
|
|
||||||
@tool("get_weather", "Get the current weather for a location", {"location": str})
|
|
||||||
async def get_weather(args):
|
|
||||||
location = args["location"]
|
|
||||||
return {"content": [{"type": "text", "text": f"The weather in {location} is sunny and 72°F."}]}
|
|
||||||
|
|
||||||
server = create_sdk_mcp_server("weather-tools", tools=[get_weather])
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
options = ClaudeAgentOptions(mcp_servers={"weather": server})
|
|
||||||
async with ClaudeSDKClient(options=options) as client:
|
|
||||||
await client.query("What's the weather in Paris?")
|
|
||||||
async for message in client.receive_response():
|
|
||||||
if isinstance(message, AssistantMessage):
|
|
||||||
for block in message.content:
|
|
||||||
if isinstance(block, TextBlock):
|
|
||||||
print(block.text)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Hooks
|
|
||||||
|
|
||||||
### After Tool Use Hook
|
|
||||||
|
|
||||||
Log file changes after any edit:
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from datetime import datetime
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, ResultMessage
|
|
||||||
|
|
||||||
async def log_file_change(input_data, tool_use_id, context):
|
|
||||||
file_path = input_data.get('tool_input', {}).get('file_path', 'unknown')
|
|
||||||
with open('./audit.log', 'a') as f:
|
|
||||||
f.write(f"{datetime.now()}: modified {file_path}\n")
|
|
||||||
return {}
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async for message in query(
|
|
||||||
prompt="Refactor utils.py to improve readability",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Read", "Edit", "Write"],
|
|
||||||
permission_mode="acceptEdits",
|
|
||||||
hooks={
|
|
||||||
"PostToolUse": [HookMatcher(matcher="Edit|Write", hooks=[log_file_change])]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Subagents
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition, ResultMessage
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async for message in query(
|
|
||||||
prompt="Use the code-reviewer agent to review this codebase",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Read", "Glob", "Grep", "Agent"],
|
|
||||||
agents={
|
|
||||||
"code-reviewer": AgentDefinition(
|
|
||||||
description="Expert code reviewer for quality and security reviews.",
|
|
||||||
prompt="Analyze code quality and suggest improvements.",
|
|
||||||
tools=["Read", "Glob", "Grep"]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## MCP Server Integration
|
|
||||||
|
|
||||||
### Browser Automation (Playwright)
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async for message in query(
|
|
||||||
prompt="Open example.com and describe what you see",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
mcp_servers={
|
|
||||||
"playwright": {"command": "npx", "args": ["@playwright/mcp@latest"]}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Database Access (PostgreSQL)
|
|
||||||
|
|
||||||
```python
|
|
||||||
import os
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async for message in query(
|
|
||||||
prompt="Show me the top 10 users by order count",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
mcp_servers={
|
|
||||||
"postgres": {
|
|
||||||
"command": "npx",
|
|
||||||
"args": ["-y", "@modelcontextprotocol/server-postgres"],
|
|
||||||
"env": {"DATABASE_URL": os.environ["DATABASE_URL"]}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Permission Modes
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
# Default: prompt for dangerous operations
|
|
||||||
async for message in query(
|
|
||||||
prompt="Delete all test files",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Bash"],
|
|
||||||
permission_mode="default" # Will prompt before deleting
|
|
||||||
)
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Plan: agent creates a plan before making changes
|
|
||||||
async for message in query(
|
|
||||||
prompt="Refactor the auth system",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Read", "Edit"],
|
|
||||||
permission_mode="plan"
|
|
||||||
)
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Accept edits: auto-accept file edits
|
|
||||||
async for message in query(
|
|
||||||
prompt="Refactor this module",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Read", "Edit"],
|
|
||||||
permission_mode="acceptEdits"
|
|
||||||
)
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Bypass: skip all prompts (use with caution)
|
|
||||||
async for message in query(
|
|
||||||
prompt="Set up the development environment",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Bash", "Write"],
|
|
||||||
permission_mode="bypassPermissions"
|
|
||||||
)
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Error Recovery
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import (
|
|
||||||
query,
|
|
||||||
ClaudeAgentOptions,
|
|
||||||
CLINotFoundError,
|
|
||||||
CLIConnectionError,
|
|
||||||
ProcessError,
|
|
||||||
ResultMessage,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def run_with_recovery():
|
|
||||||
try:
|
|
||||||
async for message in query(
|
|
||||||
prompt="Fix the failing tests",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Read", "Edit", "Bash"],
|
|
||||||
max_turns=10
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
except CLINotFoundError:
|
|
||||||
print("Claude Code CLI not found. Install with: pip install claude-agent-sdk")
|
|
||||||
except CLIConnectionError as e:
|
|
||||||
print(f"Connection error: {e}")
|
|
||||||
except ProcessError as e:
|
|
||||||
print(f"Process error: {e}")
|
|
||||||
|
|
||||||
anyio.run(run_with_recovery)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session Resumption
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage, SystemMessage
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
session_id = None
|
|
||||||
|
|
||||||
# First query: capture the session ID
|
|
||||||
async for message in query(
|
|
||||||
prompt="Read the authentication module",
|
|
||||||
options=ClaudeAgentOptions(allowed_tools=["Read", "Glob"])
|
|
||||||
):
|
|
||||||
if isinstance(message, SystemMessage) and message.subtype == "init":
|
|
||||||
session_id = message.data.get("session_id")
|
|
||||||
|
|
||||||
# Resume with full context from the first query
|
|
||||||
async for message in query(
|
|
||||||
prompt="Now find all places that call it", # "it" = auth module
|
|
||||||
options=ClaudeAgentOptions(resume=session_id)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session History
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import list_sessions, get_session_messages
|
|
||||||
|
|
||||||
# List past sessions (sync function — no await)
|
|
||||||
sessions = list_sessions()
|
|
||||||
for session in sessions:
|
|
||||||
print(f"Session {session.session_id} in {session.cwd}")
|
|
||||||
|
|
||||||
# Retrieve messages from the most recent session (sync function — no await)
|
|
||||||
if sessions:
|
|
||||||
messages = get_session_messages(session_id=sessions[0].session_id)
|
|
||||||
for msg in messages:
|
|
||||||
print(msg)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session Mutations
|
|
||||||
|
|
||||||
```python
|
|
||||||
from claude_agent_sdk import rename_session, tag_session
|
|
||||||
|
|
||||||
session_id = "your-session-id"
|
|
||||||
|
|
||||||
# Rename a session
|
|
||||||
rename_session(session_id=session_id, title="Refactoring auth module")
|
|
||||||
|
|
||||||
# Tag a session for filtering
|
|
||||||
tag_session(session_id=session_id, tag="experiment-v2")
|
|
||||||
|
|
||||||
# Clear a tag
|
|
||||||
tag_session(session_id=session_id, tag=None)
|
|
||||||
|
|
||||||
# Scope to a specific project directory
|
|
||||||
rename_session(session_id=session_id, title="New title", directory="/path/to/project")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Custom System Prompt
|
|
||||||
|
|
||||||
```python
|
|
||||||
import anyio
|
|
||||||
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async for message in query(
|
|
||||||
prompt="Review this code",
|
|
||||||
options=ClaudeAgentOptions(
|
|
||||||
allowed_tools=["Read", "Glob", "Grep"],
|
|
||||||
system_prompt="""You are a senior code reviewer focused on:
|
|
||||||
1. Security vulnerabilities
|
|
||||||
2. Performance issues
|
|
||||||
3. Code maintainability
|
|
||||||
|
|
||||||
Always provide specific line numbers and suggestions for improvement."""
|
|
||||||
)
|
|
||||||
):
|
|
||||||
if isinstance(message, ResultMessage):
|
|
||||||
print(message.result)
|
|
||||||
|
|
||||||
anyio.run(main)
|
|
||||||
```
|
|
||||||
329
skills/claude-api/python/managed-agents/README.md
Normal file
329
skills/claude-api/python/managed-agents/README.md
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
# Managed Agents — Python
|
||||||
|
|
||||||
|
> **Bindings not shown here:** This README covers the most common managed-agents flows for Python. If you need a class, method, namespace, field, or behavior that isn't shown, WebFetch the Python SDK repo **or the relevant docs page** from `shared/live-sources.md` rather than guess. Do not extrapolate from cURL shapes or another language's SDK.
|
||||||
|
|
||||||
|
> **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.create` and pass it to every subsequent `sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI is one convenient way to create agents and environments from version-controlled YAML — its URL is in `shared/live-sources.md`. The examples below show in-code creation for completeness; in production the create call belongs in setup, not in the request path.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install anthropic
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client Initialization
|
||||||
|
|
||||||
|
```python
|
||||||
|
import anthropic
|
||||||
|
|
||||||
|
# Default (uses ANTHROPIC_API_KEY env var)
|
||||||
|
client = anthropic.Anthropic()
|
||||||
|
|
||||||
|
# Explicit API key
|
||||||
|
client = anthropic.Anthropic(api_key="your-api-key")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Environment
|
||||||
|
|
||||||
|
```python
|
||||||
|
environment = client.beta.environments.create(
|
||||||
|
name="my-dev-env",
|
||||||
|
config={
|
||||||
|
"type": "cloud",
|
||||||
|
"networking": {"type": "unrestricted"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
print(environment.id) # env_...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Agent (required first step)
|
||||||
|
|
||||||
|
> ⚠️ **There is no inline agent config.** `model`/`system`/`tools` live on the agent object, not the session. Always start with `agents.create()` — the session only takes `agent={"type": "agent", "id": agent.id}`.
|
||||||
|
|
||||||
|
### Minimal
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 1. Create the agent (reusable, versioned)
|
||||||
|
agent = client.beta.agents.create(
|
||||||
|
name="Coding Assistant",
|
||||||
|
model="claude-opus-4-6",
|
||||||
|
tools=[{"type": "agent_toolset_20260401", "default_config": {"enabled": True}}],
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. Start a session
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent={"type": "agent", "id": agent.id, "version": agent.version},
|
||||||
|
environment_id=environment.id,
|
||||||
|
)
|
||||||
|
print(session.id, session.status)
|
||||||
|
```
|
||||||
|
|
||||||
|
### With system prompt and custom tools
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
|
||||||
|
agent = client.beta.agents.create(
|
||||||
|
name="Code Reviewer",
|
||||||
|
model="claude-opus-4-6",
|
||||||
|
system="You are a senior code reviewer.",
|
||||||
|
tools=[
|
||||||
|
{"type": "agent_toolset_20260401"},
|
||||||
|
{
|
||||||
|
"type": "custom",
|
||||||
|
"name": "run_tests",
|
||||||
|
"description": "Run the test suite",
|
||||||
|
"input_schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"test_path": {"type": "string", "description": "Path to test file"}
|
||||||
|
},
|
||||||
|
"required": ["test_path"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent={"type": "agent", "id": agent.id, "version": agent.version},
|
||||||
|
environment_id=environment.id,
|
||||||
|
title="Code review session",
|
||||||
|
resources=[
|
||||||
|
{
|
||||||
|
"type": "github_repository",
|
||||||
|
"url": "https://github.com/owner/repo",
|
||||||
|
"mount_path": "/workspace/repo",
|
||||||
|
"authorization_token": os.environ["GITHUB_TOKEN"],
|
||||||
|
"branch": "main",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Send a User Message
|
||||||
|
|
||||||
|
```python
|
||||||
|
client.beta.sessions.events.send(
|
||||||
|
session_id=session.id,
|
||||||
|
events=[
|
||||||
|
{
|
||||||
|
"type": "user.message",
|
||||||
|
"content": [{"type": "text", "text": "Review the auth module"}],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Stream-first:** Open the stream *before* (or concurrently with) sending the message. The stream only delivers events that occur after it opens — stream-after-send means early events arrive buffered in one batch. See [Steering Patterns](../../shared/managed-agents-events.md#steering-patterns).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stream Events (SSE)
|
||||||
|
|
||||||
|
```python
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Stream-first: open stream, then send while stream is live
|
||||||
|
with client.beta.sessions.stream(
|
||||||
|
session_id=session.id,
|
||||||
|
) as stream:
|
||||||
|
client.beta.sessions.events.send(
|
||||||
|
session_id=session.id,
|
||||||
|
events=[{"type": "user.message", "content": [{"type": "text", "text": "..."}]}],
|
||||||
|
)
|
||||||
|
for event in stream:
|
||||||
|
... # process events
|
||||||
|
|
||||||
|
# Standalone stream iteration:
|
||||||
|
with client.beta.sessions.stream(
|
||||||
|
session_id=session.id,
|
||||||
|
) as stream:
|
||||||
|
for event in stream:
|
||||||
|
if event.type == "agent.message":
|
||||||
|
for block in event.content:
|
||||||
|
if block.type == "text":
|
||||||
|
print(block.text, end="", flush=True)
|
||||||
|
elif event.type == "agent.custom_tool_use":
|
||||||
|
# Custom tool invocation — session is now idle
|
||||||
|
print(f"\nCustom tool call: {event.tool_name}")
|
||||||
|
print(f"Input: {json.dumps(event.input)}")
|
||||||
|
# Send result back (see below)
|
||||||
|
elif event.type == "session.status_idle":
|
||||||
|
print("\n--- Agent idle ---")
|
||||||
|
elif event.type == "session.status_terminated":
|
||||||
|
print("\n--- Session terminated ---")
|
||||||
|
break
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Provide Custom Tool Result
|
||||||
|
|
||||||
|
```python
|
||||||
|
client.beta.sessions.events.send(
|
||||||
|
session_id=session.id,
|
||||||
|
events=[
|
||||||
|
{
|
||||||
|
"type": "user.custom_tool_result",
|
||||||
|
"custom_tool_use_id": "sevt_abc123",
|
||||||
|
"content": [{"type": "text", "text": "All 42 tests passed."}],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Poll Events
|
||||||
|
|
||||||
|
```python
|
||||||
|
events = client.beta.sessions.events.list(
|
||||||
|
session_id=session.id,
|
||||||
|
)
|
||||||
|
for event in events.data:
|
||||||
|
print(f"{event.type}: {event.id}")
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ **Prefer the SDK over raw `requests`/`httpx`.** If you hand-roll a poll loop, don't assume `timeout=(5, 60)` or `httpx.Timeout(120)` caps total call duration — both are **per-chunk** read timeouts (reset on every byte), so a trickling response can block forever. For a hard wall-clock deadline, track `time.monotonic()` at the loop level and bail explicitly, or wrap with `asyncio.wait_for()`. See [Receiving Events](../../shared/managed-agents-events.md#receiving-events).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Full Streaming Loop with Custom Tools
|
||||||
|
|
||||||
|
```python
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def run_custom_tool(tool_name: str, tool_input: dict) -> str:
|
||||||
|
"""Execute a custom tool and return the result."""
|
||||||
|
if tool_name == "run_tests":
|
||||||
|
# Your tool implementation here
|
||||||
|
return "All tests passed."
|
||||||
|
return f"Unknown tool: {tool_name}"
|
||||||
|
|
||||||
|
|
||||||
|
def run_session(client, session_id: str):
|
||||||
|
"""Stream events and handle custom tool calls."""
|
||||||
|
while True:
|
||||||
|
with client.beta.sessions.stream(
|
||||||
|
session_id=session_id,
|
||||||
|
) as stream:
|
||||||
|
tool_calls = []
|
||||||
|
for event in stream:
|
||||||
|
if event.type == "agent.message":
|
||||||
|
for block in event.content:
|
||||||
|
if block.type == "text":
|
||||||
|
print(block.text, end="", flush=True)
|
||||||
|
elif event.type == "agent.custom_tool_use":
|
||||||
|
tool_calls.append(event)
|
||||||
|
elif event.type == "session.status_idle":
|
||||||
|
break
|
||||||
|
elif event.type == "session.status_terminated":
|
||||||
|
return
|
||||||
|
|
||||||
|
if not tool_calls:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Process custom tool calls
|
||||||
|
results = []
|
||||||
|
for call in tool_calls:
|
||||||
|
result = run_custom_tool(call.tool_name, call.input)
|
||||||
|
results.append({
|
||||||
|
"type": "user.custom_tool_result",
|
||||||
|
"custom_tool_use_id": call.id,
|
||||||
|
"content": [{"type": "text", "text": result}],
|
||||||
|
})
|
||||||
|
|
||||||
|
client.beta.sessions.events.send(
|
||||||
|
session_id=session_id,
|
||||||
|
events=results,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upload a File
|
||||||
|
|
||||||
|
```python
|
||||||
|
with open("data.csv", "rb") as f:
|
||||||
|
file = client.beta.files.upload(
|
||||||
|
file=f,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use in a session
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent={"type": "agent", "id": agent.id, "version": agent.version},
|
||||||
|
environment_id=environment.id,
|
||||||
|
resources=[{"type": "file", "file_id": file.id, "mount_path": "/workspace/data.csv"}],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List and Download Session Files
|
||||||
|
|
||||||
|
List files the agent wrote to `/mnt/session/outputs/` during a session, then download them.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# List files associated with a session
|
||||||
|
files = client.beta.files.list(session_id=session.id)
|
||||||
|
for f in files.data:
|
||||||
|
print(f.filename, f.size_bytes)
|
||||||
|
# Download each file and save to disk
|
||||||
|
file_content = client.beta.files.download(f.id)
|
||||||
|
file_content.write_to_file(f.filename)
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 There's a brief indexing lag (~1–3s) between `session.status_idle` and output files appearing in `files.list` (with `scope=session_id` as a query param). Retry once or twice if the list is empty.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Management
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Get session details
|
||||||
|
session = client.beta.sessions.retrieve(session_id="sess_abc123")
|
||||||
|
print(session.status, session.usage)
|
||||||
|
|
||||||
|
# List sessions
|
||||||
|
sessions = client.beta.sessions.list()
|
||||||
|
|
||||||
|
# Delete a session
|
||||||
|
client.beta.sessions.delete(session_id="sess_abc123")
|
||||||
|
|
||||||
|
# Archive a session
|
||||||
|
client.beta.sessions.archive(session_id="sess_abc123")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MCP Server Integration
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Agent declares MCP server (no auth here — auth goes in a vault)
|
||||||
|
agent = client.beta.agents.create(
|
||||||
|
name="MCP Agent",
|
||||||
|
model="claude-opus-4-6",
|
||||||
|
mcp_servers=[
|
||||||
|
{"type": "url", "name": "my-tools", "url": "https://my-mcp-server.example.com/sse"},
|
||||||
|
],
|
||||||
|
tools=[
|
||||||
|
{"type": "agent_toolset_20260401", "default_config": {"enabled": True}},
|
||||||
|
{"type": "mcp_toolset", "mcp_server_name": "my-tools"},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Session attaches vault(s) containing credentials for those MCP server URLs
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent=agent.id,
|
||||||
|
environment_id=environment.id,
|
||||||
|
vault_ids=[vault.id],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
See `shared/managed-agents-tools.md` §Vaults for creating vaults and adding credentials.
|
||||||
389
skills/claude-api/ruby/managed-agents/README.md
Normal file
389
skills/claude-api/ruby/managed-agents/README.md
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
# Managed Agents — Ruby
|
||||||
|
|
||||||
|
> **Bindings not shown here:** This README covers the most common managed-agents flows for Ruby. If you need a class, method, namespace, field, or behavior that isn't shown, WebFetch the Ruby SDK repo **or the relevant docs page** from `shared/live-sources.md` rather than guess. Do not extrapolate from cURL shapes or another language's SDK.
|
||||||
|
|
||||||
|
> **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `client.beta.agents.create` and pass it to every subsequent `client.beta.sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI is one convenient way to create agents and environments from version-controlled YAML — its URL is in `shared/live-sources.md`. The examples below show in-code creation for completeness; in production the create call belongs in setup, not in the request path.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gem install anthropic
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client Initialization
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require "anthropic"
|
||||||
|
|
||||||
|
# Default (uses ANTHROPIC_API_KEY env var)
|
||||||
|
client = Anthropic::Client.new
|
||||||
|
|
||||||
|
# Explicit API key
|
||||||
|
client = Anthropic::Client.new(api_key: "your-api-key")
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ **Trailing underscores:** The Ruby SDK uses `system_:` and `send_(` (trailing underscore) to avoid shadowing `Kernel#system` and `Kernel#send`. Use these forms throughout managed-agents code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Environment
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
environment = client.beta.environments.create(
|
||||||
|
name: "my-dev-env",
|
||||||
|
config: {
|
||||||
|
type: "cloud",
|
||||||
|
networking: {type: "unrestricted"}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
puts "Environment ID: #{environment.id}" # env_...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Agent (required first step)
|
||||||
|
|
||||||
|
> ⚠️ **There is no inline agent config.** `model`/`system_`/`tools` live on the agent object, not the session. Always start with `client.beta.agents.create()` — the session takes either `agent: agent.id` or the typed hash form `agent: {type: "agent", id: agent.id, version: agent.version}`.
|
||||||
|
|
||||||
|
### Minimal
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# 1. Create the agent (reusable, versioned)
|
||||||
|
agent = client.beta.agents.create(
|
||||||
|
name: "Coding Assistant",
|
||||||
|
model: :"claude-opus-4-6",
|
||||||
|
system_: "You are a helpful coding assistant.",
|
||||||
|
tools: [{type: "agent_toolset_20260401"}]
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. Start a session
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent: {type: "agent", id: agent.id, version: agent.version},
|
||||||
|
environment_id: environment.id,
|
||||||
|
title: "Quickstart session"
|
||||||
|
)
|
||||||
|
puts "Session ID: #{session.id}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updating an Agent
|
||||||
|
|
||||||
|
Updates create new versions; the agent object is immutable per version.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
updated_agent = client.beta.agents.update(
|
||||||
|
agent.id,
|
||||||
|
version: agent.version,
|
||||||
|
system_: "You are a helpful coding agent. Always write tests."
|
||||||
|
)
|
||||||
|
puts "New version: #{updated_agent.version}"
|
||||||
|
|
||||||
|
# List all versions
|
||||||
|
client.beta.agents.versions.list(agent.id).auto_paging_each do |version|
|
||||||
|
puts "Version #{version.version}: #{version.updated_at.iso8601}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Archive the agent
|
||||||
|
archived = client.beta.agents.archive(agent.id)
|
||||||
|
puts "Archived at: #{archived.archived_at.iso8601}"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Send a User Message
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
client.beta.sessions.events.send_(
|
||||||
|
session.id,
|
||||||
|
events: [{
|
||||||
|
type: "user.message",
|
||||||
|
content: [{type: "text", text: "Review the auth module"}]
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Stream-first:** Open the stream *before* (or concurrently with) sending the message. The stream only delivers events that occur after it opens — stream-after-send means early events arrive buffered in one batch. See [Steering Patterns](../../shared/managed-agents-events.md#steering-patterns).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stream Events (SSE)
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Open the stream first, then send the user message
|
||||||
|
stream = client.beta.sessions.events.stream_events(session.id)
|
||||||
|
|
||||||
|
client.beta.sessions.events.send_(
|
||||||
|
session.id,
|
||||||
|
events: [{
|
||||||
|
type: "user.message",
|
||||||
|
content: [{type: "text", text: "Summarize the repo README"}]
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
|
||||||
|
stream.each do |event|
|
||||||
|
case event.type
|
||||||
|
in :"agent.message"
|
||||||
|
event.content.each { |block| print block.text }
|
||||||
|
in :"agent.tool_use"
|
||||||
|
puts "\n[Using tool: #{event.name}]"
|
||||||
|
in :"session.status_idle"
|
||||||
|
break
|
||||||
|
in :"session.error"
|
||||||
|
puts "\n[Error: #{event.error&.message || "unknown"}]"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
# ignore other event types
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
> ℹ️ Event `.type` is a Symbol (compare with `:"agent.message"`, not `"agent.message"`).
|
||||||
|
|
||||||
|
### Reconnecting and Tailing
|
||||||
|
|
||||||
|
When reconnecting mid-session, list past events first to dedupe, then tail live events:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require "set"
|
||||||
|
|
||||||
|
stream = client.beta.sessions.events.stream_events(session.id)
|
||||||
|
|
||||||
|
# Stream is open and buffering. List history before tailing live.
|
||||||
|
seen_event_ids = Set.new
|
||||||
|
client.beta.sessions.events.list(session.id).auto_paging_each { |past| seen_event_ids << past.id }
|
||||||
|
|
||||||
|
# Tail live events, skipping anything already seen
|
||||||
|
stream.each do |event|
|
||||||
|
next if seen_event_ids.include?(event.id)
|
||||||
|
seen_event_ids << event.id
|
||||||
|
case event.type
|
||||||
|
in :"agent.message"
|
||||||
|
event.content.each { |block| print block.text }
|
||||||
|
in :"session.status_idle"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
# ignore other event types
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Provide Custom Tool Result
|
||||||
|
|
||||||
|
> ℹ️ The Ruby managed-agents bindings for `user.custom_tool_result` are not yet documented in this skill or in the apps source examples. Refer to `shared/managed-agents-events.md` for the wire format and the `anthropic` Ruby gem repository for the corresponding params.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Poll Events
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
client.beta.sessions.events.list(session.id).auto_paging_each do |event|
|
||||||
|
puts "#{event.type}: #{event.id}"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upload a File
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require "pathname"
|
||||||
|
|
||||||
|
file = client.beta.files.upload(file: Pathname("data.csv"))
|
||||||
|
puts "File ID: #{file.id}"
|
||||||
|
|
||||||
|
# Mount in a session
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent: agent.id,
|
||||||
|
environment_id: environment.id,
|
||||||
|
resources: [
|
||||||
|
{
|
||||||
|
type: "file",
|
||||||
|
file_id: file.id,
|
||||||
|
mount_path: "/workspace/data.csv"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add and Manage Resources on an Existing Session
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Attach an additional file to an open session
|
||||||
|
resource = client.beta.sessions.resources.add(
|
||||||
|
session.id,
|
||||||
|
type: "file",
|
||||||
|
file_id: file.id
|
||||||
|
)
|
||||||
|
puts resource.id # "sesrsc_01ABC..."
|
||||||
|
|
||||||
|
# List resources on the session
|
||||||
|
listed = client.beta.sessions.resources.list(session.id)
|
||||||
|
listed.data.each { |entry| puts "#{entry.id} #{entry.type}" }
|
||||||
|
|
||||||
|
# Detach a resource
|
||||||
|
client.beta.sessions.resources.delete(resource.id, session_id: session.id)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List and Download Session Files
|
||||||
|
|
||||||
|
> ℹ️ Listing and downloading files an agent wrote during a session is not yet documented for Ruby in this skill or in the apps source examples. See `shared/managed-agents-events.md` and the `anthropic` Ruby gem repository for the file list/download bindings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Management
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# List environments
|
||||||
|
environments = client.beta.environments.list
|
||||||
|
|
||||||
|
# Retrieve a specific environment
|
||||||
|
env = client.beta.environments.retrieve(environment.id)
|
||||||
|
|
||||||
|
# Archive an environment (read-only, existing sessions continue)
|
||||||
|
client.beta.environments.archive(environment.id)
|
||||||
|
|
||||||
|
# Delete an environment (only if no sessions reference it)
|
||||||
|
client.beta.environments.delete(environment.id)
|
||||||
|
|
||||||
|
# Delete a session
|
||||||
|
client.beta.sessions.delete(session.id)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MCP Server Integration
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Agent declares MCP server (no auth here — auth goes in a vault)
|
||||||
|
agent = client.beta.agents.create(
|
||||||
|
name: "GitHub Assistant",
|
||||||
|
model: :"claude-opus-4-6",
|
||||||
|
mcp_servers: [
|
||||||
|
{
|
||||||
|
type: "url",
|
||||||
|
name: "github",
|
||||||
|
url: "https://api.githubcopilot.com/mcp/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tools: [
|
||||||
|
{type: "agent_toolset_20260401"},
|
||||||
|
{type: "mcp_toolset", mcp_server_name: "github"}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Session attaches vault(s) containing credentials for those MCP server URLs
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent: {type: "agent", id: agent.id, version: agent.version},
|
||||||
|
environment_id: environment.id,
|
||||||
|
vault_ids: [vault.id]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
See `shared/managed-agents-tools.md` §Vaults for creating vaults and adding credentials.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vaults
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Create a vault
|
||||||
|
vault = client.beta.vaults.create(
|
||||||
|
display_name: "Alice",
|
||||||
|
metadata: {external_user_id: "usr_abc123"}
|
||||||
|
)
|
||||||
|
puts vault.id # "vlt_01ABC..."
|
||||||
|
|
||||||
|
# Add an OAuth credential
|
||||||
|
credential = client.beta.vaults.credentials.create(
|
||||||
|
vault.id,
|
||||||
|
display_name: "Alice's Slack",
|
||||||
|
auth: {
|
||||||
|
type: "mcp_oauth",
|
||||||
|
mcp_server_url: "https://mcp.slack.com/mcp",
|
||||||
|
access_token: "xoxp-...",
|
||||||
|
expires_at: "2026-04-15T00:00:00Z",
|
||||||
|
refresh: {
|
||||||
|
token_endpoint: "https://slack.com/api/oauth.v2.access",
|
||||||
|
client_id: "1234567890.0987654321",
|
||||||
|
scope: "channels:read chat:write",
|
||||||
|
refresh_token: "xoxe-1-...",
|
||||||
|
token_endpoint_auth: {
|
||||||
|
type: "client_secret_post",
|
||||||
|
client_secret: "abc123..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Rotate the credential (e.g., after a token refresh)
|
||||||
|
client.beta.vaults.credentials.update(
|
||||||
|
credential.id,
|
||||||
|
vault_id: vault.id,
|
||||||
|
auth: {
|
||||||
|
type: "mcp_oauth",
|
||||||
|
access_token: "xoxp-new-...",
|
||||||
|
expires_at: "2026-05-15T00:00:00Z",
|
||||||
|
refresh: {refresh_token: "xoxe-1-new-..."}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Archive a vault
|
||||||
|
client.beta.vaults.archive(vault.id)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GitHub Repository Integration
|
||||||
|
|
||||||
|
Mount a GitHub repository as a session resource (a vault holds the GitHub MCP credential):
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent: agent.id,
|
||||||
|
environment_id: environment.id,
|
||||||
|
vault_ids: [vault.id],
|
||||||
|
resources: [
|
||||||
|
{
|
||||||
|
type: "github_repository",
|
||||||
|
url: "https://github.com/org/repo",
|
||||||
|
mount_path: "/workspace/repo",
|
||||||
|
authorization_token: "ghp_your_github_token"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple repositories on the same session:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
resources = [
|
||||||
|
{
|
||||||
|
type: "github_repository",
|
||||||
|
url: "https://github.com/org/frontend",
|
||||||
|
mount_path: "/workspace/frontend",
|
||||||
|
authorization_token: "ghp_your_github_token"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "github_repository",
|
||||||
|
url: "https://github.com/org/backend",
|
||||||
|
mount_path: "/workspace/backend",
|
||||||
|
authorization_token: "ghp_your_github_token"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Rotating a repository's authorization token:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
listed = client.beta.sessions.resources.list(session.id)
|
||||||
|
repo_resource_id = listed.data.first.id
|
||||||
|
|
||||||
|
client.beta.sessions.resources.update(
|
||||||
|
repo_resource_id,
|
||||||
|
session_id: session.id,
|
||||||
|
authorization_token: "ghp_your_new_github_token"
|
||||||
|
)
|
||||||
|
```
|
||||||
101
skills/claude-api/shared/agent-design.md
Normal file
101
skills/claude-api/shared/agent-design.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# Agent Design Patterns
|
||||||
|
|
||||||
|
This file covers decision heuristics for building agents on the Claude API: which primitives to reach for, how to design your tool surface, and how to manage context and cost over long runs. For per-tool mechanics and code examples, see `tool-use-concepts.md` and the language-specific folders.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Model Parameters
|
||||||
|
|
||||||
|
| Parameter | When to use it | What to expect |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Adaptive thinking** (`thinking: {type: "adaptive"}`) | When you want Claude to control when and how much to think. | Claude determines thinking depth per request and automatically interleaves thinking between tool calls. No token budget to tune. |
|
||||||
|
| **Effort** (`output_config: {effort: ...}`) | When adjusting the tradeoff between thoroughness and token efficiency. | Lower effort → fewer and more-consolidated tool calls, less preamble, terser confirmations. `medium` is often a favorable balance. Use `max` when correctness matters more than cost. |
|
||||||
|
|
||||||
|
See `SKILL.md` §Thinking & Effort for model support and parameter details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Designing Your Tool Surface
|
||||||
|
|
||||||
|
### Bash vs. dedicated tools
|
||||||
|
|
||||||
|
Claude doesn't know your application's security boundary, approval policy, or UX surface. Claude emits tool calls; your harness handles them. The shape of those tool calls determines what the harness can do.
|
||||||
|
|
||||||
|
A **bash tool** gives Claude broad programmatic leverage — it can perform almost any action. But it gives the harness only an opaque command string, the same shape for every action. Promoting an action to a **dedicated tool** gives the harness an action-specific hook with typed arguments it can intercept, gate, render, or audit.
|
||||||
|
|
||||||
|
**When to promote an action to a dedicated tool:**
|
||||||
|
|
||||||
|
- **Security boundary.** Actions that require gating are natural candidates. Reversibility is a useful criterion: hard-to-reverse actions (external API calls, sending messages, deleting data) can be gated behind user confirmation. A `send_email` tool is easy to gate; `bash -c "curl -X POST ..."` is not.
|
||||||
|
- **Staleness checks.** A dedicated `edit` tool can reject writes if the file changed since Claude last read it. Bash can't enforce that invariant.
|
||||||
|
- **Rendering.** Some actions benefit from custom UI. Claude Code promotes question-asking to a tool so it can render as a modal, present options, and block the agent loop until answered.
|
||||||
|
- **Scheduling.** Read-only tools like `glob` and `grep` can be marked parallel-safe. When the same actions run through bash, the harness can't tell a parallel-safe `grep` from a parallel-unsafe `git push`, so it must serialize.
|
||||||
|
|
||||||
|
**Rule of thumb:** Start with bash for breadth. Promote to dedicated tools when you need to gate, render, audit, or parallelize the action.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anthropic-Provided Tools
|
||||||
|
|
||||||
|
| Tool | Side | When to use it | What to expect |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| **Bash** | Client | Claude needs to execute shell commands. | Claude emits commands; your harness executes them. Reference implementation provided. |
|
||||||
|
| **Text editor** | Client | Claude needs to read or edit files. | Claude views, creates, and edits files via your implementation. Reference implementation provided. |
|
||||||
|
| **Computer use** | Client or Server | Claude needs to interact with GUIs, web apps, or visual interfaces. | Claude takes screenshots and issues mouse/keyboard commands. Can be self-hosted (you run the environment) or Anthropic-hosted. |
|
||||||
|
| **Code execution** | Server | Claude needs to run code in a sandbox you don't want to manage. | Anthropic-hosted container with built-in file and bash sub-tools. No client-side execution. |
|
||||||
|
| **Web search / fetch** | Server | Claude needs information past its training cutoff (news, current events, recent docs) or the content of a specific URL. | Claude issues a query or URL; Anthropic executes it and returns results with citations. |
|
||||||
|
| **Memory** | Client | Claude needs to save context across sessions. | Claude reads/writes a `/memories` directory. You implement the storage backend. |
|
||||||
|
|
||||||
|
**Client-side** tools are defined by Anthropic (name, schema, Claude's usage pattern) but executed by your harness. Anthropic provides reference implementations. **Server-side** tools run entirely on Anthropic infrastructure — declare them in `tools` and Claude handles the rest.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Composing Tool Calls: Programmatic Tool Calling
|
||||||
|
|
||||||
|
With standard tool use, each tool call is a round trip: Claude calls the tool, the result lands in Claude's context, Claude reasons about it, then calls the next tool. Three sequential actions (read profile → look up orders → check inventory) means three round trips. Each adds latency and tokens, and most of the intermediate data is never needed again.
|
||||||
|
|
||||||
|
**Programmatic tool calling (PTC)** lets Claude compose those calls into a script instead. The script runs in the code execution container. When the script calls a tool, the container pauses, the call is executed (client-side or server-side), and the result returns to the running code — not to Claude's context. The script processes it with normal control flow (loops, filters, branches). Only the script's final output returns to Claude.
|
||||||
|
|
||||||
|
| When to use it | What to expect |
|
||||||
|
| --- | --- |
|
||||||
|
| Many sequential tool calls, or large intermediate results you want filtered before they hit the context window. | Claude writes code that invokes tools as functions. Runs in the code execution container. Token cost scales with final output, not intermediate results. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scaling the Tool and Instruction Set
|
||||||
|
|
||||||
|
| Feature | When to use it | What to expect |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Tool search** | Many tools available, but only a few relevant per request. Don't want all schemas in context upfront. | Claude searches the tool set and loads only relevant schemas. Tool definitions are appended, not swapped — preserves cache (see Caching below). |
|
||||||
|
| **Skills** | Task-specific instructions Claude should load only when relevant. | Each skill is a folder with a `SKILL.md`. The skill's description sits in context by default; Claude reads the full file when the task calls for it. |
|
||||||
|
|
||||||
|
Both patterns keep the fixed context small and load detail on demand.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Long-Running Agents: Managing Context
|
||||||
|
|
||||||
|
| Pattern | When to use it | What to expect |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Context editing** | Context grows stale over many turns (old tool results, completed thinking). | Tool results and thinking blocks are cleared based on configurable thresholds. Keeps the transcript lean without summarizing. |
|
||||||
|
| **Compaction** | Conversation likely to reach or exceed the context window limit. | Earlier context is summarized into a compaction block server-side. See `SKILL.md` §Compaction for the critical `response.content` handling. |
|
||||||
|
| **Memory** | State must persist across sessions (not just within one conversation). | Claude reads/writes files in a memory directory. Survives process restarts. |
|
||||||
|
|
||||||
|
**Choosing between them:** Context editing and compaction operate within a session — editing prunes stale turns, compaction summarizes when you're near the limit. Memory is for cross-session persistence. Many long-running agents use all three.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Caching for Agents
|
||||||
|
|
||||||
|
**Read `prompt-caching.md` first.** It covers the prefix-match invariant, breakpoint placement, the silent-invalidator audit, and why changing tools or models mid-session breaks the cache. This section covers only the agent-specific workarounds for those constraints.
|
||||||
|
|
||||||
|
| Constraint (from `prompt-caching.md`) | Agent-specific workaround |
|
||||||
|
| --- | --- |
|
||||||
|
| Editing the system prompt mid-session invalidates the cache. | Append a `<system-reminder>` block in the `messages` array instead. The cached prefix stays intact. Claude Code uses this for time updates and mode transitions. |
|
||||||
|
| Switching models mid-session invalidates the cache. | Spawn a **subagent** with the cheaper model for the sub-task; keep the main loop on one model. Claude Code's Explore subagents use Haiku this way. |
|
||||||
|
| Adding/removing tools mid-session invalidates the cache. | Use **tool search** for dynamic discovery — it appends tool schemas rather than swapping them, so the existing prefix is preserved. |
|
||||||
|
|
||||||
|
For multi-turn breakpoint placement, use top-level auto-caching — see `prompt-caching.md` §Placement patterns.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For live documentation on any of these features, see `live-sources.md`.
|
||||||
@@ -52,6 +52,12 @@ This file contains WebFetch URLs for fetching current information from platform.
|
|||||||
| -------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
| -------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||||
| Code Execution | `https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool.md` | "Extract code execution tool setup, file upload, container reuse, and response handling" |
|
| Code Execution | `https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool.md` | "Extract code execution tool setup, file upload, container reuse, and response handling" |
|
||||||
| Computer Use | `https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use.md` | "Extract computer use tool setup, capabilities, and implementation examples" |
|
| Computer Use | `https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use.md` | "Extract computer use tool setup, capabilities, and implementation examples" |
|
||||||
|
| Bash Tool | `https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool.md` | "Extract bash tool schema, reference implementation, and security considerations" |
|
||||||
|
| Text Editor | `https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool.md` | "Extract text editor tool commands, schema, and reference implementation" |
|
||||||
|
| Memory Tool | `https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool.md` | "Extract memory tool commands, directory structure, and implementation patterns" |
|
||||||
|
| Tool Search | `https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool.md` | "Extract tool search setup, when to use, and cache interaction" |
|
||||||
|
| Programmatic Tool Calling | `https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling.md` | "Extract PTC setup, script execution model, and tool invocation from code" |
|
||||||
|
| Skills | `https://platform.claude.com/docs/en/agents-and-tools/skills.md` | "Extract skill folder structure, SKILL.md format, and loading behavior" |
|
||||||
|
|
||||||
### Advanced Features
|
### Advanced Features
|
||||||
|
|
||||||
@@ -59,56 +65,60 @@ This file contains WebFetch URLs for fetching current information from platform.
|
|||||||
| ------------------ | ----------------------------------------------------------------------------- | --------------------------------------------------- |
|
| ------------------ | ----------------------------------------------------------------------------- | --------------------------------------------------- |
|
||||||
| Structured Outputs | `https://platform.claude.com/docs/en/build-with-claude/structured-outputs.md` | "Extract output_config.format usage and schema enforcement" |
|
| Structured Outputs | `https://platform.claude.com/docs/en/build-with-claude/structured-outputs.md` | "Extract output_config.format usage and schema enforcement" |
|
||||||
| Compaction | `https://platform.claude.com/docs/en/build-with-claude/compaction.md` | "Extract compaction setup, trigger config, and streaming with compaction" |
|
| Compaction | `https://platform.claude.com/docs/en/build-with-claude/compaction.md` | "Extract compaction setup, trigger config, and streaming with compaction" |
|
||||||
|
| Context Editing | `https://platform.claude.com/docs/en/build-with-claude/context-editing.md` | "Extract context editing thresholds, what gets cleared, and configuration" |
|
||||||
| Citations | `https://platform.claude.com/docs/en/build-with-claude/citations.md` | "Extract citation format and implementation" |
|
| Citations | `https://platform.claude.com/docs/en/build-with-claude/citations.md` | "Extract citation format and implementation" |
|
||||||
| Context Windows | `https://platform.claude.com/docs/en/build-with-claude/context-windows.md` | "Extract context window sizes and token management" |
|
| Context Windows | `https://platform.claude.com/docs/en/build-with-claude/context-windows.md` | "Extract context window sizes and token management" |
|
||||||
|
|
||||||
|
### Managed Agents
|
||||||
|
|
||||||
|
Use these when a managed-agents binding, behavior, or wire-level detail isn't covered in the cached `shared/managed-agents-*.md` concept files or in `{lang}/managed-agents/README.md`.
|
||||||
|
|
||||||
|
| Topic | URL | Extraction Prompt |
|
||||||
|
| --------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
||||||
|
| Overview | `https://platform.claude.com/docs/en/managed-agents/overview.md` | "Extract the high-level architecture and how agents/sessions/environments/vaults fit together" |
|
||||||
|
| Quickstart | `https://platform.claude.com/docs/en/managed-agents/quickstart.md` | "Extract the minimal end-to-end agent → environment → session → stream code path" |
|
||||||
|
| Agent Setup | `https://platform.claude.com/docs/en/managed-agents/agent-setup.md` | "Extract agent create/update/list-versions/archive lifecycle and parameters" |
|
||||||
|
| Define Outcomes | `https://platform.claude.com/docs/en/managed-agents/define-outcomes.md` | "Extract outcome definitions, evaluation hooks, and success criteria configuration" |
|
||||||
|
| Sessions | `https://platform.claude.com/docs/en/managed-agents/sessions.md` | "Extract session lifecycle, status transitions, idle/terminated semantics, and resume rules" |
|
||||||
|
| Environments | `https://platform.claude.com/docs/en/managed-agents/environments.md` | "Extract environment config (cloud/networking), management endpoints, and reuse model" |
|
||||||
|
| Events and Streaming | `https://platform.claude.com/docs/en/managed-agents/events-and-streaming.md` | "Extract event stream types, stream-first ordering, reconnect/dedupe, and steering patterns" |
|
||||||
|
| Tools | `https://platform.claude.com/docs/en/managed-agents/tools.md` | "Extract built-in toolset, custom tool definitions, and tool result wire format" |
|
||||||
|
| Files | `https://platform.claude.com/docs/en/managed-agents/files.md` | "Extract file upload, mount paths, session resources, and listing/downloading session outputs" |
|
||||||
|
| Permission Policies | `https://platform.claude.com/docs/en/managed-agents/permission-policies.md` | "Extract permission policy types (allow/deny/confirm) and per-tool config" |
|
||||||
|
| Multi-Agent | `https://platform.claude.com/docs/en/managed-agents/multi-agent.md` | "Extract multi-agent composition patterns, sub-agent invocation, and result handoff" |
|
||||||
|
| Observability | `https://platform.claude.com/docs/en/managed-agents/observability.md` | "Extract logging, tracing, and usage telemetry exposed by managed agents" |
|
||||||
|
| GitHub | `https://platform.claude.com/docs/en/managed-agents/github.md` | "Extract github_repository resource shape, multi-repo mounting, and token rotation" |
|
||||||
|
| MCP Connector | `https://platform.claude.com/docs/en/managed-agents/mcp-connector.md` | "Extract MCP server declaration on agents and vault-based credential injection at session" |
|
||||||
|
| Vaults | `https://platform.claude.com/docs/en/managed-agents/vaults.md` | "Extract vault create, credential add/rotate, OAuth refresh shape, and archive" |
|
||||||
|
| Skills | `https://platform.claude.com/docs/en/managed-agents/skills.md` | "Extract skill packaging and loading model for managed agents" |
|
||||||
|
| Memory | `https://platform.claude.com/docs/en/managed-agents/memory.md` | "Extract memory resource shape, scoping, and lifecycle" |
|
||||||
|
| Onboarding | `https://platform.claude.com/docs/en/managed-agents/onboarding.md` | "Extract first-run setup, prerequisites, and account/region requirements" |
|
||||||
|
| Cloud Containers | `https://platform.claude.com/docs/en/managed-agents/cloud-containers.md` | "Extract cloud container runtime, image config, and network/storage knobs" |
|
||||||
|
| Migration | `https://platform.claude.com/docs/en/managed-agents/migration.md` | "Extract migration paths from earlier APIs/preview shapes to GA managed agents" |
|
||||||
|
|
||||||
|
### Anthropic CLI
|
||||||
|
|
||||||
|
The `ant` CLI provides terminal access to the Claude API. Every API resource is exposed as a subcommand. It is one convenient way to create agents, environments, sessions, and other resources from version-controlled YAML, and to inspect responses interactively.
|
||||||
|
|
||||||
|
| Topic | URL | Extraction Prompt |
|
||||||
|
| ------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||||
|
| Anthropic CLI | `https://platform.claude.com/docs/en/api/sdks/cli.md` | "Extract CLI install, authentication, command structure, and the beta:agents/environments/sessions commands" |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Claude API SDK Repositories
|
## Claude API SDK Repositories
|
||||||
|
|
||||||
| SDK | URL | Description |
|
WebFetch these when a binding (class, method, namespace, field) isn't covered in the cached `{lang}/` skill files or in the managed-agents docs above. The SDKs include beta managed-agents support for `/v1/agents`, `/v1/sessions`, `/v1/environments`, and related resources — search the repo for `BetaManagedAgents`, `beta.agents`, `beta.sessions`, or the equivalent namespace for that language.
|
||||||
| ---------- | --------------------------------------------------------- | ------------------------------ |
|
|
||||||
| Python | `https://github.com/anthropics/anthropic-sdk-python` | `anthropic` pip package source |
|
|
||||||
| TypeScript | `https://github.com/anthropics/anthropic-sdk-typescript` | `@anthropic-ai/sdk` npm source |
|
|
||||||
| Java | `https://github.com/anthropics/anthropic-sdk-java` | `anthropic-java` Maven source |
|
|
||||||
| Go | `https://github.com/anthropics/anthropic-sdk-go` | Go module source |
|
|
||||||
| Ruby | `https://github.com/anthropics/anthropic-sdk-ruby` | `anthropic` gem source |
|
|
||||||
| C# | `https://github.com/anthropics/anthropic-sdk-csharp` | NuGet package source |
|
|
||||||
| PHP | `https://github.com/anthropics/anthropic-sdk-php` | Composer package source |
|
|
||||||
|
|
||||||
---
|
| SDK | URL | Extraction Prompt |
|
||||||
|
| ---------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||||
## Agent SDK Documentation URLs
|
| Python | `https://github.com/anthropics/anthropic-sdk-python` | "Extract beta managed-agents namespaces, classes, and method signatures (`client.beta.agents`, `client.beta.sessions`)" |
|
||||||
|
| TypeScript | `https://github.com/anthropics/anthropic-sdk-typescript` | "Extract beta managed-agents namespaces, classes, and method signatures (`client.beta.agents`, `client.beta.sessions`)" |
|
||||||
### Core Documentation
|
| Java | `https://github.com/anthropics/anthropic-sdk-java` | "Extract beta managed-agents classes, builders, and method signatures (`client.beta().agents()`, `BetaManagedAgents*`)" |
|
||||||
|
| Go | `https://github.com/anthropics/anthropic-sdk-go` | "Extract beta managed-agents types and method signatures (`client.Beta.Agents`, `BetaManagedAgents*` event types)" |
|
||||||
| Topic | URL | Extraction Prompt |
|
| Ruby | `https://github.com/anthropics/anthropic-sdk-ruby` | "Extract beta managed-agents methods and parameter shapes (`client.beta.agents`, `client.beta.sessions`)" |
|
||||||
| -------------------- | ----------------------------------------------------------- | --------------------------------------------------------------- |
|
| C# | `https://github.com/anthropics/anthropic-sdk-csharp` | "Extract beta managed-agents classes and method signatures (NuGet package, `BetaManagedAgents*` types)" |
|
||||||
| Agent SDK Overview | `https://platform.claude.com/docs/en/agent-sdk.md` | "Extract the Agent SDK overview, key features, and use cases" |
|
| PHP | `https://github.com/anthropics/anthropic-sdk-php` | "Extract beta managed-agents classes and method signatures (`$client->beta->agents`, `BetaManagedAgents*` params)" |
|
||||||
| Agent SDK Python | `https://github.com/anthropics/claude-agent-sdk-python` | "Extract Python SDK installation, imports, and basic usage" |
|
|
||||||
| Agent SDK TypeScript | `https://github.com/anthropics/claude-agent-sdk-typescript` | "Extract TypeScript SDK installation, imports, and basic usage" |
|
|
||||||
|
|
||||||
### SDK Reference (GitHub READMEs)
|
|
||||||
|
|
||||||
| Topic | URL | Extraction Prompt |
|
|
||||||
| -------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
||||||
| Python SDK | `https://raw.githubusercontent.com/anthropics/claude-agent-sdk-python/main/README.md` | "Extract Python SDK API reference, classes, and methods" |
|
|
||||||
| TypeScript SDK | `https://raw.githubusercontent.com/anthropics/claude-agent-sdk-typescript/main/README.md` | "Extract TypeScript SDK API reference, types, and functions" |
|
|
||||||
|
|
||||||
### npm/PyPI Packages
|
|
||||||
|
|
||||||
| Package | URL | Description |
|
|
||||||
| ----------------------------------- | -------------------------------------------------------------- | ------------------------- |
|
|
||||||
| claude-agent-sdk (Python) | `https://pypi.org/project/claude-agent-sdk/` | Python package on PyPI |
|
|
||||||
| @anthropic-ai/claude-agent-sdk (TS) | `https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk` | TypeScript package on npm |
|
|
||||||
|
|
||||||
### GitHub Repositories
|
|
||||||
|
|
||||||
| Resource | URL | Description |
|
|
||||||
| -------------- | ----------------------------------------------------------- | ----------------------------------- |
|
|
||||||
| Python SDK | `https://github.com/anthropics/claude-agent-sdk-python` | Python package source |
|
|
||||||
| TypeScript SDK | `https://github.com/anthropics/claude-agent-sdk-typescript` | TypeScript/Node.js package source |
|
|
||||||
| MCP Servers | `https://github.com/modelcontextprotocol` | Official MCP server implementations |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
299
skills/claude-api/shared/managed-agents-api-reference.md
Normal file
299
skills/claude-api/shared/managed-agents-api-reference.md
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
# Managed Agents — Endpoint Reference
|
||||||
|
|
||||||
|
All endpoints require `x-api-key` and `anthropic-version: 2023-06-01` headers. Managed Agents endpoints additionally require the `anthropic-beta` header.
|
||||||
|
|
||||||
|
## Beta Headers
|
||||||
|
|
||||||
|
```
|
||||||
|
anthropic-beta: managed-agents-2026-04-01
|
||||||
|
```
|
||||||
|
|
||||||
|
The SDK adds this header automatically for all `client.beta.{agents,environments,sessions,vaults}.*` calls. Skills endpoints use `skills-2025-10-02`; Files endpoints use `files-api-2025-04-14`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SDK Method Reference
|
||||||
|
|
||||||
|
All resources are under the `beta` namespace. Python and TypeScript share identical method names.
|
||||||
|
|
||||||
|
| Resource | Python / TypeScript (`client.beta.*`) | Go (`client.Beta.*`) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Agents | `agents.create` / `retrieve` / `update` / `list` / `archive` | `Agents.New` / `Get` / `Update` / `List` / `Archive` |
|
||||||
|
| Agent Versions | `agents.versions.list` | `Agents.Versions.List` |
|
||||||
|
| Environments | `environments.create` / `retrieve` / `update` / `list` / `delete` / `archive` | `Environments.New` / `Get` / `Update` / `List` / `Delete` / `Archive` |
|
||||||
|
| Sessions | `sessions.create` / `retrieve` / `update` / `list` / `delete` / `archive` | `Sessions.New` / `Get` / `Update` / `List` / `Delete` / `Archive` |
|
||||||
|
| Session Events | `sessions.events.list` / `send` / `stream` | `Sessions.Events.List` / `Send` / `StreamEvents` |
|
||||||
|
| Session Resources | `sessions.resources.add` / `retrieve` / `update` / `list` / `delete` | `Sessions.Resources.Add` / `Get` / `Update` / `List` / `Delete` |
|
||||||
|
| Vaults | `vaults.create` / `retrieve` / `update` / `list` / `delete` / `archive` | `Vaults.New` / `Get` / `Update` / `List` / `Delete` / `Archive` |
|
||||||
|
| Credentials | `vaults.credentials.create` / `retrieve` / `update` / `list` / `delete` / `archive` | `Vaults.Credentials.New` / `Get` / `Update` / `List` / `Delete` / `Archive` |
|
||||||
|
|
||||||
|
**Naming quirks to watch for:**
|
||||||
|
- Agents have **no delete** — only `archive`. Other resources have both.
|
||||||
|
- Session resources use `add` (not `create`).
|
||||||
|
- Go's event stream is `StreamEvents` (not `Stream`).
|
||||||
|
|
||||||
|
**Agent shorthand:** `agent` on session create accepts either a bare string (`agent="agent_abc123"` — uses latest version) or the full reference object (`{type: "agent", id: "agent_abc123", version: 123}`).
|
||||||
|
|
||||||
|
**Model shorthand:** `model` on agent create accepts either a bare string (`model="claude-opus-4-6"` — uses `standard` speed) or the full config object (`{type: "model_config", id: "claude-opus-4-6", speed: "fast"}`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Agents
|
||||||
|
|
||||||
|
**Step one of every flow.** Sessions require a pre-created agent — there is no inline agent config under `managed-agents-2026-04-01`.
|
||||||
|
|
||||||
|
| Method | Path | Operation | Description |
|
||||||
|
| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- |
|
||||||
|
| `GET` | `/v1/agents` | ListAgents | List agents |
|
||||||
|
| `POST` | `/v1/agents` | CreateAgent | Create a saved agent configuration |
|
||||||
|
| `GET` | `/v1/agents/{agent_id}` | GetAgent | Get agent details |
|
||||||
|
| `POST` | `/v1/agents/{agent_id}` | UpdateAgent | Update agent configuration |
|
||||||
|
| `POST` | `/v1/agents/{agent_id}/archive` | ArchiveAgent | Archive an agent (no hard delete for agents) |
|
||||||
|
| `GET` | `/v1/agents/{agent_id}/versions` | ListAgentVersions | List agent versions |
|
||||||
|
|
||||||
|
## Sessions
|
||||||
|
|
||||||
|
| Method | Path | Operation | Description |
|
||||||
|
| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- |
|
||||||
|
| `GET` | `/v1/sessions` | ListSessions | List sessions (paginated) |
|
||||||
|
| `POST` | `/v1/sessions` | CreateSession | Create a new session |
|
||||||
|
| `GET` | `/v1/sessions/{session_id}` | GetSession | Get session details |
|
||||||
|
| `POST` | `/v1/sessions/{session_id}` | UpdateSession | Update session metadata/title |
|
||||||
|
| `DELETE` | `/v1/sessions/{session_id}` | DeleteSession | Delete a session |
|
||||||
|
| `POST` | `/v1/sessions/{session_id}/archive` | ArchiveSession | Archive a session |
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
| Method | Path | Operation | Description |
|
||||||
|
| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- |
|
||||||
|
| `GET` | `/v1/sessions/{session_id}/events` | ListEvents | List events (polling, paginated) |
|
||||||
|
| `POST` | `/v1/sessions/{session_id}/events` | SendEvents | Send events (user message, tool result) |
|
||||||
|
| `GET` | `/v1/sessions/{session_id}/events/stream` | StreamEvents | Stream events via SSE |
|
||||||
|
|
||||||
|
## Session Resources
|
||||||
|
|
||||||
|
| Method | Path | Operation | Description |
|
||||||
|
| -------- | ------------------------------------------------------- | ---------------- | ---------------------------------------- |
|
||||||
|
| `GET` | `/v1/sessions/{session_id}/resources` | ListResources | List resources attached to session |
|
||||||
|
| `POST` | `/v1/sessions/{session_id}/resources` | AddResource | Attach file or github_repository mount (SDK method: `add`, not `create`) |
|
||||||
|
| `GET` | `/v1/sessions/{session_id}/resources/{resource_id}` | GetResource | Get a single resource |
|
||||||
|
| `POST` | `/v1/sessions/{session_id}/resources/{resource_id}` | UpdateResource | Update resource |
|
||||||
|
| `DELETE` | `/v1/sessions/{session_id}/resources/{resource_id}` | DeleteResource | Remove resource from session |
|
||||||
|
|
||||||
|
## Environments
|
||||||
|
|
||||||
|
| Method | Path | Operation | Description |
|
||||||
|
| -------- | ---------------------------------------------------------------- | -------------------- | ----------------------------------- |
|
||||||
|
| `POST` | `/v1/environments` | CreateEnvironment | Create environment |
|
||||||
|
| `GET` | `/v1/environments` | ListEnvironments | List environments |
|
||||||
|
| `GET` | `/v1/environments/{environment_id}` | GetEnvironment | Get environment details |
|
||||||
|
| `POST` | `/v1/environments/{environment_id}` | UpdateEnvironment | Update environment |
|
||||||
|
| `DELETE` | `/v1/environments/{environment_id}` | DeleteEnvironment | Delete environment. Returns 204. |
|
||||||
|
| `POST` | `/v1/environments/{environment_id}/archive` | ArchiveEnvironment | Archive environment (read-only; existing sessions continue) |
|
||||||
|
|
||||||
|
## Vaults
|
||||||
|
|
||||||
|
Vaults store MCP credentials that Anthropic manages on your behalf — OAuth credentials with auto-refresh, or static bearer tokens. Attach to sessions via `vault_ids`. See `managed-agents-tools.md` §Vaults for the conceptual guide and credential shapes.
|
||||||
|
|
||||||
|
| Method | Path | Operation | Description |
|
||||||
|
| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- |
|
||||||
|
| `POST` | `/v1/vaults` | CreateVault | Create a vault |
|
||||||
|
| `GET` | `/v1/vaults` | ListVaults | List vaults |
|
||||||
|
| `GET` | `/v1/vaults/{vault_id}` | GetVault | Get vault details |
|
||||||
|
| `POST` | `/v1/vaults/{vault_id}` | UpdateVault | Update vault |
|
||||||
|
| `DELETE` | `/v1/vaults/{vault_id}` | DeleteVault | Delete vault |
|
||||||
|
| `POST` | `/v1/vaults/{vault_id}/archive` | ArchiveVault | Archive vault |
|
||||||
|
|
||||||
|
## Credentials
|
||||||
|
|
||||||
|
Credentials are individual secrets stored inside a vault.
|
||||||
|
|
||||||
|
| Method | Path | Operation | Description |
|
||||||
|
| -------- | ----------------------------------------------------------------- | ------------------ | ---------------------------- |
|
||||||
|
| `POST` | `/v1/vaults/{vault_id}/credentials` | CreateCredential | Create a credential |
|
||||||
|
| `GET` | `/v1/vaults/{vault_id}/credentials` | ListCredentials | List credentials in vault |
|
||||||
|
| `GET` | `/v1/vaults/{vault_id}/credentials/{credential_id}` | GetCredential | Get credential metadata |
|
||||||
|
| `POST` | `/v1/vaults/{vault_id}/credentials/{credential_id}` | UpdateCredential | Update credential |
|
||||||
|
| `DELETE` | `/v1/vaults/{vault_id}/credentials/{credential_id}` | DeleteCredential | Delete credential |
|
||||||
|
| `POST` | `/v1/vaults/{vault_id}/credentials/{credential_id}/archive` | ArchiveCredential | Archive credential |
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| Method | Path | Operation | Description |
|
||||||
|
| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- |
|
||||||
|
| `POST` | `/v1/files` | UploadFile | Upload a file |
|
||||||
|
| `GET` | `/v1/files` | ListFiles | List files |
|
||||||
|
| `GET` | `/v1/files/{file_id}` | GetFile | Get file metadata (SDK method: `retrieve_metadata`) |
|
||||||
|
| `GET` | `/v1/files/{file_id}/content` | DownloadFile | Download file content |
|
||||||
|
| `DELETE` | `/v1/files/{file_id}` | DeleteFile | Delete a file |
|
||||||
|
|
||||||
|
## Skills
|
||||||
|
|
||||||
|
| Method | Path | Operation | Description |
|
||||||
|
| -------- | --------------------------------------------------------------- | ------------------ | ---------------------------- |
|
||||||
|
| `POST` | `/v1/skills` | CreateSkill | Create a skill |
|
||||||
|
| `GET` | `/v1/skills` | ListSkills | List skills |
|
||||||
|
| `GET` | `/v1/skills/{skill_id}` | GetSkill | Get skill details |
|
||||||
|
| `DELETE` | `/v1/skills/{skill_id}` | DeleteSkill | Delete a skill |
|
||||||
|
| `POST` | `/v1/skills/{skill_id}/versions` | CreateVersion | Create skill version |
|
||||||
|
| `GET` | `/v1/skills/{skill_id}/versions` | ListVersions | List skill versions |
|
||||||
|
| `GET` | `/v1/skills/{skill_id}/versions/{version}` | GetVersion | Get skill version |
|
||||||
|
| `DELETE` | `/v1/skills/{skill_id}/versions/{version}` | DeleteVersion | Delete skill version |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Request/Response Schema Quick Reference
|
||||||
|
|
||||||
|
### CreateAgent Request Body
|
||||||
|
|
||||||
|
**Always start here.** `model`, `system`, `tools`, `mcp_servers`, `skills` are top-level fields on this object — they do NOT go on the session.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "string (required, 1-256 chars)",
|
||||||
|
"model": "claude-opus-4-6 (required — bare string, or {id, speed} object)",
|
||||||
|
"description": "string (optional, up to 2048 chars)",
|
||||||
|
"system": "string (optional, up to 100,000 chars)",
|
||||||
|
"tools": [
|
||||||
|
{ "type": "agent_toolset_20260401" }
|
||||||
|
],
|
||||||
|
"skills": [
|
||||||
|
{ "type": "anthropic", "skill_id": "xlsx" },
|
||||||
|
{ "type": "custom", "skill_id": "skill_abc123", "version": "1" }
|
||||||
|
],
|
||||||
|
"mcp_servers": [
|
||||||
|
{
|
||||||
|
"type": "url",
|
||||||
|
"name": "github",
|
||||||
|
"url": "https://api.githubcopilot.com/mcp/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"key": "value (max 16 pairs, keys ≤64 chars, values ≤512 chars)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> Limits: `tools` max 50, `skills` max 64, `mcp_servers` max 20 (unique names).
|
||||||
|
|
||||||
|
### CreateSession Request Body
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agent": "agent_abc123 (required — string shorthand for latest version, or {type: \"agent\", id, version} object)",
|
||||||
|
"environment_id": "env_abc123 (required)",
|
||||||
|
"title": "string (optional)",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"type": "github_repository",
|
||||||
|
"url": "https://github.com/owner/repo (required)",
|
||||||
|
"authorization_token": "ghp_... (required)",
|
||||||
|
"mount_path": "/workspace/repo (optional — defaults to /workspace/<repo-name>)",
|
||||||
|
"checkout": { "type": "branch", "name": "main" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vault_ids": ["vlt_abc123 (optional — MCP credentials with auto-refresh)"],
|
||||||
|
"metadata": {
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> The `agent` field accepts only a string ID or `{type: "agent", id, version}` — `model`/`system`/`tools` live on the agent, not here.
|
||||||
|
>
|
||||||
|
> **`checkout`** accepts `{type: "branch", name: "..."}` or `{type: "commit", sha: "..."}`. Omit for the repo's default branch.
|
||||||
|
|
||||||
|
### CreateEnvironment Request Body
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "string (required)",
|
||||||
|
"description": "string (optional)",
|
||||||
|
"config": {
|
||||||
|
"type": "cloud",
|
||||||
|
"networking": {
|
||||||
|
"type": "unrestricted | limited (union — see SDK types)"
|
||||||
|
},
|
||||||
|
"packages": { }
|
||||||
|
},
|
||||||
|
"metadata": { "key": "value" }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### SendEvents Request Body
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"type": "user.message",
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": "Hello"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tool Result Event
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "user.custom_tool_result",
|
||||||
|
"custom_tool_use_id": "sevt_abc123",
|
||||||
|
"content": [{ "type": "text", "text": "Result data" }],
|
||||||
|
"is_error": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
Managed Agents endpoints use the standard Anthropic API error format. Errors are returned with an HTTP status code and a JSON body containing `type`, `error`, and `request_id`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"error": {
|
||||||
|
"type": "invalid_request_error",
|
||||||
|
"message": "Description of what went wrong"
|
||||||
|
},
|
||||||
|
"request_id": "req_011CRv1W3XQ8XpFikNYG7RnE"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Include the `request_id` when reporting issues to Anthropic — it lets us trace the request end-to-end. The inner `error.type` is one of the following:
|
||||||
|
|
||||||
|
| Status | Error type | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| 400 | `invalid_request_error` | The request was malformed or missing required parameters |
|
||||||
|
| 401 | `authentication_error` | Invalid or missing API key |
|
||||||
|
| 403 | `permission_error` | The API key doesn't have permission for this operation |
|
||||||
|
| 404 | `not_found_error` | The requested resource doesn't exist |
|
||||||
|
| 409 | `invalid_request_error` | The request conflicts with the resource's current state (e.g., sending to an archived session) |
|
||||||
|
| 413 | `request_too_large` | The request body exceeds the maximum allowed size |
|
||||||
|
| 429 | `rate_limit_error` | Too many requests — check rate limit headers for retry timing |
|
||||||
|
| 500 | `api_error` | An internal server error occurred |
|
||||||
|
| 529 | `overloaded_error` | The service is temporarily overloaded — retry with backoff |
|
||||||
|
|
||||||
|
Note that `409 Conflict` carries `error.type: "invalid_request_error"` (there is no separate `conflict_error` type); inspect both the HTTP status and the `message` to distinguish conflicts from other invalid requests.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rate Limits
|
||||||
|
|
||||||
|
Managed Agents endpoints have per-organization request-per-minute (RPM) limits, separate from your [Messages API token limits](https://platform.claude.com/docs/en/api/rate-limits). Model inference inside a session still draws from your organization's standard ITPM/OTPM limits.
|
||||||
|
|
||||||
|
| Endpoint group | Scope | RPM | Max concurrent |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Create operations (Agents, Sessions, Vaults) | organization | 60 | — |
|
||||||
|
| All other operations (Agents, Sessions, Vaults) | organization | 600 | — |
|
||||||
|
| All operations (Environments) | organization | 60 | 5 |
|
||||||
|
|
||||||
|
Files and Skills endpoints use the standard tier-based [rate limits](https://platform.claude.com/docs/en/api/rate-limits).
|
||||||
|
|
||||||
|
When a limit is exceeded the API returns `429` with a `rate_limit_error` (see [Error Handling](#error-handling) for the response envelope) and a `retry-after` header indicating how many seconds to wait before retrying. The Anthropic SDK reads this header and retries automatically.
|
||||||
205
skills/claude-api/shared/managed-agents-client-patterns.md
Normal file
205
skills/claude-api/shared/managed-agents-client-patterns.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# Managed Agents — Common Client Patterns
|
||||||
|
|
||||||
|
Patterns you'll write on the client side when driving a Managed Agent session, grounded in working SDK examples.
|
||||||
|
|
||||||
|
Code samples are TypeScript — Python and cURL follow the same shape; see `python/managed-agents/README.md` and `curl/managed-agents.md` for equivalents.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Lossless stream reconnect
|
||||||
|
|
||||||
|
**Problem:** SSE has no replay. If the connection drops mid-session, a naive reconnect re-opens the stream from "now" and you silently miss every event emitted in between.
|
||||||
|
|
||||||
|
**Solution:** on reconnect, fetch the full event history via `events.list()` *before* consuming the live stream, and dedupe on event ID as the live stream catches up.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const seenEventIds = new Set<string>()
|
||||||
|
const stream = await client.beta.sessions.events.stream(session.id)
|
||||||
|
|
||||||
|
// Stream is now open and buffering server-side. Read history first.
|
||||||
|
for await (const event of client.beta.sessions.events.list(session.id)) {
|
||||||
|
seenEventIds.add(event.id)
|
||||||
|
handle(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail the live stream. Dedupe only gates handle() — terminal checks must run
|
||||||
|
// even for already-seen events, or a terminal event that was in the history
|
||||||
|
// response gets skipped by `continue` and the loop never exits.
|
||||||
|
for await (const event of stream) {
|
||||||
|
if (!seenEventIds.has(event.id)) {
|
||||||
|
seenEventIds.add(event.id)
|
||||||
|
handle(event)
|
||||||
|
}
|
||||||
|
if (event.type === 'session.status_terminated') break
|
||||||
|
if (event.type === 'session.status_idle' && event.stop_reason.type !== 'requires_action') break
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. `processed_at` — queued vs processed
|
||||||
|
|
||||||
|
Every event on the stream carries `processed_at` (ISO 8601). For client-sent events (`user.message`, `user.interrupt`, `user.tool_confirmation`, `user.custom_tool_result`) it's `null` when the event has been queued but not yet picked up by the agent, and populated once the agent processes it. The same event appears on the stream twice — once with `processed_at: null`, once with a timestamp.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
for await (const event of stream) {
|
||||||
|
if (event.type === 'user.message') {
|
||||||
|
if (event.processed_at == null) onQueued(event.id)
|
||||||
|
else onProcessed(event.id, event.processed_at)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use this to drive pending → acknowledged UI state for anything you send. How you map a locally-rendered optimistic message to the server-assigned `event.id` is application-specific (typically via the return value of `events.send()` or FIFO ordering).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Interrupt a running session
|
||||||
|
|
||||||
|
Send `user.interrupt` as a normal event. The session keeps running until it reaches a safe boundary, then goes idle.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await client.beta.sessions.events.send(session.id, {
|
||||||
|
events: [{ type: 'user.interrupt' }],
|
||||||
|
})
|
||||||
|
|
||||||
|
// Drain until the session is truly done — see Pattern 5 for the full gate.
|
||||||
|
for await (const event of stream) {
|
||||||
|
if (event.type === 'session.status_terminated') break
|
||||||
|
if (
|
||||||
|
event.type === 'session.status_idle' &&
|
||||||
|
event.stop_reason.type !== 'requires_action'
|
||||||
|
) break
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reference: `interrupt.ts` — sends the interrupt the moment it sees `span.model_request_start`, drains to idle, then verifies via `sessions.retrieve()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. `tool_confirmation` round-trip
|
||||||
|
|
||||||
|
When the agent has `permission_policy: { type: 'always_ask' }`, any call to that tool fires an `agent.tool_use` event with `evaluated_permission === 'ask'` and the session goes idle waiting for a decision. Respond with `user.tool_confirmation`.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
for await (const event of stream) {
|
||||||
|
if (event.type === 'agent.tool_use' && event.evaluated_permission === 'ask') {
|
||||||
|
await client.beta.sessions.events.send(session.id, {
|
||||||
|
events: [{
|
||||||
|
type: 'user.tool_confirmation',
|
||||||
|
tool_use_id: event.id, // not a toolu_ id — use event.id
|
||||||
|
result: 'allow', // or 'deny'
|
||||||
|
// deny_message: '...', // optional, only with result: 'deny'
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Key points:
|
||||||
|
- `tool_use_id` is `event.id` (typically `sevt_...`), **not** a `toolu_...` ID.
|
||||||
|
- `result` is `'allow' | 'deny'`. Use `deny_message` to tell the model *why* you denied — it gets surfaced back to the agent.
|
||||||
|
- Multiple pending tools: respond once per `agent.tool_use` event with `evaluated_permission === 'ask'`.
|
||||||
|
|
||||||
|
Reference: `tool-permissions.ts`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Correct idle-break gate
|
||||||
|
|
||||||
|
Do not break on `session.status_idle` alone. The session goes idle transiently — e.g. between parallel tool executions, while waiting for a `user.tool_confirmation`, or while awaiting a `user.custom_tool_result`. Break when idle with a terminal `stop_reason`, or on `session.status_terminated`.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
for await (const event of stream) {
|
||||||
|
handle(event)
|
||||||
|
if (event.type === 'session.status_terminated') break
|
||||||
|
if (event.type === 'session.status_idle') {
|
||||||
|
if (event.stop_reason.type === 'requires_action') continue // waiting on you — handle it
|
||||||
|
break // end_turn or retries_exhausted — both terminal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`stop_reason.type` values on `session.status_idle`:
|
||||||
|
- `requires_action` — agent is waiting on a client-side event (tool confirmation, custom tool result). Handle it, don't break.
|
||||||
|
- `retries_exhausted` — terminal failure. Break, then check `sessions.retrieve()` for the error state.
|
||||||
|
- `end_turn` — normal completion.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Post-idle status-write race
|
||||||
|
|
||||||
|
The SSE stream emits `session.status_idle` slightly before the session's queryable status reflects it. Clients that break on idle and immediately call `sessions.delete()` or `sessions.archive()` will intermittently 400 with "cannot delete/archive while running."
|
||||||
|
|
||||||
|
Poll before cleanup:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
let s
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
s = await client.beta.sessions.retrieve(session.id)
|
||||||
|
if (s.status !== 'running') break
|
||||||
|
await new Promise(r => setTimeout(r, 200))
|
||||||
|
}
|
||||||
|
if (s?.status !== 'running') {
|
||||||
|
await client.beta.sessions.archive(session.id)
|
||||||
|
} // else: still running after 2s — don't archive, let it settle or escalate
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Stream-first, then send
|
||||||
|
|
||||||
|
Always open the stream **before** sending the kickoff event. Otherwise the agent may process the event and emit the first events before your consumer is attached, and you'll miss them.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const stream = await client.beta.sessions.events.stream(session.id)
|
||||||
|
await client.beta.sessions.events.send(session.id, {
|
||||||
|
events: [{ type: 'user.message', content: [{ type: 'text', text: 'Hello' }] }],
|
||||||
|
})
|
||||||
|
for await (const event of stream) { /* ... */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Promise.all([stream, send])` shape works too, but stream-first is simpler and has the same effect — the stream starts buffering the moment it's opened.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. File-mount gotchas
|
||||||
|
|
||||||
|
**The mounted resource has a different `file_id` than the file you uploaded.** Session creation makes a session-scoped copy.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const uploaded = await client.beta.files.upload({ file, purpose: 'agent_resource' })
|
||||||
|
// uploaded.id → the original file
|
||||||
|
const session = await client.beta.sessions.create({
|
||||||
|
/* ... */
|
||||||
|
resources: [{ type: 'file', file_id: uploaded.id, mount_path: '/workspace/data.csv' }],
|
||||||
|
})
|
||||||
|
// session.resources[0].file_id !== uploaded.id ← different IDs
|
||||||
|
```
|
||||||
|
|
||||||
|
Delete the original via `files.delete(uploaded.id)`; the session-scoped copy is garbage-collected with the session. `mount_path` must be absolute — see `shared/managed-agents-environments.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Keep credentials host-side via custom tools
|
||||||
|
|
||||||
|
**Problem:** putting a third-party API key in the agent's vault or environment means the sandbox holds the secret. For keys tied to a human (Linear personal keys, `gh` CLI auth) or keys you'd rather not ship into a container, that's undesirable.
|
||||||
|
|
||||||
|
**Solution:** expose the operation as a custom tool. The agent emits `agent.custom_tool_use`; your orchestrator executes the call with its own credentials and responds with `user.custom_tool_result`. The container never sees the key.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Agent template: declare the tool, no credentials
|
||||||
|
tools: [{ type: 'custom', name: 'linear_graphql', input_schema: { /* query, vars */ } }]
|
||||||
|
|
||||||
|
// Orchestrator: handle the call with host-side creds
|
||||||
|
for await (const event of stream) {
|
||||||
|
if (event.type === 'agent.custom_tool_use' && event.name === 'linear_graphql') {
|
||||||
|
const result = await linear.request(event.input.query, event.input.vars) // host's key
|
||||||
|
await client.beta.sessions.events.send(session.id, {
|
||||||
|
events: [{ type: 'user.custom_tool_result', tool_use_id: event.id, result }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Same shape works for `gh` CLI, local eval scripts, or anything else that needs host-only auth or binaries.
|
||||||
216
skills/claude-api/shared/managed-agents-core.md
Normal file
216
skills/claude-api/shared/managed-agents-core.md
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
# Managed Agents — Core Concepts
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Managed Agents is built around four core concepts:
|
||||||
|
|
||||||
|
| Concept | Endpoint | What it is |
|
||||||
|
|---|---|---|
|
||||||
|
| **Agent** | `/v1/agents` | A persisted, versioned object defining the agent's capabilities and persona: model, system prompt, tools, MCP servers, skills. **Must be created before starting a session.** See the Agents section below. |
|
||||||
|
| **Session** | `/v1/sessions` | A stateful interaction with an agent. References a pre-created agent by ID + an environment + initial instructions. Produces an event stream. |
|
||||||
|
| **Environment** | `/v1/environments` | A template defining the configuration for container provisioning. |
|
||||||
|
| **Container** | N/A | An isolated compute instance where the agent's **tools** execute (bash, file ops, code). The agent loop does not run here — it runs on Anthropic's orchestration layer and acts on the container via tool calls. |
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Anthropic orchestration layer │
|
||||||
|
Agent (config) ───────▶│ (agent loop: Claude + tool calls) │
|
||||||
|
└──────────────┬──────────────────────┘
|
||||||
|
│ tool calls
|
||||||
|
▼
|
||||||
|
Environment (template) ──▶ Container (tool execution workspace)
|
||||||
|
│
|
||||||
|
Session ─┤
|
||||||
|
├── Resources (files, repos — mounted at startup)
|
||||||
|
├── Vault IDs (MCP credential references)
|
||||||
|
└── Conversation (event stream in/out)
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Agent creation is a prerequisite.** Sessions reference a pre-created agent by ID — `model`/`system`/`tools` live on the agent object, never on the session. Every flow starts with `POST /v1/agents`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Lifecycle
|
||||||
|
|
||||||
|
```
|
||||||
|
rescheduling → running ↔ idle → terminated
|
||||||
|
```
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
| -------------- | ------------------------------------------------------------------ |
|
||||||
|
| `idle` | Agent has finished the current task, and is awaiting input. It's either waiting for input to continue working via a `user.message` or blocked awaiting a `user.custom_tool_result` or `user.tool_confirmation`. The `stop_reason` attached contains more information about why the Agent has stopped working. |
|
||||||
|
| `running` | Session has starting running, and the Agent is actively doing work. |
|
||||||
|
| `rescheduling` | Session is (re)scheduling after a retryable error has occurred, ready to be picked up by the orchestration system. |
|
||||||
|
| `terminated` | Session has terminated, entering an irreversible and unusable state. |
|
||||||
|
|
||||||
|
- Events can be sent when the session is `running` or `idle`. Messages are queued and processed in order.
|
||||||
|
- The agent transitions `idle → running` when it receives a new event, then back to `idle` when done.
|
||||||
|
- Errors surface as `session.error` events in the stream, not as a status value.
|
||||||
|
|
||||||
|
### Built-in session features
|
||||||
|
|
||||||
|
- **Context compaction** — if you approach max context, the API automatically condenses session history to keep the interaction going
|
||||||
|
- **Prompt caching** — historical repeated tokens are cached, reducing processing time and cost
|
||||||
|
- **Extended thinking** — on by default, returned as `agent.thinking` events
|
||||||
|
|
||||||
|
### Session operations
|
||||||
|
|
||||||
|
| Operation | Notes |
|
||||||
|
|---|---|
|
||||||
|
| List / fetch | Paginated list or single resource by ID |
|
||||||
|
| Update | Only `title` is updatable |
|
||||||
|
| Archive | Session becomes **read-only**. Not reversible. |
|
||||||
|
| Delete | Permanently deletes session, event history, container, and checkpoints. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sessions
|
||||||
|
|
||||||
|
A session is a running agent instance inside an environment.
|
||||||
|
|
||||||
|
### Session Object
|
||||||
|
|
||||||
|
Key fields returned by the API:
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
| --------------- | -------- | --------------------------------------------------- |
|
||||||
|
| `type` | string | Always `"session"` |
|
||||||
|
| `id` | string | Unique session ID |
|
||||||
|
| `title` | string | Human-readable title |
|
||||||
|
| `status` | string | `idle`, `running`, `rescheduling`, `terminated` |
|
||||||
|
| `created_at` | string | ISO 8601 timestamp |
|
||||||
|
| `updated_at` | string | ISO 8601 timestamp |
|
||||||
|
| `archived_at` | string | ISO 8601 timestamp (nullable) |
|
||||||
|
| `environment_id` | string | Environment ID |
|
||||||
|
| `agent` | object | Agent configuration |
|
||||||
|
| `resources` | array | Attached files and repos |
|
||||||
|
| `metadata` | object | User-provided key-value pairs (max 8 keys) |
|
||||||
|
| `usage` | object | Token usage statistics |
|
||||||
|
|
||||||
|
### Creating a session
|
||||||
|
|
||||||
|
**A session is meaningless without an agent.** Sessions reference a pre-created agent by ID. Create the agent first via `agents.create()`, then reference it:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// 1. Create the agent (reusable, versioned)
|
||||||
|
const agent = await client.beta.agents.create(
|
||||||
|
{
|
||||||
|
name: "Coding Assistant",
|
||||||
|
model: "claude-opus-4-6",
|
||||||
|
system: "You are a helpful coding agent.",
|
||||||
|
tools: [{ type: "agent_toolset_20260401"}],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Start a session that references it
|
||||||
|
const session = await client.beta.sessions.create(
|
||||||
|
{
|
||||||
|
agent: agent.id, // string shorthand → latest version. Or: { type: "agent", id: agent.id, version: agent.version }
|
||||||
|
environment_id: environmentId,
|
||||||
|
title: "Hello World Session",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Session creation parameters:**
|
||||||
|
|
||||||
|
| Field | Type | Required | Description |
|
||||||
|
| --------------- | -------- | -------- | ---------------------------------------------- |
|
||||||
|
| `agent` | string or object | **Yes** | String shorthand `"agent_abc123"` (latest version) or `{type: "agent", id, version}` |
|
||||||
|
| `environment_id`| string | **Yes** | Environment ID |
|
||||||
|
| `title` | string | No | Human-readable name (appears in logs/dashboards) |
|
||||||
|
| `resources` | array | No | Files or GitHub repos, mounted to the container at startup |
|
||||||
|
| `vault_ids` | array | No | Vault IDs (`vlt_*`) — MCP credentials with auto-refresh. See `shared/managed-agents-tools.md` → Vaults. |
|
||||||
|
| `metadata` | object | No | User-provided key-value pairs |
|
||||||
|
|
||||||
|
**Agent configuration fields** (passed to `agents.create()`, not `sessions.create()`):
|
||||||
|
|
||||||
|
| Field | Type | Required | Description |
|
||||||
|
| ------------- | -------- | -------- | ---------------------------------------------- |
|
||||||
|
| `name` | string | **Yes** | Human-readable name (1-256 chars) |
|
||||||
|
| `model` | string or object | **Yes** | Claude model ID (bare string, or `{id, speed}` object). All Claude 4.5+ models supported. |
|
||||||
|
| `system` | string | No | System prompt — defines the agent's behavior (up to 100K chars) |
|
||||||
|
| `tools` | array | No | Encompasses three kinds: (1) pre-built Claude Agent tools (`agent_toolset_20260401`), (2) MCP tools (`mcp_toolset`), and (3) custom client-side tools. Max 128. |
|
||||||
|
| `mcp_servers` | array | No | MCP server connections — standardized third-party capabilities (e.g. GitHub, Asana). Max 20, unique names. See `shared/managed-agents-tools.md` → MCP Servers. |
|
||||||
|
| `skills` | array | No | Customized "best-practices" context with progressive disclosure. Max 64. See `shared/managed-agents-tools.md` → Skills. |
|
||||||
|
| `description` | string | No | Description of the agent (up to 2048 chars) |
|
||||||
|
| `metadata` | object | No | Arbitrary key-value pairs (max 16, keys ≤64 chars, values ≤512 chars) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Agents
|
||||||
|
|
||||||
|
**This is where every Managed Agents flow begins.** The agent object is a persisted, versioned configuration — you create it once, then reference it by ID every time you start a session. No agent → no session.
|
||||||
|
|
||||||
|
### Agent Object
|
||||||
|
|
||||||
|
The API is **flat** — `model`, `system`, `tools` etc. are top-level fields, not wrapped in an `agent:{}` sub-object.
|
||||||
|
|
||||||
|
| Field | Type | Required | Description |
|
||||||
|
| ------------------ | -------- | -------- | -------------------------------------------------- |
|
||||||
|
| `name` | string | Yes | Human-readable name |
|
||||||
|
| `model` | string | Yes | Claude model ID |
|
||||||
|
| `system` | string | No | System prompt |
|
||||||
|
| `tools` | array | No | Agent toolset / MCP toolset / custom tools |
|
||||||
|
| `mcp_servers` | array | No | MCP server connections |
|
||||||
|
| `skills` | array | No | Skill references (max 64) |
|
||||||
|
| `description` | string | No | Description of the agent |
|
||||||
|
| `metadata` | object | No | Arbitrary key-value pairs |
|
||||||
|
|
||||||
|
### Lifecycle: create once, run many, update in place
|
||||||
|
|
||||||
|
The agent is a **persistent resource**, not a per-run parameter. The intended pattern:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─ setup (once) ─────────┐ ┌─ runtime (every invocation) ─┐
|
||||||
|
│ agents.create() │ │ sessions.create( │
|
||||||
|
│ → store agent_id │ ──→ │ agent={type:..., id: ID} │
|
||||||
|
│ in config/env/db │ │ ) │
|
||||||
|
└────────────────────────┘ └──────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Anti-pattern:** calling `agents.create()` at the top of every script run. This accumulates orphaned agent objects, pays create latency on every invocation, and defeats the versioning model. If you see `agents.create()` in a function that's called per-request or per-cron-tick, that's wrong — hoist it to one-time setup and persist the ID.
|
||||||
|
|
||||||
|
### Versioning
|
||||||
|
|
||||||
|
Each `POST /v1/agents/{id}` (update) creates a new immutable version (numeric timestamp, e.g. `1772585501101368014`). The agent's history is append-only — you can't edit a past version.
|
||||||
|
|
||||||
|
**Why version:**
|
||||||
|
- **Reproducibility** — pin a session to a known-good config: `{type: "agent", id, version: 3}`
|
||||||
|
- **Safe iteration** — update the agent without breaking sessions already running on the old version
|
||||||
|
- **Rollback** — if a new system prompt regresses, pin new sessions back to the prior version while you debug
|
||||||
|
|
||||||
|
**`version` is optional.** Omit it (or use the string shorthand `agent="agent_abc123"`) to get the latest version at session-creation time. Pass it explicitly (`{type: "agent", id, version: N}`) to pin for reproducibility.
|
||||||
|
|
||||||
|
**Getting the version to pin:** `agents.create()` and `agents.update()` both return `version` in the response. Store it alongside `agent_id`. To fetch the current latest for an existing agent: `GET /v1/agents/{id}` → `.version`.
|
||||||
|
|
||||||
|
**When to update vs create new:** Update (`POST /v1/agents/{id}`) when it's conceptually the same agent with tweaked behavior (better prompt, extra tool). Create a new agent when it's a different persona/purpose. Rule of thumb: if you'd give it the same `name`, update.
|
||||||
|
|
||||||
|
### Agent Endpoints
|
||||||
|
|
||||||
|
| Operation | Method | Path |
|
||||||
|
| ---------------- | -------- | ------------------------------------- |
|
||||||
|
| Create | `POST` | `/v1/agents` |
|
||||||
|
| List | `GET` | `/v1/agents` |
|
||||||
|
| Get | `GET` | `/v1/agents/{id}` |
|
||||||
|
| Update | `POST` | `/v1/agents/{id}` |
|
||||||
|
| Archive | `POST` | `/v1/agents/{id}/archive` |
|
||||||
|
|
||||||
|
### Using an Agent in a Session
|
||||||
|
|
||||||
|
Reference the agent by string ID (latest version) or by object with an explicit version:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# String shorthand — uses the agent's latest version
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent=agent.id,
|
||||||
|
environment_id=environment_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Or pin to a specific version (int)
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent={"type": "agent", "id": agent.id, "version": agent.version},
|
||||||
|
environment_id=environment_id,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
202
skills/claude-api/shared/managed-agents-environments.md
Normal file
202
skills/claude-api/shared/managed-agents-environments.md
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# Managed Agents — Environments & Resources
|
||||||
|
|
||||||
|
## Environments
|
||||||
|
|
||||||
|
Creating a session requires an `environment_id`. Environments are **reusable configuration templates** for spinning up containers in Anthropic's infrastructure — you might create different environments for different use cases (e.g. data visualization vs web development, with different package sets). Anthropic handles scaling, container lifecycle, and work orchestration.
|
||||||
|
|
||||||
|
**Environment names must be unique.** Creating an environment with an existing name returns 409.
|
||||||
|
|
||||||
|
### Networking
|
||||||
|
|
||||||
|
| Network Policy | Description |
|
||||||
|
| ------------------------------- | ------------------------------------------------------------- |
|
||||||
|
| `unrestricted` | Full egress (except legal blocklist) |
|
||||||
|
| `package_managers_and_custom` | Package managers + custom `allowed_hosts` |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"networking": {
|
||||||
|
"type": "package_managers_and_custom",
|
||||||
|
"allowed_hosts": ["api.example.com"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**MCP caveat:** If using restricted networking, make sure `allowed_hosts` includes your MCP server domains. Otherwise the container can't reach them and tools silently fail.
|
||||||
|
|
||||||
|
### Creating an environment
|
||||||
|
|
||||||
|
The SDK adds `managed-agents-2026-04-01` automatically. TypeScript:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const env = await client.beta.environments.create({
|
||||||
|
name: "my_env",
|
||||||
|
config: {
|
||||||
|
type: "cloud",
|
||||||
|
networking: { type: "unrestricted" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment CRUD
|
||||||
|
|
||||||
|
| Operation | Method | Path | Notes |
|
||||||
|
| ---------------- | -------- | ------------------------------------------ | ----- |
|
||||||
|
| Create | `POST` | `/v1/environments` | |
|
||||||
|
| List | `GET` | `/v1/environments` | Paginated (`limit`, `after_id`, `before_id`) |
|
||||||
|
| Get | `GET` | `/v1/environments/{id}` | |
|
||||||
|
| Update | `POST` | `/v1/environments/{id}` | Changes apply only to **new** containers; existing sessions keep their original config |
|
||||||
|
| Delete | `DELETE` | `/v1/environments/{id}` | Returns 204. |
|
||||||
|
| Archive | `POST` | `/v1/environments/{id}/archive` | Read-only. New sessions can't be created; existing ones continue. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
Attach files and GitHub repositories to a session. **Session creation blocks until all resources are mounted** — the container won't go `running` until every file and repo is in place. Max **999 file resources** per session. Multiple GitHub repositories per session are supported.
|
||||||
|
|
||||||
|
### File Uploads (input — host → agent)
|
||||||
|
|
||||||
|
Upload a file first via the Files API, then reference by `file_id` + `mount_path`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// 1. Upload
|
||||||
|
const file = await client.beta.files.upload({
|
||||||
|
file: fs.createReadStream("data.csv"),
|
||||||
|
purpose: "agent",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Attach as a session resource
|
||||||
|
const session = await client.beta.sessions.create({
|
||||||
|
agent: agent.id,
|
||||||
|
environment_id: envId,
|
||||||
|
resources: [
|
||||||
|
{ type: "file", file_id: file.id, mount_path: "/workspace/data.csv" }
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**`mount_path` is required** and must be absolute. Parent directories are created automatically. Agent working directory defaults to `/workspace`. Files are mounted read-only — the agent writes modified versions to new paths.
|
||||||
|
|
||||||
|
### Session outputs (output — agent → host)
|
||||||
|
|
||||||
|
The agent can write files to `/mnt/session/outputs/` during a session. These are automatically captured by the Files API and can be listed and downloaded afterwards:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// After the turn completes, list output files scoped to this session:
|
||||||
|
for await (const f of client.beta.files.list({ scope: session.id })) {
|
||||||
|
console.log(f.filename, f.size_bytes);
|
||||||
|
const resp = await client.beta.files.download(f.id);
|
||||||
|
const text = await resp.text();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- The `write` tool (or `bash`) must be enabled for the agent to create output files.
|
||||||
|
- Session-scoped `files.list` / `files.download` captures outputs written to `/mnt/session/outputs/`.
|
||||||
|
- `session_id` is a query filter on `files.list` (not yet in SDK types — cast or spread through).
|
||||||
|
- There's a brief indexing lag (~1–3s) between `session.status_idle` and output files appearing in `files.list`. Retry once or twice if empty.
|
||||||
|
|
||||||
|
This gives you a bidirectional file bridge: upload reference data in, download agent artifacts out.
|
||||||
|
|
||||||
|
### GitHub Repositories
|
||||||
|
|
||||||
|
Clones a GitHub repository into the session container during initialization, before the agent begins execution. The agent can read, edit, commit, and push via `bash` (`git`). Multiple repositories per session are supported — add one `resources` entry per repo.
|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
|
||||||
|
| Field | Required | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `type` | ✅ | `"github_repository"` |
|
||||||
|
| `url` | ✅ | The GitHub repository URL |
|
||||||
|
| `authorization_token` | ✅ | GitHub Personal Access Token with repository access. **Never echoed in API responses.** |
|
||||||
|
| `mount_path` | ❌ | Path where the repository will be cloned. Defaults to `/workspace/<repo-name>`. |
|
||||||
|
| `checkout` | ❌ | `{type: "branch", name: "..."}` or `{type: "commit", sha: "..."}`. Defaults to the repo's default branch. |
|
||||||
|
|
||||||
|
**Token permission levels** (fine-grained PATs):
|
||||||
|
- `Contents: Read` — clone only
|
||||||
|
- `Contents: Read and write` — push changes and create pull requests
|
||||||
|
|
||||||
|
> ‼️ **To generate pull requests** you also need GitHub **MCP server** access — the `github_repository` resource gives filesystem access only. See `shared/managed-agents-tools.md` → MCP Servers. The PR workflow is: edit files in the mounted repo → push branch via `bash` → create PR via MCP `create_pull_request` tool.
|
||||||
|
|
||||||
|
**TypeScript:**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// 1. Create the agent — declare GitHub MCP (no auth here)
|
||||||
|
const agent = await client.beta.agents.create(
|
||||||
|
{
|
||||||
|
name: 'GitHub Agent',
|
||||||
|
model: 'claude-opus-4-6',
|
||||||
|
mcp_servers: [
|
||||||
|
{ type: 'url', name: 'github', url: 'https://api.githubcopilot.com/mcp/' },
|
||||||
|
],
|
||||||
|
tools: [
|
||||||
|
{ type: 'agent_toolset_20260401', default_config: { enabled: true } },
|
||||||
|
{ type: 'mcp_toolset', mcp_server_name: 'github' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Start a session — attach vault for MCP auth + mount the repo
|
||||||
|
const session = await client.beta.sessions.create({
|
||||||
|
agent: agent.id,
|
||||||
|
environment_id: envId,
|
||||||
|
vault_ids: [vaultId], // vault contains the GitHub MCP OAuth credential
|
||||||
|
resources: [
|
||||||
|
{
|
||||||
|
type: 'github_repository',
|
||||||
|
url: 'https://github.com/owner/repo',
|
||||||
|
authorization_token: process.env.GITHUB_TOKEN, // repo clone token (≠ MCP auth)
|
||||||
|
checkout: { type: 'branch', name: 'main' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Python:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
|
||||||
|
agent = client.beta.agents.create(
|
||||||
|
name="GitHub Agent",
|
||||||
|
model="claude-opus-4-6",
|
||||||
|
mcp_servers=[{
|
||||||
|
"type": "url",
|
||||||
|
"name": "github",
|
||||||
|
"url": "https://api.githubcopilot.com/mcp/",
|
||||||
|
}],
|
||||||
|
tools=[
|
||||||
|
{"type": "agent_toolset_20260401", "default_config": {"enabled": True}},
|
||||||
|
{"type": "mcp_toolset", "mcp_server_name": "github"},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
session = client.beta.sessions.create(
|
||||||
|
agent=agent.id,
|
||||||
|
environment_id=env_id,
|
||||||
|
vault_ids=[vault_id], # vault contains the GitHub MCP OAuth credential
|
||||||
|
resources=[{
|
||||||
|
"type": "github_repository",
|
||||||
|
"url": "https://github.com/owner/repo",
|
||||||
|
"authorization_token": os.environ["GITHUB_TOKEN"], # repo clone token (≠ MCP auth)
|
||||||
|
"checkout": {"type": "branch", "name": "main"},
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files API
|
||||||
|
|
||||||
|
Upload and manage files for use as session resources, and download files the agent wrote to `/mnt/session/outputs/`.
|
||||||
|
|
||||||
|
| Operation | Method | Path | SDK |
|
||||||
|
| ---------------- | -------- | ------------------------------------- | --- |
|
||||||
|
| Upload | `POST` | `/v1/files` | `client.beta.files.upload({ file })` |
|
||||||
|
| List | `GET` | `/v1/files?session_id=...` | `client.beta.files.list({ session_id })` |
|
||||||
|
| Get Metadata | `GET` | `/v1/files/{id}` | `client.beta.files.retrieveMetadata(id)` |
|
||||||
|
| Download | `GET` | `/v1/files/{id}/content` | `client.beta.files.download(id)` → `Response` |
|
||||||
|
| Delete | `DELETE` | `/v1/files/{id}` | `client.beta.files.delete(id)` |
|
||||||
|
|
||||||
|
The `session_id` filter on List scopes the results to files written to `/mnt/session/outputs/` by that session. Without the filter, you get all files uploaded to your account.
|
||||||
187
skills/claude-api/shared/managed-agents-events.md
Normal file
187
skills/claude-api/shared/managed-agents-events.md
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
# Managed Agents — Events & Steering
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
### Sending Events
|
||||||
|
|
||||||
|
Send events to a session via `POST /v1/sessions/{id}/events`.
|
||||||
|
|
||||||
|
| Event Type | When to Send |
|
||||||
|
| ------------------------- | --------------------------------------------------- |
|
||||||
|
| `user.message` | Send a user message |
|
||||||
|
| `user.interrupt` | Interrupt the agent while it's running |
|
||||||
|
| `user.tool_confirmation` | Approve/deny a tool call (when `always_ask` policy) |
|
||||||
|
| `user.custom_tool_result` | Provide result for a custom tool call |
|
||||||
|
|
||||||
|
### Receiving Events
|
||||||
|
|
||||||
|
Two methods:
|
||||||
|
|
||||||
|
1. **Streaming (SSE)**: `GET /v1/sessions/{id}/events/stream` — real-time Server-Sent Events. **Long-lived** — the server sends periodic heartbeats to keep the connection alive.
|
||||||
|
2. **Polling**: `GET /v1/sessions/{id}/events` — paginated event list (query params: `limit` default 1000, `page`). **Returns immediately** — this is a plain paginated GET, not a long-poll.
|
||||||
|
|
||||||
|
All received events carry `id`, `type`, and `processed_at` (ISO 8601; `null` if not yet processed by the agent).
|
||||||
|
|
||||||
|
> ⚠️ **Robust polling (raw HTTP).** If you bypass the SDK and roll your own poll loop, don't rely on `requests` or `httpx` timeouts as wall-clock caps — they're **per-chunk** read timeouts, reset every time a byte arrives. A trickling response (heartbeats, a wedged chunked-encoding body, a misbehaving proxy) can keep the call blocked indefinitely even with `timeout=(5, 60)` or `httpx.Timeout(120)`. Neither library has a "total wall-clock" timeout built in. For a hard deadline: track `time.monotonic()` at the loop level and break/cancel if a single request exceeds your budget (e.g. via a watchdog thread, or `asyncio.wait_for()` around async httpx). **Prefer the SDK** — `client.beta.sessions.events.stream()` and `client.beta.sessions.events.list()` handle timeout + retry sanely.
|
||||||
|
>
|
||||||
|
> If `GET /v1/sessions/{id}/events` (paginated) ever hangs after headers, you've likely hit `GET /v1/sessions/{id}/events` by mistake or a server-side stall — report it; don't treat it as a client-config problem.
|
||||||
|
|
||||||
|
### Event Types (Received)
|
||||||
|
|
||||||
|
Event types use dot notation, grouped by namespace:
|
||||||
|
|
||||||
|
| Event Type | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `agent.message` | Agent text output |
|
||||||
|
| `agent.thinking` | Extended thinking blocks |
|
||||||
|
| `agent.tool_use` | Agent used a built-in tool (`agent_toolset_20260401`) |
|
||||||
|
| `agent.tool_result` | Result from a built-in tool |
|
||||||
|
| `agent.mcp_tool_use` | Agent used an MCP tool |
|
||||||
|
| `agent.mcp_tool_result` | Result from an MCP tool |
|
||||||
|
| `agent.custom_tool_use` | Agent invoked a custom tool — session goes idle, you respond with `user.custom_tool_result` |
|
||||||
|
| `agent.thread_context_compacted` | Conversation context was compacted |
|
||||||
|
| `session.status_idle` | Agent has finished the current task, and is awaiting input. It's either waiting for input to continue working via a `user.message` or blocked awaiting a `user.custom_tool_result` or `user.tool_confirmation`. The `stop_reason` attached contains more information about why the Agent has stopped working. |
|
||||||
|
| `session.status_running` | Session has starting running, and the Agent is actively doing work. |
|
||||||
|
| `session.status_rescheduled` | Session is (re)scheduling after a retryable error has occurred, ready to be picked up by the orchestration system. |
|
||||||
|
| `session.status_terminated` | Session has terminated, entering an irreversible and unusable state. |
|
||||||
|
| `session.error` | Error occurred during processing |
|
||||||
|
| `span.model_request_start` | Model inference started |
|
||||||
|
| `span.model_request_end` | Model inference completed |
|
||||||
|
|
||||||
|
The stream also echoes back user-sent events (`user.message`, `user.interrupt`, `user.tool_confirmation`, `user.custom_tool_result`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Steering Patterns
|
||||||
|
|
||||||
|
Practical patterns for driving a session via the events surface.
|
||||||
|
|
||||||
|
### Stream-first ordering
|
||||||
|
|
||||||
|
**Open the stream before sending events.** The stream only delivers events that occur *after* it's opened — it does not replay current state or historical events. If you send a message first and open the stream second, early events (including fast status transitions) arrive buffered in a single batch and you lose the ability to react to them in real time.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// ✅ Correct — stream and send concurrently
|
||||||
|
const [response] = await Promise.all([
|
||||||
|
streamEvents(sessionId), // opens SSE connection
|
||||||
|
sendMessage(sessionId, text),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ❌ Wrong — events before stream opens arrive as a single buffered batch
|
||||||
|
await sendMessage(sessionId, text);
|
||||||
|
const response = await streamEvents(sessionId);
|
||||||
|
```
|
||||||
|
|
||||||
|
**For full history,** use `GET /v1/sessions/{id}/events` (paginated list) — the stream only gives you live events from connection onward.
|
||||||
|
|
||||||
|
### Reconnecting after a dropped stream
|
||||||
|
|
||||||
|
**The SSE stream has no replay.** If your connection drops (httpx read timeout, network blip) and you reconnect, you only get events emitted *after* reconnection. Any events emitted during the gap are lost from the stream.
|
||||||
|
|
||||||
|
**The consolidation pattern:** on every (re)connect, overlap the stream with a history fetch and dedupe by event ID:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def connect_with_consolidation(client, session_id):
|
||||||
|
# 1. Open the SSE stream first
|
||||||
|
stream = client.beta.sessions.events.stream(session_id=session_id)
|
||||||
|
|
||||||
|
# 2. Fetch history to cover any gap
|
||||||
|
history = client.beta.sessions.events.list(
|
||||||
|
session_id=session_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. Yield history first, then stream — dedupe by event.id
|
||||||
|
seen = set()
|
||||||
|
for ev in history.data:
|
||||||
|
seen.add(ev.id)
|
||||||
|
yield ev
|
||||||
|
for ev in stream:
|
||||||
|
if ev.id not in seen:
|
||||||
|
seen.add(ev.id)
|
||||||
|
yield ev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Message queuing
|
||||||
|
|
||||||
|
**You don't have to wait for a response before sending the next message.** User events are queued server-side and processed in order. This is useful for chat bridges where the user sends rapid follow-ups:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// All three go into one session; agent processes them in order
|
||||||
|
await sendMessage(sessionId, "Summarize the README");
|
||||||
|
await sendMessage(sessionId, "Actually also check the CONTRIBUTING guide");
|
||||||
|
await sendMessage(sessionId, "And compare the two");
|
||||||
|
// Stream once — agent responds to all three as a coherent turn
|
||||||
|
```
|
||||||
|
|
||||||
|
Events can be sent up to the Session at any time. There is no need to wait on a specific session status to enqueue new events via `client.beta.sessions.events.send()`
|
||||||
|
|
||||||
|
### Interrupt
|
||||||
|
|
||||||
|
An `interrupt` event **jumps the queue** (ahead of any pending user messages) and forces the session into `idle`. Use this for "stop" / "nevermind" / "cancel" commands:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await client.beta.sessions.events.send(sessionId, {
|
||||||
|
events: [{ type: 'interrupt' }],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The agent stops mid-task. It does not see the interrupt as a message — it just halts. Send a follow-up `user` event to explain what to do instead.
|
||||||
|
|
||||||
|
> **Note**: Interrupt events may have empty IDs in the current implementation. When troubleshooting, use the `processed_at` timestamp along with surrounding event IDs.
|
||||||
|
|
||||||
|
### Event payloads
|
||||||
|
|
||||||
|
some events carry useful metadata beyond the status change itself:
|
||||||
|
|
||||||
|
`session.status_idle` — includes a `stop_reason` field which elaborates on why the session stopped and what type of further action is required by the user.
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "sevt_456",
|
||||||
|
"processed_at": "2026-04-07T04:27:43.197Z",
|
||||||
|
"stop_reason": {
|
||||||
|
"event_ids": [
|
||||||
|
"sevt_123"
|
||||||
|
],
|
||||||
|
"type": "requires_action"
|
||||||
|
},
|
||||||
|
"type": "status_idle"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`span.model_request_end` contains a `model_usage` field for cost tracking and efficiency analysis:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "span.model_request_end",
|
||||||
|
"id": "sevt_456",
|
||||||
|
"is_error": false,
|
||||||
|
"model_request_start_id": "sevt_123",
|
||||||
|
"model_usage": {
|
||||||
|
"cache_creation_input_tokens": 0,
|
||||||
|
"cache_read_input_tokens": 6656,
|
||||||
|
"input_tokens": 3571,
|
||||||
|
"output_tokens": 727
|
||||||
|
},
|
||||||
|
"processed_at": "2026-04-07T04:11:32.189Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**`agent.thread_context_compacted`** — emitted when the conversation history was summarized to fit context. Includes `pre_compaction_tokens` so you know how much was squeezed:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "sevt_abc123",
|
||||||
|
"processed_at": "2026-03-24T14:05:15.787Z",
|
||||||
|
"type": "agent.thread_context_compacted"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Archive
|
||||||
|
|
||||||
|
When done with a session, archive it to free resources:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await client.beta.sessions.archive(sessionId);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
114
skills/claude-api/shared/managed-agents-onboarding.md
Normal file
114
skills/claude-api/shared/managed-agents-onboarding.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# Managed Agents — Onboarding Flow
|
||||||
|
|
||||||
|
> **Invoked via `/claude-api managed-agents-onboard`?** You're in the right place. Run the interview below — don't summarize it back to the user, ask the questions.
|
||||||
|
|
||||||
|
Use this when a user wants to set up a Managed Agent from scratch. Three steps: **branch on know-vs-explore → configure the template → set up the session**. End by emitting working code.
|
||||||
|
|
||||||
|
> Read `shared/managed-agents-core.md` alongside this — it has full detail for each knob. This doc is the interview script, not the reference.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Claude Managed Agents is a hosted agent: Anthropic runs the agent loop on its orchestration layer and provisions a sandboxed container per session where the agent's tools execute. You supply the agent config and the environment config; the harness — event stream, sandbox orchestration, prompt caching, context compaction, and extended thinking — is handled for you.
|
||||||
|
|
||||||
|
**What you supply:**
|
||||||
|
- **An agent config** — tools, skills, model, system prompt. Reusable and versioned.
|
||||||
|
- **An environment config** — the sandbox your agent's tools execute in (networking, packages). Reusable across agents.
|
||||||
|
|
||||||
|
Each run of the agent is a **session**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Know or explore?
|
||||||
|
|
||||||
|
Ask the user:
|
||||||
|
|
||||||
|
> Do you already know the agent you want to build, or would you like to explore some common patterns first?
|
||||||
|
|
||||||
|
### Explore path — show the patterns
|
||||||
|
|
||||||
|
Four shapes, same runtime code path (`sessions.create()` → `sessions.events.send()` → stream). Only the trigger and sink differ.
|
||||||
|
|
||||||
|
| Pattern | Trigger | Example |
|
||||||
|
|---|---|---|
|
||||||
|
| Event-triggered | Webhook | GitHub PR push → CMA (GitHub tool) → Slack | # <------ MC maybe delete?
|
||||||
|
| Scheduled | Cron | Daily brief: browser + GitHub + Jira → CMA → Slack | # <------ MC maybe delete?
|
||||||
|
| Fire-and-forget PR | Human | Slack slash-command → CMA (GitHub tool) → PR passing CI |
|
||||||
|
| Research + dashboard | Human | Topic → CMA (web search + `frontend-design` skill) → HTML dashboard |
|
||||||
|
|
||||||
|
Ask which shape fits, then continue with the Know path using it as the reference.
|
||||||
|
|
||||||
|
### Know path — configure template
|
||||||
|
|
||||||
|
Three rounds. Batch the questions in each round; don't ask them one at a time.
|
||||||
|
|
||||||
|
**Round A — Tools.** Start here; it's the most concrete part. Three types; ask which the user wants (any combination):
|
||||||
|
|
||||||
|
| Type | What it is | How to guide |
|
||||||
|
|---|---|---|
|
||||||
|
| **Prebuilt Claude Agent tools** (`agent_toolset_20260401`) | Ready-to-use: `bash`, `read`, `write`, `edit`, `glob`, `grep`, `web_fetch`, `web_search`. Enable all at once, or individually via `enabled: true/false`. | Recommend enabling the full toolset. List the 8 tools so the user knows what they're getting. Full detail: `shared/managed-agents-tools.md` → Agent Toolset. |
|
||||||
|
| **MCP tools** | Third-party integrations (GitHub, Linear, Asana, etc.) via `mcp_toolset`. Credentials live in a vault, not inline. | Ask which services. For each, walk through MCP server URL + vault credentials. Full detail: `shared/managed-agents-tools.md` → MCP Servers + Vaults. |
|
||||||
|
| **Custom tools** | The user's own app handles these tool calls — agent fires `agent.custom_tool_use`, the app sends a result message back. | Ask for each tool: name, description, input schema. The app code that handles the event is *their* code — don't generate it. Full detail: `shared/managed-agents-tools.md` → Custom Tools. |
|
||||||
|
|
||||||
|
**Round B — Skills, files, and repos.** What the agent has on hand when it starts.
|
||||||
|
|
||||||
|
*Skills* — two types; both work the same way — Claude auto-uses them when relevant. Max 64 per agent.
|
||||||
|
- [ ] **Pre-built Agent Skills**: `xlsx`, `docx`, `pptx`, `pdf`. Reference by name.
|
||||||
|
- [ ] **Custom Skills**: skills uploaded to the user's org via the Skills API. Reference by `skill_id` + optional `version`. If the skill doesn't exist yet, walk the user through `POST /v1/skills` + `POST /v1/skills/{id}/versions` (beta header `skills-2025-10-02`). Full detail: `shared/managed-agents-tools.md` → Skills + Skills API.
|
||||||
|
|
||||||
|
*GitHub repositories* — any repos the agent needs on-disk? For each:
|
||||||
|
- [ ] Repo URL (`https://github.com/org/repo`)
|
||||||
|
- [ ] `authorization_token` (PAT or GitHub App token scoped to the repo)
|
||||||
|
- [ ] Optional `mount_path` (defaults to `/workspace/<repo-name>`) and `checkout` (branch or SHA)
|
||||||
|
|
||||||
|
Emit as `resources: [{type: "github_repository", url, authorization_token, ...}]`. Full detail: `shared/managed-agents-environments.md` → GitHub Repositories.
|
||||||
|
|
||||||
|
> ‼️ **PR creation needs the GitHub MCP server too.** `github_repository` gives filesystem access only — to open PRs, also attach the GitHub MCP server in Round A and credential it via a vault. The workflow is: edit files in the mounted repo → push branch via `bash` → create PR via the MCP `create_pull_request` tool.
|
||||||
|
|
||||||
|
*Files* — any local files to seed the session with? For each:
|
||||||
|
- [ ] Upload via the Files API → persist `file_id`
|
||||||
|
- [ ] Choose a `mount_path` — absolute, e.g. `/workspace/data.csv` (parents auto-created; files mount read-only)
|
||||||
|
|
||||||
|
Emit as `resources: [{type: "file", file_id, mount_path}]`. Max 999 file resources. Agent working directory defaults to `/workspace`. Full detail: `shared/managed-agents-environments.md` → Files API.
|
||||||
|
|
||||||
|
**Round C — Environment + identity:**
|
||||||
|
- [ ] Networking: unrestricted internet from the container, or lock egress to specific hosts? (If locked, MCP server domains must be in `allowed_hosts` or tools silently fail.)
|
||||||
|
- [ ] Name?
|
||||||
|
- [ ] Job (one or two sentences — becomes the system prompt)?
|
||||||
|
- [ ] Model? (default `claude-opus-4-6`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Set up the session
|
||||||
|
|
||||||
|
Per-run. Points at the agent + environment, attaches credentials, kicks off.
|
||||||
|
|
||||||
|
**Vault credentials** (if the agent declared MCP servers):
|
||||||
|
- [ ] Existing vault, or create one? (`client.beta.vaults.create()` + `vaults.credentials.create()`)
|
||||||
|
|
||||||
|
Credentials are write-only, matched to MCP servers by URL, auto-refreshed. See `shared/managed-agents-tools.md` → Vaults.
|
||||||
|
|
||||||
|
**Kickoff:**
|
||||||
|
- [ ] First message to the agent?
|
||||||
|
|
||||||
|
Session creation blocks until all resources mount. Open the event stream before sending the kickoff. Stream is SSE; break on `session.status_terminated`, or on `session.status_idle` with a terminal `stop_reason` — i.e. anything except `requires_action`, which fires transiently while the session waits on a tool confirmation or custom-tool result (see `shared/managed-agents-client-patterns.md` Pattern 5). Usage lands on `span.model_request_end`. Agent-written artifacts end up in `/mnt/session/outputs/` — download via `files.list({scope: session_id})`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Emit the code
|
||||||
|
|
||||||
|
Go straight from the last interview answer to the code — no preamble about the setup-vs-runtime split, no "the critical thing to internalize…", no lecture about `agents.create()` being one-time. The two-block structure below already shows that; don't narrate it. Generate **two clearly-separated blocks** per language detected (Python/TS/cURL — see SKILL.md → Language Detection):
|
||||||
|
|
||||||
|
**Block 1 — Setup (run once, store the IDs):**
|
||||||
|
1. `environments.create()` → persist `env_id`
|
||||||
|
2. `agents.create()` with everything from §Round A–C → persist `agent_id` and `agent_version`
|
||||||
|
|
||||||
|
Label: `# ONE-TIME SETUP — run once, save the IDs to config/.env`
|
||||||
|
|
||||||
|
**Block 2 — Runtime (run on every invocation):**
|
||||||
|
1. Load `env_id` + `agent_id` from config/env
|
||||||
|
2. `sessions.create(agent=AGENT_ID, environment_id=ENV_ID, resources=[...], vault_ids=[...])`
|
||||||
|
3. Open stream, `events.send()` the kickoff, loop until `session.status_terminated` or `session.status_idle && stop_reason.type !== 'requires_action'` (see `shared/managed-agents-client-patterns.md` Pattern 5 for the full gate — do not break on bare `session.status_idle`)
|
||||||
|
|
||||||
|
> ⚠️ **Never emit `agents.create()` and `sessions.create()` in the same unguarded block.** That teaches the user to create a new agent on every run — the #1 anti-pattern. If they need a single script, wrap agent creation in `if not os.getenv("AGENT_ID"):`.
|
||||||
|
|
||||||
|
Pull exact syntax from `python/managed-agents/README.md`, `typescript/managed-agents/README.md`, or `curl/managed-agents.md`. Don't invent field names.
|
||||||
61
skills/claude-api/shared/managed-agents-overview.md
Normal file
61
skills/claude-api/shared/managed-agents-overview.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Managed Agents — Overview
|
||||||
|
|
||||||
|
Managed Agents provisions a container per session as the agent's workspace. The agent loop runs on Anthropic's orchestration layer; the container is where the agent's *tools* execute — bash commands, file operations, code. You create a persisted **Agent** config (model, system prompt, tools, MCP servers, skills), then start **Sessions** that reference it. The session streams events back to you; you send user messages and tool results in.
|
||||||
|
|
||||||
|
## ⚠️ THE MANDATORY FLOW: Agent (once) → Session (every run)
|
||||||
|
|
||||||
|
**Why agents are separate objects: versioning.** An agent is a persisted, versioned config — every update creates a new immutable version, and sessions pin to a version at creation time. This lets you iterate on the agent (tweak the prompt, add a tool) without breaking sessions already running, roll back if a change regresses, and A/B test versions side-by-side. None of that works if you `agents.create()` fresh on every run.
|
||||||
|
|
||||||
|
Every session references a pre-created `/v1/agents` object. Create the agent once, store the ID, and reuse it across runs.
|
||||||
|
|
||||||
|
| Step | Call | Frequency |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | `POST /v1/agents` — `model`, `system`, `tools`, `mcp_servers`, `skills` live here | **ONCE.** Store `agent.id` **and** `agent.version`. |
|
||||||
|
| 2 | `POST /v1/sessions` — `agent: "agent_abc123"` or `{type: "agent", id, version}` | **Every run.** String shorthand uses latest version. |
|
||||||
|
|
||||||
|
If you're about to write `sessions.create()` with `model`, `system`, or `tools` on the session body — **stop**. Those fields live on `agents.create()`. The session takes a *pointer* only.
|
||||||
|
|
||||||
|
**When generating code, separate setup from runtime.** `agents.create()` belongs in a setup script (or a guarded `if agent_id is None:` block), not at the top of the hot path. If the user's code calls `agents.create()` on every invocation, they're accumulating orphaned agents and paying the create latency for nothing. The correct shape is: create once → persist the ID (config file, env var, secrets manager) → every run loads the ID and calls `sessions.create()`.
|
||||||
|
|
||||||
|
**To change the agent's behavior, use `POST /v1/agents/{id}` — don't create a new one.** Each update bumps the version; running sessions keep their pinned version, new sessions get the latest (or pin explicitly via `{type: "agent", id, version}`). See `shared/managed-agents-core.md` → Agents → Versioning.
|
||||||
|
|
||||||
|
## Beta Headers
|
||||||
|
|
||||||
|
Managed Agents is in beta. The SDK sets required beta headers automatically:
|
||||||
|
|
||||||
|
| Beta Header | What it enables |
|
||||||
|
| ------------------------------ | ---------------------------------------------------- |
|
||||||
|
| `managed-agents-2026-04-01` | Agents, Environments, Sessions, Events, Session Resources, Vaults, Credentials |
|
||||||
|
| `skills-2025-10-02` | Skills API (for managing custom skill definitions) |
|
||||||
|
| `files-api-2025-04-14` | Files API for file uploads |
|
||||||
|
|
||||||
|
**Note: do not intermix beta headers** — If you need to upload a skill or file via the Skills API or Files API you will need to use the appropriate beta header as listed above. However, you do NOT need to inlude either the Skills or Files beta header when using any of the Managed Agents endpints listed in row 1 above. Do NOT include intermix beta headers and prefer to use the Skills or Files beta headers when using their specific endpoints.
|
||||||
|
|
||||||
|
|
||||||
|
## Reading Guide
|
||||||
|
|
||||||
|
| User wants to... | Read these files |
|
||||||
|
| -------------------------------------- | ------------------------------------------------------- |
|
||||||
|
| **Get started from scratch / "help me set up an agent"** | `shared/managed-agents-onboarding.md` — guided interview (WHERE→WHO→WHAT→WATCH), then emit code |
|
||||||
|
| Understand how the API works | `shared/managed-agents-core.md` |
|
||||||
|
| See the full endpoint reference | `shared/managed-agents-api-reference.md` |
|
||||||
|
| **Create an agent** (required first step) | `shared/managed-agents-core.md` (Agents section) + language file |
|
||||||
|
| Update/version an agent | `shared/managed-agents-core.md` (Agents → Versioning) — update, don't re-create |
|
||||||
|
| Create a session | `shared/managed-agents-core.md` + `{lang}/managed-agents/README.md` |
|
||||||
|
| Configure tools and permissions | `shared/managed-agents-tools.md` |
|
||||||
|
| Set up MCP servers | `shared/managed-agents-tools.md` (MCP Servers section) |
|
||||||
|
| Stream events / handle tool_use | `shared/managed-agents-events.md` + language file |
|
||||||
|
| Set up environments | `shared/managed-agents-environments.md` + language file |
|
||||||
|
| Upload files / attach repos | `shared/managed-agents-environments.md` (Resources) |
|
||||||
|
| Store MCP credentials | `shared/managed-agents-tools.md` (Vaults section) |
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
- **Agent FIRST, then session — NO EXCEPTIONS** — the session's `agent` field accepts **only** a string ID or `{type: "agent", id, version}`. `model`, `system`, `tools`, `mcp_servers`, `skills` are **top-level fields on `POST /v1/agents`**, never on `sessions.create()`. If the user hasn't created an agent, that is step zero of every example.
|
||||||
|
- **Agent ONCE, not every run** — `agents.create()` is a setup step. Store the returned `agent_id` and reuse it; don't call `agents.create()` at the top of your hot path. If the agent's config needs to change, `POST /v1/agents/{id}` — each update creates a new version, and sessions can pin to a specific version for reproducibility.
|
||||||
|
- **MCP auth goes through vaults** — the agent's `mcp_servers` array declares `{type, name, url}` only (no auth). Credentials live in vaults (`client.beta.vaults.credentials.create`) and attach to sessions via `vault_ids`. Anthropic auto-refreshes OAuth tokens using the stored refresh token.
|
||||||
|
- **Stream to get events** — `GET /v1/sessions/{id}/events/stream` is the primary way to receive agent output in real-time.
|
||||||
|
- **SSE stream has no replay — reconnect with consolidation** — if the stream drops while a `agent.tool_use`, `agent.mcp_tool_use`, or `agent.custom_tool_use` is pending resolution (`user.tool_confirmation` for the first two, `user.custom_tool_result` for the last one), the session deadlocks (client disconnects → session idles → reconnect happens → no client resolution happens). On every (re)connect: open stream with `GET /v1/sessions/{id}/events/stream` , fetch `GET /v1/sessions/{id}/events`, dedupe by event ID, then proceed. See `shared/managed-agents-events.md` → Reconnecting after a dropped stream.
|
||||||
|
- **Don't trust HTTP-library timeouts as wall-clock caps** — `requests` `timeout=(c, r)` and `httpx.Timeout(n)` are *per-chunk* read timeouts; they reset every byte, so a trickling connection can block indefinitely. For a hard deadline on raw-HTTP polling, track `time.monotonic()` at the loop level and bail explicitly. Prefer the SDK's `sessions.events.stream()` / `session.events.list()` over hand-rolled HTTP. See `shared/managed-agents-events.md` → Receiving Events.
|
||||||
|
- **Messages queue** — you can send events while the session is `running` or `idle`; they're processed in order. No need to wait for a response before sending the next message.
|
||||||
|
- **Cloud environments only** — `config.type: "cloud"` is the only supported environment type.
|
||||||
301
skills/claude-api/shared/managed-agents-tools.md
Normal file
301
skills/claude-api/shared/managed-agents-tools.md
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
# Managed Agents — Tools & Skills
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
### Server tools vs client tools
|
||||||
|
|
||||||
|
| Type | Who runs it | How it works |
|
||||||
|
|---|---|---|
|
||||||
|
| **Prebuilt Claude Agent tools** (`agent_toolset_20260401`) | Anthropic, on the session's container | File ops, bash, web search, etc. Enable all at once or configure individually with `enabled: true/false`. |
|
||||||
|
| **MCP tools** (`mcp_toolset`) | Anthropic, on the session's container | Capabilities exposed by connected MCP servers. Grant access per-server via the toolset. |
|
||||||
|
| **Custom tools** | **You** — your application handles the call and returns results | Agent emits a `agent.custom_tool_use` event, session goes `idle`, you send back a `user.custom_tool_result` event. |
|
||||||
|
|
||||||
|
**Recommendation:** Enable all prebuilt tools via `agent_toolset_20260401`, then disable individually as needed.
|
||||||
|
|
||||||
|
**Versioning:** The toolset is a versioned, static resource. When underlying tools change, a new toolset version is created (hence `_20260401`) so you always know exactly what you're getting.
|
||||||
|
|
||||||
|
### Agent Toolset
|
||||||
|
|
||||||
|
The `agent_toolset_20260401` provides these built-in tools:
|
||||||
|
|
||||||
|
| Tool | Description |
|
||||||
|
| ---------------------- | ---------------------------------------- |
|
||||||
|
| `bash` | Execute bash commands in a shell session |
|
||||||
|
| `read` | Read a file from the local filesystem, including text, images, PDFs, and Jupyter notebooks |
|
||||||
|
| `write` | Write a file to the local filesystem |
|
||||||
|
| `edit` | Perform string replacement in a file |
|
||||||
|
| `glob` | Fast file pattern matching using glob patterns |
|
||||||
|
| `grep` | Text search using regex patterns |
|
||||||
|
| `web_fetch` | Fetch content from a URL |
|
||||||
|
| `web_search` | Search the web for information |
|
||||||
|
|
||||||
|
Enable the full toolset:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tools": [
|
||||||
|
{ "type": "agent_toolset_20260401" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Per-Tool Configuration
|
||||||
|
|
||||||
|
Override defaults for individual tools. This example enables everything except bash:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"type": "agent_toolset_20260401",
|
||||||
|
"default_config": { "enabled": true },
|
||||||
|
"configs": [
|
||||||
|
{ "name": "bash", "enabled": false }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Required | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `type` | ✅ | `"agent_toolset_20260401"` |
|
||||||
|
| `default_config` | ❌ | Applied to all tools. `{ "enabled": bool, "permission_policy": {...} }` |
|
||||||
|
| `configs` | ❌ | Per-tool overrides: `[{ "name": "...", "enabled": bool, "permission_policy": {...} }]` |
|
||||||
|
|
||||||
|
### Permission Policies
|
||||||
|
|
||||||
|
Control when server-executed tools (agent toolset + MCP) run automatically vs wait for approval. Does not apply to custom tools.
|
||||||
|
|
||||||
|
| Policy | Behavior |
|
||||||
|
|---|---|
|
||||||
|
| `always_allow` | Tool executes automatically (default) |
|
||||||
|
| `always_ask` | Session emits `session.status_idle` and pauses until you send a `tool_confirmation` event |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "agent_toolset_20260401",
|
||||||
|
"default_config": {
|
||||||
|
"enabled": true,
|
||||||
|
"permission_policy": { "type": "always_allow" }
|
||||||
|
},
|
||||||
|
"configs": [
|
||||||
|
{ "name": "bash", "permission_policy": { "type": "always_ask" } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responding to `always_ask`:** Send a `user.tool_confirmation` event with `tool_use_id` from the triggering `agent_tool_use`/`mcp_tool_use` event:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "type": "tool_confirmation", "tool_use_id": "sevt_abc123", "result": "allow" }
|
||||||
|
{ "type": "tool_confirmation", "tool_use_id": "sevt_def456", "result": "deny", "message": "Read .env.example instead" }
|
||||||
|
```
|
||||||
|
|
||||||
|
The optional `message` on a deny is delivered to the agent so it can adjust its approach.
|
||||||
|
|
||||||
|
To enable only specific tools, flip the default off and opt-in per tool:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"type": "agent_toolset_20260401",
|
||||||
|
"default_config": { "enabled": false },
|
||||||
|
"configs": [
|
||||||
|
{ "name": "bash", "enabled": true },
|
||||||
|
{ "name": "read", "enabled": true }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Tools (Client-Side)
|
||||||
|
|
||||||
|
Custom tools are executed by **your application**, not Anthropic. The flow:
|
||||||
|
|
||||||
|
1. Agent decides to use the tool → session emits a `agent.custom_tool_use` event with inputs
|
||||||
|
2. Session goes `idle` waiting for you
|
||||||
|
3. Your application executes the tool
|
||||||
|
4. You send back a `user.custom_tool_result` event with the output
|
||||||
|
5. Session resumes `running`
|
||||||
|
|
||||||
|
No permission policy needed — you're the one executing.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"type": "custom",
|
||||||
|
"name": "get_weather",
|
||||||
|
"description": "Fetch current weather for a city.",
|
||||||
|
"input_schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"city": { "type": "string", "description": "City name" }
|
||||||
|
},
|
||||||
|
"required": ["city"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### MCP Servers
|
||||||
|
|
||||||
|
MCP (Model Context Protocol) servers expose standardized third-party capabilities (e.g. Asana, GitHub, Linear). **Configuration is split across agent and vault:**
|
||||||
|
|
||||||
|
1. **Agent creation** declares which servers to connect to (`type`, `name`, `url` — no auth). The agent's `mcp_servers` array has no auth field.
|
||||||
|
2. **Vault** stores the OAuth credentials. Attach via `vault_ids` on session create.
|
||||||
|
|
||||||
|
This keeps secrets out of reusable agent definitions. Each vault credential is tied to one MCP server URL; Anthropic matches credentials to servers by URL.
|
||||||
|
|
||||||
|
**Agent side — declare servers (no auth):**
|
||||||
|
|
||||||
|
| Field | Required | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `type` | ✅ | `"url"` |
|
||||||
|
| `name` | ✅ | Unique name — referenced by `mcp_toolset.mcp_server_name` |
|
||||||
|
| `url` | ✅ | The MCP server's endpoint URL (Streamable HTTP transport) |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcp_servers": [
|
||||||
|
{ "type": "url", "name": "linear", "url": "https://mcp.linear.app/mcp" }
|
||||||
|
],
|
||||||
|
"tools": [
|
||||||
|
{ "type": "mcp_toolset", "mcp_server_name": "linear" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Session side — attach vault:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agent": "agent_abc123",
|
||||||
|
"environment_id": "env_abc123",
|
||||||
|
"vault_ids": ["vlt_abc123"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Per-tool enablement (empirical):** `mcp_toolset` has been observed accepting `default_config: {enabled: false}` + `configs: [{name, enabled: true}]` for an allowlist pattern. The API ref shows only the minimal `{type, mcp_server_name}` form.
|
||||||
|
|
||||||
|
> ⚠️ **MCP auth tokens ≠ REST API tokens.** Hosted MCP servers (`mcp.notion.com`, `mcp.linear.app`, etc.) typically require **OAuth bearer tokens**, not the service's native API keys. A Notion `ntn_` integration token authenticates against Notion's REST API but will **not** work as a vault credential for the Notion MCP server. These are different auth systems.
|
||||||
|
|
||||||
|
### Vaults — the MCP credential store
|
||||||
|
|
||||||
|
**Vaults** store OAuth credentials (access token + refresh token) that Anthropic auto-refreshes on your behalf via standard OAuth 2.0 `refresh_token` grant. This is the only way to authenticate MCP servers in the launch SDK.
|
||||||
|
|
||||||
|
> Formerly known internally as TATs (Tool/Tenant Access Tokens).
|
||||||
|
|
||||||
|
**Flow:**
|
||||||
|
|
||||||
|
1. Create a vault (`client.beta.vaults.create(...)`) — one per tenant/user, or one shared, depending on your model
|
||||||
|
2. Add MCP credentials to it (`client.beta.vaults.credentials.create(...)`) — each credential is tied to one MCP server URL
|
||||||
|
3. Reference the vault on session create via `vault_ids: ["vlt_..."]`
|
||||||
|
4. Anthropic auto-refreshes tokens before they expire; the agent uses the current access token when calling MCP tools
|
||||||
|
|
||||||
|
**Credential shape**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"display_name": "Notion (workspace-foo)",
|
||||||
|
"auth": {
|
||||||
|
"type": "mcp_oauth",
|
||||||
|
"mcp_server_url": "https://mcp.notion.com/mcp",
|
||||||
|
"access_token": "<current access token>",
|
||||||
|
"expires_at": "2026-04-02T14:00:00Z",
|
||||||
|
"refresh": {
|
||||||
|
"refresh_token": "<refresh token>",
|
||||||
|
"client_id": "<your OAuth client_id>",
|
||||||
|
"token_endpoint": "https://api.notion.com/v1/oauth/token",
|
||||||
|
"token_endpoint_auth": { "type": "none" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `refresh` block is what enables auto-refresh — `token_endpoint` is where Anthropic posts the `refresh_token` grant. `token_endpoint_auth` is a discriminated union:
|
||||||
|
|
||||||
|
| `type` | Shape | Use when |
|
||||||
|
|---|---|---|
|
||||||
|
| `"none"` | `{type: "none"}` | Public OAuth client (no secret) |
|
||||||
|
| `"client_secret_basic"` | `{type: "client_secret_basic", client_secret: "..."}` | Confidential client, secret via HTTP Basic auth |
|
||||||
|
| `"client_secret_post"` | `{type: "client_secret_post", client_secret: "..."}` | Confidential client, secret in request body |
|
||||||
|
|
||||||
|
Omit `refresh` entirely if you only have an access token with no refresh capability — it'll work until it expires, then the agent loses access.
|
||||||
|
|
||||||
|
> 💡 **Getting an OAuth token.** How you obtain the initial access and refresh tokens depends on the MCP server — consult its documentation. Once you have them, store them in a vault credential using the shape above; Anthropic auto-refreshes via the `refresh.token_endpoint` from there.
|
||||||
|
|
||||||
|
**Scoping:** Vaults are workspace-scoped. Anyone with developer+ role in the API workspace can create, read (metadata only — secrets are write-only), and attach vaults. `vault_ids` can be set at session **create** time but not via session update (the SDK docstring says "Not yet supported; requests setting this field are rejected").
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Skills
|
||||||
|
|
||||||
|
Skills are reusable, filesystem-based resources that provide your agent with domain-specific expertise: workflows, context, and best practices that transform general-purpose agents into specialists. Unlike prompts (conversation-level instructions for one-off tasks), skills load on-demand and eliminate the need to repeatedly provide the same guidance across multiple conversations.
|
||||||
|
|
||||||
|
Two types — both work the same way; the agent automatically uses them when relevant to the task at hand:
|
||||||
|
|
||||||
|
| Type | What it is |
|
||||||
|
|---|---|
|
||||||
|
| **Pre-built Anthropic skills** | Common document tasks (PowerPoint, Excel, Word, PDF). Reference by name (e.g. `xlsx`). |
|
||||||
|
| **Custom skills** | Skills you've created in your organization via the Skills API. Reference by `skill_id` + optional `version`. |
|
||||||
|
|
||||||
|
**Max 64 skills per agent.** Agent creation uses `managed-agents-2026-04-01`; the separate Skills API (for managing custom skill definitions) uses `skills-2025-10-02`.
|
||||||
|
|
||||||
|
### Enabling skills on a session
|
||||||
|
|
||||||
|
Skills are attached to the **agent** definition via `agents.create()`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const agent = await client.beta.agents.create(
|
||||||
|
{
|
||||||
|
name: "Financial Agent",
|
||||||
|
model: "claude-opus-4-6",
|
||||||
|
system: "You are a financial analysis agent.",
|
||||||
|
skills: [
|
||||||
|
{ type: "anthropic", skill_id: "xlsx" },
|
||||||
|
{ type: "custom", skill_id: "skill_abc123", version: "latest" },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Python:
|
||||||
|
|
||||||
|
```python
|
||||||
|
agent = client.beta.agents.create(
|
||||||
|
name="Financial Agent",
|
||||||
|
model="claude-opus-4-6",
|
||||||
|
system="You are a financial analysis agent.",
|
||||||
|
skills=[
|
||||||
|
{"type": "anthropic", "skill_id": "xlsx"},
|
||||||
|
{"type": "custom", "skill_id": "skill_abc123", "version": "latest"},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Skill reference fields:**
|
||||||
|
|
||||||
|
| Field | Anthropic skill | Custom skill |
|
||||||
|
|---|---|---|
|
||||||
|
| `type` | `"anthropic"` | `"custom"` |
|
||||||
|
| `skill_id` | Skill name (e.g. `"xlsx"`, `"docx"`, `"pptx"`, `"pdf"`) | Skill ID from Skills API (e.g. `"skill_abc123"`) |
|
||||||
|
| `version` | — | `"latest"` or a specific version number |
|
||||||
|
|
||||||
|
### Skills API
|
||||||
|
|
||||||
|
| Operation | Method | Path |
|
||||||
|
| --------------------- | -------- | ----------------------------------------------- |
|
||||||
|
| Create Skill | `POST` | `/v1/skills` |
|
||||||
|
| List Skills | `GET` | `/v1/skills` |
|
||||||
|
| Get Skill | `GET` | `/v1/skills/{id}` |
|
||||||
|
| Delete Skill | `DELETE` | `/v1/skills/{id}` |
|
||||||
|
| Create Version | `POST` | `/v1/skills/{id}/versions` |
|
||||||
|
| List Versions | `GET` | `/v1/skills/{id}/versions` |
|
||||||
|
| Get Version | `GET` | `/v1/skills/{id}/versions/{version}` |
|
||||||
|
| Delete Version | `DELETE` | `/v1/skills/{id}/versions/{version}` |
|
||||||
|
|
||||||
@@ -107,9 +107,17 @@ Fix by moving the dynamic piece after the last breakpoint, making it determinist
|
|||||||
- Max **4** `cache_control` breakpoints per request.
|
- Max **4** `cache_control` breakpoints per request.
|
||||||
- Goes on any content block: system text blocks, tool definitions, message content blocks (`text`, `image`, `tool_use`, `tool_result`, `document`).
|
- Goes on any content block: system text blocks, tool definitions, message content blocks (`text`, `image`, `tool_use`, `tool_result`, `document`).
|
||||||
- Top-level `cache_control` on `messages.create()` auto-places on the last cacheable block — simplest option when you don't need fine-grained placement.
|
- Top-level `cache_control` on `messages.create()` auto-places on the last cacheable block — simplest option when you don't need fine-grained placement.
|
||||||
- Minimum cacheable prefix is model-dependent (typically 1024–2048 tokens). Shorter prefixes silently won't cache even with a marker.
|
- Minimum cacheable prefix is model-dependent. Shorter prefixes silently won't cache even with a marker — no error, just `cache_creation_input_tokens: 0`:
|
||||||
|
|
||||||
**Economics:** Cache writes cost ~1.25× base input price; reads cost ~0.1×. A prefix must be used in at least two requests within TTL to break even (one writes the cache, subsequent ones read it). For bursty traffic, the 1-hour TTL keeps entries alive across gaps.
|
| Model | Minimum |
|
||||||
|
|---|---:|
|
||||||
|
| Opus 4.6, Opus 4.5, Haiku 4.5 | 4096 tokens |
|
||||||
|
| Sonnet 4.6, Haiku 3.5, Haiku 3 | 2048 tokens |
|
||||||
|
| Sonnet 4.5, Sonnet 4.1, Sonnet 4, Sonnet 3.7 | 1024 tokens |
|
||||||
|
|
||||||
|
A 3K-token prompt caches on Sonnet 4.5 but silently won't on Opus 4.6.
|
||||||
|
|
||||||
|
**Economics:** Cache reads cost ~0.1× base input price. Cache writes cost **1.25× for 5-minute TTL, 2× for 1-hour TTL**. Break-even depends on TTL: with 5-minute TTL, two requests break even (1.25× + 0.1× = 1.35× vs 2× uncached); with 1-hour TTL, you need at least three requests (2× + 0.2× = 2.2× vs 3× uncached). The 1-hour TTL keeps entries alive across gaps in bursty traffic, but the doubled write cost means it needs more reads to pay off.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -125,4 +133,39 @@ The response `usage` object reports cache activity:
|
|||||||
|
|
||||||
If `cache_read_input_tokens` is zero across repeated requests with identical prefixes, a silent invalidator is at work — diff the rendered prompt bytes between two requests to find it.
|
If `cache_read_input_tokens` is zero across repeated requests with identical prefixes, a silent invalidator is at work — diff the rendered prompt bytes between two requests to find it.
|
||||||
|
|
||||||
|
**`input_tokens` is the uncached remainder only.** Total prompt size = `input_tokens + cache_creation_input_tokens + cache_read_input_tokens`. If your agent ran for hours but `input_tokens` shows 4K, the rest was served from cache — check the sum, not the single field.
|
||||||
|
|
||||||
Language-specific access: `response.usage.cache_read_input_tokens` (Python/TS/Ruby), `$message->usage->cacheReadInputTokens` (PHP), `resp.Usage.CacheReadInputTokens` (Go/C#), `.usage().cacheReadInputTokens()` (Java).
|
Language-specific access: `response.usage.cache_read_input_tokens` (Python/TS/Ruby), `$message->usage->cacheReadInputTokens` (PHP), `resp.Usage.CacheReadInputTokens` (Go/C#), `.usage().cacheReadInputTokens()` (Java).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Invalidation hierarchy
|
||||||
|
|
||||||
|
Not every parameter change invalidates everything. The API has three cache tiers, and changes only invalidate their own tier and below:
|
||||||
|
|
||||||
|
| Change | Tools cache | System cache | Messages cache |
|
||||||
|
|---|:---:|:---:|:---:|
|
||||||
|
| Tool definitions (add/remove/reorder) | ❌ | ❌ | ❌ |
|
||||||
|
| Model switch | ❌ | ❌ | ❌ |
|
||||||
|
| `speed`, web-search, citations toggle | ✅ | ❌ | ❌ |
|
||||||
|
| System prompt content | ✅ | ❌ | ❌ |
|
||||||
|
| `tool_choice`, images, `thinking` enable/disable | ✅ | ✅ | ❌ |
|
||||||
|
| Message content | ✅ | ✅ | ❌ |
|
||||||
|
|
||||||
|
Implication: you can change `tool_choice` per-request or toggle `thinking` without losing the tools+system cache. Don't over-worry about these — only tool-definition and model changes force a full rebuild.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 20-block lookback window
|
||||||
|
|
||||||
|
Each breakpoint walks backward **at most 20 content blocks** to find a prior cache entry. If a single turn adds more than 20 blocks (common in agentic loops with many tool_use/tool_result pairs), the next request's breakpoint won't find the previous cache and silently misses.
|
||||||
|
|
||||||
|
Fix: place an intermediate breakpoint every ~15 blocks in long turns, or put the marker on a block that's within 20 of the previous turn's last cached block.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Concurrent-request timing
|
||||||
|
|
||||||
|
A cache entry becomes readable only after the first response **begins streaming**. N parallel requests with identical prefixes all pay full price — none can read what the others are still writing.
|
||||||
|
|
||||||
|
For fan-out patterns: send 1 request, await the first streamed token (not the full response), then fire the remaining N−1. They'll read the cache the first one just wrote.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Tool Use Concepts
|
# Tool Use Concepts
|
||||||
|
|
||||||
This file covers the conceptual foundations of tool use with the Claude API. For language-specific code examples, see the `python/`, `typescript/`, or other language folders.
|
This file covers the conceptual foundations of tool use with the Claude API. For language-specific code examples, see the `python/`, `typescript/`, or other language folders. For decision heuristics on which tools to expose, how to manage context in long-running agents, and caching strategy, see `agent-design.md`.
|
||||||
|
|
||||||
## User-Defined Tools
|
## User-Defined Tools
|
||||||
|
|
||||||
@@ -192,7 +192,9 @@ Without dynamic filtering, the previous `web_search_20250305` version is also av
|
|||||||
|
|
||||||
## Server-Side Tools: Programmatic Tool Calling
|
## Server-Side Tools: Programmatic Tool Calling
|
||||||
|
|
||||||
Programmatic tool calling lets Claude execute complex multi-tool workflows in code, keeping intermediate results out of the context window. Claude writes code that calls your tools directly, reducing token usage for multi-step operations.
|
With standard tool use, each tool call is a round trip: Claude calls, the result enters Claude's context, Claude reasons, then calls the next tool. Chained calls accumulate latency and tokens — most of that intermediate data is never needed again.
|
||||||
|
|
||||||
|
Programmatic tool calling lets Claude compose those calls into a script. The script runs in the code execution container; when it invokes a tool, the container pauses, the call executes, and the result returns to the running code (not to Claude's context). The script processes it with normal control flow. Only the final output returns to Claude. Use it when chaining many tool calls or when intermediate results are large and should be filtered before reaching the context window.
|
||||||
|
|
||||||
For full documentation, use WebFetch:
|
For full documentation, use WebFetch:
|
||||||
|
|
||||||
@@ -202,7 +204,7 @@ For full documentation, use WebFetch:
|
|||||||
|
|
||||||
## Server-Side Tools: Tool Search
|
## Server-Side Tools: Tool Search
|
||||||
|
|
||||||
The tool search tool lets Claude dynamically discover tools from large libraries without loading all definitions into the context window. Useful when you have many tools but only a few are relevant to any given query.
|
The tool search tool lets Claude dynamically discover tools from large libraries without loading all definitions into the context window. Use it when you have many tools but only a few are relevant to any given request. Discovered tool schemas are appended to the request, not swapped in — this preserves the prompt cache (see `agent-design.md` §Caching for Agents).
|
||||||
|
|
||||||
For full documentation, use WebFetch:
|
For full documentation, use WebFetch:
|
||||||
|
|
||||||
@@ -210,6 +212,16 @@ For full documentation, use WebFetch:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Skills
|
||||||
|
|
||||||
|
Skills package task-specific instructions that Claude loads only when relevant. Each skill is a folder containing a `SKILL.md` file. The skill's short description sits in context by default; Claude reads the full file when the current task calls for it. Use skills to keep specialized instructions out of the base system prompt without losing discoverability.
|
||||||
|
|
||||||
|
For full documentation, use WebFetch:
|
||||||
|
|
||||||
|
- URL: `https://platform.claude.com/docs/en/agents-and-tools/skills`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Tool Use Examples
|
## Tool Use Examples
|
||||||
|
|
||||||
You can provide sample tool calls directly in your tool definitions to demonstrate usage patterns and reduce parameter errors. This helps Claude understand how to correctly format tool inputs, especially for tools with complex schemas.
|
You can provide sample tool calls directly in your tool definitions to demonstrate usage patterns and reduce parameter errors. This helps Claude understand how to correctly format tool inputs, especially for tools with complex schemas.
|
||||||
@@ -230,6 +242,16 @@ For full documentation, use WebFetch:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Context Editing
|
||||||
|
|
||||||
|
Context editing clears stale tool results and thinking blocks from the transcript as a long-running agent accumulates turns. Unlike compaction (which summarizes), context editing prunes — the cleared content is removed, not replaced. Use it when old tool outputs are no longer relevant and you want to keep the transcript lean without losing the conversation structure. Thresholds for what to clear are configurable.
|
||||||
|
|
||||||
|
For full documentation, use WebFetch:
|
||||||
|
|
||||||
|
- URL: `https://platform.claude.com/docs/en/build-with-claude/context-editing`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Client-Side Tools: Memory
|
## Client-Side Tools: Memory
|
||||||
|
|
||||||
The memory tool enables Claude to store and retrieve information across conversations through a memory file directory. Claude can create, read, update, and delete files that persist between sessions.
|
The memory tool enables Claude to store and retrieve information across conversations through a memory file directory. Claude can create, read, update, and delete files that persist between sessions.
|
||||||
|
|||||||
@@ -1,297 +0,0 @@
|
|||||||
# Agent SDK — TypeScript
|
|
||||||
|
|
||||||
The Claude Agent SDK provides a higher-level interface for building AI agents with built-in tools, safety features, and agentic capabilities.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install @anthropic-ai/claude-agent-sdk
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Explain this codebase",
|
|
||||||
options: { allowedTools: ["Read", "Glob", "Grep"] },
|
|
||||||
})) {
|
|
||||||
if ("result" in message) {
|
|
||||||
console.log(message.result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Built-in Tools
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
| --------- | ------------------------------------ |
|
|
||||||
| Read | Read files in the workspace |
|
|
||||||
| Write | Create new files |
|
|
||||||
| Edit | Make precise edits to existing files |
|
|
||||||
| Bash | Execute shell commands |
|
|
||||||
| Glob | Find files by pattern |
|
|
||||||
| Grep | Search files by content |
|
|
||||||
| WebSearch | Search the web for information |
|
|
||||||
| WebFetch | Fetch and analyze web pages |
|
|
||||||
| AskUserQuestion | Ask user clarifying questions |
|
|
||||||
| Agent | Spawn subagents |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Permission System
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Refactor the authentication module",
|
|
||||||
options: {
|
|
||||||
allowedTools: ["Read", "Edit", "Write"],
|
|
||||||
permissionMode: "acceptEdits",
|
|
||||||
},
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Permission modes:
|
|
||||||
|
|
||||||
- `"default"`: Prompt for dangerous operations
|
|
||||||
- `"plan"`: Planning only, no execution
|
|
||||||
- `"acceptEdits"`: Auto-accept file edits
|
|
||||||
- `"dontAsk"`: Don't prompt — **denies** anything not pre-approved (not an auto-approve mode)
|
|
||||||
- `"bypassPermissions"`: Skip all prompts (requires `allowDangerouslySkipPermissions: true` in options)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## MCP (Model Context Protocol) Support
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Open example.com and describe what you see",
|
|
||||||
options: {
|
|
||||||
mcpServers: {
|
|
||||||
playwright: { command: "npx", args: ["@playwright/mcp@latest"] },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### In-Process MCP Tools
|
|
||||||
|
|
||||||
You can define custom tools that run in-process using `tool()` and `createSdkMcpServer`:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
const myTool = tool("my-tool", "Description", { input: z.string() }, async (args) => {
|
|
||||||
return { content: [{ type: "text", text: "result" }] };
|
|
||||||
});
|
|
||||||
|
|
||||||
const server = createSdkMcpServer({ name: "my-server", tools: [myTool] });
|
|
||||||
|
|
||||||
// Pass to query
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Use my-tool to do something",
|
|
||||||
options: { mcpServers: { myServer: server } },
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Hooks
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { query, HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
import { appendFileSync } from "fs";
|
|
||||||
|
|
||||||
const logFileChange: HookCallback = async (input) => {
|
|
||||||
const filePath = (input as any).tool_input?.file_path ?? "unknown";
|
|
||||||
appendFileSync(
|
|
||||||
"./audit.log",
|
|
||||||
`${new Date().toISOString()}: modified ${filePath}\n`,
|
|
||||||
);
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Refactor utils.py to improve readability",
|
|
||||||
options: {
|
|
||||||
allowedTools: ["Read", "Edit", "Write"],
|
|
||||||
permissionMode: "acceptEdits",
|
|
||||||
hooks: {
|
|
||||||
PostToolUse: [{ matcher: "Edit|Write", hooks: [logFileChange] }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Hook event inputs for tool-lifecycle events (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`) include `agent_id` and `agent_type` fields, allowing hooks to identify which agent (main or subagent) triggered the tool call.
|
|
||||||
|
|
||||||
Available hook events: `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `Notification`, `UserPromptSubmit`, `SessionStart`, `SessionEnd`, `Stop`, `SubagentStart`, `SubagentStop`, `PreCompact`, `PermissionRequest`, `Setup`, `TeammateIdle`, `TaskCompleted`, `ConfigChange`, `Elicitation`, `ElicitationResult`, `WorktreeCreate`, `WorktreeRemove`, `InstructionsLoaded`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Common Options
|
|
||||||
|
|
||||||
`query()` takes a top-level `prompt` (string) and an `options` object:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
query({ prompt: "...", options: { ... } })
|
|
||||||
```
|
|
||||||
|
|
||||||
| Option | Type | Description |
|
|
||||||
| ----------------------------------- | ------ | -------------------------------------------------------------------------- |
|
|
||||||
| `cwd` | string | Working directory for file operations |
|
|
||||||
| `allowedTools` | array | Tools the agent can use (e.g., `["Read", "Edit", "Bash"]`) |
|
|
||||||
| `tools` | array \| preset | Built-in tools to make available (`string[]` or `{type:'preset', preset:'claude_code'}`) |
|
|
||||||
| `disallowedTools` | array | Tools to explicitly disallow |
|
|
||||||
| `permissionMode` | string | How to handle permission prompts |
|
|
||||||
| `allowDangerouslySkipPermissions` | bool | Must be `true` to use `permissionMode: "bypassPermissions"` |
|
|
||||||
| `mcpServers` | object | MCP servers to connect to |
|
|
||||||
| `hooks` | object | Hooks for customizing behavior |
|
|
||||||
| `systemPrompt` | string \| preset | Custom system prompt (`string` or `{type:'preset', preset:'claude_code', append?:string}`) |
|
|
||||||
| `maxTurns` | number | Maximum agent turns before stopping |
|
|
||||||
| `maxBudgetUsd` | number | Maximum budget in USD for the query |
|
|
||||||
| `model` | string | Model ID (default: determined by CLI) |
|
|
||||||
| `agents` | object | Subagent definitions (`Record<string, AgentDefinition>`) |
|
|
||||||
| `outputFormat` | object | Structured output schema |
|
|
||||||
| `thinking` | object | Thinking/reasoning control |
|
|
||||||
| `betas` | array | Beta features to enable (e.g., `["context-1m-2025-08-07"]`) |
|
|
||||||
| `settingSources` | array | Settings to load (e.g., `["project"]`). Default: none (no CLAUDE.md files) |
|
|
||||||
| `env` | object | Environment variables to set for the session |
|
|
||||||
| `agentProgressSummaries` | bool | Enable periodic AI-generated progress summaries on `task_progress` events |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Subagents
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Use the code-reviewer agent to review this codebase",
|
|
||||||
options: {
|
|
||||||
allowedTools: ["Read", "Glob", "Grep", "Agent"],
|
|
||||||
agents: {
|
|
||||||
"code-reviewer": {
|
|
||||||
description: "Expert code reviewer for quality and security reviews.",
|
|
||||||
prompt: "Analyze code quality and suggest improvements.",
|
|
||||||
tools: ["Read", "Glob", "Grep"],
|
|
||||||
// Optional: skills, mcpServers for subagent customization
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Message Types
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Find TODO comments",
|
|
||||||
options: { allowedTools: ["Read", "Glob", "Grep"] },
|
|
||||||
})) {
|
|
||||||
if ("result" in message) {
|
|
||||||
console.log(message.result);
|
|
||||||
console.log(`Stop reason: ${message.stop_reason}`); // e.g., "end_turn", "tool_use", "max_tokens"
|
|
||||||
} else if (message.type === "system" && message.subtype === "init") {
|
|
||||||
const sessionId = message.session_id; // Capture for resuming later
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Task-related system messages are also emitted for subagent operations:
|
|
||||||
- `task_started` — emitted when a subagent task is registered
|
|
||||||
- `task_progress` — real-time progress updates with cumulative usage metrics, tool counts, and duration (enable `agentProgressSummaries` option for periodic AI-generated summaries via the `summary` field)
|
|
||||||
- `task_notification` — task completion notifications (includes `tool_use_id` for correlating with originating tool calls)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session History
|
|
||||||
|
|
||||||
Retrieve past session data:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { listSessions, getSessionMessages, getSessionInfo } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
|
|
||||||
// List all past sessions (supports pagination via limit/offset)
|
|
||||||
const sessions = await listSessions({ limit: 20, offset: 0 });
|
|
||||||
for (const session of sessions) {
|
|
||||||
console.log(`${session.sessionId}: ${session.cwd} (tag: ${session.tag})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get metadata for a single session
|
|
||||||
const sessionId = sessions[0]?.sessionId;
|
|
||||||
const info = await getSessionInfo(sessionId);
|
|
||||||
console.log(info.tag, info.createdAt);
|
|
||||||
|
|
||||||
// Get messages from a specific session (supports pagination via limit/offset)
|
|
||||||
const messages = await getSessionMessages(sessionId, { limit: 50, offset: 0 });
|
|
||||||
for (const msg of messages) {
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Session Mutations
|
|
||||||
|
|
||||||
Rename, tag, or fork sessions:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { renameSession, tagSession, forkSession } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
|
|
||||||
// Rename a session
|
|
||||||
await renameSession(sessionId, "My refactoring session");
|
|
||||||
|
|
||||||
// Tag a session
|
|
||||||
await tagSession(sessionId, "experiment");
|
|
||||||
|
|
||||||
// Clear a tag
|
|
||||||
await tagSession(sessionId, null);
|
|
||||||
|
|
||||||
// Fork a session — branch a conversation from a specific point
|
|
||||||
const { sessionId: forkedId } = await forkSession(sessionId);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## MCP Server Management
|
|
||||||
|
|
||||||
Manage MCP servers at runtime on a running query:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Reconnect a disconnected MCP server
|
|
||||||
await queryHandle.reconnectMcpServer("my-server");
|
|
||||||
|
|
||||||
// Toggle an MCP server on/off
|
|
||||||
await queryHandle.toggleMcpServer("my-server", false); // (name, enabled) — both required
|
|
||||||
|
|
||||||
// Get status of ALL configured MCP servers — returns an ARRAY
|
|
||||||
const statuses: McpServerStatus[] = await queryHandle.mcpServerStatus();
|
|
||||||
for (const s of statuses) {
|
|
||||||
console.log(s.name, s.scope, s.tools.length, s.error);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Always specify allowedTools** — Explicitly list which tools the agent can use
|
|
||||||
2. **Set working directory** — Always specify `cwd` for file operations
|
|
||||||
3. **Use appropriate permission modes** — Start with `"default"` and only escalate when needed
|
|
||||||
4. **Handle all message types** — Check for `result` property to get agent output
|
|
||||||
5. **Limit maxTurns** — Prevent runaway agents with reasonable limits
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
# Agent SDK Patterns — TypeScript
|
|
||||||
|
|
||||||
## Basic Agent
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Explain what this repository does",
|
|
||||||
options: {
|
|
||||||
cwd: "/path/to/project",
|
|
||||||
allowedTools: ["Read", "Glob", "Grep"],
|
|
||||||
},
|
|
||||||
})) {
|
|
||||||
if ("result" in message) {
|
|
||||||
console.log(message.result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Hooks
|
|
||||||
|
|
||||||
### After Tool Use Hook
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { query, HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
import { appendFileSync } from "fs";
|
|
||||||
|
|
||||||
const logFileChange: HookCallback = async (input) => {
|
|
||||||
const filePath = (input as any).tool_input?.file_path ?? "unknown";
|
|
||||||
appendFileSync(
|
|
||||||
"./audit.log",
|
|
||||||
`${new Date().toISOString()}: modified ${filePath}\n`,
|
|
||||||
);
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Refactor utils.py to improve readability",
|
|
||||||
options: {
|
|
||||||
allowedTools: ["Read", "Edit", "Write"],
|
|
||||||
permissionMode: "acceptEdits",
|
|
||||||
hooks: {
|
|
||||||
PostToolUse: [{ matcher: "Edit|Write", hooks: [logFileChange] }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Subagents
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Use the code-reviewer agent to review this codebase",
|
|
||||||
options: {
|
|
||||||
allowedTools: ["Read", "Glob", "Grep", "Agent"],
|
|
||||||
agents: {
|
|
||||||
"code-reviewer": {
|
|
||||||
description: "Expert code reviewer for quality and security reviews.",
|
|
||||||
prompt: "Analyze code quality and suggest improvements.",
|
|
||||||
tools: ["Read", "Glob", "Grep"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## MCP Server Integration
|
|
||||||
|
|
||||||
### Browser Automation (Playwright)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Open example.com and describe what you see",
|
|
||||||
options: {
|
|
||||||
mcpServers: {
|
|
||||||
playwright: { command: "npx", args: ["@playwright/mcp@latest"] },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session Resumption
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
|
|
||||||
let sessionId: string | undefined;
|
|
||||||
|
|
||||||
// First query: capture the session ID
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Read the authentication module",
|
|
||||||
options: { allowedTools: ["Read", "Glob"] },
|
|
||||||
})) {
|
|
||||||
if (message.type === "system" && message.subtype === "init") {
|
|
||||||
sessionId = message.session_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resume with full context from the first query
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Now find all places that call it",
|
|
||||||
options: { resume: sessionId },
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session History
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { listSessions, getSessionMessages, getSessionInfo } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
// List past sessions (supports pagination via limit/offset)
|
|
||||||
const sessions = await listSessions();
|
|
||||||
for (const session of sessions) {
|
|
||||||
console.log(`Session ${session.sessionId} in ${session.cwd} (tag: ${session.tag})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get metadata for a single session
|
|
||||||
if (sessions.length > 0) {
|
|
||||||
const info = await getSessionInfo(sessions[0].sessionId);
|
|
||||||
console.log(`Created: ${info.createdAt}, Tag: ${info.tag}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve messages from the most recent session
|
|
||||||
if (sessions.length > 0) {
|
|
||||||
const messages = await getSessionMessages(sessions[0].sessionId, { limit: 50 });
|
|
||||||
for (const msg of messages) {
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session Mutations
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { renameSession, tagSession, forkSession } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const sessionId = "your-session-id";
|
|
||||||
|
|
||||||
// Rename a session
|
|
||||||
await renameSession(sessionId, "Refactoring auth module");
|
|
||||||
|
|
||||||
// Tag a session for filtering
|
|
||||||
await tagSession(sessionId, "experiment-v2");
|
|
||||||
|
|
||||||
// Clear a tag
|
|
||||||
await tagSession(sessionId, null);
|
|
||||||
|
|
||||||
// Fork a conversation to branch from a point
|
|
||||||
const { sessionId: forkedId } = await forkSession(sessionId);
|
|
||||||
console.log(`Forked session: ${forkedId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Custom System Prompt
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
||||||
|
|
||||||
for await (const message of query({
|
|
||||||
prompt: "Review this code",
|
|
||||||
options: {
|
|
||||||
allowedTools: ["Read", "Glob", "Grep"],
|
|
||||||
systemPrompt: `You are a senior code reviewer focused on:
|
|
||||||
1. Security vulnerabilities
|
|
||||||
2. Performance issues
|
|
||||||
3. Code maintainability
|
|
||||||
|
|
||||||
Always provide specific line numbers and suggestions for improvement.`,
|
|
||||||
},
|
|
||||||
})) {
|
|
||||||
if ("result" in message) console.log(message.result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
359
skills/claude-api/typescript/managed-agents/README.md
Normal file
359
skills/claude-api/typescript/managed-agents/README.md
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
# Managed Agents — TypeScript
|
||||||
|
|
||||||
|
> **Bindings not shown here:** This README covers the most common managed-agents flows for TypeScript. If you need a class, method, namespace, field, or behavior that isn't shown, WebFetch the TypeScript SDK repo **or the relevant docs page** from `shared/live-sources.md` rather than guess. Do not extrapolate from cURL shapes or another language's SDK.
|
||||||
|
|
||||||
|
> **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.create` and pass it to every subsequent `sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI is one convenient way to create agents and environments from version-controlled YAML — its URL is in `shared/live-sources.md`. The examples below show in-code creation for completeness; in production the create call belongs in setup, not in the request path.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @anthropic-ai/sdk
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client Initialization
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import Anthropic from "@anthropic-ai/sdk";
|
||||||
|
|
||||||
|
// Default (uses ANTHROPIC_API_KEY env var)
|
||||||
|
const client = new Anthropic();
|
||||||
|
|
||||||
|
// Explicit API key
|
||||||
|
const client = new Anthropic({ apiKey: "your-api-key" });
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Environment
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const environment = await client.beta.environments.create(
|
||||||
|
{
|
||||||
|
name: "my-dev-env",
|
||||||
|
config: {
|
||||||
|
type: "cloud",
|
||||||
|
networking: { type: "unrestricted" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
console.log(environment.id); // env_...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create an Agent (required first step)
|
||||||
|
|
||||||
|
> ⚠️ **There is no inline agent config.** `model`/`system`/`tools` live on the agent object, not the session. Always start with `agents.create()` — the session only takes `agent: { type: "agent", id: agent.id }`.
|
||||||
|
|
||||||
|
### Minimal
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 1. Create the agent (reusable, versioned)
|
||||||
|
const agent = await client.beta.agents.create(
|
||||||
|
{
|
||||||
|
name: "Coding Assistant",
|
||||||
|
model: "claude-opus-4-6",
|
||||||
|
tools: [{ type: "agent_toolset_20260401", default_config: { enabled: true } }],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Start a session
|
||||||
|
const session = await client.beta.sessions.create(
|
||||||
|
{
|
||||||
|
agent: { type: "agent", id: agent.id, version: agent.version },
|
||||||
|
environment_id: environment.id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
console.log(session.id, session.status);
|
||||||
|
```
|
||||||
|
|
||||||
|
### With system prompt and custom tools
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const agent = await client.beta.agents.create(
|
||||||
|
{
|
||||||
|
name: "Code Reviewer",
|
||||||
|
model: "claude-opus-4-6",
|
||||||
|
system: "You are a senior code reviewer.",
|
||||||
|
tools: [
|
||||||
|
{ type: "agent_toolset_20260401", default_config: { enabled: true } },
|
||||||
|
{
|
||||||
|
type: "custom",
|
||||||
|
name: "run_tests",
|
||||||
|
description: "Run the test suite",
|
||||||
|
input_schema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
test_path: { type: "string", description: "Path to test file" },
|
||||||
|
},
|
||||||
|
required: ["test_path"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const session = await client.beta.sessions.create(
|
||||||
|
{
|
||||||
|
agent: { type: "agent", id: agent.id, version: agent.version },
|
||||||
|
environment_id: environment.id,
|
||||||
|
title: "Code review session",
|
||||||
|
resources: [
|
||||||
|
{
|
||||||
|
type: "github_repository",
|
||||||
|
url: "https://github.com/owner/repo",
|
||||||
|
mount_path: "/workspace/repo",
|
||||||
|
authorization_token: process.env.GITHUB_TOKEN,
|
||||||
|
branch: "main",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Send a User Message
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
await client.beta.sessions.events.send(
|
||||||
|
session.id,
|
||||||
|
{
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
type: "user.message",
|
||||||
|
content: [{ type: "text", text: "Review the auth module" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **Stream-first:** Open the stream *before* (or concurrently with) sending the message. The stream only delivers events that occur after it opens — stream-after-send means early events arrive buffered in one batch. See [Steering Patterns](../../shared/managed-agents-events.md#steering-patterns).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stream Events (SSE)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Stream-first: open stream and send concurrently
|
||||||
|
const [events] = await Promise.all([
|
||||||
|
collectStream(session.id),
|
||||||
|
client.beta.sessions.events.send(
|
||||||
|
session.id,
|
||||||
|
{ events: [{ type: "user.message", content: [{ type: "text", text: "..." }] }] },
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Standalone stream iteration:
|
||||||
|
const stream = await client.beta.sessions.stream(
|
||||||
|
session.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
for await (const event of stream) {
|
||||||
|
switch (event.type) {
|
||||||
|
case "agent.message":
|
||||||
|
for (const block of event.content) {
|
||||||
|
if (block.type === "text") {
|
||||||
|
process.stdout.write(block.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "agent.custom_tool_use":
|
||||||
|
// Custom tool invocation — session is now idle
|
||||||
|
console.log(`\nCustom tool call: ${event.tool_name}`);
|
||||||
|
console.log(`Input: ${JSON.stringify(event.input)}`);
|
||||||
|
break;
|
||||||
|
case "session.status_idle":
|
||||||
|
console.log("\n--- Agent idle ---");
|
||||||
|
break;
|
||||||
|
case "session.status_terminated":
|
||||||
|
console.log("\n--- Session terminated ---");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Provide Custom Tool Result
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
await client.beta.sessions.events.send(
|
||||||
|
session.id,
|
||||||
|
{
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
type: "user.custom_tool_result",
|
||||||
|
custom_tool_use_id: "sevt_abc123",
|
||||||
|
content: [{ type: "text", text: "All 42 tests passed." }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Poll Events
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const events = await client.beta.sessions.events.list(
|
||||||
|
session.id,
|
||||||
|
);
|
||||||
|
for (const event of events.data) {
|
||||||
|
console.log(`${event.type}: ${event.id}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Full Streaming Loop with Custom Tools
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function runCustomTool(toolName: string, toolInput: unknown): string {
|
||||||
|
if (toolName === "run_tests") {
|
||||||
|
// Your tool implementation here
|
||||||
|
return "All tests passed.";
|
||||||
|
}
|
||||||
|
return `Unknown tool: ${toolName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runSession(client: Anthropic, sessionId: string) {
|
||||||
|
while (true) {
|
||||||
|
const stream = await client.beta.sessions.stream(
|
||||||
|
sessionId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const toolCalls: Array<{ custom_tool_use_id: string; tool_name: string; input: unknown }> = [];
|
||||||
|
|
||||||
|
for await (const event of stream) {
|
||||||
|
if (event.type === "agent.message") {
|
||||||
|
for (const block of event.content) {
|
||||||
|
if (block.type === "text") {
|
||||||
|
process.stdout.write(block.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event.type === "agent.custom_tool_use") {
|
||||||
|
toolCalls.push({
|
||||||
|
id: event.id,
|
||||||
|
tool_name: event.tool_name,
|
||||||
|
input: event.input,
|
||||||
|
});
|
||||||
|
} else if (event.type === "session.status_idle") {
|
||||||
|
break;
|
||||||
|
} else if (event.type === "session.status_terminated") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toolCalls.length === 0) break;
|
||||||
|
|
||||||
|
// Process custom tool calls
|
||||||
|
const results = toolCalls.map((call) => ({
|
||||||
|
type: "user.custom_tool_result" as const,
|
||||||
|
custom_tool_use_id: call.id,
|
||||||
|
content: [{ type: "text" as const, text: runCustomTool(call.tool_name, call.input) }],
|
||||||
|
}));
|
||||||
|
|
||||||
|
await client.beta.sessions.events.send(
|
||||||
|
sessionId,
|
||||||
|
{ events: results },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upload a File
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
const file = await client.beta.files.upload({
|
||||||
|
file: fs.createReadStream("data.csv"),
|
||||||
|
purpose: "agent",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use in a session
|
||||||
|
const session = await client.beta.sessions.create(
|
||||||
|
{
|
||||||
|
agent: { type: "agent", id: agent.id, version: agent.version },
|
||||||
|
environment_id: environment.id,
|
||||||
|
resources: [{ type: "file", file_id: file.id, mount_path: "/workspace/data.csv" }],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## List and Download Session Files
|
||||||
|
|
||||||
|
List files the agent wrote to `/mnt/session/outputs/` during a session, then download them.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
// List files associated with a session
|
||||||
|
const files = await client.beta.files.list({
|
||||||
|
scope: session.id,
|
||||||
|
});
|
||||||
|
for (const f of files.data) {
|
||||||
|
console.log(f.filename, f.size_bytes);
|
||||||
|
|
||||||
|
// Download and save to disk
|
||||||
|
const resp = await client.beta.files.download(f.id);
|
||||||
|
const buffer = Buffer.from(await resp.arrayBuffer());
|
||||||
|
fs.writeFileSync(f.filename, buffer);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 There's a brief indexing lag (~1–3s) between `session.status_idle` and output files appearing in `files.list`. Retry once or twice if the list is empty.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Management
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Get session details
|
||||||
|
const session = await client.beta.sessions.retrieve("sess_abc123");
|
||||||
|
console.log(session.status, session.usage);
|
||||||
|
|
||||||
|
// List sessions
|
||||||
|
const sessions = await client.beta.sessions.list();
|
||||||
|
|
||||||
|
// Delete a session
|
||||||
|
await client.beta.sessions.delete("sess_abc123");
|
||||||
|
|
||||||
|
// Archive a session
|
||||||
|
await client.beta.sessions.archive("sess_abc123");
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MCP Server Integration
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Agent declares MCP server (no auth here — auth goes in a vault)
|
||||||
|
const agent = await client.beta.agents.create({
|
||||||
|
name: "MCP Agent",
|
||||||
|
model: "claude-opus-4-6",
|
||||||
|
mcp_servers: [
|
||||||
|
{ type: "url", name: "my-tools", url: "https://my-mcp-server.example.com/sse" },
|
||||||
|
],
|
||||||
|
tools: [
|
||||||
|
{ type: "agent_toolset_20260401", default_config: { enabled: true } },
|
||||||
|
{ type: "mcp_toolset", mcp_server_name: "my-tools" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Session attaches vault(s) containing credentials for those MCP server URLs
|
||||||
|
const session = await client.beta.sessions.create({
|
||||||
|
agent: agent.id,
|
||||||
|
environment_id: environment.id,
|
||||||
|
vault_ids: [vault.id],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
See `shared/managed-agents-tools.md` §Vaults for creating vaults and adding credentials.
|
||||||
Reference in New Issue
Block a user