Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ All notable changes to `mcp/sdk` will be documented in this file.
* Fix an `int` parameter receiving a wrong value: a number beyond the integer range wrapped around, also past the schema's `minimum` or `maximum`, a numeric string beyond it became `PHP_INT_MAX` or `PHP_INT_MIN`, and `"--5"` became `0`. They are refused now.
* Fix a nullable parameter with a default (e.g. `?string $category = 'books'`) receiving the default when the client sends `null`.
* Fix DocBlock types producing wrong schemas, so valid arguments were rejected with `-32602`: PHPStan/Psalm pseudo-types and literal types (`non-empty-string`, `positive-int`, `'asc'|'desc'`, `array<array-key, T>`) get the JSON Schema type of their base type, nullable and union keys of an array shape (`array{nickname: string|null}`) and union elements of a generic (`array<string|null>`) get their own types instead of `object`, and a `null` inside them, or in a class name, no longer makes the parameter nullable.
* Fix a variadic enum parameter (e.g. `Status ...$statuses`) declaring its items as `{"type": "object"}`, so every call was rejected with `-32602`: the items list the enum's values, as for a single enum parameter.
* Fix tool name validation to follow the spec (1 to 128 characters of `A-Z`, `a-z`, `0-9`, `_`, `-` and `.`): names of 65 to 128 characters no longer log a warning, names with a `/` or a trailing newline do.
* Fix `content`, `messages` and `contents` being sent as a JSON object when the handler returned a non-list array, e.g. from `array_filter()`, and named keys in nested mixed arrays overwriting each other.
* Fix a scalar `structuredContent` on `2026-07-28` going out as raw text in `content`: `'sunny'` is sent as the JSON `"sunny"`, as SEP-2106 requires.
Expand Down
6 changes: 5 additions & 1 deletion src/Server/Capability/Discovery/SchemaGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,14 @@ private function buildVariadicParameterSchema(array $paramInfo): array

// If no items specified by Schema attribute, infer from type
if (!isset($paramSchema['items'])) {
// Reflection gives a variadic its element type, so an enum constrains each item.
$enumItems = $this->applyEnumConstraints([], $paramInfo);
$itemJsonTypes = $this->mapPhpTypeToJsonSchemaType($paramInfo['type_string']);
$nonNullItemTypes = array_filter($itemJsonTypes, static fn ($t) => 'null' !== $t);

if (1 === \count($nonNullItemTypes)) {
if ([] !== $enumItems) {
$paramSchema['items'] = $enumItems;
} elseif (1 === \count($nonNullItemTypes)) {
$paramSchema['items'] = ['type' => $nonNullItemTypes[0]];
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,14 @@ public function untypedVariadic(...$values): void
{
}

public function variadicBackedEnum(BackedStringEnum ...$options): void
{
}

public function variadicUnitEnum(UnitEnum ...$answers): void
{
}

/**
* Variadic with Schema constraints.
*
Expand Down
10 changes: 10 additions & 0 deletions tests/Unit/Server/Capability/Discovery/SchemaGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,16 @@ public function testRepresentsVariadicStringParametersAsArrayOfStrings(): void
$this->assertArrayNotHasKey('required', $schema);
}

public function testRepresentsVariadicEnumParametersAsArrayOfEnumValues(): void
{
$backed = $this->schemaGenerator->generate(new \ReflectionMethod(SchemaGeneratorFixture::class, 'variadicBackedEnum'));
$this->assertEquals(['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['A', 'B']]], $backed['properties']['options']);
$this->assertSame([], (new SchemaValidator())->validateAgainstJsonSchema(['options' => ['A', 'B']], $backed));

$unit = $this->schemaGenerator->generate(new \ReflectionMethod(SchemaGeneratorFixture::class, 'variadicUnitEnum'));
$this->assertEquals(['type' => 'array', 'items' => ['type' => 'string', 'enum' => ['Yes', 'No']]], $unit['properties']['answers']);
}

public function testAppliesItemConstraintsToVariadicParameters(): void
{
$method = new \ReflectionMethod(SchemaGeneratorFixture::class, 'variadicWithConstraints');
Expand Down
Loading