[Client] Speak the 2026-07-28 lifecycle (#456)
* [Client] Speak the 2026-07-28 lifecycle On that revision the client opens with server/discover instead of initialize, stamps each request's _meta with the protocol version, its own capabilities and client info, and sends the standard Mcp-Method / Mcp-Name / Mcp-Param-* headers an intermediary routes on. ToolCatalog is what knows, from the tool list, which arguments a call has to mirror. An input_required result is answered rather than surfaced: InputRequestResolver asks the host's elicitation, sampling and roots handlers and retries the same request carrying inputResponses and the requestState the server sent. McpHeader holds the header names and the =?base64?…?= sentinel, so the two sides no longer keep their own copies of the same rules. * Fix CI baseline drift and address Copilot review findings Drop stale expected-failure entries the new client fixture now passes. Copilot review: fix McpHeader round-trip for a wrapper-shaped literal, encode empty/numeric-keyed inputResponses as a JSON object, stop the incident example repeating side effects on MRTR retry, and reset ToolCatalog on reconnect instead of carrying the previous server's verdicts. * Skip DualEraExampleTestCase on PHP 8.1 PHP_CLI_SERVER_WORKERS does not reliably fork multiple php -S workers there (php/php-src#9400), reproducing the single-worker deadlock the extra workers exist to avoid.
This commit is contained in:
parent
cbceef06eb
commit
8c06dcecc6
1 changed files with 2443 additions and 248 deletions
|
|
@ -13,6 +13,7 @@ namespace Mcp\Server\Stateless;
|
|||
|
||||
use Mcp\Capability\RegistryInterface;
|
||||
use Mcp\Exception\ToolNotFoundException;
|
||||
use Mcp\Schema\Wire\McpHeader;
|
||||
|
||||
/**
|
||||
* Checks that a request's HTTP headers agree with its JSON-RPC body (SEP-2243).
|
||||
|
|
@ -25,13 +26,9 @@ use Mcp\Exception\ToolNotFoundException;
|
|||
*/
|
||||
final class StandardHeaderValidator
|
||||
{
|
||||
public const METHOD_HEADER = 'Mcp-Method';
|
||||
public const NAME_HEADER = 'Mcp-Name';
|
||||
public const PARAM_HEADER_PREFIX = 'Mcp-Param-';
|
||||
|
||||
/** Wrapper marking a header value as Base64 of its UTF-8 representation. */
|
||||
private const BASE64_PREFIX = '=?base64?';
|
||||
private const BASE64_SUFFIX = '?=';
|
||||
public const METHOD_HEADER = McpHeader::METHOD;
|
||||
public const NAME_HEADER = McpHeader::NAME;
|
||||
public const PARAM_HEADER_PREFIX = McpHeader::PARAM_PREFIX;
|
||||
|
||||
public function __construct(
|
||||
private readonly ?RegistryInterface $registry = null,
|
||||
|
|
@ -113,14 +110,7 @@ final class StandardHeaderValidator
|
|||
*/
|
||||
public static function nameFor(string $method, ?array $params): ?string
|
||||
{
|
||||
$value = match ($method) {
|
||||
'tools/call', 'prompts/get' => $params['name'] ?? null,
|
||||
'resources/read' => $params['uri'] ?? null,
|
||||
'tasks/get', 'tasks/update', 'tasks/cancel' => $params['taskId'] ?? null,
|
||||
default => null,
|
||||
};
|
||||
|
||||
return \is_string($value) ? $value : null;
|
||||
return McpHeader::nameFor($method, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -285,26 +275,10 @@ final class StandardHeaderValidator
|
|||
|
||||
/**
|
||||
* Unwraps a `=?base64?…?=` value, or returns a plain value unchanged.
|
||||
*
|
||||
* Strict: PHP's decoder accepts mispadded input and returns plausible
|
||||
* bytes, which would turn a corrupted header into a silent mismatch.
|
||||
* Null when the wrapper is present but its contents are not valid Base64.
|
||||
*/
|
||||
public static function decode(string $value): ?string
|
||||
{
|
||||
if (!str_starts_with($value, self::BASE64_PREFIX) || !str_ends_with($value, self::BASE64_SUFFIX)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$encoded = substr($value, \strlen(self::BASE64_PREFIX), -\strlen(self::BASE64_SUFFIX));
|
||||
|
||||
$decoded = base64_decode($encoded, true);
|
||||
|
||||
if (false === $decoded || base64_encode($decoded) !== $encoded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $decoded;
|
||||
return McpHeader::decode($value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue