# 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" "guzzlehttp/guzzle:^7" ``` ## 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}\ "; // 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: '{{OPUS_ID}}', 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}\ "; ``` ### 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}\ "; // List all versions foreach ($client->beta->agents->versions->list($agent->id)->pagingEachItem() as $version) { echo "Version {$version->version}: {$version->updatedAt->format(DateTimeInterface::ATOM)}\ "; } // Archive the agent $archived = $client->beta->agents->archive($agent->id); echo "Archived at: {$archived->archivedAt->format(DateTimeInterface::ATOM)}\ "; ``` --- ## 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("\ [Using tool: {$event->name}]\ "), 'session.error' => printf("\ [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}\ "; } ``` --- ## 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}\ "; // 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}\ "; // "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}\ "; } // Detach a resource $client->beta->sessions->resources->delete($resource->id, sessionID: $session->id); ``` --- ## List and Download Session Files ```php $files = $client->beta->files->list( scopeID: 'sesn_abc123', betas: ['managed-agents-2026-04-01'], ); $content = $client->beta->files->download($files->data[0]->id); file_put_contents('output.txt', $content); ``` --- ## 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: '{{OPUS_ID}}', 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 . "\ "; // "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', 'mount_path' => '/workspace/repo', 'authorization_token' => 'ghp_your_github_token', ], ], ); ``` Multiple repositories on the same session: ```php $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: ```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', ); ```