@@ -10,6 +10,7 @@ servers. Create production-ready MCP servers in PHP with modern architecture, fl
1010comprehensive 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
814953We welcome contributions! Please follow these guidelines:
0 commit comments