Skip to content

[Client] Speak the 2026-07-28 lifecycle - #456

Merged
chr-hertel merged 3 commits into
modelcontextprotocol:mainfrom
chr-hertel:pr/2026-client
Aug 19, 2026
Merged

[Client] Speak the 2026-07-28 lifecycle#456
chr-hertel merged 3 commits into
modelcontextprotocol:mainfrom
chr-hertel:pr/2026-client

Conversation

@chr-hertel

Copy link
Copy Markdown
Member

Stacked on #455. This is the client half of the revision — with it, both sides of the SDK speak 2026-07-28.

Client opens with server/discover instead of initialize on that revision, 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. Mcp\Client\Stateless\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.

One refactor to already-merged code. Mcp\Schema\Wire\McpHeader holds the header names and the =?base64?…?= sentinel both sides share. The x-mcp-header annotation rules from #441 move there too, as McpHeader::checkAnnotations() / annotations(), and Tool delegates to them. The client needs those rules for a schema a peer sent, which the private methods on Tool could not serve; the rules themselves are unchanged, and ToolHeaderAnnotationTest still covers them through Tool.


Cross-fork PRs can only target main, so this diff also carries its ancestors until they merge. Only the last commit(s) belong to this PR — GitHub's "Commits" tab separates them.

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds client support for the 2026-07-28 stateless MCP lifecycle.

Changes:

  • Adds discovery, request envelopes, protocol headers, and version reconciliation.
  • Resolves multi-round-trip input requests automatically.
  • Shares header validation rules and adds dual-era/conformance coverage.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
CHANGELOG.md Documents client lifecycle support.
src/Client.php Integrates tool catalog validation.
src/Client/Protocol.php Implements modern lifecycle and retries.
src/Client/Stateless/HeaderFactory.php Generates protocol headers.
src/Client/Stateless/InputRequestResolver.php Resolves embedded input requests.
src/Client/Stateless/RequestEnvelope.php Stamps modern request metadata.
src/Client/Stateless/ToolCatalog.php Tracks tool header annotations.
src/Client/Transport/HeaderAwareTransportInterface.php Defines per-message header callbacks.
src/Client/Transport/HttpTransport.php Sends generated HTTP headers.
src/Schema/Request/DiscoverRequest.php Models server/discover.
src/Schema/Tool.php Delegates annotation validation.
src/Schema/Wire/McpHeader.php Centralizes header rules.
src/Server/Stateless/StandardHeaderValidator.php Uses shared header utilities.
examples/server/client-communication/ClientAwareService.php Converts examples to MRTR.
examples/server/elicitation/ElicitationHandlers.php Supports elicitation across eras.
tests/Conformance/client.php Adds modern conformance scenarios.
tests/Integration/DualEraElicitationTest.php Tests elicitation in both eras.
tests/Integration/DualEraExampleTestCase.php Provides dual-era test infrastructure.
tests/Integration/HandshakeTest.php Updates lifecycle expectations.
tests/Unit/Client/ProtocolTest.php Tests modern protocol initialization.
tests/Unit/Client/Stateless/HeaderFactoryTest.php Tests generated headers.
tests/Unit/Client/Stateless/InputRequestResolverTest.php Tests input resolution.
tests/Unit/Client/Stateless/ToolCatalogTest.php Tests catalog behavior.
tests/Unit/Schema/Wire/McpHeaderTest.php Tests shared header encoding.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

return null;
}

return self::isSafe($rendered) ? $rendered : self::wrap($rendered);
Comment thread src/Client/Protocol.php Outdated
// between rounds.
$payload['params'] = [
...($payload['params'] ?? []),
'inputResponses' => $this->inputRequests->resolve($asked),
);

$result = $clientGateway->sample($prompt, 350, 90, ['temperature' => 0.5]);
$result = $context->getInputContext()?->samplingResult('recommendation');
Comment thread src/Client/Protocol.php
@@ -80,37 +128,65 @@ public function __construct(
public function connect(TransportInterface $transport, Configuration $config): void
{
$this->transport = $transport;
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.
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.
@chr-hertel
chr-hertel merged commit 8c06dce into modelcontextprotocol:main Aug 19, 2026
25 checks passed
@chr-hertel
chr-hertel deleted the pr/2026-client branch August 19, 2026 00:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants