diff --git a/php/Boot.php b/php/Boot.php index 14565c4a..61898e04 100644 --- a/php/Boot.php +++ b/php/Boot.php @@ -115,6 +115,23 @@ public function register(): void ]), ); + // Alias the registry under dappcore/mcp's AgentResourceProvider name so + // that package's HTTP plans:// and sessions:// endpoints resolve it. + // + // A string, not ::class: this package keeps its own copy of Core\Mcp + // rather than depending on dappcore/mcp, so the interface cannot be + // named here — referencing it would resolve against this repo's own + // Core\Mcp tree, where it does not exist. mcp accepts a provider + // structurally for exactly this reason; the registry already satisfies + // its one method, read(). + // + // Harmless when mcp is absent: nothing resolves the alias, and binding + // a container key costs nothing. + $this->app->alias( + AgentResourceRegistry::class, + 'Core\\Mcp\\Resources\\Contracts\\AgentResourceProvider', + ); + $this->app->singleton(ForgejoService::class, function ($app) { return new ForgejoService( baseUrl: (string) config('agentic.forge_url', 'https://forge.lthn.ai'), diff --git a/php/tests/Feature/Mcp/Resources/AgentResourceProviderAliasTest.php b/php/tests/Feature/Mcp/Resources/AgentResourceProviderAliasTest.php new file mode 100644 index 00000000..68818d4f --- /dev/null +++ b/php/tests/Feature/Mcp/Resources/AgentResourceProviderAliasTest.php @@ -0,0 +1,51 @@ +bound(PROVIDER_KEY))->toBeTrue() + ->and(app(PROVIDER_KEY))->toBeInstanceOf(AgentResourceRegistry::class); +}); + +it('is the same instance as the registry itself', function (): void { + // An alias, not a second binding: two registries would drift. + expect(app(PROVIDER_KEY))->toBe(app(AgentResourceRegistry::class)); +}); + +it('satisfies the one method mcp calls on it', function (): void { + AgentPlan::factory()->create(['slug' => 'aliased-plan', 'title' => 'Aliased Plan']); + + $contents = app(PROVIDER_KEY)->read('plans://all'); + + expect($contents)->not->toBeNull() + ->and($contents['mimeType'])->toBe('text/markdown') + ->and($contents['text'])->toContain('aliased-plan'); +}); + +it('reports nothing for a uri it does not serve', function (): void { + // mcp turns null into a clean not-found rather than a body saying so. + expect(app(PROVIDER_KEY)->read('nonsense://thing'))->toBeNull(); +});