You can intercept every HTTP request by passing middleware to the requestOptions. Middleware can be a callable with the signature callable(RequestInterface $request, callable $next): ResponseInterface or a class implementing Anthropic\Middleware with a handle() method.
Middleware runs once per HTTP attempt, inside the SDK's retry loop. If a middleware reads a response body, it must rewind it or return a replacement so the SDK can decode it. Middleware can be applied at the client level (all requests) or the request level (single call).
<?php
use Anthropic\Client;
use Anthropic\Middleware;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
// Example: A callable middleware
$logger = function (RequestInterface $request, callable $next): ResponseInterface {
error_log("-> {$request->getMethod()} {$request->getUri()}");
$response = $next($request);
error_log("<- {$response->getStatusCode()}");
return $response;
};
// Example: A class-based middleware
final class TracingMiddleware implements Middleware
{
public function handle(RequestInterface $request, \Closure $next): ResponseInterface
{
return $next($request->withHeader('x-trace-id', bin2hex(random_bytes(8))));
}
}
// Apply to client-level (all requests)
$client = new Client(requestOptions: ['middleware' => [$logger, new TracingMiddleware]]);
// Apply to request-level (this call only)
$client->messages->create(
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
model: 'claude-sonnet-5',
requestOptions: ['middleware' => [$logger]],
);