From bd0fe92f4cfd70666e5dbd245722a917c6fc4bc7 Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Sat, 15 Aug 2026 01:01:52 +0200 Subject: [PATCH] [Server] Add ClientGateway::supportsSampling() Sampling was the one client capability a tool could not check before asking. `supportsRoots()` and `supportsElicitation()` both read the capabilities the client advertised during the handshake; sampling is stored in that same session data but had no accessor, so a tool had to issue the request and catch the refusal. --- CHANGELOG.md | 1 + src/Server/ClientGateway.php | 17 +++++++++++++++++ tests/Unit/Server/ClientGatewayTest.php | 20 ++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70c9bc2f..882a189a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to `mcp/sdk` will be documented in this file. * Prompt generators returning content as typed arrays (`['type' => 'text', ...]` etc.) no longer lose the optional fields: `annotations` on every content type, and `_meta` and an explicit `mimeType` on embedded resource contents, now carry through to the resulting `PromptMessage` instead of being silently dropped. A missing resource `mimeType` still defaults to `text/plain`/`application/octet-stream` as before. * Add `annotations` support to `ImageContent` (constructor, `fromArray()`, `fromFile()`, `fromString()`, `jsonSerialize()`), matching `TextContent` and `AudioContent`. * Add client-side `roots/list` handler (`ListRootsRequestHandler` + `RootsCallbackInterface`) and `Client::sendRootsListChanged()`, plus server-side `ClientGateway::listRoots()` / `supportsRoots()` and `ListRootsResult::fromArray()`. +* Add `ClientGateway::supportsSampling()`, so a tool can check the client's advertised capabilities before issuing a `sampling/createMessage` request instead of asking and catching the refusal. Matches the existing `supportsRoots()` and `supportsElicitation()`. * Add `Mcp\Schema\Content\ResourceLink` for the spec's `resource_link` content block (protocol revision 2025-06-18+), letting tool results and prompt messages reference a resource by URI/name without embedding its contents. Accepted anywhere `resource` (`EmbeddedResource`) content is (de)serialized: `CallToolResult::fromArray()`, `PromptMessage::fromArray()`, and `PromptResultFormatter`. 0.7.0 diff --git a/src/Server/ClientGateway.php b/src/Server/ClientGateway.php index d7d55eb6..5205a613 100644 --- a/src/Server/ClientGateway.php +++ b/src/Server/ClientGateway.php @@ -250,6 +250,23 @@ public function supportsElicitation(): bool return \array_key_exists('elicitation', $capabilities); } + /** + * Check if the connected client supports sampling. + * + * Sampling lets a server borrow the client's model during tool execution. + * This method checks the client's advertised capabilities to determine if + * sampling/createMessage requests are supported. + * + * @return bool True if the client supports sampling, false otherwise + */ + public function supportsSampling(): bool + { + $capabilities = (array) $this->session->get('client_capabilities', []); + + // MCP spec: capability presence indicates support (value is typically {} or []) + return \array_key_exists('sampling', $capabilities); + } + /** * Send a request to the client and wait for a response (blocking). * diff --git a/tests/Unit/Server/ClientGatewayTest.php b/tests/Unit/Server/ClientGatewayTest.php index 409669e0..e6c2cee2 100644 --- a/tests/Unit/Server/ClientGatewayTest.php +++ b/tests/Unit/Server/ClientGatewayTest.php @@ -43,6 +43,26 @@ public function testSupportsRootsReturnsFalseWhenNotAdvertised(): void $this->assertFalse($gateway->supportsRoots()); } + public function testSupportsSamplingReturnsTrueWhenAdvertised(): void + { + $session = $this->createMock(SessionInterface::class); + $session->method('get')->with('client_capabilities', [])->willReturn(['sampling' => []]); + + $gateway = new ClientGateway($session); + + $this->assertTrue($gateway->supportsSampling()); + } + + public function testSupportsSamplingReturnsFalseWhenNotAdvertised(): void + { + $session = $this->createMock(SessionInterface::class); + $session->method('get')->with('client_capabilities', [])->willReturn(['roots' => []]); + + $gateway = new ClientGateway($session); + + $this->assertFalse($gateway->supportsSampling()); + } + public function testListRootsReturnsRootsFromClient(): void { $session = $this->createMock(SessionInterface::class);