acc-create-responder
Generates ADR Responder classes for PHP 8.5. Creates HTTP response builders with PSR-7/PSR-17 support. Includes unit tests.
Best use case
acc-create-responder is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Generates ADR Responder classes for PHP 8.5. Creates HTTP response builders with PSR-7/PSR-17 support. Includes unit tests.
Teams using acc-create-responder should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/acc-create-responder/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How acc-create-responder Compares
| Feature / Agent | acc-create-responder | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Generates ADR Responder classes for PHP 8.5. Creates HTTP response builders with PSR-7/PSR-17 support. Includes unit tests.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Responder Generator
Generate ADR-compliant Responder classes for HTTP response building.
## Responder Characteristics
- **Response Building**: Creates complete HTTP Response (status, headers, body)
- **No Business Logic**: Only format and transform data
- **No Domain Access**: No repository or service calls
- **Error Mapping**: Maps domain errors to HTTP status codes
- **Content Type**: Sets appropriate Content-Type header
- **PSR Compliance**: Uses PSR-7 and PSR-17 interfaces
## Template
```php
<?php
declare(strict_types=1);
namespace Presentation\Api\{Context}\{Action};
use Application\{Context}\UseCase\{Action}\{Action}Result;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class {Action}Responder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond({Action}Result $result): ResponseInterface
{
if ($result->isFailure()) {
return $this->handleFailure($result);
}
return $this->success($result);
}
private function success({Action}Result $result): ResponseInterface
{
{successResponse}
}
private function handleFailure({Action}Result $result): ResponseInterface
{
return match ($result->failureReason()) {
{errorMapping}
default => $this->badRequest($result->errorMessage()),
};
}
private function json(array $data, int $status = 200): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json; charset=utf-8')
->withBody($body);
}
{helperMethods}
}
```
## Test Template
```php
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Api\{Context}\{Action};
use Application\{Context}\UseCase\{Action}\{Action}Result;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use Presentation\Api\{Context}\{Action}\{Action}Responder;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
#[Group('unit')]
#[CoversClass({Action}Responder::class)]
final class {Action}ResponderTest extends TestCase
{
private ResponseFactoryInterface $responseFactory;
private StreamFactoryInterface $streamFactory;
private {Action}Responder $responder;
protected function setUp(): void
{
$this->responseFactory = $this->createMock(ResponseFactoryInterface::class);
$this->streamFactory = $this->createMock(StreamFactoryInterface::class);
$this->responder = new {Action}Responder(
$this->responseFactory,
$this->streamFactory,
);
$this->setupMocks();
}
public function testSuccessReturns{ExpectedStatus}(): void
{
$result = {Action}Result::success({successData});
$response = $this->responder->respond($result);
self::assertSame({expectedStatusCode}, $response->getStatusCode());
}
{failureTests}
private function setupMocks(): void
{
$stream = $this->createMock(StreamInterface::class);
$this->streamFactory->method('createStream')->willReturn($stream);
$response = $this->createMock(ResponseInterface::class);
$response->method('withHeader')->willReturnSelf();
$response->method('withBody')->willReturnSelf();
$response->method('getStatusCode')->willReturnCallback(
fn () => $this->responseFactory->lastStatus ?? 200
);
$this->responseFactory->method('createResponse')->willReturnCallback(
function (int $status) use ($response) {
$this->responseFactory->lastStatus = $status;
$mock = clone $response;
$mock->method('getStatusCode')->willReturn($status);
return $mock;
}
);
}
}
```
## Responder Patterns
### Create Responder (201)
```php
<?php
declare(strict_types=1);
namespace Presentation\Api\User\Create;
use Application\User\UseCase\CreateUser\CreateUserResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class CreateUserResponder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond(CreateUserResult $result): ResponseInterface
{
if ($result->isFailure()) {
return match ($result->failureReason()) {
'email_exists' => $this->conflict('User with this email already exists'),
'invalid_email' => $this->badRequest('Invalid email format'),
default => $this->badRequest($result->errorMessage()),
};
}
return $this->created([
'id' => $result->userId(),
'email' => $result->email(),
]);
}
private function created(array $data): ResponseInterface
{
return $this->json($data, 201);
}
private function conflict(string $message): ResponseInterface
{
return $this->json(['error' => $message], 409);
}
private function badRequest(string $message): ResponseInterface
{
return $this->json(['error' => $message], 400);
}
private function json(array $data, int $status): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json')
->withBody($body);
}
}
```
### Get Responder (200/404)
```php
<?php
declare(strict_types=1);
namespace Presentation\Api\User\GetById;
use Application\User\UseCase\GetUserById\GetUserByIdResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class GetUserByIdResponder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond(GetUserByIdResult $result): ResponseInterface
{
if ($result->isNotFound()) {
return $this->notFound('User not found');
}
$user = $result->user();
return $this->json([
'id' => $user->id()->toString(),
'email' => $user->email()->value(),
'name' => $user->name(),
'created_at' => $user->createdAt()->format('c'),
]);
}
private function notFound(string $message): ResponseInterface
{
return $this->json(['error' => $message], 404);
}
private function json(array $data, int $status = 200): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json')
->withBody($body);
}
}
```
### List Responder with Pagination
```php
<?php
declare(strict_types=1);
namespace Presentation\Api\User\ListAll;
use Application\User\UseCase\ListUsers\ListUsersResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class ListUsersResponder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond(ListUsersResult $result): ResponseInterface
{
$users = array_map(
fn ($user) => [
'id' => $user->id()->toString(),
'email' => $user->email()->value(),
'name' => $user->name(),
],
$result->users()
);
return $this->json([
'data' => $users,
'meta' => [
'total' => $result->total(),
'page' => $result->page(),
'per_page' => $result->perPage(),
'total_pages' => $result->totalPages(),
],
]);
}
private function json(array $data, int $status = 200): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json')
->withBody($body);
}
}
```
### Delete Responder (204)
```php
<?php
declare(strict_types=1);
namespace Presentation\Api\User\Delete;
use Application\User\UseCase\DeleteUser\DeleteUserResult;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
final readonly class DeleteUserResponder
{
public function __construct(
private ResponseFactoryInterface $responseFactory,
private StreamFactoryInterface $streamFactory,
) {
}
public function respond(DeleteUserResult $result): ResponseInterface
{
if ($result->isNotFound()) {
return $this->notFound('User not found');
}
if ($result->isFailure()) {
return $this->badRequest($result->errorMessage());
}
return $this->noContent();
}
private function noContent(): ResponseInterface
{
return $this->responseFactory->createResponse(204);
}
private function notFound(string $message): ResponseInterface
{
return $this->json(['error' => $message], 404);
}
private function badRequest(string $message): ResponseInterface
{
return $this->json(['error' => $message], 400);
}
private function json(array $data, int $status): ResponseInterface
{
$body = $this->streamFactory->createStream(
json_encode($data, JSON_THROW_ON_ERROR)
);
return $this->responseFactory->createResponse($status)
->withHeader('Content-Type', 'application/json')
->withBody($body);
}
}
```
## HTTP Status Mapping
| Domain Condition | HTTP Status | Method |
|------------------|-------------|--------|
| Success (create) | 201 | `created()` |
| Success (read) | 200 | `json()` |
| Success (update) | 200 | `json()` |
| Success (delete) | 204 | `noContent()` |
| Not found | 404 | `notFound()` |
| Already exists | 409 | `conflict()` |
| Validation error | 422 | `unprocessableEntity()` |
| Invalid input | 400 | `badRequest()` |
| Unauthorized | 401 | `unauthorized()` |
| Forbidden | 403 | `forbidden()` |
## File Placement
| Component | Path |
|-----------|------|
| Responder | `src/Presentation/Api/{Context}/{Action}/{Action}Responder.php` |
| Interface | `src/Presentation/Shared/Responder/ResponderInterface.php` |
| Abstract | `src/Presentation/Shared/Responder/AbstractJsonResponder.php` |
| Test | `tests/Unit/Presentation/Api/{Context}/{Action}/{Action}ResponderTest.php` |
## Generation Instructions
When asked to create a Responder:
1. **Identify operation type** (create, read, update, delete)
2. **Determine success status** (201, 200, 204)
3. **List possible failures** and their HTTP codes
4. **Define response structure** (what data to return)
5. **Generate Responder class** with proper namespace
6. **Generate test** for each status code path
## Naming Conventions
| HTTP Method | Responder Name | Success Status |
|-------------|----------------|----------------|
| GET (single) | Get{Resource}ByIdResponder | 200 |
| GET (list) | List{Resource}sResponder | 200 |
| POST | Create{Resource}Responder | 201 |
| PUT | Update{Resource}Responder | 200 |
| PATCH | Patch{Resource}Responder | 200 |
| DELETE | Delete{Resource}Responder | 204 |
## References
For detailed patterns and examples:
- `references/templates.md` — Additional Responder templates
- `references/examples.md` — Real-world Responder examplesRelated Skills
agent-ops-create-python-project
Create a plan and issues for implementation of a production-ready Python project with proper structure, tooling, and best practices.
acc-create-use-case
Generates Application Use Cases for PHP 8.5. Creates orchestration services that coordinate domain objects, handle transactions, and dispatch events. Includes unit tests.
acc-create-state
Generates State pattern for PHP 8.5. Creates state machines with context, state interface, and concrete states for behavior changes. Includes unit tests.
acc-create-saga-pattern
Generates Saga pattern components for PHP 8.5. Creates Saga interfaces, steps, orchestrator, state management, and compensation logic with unit tests.
acc-create-retry-pattern
Generates Retry pattern for PHP 8.5. Creates resilience component with exponential backoff, jitter, and configurable retry strategies. Includes unit tests.
acc-create-repository
Generates DDD Repository interfaces and implementation stubs for PHP 8.5. Creates domain interfaces in Domain layer, implementation in Infrastructure.
acc-create-rate-limiter
Generates Rate Limiter pattern for PHP 8.5. Creates request throttling with token bucket, sliding window, and fixed window algorithms. Includes unit tests.
acc-create-psr6-cache
Generates PSR-6 Cache implementation for PHP 8.5. Creates CacheItemPoolInterface and CacheItemInterface implementations with TTL handling and deferred saves. Includes unit tests.
acc-create-psr3-logger
Generates PSR-3 Logger implementation for PHP 8.5. Creates LoggerInterface implementations with log levels, context interpolation, and LoggerAwareTrait usage. Includes unit tests.
acc-create-psr20-clock
Generates PSR-20 Clock implementation for PHP 8.5. Creates ClockInterface implementations including SystemClock, FrozenClock, and OffsetClock for time abstraction and testing. Includes unit tests.
acc-create-psr17-http-factory
Generates PSR-17 HTTP Factories implementation for PHP 8.5. Creates RequestFactoryInterface, ResponseFactoryInterface, StreamFactoryInterface, UriFactoryInterface, ServerRequestFactoryInterface, UploadedFileFactoryInterface. Includes unit tests.
acc-create-psr16-simple-cache
Generates PSR-16 Simple Cache implementation for PHP 8.5. Creates CacheInterface with get/set/delete operations and TTL handling. Includes unit tests.