|
5 | 5 | namespace PhpMcp\Server\Attributes; |
6 | 6 |
|
7 | 7 | use Attribute; |
8 | | -use PhpMcp\Server\Attributes\Schema\ArrayItems; |
9 | | -use PhpMcp\Server\Attributes\Schema\Property; |
10 | 8 |
|
11 | | -#[Attribute(Attribute::TARGET_PARAMETER)] |
| 9 | +/** |
| 10 | + * Defines a JSON Schema for a method's input or an individual parameter. |
| 11 | + * |
| 12 | + * When used at the method level, it describes an object schema where properties |
| 13 | + * correspond to the method's parameters. |
| 14 | + * |
| 15 | + * When used at the parameter level, it describes the schema for that specific parameter. |
| 16 | + * If 'type' is omitted at the parameter level, it will be inferred. |
| 17 | + */ |
| 18 | +#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER)] |
12 | 19 | class Schema |
13 | 20 | { |
14 | | - /** @var Property[] */ |
15 | | - protected array $properties = []; |
| 21 | + /** |
| 22 | + * The complete JSON schema array. |
| 23 | + * If provided, it takes precedence over individual properties like $type, $properties, etc. |
| 24 | + */ |
| 25 | + public ?array $definition = null; |
| 26 | + |
| 27 | + /** |
| 28 | + * Alternatively, provide individual top-level schema keywords. |
| 29 | + * These are used if $definition is null. |
| 30 | + */ |
| 31 | + public ?string $type = null; |
| 32 | + public ?string $description = null; |
| 33 | + public mixed $default = null; |
| 34 | + public ?array $enum = null; // list of allowed values |
| 35 | + public ?string $format = null; // e.g., 'email', 'date-time' |
| 36 | + |
| 37 | + // Constraints for string |
| 38 | + public ?int $minLength = null; |
| 39 | + public ?int $maxLength = null; |
| 40 | + public ?string $pattern = null; |
| 41 | + |
| 42 | + // Constraints for number/integer |
| 43 | + public int|float|null $minimum = null; |
| 44 | + public int|float|null $maximum = null; |
| 45 | + public ?bool $exclusiveMinimum = null; |
| 46 | + public ?bool $exclusiveMaximum = null; |
| 47 | + public int|float|null $multipleOf = null; |
| 48 | + |
| 49 | + // Constraints for array |
| 50 | + public ?array $items = null; // JSON schema for array items |
| 51 | + public ?int $minItems = null; |
| 52 | + public ?int $maxItems = null; |
| 53 | + public ?bool $uniqueItems = null; |
| 54 | + |
| 55 | + // Constraints for object (primarily used when Schema is on a method or an object-typed parameter) |
| 56 | + public ?array $properties = null; // [propertyName => [schema array], ...] |
| 57 | + public ?array $required = null; // [propertyName, ...] |
| 58 | + public bool|array|null $additionalProperties = null; // true, false, or a schema array |
16 | 59 |
|
17 | 60 | /** |
18 | | - * @param string|null $format String format (email, date-time, uri, etc.) |
19 | | - * @param int|null $minLength Minimum string length |
20 | | - * @param int|null $maxLength Maximum string length |
21 | | - * @param string|null $pattern Regular expression pattern |
22 | | - * @param int|float|null $minimum Minimum numeric value |
23 | | - * @param int|float|null $maximum Maximum numeric value |
24 | | - * @param bool|null $exclusiveMinimum Whether minimum is exclusive |
25 | | - * @param bool|null $exclusiveMaximum Whether maximum is exclusive |
26 | | - * @param int|float|null $multipleOf Value must be multiple of this number |
27 | | - * @param ArrayItems|null $items Schema for array items |
28 | | - * @param int|null $minItems Minimum array items |
29 | | - * @param int|null $maxItems Maximum array items |
30 | | - * @param bool|null $uniqueItems Whether array items must be unique |
31 | | - * @param Property[] $properties Properties for object validation |
32 | | - * @param string[]|null $required Required properties for objects |
33 | | - * @param bool|Schema|null $additionalProperties Whether additional properties are allowed |
34 | | - * @param mixed|null $enum List of allowed values |
35 | | - * @param mixed|null $default Default value |
| 61 | + * @param array|null $definition A complete JSON schema array. If provided, other parameters are ignored. |
| 62 | + * @param Type|null $type The JSON schema type. |
| 63 | + * @param string|null $description Description of the element. |
| 64 | + * @param array|null $enum Allowed enum values. |
| 65 | + * @param string|null $format String format (e.g., 'date-time', 'email'). |
| 66 | + * @param int|null $minLength Minimum length for strings. |
| 67 | + * @param int|null $maxLength Maximum length for strings. |
| 68 | + * @param string|null $pattern Regex pattern for strings. |
| 69 | + * @param int|float|null $minimum Minimum value for numbers/integers. |
| 70 | + * @param int|float|null $maximum Maximum value for numbers/integers. |
| 71 | + * @param bool|null $exclusiveMinimum Exclusive minimum. |
| 72 | + * @param bool|null $exclusiveMaximum Exclusive maximum. |
| 73 | + * @param int|float|null $multipleOf Must be a multiple of this value. |
| 74 | + * @param array|null $items JSON Schema for items if type is 'array'. |
| 75 | + * @param int|null $minItems Minimum items for an array. |
| 76 | + * @param int|null $maxItems Maximum items for an array. |
| 77 | + * @param bool|null $uniqueItems Whether array items must be unique. |
| 78 | + * @param array|null $properties Property definitions if type is 'object'. [name => schema_array]. |
| 79 | + * @param array|null $required List of required properties for an object. |
| 80 | + * @param bool|array|null $additionalProperties Policy for additional properties in an object. |
36 | 81 | */ |
37 | 82 | public function __construct( |
38 | | - public ?string $format = null, |
39 | | - public ?int $minLength = null, |
40 | | - public ?int $maxLength = null, |
41 | | - public ?string $pattern = null, |
42 | | - public int|float|null $minimum = null, |
43 | | - public int|float|null $maximum = null, |
44 | | - public ?bool $exclusiveMinimum = null, |
45 | | - public ?bool $exclusiveMaximum = null, |
46 | | - public int|float|null $multipleOf = null, |
47 | | - public ?ArrayItems $items = null, |
48 | | - public ?int $minItems = null, |
49 | | - public ?int $maxItems = null, |
50 | | - public ?bool $uniqueItems = null, |
51 | | - array $properties = [], |
52 | | - public ?array $required = null, |
53 | | - public bool|Schema|null $additionalProperties = null, |
54 | | - public mixed $enum = null, |
55 | | - public mixed $default = null, |
| 83 | + ?array $definition = null, |
| 84 | + ?string $type = null, |
| 85 | + ?string $description = null, |
| 86 | + ?array $enum = null, |
| 87 | + ?string $format = null, |
| 88 | + ?int $minLength = null, |
| 89 | + ?int $maxLength = null, |
| 90 | + ?string $pattern = null, |
| 91 | + int|float|null $minimum = null, |
| 92 | + int|float|null $maximum = null, |
| 93 | + ?bool $exclusiveMinimum = null, |
| 94 | + ?bool $exclusiveMaximum = null, |
| 95 | + int|float|null $multipleOf = null, |
| 96 | + ?array $items = null, |
| 97 | + ?int $minItems = null, |
| 98 | + ?int $maxItems = null, |
| 99 | + ?bool $uniqueItems = null, |
| 100 | + ?array $properties = null, |
| 101 | + ?array $required = null, |
| 102 | + bool|array|null $additionalProperties = null |
56 | 103 | ) { |
57 | | - $this->properties = $properties; |
| 104 | + if ($definition !== null) { |
| 105 | + $this->definition = $definition; |
| 106 | + } else { |
| 107 | + $this->type = $type; |
| 108 | + $this->description = $description; |
| 109 | + $this->enum = $enum; |
| 110 | + $this->format = $format; |
| 111 | + $this->minLength = $minLength; |
| 112 | + $this->maxLength = $maxLength; |
| 113 | + $this->pattern = $pattern; |
| 114 | + $this->minimum = $minimum; |
| 115 | + $this->maximum = $maximum; |
| 116 | + $this->exclusiveMinimum = $exclusiveMinimum; |
| 117 | + $this->exclusiveMaximum = $exclusiveMaximum; |
| 118 | + $this->multipleOf = $multipleOf; |
| 119 | + $this->items = $items; |
| 120 | + $this->minItems = $minItems; |
| 121 | + $this->maxItems = $maxItems; |
| 122 | + $this->uniqueItems = $uniqueItems; |
| 123 | + $this->properties = $properties; |
| 124 | + $this->required = $required; |
| 125 | + $this->additionalProperties = $additionalProperties; |
| 126 | + } |
58 | 127 | } |
59 | 128 |
|
60 | 129 | /** |
61 | | - * Convert to JSON Schema array |
| 130 | + * Converts the attribute's definition to a JSON schema array. |
62 | 131 | */ |
63 | 132 | public function toArray(): array |
64 | 133 | { |
65 | | - $schema = []; |
66 | | - |
67 | | - // String constraints |
68 | | - if ($this->format !== null) { |
69 | | - $schema['format'] = $this->format; |
70 | | - } |
71 | | - if ($this->minLength !== null) { |
72 | | - $schema['minLength'] = $this->minLength; |
73 | | - } |
74 | | - if ($this->maxLength !== null) { |
75 | | - $schema['maxLength'] = $this->maxLength; |
76 | | - } |
77 | | - if ($this->pattern !== null) { |
78 | | - $schema['pattern'] = $this->pattern; |
| 134 | + if ($this->definition !== null) { |
| 135 | + return [ |
| 136 | + 'definition' => $this->definition, |
| 137 | + ]; |
79 | 138 | } |
80 | 139 |
|
81 | | - // Numeric constraints |
82 | | - if ($this->minimum !== null) { |
83 | | - $schema['minimum'] = $this->minimum; |
84 | | - } |
85 | | - if ($this->maximum !== null) { |
86 | | - $schema['maximum'] = $this->maximum; |
87 | | - } |
88 | | - if ($this->exclusiveMinimum !== null) { |
89 | | - $schema['exclusiveMinimum'] = $this->exclusiveMinimum; |
90 | | - } |
91 | | - if ($this->exclusiveMaximum !== null) { |
92 | | - $schema['exclusiveMaximum'] = $this->exclusiveMaximum; |
93 | | - } |
94 | | - if ($this->multipleOf !== null) { |
95 | | - $schema['multipleOf'] = $this->multipleOf; |
96 | | - } |
97 | | - |
98 | | - // Array constraints |
99 | | - if ($this->items !== null) { |
100 | | - $schema['items'] = $this->items->toArray(); |
| 140 | + $schema = []; |
| 141 | + if ($this->type !== null) { |
| 142 | + $schema['type'] = $this->type; |
101 | 143 | } |
102 | | - if ($this->minItems !== null) { |
103 | | - $schema['minItems'] = $this->minItems; |
| 144 | + if ($this->description !== null) { |
| 145 | + $schema['description'] = $this->description; |
104 | 146 | } |
105 | | - if ($this->maxItems !== null) { |
106 | | - $schema['maxItems'] = $this->maxItems; |
| 147 | + if ($this->enum !== null) { |
| 148 | + $schema['enum'] = $this->enum; |
107 | 149 | } |
108 | | - if ($this->uniqueItems !== null) { |
109 | | - $schema['uniqueItems'] = $this->uniqueItems; |
| 150 | + if ($this->format !== null) { |
| 151 | + $schema['format'] = $this->format; |
110 | 152 | } |
111 | 153 |
|
112 | | - // Object constraints |
113 | | - if (!empty($this->properties)) { |
114 | | - $props = []; |
115 | | - foreach ($this->properties as $property) { |
116 | | - $props[$property->name] = $property->toArray(); |
117 | | - } |
118 | | - $schema['properties'] = $props; |
119 | | - } |
| 154 | + // String |
| 155 | + if ($this->minLength !== null) $schema['minLength'] = $this->minLength; |
| 156 | + if ($this->maxLength !== null) $schema['maxLength'] = $this->maxLength; |
| 157 | + if ($this->pattern !== null) $schema['pattern'] = $this->pattern; |
120 | 158 |
|
121 | | - if ($this->required !== null) { |
122 | | - $schema['required'] = $this->required; |
123 | | - } |
| 159 | + // Numeric |
| 160 | + if ($this->minimum !== null) $schema['minimum'] = $this->minimum; |
| 161 | + if ($this->maximum !== null) $schema['maximum'] = $this->maximum; |
| 162 | + if ($this->exclusiveMinimum !== null) $schema['exclusiveMinimum'] = $this->exclusiveMinimum; |
| 163 | + if ($this->exclusiveMaximum !== null) $schema['exclusiveMaximum'] = $this->exclusiveMaximum; |
| 164 | + if ($this->multipleOf !== null) $schema['multipleOf'] = $this->multipleOf; |
124 | 165 |
|
125 | | - if ($this->additionalProperties !== null) { |
126 | | - if ($this->additionalProperties instanceof self) { |
127 | | - $schema['additionalProperties'] = $this->additionalProperties->toArray(); |
128 | | - } else { |
129 | | - $schema['additionalProperties'] = $this->additionalProperties; |
130 | | - } |
131 | | - } |
| 166 | + // Array |
| 167 | + if ($this->items !== null) $schema['items'] = $this->items; |
| 168 | + if ($this->minItems !== null) $schema['minItems'] = $this->minItems; |
| 169 | + if ($this->maxItems !== null) $schema['maxItems'] = $this->maxItems; |
| 170 | + if ($this->uniqueItems !== null) $schema['uniqueItems'] = $this->uniqueItems; |
132 | 171 |
|
133 | | - // General constraints |
134 | | - if ($this->enum !== null) { |
135 | | - $schema['enum'] = $this->enum; |
136 | | - } |
137 | | - if ($this->default !== null) { |
138 | | - $schema['default'] = $this->default; |
139 | | - } |
| 172 | + // Object |
| 173 | + if ($this->properties !== null) $schema['properties'] = $this->properties; |
| 174 | + if ($this->required !== null) $schema['required'] = $this->required; |
| 175 | + if ($this->additionalProperties !== null) $schema['additionalProperties'] = $this->additionalProperties; |
140 | 176 |
|
141 | 177 | return $schema; |
142 | 178 | } |
|
0 commit comments