-
Notifications
You must be signed in to change notification settings - Fork 57
[Server] Support server notifications #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[Server] Support server notifications #42
Conversation
Hi @chr-hertel, I created a PoC of a solution. There are a few things that do not work 100% correct but I wanted a quick validation around the idea. I had to implement a few new things so I wasn't sure if I understood correctly the issue. |
Nice one @Bellangelo - thanks for working on this! :) So, i just gave it a quick read, maybe i missed something. so using an event dispatcher in the registry was an idea to publish events, but i wonder if we'd go with your with the publisher there is a central stack of notifications that get's emptied by the server - who else would listen to those events? and if there's only one consumer of events why not push from the emitting service (registry) to the publisher directly? |
Good question @chr-hertel, my initial thought for the PSR event dispatcher was that the developer building the server might also want to listen to these events. That’s why I’m dispatching through the developer’s event dispatcher here: If my understanding is off, then you’re right and we could call the |
yup, valid point, server application code might listen to that as well - but on the other hand i'd expect that the impulse for change is rather coming from the application side anyways, since the server itself doesn't have inner logic to change the lists - as of now. |
Transitionally depending on symfony is not a good thing; it lacks flexibility |
hi @chr-hertel, it should be ready now. |
Hi @Bellangelo 👋 I noticed that in addition to the Is there a specific reason or design goal behind this approach? I’d love to better understand the intention here. 🙂 |
Hey @butschster, I did this to minimize how many things the dependents know about their dependencies. If the |
src/Capability/Registry.php
Outdated
$this->tools[$toolName] = new ToolReference($tool, $handler, $isManual); | ||
|
||
$this->eventDispatcher?->dispatch(new ToolListChangedEvent()); | ||
$this->notificationPublisher->enqueue(ToolListChangedNotification::class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't it be a bit easier to just go with
$this->notificationPublisher->enqueue(ToolListChangedNotification::class); | |
$this->notificationPublisher->enqueue(new ToolListChangedNotification()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chr-hertel Hmm.. I thought that they would need to pass through the MessageFactory
. If they don't need to pass, should I also remove these notifications from the MessageFactory
? Because they won't be send as a response to a prompt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MessageFactory
removed from the NotificationPublisher
in this commit 3019972
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the MessageFactory
is intended to handle incoming json/array payload - in this case it's outgoing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know but it seems that it contains outgoing notifications https://github.com/modelcontextprotocol/php-sdk/blob/main/src/JsonRpc/MessageFactory.php#L39. So we should remove these from the MessageFactory
, right?
src/JsonRpc/MessageFactory.php
Outdated
* @return class-string<HasMethodInterface> | ||
*/ | ||
private function getType(string $method): string | ||
private function getType(string $method/**/): string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private function getType(string $method/**/): string | |
private function getType(string $method): string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 759a379
src/Server/NotificationPublisher.php
Outdated
$out = $this->queue; | ||
$this->queue = []; | ||
|
||
yield from $out; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should work as well
$out = $this->queue; | |
$this->queue = []; | |
yield from $out; | |
yield from $this->queue; | |
$this->queue = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in f226b2b
src/Capability/Registry.php
Outdated
private array $resourceTemplates = []; | ||
|
||
public function __construct( | ||
private readonly NotificationPublisher $notificationPublisher, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private readonly NotificationPublisher $notificationPublisher, | |
private readonly NotificationPublisher $notificationPublisher = new NotificationPublisher(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 4c2415b
2d2b84f
to
759a379
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for merging from my point of view - thanks @Bellangelo!
@CodeWithKyrian any thoughts? :)
Needs a rebase to solve the conflicts |
@chr-hertel Done. |
@Bellangelo please rebase and adjust to fit the new architecture. I’m also wondering if there’s a possibility of using the session as a message queue, since it can be persistent (depending on the store) and is unique to each client connection. Just rough thoughts at this point. @chr-hertel, any thoughts? |
@CodeWithKyrian that would be even required for making this work on the http transport, right? |
Introduce a NotificationPublisher that queues server-initiated MCP notifications. The Server now flushes this queue each tick and sends any pending notifications to the client.
We initially wired this via an EventDispatcher between the Registry and notifications, but, as per the discussion in this PR (see final comment), we simplified the design to invoke NotificationPublisher directly at the mutation points. As a result, the EventDispatcher wiring and the interim internal events used for that wiring have been removed.
Motivation and Context
Closes #12
How Has This Been Tested?
Unit and Integration tests. I have added extra tests for the
Registry
to increase its coverage.Breaking Changes
None for typical users constructing the server via
ServerBuilder
.(If someone manually instantiates Server, they now pass the
NotificationPublisher
)Types of changes
Checklist
Additional context