Skip to content

Commit 89c378f

Browse files
committed
[MCP Bundle] autoconfigure, tag and inject registry loader
1 parent a54c63b commit 89c378f

File tree

4 files changed

+58
-3
lines changed

4 files changed

+58
-3
lines changed

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,28 @@
1212
namespace Symfony\AI\McpBundle\DependencyInjection;
1313

1414
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
1516
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
1617
use Symfony\Component\DependencyInjection\ContainerBuilder;
1718
use Symfony\Component\DependencyInjection\Reference;
1819

1920
final class McpPass implements CompilerPassInterface
2021
{
22+
use PriorityTaggedServiceTrait;
23+
2124
public function process(ContainerBuilder $container): void
2225
{
2326
if (!$container->hasDefinition('mcp.server.builder')) {
2427
return;
2528
}
2629

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+
2737
$allMcpServices = [];
2838
$mcpTags = ['mcp.tool', 'mcp.prompt', 'mcp.resource', 'mcp.resource_template'];
2939

@@ -42,8 +52,6 @@ public function process(ContainerBuilder $container): void
4252
}
4353

4454
$serviceLocatorRef = ServiceLocatorTagPass::register($container, $serviceReferences);
45-
46-
$container->getDefinition('mcp.server.builder')
47-
->addMethodCall('setContainer', [$serviceLocatorRef]);
55+
$definition->addMethodCall('setContainer', [$serviceLocatorRef]);
4856
}
4957
}

src/mcp-bundle/src/McpBundle.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Mcp\Capability\Attribute\McpResource;
1717
use Mcp\Capability\Attribute\McpResourceTemplate;
1818
use Mcp\Capability\Attribute\McpTool;
19+
use Mcp\Capability\Registry\Loader\LoaderInterface;
1920
use Mcp\Server\Session\FileSessionStore;
2021
use Mcp\Server\Session\InMemorySessionStore;
2122
use Symfony\AI\McpBundle\Command\McpCommand;
@@ -58,6 +59,9 @@ public function loadExtension(array $config, ContainerConfigurator $container, C
5859

5960
$this->registerMcpAttributes($builder);
6061

62+
$builder->registerForAutoconfiguration(LoaderInterface::class)
63+
->addTag('mcp.loader');
64+
6165
if ($builder->getParameter('kernel.debug')) {
6266
$traceableRegistry = (new Definition('mcp.traceable_registry'))
6367
->setClass(TraceableRegistry::class)

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\AI\McpBundle\Tests\DependencyInjection;
1313

14+
use Mcp\Capability\Registry\Loader\LoaderInterface;
1415
use PHPUnit\Framework\Attributes\DataProvider;
1516
use PHPUnit\Framework\TestCase;
1617
use Symfony\AI\McpBundle\McpBundle;
@@ -349,6 +350,15 @@ public function testDiscoveryWithExcludeDirsOnly()
349350
$this->assertSame(['src/DataFixtures'], $container->getParameter('mcp.discovery.exclude_dirs'));
350351
}
351352

353+
public function testLoaderInterfaceAutoconfiguration()
354+
{
355+
$container = $this->buildContainer([]);
356+
$autoconfigured = $container->getAutoconfiguredInstanceof();
357+
$this->assertArrayHasKey(LoaderInterface::class, $autoconfigured);
358+
$definition = $autoconfigured[LoaderInterface::class];
359+
$this->assertTrue($definition->hasTag('mcp.loader'));
360+
}
361+
352362
private function buildContainer(array $configuration): ContainerBuilder
353363
{
354364
$container = new ContainerBuilder();

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,37 @@ 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+
}
141174
}

0 commit comments

Comments
 (0)