Confirm your framework version before you install. As of 1.9.0 the package requires laravel/framework ^12.69|^13.30. That is not a stylistic bump: Laravel 11 went end-of-life for security fixes on March 12, 2026, and three advisories (GHSA-5vg9-5847-vvmq, GHSA-crmm-hgp2-wgrp, GHSA-jh5r-qr3c-85q8) affect every L11 release. The new floors (12.69.0 / 13.30.0) are the first versions containing all three fixes.
# On Laravel 12.69+ or 13.30+
composer require mozex/anthropic-laravel
# On Laravel 11 this silently resolves to 1.8.2 — check what you actually got
composer show mozex/anthropic-laravel
Then publish and configure:
php artisan vendor:publish --tag="anthropic-config" # confirm tag in the service provider
ANTHROPIC_API_KEY=sk-ant-...
# ANTHROPIC_BASE_URL=... # only if you proxy through a gateway
Where to look first, in order: config/anthropic.php (what is actually configurable), the package service provider (what is bound into the container and under which key), the README usage block, then the changelog for anything the README lags behind.
First use case: a single non-streaming message from php artisan tinker or a throwaway route, printing the text content blocks. Do this before wiring anything into a controller — it proves the API key, the model name, and outbound network egress (a common silent failure inside containers) in about two minutes.
$response = Anthropic::messages()->create([
'model' => config('anthropic.model'),
'max_tokens' => 512,
'messages' => [['role' => 'user', 'content' => 'Say hello in one sentence.']],
]);
Resolve from the container; don't new the client. Use the facade for scripts and one-offs, but constructor-inject the underlying client (or your own wrapper interface) in application code. This is the single decision that makes the rest of these patterns possible.
Put every call behind one application service. Controllers, jobs, and commands should call App\Services\Claude — not the vendor client. That class owns timeouts, retry policy, model selection, logging, and token accounting. When the SDK moves, you edit one file.
final class Claude
{
public function __construct(private AnthropicClient $client) {}
public function complete(string $system, array $messages, int $maxTokens = 1024): string
{
return retry(3, function () use ($system, $messages, $maxTokens) {
$res = $this->client->messages()->create([
'model' => config('anthropic.model'),
'system' => $system,
'max_tokens' => $maxTokens,
'messages' => $messages,
]);
return collect($res['content'] ?? [])
->where('type', 'text')
->pluck('text')
->implode("\n");
}, 500);
}
}
Keep prompts out of PHP literals. Store system prompts in lang/ files, config, or dedicated prompt classes. Prompts are content and get edited far more often than code; burying them in a controller guarantees drift and makes them untestable.
Streaming belongs behind a streamed response, not a normal one. For chat UIs, return response()->stream() (or the package's stream helper) with text/event-stream, and disable output buffering. Anything that buffers — Debugbar, some middleware, nginx proxy_buffering — turns streaming back into a slow blocking request and hides the bug until production.
Tool use / function calling: loop, don't recurse. Declare tool schemas, dispatch each tool_use block to a real Laravel action or service, append tool_result blocks, and re-call until stop_reason is no longer tool_use. Put a hard iteration cap (5–8) on the loop; a model that keeps calling an erroring tool will happily burn your budget.
Structured output: validate, don't trust. Ask for JSON, then run it through Validator::make() or hydrate a DTO (spatie/laravel-data). On failure, retry once with the validation errors appended to the prompt. Never json_decode() straight into business logic.
Push anything slow or large into a queue. Requests over a few seconds should be jobs with explicit $timeout, $tries, and $backoff. Add ShouldBeUnique or a RateLimited middleware for per-user throttling — spend control is an application concern, not something the package gives you.
Testing: bind a fake implementation of your Claude interface in the container for feature tests, and unit-test prompt assembly as pure string/array assertions. If you must test one level lower, fake at the HTTP layer. No test suite should need a live key.
--ignore-platform-reqs or by widening the package constraint; upgrade the framework to 12.69+ / 13.30+ first, then the package, then re-run your suite. The Laravel major bump will surface unrelated breaking changes (middleware, exception handling) that you want attributed correctly.composer update will quietly resolve differently per job. Pin mozex/anthropic-laravel:^1.8.2 where you genuinely need L11 compatibility, and ^1.9 only on the L12.69+/L13.30+ jobs — otherwise your green build doesn't mean what you think.config('anthropic.api_key') inside the config file is fine; env() calls sprinkled through your own code break the moment php artisan config:cache runs. This is the most common "works locally, broken in prod" failure with this class of package.retry() helper stacks on top of it. A 3× SDK retry wrapped in a 3× app retry is nine billable attempts.How can I help you explore Laravel packages today?