[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
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
namespace Mcp\Capability;
|
||||
|
||||
use Mcp\Capability\Discovery\DiscoveryState;
|
||||
use Mcp\Capability\Registry\PromptReference;
|
||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
||||
use Mcp\Capability\Registry\ReferenceRegistryInterface;
|
||||
|
|
@ -260,8 +261,10 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
|
|||
|
||||
public function getResourceTemplates(): array
|
||||
{
|
||||
return array_map(fn (ResourceTemplateReference $template) => $template->resourceTemplate,
|
||||
$this->resourceTemplates);
|
||||
return array_map(
|
||||
fn (ResourceTemplateReference $template) => $template->resourceTemplate,
|
||||
$this->resourceTemplates
|
||||
);
|
||||
}
|
||||
|
||||
public function hasElements(): bool
|
||||
|
|
@ -271,4 +274,57 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
|
|||
|| !empty($this->prompts)
|
||||
|| !empty($this->resourceTemplates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current discovery state (only discovered elements, not manual ones).
|
||||
*/
|
||||
public function getDiscoveryState(): DiscoveryState
|
||||
{
|
||||
return new DiscoveryState(
|
||||
tools: array_filter($this->tools, fn ($tool) => !$tool->isManual),
|
||||
resources: array_filter($this->resources, fn ($resource) => !$resource->isManual),
|
||||
prompts: array_filter($this->prompts, fn ($prompt) => !$prompt->isManual),
|
||||
resourceTemplates: array_filter($this->resourceTemplates, fn ($template) => !$template->isManual),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set discovery state, replacing all discovered elements.
|
||||
* Manual elements are preserved.
|
||||
*/
|
||||
public function setDiscoveryState(DiscoveryState $state): void
|
||||
{
|
||||
// Clear existing discovered elements
|
||||
$this->clear();
|
||||
|
||||
// Import new discovered elements
|
||||
foreach ($state->getTools() as $name => $tool) {
|
||||
$this->tools[$name] = $tool;
|
||||
}
|
||||
|
||||
foreach ($state->getResources() as $uri => $resource) {
|
||||
$this->resources[$uri] = $resource;
|
||||
}
|
||||
|
||||
foreach ($state->getPrompts() as $name => $prompt) {
|
||||
$this->prompts[$name] = $prompt;
|
||||
}
|
||||
|
||||
foreach ($state->getResourceTemplates() as $uriTemplate => $template) {
|
||||
$this->resourceTemplates[$uriTemplate] = $template;
|
||||
}
|
||||
|
||||
// Dispatch events for the imported elements
|
||||
if ($this->eventDispatcher instanceof EventDispatcherInterface) {
|
||||
if (!empty($state->getTools())) {
|
||||
$this->eventDispatcher->dispatch(new ToolListChangedEvent());
|
||||
}
|
||||
if (!empty($state->getResources()) || !empty($state->getResourceTemplates())) {
|
||||
$this->eventDispatcher->dispatch(new ResourceListChangedEvent());
|
||||
}
|
||||
if (!empty($state->getPrompts())) {
|
||||
$this->eventDispatcher->dispatch(new PromptListChangedEvent());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue