Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
76f0641
Add method to create messages by type
Bellangelo Sep 6, 2025
b61828f
Parse and queue notifications
Bellangelo Sep 6, 2025
e4780c6
Send notifications
Bellangelo Sep 6, 2025
f571e71
Clean-up and fix tests
Bellangelo Sep 6, 2025
b5622c6
Install Symfony dispatcher to handle events for internal usage
Bellangelo Sep 6, 2025
be26b49
Use Symfony dispatcher for internal usage
Bellangelo Sep 6, 2025
4af2d4a
Test the new notification publisher
Bellangelo Sep 6, 2025
d6187dd
Fix code style
Bellangelo Sep 6, 2025
413660b
Add auth annotations
Bellangelo Sep 6, 2025
1fa635d
Test new message factory method
Bellangelo Sep 6, 2025
138fea6
Remove event dispatcher
Bellangelo Sep 7, 2025
abdd1f2
No need for extra abstractions if we are calling notifications directly
Bellangelo Sep 7, 2025
fde283c
Server accepts different argument now
Bellangelo Sep 7, 2025
6beda2d
Fix return type
Bellangelo Sep 7, 2025
de10bcd
No need for an id
Bellangelo Sep 7, 2025
8c81487
Clean-up phpdoc
Bellangelo Sep 7, 2025
8b201e1
Update tests
Bellangelo Sep 7, 2025
4900058
Fix sending notifications
Bellangelo Sep 7, 2025
7a31c24
Use generator to return notifications
Bellangelo Sep 7, 2025
efd3c0a
Test Registry
Bellangelo Sep 7, 2025
8c55c25
Let the caller handle the construction of the notification
Bellangelo Sep 9, 2025
f39c614
Remove unused method
Bellangelo Sep 9, 2025
759a379
Fix code style
Bellangelo Sep 9, 2025
f226b2b
Simplify logic
Bellangelo Sep 9, 2025
4c2415b
Make NotificationPublisher optional
Bellangelo Sep 9, 2025
271befb
Merge branch 'main' into support-server-notifications
Bellangelo Sep 15, 2025
4a29253
Update tests based on new constructor
Bellangelo Sep 15, 2025
244a81e
Unify tests
Bellangelo Sep 15, 2025
dab3bf3
Fix styling
Bellangelo Sep 15, 2025
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
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"opis/json-schema": "^2.4",
"phpdocumentor/reflection-docblock": "^5.6",
"psr/container": "^2.0",
"psr/event-dispatcher": "^1.0",
"psr/log": "^1.0 || ^2.0 || ^3.0",
"symfony/finder": "^6.4 || ^7.3",
"symfony/uid": "^6.4 || ^7.3"
Expand Down
4 changes: 3 additions & 1 deletion examples/09-standalone-cli/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
$logger
);

$notificationPublisher = new Mcp\Server\NotificationPublisher();

// Set up the server
$sever = new Mcp\Server($jsonRpcHandler, $logger);
$sever = new Mcp\Server($jsonRpcHandler, $notificationPublisher, $logger);

// Create the transport layer using Stdio
$transport = new Mcp\Server\Transport\StdioTransport(logger: $logger);
Expand Down
26 changes: 13 additions & 13 deletions src/Capability/Registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@
use Mcp\Capability\Registry\ResourceReference;
use Mcp\Capability\Registry\ResourceTemplateReference;
use Mcp\Capability\Registry\ToolReference;
use Mcp\Event\PromptListChangedEvent;
use Mcp\Event\ResourceListChangedEvent;
use Mcp\Event\ResourceTemplateListChangedEvent;
use Mcp\Event\ToolListChangedEvent;
use Mcp\Schema\Notification\PromptListChangedNotification;
use Mcp\Schema\Notification\ResourceListChangedNotification;
use Mcp\Schema\Notification\ToolListChangedNotification;
use Mcp\Schema\Prompt;
use Mcp\Schema\Resource;
use Mcp\Schema\ResourceTemplate;
use Mcp\Schema\ServerCapabilities;
use Mcp\Schema\Tool;
use Psr\EventDispatcher\EventDispatcherInterface;
use Mcp\Server\NotificationPublisher;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;

Expand Down Expand Up @@ -61,7 +60,7 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
private array $resourceTemplates = [];

public function __construct(
private readonly ?EventDispatcherInterface $eventDispatcher = null,
private readonly NotificationPublisher $notificationPublisher = new NotificationPublisher(),
private readonly LoggerInterface $logger = new NullLogger(),
) {
}
Expand All @@ -74,12 +73,12 @@ public function getCapabilities(): ServerCapabilities

return new ServerCapabilities(
tools: [] !== $this->tools,
toolsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
toolsListChanged: true,
resources: [] !== $this->resources || [] !== $this->resourceTemplates,
resourcesSubscribe: false,
resourcesListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
resourcesListChanged: true,
prompts: [] !== $this->prompts,
promptsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
promptsListChanged: true,
logging: false,
completions: true,
);
Expand All @@ -100,7 +99,7 @@ public function registerTool(Tool $tool, callable|array|string $handler, bool $i

$this->tools[$toolName] = new ToolReference($tool, $handler, $isManual);

$this->eventDispatcher?->dispatch(new ToolListChangedEvent());
$this->notificationPublisher->enqueue(new ToolListChangedNotification());
}

public function registerResource(Resource $resource, callable|array|string $handler, bool $isManual = false): void
Expand All @@ -118,7 +117,7 @@ public function registerResource(Resource $resource, callable|array|string $hand

$this->resources[$uri] = new ResourceReference($resource, $handler, $isManual);

$this->eventDispatcher?->dispatch(new ResourceListChangedEvent());
$this->notificationPublisher->enqueue(new ResourceListChangedNotification());
}

public function registerResourceTemplate(
Expand All @@ -145,7 +144,8 @@ public function registerResourceTemplate(
$completionProviders,
);

$this->eventDispatcher?->dispatch(new ResourceTemplateListChangedEvent());
// TODO: Create ResourceTemplateListChangedNotification.
// $this->notificationPublisher->enqueue(ResourceTemplateListChangedNotification::class);
}

public function registerPrompt(
Expand All @@ -167,7 +167,7 @@ public function registerPrompt(

$this->prompts[$promptName] = new PromptReference($prompt, $handler, $isManual, $completionProviders);

$this->eventDispatcher?->dispatch(new PromptListChangedEvent());
$this->notificationPublisher->enqueue(new PromptListChangedNotification());
}

public function clear(): void
Expand Down
19 changes: 0 additions & 19 deletions src/Event/PromptListChangedEvent.php

This file was deleted.

19 changes: 0 additions & 19 deletions src/Event/ResourceListChangedEvent.php

This file was deleted.

19 changes: 0 additions & 19 deletions src/Event/ResourceTemplateListChangedEvent.php

This file was deleted.

19 changes: 0 additions & 19 deletions src/Event/ToolListChangedEvent.php

This file was deleted.

4 changes: 4 additions & 0 deletions src/JsonRpc/MessageFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
use Mcp\Schema\Request;

/**
* @phpstan-type RequestData array{
* params?: array<string, mixed>,
* }
*
* @author Christopher Hertel <[email protected]>
*/
final class MessageFactory
Expand Down
14 changes: 14 additions & 0 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Mcp;

use Mcp\JsonRpc\Handler;
use Mcp\Server\NotificationPublisher;
use Mcp\Server\ServerBuilder;
use Mcp\Server\TransportInterface;
use Psr\Log\LoggerInterface;
Expand All @@ -24,6 +25,7 @@ final class Server
{
public function __construct(
private readonly Handler $jsonRpcHandler,
private readonly NotificationPublisher $notificationPublisher,
private readonly LoggerInterface $logger = new NullLogger(),
) {
}
Expand Down Expand Up @@ -63,6 +65,18 @@ public function connect(TransportInterface $transport): void
}
}

foreach ($this->notificationPublisher->flush() as $notification) {
try {
$transport->send(json_encode($notification, \JSON_THROW_ON_ERROR));
} catch (\JsonException $e) {
$this->logger->error('Failed to encode notification to JSON.', [
'notification' => $notification::class,
'exception' => $e,
]);
continue;
}
}

usleep(1000);
}

Expand Down
40 changes: 40 additions & 0 deletions src/Server/NotificationPublisher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?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\Server;

use Mcp\Schema\JsonRpc\Notification;

/**
* @author Aggelos Bellos <[email protected]>
*/
class NotificationPublisher
{
/** @var list<Notification> */
private array $queue = [];

public function enqueue(Notification $notification): void
{
$this->queue[] = $notification;
}

/**
* @return \Generator<int, Notification, void, void>
*/
public function flush(): iterable
{
yield from $this->queue;

$this->queue = [];
}
}
15 changes: 3 additions & 12 deletions src/Server/ServerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
use Mcp\Schema\ToolAnnotations;
use Mcp\Server;
use Psr\Container\ContainerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Psr\SimpleCache\CacheInterface;
Expand All @@ -61,8 +60,6 @@ final class ServerBuilder

private ?PromptGetterInterface $promptGetter = null;

private ?EventDispatcherInterface $eventDispatcher = null;

private ?ContainerInterface $container = null;

private ?int $paginationLimit = 50;
Expand Down Expand Up @@ -154,13 +151,6 @@ public function setLogger(LoggerInterface $logger): self
return $this;
}

public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): self
{
$this->eventDispatcher = $eventDispatcher;

return $this;
}

public function setToolCaller(ToolCallerInterface $toolCaller): self
{
$this->toolCaller = $toolCaller;
Expand Down Expand Up @@ -276,9 +266,9 @@ public function addPrompt(callable|array|string $handler, ?string $name = null,
public function build(): Server
{
$logger = $this->logger ?? new NullLogger();

$notificationPublisher = new NotificationPublisher();
$container = $this->container ?? new Container();
$registry = new Registry($this->eventDispatcher, $logger);
$registry = new Registry($notificationPublisher, $logger);

$referenceHandler = new ReferenceHandler($container);
$toolCaller = $this->toolCaller ??= new ToolCaller($registry, $referenceHandler, $logger);
Expand All @@ -302,6 +292,7 @@ public function build(): Server
promptGetter: $promptGetter,
logger: $logger,
),
notificationPublisher: $notificationPublisher,
logger: $logger,
);
}
Expand Down
Loading