Skip to content
Draft
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
31 changes: 22 additions & 9 deletions src/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,56 @@ class Response
use Conditionable;
use Macroable;

/**
* @param array<string, mixed>|null $meta
*/
protected function __construct(
protected Content $content,
protected Role $role = Role::USER,
protected bool $isError = false,
protected ?array $meta = null,
) {
//
}

/**
* @param array<string, mixed> $params
* @param array<string, mixed>|null $meta
*/
public static function notification(string $method, array $params = []): static
public static function notification(string $method, array $params = [], ?array $meta = null): static
{
return new static(new Notification($method, $params));
return new static(new Notification($method, $params, $meta));
}

public static function text(string $text): static
/**
* @param array<string, mixed>|null $meta
*/
public static function text(string $text, ?array $meta = null): static
{
return new static(new Text($text));
return new static(new Text($text, $meta));
}

/**
* @internal
*
* @param array<string, mixed>|null $meta
*
* @throws JsonException
*/
public static function json(mixed $content): static
public static function json(mixed $content, ?array $meta = null): static
{
return static::text(json_encode(
$content,
JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT,
));
), $meta);
}

public static function blob(string $content): static
/**
* @param array<string, mixed>|null $meta
*/
public static function blob(string $content, ?array $meta = null): static
{
return new static(new Blob($content));
return new static(new Blob($content, $meta));
}

public static function error(string $text): static
Expand Down Expand Up @@ -86,7 +99,7 @@ public static function image(): Content

public function asAssistant(): static
{
return new static($this->content, Role::ASSISTANT, $this->isError);
return new static($this->content, Role::ASSISTANT, $this->isError, $this->meta);
}

public function isNotification(): bool
Expand Down
22 changes: 15 additions & 7 deletions src/Server/Content/Blob.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,24 @@
namespace Laravel\Mcp\Server\Content;

use InvalidArgumentException;
use Laravel\Mcp\Server\Content\Concerns\HasMeta;
use Laravel\Mcp\Server\Contracts\Content;
use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Resource;
use Laravel\Mcp\Server\Tool;

class Blob implements Content
{
public function __construct(protected string $content)
{
//
use HasMeta;

/**
* @param array<string, mixed>|null $meta
*/
public function __construct(
protected string $content,
?array $meta = null
) {
$this->meta = $meta;
}

/**
Expand Down Expand Up @@ -42,13 +50,13 @@ public function toPrompt(Prompt $prompt): array
*/
public function toResource(Resource $resource): array
{
return [
return $this->withMeta([
'blob' => base64_encode($this->content),
'uri' => $resource->uri(),
'name' => $resource->name(),
'title' => $resource->title(),
'mimeType' => $resource->mimeType(),
];
]);
}

public function __toString(): string
Expand All @@ -61,9 +69,9 @@ public function __toString(): string
*/
public function toArray(): array
{
return [
return $this->withMeta([
'type' => 'blob',
'blob' => $this->content,
];
]);
}
}
24 changes: 24 additions & 0 deletions src/Server/Content/Concerns/HasMeta.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Laravel\Mcp\Server\Content\Concerns;

trait HasMeta
{
/**
* @var array<string, mixed>|null
*/
protected ?array $meta = null;

/**
* @param array<string, mixed> $baseArray
* @return array<string, mixed>
*/
protected function withMeta(array $baseArray): array
{
return ($meta = $this->meta)
? [...$baseArray, '_meta' => $meta]
: $baseArray;
}
}
16 changes: 13 additions & 3 deletions src/Server/Content/Notification.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@

namespace Laravel\Mcp\Server\Content;

use Laravel\Mcp\Server\Content\Concerns\HasMeta;
use Laravel\Mcp\Server\Contracts\Content;
use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Resource;
use Laravel\Mcp\Server\Tool;

class Notification implements Content
{
use HasMeta;

/**
* @param array<string, mixed> $params
* @param array<string, mixed>|null $meta
*/
public function __construct(protected string $method, protected array $params)
public function __construct(protected string $method, protected array $params, ?array $meta = null)
{
//
$this->meta = $meta;
}

/**
Expand Down Expand Up @@ -53,9 +57,15 @@ public function __toString(): string
*/
public function toArray(): array
{
$params = $this->params;

if ($this->meta !== null && $this->meta !== [] && ! isset($params['_meta'])) {
$params['_meta'] = $this->meta;
}

return [
'method' => $this->method,
'params' => $this->params,
'params' => $params,
];
}
}
22 changes: 15 additions & 7 deletions src/Server/Content/Text.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,24 @@

namespace Laravel\Mcp\Server\Content;

use Laravel\Mcp\Server\Content\Concerns\HasMeta;
use Laravel\Mcp\Server\Contracts\Content;
use Laravel\Mcp\Server\Prompt;
use Laravel\Mcp\Server\Resource;
use Laravel\Mcp\Server\Tool;

class Text implements Content
{
public function __construct(protected string $text)
{
//
use HasMeta;

/**
* @param array<string, mixed>|null $meta
*/
public function __construct(
protected string $text,
?array $meta = null
) {
$this->meta = $meta;
}

/**
Expand All @@ -37,13 +45,13 @@ public function toPrompt(Prompt $prompt): array
*/
public function toResource(Resource $resource): array
{
return [
return $this->withMeta([
'text' => $this->text,
'uri' => $resource->uri(),
'name' => $resource->name(),
'title' => $resource->title(),
'mimeType' => $resource->mimeType(),
];
]);
}

public function __toString(): string
Expand All @@ -56,9 +64,9 @@ public function __toString(): string
*/
public function toArray(): array
{
return [
return $this->withMeta([
'type' => 'text',
'text' => $this->text,
];
]);
}
}
26 changes: 26 additions & 0 deletions src/Server/Primitive.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ abstract class Primitive implements Arrayable

protected string $description = '';

/**
* @var array<string, mixed>|null
*/
protected ?array $meta = null;

public function name(): string
{
return $this->name === ''
Expand All @@ -40,6 +45,14 @@ public function description(): string
: $this->description;
}

/**
* @return array<string, mixed>|null
*/
public function meta(): ?array
{
return $this->meta;
}

public function eligibleForRegistration(): bool
{
if (method_exists($this, 'shouldRegister')) {
Expand All @@ -49,6 +62,19 @@ public function eligibleForRegistration(): bool
return true;
}

/**
* @template T of array<string, mixed>
*
* @param T $baseArray
* @return T&array{_meta?: array<string, mixed>}
*/
protected function withMeta(array $baseArray): array
{
return ($meta = $this->meta())
? [...$baseArray, '_meta' => $meta]
: $baseArray;
}

/**
* @return array<string, mixed>
*/
Expand Down
7 changes: 4 additions & 3 deletions src/Server/Prompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,19 @@ public function toMethodCall(): array
}

/**
* @return array{name: string, title: string, description: string, arguments: array<int, array{name: string, description: string, required: bool}>}
* @return array{name: string, title: string, description: string, arguments: array<int, array{name: string, description: string, required: bool, _meta?: array<string, mixed>}>}
*/
public function toArray(): array
{
return [
// @phpstan-ignore return.type
return $this->withMeta([
'name' => $this->name(),
'title' => $this->title(),
'description' => $this->description(),
'arguments' => array_map(
fn (Argument $argument): array => $argument->toArray(),
$this->arguments(),
),
];
]);
}
}
15 changes: 13 additions & 2 deletions src/Server/Resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,25 @@ public function toMethodCall(): array
return ['uri' => $this->uri()];
}

/**
* @return array{
* name: string,
* title: string,
* description: string,
* uri: string,
* mimeType: string,
* _meta?: array<string, mixed>
* }
*/
public function toArray(): array
{
return [
// @phpstan-ignore return.type
return $this->withMeta([
'name' => $this->name(),
'title' => $this->title(),
'description' => $this->description(),
'uri' => $this->uri(),
'mimeType' => $this->mimeType(),
];
]);
}
}
10 changes: 7 additions & 3 deletions src/Server/Tool.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,28 @@ public function toMethodCall(): array
* title?: string|null,
* description?: string|null,
* inputSchema?: array<string, mixed>,
* annotations?: array<string, mixed>|object
* annotations?: array<string, mixed>|object,
* _meta?: array<string, mixed>
* }
*/
public function toArray(): array
{
$annotations = $this->annotations();

$schema = JsonSchema::object(
$this->schema(...),
)->toArray();

$schema['properties'] ??= (object) [];

return [
// @phpstan-ignore return.type
return $this->withMeta([
'name' => $this->name(),
'title' => $this->title(),
'description' => $this->description(),
'inputSchema' => $schema,
'annotations' => $annotations === [] ? (object) [] : $annotations,
];
]);

}
}
8 changes: 8 additions & 0 deletions src/Server/Transport/JsonRpcRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@
return $this->params[$key] ?? $default;
}

/**
* @retrun array<string, mixed>|null
*/
public function meta(): ?array

Check failure on line 66 in src/Server/Transport/JsonRpcRequest.php

View workflow job for this annotation

GitHub Actions / PHP 8.4 - Laravel 12

Method Laravel\Mcp\Server\Transport\JsonRpcRequest::meta() return type has no value type specified in iterable type array.
{
return is_array($this->params['_meta']) ? $this->params['_meta'] : null;
}

public function toRequest(): Request
{
return new Request($this->params['arguments'] ?? [], $this->sessionId);
Expand Down
Loading
Loading