[Schema][Server] Harden sampling tools against the spec (#420)
* harden sampling tools against the spec Report tool-flow violations as -32602 instead of dropping the request, accept resource_link in tool results, gate tools on sampling.tools, and reject empty content. * normalize keyed content arrays to lists array_filter() and friends preserve keys, and a keyed array serializes as a JSON object instead of the content-block array the schema requires.
This commit is contained in:
parent
becce29c41
commit
43815e38a8
1 changed files with 1212 additions and 167 deletions
|
|
@ -163,6 +163,9 @@ class ClientGateway
|
|||
toolChoice: $options['toolChoice'] ?? null,
|
||||
);
|
||||
|
||||
// Fail here rather than letting the client reject the request with -32602.
|
||||
$request->validateToolFlow();
|
||||
|
||||
$response = $this->request($request, $timeout);
|
||||
|
||||
if ($response instanceof Error) {
|
||||
|
|
@ -276,6 +279,47 @@ class ClientGateway
|
|||
return \array_key_exists('sampling', $capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the connected client supports tools during sampling.
|
||||
*
|
||||
* Per the spec a server MUST NOT put `tools` or `toolChoice` on a
|
||||
* `sampling/createMessage` request unless the client advertised
|
||||
* `sampling.tools`, so check this before passing either option to
|
||||
* {@see self::sample()}.
|
||||
*
|
||||
* @return bool True if the client supports tool-enabled sampling, false otherwise
|
||||
*/
|
||||
public function supportsSamplingTools(): bool
|
||||
{
|
||||
return $this->hasSamplingSubCapability('tools');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the connected client supports context inclusion during sampling.
|
||||
*
|
||||
* The `includeContext` values other than `none` are soft-deprecated and should
|
||||
* only be sent when the client advertised `sampling.context`.
|
||||
*
|
||||
* @return bool True if the client supports sampling context, false otherwise
|
||||
*/
|
||||
public function supportsSamplingContext(): bool
|
||||
{
|
||||
return $this->hasSamplingSubCapability('context');
|
||||
}
|
||||
|
||||
private function hasSamplingSubCapability(string $name): bool
|
||||
{
|
||||
$capabilities = (array) $this->session->get('client_capabilities', []);
|
||||
$sampling = $capabilities['sampling'] ?? null;
|
||||
|
||||
if (!\is_array($sampling) && !\is_object($sampling)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// MCP spec: capability presence indicates support (value is typically {} or [])
|
||||
return \array_key_exists($name, (array) $sampling);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request to the client and wait for a response (blocking).
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue