mozex/anthropic-php
Community-maintained PHP SDK for the Anthropic API. Send messages, stream responses, call tools, use extended thinking, web search, code execution, files, and batches. PSR-18 compatible, works with any HTTP client; Laravel wrapper available.
## Getting Started
### Minimal Setup in Laravel
1. **Install the package** (preferably with the Laravel wrapper for easier integration):
```bash
composer require mozex/anthropic-php mozex/anthropic-laravel
php artisan vendor:publish --provider="Mozex\Anthropic\AnthropicServiceProvider"
.env:
ANTHROPIC_API_KEY=your_api_key_here
ANTHROPIC_MODEL=claude-sonnet-4-6
use Mozex\Anthropic\Facades\Anthropic;
$response = Anthropic::messages()->create([
'model' => config('anthropic.model'),
'messages' => [
['role' => 'user', 'content' => 'Hello, how are you?'],
],
]);
return $response->content[0]->text;
Anthropic::) for quick access in controllers/views.ClientFake (see Testing for examples).messages()->create() for synchronous responses or createStreamed() for real-time UX.$stream = Anthropic::messages()->createStreamed([
'model' => config('anthropic.model'),
'messages' => $conversationHistory,
'stream' => true,
]);
foreach ($stream as $chunk) {
if ($chunk->type === 'content_block_delta' && $chunk->delta->type === 'text_delta') {
echo $chunk->delta->text; // Stream output to UI
}
}
conversations table) and hydrate it before each request.$response = Anthropic::messages()->create([
'tools' => [
['name' => 'fetch_user_data', 'description' => 'Fetch user data by ID', 'input_schema' => [...]],
],
'messages' => [['role' => 'user', 'content' => 'Get user #123 details']],
]);
// Handle tool calls in a Laravel middleware or service
if ($response->content[0]->type === 'tool_use') {
$userData = User::find($response->content[0]->input['user_id']);
return Anthropic::messages()->create([
'messages' => [
['role' => 'assistant', 'content' => 'User data: ' . json_encode($userData)],
],
]);
}
batches()->create() for parallelized requests (e.g., processing multiple user queries).$batch = Anthropic::batches()->create([
'model' => config('anthropic.model'),
'messages' => [
['role' => 'user', 'content' => 'Query 1'],
['role' => 'user', 'content' => 'Query 2'],
],
]);
// Upload
$file = Anthropic::files()->upload(['file' => fopen('contract.pdf', 'r')]);
// Reference in message
$response = Anthropic::messages()->create([
'betas' => ['files-api-2025-04-14'],
'messages' => [
['role' => 'user', 'content' => [
['type' => 'text', 'text' => 'Summarize this document.'],
['type' => 'document', 'source' => ['type' => 'file', 'file_id' => $file->id]],
]],
],
]);
$response->meta()->rateLimits to avoid throttling.try-catch with Anthropic\Exceptions\AnthropicException for API errors.Anthropic::tokenizer() to count tokens before sending requests.dispatch(new ProcessAnthropicBatch($requestData));
Beta Features:
betas in requests (e.g., ['files-api-2025-04-14']).config('anthropic.betas') or hardcode the latest beta from the Anthropic docs.files() calls, but messages require manual inclusion.Streaming Quirks:
$chunk->type and $chunk->delta->type before processing:
if ($chunk->type === 'content_block_delta' && $chunk->delta->type === 'text_delta') {
// Safe to process
}
\Log::debug('Stream chunk:', ['chunk' => $chunk->toArray()]);
Tool Use Loops:
max_turns parameter or validate tool responses in middleware:
if ($response->stop_reason === 'tool_use') {
$turnCount++;
if ($turnCount > 3) throw new \Exception('Max turns exceeded');
}
File API Limitations:
files()->download()) are only available for files generated by code execution or Skills, not user-uploaded files.storage/app/public) and reference them locally.Token Counting:
Anthropic::tokenizer()->countTokens($message) to validate inputs:
$tokenCount = Anthropic::tokenizer()->countTokens($request['messages']);
if ($tokenCount > config('anthropic.max_input_tokens')) {
throw new \Exception('Message too long');
}
ANTHROPIC_DEBUG=true in .env to log raw API requests/responses.ClientFake for unit tests:
$fakeClient = new \Anthropic\Testing\ClientFake([
\Anthropic\Responses\Messages\CreateResponse::fake([
'content' => [['type' => 'text', 'text' => 'Test response']],
]),
]);
$fakeClient->assertSent(\Anthropic\Resources\Messages::class, fn($method, $params) => ...);
$response->meta()->customHeaders for anthropic-rate-limit-* to debug throttling.Custom HTTP Clients:
'http_client' => \GuzzleHttp\Client::class,
'http_client_config' => ['timeout' => 30],
$client = \Anthropic\Anthropic::factory()
->withHttpClient(new \Symfony\Contracts\HttpClient\HttpClient())
->make();
Middleware for Tool Handling:
public function handle($request, Closure $next) {
$response = $next($request);
if ($response->content[0]->type === 'tool_use') {
$result = $this->executeTool($response->content[0]->input);
return $this->resumeConversation($result);
}
return $response;
}
Event Listeners:
event(new \Anthropic\Events\MessageSent($
How can I help you explore Laravel packages today?