Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 62 additions & 4 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Laravel\Mcp;

use Illuminate\Container\Container;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Laravel\Mcp\Client\ClientManager;
use Laravel\Mcp\Client\Contracts\Transport;
Expand All @@ -20,13 +21,16 @@
use Laravel\Mcp\Client\Primitives\Resource;
use Laravel\Mcp\Client\Primitives\Tool;
use Laravel\Mcp\Client\Protocol;
use Laravel\Mcp\Client\Schema\DiscoverResult;
use Laravel\Mcp\Client\Schema\InitializeResult;
use Laravel\Mcp\Client\Schema\PromptResult;
use Laravel\Mcp\Client\Schema\ResourceReadResult;
use Laravel\Mcp\Client\Schema\ToolResult;
use Laravel\Mcp\Client\Transport\HttpTransport;
use Laravel\Mcp\Client\Transport\StdioTransport;
use Laravel\Mcp\Client\Transport\TransportFactory;
use Laravel\Mcp\Enums\ProtocolVersion;
use Laravel\Mcp\Exceptions\ClientException;
use Laravel\Mcp\Schema\Implementation;

class Client
Expand Down Expand Up @@ -101,6 +105,57 @@ public function initializeResult(): ?InitializeResult
return $this->protocol->initializeResult();
}

public function discoverResult(): ?DiscoverResult
{
return $this->protocol->discoverResult();
}

/**
* @return array<string, mixed>
*/
public function capabilities(): array
{
$this->protocol->connect();

return $this->protocol->capabilities();
}

public function serverInfo(): ?Implementation
{
$this->protocol->connect();

return $this->protocol->serverInfo();
}

public function instructions(): ?string
{
$this->protocol->connect();

return $this->protocol->instructions();
}

public function withProtocolVersion(?ProtocolVersion $version): static
{
if ($version instanceof ProtocolVersion && ! in_array($version->value, ProtocolVersion::clientSupported(), true)) {
throw new ClientException(sprintf(
'This client does not support protocol version [%s]. It supports [%s].',
$version->value,
implode(', ', ProtocolVersion::clientSupported()),
));
}

$this->protocol->pinProtocolVersion($version);

return $this;
}

public function protocolVersion(): ProtocolVersion
{
$this->protocol->connect();

return $this->protocol->connectionProtocol();
}

public function ping(): void
{
(new Ping)->handle($this->protocol);
Expand Down Expand Up @@ -191,6 +246,7 @@ public function __serialize(): array
'name' => null,
'clientInfo' => $this->clientInfo,
'transport' => $this->transport->recipe(),
'protocolVersion' => $this->protocol->pinnedProtocolVersion()?->value,
];
}

Expand All @@ -199,21 +255,23 @@ public function __serialize(): array
*/
public function __unserialize(array $data): void
{
$this->name = $data['name'] ?? null;
$this->name = Arr::get($data, 'name');

if ($this->name !== null) {
$resolved = Container::getInstance()->make(ClientManager::class)->build($this->name);

$this->transport = $resolved->transport;
$this->clientInfo = $resolved->clientInfo;
$pinned = $resolved->protocol->pinnedProtocolVersion();
} else {
$this->clientInfo = $data['clientInfo'];
$this->transport = TransportFactory::fromRecipe($data['transport']);
$this->clientInfo = Arr::get($data, 'clientInfo');
$this->transport = TransportFactory::fromRecipe(Arr::get($data, 'transport'));
$pinned = ProtocolVersion::tryFrom((string) Arr::get($data, 'protocolVersion'));
}

$this->clientInfo ??= $this->defaultClientInfo();

$this->protocol = new Protocol($this->transport, $this->clientInfo);
$this->protocol = new Protocol($this->transport, $this->clientInfo, $pinned);
}

public function __destruct()
Expand Down
2 changes: 0 additions & 2 deletions src/Client/Contracts/Transport.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ public function receive(): string;

public function setTimeoutSeconds(float $seconds): void;

public function setProtocolVersion(string $version): void;

/**
* @return array<string, mixed>
*/
Expand Down
12 changes: 12 additions & 0 deletions src/Client/Contracts/UsesProtocol.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Laravel\Mcp\Client\Contracts;

use Laravel\Mcp\Enums\ProtocolVersion;

interface UsesProtocol
{
public function useProtocol(ProtocolVersion $protocolVersion): void;
}
10 changes: 10 additions & 0 deletions src/Client/Exceptions/TimeoutException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Laravel\Mcp\Client\Exceptions;

class TimeoutException extends TransportException
{
//
}
12 changes: 12 additions & 0 deletions src/Client/Exceptions/TransportException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Laravel\Mcp\Client\Exceptions;

use Laravel\Mcp\Exceptions\ClientException;

class TransportException extends ClientException
{
//
}
33 changes: 33 additions & 0 deletions src/Client/Methods/Discover.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Laravel\Mcp\Client\Methods;

use Laravel\Mcp\Client\Contracts\Method;
use Laravel\Mcp\Client\Protocol;
use Laravel\Mcp\Client\Schema\DiscoverResult;

/**
* @implements Method<DiscoverResult>
*/
class Discover implements Method
{
public function method(): string
{
return 'server/discover';
}

/**
* @return array<string, mixed>
*/
public function params(): array
{
return [];
}

public function handle(Protocol $protocol): DiscoverResult
{
return DiscoverResult::from($protocol->dispatch($this));
}
}
8 changes: 5 additions & 3 deletions src/Client/Methods/Initialize.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
*/
class Initialize implements Method
{
public function __construct(protected Implementation $clientInfo)
{
public function __construct(
protected Implementation $clientInfo,
protected ProtocolVersion $protocolVersion = ProtocolVersion::V2025_11_25,
) {
//
}

Expand All @@ -31,7 +33,7 @@ public function method(): string
public function params(): array
{
return [
'protocolVersion' => ProtocolVersion::V2025_11_25->value,
'protocolVersion' => $this->protocolVersion->value,
'capabilities' => (object) [],
'clientInfo' => $this->clientInfo->toArray(),
];
Expand Down
46 changes: 46 additions & 0 deletions src/Client/NegotiatedConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Laravel\Mcp\Client;

use Laravel\Mcp\Client\Schema\DiscoverResult;
use Laravel\Mcp\Client\Schema\InitializeResult;
use Laravel\Mcp\Enums\ProtocolVersion;
use Laravel\Mcp\Schema\Implementation;

class NegotiatedConnection
{
public function __construct(
public ProtocolVersion $protocolVersion,
public DiscoverResult|InitializeResult $result,
) {}

public function discoverResult(): ?DiscoverResult
{
return $this->result instanceof DiscoverResult ? $this->result : null;
}

public function initializeResult(): ?InitializeResult
{
return $this->result instanceof InitializeResult ? $this->result : null;
}

/**
* @return array<string, mixed>
*/
public function capabilities(): array
{
return $this->result->capabilities;
}

public function serverInfo(): ?Implementation
{
return $this->result->serverInfo;
}

public function instructions(): ?string
{
return $this->result->instructions;
}
}
Loading
Loading