From a14dc855b040b6ecfeb3dc86b77aaa38f3b1f6d1 Mon Sep 17 00:00:00 2001 From: samko Date: Sat, 2 Aug 2025 12:29:53 +0200 Subject: [PATCH 1/2] allow to define config when resolving tools --- src/Facades/Relay.php | 4 +- src/Relay.php | 8 ++- src/RelayFactory.php | 11 ++-- tests/Feature/Facades/RelayFacadeTest.php | 54 ++++++++++++++++ tests/Unit/RelayFactoryTest.php | 76 +++++++++++++++++++++++ 5 files changed, 146 insertions(+), 7 deletions(-) diff --git a/src/Facades/Relay.php b/src/Facades/Relay.php index e778a68..a17e73c 100644 --- a/src/Facades/Relay.php +++ b/src/Facades/Relay.php @@ -9,8 +9,8 @@ use Prism\Relay\RelayFactory; /** - * @method static RelayFactory make(string $serverName) - * @method static array tools(string $serverName) + * @method static \Prism\Relay\Relay make(string $serverName, array $config = null) + * @method static array tools(string $serverName, array $config = null) * * @see RelayFactory */ diff --git a/src/Relay.php b/src/Relay.php index bc2c09d..4cecfa6 100644 --- a/src/Relay.php +++ b/src/Relay.php @@ -25,7 +25,7 @@ class Relay /** * @throws ServerConfigurationException */ - public function __construct(protected string $serverName) + public function __construct(protected string $serverName, protected ?array $customConfig = null) { $this->resolveServerConfig(); $this->initializeTransport(); @@ -513,6 +513,12 @@ protected function convertResponseToString(mixed $response): string */ protected function resolveServerConfig(): void { + if ($this->customConfig !== null) { + $this->serverConfig = $this->customConfig; + + return; + } + if (function_exists('app') && app()->bound('config')) { $servers = config('relay.servers', []); diff --git a/src/RelayFactory.php b/src/RelayFactory.php index 5154456..6a3e321 100644 --- a/src/RelayFactory.php +++ b/src/RelayFactory.php @@ -11,21 +11,24 @@ class RelayFactory { /** + * @param array|null $config + * * @throws ServerConfigurationException */ - public function make(string $serverName): Relay + public function make(string $serverName, ?array $config = null): Relay { - return new Relay($serverName); + return new Relay($serverName, $config); } /** + * @param array|null $config * @return array * * @throws ServerConfigurationException * @throws ToolDefinitionException */ - public function tools(string $serverName): array + public function tools(string $serverName, ?array $config = null): array { - return $this->make($serverName)->tools(); + return $this->make($serverName, $config)->tools(); } } diff --git a/tests/Feature/Facades/RelayFacadeTest.php b/tests/Feature/Facades/RelayFacadeTest.php index 3496668..ec0c428 100644 --- a/tests/Feature/Facades/RelayFacadeTest.php +++ b/tests/Feature/Facades/RelayFacadeTest.php @@ -55,3 +55,57 @@ ->toBeArray() ->toHaveCount(2); }); + +it('can make relay with custom config through facade', function (): void { + $customConfig = [ + 'transport' => \Prism\Relay\Enums\Transport::Http, + 'url' => 'http://custom.example.com/api', + 'timeout' => 45, + ]; + + $relay = Relay::make('custom_facade_server', $customConfig); + + expect($relay) + ->toBeInstanceOf(RelayClass::class) + ->and($relay->getServerName()) + ->toBe('custom_facade_server'); +}); + +it('can get tools with custom config through facade', function (): void { + $customConfig = [ + 'transport' => \Prism\Relay\Enums\Transport::Http, + 'url' => 'http://tools.example.com/api', + 'timeout' => 30, + ]; + + // Mock the factory to test the facade call + $mock = $this->mock(RelayFactory::class); + $mock->shouldReceive('tools') + ->once() + ->with('custom_tools_server', $customConfig) + ->andReturn([ + new \Prism\Prism\Tool, + ]); + + app()->instance('relay', $mock); + + $tools = Relay::tools('custom_tools_server', $customConfig); + + expect($tools) + ->toBeArray() + ->toHaveCount(1); +}); + +it('facade make method works without config parameter', function (): void { + config()->set('relay.servers.standard_test', [ + 'url' => 'http://standard.example.com/api', + 'timeout' => 30, + ]); + + $relay = Relay::make('standard_test'); + + expect($relay) + ->toBeInstanceOf(RelayClass::class) + ->and($relay->getServerName()) + ->toBe('standard_test'); +}); diff --git a/tests/Unit/RelayFactoryTest.php b/tests/Unit/RelayFactoryTest.php index 397f8ca..bc31e6d 100644 --- a/tests/Unit/RelayFactoryTest.php +++ b/tests/Unit/RelayFactoryTest.php @@ -48,3 +48,79 @@ $factory = new RelayFactory; expect(method_exists($factory, 'tools'))->toBeTrue(); }); + +it('creates a Relay instance with custom config', function (): void { + $customConfig = [ + 'transport' => \Prism\Relay\Enums\Transport::Http, + 'url' => 'http://custom.example.com/api', + 'timeout' => 45, + ]; + + $factory = new RelayFactory; + $relay = $factory->make('custom_server', $customConfig); + + expect($relay) + ->toBeInstanceOf(Relay::class) + ->and($relay->getServerName()) + ->toBe('custom_server'); +}); + +it('creates Relay with custom config when config parameter is provided', function (): void { + $customConfig = [ + 'transport' => \Prism\Relay\Enums\Transport::Stdio, + 'command' => ['echo', 'test'], + 'timeout' => 60, + 'env' => ['TEST_VAR' => 'test_value'], + ]; + + $factory = new RelayFactory; + $relay = $factory->make('stdio_server', $customConfig); + + expect($relay) + ->toBeInstanceOf(Relay::class) + ->and($relay->getServerName()) + ->toBe('stdio_server'); +}); + +it('uses Laravel config when no custom config provided', function (): void { + config()->set('relay.servers.laravel_server', [ + 'url' => 'http://laravel.example.com/api', + 'timeout' => 30, + ]); + + $factory = new RelayFactory; + + // Test both ways produce same result + $relay1 = $factory->make('laravel_server'); + $relay2 = $factory->make('laravel_server', null); + + expect($relay1) + ->toBeInstanceOf(Relay::class) + ->and($relay2) + ->toBeInstanceOf(Relay::class) + ->and($relay1->getServerName()) + ->toBe('laravel_server') + ->and($relay2->getServerName()) + ->toBe('laravel_server'); +}); + +it('tools method works with custom config', function (): void { + $customConfig = [ + 'transport' => \Prism\Relay\Enums\Transport::Http, + 'url' => 'http://tools.example.com/api', + 'timeout' => 30, + ]; + + $factory = new RelayFactory; + + // Verify method exists and accepts config parameter + expect(method_exists($factory, 'tools'))->toBeTrue(); + + // Test method signature (this will call the method but we expect it might fail due to no real server) + try { + $factory->tools('tools_server', $customConfig); + } catch (\Throwable $e) { + // Expected to fail since we don't have a real MCP server, but method should accept the parameters + expect($e)->toBeInstanceOf(\Throwable::class); + } +}); From 8a2394c2c4a567f66f629add12e2856457d6f04b Mon Sep 17 00:00:00 2001 From: samko Date: Sat, 2 Aug 2025 12:30:03 +0200 Subject: [PATCH 2/2] fix phpstan --- src/Relay.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Relay.php b/src/Relay.php index 4cecfa6..031d0e5 100644 --- a/src/Relay.php +++ b/src/Relay.php @@ -23,6 +23,8 @@ class Relay protected Transport $transport; /** + * @param array|null $customConfig + * * @throws ServerConfigurationException */ public function __construct(protected string $serverName, protected ?array $customConfig = null)