(new McpApps())->getCapabilities(), ]; $caps = new ServerCapabilities( tools: true, resources: true, extensions: $extensions, ); $this->assertSame($extensions, $caps->extensions); } public function testServerCapabilitiesExtensionsDefaultNull(): void { $caps = new ServerCapabilities(); $this->assertNull($caps->extensions); } public function testServerCapabilitiesJsonSerializeWithExtensions(): void { $caps = new ServerCapabilities( tools: true, resources: true, prompts: false, extensions: [ McpApps::EXTENSION_ID => (new McpApps())->getCapabilities(), ], ); $json = $caps->jsonSerialize(); $this->assertArrayHasKey('extensions', $json); $this->assertObjectHasProperty(McpApps::EXTENSION_ID, $json['extensions']); } public function testServerCapabilitiesJsonSerializeWithoutExtensions(): void { $caps = new ServerCapabilities(tools: true); $json = $caps->jsonSerialize(); $this->assertArrayNotHasKey('extensions', $json); } public function testServerCapabilitiesFromArrayWithExtensions(): void { $data = [ 'tools' => new \stdClass(), 'extensions' => [ McpApps::EXTENSION_ID => ['mimeTypes' => ['text/html;profile=mcp-app']], ], ]; $caps = ServerCapabilities::fromArray($data); $this->assertTrue($caps->tools); $this->assertNotNull($caps->extensions); $this->assertArrayHasKey(McpApps::EXTENSION_ID, $caps->extensions); $this->assertSame(['text/html;profile=mcp-app'], $caps->extensions[McpApps::EXTENSION_ID]['mimeTypes']); } public function testServerCapabilitiesFromArrayWithoutExtensions(): void { $caps = ServerCapabilities::fromArray(['tools' => new \stdClass()]); $this->assertNull($caps->extensions); } public function testServerCapabilitiesFromArrayRejectsNonArrayExtensions(): void { // @phpstan-ignore-next-line — intentionally malformed payload $caps = ServerCapabilities::fromArray(['tools' => new \stdClass(), 'extensions' => 'not-an-array']); $this->assertNull($caps->extensions); } public function testServerCapabilitiesWithExtensionsMerges(): void { $caps = new ServerCapabilities( tools: true, extensions: ['a' => ['x' => 1], 'b' => ['y' => 2]], ); $merged = $caps->withExtensions(['b' => ['y' => 99], 'c' => ['z' => 3]]); $this->assertSame(['x' => 1], $merged->extensions['a']); $this->assertSame(['y' => 99], $merged->extensions['b'], 'new entry overrides existing id'); $this->assertSame(['z' => 3], $merged->extensions['c']); $this->assertSame(['a' => ['x' => 1], 'b' => ['y' => 2]], $caps->extensions, 'original is unchanged'); } public function testClientCapabilitiesWithExtensions(): void { $extensions = [ McpApps::EXTENSION_ID => (new McpApps())->getCapabilities(), ]; $caps = new ClientCapabilities( extensions: $extensions, ); $this->assertSame($extensions, $caps->extensions); } public function testClientCapabilitiesExtensionsDefaultNull(): void { $caps = new ClientCapabilities(); $this->assertNull($caps->extensions); } public function testClientCapabilitiesJsonSerializeWithExtensions(): void { $caps = new ClientCapabilities( extensions: [ McpApps::EXTENSION_ID => (new McpApps())->getCapabilities(), ], ); $json = $caps->jsonSerialize(); $this->assertArrayHasKey('extensions', $json); $this->assertObjectHasProperty(McpApps::EXTENSION_ID, $json['extensions']); } public function testClientCapabilitiesJsonSerializeWithoutExtensions(): void { $caps = new ClientCapabilities(); $json = $caps->jsonSerialize(); // ClientCapabilities returns \stdClass when empty (so it serializes as `{}`, not `[]`). if (\is_array($json)) { $this->assertArrayNotHasKey('extensions', $json); } else { $this->assertObjectNotHasProperty('extensions', $json); } } public function testClientCapabilitiesFromArrayWithExtensions(): void { $data = [ 'roots' => ['listChanged' => true], 'extensions' => [ McpApps::EXTENSION_ID => ['mimeTypes' => ['text/html;profile=mcp-app']], ], ]; $caps = ClientCapabilities::fromArray($data); $this->assertTrue($caps->roots); $this->assertTrue($caps->rootsListChanged); $this->assertNotNull($caps->extensions); $this->assertArrayHasKey(McpApps::EXTENSION_ID, $caps->extensions); } public function testClientCapabilitiesFromArrayWithoutExtensions(): void { $caps = ClientCapabilities::fromArray(['roots' => ['listChanged' => true]]); $this->assertNull($caps->extensions); } public function testClientCapabilitiesFromArrayRejectsNonArrayExtensions(): void { // @phpstan-ignore-next-line — intentionally malformed payload $caps = ClientCapabilities::fromArray(['roots' => ['listChanged' => true], 'extensions' => 'not-an-array']); $this->assertNull($caps->extensions); } public function testBackwardCompatibilityServerCapabilities(): void { $caps = new ServerCapabilities( tools: true, toolsListChanged: false, resources: true, resourcesSubscribe: false, resourcesListChanged: false, prompts: true, promptsListChanged: false, logging: false, completions: false, experimental: null, ); $this->assertNull($caps->extensions); $json = $caps->jsonSerialize(); $this->assertArrayNotHasKey('extensions', $json); } public function testBackwardCompatibilityClientCapabilities(): void { $caps = new ClientCapabilities( roots: true, rootsListChanged: true, sampling: true, elicitation: true, experimental: null, ); $this->assertNull($caps->extensions); $json = $caps->jsonSerialize(); $this->assertArrayNotHasKey('extensions', $json); } }