chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
# Claude API — PHP
|
||||
|
||||
> **Note:** The PHP SDK is the official Anthropic SDK for PHP. A beta tool runner is available via `$client->beta->messages->toolRunner()`. Structured output helpers are supported via `StructuredOutputModel` classes. Agent SDK is not available. Bedrock, Vertex AI, and Foundry clients are supported.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
composer require "anthropic-ai/sdk"
|
||||
```
|
||||
|
||||
## Client Initialization
|
||||
|
||||
```php
|
||||
use Anthropic\Client;
|
||||
|
||||
// Using API key from environment variable
|
||||
$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));
|
||||
```
|
||||
|
||||
### Amazon Bedrock
|
||||
|
||||
```php
|
||||
use Anthropic\Bedrock;
|
||||
|
||||
// Constructor is private — use the static factory. Reads AWS credentials from env.
|
||||
$client = Bedrock\Client::fromEnvironment(region: 'us-east-1');
|
||||
```
|
||||
|
||||
### Google Vertex AI
|
||||
|
||||
```php
|
||||
use Anthropic\Vertex;
|
||||
|
||||
// Constructor is private. Parameter is `location`, not `region`.
|
||||
$client = Vertex\Client::fromEnvironment(
|
||||
location: 'us-east5',
|
||||
projectId: 'my-project-id',
|
||||
);
|
||||
```
|
||||
|
||||
### Anthropic Foundry
|
||||
|
||||
```php
|
||||
use Anthropic\Foundry;
|
||||
|
||||
// Constructor is private. baseUrl or resource is required.
|
||||
$client = Foundry\Client::withCredentials(
|
||||
authToken: getenv('ANTHROPIC_FOUNDRY_AUTH_TOKEN'),
|
||||
baseUrl: 'https://<resource>.services.ai.azure.com/anthropic',
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Basic Message Request
|
||||
|
||||
```php
|
||||
$message = $client->messages->create(
|
||||
model: 'claude-opus-4-7',
|
||||
maxTokens: 16000,
|
||||
messages: [
|
||||
['role' => 'user', 'content' => 'What is the capital of France?'],
|
||||
],
|
||||
);
|
||||
|
||||
// content is an array of polymorphic blocks (TextBlock, ToolUseBlock,
|
||||
// ThinkingBlock). Accessing ->text on content[0] without checking the block
|
||||
// type will throw if the first block is not a TextBlock (e.g., when extended
|
||||
// thinking is enabled and a ThinkingBlock comes first). Always guard:
|
||||
foreach ($message->content as $block) {
|
||||
if ($block->type === 'text') {
|
||||
echo $block->text;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you only want the first text block:
|
||||
|
||||
```php
|
||||
foreach ($message->content as $block) {
|
||||
if ($block->type === 'text') {
|
||||
echo $block->text;
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Streaming
|
||||
|
||||
> **Requires SDK v0.5.0+.** v0.4.0 and earlier used a single `$params` array; calling with named parameters throws `Unknown named parameter $model`. Upgrade: `composer require "anthropic-ai/sdk:^0.7"`
|
||||
|
||||
```php
|
||||
use Anthropic\Messages\RawContentBlockDeltaEvent;
|
||||
use Anthropic\Messages\TextDelta;
|
||||
|
||||
$stream = $client->messages->createStream(
|
||||
model: 'claude-opus-4-7',
|
||||
maxTokens: 64000,
|
||||
messages: [
|
||||
['role' => 'user', 'content' => 'Write a haiku'],
|
||||
],
|
||||
);
|
||||
|
||||
foreach ($stream as $event) {
|
||||
if ($event instanceof RawContentBlockDeltaEvent && $event->delta instanceof TextDelta) {
|
||||
echo $event->delta->text;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tool Use
|
||||
|
||||
### Tool Runner (Beta)
|
||||
|
||||
**Beta:** The PHP SDK provides a tool runner via `$client->beta->messages->toolRunner()`. Define tools with `BetaRunnableTool` — a definition array plus a `run` closure:
|
||||
|
||||
```php
|
||||
use Anthropic\Lib\Tools\BetaRunnableTool;
|
||||
|
||||
$weatherTool = new BetaRunnableTool(
|
||||
definition: [
|
||||
'name' => 'get_weather',
|
||||
'description' => 'Get the current weather for a location.',
|
||||
'input_schema' => [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'location' => ['type' => 'string', 'description' => 'City and state'],
|
||||
],
|
||||
'required' => ['location'],
|
||||
],
|
||||
],
|
||||
run: function (array $input): string {
|
||||
return "The weather in {$input['location']} is sunny and 72°F.";
|
||||
},
|
||||
);
|
||||
|
||||
$runner = $client->beta->messages->toolRunner(
|
||||
maxTokens: 16000,
|
||||
messages: [['role' => 'user', 'content' => 'What is the weather in Paris?']],
|
||||
model: 'claude-opus-4-7',
|
||||
tools: [$weatherTool],
|
||||
);
|
||||
|
||||
foreach ($runner as $message) {
|
||||
foreach ($message->content as $block) {
|
||||
if ($block->type === 'text') {
|
||||
echo $block->text;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Manual Loop
|
||||
|
||||
Tools are passed as arrays. **The SDK uses camelCase keys** (`inputSchema`, `toolUseID`, `stopReason`) and auto-maps to the API's snake_case on the wire — since v0.5.0. See [shared tool use concepts](../shared/tool-use-concepts.md) for the loop pattern.
|
||||
|
||||
```php
|
||||
use Anthropic\Messages\ToolUseBlock;
|
||||
|
||||
$tools = [
|
||||
[
|
||||
'name' => 'get_weather',
|
||||
'description' => 'Get the current weather in a given location',
|
||||
'inputSchema' => [ // camelCase, not input_schema
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'location' => ['type' => 'string', 'description' => 'City and state'],
|
||||
],
|
||||
'required' => ['location'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$messages = [['role' => 'user', 'content' => 'What is the weather in SF?']];
|
||||
|
||||
$response = $client->messages->create(
|
||||
model: 'claude-opus-4-7',
|
||||
maxTokens: 16000,
|
||||
tools: $tools,
|
||||
messages: $messages,
|
||||
);
|
||||
|
||||
while ($response->stopReason === 'tool_use') { // camelCase property
|
||||
$toolResults = [];
|
||||
foreach ($response->content as $block) {
|
||||
if ($block instanceof ToolUseBlock) {
|
||||
// $block->name : string — tool name to dispatch on
|
||||
// $block->input : array<string,mixed> — parsed JSON input
|
||||
// $block->id : string — pass back as toolUseID
|
||||
$result = executeYourTool($block->name, $block->input);
|
||||
$toolResults[] = [
|
||||
'type' => 'tool_result',
|
||||
'toolUseID' => $block->id, // camelCase, not tool_use_id
|
||||
'content' => $result,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Append assistant turn + user turn with tool results
|
||||
$messages[] = ['role' => 'assistant', 'content' => $response->content];
|
||||
$messages[] = ['role' => 'user', 'content' => $toolResults];
|
||||
|
||||
$response = $client->messages->create(
|
||||
model: 'claude-opus-4-7',
|
||||
maxTokens: 16000,
|
||||
tools: $tools,
|
||||
messages: $messages,
|
||||
);
|
||||
}
|
||||
|
||||
// Final text response
|
||||
foreach ($response->content as $block) {
|
||||
if ($block->type === 'text') {
|
||||
echo $block->text;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`$block->type === 'tool_use'` also works; `instanceof ToolUseBlock` narrows for PHPStan.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Extended Thinking
|
||||
|
||||
**Adaptive thinking is the recommended mode for Claude 4.6+ models.** Claude decides dynamically when and how much to think.
|
||||
|
||||
```php
|
||||
use Anthropic\Messages\ThinkingBlock;
|
||||
|
||||
$message = $client->messages->create(
|
||||
model: 'claude-opus-4-7',
|
||||
maxTokens: 16000,
|
||||
thinking: ['type' => 'adaptive'],
|
||||
messages: [
|
||||
['role' => 'user', 'content' => 'Solve: 27 * 453'],
|
||||
],
|
||||
);
|
||||
|
||||
// ThinkingBlock(s) precede TextBlock in content
|
||||
foreach ($message->content as $block) {
|
||||
if ($block instanceof ThinkingBlock) {
|
||||
echo "Thinking:\n{$block->thinking}\n\n";
|
||||
// $block->signature is an opaque string — preserve verbatim if
|
||||
// passing thinking blocks back in multi-turn conversations
|
||||
} elseif ($block->type === 'text') {
|
||||
echo "Answer: {$block->text}\n";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Deprecated:** `['type' => 'enabled', 'budgetTokens' => N]` (fixed-budget extended thinking) still works on Claude 4.6 but is deprecated. Use adaptive thinking above.
|
||||
|
||||
`$block->type === 'thinking'` also works for the check; `instanceof` narrows for PHPStan.
|
||||
|
||||
---
|
||||
|
||||
## Prompt Caching
|
||||
|
||||
`system:` takes an array of text blocks; set `cacheControl` on the last block. Array-shape syntax (camelCase keys) is idiomatic. For placement patterns and the silent-invalidator audit checklist, see `shared/prompt-caching.md`.
|
||||
|
||||
```php
|
||||
$message = $client->messages->create(
|
||||
model: 'claude-opus-4-7',
|
||||
maxTokens: 16000,
|
||||
system: [
|
||||
['type' => 'text', 'text' => $longSystemPrompt, 'cacheControl' => ['type' => 'ephemeral']],
|
||||
],
|
||||
messages: [['role' => 'user', 'content' => 'Summarize the key points']],
|
||||
);
|
||||
```
|
||||
|
||||
For 1-hour TTL: `'cacheControl' => ['type' => 'ephemeral', 'ttl' => '1h']`. There's also a top-level `cacheControl:` on `messages->create(...)` that auto-places on the last cacheable block.
|
||||
|
||||
Verify hits via `$message->usage->cacheCreationInputTokens` / `$message->usage->cacheReadInputTokens`.
|
||||
|
||||
---
|
||||
|
||||
## Structured Outputs
|
||||
|
||||
### Using StructuredOutputModel (Recommended)
|
||||
|
||||
Define a PHP class implementing `StructuredOutputModel` and pass it as `outputConfig`:
|
||||
|
||||
```php
|
||||
use Anthropic\Lib\Contracts\StructuredOutputModel;
|
||||
use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
|
||||
use Anthropic\Lib\Attributes\Constrained;
|
||||
|
||||
class Person implements StructuredOutputModel
|
||||
{
|
||||
use StructuredOutputModelTrait;
|
||||
|
||||
#[Constrained(description: 'Full name')]
|
||||
public string $name;
|
||||
|
||||
public int $age;
|
||||
|
||||
public ?string $email = null; // nullable = optional field
|
||||
}
|
||||
|
||||
$message = $client->messages->create(
|
||||
model: 'claude-opus-4-7',
|
||||
maxTokens: 16000,
|
||||
messages: [['role' => 'user', 'content' => 'Generate a profile for Alice, age 30']],
|
||||
outputConfig: ['format' => Person::class],
|
||||
);
|
||||
|
||||
$person = $message->parsedOutput(); // Person instance
|
||||
echo $person->name;
|
||||
```
|
||||
|
||||
Types are inferred from PHP type hints. Use `#[Constrained(description: '...')]` to add descriptions. Nullable properties (`?string`) become optional fields.
|
||||
|
||||
### Raw Schema
|
||||
|
||||
```php
|
||||
$message = $client->messages->create(
|
||||
model: 'claude-opus-4-7',
|
||||
maxTokens: 16000,
|
||||
messages: [['role' => 'user', 'content' => 'Extract: John (john@co.com), Enterprise plan']],
|
||||
outputConfig: [
|
||||
'format' => [
|
||||
'type' => 'json_schema',
|
||||
'schema' => [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'name' => ['type' => 'string'],
|
||||
'email' => ['type' => 'string'],
|
||||
'plan' => ['type' => 'string'],
|
||||
],
|
||||
'required' => ['name', 'email', 'plan'],
|
||||
'additionalProperties' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
// First text block contains valid JSON
|
||||
foreach ($message->content as $block) {
|
||||
if ($block->type === 'text') {
|
||||
$data = json_decode($block->text, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Beta Features & Server-Side Tools
|
||||
|
||||
**`betas:` is NOT a param on `$client->messages->create()`** — it only exists on the beta namespace. Use it for features that need an explicit opt-in header:
|
||||
|
||||
```php
|
||||
use Anthropic\Beta\Messages\BetaRequestMCPServerURLDefinition;
|
||||
|
||||
$response = $client->beta->messages->create(
|
||||
model: 'claude-opus-4-7',
|
||||
maxTokens: 16000,
|
||||
mcpServers: [
|
||||
BetaRequestMCPServerURLDefinition::with(
|
||||
name: 'my-server',
|
||||
url: 'https://example.com/mcp',
|
||||
),
|
||||
],
|
||||
betas: ['mcp-client-2025-11-20'], // only valid on ->beta->messages
|
||||
messages: [['role' => 'user', 'content' => 'Use the MCP tools']],
|
||||
);
|
||||
```
|
||||
|
||||
**Server-side tools** (bash, web_search, text_editor, code_execution) are GA and work on both paths — `Anthropic\Messages\ToolBash20250124` / `WebSearchTool20260209` / `ToolTextEditor20250728` / `CodeExecutionTool20260120` for non-beta, `Anthropic\Beta\Messages\BetaToolBash20250124` / `BetaWebSearchTool20260209` / `BetaToolTextEditor20250728` / `BetaCodeExecutionTool20260120` for beta. No `betas:` header needed for these.
|
||||
@@ -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-7',
|
||||
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-7',
|
||||
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',
|
||||
);
|
||||
```
|
||||
Reference in New Issue
Block a user