[Server] Serve both protocol eras from one endpoint (#455)
* [Server] Serve both protocol eras from one endpoint The two lifecycles share a transport, not a dispatcher. StreamableHttpTransport classifies each request - a 2026-07-28 envelope, an initialize handshake, or a session-bound follow-up - through InboundClassifier and routes it to the dispatcher that owns it, so one URL answers a modern client and a handshake-era one alike. Builder::build() carries both; withoutModernEra() opts out and setModernVersions() narrows what the modern leg answers for. InputRequiredShim lets a handler written for multi round-trip requests also serve a handshake-era client, by turning each ask into the request/response exchange that era has - so a handler is written once rather than twice. The conformance fixture collapses into one server for the same reason: both legs now hit the same URL. * Trim redundant comments, fix stale in-memory claim in conformance fixture * Drop stale json-schema-2020-12 baseline entries The new fixture tool makes this scenario pass in both eras now. * Reject handshake revisions in setModernVersions(), warn on unsafe custom middleware Copilot review: setModernVersions() accepted revisions InboundClassifier would never route to the modern leg. StreamableHttpTransport now warns when a custom middleware list carries ProtocolVersionMiddleware, since it runs before era classification and rejects modern-era traffic by default. Also fixed a misleading docblock claim on the same middleware.
This commit is contained in:
parent
b97389325f
commit
cbceef06eb
1 changed files with 2060 additions and 323 deletions
|
|
@ -34,6 +34,54 @@ final class Elements
|
|||
public const TEST_IMAGE_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==';
|
||||
public const TEST_AUDIO_BASE64 = 'UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0YQIAAAA=';
|
||||
|
||||
/**
|
||||
* The focal `inputSchema` the `json-schema-2020-12` scenario expects a
|
||||
* server to advertise verbatim.
|
||||
*
|
||||
* It is handed to `addTool()` raw rather than generated from a signature,
|
||||
* because the point is that the SDK passes an author-supplied 2020-12
|
||||
* schema through `tools/list` untouched: `$schema`/`$defs`/
|
||||
* `additionalProperties` (SEP-1613) and the composition, conditional and
|
||||
* `$anchor` keywords (SEP-2106) must all survive.
|
||||
*
|
||||
* Mirrors `JSON_SCHEMA_2020_12_FIXTURE` in the conformance suite; keep the
|
||||
* two in sync.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function jsonSchema2020_12Fixture(): array
|
||||
{
|
||||
return [
|
||||
'$schema' => 'https://json-schema.org/draft/2020-12/schema',
|
||||
'type' => 'object',
|
||||
'$defs' => [
|
||||
'address' => [
|
||||
'$anchor' => 'addressDef',
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'street' => ['type' => 'string'],
|
||||
'city' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
],
|
||||
'properties' => [
|
||||
'name' => ['type' => 'string'],
|
||||
'address' => ['$ref' => '#/$defs/address'],
|
||||
'contactMethod' => ['type' => 'string', 'enum' => ['phone', 'email']],
|
||||
'phone' => ['type' => 'string'],
|
||||
'email' => ['type' => 'string'],
|
||||
],
|
||||
'allOf' => [['anyOf' => [['required' => ['phone']], ['required' => ['email']]]]],
|
||||
'if' => [
|
||||
'properties' => ['contactMethod' => ['const' => 'phone']],
|
||||
'required' => ['contactMethod'],
|
||||
],
|
||||
'then' => ['required' => ['phone']],
|
||||
'else' => ['required' => ['email']],
|
||||
'additionalProperties' => false,
|
||||
];
|
||||
}
|
||||
|
||||
public function toolMultipleTypes(): CallToolResult
|
||||
{
|
||||
return new CallToolResult([
|
||||
|
|
|
|||
Loading…
Reference in a new issue