[Schema][Server] feat: support sampling with tools (#409)

* feat: support sampling with tools

* address sampling tools review feedback

* tighten sampling tools spec compliance

* drop SamplingStopReason enum in favor of open string

The spec leaves stopReason open for provider-specific values, so upcasting
the four known ones to enum cases makes every future enum addition a silent
BC break for string comparisons.

---------

Co-authored-by: Christopher Hertel <mail@christopher-hertel.de>
This commit is contained in:
wWzZb 2026-08-15 09:28:11 +08:00 • committed by GitHub
commit 82aac972c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 682 additions and 27 deletions

View file

@ -551,6 +551,42 @@ $client = Client::builder()
->build();
```
#### Sampling with Tools
Clients that support tool-enabled sampling should advertise that capability and forward the request's `tools` and
`toolChoice` fields to their LLM provider. A provider response that requests tools can be returned as one or more
`ToolUseContent` blocks:
```php
use Mcp\Schema\ClientCapabilities;
use Mcp\Schema\Content\ToolUseContent;
use Mcp\Schema\Enum\Role;
use Mcp\Schema\Result\CreateSamplingMessageResult;
$client = Client::builder()
->setCapabilities(new ClientCapabilities(
sampling: true,
samplingContext: true,
samplingTools: true,
))
->addRequestHandler(new SamplingRequestHandler($samplingCallback))
->build();
// Inside the sampling callback, after invoking the LLM provider:
return new CreateSamplingMessageResult(
role: Role::Assistant,
content: array_map(
static fn ($call) => new ToolUseContent($call->id, $call->name, $call->input),
$providerResponse->toolCalls,
),
model: $providerResponse->model,
stopReason: 'toolUse',
);
```
The server executes the requested tools and sends their results in a later sampling request as `ToolResultContent`
blocks in a user message. The client should pass those blocks back to the LLM provider to continue the sampling loop.
> [!IMPORTANT]
> **Error Handling in Sampling Callbacks:**
>

View file

@ -32,7 +32,7 @@ class MyService
## Sampling
With [sampling](https://modelcontextprotocol.io/specification/2025-06-18/client/sampling) servers can request clients to
With [sampling](https://modelcontextprotocol.io/specification/2025-11-25/client/sampling) servers can request clients to
execute "completions" or "generations" with a language model for them:
```php
@ -41,12 +41,16 @@ $result = $clientGateway->sample('Roses are red, violets are', 350, 90, ['temper
The `sample` method accepts four arguments:
1. `message`, which is **required** and accepts a string, an instance of `Content` or an array of `SampleMessage` instances.
1. `message`, which is **required** and accepts a string, an instance of `Content` or an array of `SamplingMessage` instances.
2. `maxTokens`, which defaults to `1000`
3. `timeout` in seconds, which defaults to `120`
4. `options` which might include `system_prompt`, `preferences` for model choice, `includeContext`, `temperature`, `stopSequences` and `metadata`
4. `options` which might include `systemPrompt`, `preferences` for model choice, `includeContext`, `temperature`,
`stopSequences`, `metadata`, `tools`, and `toolChoice`
[Find more details to sampling payload in the specification.](https://modelcontextprotocol.io/specification/2025-06-18/client/sampling#protocol-messages)
Only send `includeContext` when the client advertises `sampling.context`, and only send `tools` or `toolChoice` when it
advertises `sampling.tools`. The context modes other than `none` are soft-deprecated by the current specification.
[Find more details to sampling payload in the specification.](https://modelcontextprotocol.io/specification/2025-11-25/client/sampling#protocol-messages)
## Logging