Skip to content

Commit aa3090d

Browse files
committed
feat: add docs for Class-Based Tool Handlers with Schema Mapping
1 parent a0a4e56 commit aa3090d

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed

README.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ servers. Create production-ready MCP servers in PHP with modern architecture, fl
1010
comprehensive feature support.
1111

1212
## Table of Contents
13+
1314
- [Requirements](#requirements)
1415
- [Installation](#installation)
1516
- [Framework Integration](#framework-integration)
@@ -809,6 +810,144 @@ final readonly class LoggingHandlerDecorator implements HandlerInterface
809810
}
810811
```
811812

813+
### Class-Based Tool Handlers with Schema Mapping
814+
815+
For more advanced use cases with automatic schema generation and request mapping, you can create handlers that work with
816+
DTO classes:
817+
818+
```php
819+
use Spiral\McpServer\SchemaMapperInterface;
820+
821+
final readonly class ClassHandler implements HandlerInterface
822+
{
823+
public function __construct(
824+
private FactoryInterface $factory,
825+
private SchemaMapperInterface $schemaMapper,
826+
private \ReflectionClass $class,
827+
private ?string $schemaClass = null,
828+
) {}
829+
830+
public function handle(
831+
array $arguments,
832+
Context $context,
833+
): mixed {
834+
/** @var callable $tool */
835+
$tool = $this->factory->make($this->class->getName());
836+
837+
if ($this->schemaClass === null) {
838+
return $tool();
839+
}
840+
841+
// Map raw arguments to strongly-typed DTO
842+
$object = $this->schemaMapper->toObject(
843+
json: \json_encode($arguments),
844+
class: $this->schemaClass,
845+
);
846+
847+
return $tool($object);
848+
}
849+
}
850+
```
851+
852+
**Schema Mapper Interface:**
853+
854+
```php
855+
interface SchemaMapperInterface
856+
{
857+
/**
858+
* Generate JSON schema from PHP class
859+
* @param class-string $class
860+
*/
861+
public function toJsonSchema(string $class): array;
862+
863+
/**
864+
* Map JSON to strongly-typed PHP object
865+
* @template T of object
866+
* @param class-string<T>|null $class
867+
* @return T
868+
*/
869+
public function toObject(string $json, ?string $class = null): object;
870+
}
871+
```
872+
873+
**Implementation with Valinor and spiral/json-schema-generator:**
874+
875+
```php
876+
use CuyZ\Valinor\Mapper\TreeMapper;
877+
use Spiral\JsonSchemaGenerator\Generator as JsonSchemaGenerator;
878+
879+
final readonly class SchemaMapper implements SchemaMapperInterface
880+
{
881+
public function __construct(
882+
private JsonSchemaGenerator $generator,
883+
private TreeMapper $mapper,
884+
) {}
885+
886+
public function toJsonSchema(string $class): array
887+
{
888+
if (\json_validate($class)) {
889+
return \json_decode($class, associative: true);
890+
}
891+
892+
if (\class_exists($class)) {
893+
return $this->generator->generate($class)->jsonSerialize();
894+
}
895+
896+
throw new \InvalidArgumentException("Invalid class or JSON schema: {$class}");
897+
}
898+
899+
public function toObject(string $json, ?string $class = null): object
900+
{
901+
if ($class === null) {
902+
return \json_decode($json, associative: false);
903+
}
904+
905+
return $this->mapper->map($class, \json_decode($json, associative: true));
906+
}
907+
}
908+
```
909+
910+
**Usage Example:**
911+
912+
```php
913+
// DTO class
914+
final readonly class CalculatorRequest
915+
{
916+
public function __construct(
917+
public string $operation,
918+
public float $a,
919+
public float $b,
920+
) {}
921+
}
922+
923+
// Tool implementation
924+
final readonly class Calculator
925+
{
926+
public function __invoke(CalculatorRequest $request): float
927+
{
928+
return match($request->operation) {
929+
'add' => $request->a + $request->b,
930+
'subtract' => $request->a - $request->b,
931+
'multiply' => $request->a * $request->b,
932+
'divide' => $request->b !== 0.0 ? $request->a / $request->b
933+
: throw new InvalidArgumentException('Division by zero'),
934+
default => throw new InvalidArgumentException('Unknown operation')
935+
};
936+
}
937+
}
938+
939+
// Registration with automatic schema generation
940+
$schema = $schemaMapper->toJsonSchema(CalculatorRequest::class);
941+
$tool = Tool::make('calculator', 'Performs calculations', $schema);
942+
$handler = new ClassHandler(
943+
$factory,
944+
$schemaMapper,
945+
new \ReflectionClass(Calculator::class),
946+
CalculatorRequest::class,
947+
);
948+
$registry->registerTool($tool, $handler);
949+
```
950+
812951
## Contributing
813952

814953
We welcome contributions! Please follow these guidelines:

0 commit comments

Comments
 (0)