Skip to content

Commit 520cdf8

Browse files
committed
[MCP Bundle] autoconfigure request and notification handlers
1 parent 89c378f commit 520cdf8

File tree

5 files changed

+82
-41
lines changed

5 files changed

+82
-41
lines changed

src/mcp-bundle/config/services.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
->call('setEventDispatcher', [service('event_dispatcher')])
3535
->call('setRegistry', [service('mcp.registry')])
3636
->call('setSession', [service('mcp.session.store')])
37+
->call('addRequestHandlers', [tagged_iterator('mcp.request_handler')])
38+
->call('addNotificationHandlers', [tagged_iterator('mcp.notification_handler')])
39+
->call('addLoaders', [tagged_iterator('mcp.loader')])
3740
->call('setDiscovery', [param('kernel.project_dir'), param('mcp.discovery.scan_dirs'), param('mcp.discovery.exclude_dirs')])
3841

3942
->set('mcp.server', Server::class)

src/mcp-bundle/src/DependencyInjection/McpPass.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,6 @@ public function process(ContainerBuilder $container): void
2727
return;
2828
}
2929

30-
$definition = $container->getDefinition('mcp.server.builder');
31-
32-
$loaderReferences = $this->findAndSortTaggedServices('mcp.loader', $container);
33-
if ([] !== $loaderReferences) {
34-
$definition->addMethodCall('addLoaders', $loaderReferences);
35-
}
36-
3730
$allMcpServices = [];
3831
$mcpTags = ['mcp.tool', 'mcp.prompt', 'mcp.resource', 'mcp.resource_template'];
3932

@@ -52,6 +45,6 @@ public function process(ContainerBuilder $container): void
5245
}
5346

5447
$serviceLocatorRef = ServiceLocatorTagPass::register($container, $serviceReferences);
55-
$definition->addMethodCall('setContainer', [$serviceLocatorRef]);
48+
$container->getDefinition('mcp.server.builder')->addMethodCall('setContainer', [$serviceLocatorRef]);
5649
}
5750
}

src/mcp-bundle/src/McpBundle.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use Mcp\Capability\Attribute\McpResourceTemplate;
1818
use Mcp\Capability\Attribute\McpTool;
1919
use Mcp\Capability\Registry\Loader\LoaderInterface;
20+
use Mcp\Server\Handler\Notification\NotificationHandlerInterface;
21+
use Mcp\Server\Handler\Request\RequestHandlerInterface;
2022
use Mcp\Server\Session\FileSessionStore;
2123
use Mcp\Server\Session\InMemorySessionStore;
2224
use Symfony\AI\McpBundle\Command\McpCommand;
@@ -62,6 +64,12 @@ public function loadExtension(array $config, ContainerConfigurator $container, C
6264
$builder->registerForAutoconfiguration(LoaderInterface::class)
6365
->addTag('mcp.loader');
6466

67+
$builder->registerForAutoconfiguration(RequestHandlerInterface::class)
68+
->addTag('mcp.request_handler');
69+
70+
$builder->registerForAutoconfiguration(NotificationHandlerInterface::class)
71+
->addTag('mcp.notification_handler');
72+
6573
if ($builder->getParameter('kernel.debug')) {
6674
$traceableRegistry = (new Definition('mcp.traceable_registry'))
6775
->setClass(TraceableRegistry::class)

src/mcp-bundle/tests/DependencyInjection/McpBundleTest.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
namespace Symfony\AI\McpBundle\Tests\DependencyInjection;
1313

1414
use Mcp\Capability\Registry\Loader\LoaderInterface;
15+
use Mcp\Server\Handler\Notification\NotificationHandlerInterface;
16+
use Mcp\Server\Handler\Request\RequestHandlerInterface;
1517
use PHPUnit\Framework\Attributes\DataProvider;
1618
use PHPUnit\Framework\TestCase;
1719
use Symfony\AI\McpBundle\McpBundle;
20+
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
1821
use Symfony\Component\DependencyInjection\ContainerBuilder;
1922

2023
class McpBundleTest extends TestCase
@@ -149,6 +152,7 @@ public function testServerServices()
149152
$builderDefinition = $container->getDefinition('mcp.server.builder');
150153
$methodCalls = $builderDefinition->getMethodCalls();
151154

155+
// Check for setEventDispatcher method call
152156
$hasEventDispatcherCall = false;
153157
foreach ($methodCalls as $call) {
154158
if ('setEventDispatcher' === $call[0]) {
@@ -157,6 +161,54 @@ public function testServerServices()
157161
}
158162
}
159163
$this->assertTrue($hasEventDispatcherCall, 'ServerBuilder should have setEventDispatcher method call');
164+
165+
// Check for addRequestHandlers with proper tag
166+
$hasRequestHandlers = false;
167+
foreach ($methodCalls as $call) {
168+
if ('addRequestHandlers' === $call[0]) {
169+
$argument = $call[1][0];
170+
if (
171+
$argument instanceof TaggedIteratorArgument
172+
&& 'mcp.request_handler' === $argument->getTag()
173+
) {
174+
$hasRequestHandlers = true;
175+
break;
176+
}
177+
}
178+
}
179+
$this->assertTrue($hasRequestHandlers, 'ServerBuilder should have addRequestHandlers with mcp.request_handler tag');
180+
181+
// Check for addNotificationHandlers with proper tag
182+
$hasNotificationHandlers = false;
183+
foreach ($methodCalls as $call) {
184+
if ('addNotificationHandlers' === $call[0]) {
185+
$argument = $call[1][0];
186+
if (
187+
$argument instanceof TaggedIteratorArgument
188+
&& 'mcp.notification_handler' === $argument->getTag()
189+
) {
190+
$hasNotificationHandlers = true;
191+
break;
192+
}
193+
}
194+
}
195+
$this->assertTrue($hasNotificationHandlers, 'ServerBuilder should have addNotificationHandlers with mcp.notification_handler tag');
196+
197+
// Check for addLoaders with proper tag
198+
$hasLoaders = false;
199+
foreach ($methodCalls as $call) {
200+
if ('addLoaders' === $call[0]) {
201+
$argument = $call[1][0];
202+
if (
203+
$argument instanceof \Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument
204+
&& 'mcp.loader' === $argument->getTag()
205+
) {
206+
$hasLoaders = true;
207+
break;
208+
}
209+
}
210+
}
211+
$this->assertTrue($hasLoaders, 'ServerBuilder should have addLoaders with mcp.loader tag');
160212
}
161213

162214
public function testMcpToolAttributeAutoconfiguration()
@@ -359,6 +411,24 @@ public function testLoaderInterfaceAutoconfiguration()
359411
$this->assertTrue($definition->hasTag('mcp.loader'));
360412
}
361413

414+
public function testRequestHandlerInterfaceAutoconfiguration()
415+
{
416+
$container = $this->buildContainer([]);
417+
$autoconfigured = $container->getAutoconfiguredInstanceof();
418+
$this->assertArrayHasKey(RequestHandlerInterface::class, $autoconfigured);
419+
$definition = $autoconfigured[RequestHandlerInterface::class];
420+
$this->assertTrue($definition->hasTag('mcp.request_handler'));
421+
}
422+
423+
public function testNotificationHandlerInterfaceAutoconfiguration()
424+
{
425+
$container = $this->buildContainer([]);
426+
$autoconfigured = $container->getAutoconfiguredInstanceof();
427+
$this->assertArrayHasKey(NotificationHandlerInterface::class, $autoconfigured);
428+
$definition = $autoconfigured[NotificationHandlerInterface::class];
429+
$this->assertTrue($definition->hasTag('mcp.notification_handler'));
430+
}
431+
362432
private function buildContainer(array $configuration): ContainerBuilder
363433
{
364434
$container = new ContainerBuilder();

src/mcp-bundle/tests/DependencyInjection/McpPassTest.php

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -138,37 +138,4 @@ public function testHandlesPartialMcpServices()
138138
$this->assertInstanceOf(Reference::class, $services['tool_service']->getValues()[0]);
139139
$this->assertInstanceOf(Reference::class, $services['prompt_service']->getValues()[0]);
140140
}
141-
142-
public function testInjectsLoadersIntoBuilder()
143-
{
144-
$container = new ContainerBuilder();
145-
$container->setDefinition('mcp.server.builder', new Definition());
146-
147-
$container->setDefinition('loader_one', (new Definition())->addTag('mcp.loader'));
148-
$container->setDefinition('loader_two', (new Definition())->addTag('mcp.loader'));
149-
150-
$pass = new McpPass();
151-
$pass->process($container);
152-
153-
$builderDefinition = $container->getDefinition('mcp.server.builder');
154-
$methodCalls = $builderDefinition->getMethodCalls();
155-
156-
$addLoadersCall = null;
157-
foreach ($methodCalls as $call) {
158-
if ('addLoaders' === $call[0]) {
159-
$addLoadersCall = $call;
160-
break;
161-
}
162-
}
163-
164-
$this->assertNotNull($addLoadersCall, 'Builder should have addLoaders method call');
165-
166-
// Verify arguments are References
167-
$args = $addLoadersCall[1];
168-
$this->assertContainsOnlyInstancesOf(Reference::class, $args);
169-
170-
$ids = array_map(fn (Reference $ref) => (string) $ref, $args);
171-
$this->assertContains('loader_one', $ids);
172-
$this->assertContains('loader_two', $ids);
173-
}
174141
}

0 commit comments

Comments
 (0)