[Server] Implement discovery caching for improved performance (#39)
* feat: implement discovery caching with state-based approach - Add DiscoveryState class to encapsulate discovered MCP capabilities - Add exportDiscoveryState and importDiscoveryState methods to Registry - Modify Discoverer to return DiscoveryState instead of void - Create CachedDiscoverer decorator for caching discovery results - Add importDiscoveryState method to ReferenceRegistryInterface - Update ServerBuilder to use caching with withCache() method - Update tests to work with new state-based approach - Add example demonstrating cached discovery functionality - Add PSR-16 SimpleCache and Symfony Cache dependencies * docs: add comprehensive discovery caching documentation - Add detailed documentation explaining discovery caching architecture - Include usage examples for different cache implementations - Document performance benefits and best practices - Add troubleshooting guide and migration instructions - Include complete API reference for all caching components - Fix PHPStan issues by regenerating baseline - Apply PHP CS Fixer formatting to all new files * refactor: address reviewer feedback and clean up discovery caching - Make DiscoveryState class final - Cache DiscoveryState objects directly instead of arrays (no serialization needed) - Rename exportDiscoveryState/importDiscoveryState to getDiscoveryState/setDiscoveryState - Add getDiscoveryState method to ReferenceRegistryInterface - Remove TTL parameter from CachedDiscoverer (no expiration by default) - Remove unused methods from DiscoveryState (toArray, fromArray, merge) - Simplify ServerBuilder to handle decoration internally - Make Discoverer.applyDiscoveryState() internal (no longer public API) - Simplify documentation to focus on user perspective - Remove unnecessary development comments - Update all tests to work with new architecture - All tests pass, PHPStan clean, code formatting applied * refactor: clean up comments and formatting in server.php example * refactor: improve code formatting and consistency in discovery classes and example server * Fix method names in cached discovery example - Change withServerInfo() to setServerInfo() - Change withDiscovery() to setDiscovery() - Change withLogger() to setLogger() - Fixes PHPStan static analysis errors * refactor: update method names in ServerBuilder for consistency - Change withCache() to setCache() in ServerBuilder - Update example and documentation to reflect method name changes - Ensure consistency across caching implementation in examples and documentation * refactor: reorganize examples and fix PHPStan baseline - Move cached discovery example from examples/10 to examples/09 - Update composer.json autoload-dev namespace mapping - Fix PHPStan baseline by removing invalid entries for non-existent files - Update server.php to follow proper example pattern with logging and lifecycle * fix: correct formatting in server.php
This commit is contained in:
parent
9e168832ca
commit
4db831778b
1 changed files with 717 additions and 114 deletions
98
tests/Capability/Discovery/CachedDiscovererTest.php
Normal file
98
tests/Capability/Discovery/CachedDiscovererTest.php
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* 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\Tests\Capability\Discovery;
|
||||
|
||||
use Mcp\Capability\Discovery\CachedDiscoverer;
|
||||
use Mcp\Capability\Discovery\Discoverer;
|
||||
use Mcp\Capability\Discovery\DiscoveryState;
|
||||
use Mcp\Capability\Registry;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
|
||||
class CachedDiscovererTest extends TestCase
|
||||
{
|
||||
public function testCachedDiscovererUsesCacheOnSecondCall(): void
|
||||
{
|
||||
$registry = new Registry(null, new NullLogger());
|
||||
$discoverer = new Discoverer($registry, new NullLogger());
|
||||
|
||||
$cache = $this->createMock(CacheInterface::class);
|
||||
$cache->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn(null);
|
||||
|
||||
$cache->expects($this->once())
|
||||
->method('set')
|
||||
->willReturn(true);
|
||||
|
||||
$cachedDiscoverer = new CachedDiscoverer(
|
||||
$discoverer,
|
||||
$cache,
|
||||
new NullLogger()
|
||||
);
|
||||
|
||||
$result = $cachedDiscoverer->discover('/test/path', ['.'], []);
|
||||
$this->assertInstanceOf(DiscoveryState::class, $result);
|
||||
}
|
||||
|
||||
public function testCachedDiscovererReturnsCachedResults(): void
|
||||
{
|
||||
$registry = new Registry(null, new NullLogger());
|
||||
$discoverer = new Discoverer($registry, new NullLogger());
|
||||
|
||||
$cache = $this->createMock(CacheInterface::class);
|
||||
$cachedState = new DiscoveryState();
|
||||
$cache->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn($cachedState);
|
||||
|
||||
$cache->expects($this->never())
|
||||
->method('set');
|
||||
|
||||
$cachedDiscoverer = new CachedDiscoverer(
|
||||
$discoverer,
|
||||
$cache,
|
||||
new NullLogger()
|
||||
);
|
||||
|
||||
$result = $cachedDiscoverer->discover('/test/path', ['.'], []);
|
||||
$this->assertInstanceOf(DiscoveryState::class, $result);
|
||||
}
|
||||
|
||||
public function testCacheKeyGeneration(): void
|
||||
{
|
||||
$registry = new Registry(null, new NullLogger());
|
||||
$discoverer = new Discoverer($registry, new NullLogger());
|
||||
|
||||
$cache = $this->createMock(CacheInterface::class);
|
||||
|
||||
$cache->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->willReturn(null);
|
||||
|
||||
$cache->expects($this->exactly(2))
|
||||
->method('set')
|
||||
->willReturn(true);
|
||||
|
||||
$cachedDiscoverer = new CachedDiscoverer(
|
||||
$discoverer,
|
||||
$cache,
|
||||
new NullLogger()
|
||||
);
|
||||
|
||||
$result1 = $cachedDiscoverer->discover('/path1', ['.'], []);
|
||||
$result2 = $cachedDiscoverer->discover('/path2', ['.'], []);
|
||||
$this->assertInstanceOf(DiscoveryState::class, $result1);
|
||||
$this->assertInstanceOf(DiscoveryState::class, $result2);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue