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
15 changed files with 1516 additions and 2424 deletions

View file

@ -22,36 +22,22 @@ use Mcp\Schema\JsonRpc\Request;
class CallToolRequest extends Request
{
/**
* @param string $name the name of the tool to invoke
* @param array<string, mixed> $arguments the arguments to pass to the tool
* @param ?array<string, mixed> $_meta optional metadata to include in the request
* @param string $name the name of the tool to invoke
* @param array<string, mixed> $arguments the arguments to pass to the tool
*/
public function __construct(
string|int $id,
public readonly string $name,
public readonly array $arguments,
public readonly ?array $_meta = null,
) {
$params = [
'name' => $name,
'arguments' => (object) $arguments,
];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct($id, 'tools/call', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('tools/call' !== $request->method) {
throw new InvalidArgumentException('Request is not a call tool request');
}
$params = $request->params ?? [];
return 'tools/call';
}
protected static function fromParams(?array $params): Request
{
if (!isset($params['name']) || !\is_string($params['name'])) {
throw new InvalidArgumentException('Missing or invalid "name" parameter for tools/call.');
}
@ -67,10 +53,16 @@ class CallToolRequest extends Request
}
return new self(
$request->id,
$params['name'],
$arguments,
$params['_meta'] ?? null
);
}
protected function getParams(): ?array
{
return [
'name' => $this->name,
'arguments' => $this->arguments,
];
}
}

View file

@ -26,34 +26,20 @@ class CompletionCompleteRequest extends Request
/**
* @param PromptReference|ResourceReference $ref the prompt or resource to complete
* @param array{ name: string, value: string } $argument the argument to complete
* @param ?array<string, mixed> $_meta optional metadata to include in the request
*/
public function __construct(
string|int $id,
public readonly PromptReference|ResourceReference $ref,
public readonly array $argument,
public readonly ?array $_meta = null,
) {
$params = [
'ref' => $this->ref,
'argument' => $this->argument,
];
if (null !== $this->_meta) {
$params['_meta'] = $this->_meta;
}
parent::__construct($id, 'completion/complete', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('completion/complete' !== $request->method) {
throw new InvalidArgumentException('Request is not a completion/complete request');
}
$params = $request->params;
return 'completion/complete';
}
protected static function fromParams(?array $params): Request
{
if (!isset($params['ref']) || !\is_array($params['ref'])) {
throw new InvalidArgumentException('Missing or invalid "ref" parameter for completion/complete.');
}
@ -68,6 +54,14 @@ class CompletionCompleteRequest extends Request
throw new InvalidArgumentException('Missing or invalid "argument" parameter for completion/complete.');
}
return new self($request->id, $ref, $params['argument'], $params['_meta'] ?? null);
return new self($ref, $params['argument']);
}
protected function getParams(): ?array
{
return [
'ref' => $this->ref,
'argument' => $this->argument,
];
}
}

View file

@ -40,10 +40,8 @@ class CreateSamplingMessageRequest extends Request
* @param string[]|null $stopSequences A list of sequences to stop sampling at. The client MAY ignore this request.
* @param ?array<string, mixed> $metadata Optional metadata to pass through to the LLM provider. The format of
* this metadata is provider-specific.
* @param ?array<string, mixed> $_meta optional metadata to include in the request
*/
public function __construct(
string|int $id,
public readonly array $messages,
public readonly int $maxTokens,
public readonly ?ModelPreferences $preferences = null,
@ -52,8 +50,43 @@ class CreateSamplingMessageRequest extends Request
public readonly ?float $temperature = null,
public readonly ?array $stopSequences = null,
public readonly ?array $metadata = null,
public readonly ?array $_meta = null,
) {
}
public static function getMethod(): string
{
return 'sampling/createMessage';
}
protected static function fromParams(?array $params): Request
{
if (!isset($params['messages']) || !\is_array($params['messages'])) {
throw new \InvalidArgumentException('Missing or invalid "messages" parameter for sampling/createMessage.');
}
if (!isset($params['maxTokens']) || !\is_int($params['maxTokens'])) {
throw new \InvalidArgumentException('Missing or invalid "maxTokens" parameter for sampling/createMessage.');
}
$preferences = null;
if (isset($params['preferences'])) {
$preferences = ModelPreferences::fromArray($params['preferences']);
}
return new self(
$params['messages'],
$params['maxTokens'],
$preferences,
$params['systemPrompt'] ?? null,
$params['includeContext'] ?? null,
$params['temperature'] ?? null,
$params['stopSequences'] ?? null,
$params['metadata'] ?? null,
);
}
protected function getParams(): ?array
{
$params = [
'messages' => $this->messages,
'maxTokens' => $this->maxTokens,
@ -83,10 +116,6 @@ class CreateSamplingMessageRequest extends Request
$params['metadata'] = $this->metadata;
}
if (null !== $this->_meta) {
$params['_meta'] = $this->_meta;
}
parent::__construct($id, 'sampling/createMessage', $params);
return $params;
}
}

View file

@ -22,47 +22,47 @@ use Mcp\Schema\JsonRpc\Request;
class GetPromptRequest extends Request
{
/**
* @param string|int $id the ID of the request to cancel
* @param string $name the name of the prompt to get
* @param array<string, mixed>|null $arguments the arguments to pass to the prompt
* @param ?array<string, mixed> $_meta optional metadata to include in the request
*/
public function __construct(
string|int $id,
public readonly string $name,
public readonly ?array $arguments = null,
public readonly ?array $_meta = null,
) {
$params = ['name' => $name];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
if (null !== $arguments) {
$params['arguments'] = (object) $arguments;
}
parent::__construct($id, 'prompts/get', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('prompts/get' !== $request->method) {
throw new InvalidArgumentException('Request is not a prompts/get request');
}
$params = $request->params;
return 'prompts/get';
}
protected static function fromParams(?array $params): Request
{
if (!isset($params['name']) || !\is_string($params['name']) || empty($params['name'])) {
throw new InvalidArgumentException('Missing or invalid "name" parameter for prompts/get.');
}
$arguments = $params['arguments'] ?? new \stdClass();
if (!\is_array($arguments) && !$arguments instanceof \stdClass) {
throw new InvalidArgumentException('Parameter "arguments" must be an object/array for prompts/get.');
$arguments = $params['arguments'] ?? null;
if (null !== $arguments) {
if ($arguments instanceof \stdClass) {
$arguments = (array) $arguments;
}
if (!\is_array($arguments)) {
throw new InvalidArgumentException('Parameter "arguments" must be an array for prompts/get.');
}
}
return new self($request->id, $params['name'], $arguments, $params['_meta'] ?? null);
return new self($params['name'], $arguments);
}
protected function getParams(): ?array
{
$params = ['name' => $this->name];
if (null !== $this->arguments) {
$params['arguments'] = $this->arguments;
}
return $params;
}
}

View file

@ -24,40 +24,24 @@ use Mcp\Schema\JsonRpc\Request;
class InitializeRequest extends Request
{
/**
* @param string|int $id the ID of the request
* @param string $protocolVersion The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well.
* @param ClientCapabilities $capabilities the capabilities of the client
* @param Implementation $clientInfo information about the client
* @param ?array<string, mixed> $_meta additional metadata about the request
* @param string $protocolVersion The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well.
* @param ClientCapabilities $capabilities the capabilities of the client
* @param Implementation $clientInfo information about the client
*/
public function __construct(
string|int $id,
public readonly string $protocolVersion,
public readonly ClientCapabilities $capabilities,
public readonly Implementation $clientInfo,
public readonly ?array $_meta = null,
) {
$params = [
'protocolVersion' => $this->protocolVersion,
'capabilities' => $this->capabilities,
'clientInfo' => $this->clientInfo,
];
if (null !== $this->_meta) {
$params['_meta'] = $this->_meta;
}
parent::__construct($id, 'initialize', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('initialize' !== $request->method) {
throw new InvalidArgumentException('Request is not an initialize request');
}
$params = $request->params;
return 'initialize';
}
protected static function fromParams(?array $params): Request
{
if (!isset($params['protocolVersion'])) {
throw new InvalidArgumentException('protocolVersion is required');
}
@ -72,6 +56,15 @@ class InitializeRequest extends Request
}
$clientInfo = Implementation::fromArray($params['clientInfo']);
return new self($request->id, $params['protocolVersion'], $capabilities, $clientInfo, $params['_meta'] ?? null);
return new self($params['protocolVersion'], $capabilities, $clientInfo);
}
protected function getParams(): ?array
{
return [
'protocolVersion' => $this->protocolVersion,
'capabilities' => $this->capabilities,
'clientInfo' => $this->clientInfo,
];
}
}

View file

@ -11,7 +11,6 @@
namespace Mcp\Schema\Request;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\JsonRpc\Request;
/**
@ -24,31 +23,30 @@ class ListPromptsRequest extends Request
/**
* If provided, the server should return results starting after this cursor.
*
* @param string|null $cursor an opaque token representing the current pagination position
* @param array<string, mixed>|null $_meta optional metadata to include in the request
* @param string|null $cursor an opaque token representing the current pagination position
*/
public function __construct(
string|int $id,
public readonly ?string $cursor = null,
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $cursor) {
$params['cursor'] = $cursor;
}
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct($id, 'prompts/list', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('prompts/list' !== $request->method) {
throw new InvalidArgumentException('Request is not a prompts/list request');
return 'prompts/list';
}
protected static function fromParams(?array $params): Request
{
return new self($params['cursor'] ?? null);
}
protected function getParams(): ?array
{
$params = [];
if (null !== $this->cursor) {
$params['cursor'] = $this->cursor;
}
return new self($request->id, $request->params['cursor'] ?? null, $request->params['_meta'] ?? null);
return $params ?: null;
}
}

View file

@ -11,7 +11,6 @@
namespace Mcp\Schema\Request;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\JsonRpc\Request;
/**
@ -25,30 +24,29 @@ class ListResourceTemplatesRequest extends Request
* @param string|null $cursor An opaque token representing the current pagination position.
*
* If provided, the server should return results starting after this cursor.
* @param ?array<string, mixed> $_meta optional metadata to include in the request
*/
public function __construct(
string|int $id,
public readonly ?string $cursor = null,
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $cursor) {
$params['cursor'] = $cursor;
}
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct($id, 'resources/templates/list', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('resources/templates/list' !== $request->method) {
throw new InvalidArgumentException('Request is not a list resource templates request');
return 'resources/templates/list';
}
protected static function fromParams(?array $params): Request
{
return new self($params['cursor'] ?? null);
}
protected function getParams(): ?array
{
$params = [];
if (null !== $this->cursor) {
$params['cursor'] = $this->cursor;
}
return new self($request->id, $request->params['cursor'] ?? null, $request->params['_meta'] ?? null);
return $params ?: null;
}
}

View file

@ -11,7 +11,6 @@
namespace Mcp\Schema\Request;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\JsonRpc\Request;
/**
@ -25,30 +24,29 @@ class ListResourcesRequest extends Request
* @param string|null $cursor An opaque token representing the current pagination position.
*
* If provided, the server should return results starting after this cursor.
* @param ?array<string, mixed> $_meta optional metadata to include in the request
*/
public function __construct(
string|int $id,
public readonly ?string $cursor = null,
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $cursor) {
$params['cursor'] = $cursor;
}
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct($id, 'resources/list', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('resources/list' !== $request->method) {
throw new InvalidArgumentException('Request is not a list resources request');
return 'resources/list';
}
protected static function fromParams(?array $params): Request
{
return new self($params['cursor'] ?? null);
}
protected function getParams(): ?array
{
$params = [];
if (null !== $this->cursor) {
$params['cursor'] = $this->cursor;
}
return new self($request->id, $request->params['cursor'] ?? null, $request->params['_meta'] ?? null);
return $params ?: null;
}
}

View file

@ -26,18 +26,22 @@ use Mcp\Schema\JsonRpc\Request;
*/
class ListRootsRequest extends Request
{
/**
* @param ?array<string, mixed> $_meta
*/
public function __construct(
string|int $id,
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
}
parent::__construct($id, 'roots/list', $params);
public static function getMethod(): string
{
return 'roots/list';
}
protected static function fromParams(?array $params): Request
{
return new self();
}
protected function getParams(): ?array
{
return null;
}
}

View file

@ -11,7 +11,6 @@
namespace Mcp\Schema\Request;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Schema\JsonRpc\Request;
/**
@ -25,30 +24,29 @@ class ListToolsRequest extends Request
* @param string|null $cursor An opaque token representing the current pagination position.
*
* If provided, the server should return results starting after this cursor.
* @param ?array<string, mixed> $_meta optional metadata to include in the request
*/
public function __construct(
string|int $id,
public readonly ?string $cursor = null,
public readonly ?array $_meta = null,
) {
$params = [];
if (null !== $cursor) {
$params['cursor'] = $cursor;
}
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct($id, 'tools/list', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('tools/list' !== $request->method) {
throw new InvalidArgumentException('Request is not a list tools request');
return 'tools/list';
}
protected static function fromParams(?array $params): Request
{
return new self($params['cursor'] ?? null);
}
protected function getParams(): ?array
{
$params = [];
if (null !== $this->cursor) {
$params['cursor'] = $this->cursor;
}
return new self($request->id, $request->params['cursor'] ?? null, $request->params['_meta'] ?? null);
return $params ?: null;
}
}

View file

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

View file

@ -22,37 +22,31 @@ use Mcp\Schema\JsonRpc\Request;
class ReadResourceRequest extends Request
{
/**
* @param string $uri the URI of the resource to read
* @param ?array<string, mixed> $_meta optional metadata to include in the request
* @param string $uri the URI of the resource to read
*/
public function __construct(
string|int $id,
public readonly string $uri,
public readonly ?array $_meta = null,
) {
$params = [
'uri' => $uri,
];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct($id, 'resources/read', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('resources/read' !== $request->method) {
throw new InvalidArgumentException('Request is not a read resource request');
}
$params = $request->params;
return 'resources/read';
}
protected static function fromParams(?array $params): Request
{
if (!isset($params['uri']) || !\is_string($params['uri']) || empty($params['uri'])) {
throw new InvalidArgumentException('Missing or invalid "uri" parameter for resources/read.');
}
return new self($request->id, $params['uri'], $params['_meta'] ?? null);
return new self($params['uri']);
}
protected function getParams(): ?array
{
return [
'uri' => $this->uri,
];
}
}

View file

@ -23,34 +23,29 @@ use Mcp\Schema\JsonRpc\Request;
class ResourceSubscribeRequest extends Request
{
/**
* @param string $uri the URI of the resource to subscribe to
* @param ?array<string, mixed> $_meta optional metadata to include in the request
* @param string $uri the URI of the resource to subscribe to
*/
public function __construct(
string|int $id,
public readonly string $uri,
public readonly ?array $_meta = null,
) {
$params = ['uri' => $uri];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct($id, 'resources/subscribe', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('resources/subscribe' !== $request->method) {
throw new InvalidArgumentException('Request is not a resource subscribe request');
}
$params = $request->params;
return 'resources/subscribe';
}
protected static function fromParams(?array $params): Request
{
if (!isset($params['uri']) || !\is_string($params['uri']) || empty($params['uri'])) {
throw new InvalidArgumentException('Missing or invalid "uri" parameter for resources/subscribe.');
}
return new self($request->id, $params['uri'], $params['_meta'] ?? null);
return new self($params['uri']);
}
protected function getParams(): ?array
{
return ['uri' => $this->uri];
}
}

View file

@ -23,34 +23,29 @@ use Mcp\Schema\JsonRpc\Request;
class ResourceUnsubscribeRequest extends Request
{
/**
* @param string $uri the URI of the resource to unsubscribe from
* @param ?array<string, mixed> $_meta optional metadata to include in the request
* @param string $uri the URI of the resource to unsubscribe from
*/
public function __construct(
string|int $id,
public readonly string $uri,
public readonly ?array $_meta = null,
) {
$params = ['uri' => $uri];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct($id, 'resources/unsubscribe', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('resources/unsubscribe' !== $request->method) {
throw new InvalidArgumentException('Request is not a resource unsubscribe request');
}
$params = $request->params;
return 'resources/unsubscribe';
}
protected static function fromParams(?array $params): Request
{
if (!isset($params['uri']) || !\is_string($params['uri']) || empty($params['uri'])) {
throw new InvalidArgumentException('Missing or invalid "uri" parameter for resources/unsubscribe.');
}
return new self($request->id, $params['uri'], $params['_meta'] ?? null);
return new self($params['uri']);
}
protected function getParams(): ?array
{
return ['uri' => $this->uri];
}
}

View file

@ -23,39 +23,33 @@ use Mcp\Schema\JsonRpc\Request;
class SetLogLevelRequest extends Request
{
/**
* @param LoggingLevel $level The level of logging that the client wants to receive from the server. The server
* should send all logs at this level and higher (i.e., more severe) to the client as
* notifications/message.
* @param ?array<string, mixed> $_meta optional metadata to include in the request
* @param LoggingLevel $level The level of logging that the client wants to receive from the server. The server
* should send all logs at this level and higher (i.e., more severe) to the client as
* notifications/message.
*/
public function __construct(
string|int $id,
public readonly LoggingLevel $level,
public readonly ?array $_meta = null,
) {
$params = [
'level' => $level->value,
];
if (null !== $_meta) {
$params['_meta'] = $_meta;
}
parent::__construct($id, 'logging/setLevel', $params);
}
public static function fromRequest(Request $request): self
public static function getMethod(): string
{
if ('logging/setLevel' !== $request->method) {
throw new InvalidArgumentException('Request is not a logging/setLevel request');
}
$params = $request->params;
return 'logging/setLevel';
}
protected static function fromParams(?array $params): self
{
if (!isset($params['level']) || !\is_string($params['level']) || empty($params['level'])) {
throw new InvalidArgumentException('Missing or invalid "level" parameter for logging/setLevel.');
throw new InvalidArgumentException('Missing or invalid "level" parameter for "logging/setLevel".');
}
return new self($request->id, LoggingLevel::from($params['level']), $params['_meta'] ?? null);
return new self(LoggingLevel::from($params['level']));
}
protected function getParams(): ?array
{
return [
'level' => $this->level->value,
];
}
}