Kick off the SDK based on Symfony's MCP SDK

This commit is contained in:
Christopher Hertel 2025-07-25 11:40:39 +02:00
commit ee0dffe369
No known key found for this signature in database
GPG key ID: 111F6BFF7B6B5AA3
60 changed files with 3924 additions and 0 deletions

View file

@ -0,0 +1,29 @@
<?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\Capability\Prompt;
use Mcp\Exception\InvalidCursorException;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface CollectionInterface
{
/**
* @param int $count the number of metadata items to return
*
* @return iterable<MetadataInterface>
*
* @throws InvalidCursorException if no item with $lastIdentifier was found
*/
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable;
}

View file

@ -0,0 +1,20 @@
<?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\Capability\Prompt;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface IdentifierInterface
{
public function getName(): string;
}

View file

@ -0,0 +1,29 @@
<?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\Capability\Prompt;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface MetadataInterface extends IdentifierInterface
{
public function getDescription(): ?string;
/**
* @return list<array{
* name: string,
* description?: string,
* required?: bool,
* }>
*/
public function getArguments(): array;
}

View file

@ -0,0 +1,28 @@
<?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\Capability\Prompt;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class PromptGet
{
/**
* @param array<string, mixed> $arguments
*/
public function __construct(
public readonly string $id,
public readonly string $name,
public readonly array $arguments = [],
) {
}
}

View file

@ -0,0 +1,27 @@
<?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\Capability\Prompt;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class PromptGetResult
{
/**
* @param list<PromptGetResultMessages> $messages
*/
public function __construct(
public readonly string $description,
public readonly array $messages = [],
) {
}
}

View file

@ -0,0 +1,30 @@
<?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\Capability\Prompt;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class PromptGetResultMessages
{
public function __construct(
public readonly string $role,
public readonly string $result,
/**
* @var "text"|"image"|"audio"|"resource"|non-empty-string
*/
public readonly string $type = 'text',
public readonly string $mimeType = 'text/plan',
public readonly ?string $uri = null,
) {
}
}

View file

@ -0,0 +1,27 @@
<?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\Capability\Prompt;
use Mcp\Exception\PromptGetException;
use Mcp\Exception\PromptNotFoundException;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface PromptGetterInterface
{
/**
* @throws PromptGetException if the prompt execution fails
* @throws PromptNotFoundException if the prompt is not found
*/
public function get(PromptGet $input): PromptGetResult;
}

View file

@ -0,0 +1,77 @@
<?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\Capability;
use Mcp\Capability\Prompt\CollectionInterface;
use Mcp\Capability\Prompt\IdentifierInterface;
use Mcp\Capability\Prompt\MetadataInterface;
use Mcp\Capability\Prompt\PromptGet;
use Mcp\Capability\Prompt\PromptGetResult;
use Mcp\Capability\Prompt\PromptGetterInterface;
use Mcp\Exception\InvalidCursorException;
use Mcp\Exception\PromptGetException;
use Mcp\Exception\PromptNotFoundException;
/**
* A collection of prompts. All prompts need to implement IdentifierInterface.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class PromptChain implements PromptGetterInterface, CollectionInterface
{
public function __construct(
/**
* @var IdentifierInterface[]
*/
private readonly array $items,
) {
}
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable
{
$found = null === $lastIdentifier;
foreach ($this->items as $item) {
if (!$item instanceof MetadataInterface) {
continue;
}
if (false === $found) {
$found = $item->getName() === $lastIdentifier;
continue;
}
yield $item;
if (--$count <= 0) {
break;
}
}
if (!$found) {
throw new InvalidCursorException($lastIdentifier);
}
}
public function get(PromptGet $input): PromptGetResult
{
foreach ($this->items as $item) {
if ($item instanceof PromptGetterInterface && $input->name === $item->getName()) {
try {
return $item->get($input);
} catch (\Throwable $e) {
throw new PromptGetException($input, $e);
}
}
}
throw new PromptNotFoundException($input);
}
}

View file

@ -0,0 +1,29 @@
<?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\Capability\Resource;
use Mcp\Exception\InvalidCursorException;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface CollectionInterface
{
/**
* @param int $count the number of metadata items to return
*
* @return iterable<MetadataInterface>
*
* @throws InvalidCursorException if no item with $lastIdentifier was found
*/
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable;
}

View file

@ -0,0 +1,20 @@
<?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\Capability\Resource;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface IdentifierInterface
{
public function getUri(): string;
}

View file

@ -0,0 +1,29 @@
<?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\Capability\Resource;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface MetadataInterface extends IdentifierInterface
{
public function getName(): string;
public function getDescription(): ?string;
public function getMimeType(): ?string;
/**
* Size in bytes.
*/
public function getSize(): ?int;
}

View file

@ -0,0 +1,24 @@
<?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\Capability\Resource;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class ResourceRead
{
public function __construct(
public readonly string $id,
public readonly string $uri,
) {
}
}

View file

@ -0,0 +1,30 @@
<?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\Capability\Resource;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class ResourceReadResult
{
public function __construct(
public readonly string $result,
public readonly string $uri,
/**
* @var "text"|"blob"
*/
public readonly string $type = 'text',
public readonly string $mimeType = 'text/plain',
) {
}
}

View file

@ -0,0 +1,27 @@
<?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\Capability\Resource;
use Mcp\Exception\ResourceNotFoundException;
use Mcp\Exception\ResourceReadException;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface ResourceReaderInterface
{
/**
* @throws ResourceReadException if the resource execution fails
* @throws ResourceNotFoundException if the resource is not found
*/
public function read(ResourceRead $input): ResourceReadResult;
}

View file

@ -0,0 +1,77 @@
<?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\Capability;
use Mcp\Capability\Resource\CollectionInterface;
use Mcp\Capability\Resource\IdentifierInterface;
use Mcp\Capability\Resource\MetadataInterface;
use Mcp\Capability\Resource\ResourceRead;
use Mcp\Capability\Resource\ResourceReaderInterface;
use Mcp\Capability\Resource\ResourceReadResult;
use Mcp\Exception\InvalidCursorException;
use Mcp\Exception\ResourceNotFoundException;
use Mcp\Exception\ResourceReadException;
/**
* A collection of resources. All resources need to implement IdentifierInterface.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class ResourceChain implements CollectionInterface, ResourceReaderInterface
{
public function __construct(
/**
* @var IdentifierInterface[]
*/
private readonly array $items,
) {
}
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable
{
$found = null === $lastIdentifier;
foreach ($this->items as $item) {
if (!$item instanceof MetadataInterface) {
continue;
}
if (false === $found) {
$found = $item->getUri() === $lastIdentifier;
continue;
}
yield $item;
if (--$count <= 0) {
break;
}
}
if (!$found) {
throw new InvalidCursorException($lastIdentifier);
}
}
public function read(ResourceRead $input): ResourceReadResult
{
foreach ($this->items as $item) {
if ($item instanceof ResourceReaderInterface && $input->uri === $item->getUri()) {
try {
return $item->read($input);
} catch (\Throwable $e) {
throw new ResourceReadException($input, $e);
}
}
}
throw new ResourceNotFoundException($input);
}
}

View file

@ -0,0 +1,29 @@
<?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\Capability\Tool;
use Mcp\Exception\InvalidCursorException;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface CollectionInterface
{
/**
* @param int $count the number of metadata items to return
*
* @return iterable<MetadataInterface>
*
* @throws InvalidCursorException if no item with $lastIdentifier was found
*/
public function getMetadata(int $count, ?string $lastIdentifier = null): iterable;
}

View file

@ -0,0 +1,20 @@
<?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\Capability\Tool;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface IdentifierInterface
{
public function getName(): string;
}

View file

@ -0,0 +1,32 @@
<?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\Capability\Tool;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
interface MetadataInterface extends IdentifierInterface
{
public function getDescription(): string;
/**
* @return array{
* type?: string,
* required?: list<string>,
* properties?: array<string, array{
* type: string,
* description?: string,
* }>,
* }
*/
public function getInputSchema(): array;
}

View file

@ -0,0 +1,28 @@
<?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\Capability\Tool;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class ToolCall
{
/**
* @param array<string, mixed> $arguments
*/
public function __construct(
public readonly string $id,
public readonly string $name,
public readonly array $arguments = [],
) {
}
}

View file

@ -0,0 +1,30 @@
<?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\Capability\Tool;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
final class ToolCallResult
{
public function __construct(
public readonly string $result,
/**
* @var "text"|"image"|"audio"|"resource"|non-empty-string
*/
public readonly string $type = 'text',
public readonly string $mimeType = 'text/plan',
public readonly bool $isError = false,
public readonly ?string $uri = null,
) {
}
}

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