From cb7faca3ee9129a62d562e74bc178bbadf964c90 Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Mon, 2 Nov 2026 22:52:38 +0100 Subject: [PATCH] [Server] Fix `"0"` prefix offering all completions `empty("0")` is true, so `EnumCompletionProvider` and `ListCompletionProvider` returned every value for that prefix. --- CHANGELOG.md | 1 + src/Capability/Completion/EnumCompletionProvider.php | 2 +- src/Capability/Completion/ListCompletionProvider.php | 2 +- .../Completion/EnumCompletionProviderTest.php | 11 +++++++++-- .../Completion/ListCompletionProviderTest.php | 7 +++++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c8beb09..74954a863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to `mcp/sdk` will be documented in this file. * Fix `InMemorySessionStore::exists()` reporting a session past its TTL as existing, while `read()` already treated it as gone. * Fix `Psr16NotificationBus` subscribers missing the notifications published after the bus stayed quiet for longer than its TTL: the sequence counter no longer expires, and a subscriber whose cursor is ahead of a lost counter reads what the bus holds. * Fix `Session::save()` wiping the other keys of a session that the store failed to read, e.g. when `Psr16SessionStore` hits a cache error: it now returns `false` instead of writing the changed keys alone. +* Fix `EnumCompletionProvider` and `ListCompletionProvider` offering all values for the prefix `"0"` instead of only the values that start with it. * Add `Psr6SessionStore` and `Psr6NotificationBus`, which work like `Psr16SessionStore` and `Psr16NotificationBus` over a PSR-6 cache pool. `Builder::setDiscovery()` also accepts a PSR-6 pool as `$cache`. ### Client diff --git a/src/Capability/Completion/EnumCompletionProvider.php b/src/Capability/Completion/EnumCompletionProvider.php index 3c50eba56..f98161932 100644 --- a/src/Capability/Completion/EnumCompletionProvider.php +++ b/src/Capability/Completion/EnumCompletionProvider.php @@ -40,7 +40,7 @@ public function __construct(string $enumClass) public function getCompletions(string $currentValue): array { - if (empty($currentValue)) { + if ('' === $currentValue) { return $this->values; } diff --git a/src/Capability/Completion/ListCompletionProvider.php b/src/Capability/Completion/ListCompletionProvider.php index 45a41c45b..5e066dec9 100644 --- a/src/Capability/Completion/ListCompletionProvider.php +++ b/src/Capability/Completion/ListCompletionProvider.php @@ -31,7 +31,7 @@ public function __construct(array $values) public function getCompletions(string $currentValue): array { - if (empty($currentValue)) { + if ('' === $currentValue) { return $this->values; } diff --git a/tests/Unit/Capability/Completion/EnumCompletionProviderTest.php b/tests/Unit/Capability/Completion/EnumCompletionProviderTest.php index f0ca3d46b..4bbc292ad 100644 --- a/tests/Unit/Capability/Completion/EnumCompletionProviderTest.php +++ b/tests/Unit/Capability/Completion/EnumCompletionProviderTest.php @@ -32,10 +32,17 @@ public function testPrefixMatchesKeepCaseOrder(): void $this->assertSame([], $provider->getCompletions('missing')); } - public function testZeroPrefixOffersAllCases(): void + public function testEmptyPrefixOffersAllCases(): void { $provider = new EnumCompletionProvider(CompletionFixture::class); - $this->assertSame(['alpha', 'alpine', 'algebra', '0'], $provider->getCompletions('0')); + $this->assertSame(['alpha', 'alpine', 'algebra', '0'], $provider->getCompletions('')); + } + + public function testZeroIsMatchedAsPrefix(): void + { + $provider = new EnumCompletionProvider(CompletionFixture::class); + + $this->assertSame(['0'], $provider->getCompletions('0')); } } diff --git a/tests/Unit/Capability/Completion/ListCompletionProviderTest.php b/tests/Unit/Capability/Completion/ListCompletionProviderTest.php index 9a9ca9fc9..4870d7373 100644 --- a/tests/Unit/Capability/Completion/ListCompletionProviderTest.php +++ b/tests/Unit/Capability/Completion/ListCompletionProviderTest.php @@ -42,6 +42,13 @@ public function testNumericValuesBecomeStrings(): void $this->assertSame(['101', '102.5'], $provider->getCompletions('10')); } + public function testZeroIsMatchedAsPrefix(): void + { + $provider = new ListCompletionProvider(['zero', '0', '01']); + + $this->assertSame(['0', '01'], $provider->getCompletions('0')); + } + public function testKeysAreDiscarded(): void { $provider = new ListCompletionProvider([5 => 'a', 9 => 'b']);