* Address review feedback on resource_link Three follow-ups from review of the resource_link work: * PromptResultFormatter: add a regression test proving the optional fields of a typed resource_link block (title, description, mimeType, size, annotations, _meta) survive formatting. The delegation to ResourceLink::fromArray() already landed while rebasing onto the PromptResultFormatter refactor, but the existing test only supplied uri/name and so could not catch a reintroduced field drop. * ResourceLink::fromArray(): validate optional fields consistently, the way the equivalent ResourceDefinition::fromArray() already does. description, mimeType and size now raise InvalidArgumentException instead of surfacing a TypeError (or silently coercing, for size's (int) cast); annotations goes through Annotations::tryFromArray(), which carries the is_array() guard that icons already had; icons uses Icon::listFromArray() so a non-array entry is reported in context. * ToolReference::extractStructuredContent(): never emit a list as structuredContent. The Content guard added earlier fixed only one instance of the real invariant - structuredContent must be a JSON object, and a PHP list can never be one. Tool::fromArray() already enforces the matching rule by rejecting an outputSchema whose type is not "object", so this aligns the runtime path with it. The test asserting the opposite for an array-typed outputSchema contradicted the phpstan type and the fromArray() check introduced alongside it in the same commit, so it is replaced by tests for the two list shapes. * Apply the structuredContent object rule to object results too `extractStructuredContent()` guards raw array results against being emitted as a JSON array, but the object branch handed back whatever `json_decode()` produced. A `JsonSerializable` returning a list or a scalar slipped straight through, producing exactly the `structuredContent` the array guard exists to prevent — and a return value that contradicts the method's own `array<string, mixed>|null` signature. Check the decoded value before returning it, and cover the object branch, which had no tests at all. * Document structured tool output `outputSchema` and `structuredContent` were undocumented: the tool return value docs covered only the `content` side, and the schema generation section is about tool parameters. Add a "Structured Output" subsection covering how to declare the schema, which return values populate `structuredContent`, and why a list has to be wrapped in a key to get structured output at all. * Gate structuredContent on the negotiated protocol revision SEP-2106 (revision 2026-07-28) widens structuredContent to any JSON value; earlier revisions require an object. Resolve the revision per request and apply the matching rule, and warn when a declared outputSchema yields none. * Resolve the protocol revision on RequestContext Keeps the `_meta`-then-session lookup in one place instead of per handler, and exposes the negotiated revision to tool handlers. * Document reading the negotiated revision from RequestContext * Warn when a self-built CallToolResult carries an invalid structuredContent Returning a CallToolResult opts out of the extraction rules, so the value is still sent unchanged — but a JSON array is not valid before SEP-2106.
93 lines
3.3 KiB
Markdown
93 lines
3.3 KiB
Markdown
# Client Communication
|
|
|
|
MCP supports various ways a server can communicate back to a server on top of the main request-response flow.
|
|
|
|
## Table of Contents
|
|
|
|
- [ClientGateway](#client-gateway)
|
|
- [Sampling](#sampling)
|
|
- [Logging](#logging)
|
|
- [Notification](#notification)
|
|
- [Progress](#progress)
|
|
|
|
## ClientGateway
|
|
|
|
Every communication back to client is handled using the `Mcp\Server\ClientGateway` and its dedicated methods per
|
|
operation. To use the `ClientGateway` in your code, you need to use method argument injection for `RequestContext`.
|
|
|
|
Every reference of a MCP element, that translates to an actual method call, can just add an type-hinted argument for the
|
|
`RequestContext` and the SDK will take care to include the gateway in the arguments of the method call:
|
|
|
|
```php
|
|
use Mcp\Capability\Attribute\McpTool;
|
|
use Mcp\Server\RequestContext;
|
|
|
|
class MyService
|
|
{
|
|
#[McpTool(name: 'my_tool', description: 'My Tool Description')]
|
|
public function myTool(RequestContext $context): string
|
|
{
|
|
$context->getClientGateway()->log(...);
|
|
```
|
|
|
|
The same object also carries the protocol revision negotiated for the current request, which is useful when a feature is
|
|
only available from a certain revision on:
|
|
|
|
```php
|
|
use Mcp\Schema\Enum\ProtocolVersion;
|
|
|
|
if ($context->getProtocolVersion()->isAtLeast(ProtocolVersion::V2026_07_28)) {
|
|
// e.g. a bare list is only valid as `structuredContent` from this revision on
|
|
}
|
|
```
|
|
|
|
## Sampling
|
|
|
|
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
|
|
$result = $clientGateway->sample('Roses are red, violets are', 350, 90, ['temperature' => 0.5]);
|
|
```
|
|
|
|
The `sample` method accepts four arguments:
|
|
|
|
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 `systemPrompt`, `preferences` for model choice, `includeContext`, `temperature`,
|
|
`stopSequences`, `metadata`, `tools`, and `toolChoice`
|
|
|
|
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
|
|
|
|
The [Logging](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging) utility enables servers
|
|
to send structured log messages as notification to clients:
|
|
|
|
```php
|
|
use Mcp\Schema\Enum\LoggingLevel;
|
|
|
|
$clientGateway->log(LoggingLevel::Warning, 'The end is near.');
|
|
```
|
|
|
|
## Progress
|
|
|
|
With a [Progress](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress#progress)
|
|
notification a server can update a client while an operation is ongoing:
|
|
|
|
```php
|
|
$clientGateway->progress(4.2, 10, 'Downloading needed images.');
|
|
```
|
|
|
|
## Notification
|
|
|
|
Lastly, the server can push all kind of notifications, that implement the `Mcp\Schema\JsonRpc\Notification` interface
|
|
to the client to:
|
|
|
|
```php
|
|
$clientGateway->notify($yourNotification);
|
|
```
|