Skip to content

[Capability] An array shape with a nullable or union key rejects valid tool arguments #612

Open

Description

@Revinand

Describe the bug

A tool whose array parameter is documented with an array shape that has a nullable key, such as @param array{name: string, nickname: string|null} $user, rejects every call with -32602. SchemaGenerator looks for null anywhere in the type string to decide whether a parameter is nullable, so the null of the nickname key makes the whole parameter array|null and the schema loses the shape.

A ?string key does not hit that check, but the shape conversion maps ?string and union keys such as int|string to object, so those keys reject their values instead.

To Reproduce

final class Users
{
    /**
     * @param array{name: string, nickname: string|null} $user
     */
    public function greet(array $user): string
    {
        return 'Hello '.($user['nickname'] ?? $user['name']);
    }
}

Server::builder()
    ->setServerInfo('repro', '1.0.0')
    ->addTool([Users::class, 'greet'], 'greet')
    ->build()
    ->run(new StdioTransport());

tools/list gives user this schema:

{"type": ["array", "null"], "items": {}}

and tools/call with {"user": {"name": "Ada", "nickname": null}} returns:

-32602 Invalid parameters for tool 'greet': Property '/user': Invalid type. Expected `array|null`, but received `object`.

With array{name: string, age: ?int} or array{id: int|string}, the schema keeps the shape but types age and id as object, so "age": 5 and "id": 7 fail with "Expected object, but received integer".

Expected behavior

The schema keeps the shape and types each key from its own union, for example:

{"type": "object", "properties": {"name": {"type": "string"}, "nickname": {"type": ["string", "null"]}}, "required": ["name", "nickname"]}

and the call succeeds. Only a top-level |null, as in array{id: int}|null, makes the parameter nullable.

Environment

mcp/sdk v0.9.0 and main (57b45eb), PHP 8.5.

I have a fix with a test and will open a PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions