Adopt Schema component in server handlers

This commit is contained in:
Christopher Hertel 2025-08-03 22:06:46 +02:00
commit 09707b4798
No known key found for this signature in database
GPG key ID: 111F6BFF7B6B5AA3
45 changed files with 1516 additions and 2424 deletions

View file

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Schema;
/**
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
interface Constants
{
public const LATEST_PROTOCOL_VERSION = '2025-03-26';
public const JSONRPC_VERSION = '2.0';
public const PARSE_ERROR = -32700;
public const INVALID_REQUEST = -32600;
public const METHOD_NOT_FOUND = -32601;
public const INVALID_PARAMS = -32602;
public const INTERNAL_ERROR = -32603;
public const SERVER_ERROR = -32000;
}

View file

@ -61,7 +61,7 @@ class TextResourceContents extends ResourceContents
{
return [
'text' => $this->text,
...$this->jsonSerialize(),
...parent::jsonSerialize(),
];
}
}

View file

@ -1,164 +0,0 @@
<?php
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Schema\JsonRpc;
use Mcp\Exception\InvalidArgumentException;
/**
* A JSON-RPC batch request, as described in https://www.jsonrpc.org/specification#batch.
*
* @phpstan-import-type RequestData from Request
* @phpstan-import-type NotificationData from Notification
*
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
class BatchRequest implements MessageInterface, \Countable
{
/**
* Create a new JSON-RPC 2.0 batch of requests/notifications.
*
* @param array<Request|Notification> $items
*/
public function __construct(public readonly array $items)
{
foreach ($items as $item) {
if (!($item instanceof Request || $item instanceof Notification)) {
throw new InvalidArgumentException('All items in BatchRequest must be instances of Request or Notification.');
}
}
}
/**
* @param array<RequestData|NotificationData> $data
*/
public static function fromArray(array $data): self
{
if (empty($data)) {
throw new InvalidArgumentException('BatchRequest data array must not be empty.');
}
$items = [];
foreach ($data as $itemData) {
if (!\is_array($itemData)) {
throw new InvalidArgumentException('BatchRequest item data must be an array.');
}
if (isset($itemData['id'])) {
$items[] = Request::fromArray($itemData);
} elseif (isset($itemData['method'])) {
$items[] = Notification::fromArray($itemData);
} else {
throw new InvalidArgumentException('Invalid item in BatchRequest data: missing "method" or "id".');
}
}
return new self($items);
}
public function getId(): string|int
{
foreach ($this->items as $item) {
if ($item instanceof Request) {
return $item->getId();
}
}
return '';
}
/**
* Check if this batch has any requests.
*/
public function hasRequests(): bool
{
$hasRequests = false;
foreach ($this->items as $item) {
if ($item instanceof Request) {
$hasRequests = true;
break;
}
}
return $hasRequests;
}
/**
* Check if this batch has any notifications.
*/
public function hasNotifications(): bool
{
$hasNotifications = false;
foreach ($this->items as $item) {
if ($item instanceof Notification) {
$hasNotifications = true;
break;
}
}
return $hasNotifications;
}
/**
* Get all requests in this batch.
*
* @return array<Request>
*/
public function getRequests(): array
{
return array_filter($this->items, fn ($item) => $item instanceof Request);
}
/**
* Get all notifications in this batch.
*
* @return array<Notification>
*/
public function getNotifications(): array
{
return array_filter($this->items, fn ($item) => $item instanceof Notification);
}
/**
* Get all elements in this batch.
*
* @return array<Request|Notification>
*/
public function getAll(): array
{
return $this->items;
}
/**
* Count the total number of elements in this batch.
*/
public function count(): int
{
return \count($this->items);
}
public function nRequests(): int
{
return \count($this->getRequests());
}
public function nNotifications(): int
{
return \count($this->getNotifications());
}
/**
* @return array<Request|Notification>
*/
public function jsonSerialize(): array
{
return $this->items;
}
}

View file

@ -1,138 +0,0 @@
<?php
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Schema\JsonRpc;
use Mcp\Exception\InvalidArgumentException;
/**
* A JSON-RPC batch response, as described in https://www.jsonrpc.org/specification#batch.
*
* @phpstan-import-type ResponseData from Response
* @phpstan-import-type ErrorData from Error
*
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
class BatchResponse implements MessageInterface, \Countable
{
/**
* @param array<Response|Error> $items the individual responses/errors in this batch
*/
public function __construct(
public array $items,
) {
foreach ($items as $item) {
if (!($item instanceof Response || $item instanceof Error)) {
throw new InvalidArgumentException('All items in BatchResponse must be instances of Response or Error.');
}
}
}
/**
* @param array<ErrorData|ResponseData> $data
*/
public static function fromArray(array $data): self
{
if (empty($data)) {
throw new InvalidArgumentException('BatchResponse data array must not be empty.');
}
$items = [];
foreach ($data as $itemData) {
if (!\is_array($itemData)) {
throw new InvalidArgumentException('BatchResponse item data must be an array.');
}
if (isset($itemData['id'])) {
$items[] = Response::fromArray($itemData);
} elseif (isset($itemData['error'])) {
$items[] = Error::fromArray($itemData);
} else {
throw new InvalidArgumentException('Invalid item in BatchResponse data: missing "id" or "error".');
}
}
return new self($items);
}
public function getId(): string|int
{
foreach ($this->items as $item) {
if ($item instanceof Response) {
return $item->getId();
}
}
throw new InvalidArgumentException('BatchResponse does not contain any Response items with an "id".');
}
public function hasResponses(): bool
{
$hasResponses = false;
foreach ($this->items as $item) {
if ($item instanceof Response) {
$hasResponses = true;
break;
}
}
return $hasResponses;
}
public function hasErrors(): bool
{
$hasErrors = false;
foreach ($this->items as $item) {
if ($item instanceof Error) {
$hasErrors = true;
break;
}
}
return $hasErrors;
}
/**
* @return Response[]
*/
public function getResponses(): array
{
return array_filter($this->items, fn ($item) => $item instanceof Response);
}
/**
* @return Error[]
*/
public function getErrors(): array
{
return array_filter($this->items, fn ($item) => $item instanceof Error);
}
/**
* @return Error[]|Response[]
*/
public function getAll(): array
{
return $this->items;
}
public function count(): int
{
return \count($this->items);
}
/**
* @return Error[]|Response[]
*/
public function jsonSerialize(): array
{
return $this->items;
}
}

View file

@ -12,7 +12,6 @@
namespace Mcp\Schema\JsonRpc;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\Constants;
/**
* A response to a request that indicates an error occurred.
@ -29,6 +28,14 @@ use Mcp\Schema\Constants;
*/
class Error implements MessageInterface
{
public const PARSE_ERROR = -32700;
public const INVALID_REQUEST = -32600;
public const METHOD_NOT_FOUND = -32601;
public const INVALID_PARAMS = -32602;
public const INTERNAL_ERROR = -32603;
public const SERVER_ERROR = -32000;
public const RESOURCE_NOT_FOUND = -32002;
/**
* @param int $code the error type that occurred
* @param string $message a short description of the error
@ -47,7 +54,7 @@ class Error implements MessageInterface
*/
public static function fromArray(array $data): self
{
if (!isset($data['jsonrpc']) || Constants::JSONRPC_VERSION !== $data['jsonrpc']) {
if (!isset($data['jsonrpc']) || MessageInterface::JSONRPC_VERSION !== $data['jsonrpc']) {
throw new InvalidArgumentException('Invalid or missing "jsonrpc" in Error data.');
}
if (!isset($data['id']) || !\is_string($data['id'])) {
@ -65,32 +72,32 @@ class Error implements MessageInterface
public static function forParseError(string $message, string|int $id = ''): self
{
return new self($id, Constants::PARSE_ERROR, $message);
return new self($id, self::PARSE_ERROR, $message);
}
public static function forInvalidRequest(string $message, string|int $id = ''): self
{
return new self($id, Constants::INVALID_REQUEST, $message);
return new self($id, self::INVALID_REQUEST, $message);
}
public static function forMethodNotFound(string $message, string|int $id = ''): self
{
return new self($id, Constants::METHOD_NOT_FOUND, $message);
return new self($id, self::METHOD_NOT_FOUND, $message);
}
public static function forInvalidParams(string $message, string|int $id = ''): self
{
return new self($id, Constants::INVALID_PARAMS, $message);
return new self($id, self::INVALID_PARAMS, $message);
}
public static function forInternalError(string $message, string|int $id = ''): self
{
return new self($id, Constants::INTERNAL_ERROR, $message);
return new self($id, self::INTERNAL_ERROR, $message);
}
public static function forServerError(string $message, string|int $id = ''): self
{
return new self($id, Constants::SERVER_ERROR, $message);
return new self($id, self::SERVER_ERROR, $message);
}
public function getId(): string|int
@ -121,7 +128,7 @@ class Error implements MessageInterface
}
return [
'jsonrpc' => Constants::JSONRPC_VERSION,
'jsonrpc' => MessageInterface::JSONRPC_VERSION,
'id' => $this->id,
'error' => $error,
];

View file

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Schema\JsonRpc;
/**
* Interface for all incoming JSON-RPC messages that should be processed by a handler, and are expected to have a method.
*
* @author Christopher Hertel <mail@christopher-hertel.de>
*/
interface HasMethodInterface
{
public static function getMethod(): string;
/**
* @param array<string, mixed> $data
*/
public static function fromArray(array $data): self;
}

View file

@ -18,4 +18,5 @@ namespace Mcp\Schema\JsonRpc;
*/
interface MessageInterface extends \JsonSerializable
{
public const JSONRPC_VERSION = '2.0';
}

View file

@ -12,7 +12,6 @@
namespace Mcp\Schema\JsonRpc;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\Constants;
/**
* @phpstan-type NotificationData array{
@ -23,17 +22,14 @@ use Mcp\Schema\Constants;
*
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
class Notification implements MessageInterface
abstract class Notification implements HasMethodInterface, MessageInterface
{
/**
* @param string $method the name of the method to be invoked
* @param ?array<string, mixed> $params parameters for the method
* @var array<string, mixed>|null
*/
public function __construct(
public readonly string $method,
public readonly ?array $params = null,
) {
}
protected ?array $meta;
abstract public static function getMethod(): string;
/**
* @param NotificationData $data
@ -51,16 +47,19 @@ class Notification implements MessageInterface
throw new InvalidArgumentException('"params" for Notification must be an array/object or null.');
}
return new self($data['method'], $params);
$notification = static::fromParams($params);
if (isset($data['params']['_meta'])) {
$notification->meta = $data['params']['_meta'];
}
return $notification;
}
/**
* @return null
* @param array<string, mixed>|null $params
*/
public function getId()
{
return null;
}
abstract protected static function fromParams(?array $params): self;
/**
* @return NotificationData
@ -68,13 +67,22 @@ class Notification implements MessageInterface
public function jsonSerialize(): array
{
$array = [
'jsonrpc' => Constants::JSONRPC_VERSION,
'method' => $this->method,
'jsonrpc' => MessageInterface::JSONRPC_VERSION,
'method' => static::getMethod(),
];
if (null !== $this->params) {
$array['params'] = $this->params;
if (null !== $params = $this->getParams()) {
$array['params'] = $params;
}
if (null !== $this->meta && !isset($params['meta'])) {
$array['params']['_meta'] = $this->meta;
}
return $array;
}
/**
* @return array<string, mixed>|null
*/
abstract protected function getParams(): ?array;
}

View file

@ -1,80 +0,0 @@
<?php
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Schema\JsonRpc;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\Constants;
/**
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
class Parser
{
/**
* Parses a raw JSON string into a JSON-RPC Message object (Request, Notification, Response, Error, or Batch variants).
*
* This method determines if the incoming message is a request-like message (Request, Notification, BatchRequest)
* or a response-like message (Response, Error, BatchResponse) based on the presence of 'method' vs 'result'/'error'.
*
* @param string $json the raw JSON string to parse
*
* @return MessageInterface a specific instance of Request, Notification, Response, Error, BatchRequest, or BatchResponse
*
* @throws \JsonException if the string is not valid JSON
* @throws InvalidArgumentException if the JSON structure does not conform to a recognizable JSON-RPC message type
*/
public static function parse(string $json): MessageInterface
{
$data = json_decode($json, true, 512, \JSON_THROW_ON_ERROR);
if (!\is_array($data)) {
throw new InvalidArgumentException('Invalid JSON-RPC message: Root must be an object or array.');
}
if (array_is_list($data) && !empty($data)) {
$firstItem = $data[0];
if (!\is_array($firstItem)) {
throw new InvalidArgumentException('Invalid JSON-RPC batch: Items must be objects.');
}
if (isset($firstItem['method'])) {
return BatchRequest::fromArray($data);
} elseif (isset($firstItem['id']) && (isset($firstItem['result']) || isset($firstItem['error']))) {
return BatchResponse::fromArray($data);
} else {
throw new InvalidArgumentException('Invalid JSON-RPC batch: Items are not recognizable requests or responses.');
}
}
if (!isset($data['jsonrpc']) || Constants::JSONRPC_VERSION !== $data['jsonrpc']) {
throw new InvalidArgumentException('Invalid or missing "jsonrpc" version. Must be "'.Constants::JSONRPC_VERSION.'".');
}
if (isset($data['method'])) {
if (isset($data['id'])) {
return Request::fromArray($data);
} else {
return Notification::fromArray($data);
}
} elseif (isset($data['id'])) {
if (\array_key_exists('result', $data)) {
return Response::fromArray($data);
} elseif (isset($data['error'])) {
return Error::fromArray($data);
} else {
throw new InvalidArgumentException('Invalid JSON-RPC response/error: Missing "result" or "error" field for message with "id".');
}
}
throw new InvalidArgumentException('Unrecognized JSON-RPC message structure.');
}
}

View file

@ -12,7 +12,6 @@
namespace Mcp\Schema\JsonRpc;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\Constants;
/**
* @phpstan-type RequestData array{
@ -24,31 +23,22 @@ use Mcp\Schema\Constants;
*
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
class Request implements MessageInterface
abstract class Request implements HasMethodInterface, MessageInterface
{
protected string|int $id;
/**
* @param string|int $id a unique identifier for the request
* @param string $method the name of the method to be invoked
* @param array<string, mixed>|null $params parameters for the method
* @var array<string, mixed>|null
*/
public function __construct(
public readonly string|int $id,
public readonly string $method,
public readonly ?array $params = null,
) {
}
protected ?array $meta;
public function getId(): string|int
{
return $this->id;
}
abstract public static function getMethod(): string;
/**
* @param RequestData $data
*/
public static function fromArray(array $data): self
{
if (($data['jsonrpc'] ?? null) !== Constants::JSONRPC_VERSION) {
if (($data['jsonrpc'] ?? null) !== MessageInterface::JSONRPC_VERSION) {
throw new InvalidArgumentException('Invalid or missing "jsonrpc" version for Request.');
}
if (!isset($data['id']) || !\is_string($data['id']) && !\is_int($data['id'])) {
@ -65,7 +55,24 @@ class Request implements MessageInterface
throw new InvalidArgumentException('"params" for Request must be an array/object or null.');
}
return new self($data['id'], $data['method'], $params);
$request = static::fromParams($params);
$request->id = $data['id'];
if (isset($data['params']['_meta'])) {
$request->meta = $data['params']['_meta'];
}
return $request;
}
/**
* @param array<string, mixed>|null $params
*/
abstract protected static function fromParams(?array $params): self;
public function getId(): string|int
{
return $this->id;
}
/**
@ -74,14 +81,23 @@ class Request implements MessageInterface
public function jsonSerialize(): array
{
$array = [
'jsonrpc' => Constants::JSONRPC_VERSION,
'jsonrpc' => MessageInterface::JSONRPC_VERSION,
'id' => $this->id,
'method' => $this->method,
'method' => static::getMethod(),
];
if (null !== $this->params) {
$array['params'] = $this->params;
if (null !== $params = $this->getParams()) {
$array['params'] = $params;
}
if (null !== $this->meta && !isset($params['meta'])) {
$array['params']['_meta'] = $this->meta;
}
return $array;
}
/**
* @return array<string, mixed>|null
*/
abstract protected function getParams(): ?array;
}

View file

@ -12,7 +12,6 @@
namespace Mcp\Schema\JsonRpc;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\Constants;
/**
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
@ -45,7 +44,7 @@ class Response implements MessageInterface
*/
public static function fromArray(array $data): self
{
if (($data['jsonrpc'] ?? null) !== Constants::JSONRPC_VERSION) {
if (($data['jsonrpc'] ?? null) !== MessageInterface::JSONRPC_VERSION) {
throw new InvalidArgumentException('Invalid or missing "jsonrpc" version for Response.');
}
if (!isset($data['id'])) {
@ -71,7 +70,7 @@ class Response implements MessageInterface
public function jsonSerialize(): array
{
return [
'jsonrpc' => Constants::JSONRPC_VERSION,
'jsonrpc' => MessageInterface::JSONRPC_VERSION,
'id' => $this->id,
'result' => $this->result,
];

View file

@ -24,6 +24,13 @@ namespace Mcp\Schema;
* up to the client to decide how to interpret these preferences and how to
* balance them against other considerations.
*
* @phpstan-type ModelPreferencesData array{
* hints?: ModelHint[],
* costPriority?: float,
* speedPriority?: float,
* intelligencePriority?: float,
* }
*
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
class ModelPreferences implements \JsonSerializable
@ -50,12 +57,20 @@ class ModelPreferences implements \JsonSerializable
}
/**
* @return array{
* hints?: ModelHint[],
* costPriority?: float,
* speedPriority?: float,
* intelligencePriority?: float,
* }
* @param ModelPreferencesData $preferences
*/
public static function fromArray(array $preferences): self
{
return new self(
$preferences['hints'] ?? null,
$preferences['costPriority'] ?? null,
$preferences['speedPriority'] ?? null,
$preferences['intelligencePriority'] ?? null,
);
}
/**
* @return ModelPreferencesData
*/
public function jsonSerialize(): array
{

View file

@ -28,40 +28,37 @@ use Mcp\Schema\JsonRpc\Notification;
class CancelledNotification extends Notification
{
/**
* @param string $requestId The ID of the request that is being cancelled. This MUST correspond to the ID of a request previously issued in the same direction.
* @param ?string $reason An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.
* @param ?array<string, mixed> $_meta additional metadata about the notification
* @param string|int $requestId The ID of the request that is being cancelled. This MUST correspond to the ID of a request previously issued in the same direction.
* @param ?string $reason An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.
*/
public function __construct(
public readonly string $requestId,
public readonly string|int $requestId,
public readonly ?string $reason = null,
public readonly ?array $_meta = null,
) {
$params = [
'requestId' => $this->requestId,
];
}
public static function getMethod(): string
{
return 'notifications/cancelled';
}
protected static function fromParams(?array $params): Notification
{
if (null === $params || !isset($params['requestId']) || (!\is_string($params['requestId']) && !\is_int($params['requestId']))) {
throw new InvalidArgumentException('Invalid or missing "requestId" parameter for "notifications/cancelled" notification.');
}
return new self($params['requestId'], $params['reason'] ?? null);
}
protected function getParams(): ?array
{
$params = ['requestId' => $this->requestId];
if (null !== $this->reason) {
$params['reason'] = $this->reason;
}
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct('notifications/cancelled', $params);
}
public static function fromNotification(Notification $notification): self
{
if ('notifications/cancelled' !== $notification->method) {
throw new InvalidArgumentException('Notification is not a notifications/cancelled notification');
}
$params = $notification->params;
if (!isset($params['requestId']) || !\is_string($params['requestId'])) {
throw new InvalidArgumentException('Missing or invalid requestId parameter for notifications/cancelled notification');
}
return new self($params['requestId'], $params['reason'] ?? null, $params['_meta'] ?? null);
return $params;
}
}

View file

@ -11,7 +11,6 @@
namespace Mcp\Schema\Notification;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\JsonRpc\Notification;
/**
@ -21,27 +20,18 @@ use Mcp\Schema\JsonRpc\Notification;
*/
class InitializedNotification extends Notification
{
/**
* @param array<string, mixed>|null $_meta
*/
public function __construct(
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct('notifications/initialized', $params);
public static function getMethod(): string
{
return 'notifications/initialized';
}
public static function fromNotification(Notification $notification): self
public static function fromParams(?array $params): self
{
if ('notifications/initialized' !== $notification->method) {
throw new InvalidArgumentException('Notification is not a notifications/initialized notification');
}
return new self();
}
return new self($notification->params['_meta'] ?? null);
protected function getParams(): ?array
{
return null;
}
}

View file

@ -21,51 +21,49 @@ use Mcp\Schema\JsonRpc\Notification;
class LoggingMessageNotification extends Notification
{
/**
* @param LoggingLevel $level the severity of this log message
* @param mixed $data The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here.
* @param string $logger an optional name of the logger issuing this message
* @param ?array<string, mixed> $_meta optional metadata to include in the notification
* @param LoggingLevel $level the severity of this log message
* @param mixed $data The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here.
* @param ?string $logger an optional name of the logger issuing this message
*/
public function __construct(
public readonly LoggingLevel $level,
public readonly mixed $data,
public readonly ?string $logger = null,
public readonly ?array $_meta = null,
) {
$params = [
'level' => $level->value,
'data' => \is_string($data) ? $data : json_encode($data),
];
if (null !== $logger) {
$params['logger'] = $logger;
}
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct('notifications/message', $params);
}
public static function fromNotification(Notification $notification): self
public static function getMethod(): string
{
if ('notifications/message' !== $notification->method) {
throw new InvalidArgumentException('Notification is not a notifications/message notification');
}
$params = $notification->params;
return 'notifications/message';
}
protected static function fromParams(?array $params): Notification
{
if (!isset($params['level']) || !\is_string($params['level'])) {
throw new InvalidArgumentException('Missing or invalid level parameter for notifications/message notification');
throw new InvalidArgumentException('Missing or invalid "level" parameter for "notifications/message" notification.');
}
if (!isset($params['data'])) {
throw new InvalidArgumentException('Missing data parameter for notifications/message notification');
throw new InvalidArgumentException('Missing "data" parameter for "notifications/message" notification.');
}
$level = LoggingLevel::from($params['level']);
$data = \is_string($params['data']) ? $params['data'] : json_encode($params['data']);
return new self($level, $params['data'], $params['logger'] ?? null, $params['_meta'] ?? null);
return new self($level, $data, $params['logger'] ?? null);
}
protected function getParams(): ?array
{
$params = [
'level' => $this->level->value,
'data' => $this->data,
];
if (null !== $this->logger) {
$params['logger'] = $this->logger;
}
return $params;
}
}

View file

@ -22,45 +22,59 @@ use Mcp\Schema\JsonRpc\Notification;
class ProgressNotification extends Notification
{
/**
* @param string|int $progressToken the progress token which was given in the initial request, used to
* associate this notification with the request that is proceeding
* @param float $progress The progress thus far. This should increase every time progress is
* made, even if the total is unknown.
* @param ?float $total total number of items to process (or total progress required), if known
* @param ?string $message an optional message describing the current progress
* @param ?array<string, mixed> $_meta optional metadata to include in the notification
* @param string|int $progressToken the progress token which was given in the initial request, used to
* associate this notification with the request that is proceeding
* @param float $progress The progress thus far. This should increase every time progress is
* made, even if the total is unknown.
* @param ?float $total total number of items to process (or total progress required), if known
* @param ?string $message an optional message describing the current progress
*/
public function __construct(
public readonly string|int $progressToken,
public readonly float $progress,
public readonly ?float $total = null,
public readonly ?string $message = null,
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct('notifications/progress', $params);
}
public static function fromNotification(Notification $notification): self
public static function getMethod(): string
{
if ('notifications/progress' !== $notification->method) {
throw new InvalidArgumentException('Notification is not a notifications/progress notification');
}
$params = $notification->params;
return 'notifications/progress';
}
protected static function fromParams(?array $params): Notification
{
if (!isset($params['progressToken']) || !\is_string($params['progressToken'])) {
throw new InvalidArgumentException('Missing or invalid progressToken parameter for notifications/progress notification');
throw new InvalidArgumentException('Missing or invalid "progressToken" parameter for "notifications/progress" notification.');
}
if (!isset($params['progress']) || !\is_float($params['progress'])) {
throw new InvalidArgumentException('Missing or invalid progress parameter for notifications/progress notification');
throw new InvalidArgumentException('Missing or invalid "progress" parameter for "notifications/progress" notification.');
}
return new self($params['progressToken'], $params['progress'], $params['total'] ?? null, $params['message'] ?? null, $params['_meta'] ?? null);
return new self(
$params['progressToken'],
$params['progress'],
$params['total'] ?? null,
$params['message'] ?? null,
);
}
protected function getParams(): ?array
{
$params = [
'progressToken' => $this->progressToken,
'progress' => $this->progress,
];
if (null !== $this->total) {
$params['total'] = $this->total;
}
if (null !== $this->message) {
$params['message'] = $this->message;
}
return $params;
}
}

View file

@ -11,7 +11,6 @@
namespace Mcp\Schema\Notification;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\JsonRpc\Notification;
/**
@ -21,26 +20,18 @@ use Mcp\Schema\JsonRpc\Notification;
*/
class PromptListChangedNotification extends Notification
{
/**
* @param array<string, mixed>|null $_meta
*/
public function __construct(
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct('notifications/prompts/list_changed', $params);
public static function getMethod(): string
{
return 'notifications/prompts/list_changed';
}
public static function fromNotification(Notification $notification): self
protected static function fromParams(?array $params): Notification
{
if ('notifications/prompts/list_changed' !== $notification->method) {
throw new InvalidArgumentException('Notification is not a notifications/prompts/list_changed notification');
}
return new self();
}
return new self($notification->params['_meta'] ?? null);
protected function getParams(): ?array
{
return null;
}
}

View file

@ -11,7 +11,6 @@
namespace Mcp\Schema\Notification;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\JsonRpc\Notification;
/**
@ -21,26 +20,18 @@ use Mcp\Schema\JsonRpc\Notification;
*/
class ResourceListChangedNotification extends Notification
{
/**
* @param array<string, mixed>|null $_meta
*/
public function __construct(
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct('notifications/resources/list_changed', $params);
public static function getMethod(): string
{
return 'notifications/resources/list_changed';
}
public static function fromNotification(Notification $notification): self
protected static function fromParams(?array $params): Notification
{
if ('notifications/resources/list_changed' !== $notification->method) {
throw new InvalidArgumentException('Notification is not a notifications/resources/list_changed notification');
}
return new self();
}
return new self($notification->params['_meta'] ?? null);
protected function getParams(): ?array
{
return null;
}
}

View file

@ -19,33 +19,29 @@ use Mcp\Schema\JsonRpc\Notification;
*/
class ResourceUpdatedNotification extends Notification
{
/**
* @param array<string, mixed>|null $_meta
*/
public function __construct(
public readonly string $uri,
public readonly ?array $_meta = null,
) {
$params = ['uri' => $uri];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct('notifications/resources/updated', $params);
}
public static function fromNotification(Notification $notification): self
public static function getMethod(): string
{
if ('notifications/resources/updated' !== $notification->method) {
throw new InvalidArgumentException('Notification is not a notifications/resources/updated notification');
return 'notifications/resources/updated';
}
protected static function fromParams(?array $params): Notification
{
if (null === $params || !isset($params['uri']) || !\is_string($params['uri'])) {
throw new InvalidArgumentException('Invalid or missing "uri" parameter for notifications/resources/updated notification.');
}
$params = $notification->params;
return new self($params['uri']);
}
if (!isset($params['uri']) || !\is_string($params['uri'])) {
throw new InvalidArgumentException('Missing or invalid uri parameter for notifications/resources/updated notification');
}
return new self($params['uri'], $params['_meta'] ?? null);
protected function getParams(): ?array
{
return [
'uri' => $this->uri,
];
}
}

View file

@ -11,7 +11,6 @@
namespace Mcp\Schema\Notification;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\JsonRpc\Notification;
/**
@ -23,26 +22,18 @@ use Mcp\Schema\JsonRpc\Notification;
*/
class RootsListChangedNotification extends Notification
{
/**
* @param array<string, mixed>|null $_meta
*/
public function __construct(
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct('notifications/roots/list_changed', $params);
public static function getMethod(): string
{
return 'notifications/roots/list_changed';
}
public static function fromNotification(Notification $notification): self
protected static function fromParams(?array $params): Notification
{
if ('notifications/roots/list_changed' !== $notification->method) {
throw new InvalidArgumentException('Notification is not a notifications/roots/list_changed notification');
}
return new self();
}
return new self($notification->params['_meta'] ?? null);
protected function getParams(): ?array
{
return null;
}
}

Some files were not shown because too many files have changed in this diff Show more