Kick off the SDK based on Symfony's MCP SDK
This commit is contained in:
parent
0044ffb365
commit
ee0dffe369
89 changed files with 3924 additions and 0 deletions
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
/.git* export-ignore
|
||||||
|
/examples export-ignore
|
||||||
|
/tests export-ignore
|
||||||
|
/.php-cs-fixer.dist.php export-ignore
|
||||||
|
/phpstan.dist.neon export-ignore
|
||||||
|
/phpunit.xml.dist export-ignore
|
||||||
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* @chr-hertel @Nyholm @CodeWithKyrian
|
||||||
64
.github/workflows/pipeline.yml
vendored
Normal file
64
.github/workflows/pipeline.yml
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
name: pipeline
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php: ['8.1', '8.2', '8.3', '8.4']
|
||||||
|
dependencies: ['lowest', 'highest']
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php }}
|
||||||
|
coverage: "none"
|
||||||
|
|
||||||
|
- name: Install Composer
|
||||||
|
uses: "ramsey/composer-install@v3"
|
||||||
|
with:
|
||||||
|
dependency-versions: "${{ matrix.dependencies }}"
|
||||||
|
|
||||||
|
- name: Composer Validation
|
||||||
|
run: composer validate --strict
|
||||||
|
|
||||||
|
- name: Install PHP Dependencies
|
||||||
|
run: composer install --no-scripts
|
||||||
|
|
||||||
|
- name: Tests
|
||||||
|
run: vendor/bin/phpunit
|
||||||
|
|
||||||
|
qa:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.1'
|
||||||
|
coverage: "none"
|
||||||
|
|
||||||
|
- name: Install Composer
|
||||||
|
uses: "ramsey/composer-install@v3"
|
||||||
|
|
||||||
|
- name: Composer Validation
|
||||||
|
run: composer validate --strict
|
||||||
|
|
||||||
|
- name: Install PHP Dependencies
|
||||||
|
run: composer install --no-scripts
|
||||||
|
|
||||||
|
- name: Code Style PHP
|
||||||
|
run: vendor/bin/php-cs-fixer fix --dry-run
|
||||||
|
|
||||||
|
- name: PHPStan
|
||||||
|
run: vendor/bin/phpstan analyse
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
.phpunit.cache
|
||||||
|
.php-cs-fixer.cache
|
||||||
|
composer.lock
|
||||||
|
vendor
|
||||||
45
.php-cs-fixer.dist.php
Normal file
45
.php-cs-fixer.dist.php
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!file_exists(__DIR__.'/src')) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileHeaderParts = [
|
||||||
|
<<<'EOF'
|
||||||
|
This file is part of the official PHP MCP SDK.
|
||||||
|
|
||||||
|
A collaboration between Symfony and the PHP Foundation.
|
||||||
|
|
||||||
|
EOF,
|
||||||
|
<<<'EOF'
|
||||||
|
|
||||||
|
For the full copyright and license information, please view the LICENSE
|
||||||
|
file that was distributed with this source code.
|
||||||
|
EOF,
|
||||||
|
];
|
||||||
|
|
||||||
|
return (new PhpCsFixer\Config())
|
||||||
|
// @see https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/pull/7777
|
||||||
|
->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect())
|
||||||
|
->setRules([
|
||||||
|
'@Symfony' => true,
|
||||||
|
'@Symfony:risky' => true,
|
||||||
|
'protected_to_private' => false,
|
||||||
|
'declare_strict_types' => false,
|
||||||
|
'header_comment' => [
|
||||||
|
'header' => implode('', $fileHeaderParts),
|
||||||
|
],
|
||||||
|
'php_unit_test_case_static_method_calls' => ['call_type' => 'this'],
|
||||||
|
])
|
||||||
|
->setRiskyAllowed(true)
|
||||||
|
->setFinder((new PhpCsFixer\Finder())->in(__DIR__))
|
||||||
|
;
|
||||||
24
CHANGELOG.md
Normal file
24
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
CHANGELOG
|
||||||
|
=========
|
||||||
|
|
||||||
|
0.1
|
||||||
|
---
|
||||||
|
|
||||||
|
* Add Model Context Protocol (MCP) implementation for LLM-application communication
|
||||||
|
* Add JSON-RPC based protocol handling with `JsonRpcHandler`
|
||||||
|
* Add three core MCP capabilities:
|
||||||
|
- Resources: File-like data readable by clients (API responses, file contents)
|
||||||
|
- Tools: Functions callable by LLMs (with user approval)
|
||||||
|
- Prompts: Pre-written templates for specific tasks
|
||||||
|
* Add multiple transport implementations:
|
||||||
|
- Symfony Console Transport for testing and CLI applications
|
||||||
|
- Stream Transport supporting Server-Sent Events (SSE) and HTTP streaming
|
||||||
|
- STDIO transport for command-line interfaces
|
||||||
|
* Add capability chains for organizing features:
|
||||||
|
- `ToolChain` for tool management
|
||||||
|
- `ResourceChain` for resource management
|
||||||
|
- `PromptChain` for prompt template management
|
||||||
|
* Add Server component managing transport connections
|
||||||
|
* Add request/notification handlers for MCP operations
|
||||||
|
* Add standardized interface enabling LLMs to interact with external systems
|
||||||
|
* Add support for building LLM "plugins" with extra context capabilities
|
||||||
19
LICENSE
Normal file
19
LICENSE
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
25
Makefile
Normal file
25
Makefile
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
.PHONY: deps-stable deps-low cs rector phpstan tests coverage run-examples ci ci-stable ci-lowest
|
||||||
|
|
||||||
|
deps-stable:
|
||||||
|
composer update --prefer-stable
|
||||||
|
|
||||||
|
deps-low:
|
||||||
|
composer update --prefer-lowest
|
||||||
|
|
||||||
|
cs:
|
||||||
|
vendor/bin/php-cs-fixer fix --diff --verbose
|
||||||
|
|
||||||
|
phpstan:
|
||||||
|
vendor/bin/phpstan --memory-limit=-1
|
||||||
|
|
||||||
|
tests:
|
||||||
|
vendor/bin/phpunit
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html=coverage
|
||||||
|
|
||||||
|
ci: ci-stable
|
||||||
|
|
||||||
|
ci-stable: deps-stable cs phpstan tests
|
||||||
|
|
||||||
|
ci-lowest: deps-low cs phpstan tests
|
||||||
46
composer.json
Normal file
46
composer.json
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "mcp/sdk",
|
||||||
|
"type": "library",
|
||||||
|
"description": "Model Context Protocol SDK for Client and Server applications in PHP",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Christopher Hertel",
|
||||||
|
"email": "mail@christopher-hertel.de"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Nyholm",
|
||||||
|
"email": "tobias.nyholm@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Kyrian Obikwelu",
|
||||||
|
"email": "koshnawaza@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^8.1",
|
||||||
|
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||||
|
"symfony/uid": "^6.4 || ^7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^2.1",
|
||||||
|
"phpunit/phpunit": "^10.5",
|
||||||
|
"symfony/console": "^6.4 || ^7.0",
|
||||||
|
"psr/cache": "^3.0",
|
||||||
|
"php-cs-fixer/shim": "^3.84"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"symfony/console": "To use SymfonyConsoleTransport for STDIO",
|
||||||
|
"psr/cache": "To use CachePoolStore with SSE Transport"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Mcp\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Mcp\\Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
157
doc/index.rst
Normal file
157
doc/index.rst
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
Model Context Protocol SDK
|
||||||
|
==========================
|
||||||
|
|
||||||
|
The PHP MCP SDK is the low level library that enables communication between a PHP application and an LLM model.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Install the SDK using Composer:
|
||||||
|
|
||||||
|
.. code-block:: terminal
|
||||||
|
|
||||||
|
$ composer require mcp/sdk
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
The `Model Context Protocol`_ is built on top of JSON-RPC. There two types of
|
||||||
|
messages. A Notification and Request. The Notification is just a status update
|
||||||
|
that something has happened. There is never a response to a Notification. A Request
|
||||||
|
is a message that expects a response. There are 3 concepts/capabilities that you
|
||||||
|
may use. These are::
|
||||||
|
|
||||||
|
1. **Resources**: File-like data that can be read by clients (like API responses or file contents)
|
||||||
|
1. **Tools**: Functions that can be called by the LLM (with user approval)
|
||||||
|
1. **Prompts**: Pre-written templates that help users accomplish specific tasks
|
||||||
|
|
||||||
|
The SDK comes with NotificationHandlers and RequestHandlers which are expected
|
||||||
|
to be wired up in your application.
|
||||||
|
|
||||||
|
JsonRpcHandler
|
||||||
|
..............
|
||||||
|
|
||||||
|
The ``Mcp\Server\JsonRpcHandler`` is the heart of the SDK. It is here
|
||||||
|
you inject the NotificationHandlers and RequestHandlers. It is recommended to use
|
||||||
|
the built-in handlers in ``Mcp\Server\NotificationHandlers\*`` and
|
||||||
|
``Mcp\Server\RequestHandlers\*``.
|
||||||
|
|
||||||
|
The ``Mcp\Server\JsonRpcHandler`` is started and kept running by
|
||||||
|
the ``Mcp\Server``
|
||||||
|
|
||||||
|
Transports
|
||||||
|
..........
|
||||||
|
|
||||||
|
The SDK supports multiple transports for sending and receiving messages. The
|
||||||
|
``Mcp\Server`` is using the transport to fetch a message, then
|
||||||
|
give it to the ``Mcp\Server\JsonRpcHandler`` and finally send the
|
||||||
|
response/error back to the transport. The SDK comes with a few transports::
|
||||||
|
|
||||||
|
1. **Symfony Console Transport**: Good for testing and for CLI applications
|
||||||
|
1. **Stream Transport**: It uses Server Side Events (SSE) and HTTP streaming
|
||||||
|
|
||||||
|
Capabilities
|
||||||
|
............
|
||||||
|
|
||||||
|
Any client would like to discover the capabilities of the server. Exactly what
|
||||||
|
the server supports is defined in the ``Mcp\Server\RequestHandler\InitializeHandler``.
|
||||||
|
When the client connects, it sees the capabilities and will ask the server to list
|
||||||
|
the tools/resource/prompts etc. When you want to add a new capability, example a
|
||||||
|
**Tool** that can tell the current time, you need to provide some metadata to the
|
||||||
|
``Mcp\Server\RequestHandler\ToolListHandler``::
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Mcp\Capability\Tool\MetadataInterface;
|
||||||
|
|
||||||
|
class CurrentTimeToolMetadata implements MetadataInterface
|
||||||
|
{
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'Current time';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Returns the current time in UTC';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInputSchema(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => [
|
||||||
|
'format' => [
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'The format of the time, e.g. "Y-m-d H:i:s"',
|
||||||
|
'default' => 'Y-m-d H:i:s',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'required' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
We would also need a class to actually execute the tool::
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Mcp\Capability\Tool\IdentifierInterface;
|
||||||
|
use Mcp\Capability\Tool\ToolCall;
|
||||||
|
use Mcp\Capability\Tool\ToolCallResult;
|
||||||
|
use Mcp\Capability\Tool\ToolExecutorInterface;
|
||||||
|
|
||||||
|
class CurrentTimeToolExecutor implements ToolExecutorInterface, IdentifierInterface
|
||||||
|
{
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'Current time';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function call(ToolCall $input): ToolCallResult
|
||||||
|
{
|
||||||
|
$format = $input->arguments['format'] ?? 'Y-m-d H:i:s';
|
||||||
|
|
||||||
|
return new ToolCallResult(
|
||||||
|
(new \DateTime('now', new \DateTimeZone('UTC')))->format($format)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
If you have multiple tools, you can put them in a ToolChain::
|
||||||
|
|
||||||
|
$tools = new ToolChain([
|
||||||
|
new CurrentTimeToolMetadata(),
|
||||||
|
new CurrentTimeToolExecutor(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$jsonRpcHandler = new Mcp\Server\JsonRpcHandler(
|
||||||
|
new Mcp\Message\Factory(),
|
||||||
|
[
|
||||||
|
new ToolCallHandler($tools),
|
||||||
|
new ToolListHandler($tools),
|
||||||
|
// Other RequestHandlers ...
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// Other NotificationHandlers ...
|
||||||
|
],
|
||||||
|
new NullLogger()
|
||||||
|
);
|
||||||
|
|
||||||
|
With this metadata and executor, the client can now call the tool.
|
||||||
|
|
||||||
|
Extending the SDK
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
If you want to extend the SDK, you can create your own RequestHandlers and NotificationHandlers.
|
||||||
|
The provided one are very good defaults for most applications but they are not
|
||||||
|
a requirement.
|
||||||
|
|
||||||
|
If you do decide to use them, you get the benefit of having a well-defined interfaces
|
||||||
|
and value objects to work with. They will assure that you follow the `Model Context Protocol`_.
|
||||||
|
specification.
|
||||||
|
|
||||||
|
You also have the Transport abstraction that allows you to create your own transport
|
||||||
|
if non of the standard ones fit your needs.
|
||||||
|
|
||||||
|
.. _`Model Context Protocol`: https://modelcontextprotocol.io/
|
||||||
29
examples/cli/README.md
Normal file
29
examples/cli/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Example app with CLI
|
||||||
|
|
||||||
|
This is just for testing and debugging purposes.
|
||||||
|
|
||||||
|
|
||||||
|
Install and create symlink with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/your/project/examples/cli
|
||||||
|
composer update
|
||||||
|
rm -rf vendor/mcp/sdk/src
|
||||||
|
ln -s /path/to/your/project/src /path/to/your/project/examples/cli/vendor/mcp/sdk/src
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the CLI with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DEBUG=1 php index.php
|
||||||
|
```
|
||||||
|
|
||||||
|
You will see debug outputs to help you understand what is happening.
|
||||||
|
|
||||||
|
In this terminal you can now test add some json strings. See `example-requests.json`.
|
||||||
|
|
||||||
|
Run with Inspector:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @modelcontextprotocol/inspector php index.php
|
||||||
|
```
|
||||||
27
examples/cli/composer.json
Normal file
27
examples/cli/composer.json
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "mcp/cli-server-example",
|
||||||
|
"description": "An example application for CLI",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "project",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Tobias Nyholm",
|
||||||
|
"email": "tobias.nyholm@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"mcp/sdk": "@dev",
|
||||||
|
"symfony/console": "^7.2"
|
||||||
|
},
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repositories": [
|
||||||
|
{ "type": "path", "url": "../../" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
12
examples/cli/example-requests.json
Normal file
12
examples/cli/example-requests.json
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[
|
||||||
|
{"jsonrpc": "2.0", "id": 1, "method": "resources/list", "params": []},
|
||||||
|
{"jsonrpc": "2.0", "id": 2, "method": "resources/read", "params": {"uri": "file:///project/src/main.rs"}},
|
||||||
|
|
||||||
|
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
|
||||||
|
{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "Current time"}},
|
||||||
|
{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "Current time","arguments": {"format": "Y-m-d"}}},
|
||||||
|
|
||||||
|
{"jsonrpc": "2.0", "id": 1, "method": "prompts/list"},
|
||||||
|
{"jsonrpc": "2.0", "id": 2 ,"method": "prompts/get", "params": {"name": "Greet"}},
|
||||||
|
{"jsonrpc": "2.0", "id": 2 ,"method": "prompts/get", "params": {"name": "Greet", "arguments": { "firstName": "Tobias" }}}
|
||||||
|
]
|
||||||
39
examples/cli/index.php
Normal file
39
examples/cli/index.php
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require __DIR__.'/vendor/autoload.php';
|
||||||
|
|
||||||
|
use Symfony\Component\Console as SymfonyConsole;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
$debug = (bool) ($_SERVER['DEBUG'] ?? false);
|
||||||
|
|
||||||
|
// Setup input, output and logger
|
||||||
|
$input = new SymfonyConsole\Input\ArgvInput($argv);
|
||||||
|
$output = new SymfonyConsole\Output\ConsoleOutput($debug ? OutputInterface::VERBOSITY_VERY_VERBOSE : OutputInterface::VERBOSITY_NORMAL);
|
||||||
|
$logger = new SymfonyConsole\Logger\ConsoleLogger($output);
|
||||||
|
|
||||||
|
// Configure the JsonRpcHandler and build the functionality
|
||||||
|
$jsonRpcHandler = new Mcp\Server\JsonRpcHandler(
|
||||||
|
new Mcp\Message\Factory(),
|
||||||
|
App\Builder::buildRequestHandlers(),
|
||||||
|
App\Builder::buildNotificationHandlers(),
|
||||||
|
$logger
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set up the server
|
||||||
|
$sever = new Mcp\Server($jsonRpcHandler, $logger);
|
||||||
|
|
||||||
|
// Create the transport layer using Symfony Console
|
||||||
|
$transport = new Mcp\Server\Transport\Stdio\SymfonyConsoleTransport($input, $output);
|
||||||
|
|
||||||
|
// Start our application
|
||||||
|
$sever->connect($transport);
|
||||||
72
examples/cli/src/Builder.php
Normal file
72
examples/cli/src/Builder.php
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?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 App;
|
||||||
|
|
||||||
|
use Mcp\Capability\PromptChain;
|
||||||
|
use Mcp\Capability\ResourceChain;
|
||||||
|
use Mcp\Capability\ToolChain;
|
||||||
|
use Mcp\Server\NotificationHandler\InitializedHandler;
|
||||||
|
use Mcp\Server\NotificationHandlerInterface;
|
||||||
|
use Mcp\Server\RequestHandler\InitializeHandler;
|
||||||
|
use Mcp\Server\RequestHandler\PingHandler;
|
||||||
|
use Mcp\Server\RequestHandler\PromptGetHandler;
|
||||||
|
use Mcp\Server\RequestHandler\PromptListHandler;
|
||||||
|
use Mcp\Server\RequestHandler\ResourceListHandler;
|
||||||
|
use Mcp\Server\RequestHandler\ResourceReadHandler;
|
||||||
|
use Mcp\Server\RequestHandler\ToolCallHandler;
|
||||||
|
use Mcp\Server\RequestHandler\ToolListHandler;
|
||||||
|
use Mcp\Server\RequestHandlerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
|
*/
|
||||||
|
class Builder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return list<RequestHandlerInterface>
|
||||||
|
*/
|
||||||
|
public static function buildRequestHandlers(): array
|
||||||
|
{
|
||||||
|
$promptManager = new PromptChain([
|
||||||
|
new ExamplePrompt(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resourceManager = new ResourceChain([
|
||||||
|
new ExampleResource(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$toolManager = new ToolChain([
|
||||||
|
new ExampleTool(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
new InitializeHandler(),
|
||||||
|
new PingHandler(),
|
||||||
|
new PromptListHandler($promptManager),
|
||||||
|
new PromptGetHandler($promptManager),
|
||||||
|
new ResourceListHandler($resourceManager),
|
||||||
|
new ResourceReadHandler($resourceManager),
|
||||||
|
new ToolCallHandler($toolManager),
|
||||||
|
new ToolListHandler($toolManager),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<NotificationHandlerInterface>
|
||||||
|
*/
|
||||||
|
public static function buildNotificationHandlers(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new InitializedHandler(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
58
examples/cli/src/ExamplePrompt.php
Normal file
58
examples/cli/src/ExamplePrompt.php
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?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 App;
|
||||||
|
|
||||||
|
use Mcp\Capability\Prompt\MetadataInterface;
|
||||||
|
use Mcp\Capability\Prompt\PromptGet;
|
||||||
|
use Mcp\Capability\Prompt\PromptGetResult;
|
||||||
|
use Mcp\Capability\Prompt\PromptGetResultMessages;
|
||||||
|
use Mcp\Capability\Prompt\PromptGetterInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
|
*/
|
||||||
|
class ExamplePrompt implements MetadataInterface, PromptGetterInterface
|
||||||
|
{
|
||||||
|
public function get(PromptGet $input): PromptGetResult
|
||||||
|
{
|
||||||
|
$firstName = $input->arguments['first name'] ?? null;
|
||||||
|
|
||||||
|
return new PromptGetResult(
|
||||||
|
$this->getDescription(),
|
||||||
|
[new PromptGetResultMessages(
|
||||||
|
'user',
|
||||||
|
\sprintf('Hello %s', $firstName ?? 'World')
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'Greet';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): ?string
|
||||||
|
{
|
||||||
|
return 'Greet a person with a nice message';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArguments(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name' => 'first name',
|
||||||
|
'description' => 'The name of the person to greet',
|
||||||
|
'required' => false,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
56
examples/cli/src/ExampleResource.php
Normal file
56
examples/cli/src/ExampleResource.php
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?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 App;
|
||||||
|
|
||||||
|
use Mcp\Capability\Resource\MetadataInterface;
|
||||||
|
use Mcp\Capability\Resource\ResourceRead;
|
||||||
|
use Mcp\Capability\Resource\ResourceReaderInterface;
|
||||||
|
use Mcp\Capability\Resource\ResourceReadResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
|
*/
|
||||||
|
class ExampleResource implements MetadataInterface, ResourceReaderInterface
|
||||||
|
{
|
||||||
|
public function read(ResourceRead $input): ResourceReadResult
|
||||||
|
{
|
||||||
|
return new ResourceReadResult(
|
||||||
|
'Content of '.$this->getName(),
|
||||||
|
$this->getUri(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUri(): string
|
||||||
|
{
|
||||||
|
return 'file:///project/src/main.rs';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'My resource';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): ?string
|
||||||
|
{
|
||||||
|
return 'This is just an example';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMimeType(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize(): ?int
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
57
examples/cli/src/ExampleTool.php
Normal file
57
examples/cli/src/ExampleTool.php
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?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 App;
|
||||||
|
|
||||||
|
use Mcp\Capability\Tool\MetadataInterface;
|
||||||
|
use Mcp\Capability\Tool\ToolCall;
|
||||||
|
use Mcp\Capability\Tool\ToolCallResult;
|
||||||
|
use Mcp\Capability\Tool\ToolExecutorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||||
|
*/
|
||||||
|
class ExampleTool implements MetadataInterface, ToolExecutorInterface
|
||||||
|
{
|
||||||
|
public function call(ToolCall $input): ToolCallResult
|
||||||
|
{
|
||||||
|
$format = $input->arguments['format'] ?? 'Y-m-d H:i:s';
|
||||||
|
|
||||||
|
return new ToolCallResult(
|
||||||
|
(new \DateTime('now', new \DateTimeZone('UTC')))->format($format)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'Current time';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Returns the current time in UTC';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInputSchema(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => [
|
||||||
|
'format' => [
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'The format of the time, e.g. "Y-m-d H:i:s"',
|
||||||
|
'default' => 'Y-m-d H:i:s',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'required' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
9
phpstan.dist.neon
Normal file
9
phpstan.dist.neon
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
parameters:
|
||||||
|
level: 6
|
||||||
|
paths:
|
||||||
|
- examples/
|
||||||
|
- src/
|
||||||
|
- tests/
|
||||||
|
excludePaths:
|
||||||
|
- examples/cli/vendor/* (?)
|
||||||
|
treatPhpDocTypesAsCertain: false
|
||||||
22
phpunit.xml.dist
Normal file
22
phpunit.xml.dist
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
|
||||||
|
cacheDirectory=".phpunit.cache"
|
||||||
|
colors="true"
|
||||||
|
executionOrder="depends,defects"
|
||||||
|
beStrictAboutOutputDuringTests="true"
|
||||||
|
failOnRisky="true"
|
||||||
|
failOnWarning="true">
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="default">
|
||||||
|
<directory>tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<source restrictNotices="true" restrictWarnings="true">
|
||||||
|
<include>
|
||||||
|
<directory>src</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
</phpunit>
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue