From 1fdfb853e60aebf9802be2e828416139bbb29841 Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 29 May 2026 10:57:08 +0200 Subject: [PATCH 01/12] Update dependencies and enhance type validation in session handling --- composer.json | 4 ++-- src/DI/Helpers/BuilderMan.php | 6 +++++- src/DI/McpExtension.php | 7 ++++--- src/Http/GuzzleBridge.php | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index fc902ec..169d295 100644 --- a/composer.json +++ b/composer.json @@ -25,11 +25,11 @@ "psr/container": "^2.0.2", "psr/cache": "^3.0.0", "psr/simple-cache": "^3.0.0", - "mcp/sdk": "dev-main#369932378f7bcfa29a00efc1530b70c4d501a24b" + "mcp/sdk": "^0.5.0" }, "require-dev": { "ext-pdo": "*", - "contributte/phpstan": "^0.2.0", + "contributte/phpstan": "^0.3.0", "contributte/qa": "^0.5.0", "contributte/tester": "^0.5.0", "mockery/mockery": "^1.6.12", diff --git a/src/DI/Helpers/BuilderMan.php b/src/DI/Helpers/BuilderMan.php index 233b183..24d1a9d 100644 --- a/src/DI/Helpers/BuilderMan.php +++ b/src/DI/Helpers/BuilderMan.php @@ -31,7 +31,11 @@ public function getServiceDefinitionsByTag(string $tag): array $definitions = []; foreach ($builder->findByTag($tag) as $serviceName => $tagValue) { - $definitions[(string) $tagValue] = $builder->getDefinition($serviceName); + if (!is_string($tagValue)) { + continue; + } + + $definitions[$tagValue] = $builder->getDefinition($serviceName); } return $definitions; diff --git a/src/DI/McpExtension.php b/src/DI/McpExtension.php index ff7f127..426de0b 100644 --- a/src/DI/McpExtension.php +++ b/src/DI/McpExtension.php @@ -17,7 +17,7 @@ use Mcp\Server\Builder; use Mcp\Server\Session\FileSessionStore; use Mcp\Server\Session\InMemorySessionStore; -use Mcp\Server\Session\Psr16StoreSession; +use Mcp\Server\Session\Psr16SessionStore; use Nette\Application\IPresenterFactory; use Nette\DI\CompilerExtension; use Nette\DI\Definitions\ServiceDefinition; @@ -178,7 +178,8 @@ private function loadServerConfiguration(string $serverName, stdClass $serverCon // Server:Session switch ($serverConfig->session->type) { case 'file': - $path = $serverConfig->session->path ?? (isset($builder->parameters['tempDir']) ? $builder->parameters['tempDir'] . '/mcp' : null); + $tempDir = $builder->parameters['tempDir'] ?? null; + $path = $serverConfig->session->path ?? (is_string($tempDir) ? $tempDir . '/mcp' : null); if ($path === null) { throw new LogicalException( sprintf('Session path must be configured for file sessions (server "%s"). Either set session.path or ensure %%tempDir%% is available.', $serverName) @@ -198,7 +199,7 @@ private function loadServerConfiguration(string $serverName, stdClass $serverCon } $cacheService = BuilderMan::of($this)->resolveService($serverConfig->session->cache); - $sessionStore = new Statement(Psr16StoreSession::class, [$cacheService, $serverConfig->session->prefix, $serverConfig->session->ttl]); + $sessionStore = new Statement(Psr16SessionStore::class, [$cacheService, $serverConfig->session->prefix, $serverConfig->session->ttl]); break; default: $sessionStore = null; diff --git a/src/Http/GuzzleBridge.php b/src/Http/GuzzleBridge.php index d2bd371..686b14b 100644 --- a/src/Http/GuzzleBridge.php +++ b/src/Http/GuzzleBridge.php @@ -22,7 +22,7 @@ public static function fromNette(IRequest $netteRequest): ServerRequestInterface $headers = []; foreach ($netteRequest->getHeaders() as $name => $value) { - $headers[$name] = is_array($value) ? $value : [$value]; + $headers[$name] = [$value]; } $body = $netteRequest->getRawBody(); From c0cc4d7f160c404d45242590cca5f30a319b128c Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 29 May 2026 10:59:29 +0200 Subject: [PATCH 02/12] Update package name in composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 169d295..9762f69 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "contributte/mcp", + "name": "demmmmios/mcp", "description": "Doctrine DBAL for Nette Framework", "keywords": [ "nette", From 66cab145c799c97b4df2a81545fdf19554a1e298 Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 29 May 2026 11:55:27 +0200 Subject: [PATCH 03/12] Downgrade PHP version requirement to >=8.3 in composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9762f69..8093cc3 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php": ">=8.4", + "php": ">=8.3", "guzzlehttp/guzzle": "^7.10.0", "psr/container": "^2.0.2", "psr/cache": "^3.0.0", From 73f9f701c2d3f7345dfe60971c8f1d5942eb27cd Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 26 Jun 2026 10:34:10 +0200 Subject: [PATCH 04/12] Upgrade `mcp/sdk` to v0.6.0 and refactor registry methods to improve type consistency and functionality --- composer.json | 2 +- src/Http/StreamableTransportFactory.php | 1 - src/Registry/TraceableRegistry.php | 58 +++++++++++++++++-------- tests/Toolkit/ServerInspector.php | 4 +- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/composer.json b/composer.json index 8093cc3..85a2375 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "psr/container": "^2.0.2", "psr/cache": "^3.0.0", "psr/simple-cache": "^3.0.0", - "mcp/sdk": "^0.5.0" + "mcp/sdk": "^0.6.0" }, "require-dev": { "ext-pdo": "*", diff --git a/src/Http/StreamableTransportFactory.php b/src/Http/StreamableTransportFactory.php index e64cbe1..815ec0e 100644 --- a/src/Http/StreamableTransportFactory.php +++ b/src/Http/StreamableTransportFactory.php @@ -36,7 +36,6 @@ public function create(mixed ...$args): TransportInterface $serverRequest, $this->responseFactory, $this->streamFactory, - [], $this->logger, ); } diff --git a/src/Registry/TraceableRegistry.php b/src/Registry/TraceableRegistry.php index e8d0dbe..e625c0f 100644 --- a/src/Registry/TraceableRegistry.php +++ b/src/Registry/TraceableRegistry.php @@ -2,7 +2,6 @@ namespace Contributte\Mcp\Registry; -use Mcp\Capability\Discovery\DiscoveryState; use Mcp\Capability\Registry\PromptReference; use Mcp\Capability\Registry\ResourceReference; use Mcp\Capability\Registry\ResourceTemplateReference; @@ -10,7 +9,7 @@ use Mcp\Capability\RegistryInterface; use Mcp\Schema\Page; use Mcp\Schema\Prompt; -use Mcp\Schema\Resource; +use Mcp\Schema\ResourceDefinition; use Mcp\Schema\ResourceTemplate; use Mcp\Schema\Tool; @@ -39,14 +38,14 @@ public function getCalls(): array return $this->calls; } - public function registerTool(Tool $tool, callable|array|string $handler, bool $isManual = false): void + public function registerTool(Tool $tool, callable|array|string $handler): ToolReference { - $this->registry->registerTool($tool, $handler, $isManual); + return $this->registry->registerTool($tool, $handler); } - public function registerResource(Resource $resource, callable|array|string $handler, bool $isManual = false): void + public function registerResource(ResourceDefinition $resource, callable|array|string $handler): ResourceReference { - $this->registry->registerResource($resource, $handler, $isManual); + return $this->registry->registerResource($resource, $handler); } /** @@ -56,10 +55,9 @@ public function registerResourceTemplate( ResourceTemplate $template, callable|array|string $handler, array $completionProviders = [], - bool $isManual = false, - ): void + ): ResourceTemplateReference { - $this->registry->registerResourceTemplate($template, $handler, $completionProviders, $isManual); + return $this->registry->registerResourceTemplate($template, $handler, $completionProviders); } /** @@ -69,25 +67,49 @@ public function registerPrompt( Prompt $prompt, callable|array|string $handler, array $completionProviders = [], - bool $isManual = false, - ): void + ): PromptReference { - $this->registry->registerPrompt($prompt, $handler, $completionProviders, $isManual); + return $this->registry->registerPrompt($prompt, $handler, $completionProviders); } - public function clear(): void + public function unregisterTool(string $name): void { - $this->registry->clear(); + $this->registry->unregisterTool($name); } - public function getDiscoveryState(): DiscoveryState + public function unregisterResource(string $uri): void { - return $this->registry->getDiscoveryState(); + $this->registry->unregisterResource($uri); } - public function setDiscoveryState(DiscoveryState $state): void + public function unregisterResourceTemplate(string $uriTemplate): void { - $this->registry->setDiscoveryState($state); + $this->registry->unregisterResourceTemplate($uriTemplate); + } + + public function unregisterPrompt(string $name): void + { + $this->registry->unregisterPrompt($name); + } + + public function hasTool(string $name): bool + { + return $this->registry->hasTool($name); + } + + public function hasResource(string $uri): bool + { + return $this->registry->hasResource($uri); + } + + public function hasResourceTemplate(string $uriTemplate): bool + { + return $this->registry->hasResourceTemplate($uriTemplate); + } + + public function hasPrompt(string $name): bool + { + return $this->registry->hasPrompt($name); } public function hasTools(): bool diff --git a/tests/Toolkit/ServerInspector.php b/tests/Toolkit/ServerInspector.php index 067315c..400aa48 100644 --- a/tests/Toolkit/ServerInspector.php +++ b/tests/Toolkit/ServerInspector.php @@ -4,7 +4,7 @@ use Mcp\Capability\RegistryInterface; use Mcp\Schema\Prompt; -use Mcp\Schema\Resource; +use Mcp\Schema\ResourceDefinition; use Mcp\Schema\Tool; use Mcp\Server; use Mcp\Server\Configuration; @@ -43,7 +43,7 @@ public static function hasTools(Server $server): bool } /** - * @return array + * @return array */ public static function getResources(Server $server): array { From e6e9c4354f037b951e390e830a7fe69b479a6da8 Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 26 Jun 2026 10:39:11 +0200 Subject: [PATCH 05/12] Add `symfony/finder` dependency and set default `Tool` title to null in tests --- composer.json | 1 + tests/Cases/Tracy/TraceableRegistry.phpt | 1 + 2 files changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 85a2375..fb7c571 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,7 @@ "nette/di": "^3.2.5", "nette/http": "^3.3.3", "symfony/console": "^7.4.1 || ^8.0.1", + "symfony/finder": "^7.4.1 || ^8.0.1", "tracy/tracy": "^2.11.0" }, "autoload": { diff --git a/tests/Cases/Tracy/TraceableRegistry.phpt b/tests/Cases/Tracy/TraceableRegistry.phpt index ae948f8..d7e6c7c 100644 --- a/tests/Cases/Tracy/TraceableRegistry.phpt +++ b/tests/Cases/Tracy/TraceableRegistry.phpt @@ -71,6 +71,7 @@ Toolkit::test(function (): void { $tool = new Tool( name: 'test-tool', + title: null, inputSchema: ['type' => 'object', 'properties' => [], 'required' => []], description: 'A test tool', annotations: null, From 0ce7a60e14ae5a99fdc26a621d032ba516b9a94f Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 3 Jul 2026 15:59:50 +0200 Subject: [PATCH 06/12] Filter out `DnsRebindingProtectionMiddleware` from default middleware in `StreamableTransportFactory` --- src/Http/StreamableTransportFactory.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Http/StreamableTransportFactory.php b/src/Http/StreamableTransportFactory.php index 815ec0e..6e0d0dd 100644 --- a/src/Http/StreamableTransportFactory.php +++ b/src/Http/StreamableTransportFactory.php @@ -3,6 +3,7 @@ namespace Contributte\Mcp\Http; use Contributte\Mcp\Exception\LogicalException; +use Mcp\Server\Transport\Http\Middleware\DnsRebindingProtectionMiddleware; use Mcp\Server\Transport\StreamableHttpTransport; use Mcp\Server\Transport\TransportInterface; use Psr\Http\Message\ResponseFactoryInterface; @@ -37,7 +38,18 @@ public function create(mixed ...$args): TransportInterface $this->responseFactory, $this->streamFactory, $this->logger, + $this->createMiddleware(), ); } + /** + * @return list<\Psr\Http\Server\MiddlewareInterface> + */ + private function createMiddleware(): array + { + $middleware = StreamableHttpTransport::defaultMiddleware(); + + return array_filter($middleware, static fn($item) => !$item instanceof DnsRebindingProtectionMiddleware); + } + } From 330ab2ad39b6d82d1f9b4cee4137ee84562b01c4 Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 10 Jul 2026 14:42:26 +0200 Subject: [PATCH 07/12] Downgrade PHP version requirement to >=8.2 and fix minor type and return value inconsistencies --- composer.json | 2 +- phpstan.neon | 2 +- src/DI/McpExtension.php | 6 +++--- src/Http/StreamableTransportFactory.php | 2 +- src/Tracy/McpPanel.php | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index fb7c571..aa64cb3 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php": ">=8.3", + "php": ">=8.2", "guzzlehttp/guzzle": "^7.10.0", "psr/container": "^2.0.2", "psr/cache": "^3.0.0", diff --git a/phpstan.neon b/phpstan.neon index 201ecd8..16b3197 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,7 +3,7 @@ includes: parameters: level: 9 - phpVersion: 80400 + phpVersion: 80200 scanDirectories: - src diff --git a/src/DI/McpExtension.php b/src/DI/McpExtension.php index 426de0b..c14f90f 100644 --- a/src/DI/McpExtension.php +++ b/src/DI/McpExtension.php @@ -34,9 +34,9 @@ class McpExtension extends CompilerExtension { - public const string SERVER_TAG = 'contributte.mcp.server'; - public const string SERVER_FACTORY_TAG = 'contributte.mcp.server_factory'; - public const string TRANSPORT_FACTORY_TAG = 'contributte.mcp.transport_factory'; + public const SERVER_TAG = 'contributte.mcp.server'; + public const SERVER_FACTORY_TAG = 'contributte.mcp.server_factory'; + public const TRANSPORT_FACTORY_TAG = 'contributte.mcp.transport_factory'; public function getConfigSchema(): Schema { diff --git a/src/Http/StreamableTransportFactory.php b/src/Http/StreamableTransportFactory.php index 6e0d0dd..11a07a6 100644 --- a/src/Http/StreamableTransportFactory.php +++ b/src/Http/StreamableTransportFactory.php @@ -49,7 +49,7 @@ private function createMiddleware(): array { $middleware = StreamableHttpTransport::defaultMiddleware(); - return array_filter($middleware, static fn($item) => !$item instanceof DnsRebindingProtectionMiddleware); + return array_values(array_filter($middleware, static fn($item) => !$item instanceof DnsRebindingProtectionMiddleware)); } } diff --git a/src/Tracy/McpPanel.php b/src/Tracy/McpPanel.php index 498db26..9375a1c 100644 --- a/src/Tracy/McpPanel.php +++ b/src/Tracy/McpPanel.php @@ -34,7 +34,7 @@ public function getTab(): string ob_start(); require __DIR__ . '/templates/tab.phtml'; - return (string) ob_get_clean(); + return ob_get_clean(); } public function getPanel(): string @@ -52,7 +52,7 @@ public function getPanel(): string ob_start(); require __DIR__ . '/templates/panel.phtml'; - return (string) ob_get_clean(); + return ob_get_clean(); } } From 2edf4ed2eb0a627defbc6a363d5711d07f1a165c Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 10 Jul 2026 14:56:28 +0200 Subject: [PATCH 08/12] Relax PSR dependencies to allow broader version compatibility in composer.json --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index aa64cb3..75e8122 100644 --- a/composer.json +++ b/composer.json @@ -22,9 +22,9 @@ "require": { "php": ">=8.2", "guzzlehttp/guzzle": "^7.10.0", - "psr/container": "^2.0.2", - "psr/cache": "^3.0.0", - "psr/simple-cache": "^3.0.0", + "psr/container": "^1.0 || ^2.0", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "psr/simple-cache": "^2.0 || ^3.0", "mcp/sdk": "^0.6.0" }, "require-dev": { From 90a3d273c5650511e57148cbaa4ceffc7f3985ac Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 10 Jul 2026 15:12:41 +0200 Subject: [PATCH 09/12] Update package metadata in composer.json --- composer.json | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/composer.json b/composer.json index 75e8122..d1f5191 100644 --- a/composer.json +++ b/composer.json @@ -1,18 +1,13 @@ { "name": "demmmmios/mcp", - "description": "Doctrine DBAL for Nette Framework", + "description": "MCP server for Nette Framework", "keywords": [ "nette", - "doctrine", - "dbal", - "database", - "mysql", - "postgres", - "sqlite" + "mcp" ], "type": "library", "license": "MIT", - "homepage": "https://github.com/contributte/doctrine-dbal", + "homepage": "https://github.com/contributte/mcp", "authors": [ { "name": "Milan Felix Ć ulc", @@ -50,9 +45,6 @@ "Tests\\": "tests" } }, - "conflict": { - "nette/neon": "<3.4.5" - }, "minimum-stability": "dev", "prefer-stable": true, "config": { From 13ca8cc3dfe976e38ed98afe735d2c0427627f9d Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Thu, 16 Jul 2026 12:15:07 +0200 Subject: [PATCH 10/12] Upgrade `mcp/sdk` to v0.7.0 and add `MiddlewareInterface` import in `StreamableTransportFactory` --- composer.json | 2 +- src/Http/StreamableTransportFactory.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index d1f5191..1f4f209 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "psr/container": "^1.0 || ^2.0", "psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/simple-cache": "^2.0 || ^3.0", - "mcp/sdk": "^0.6.0" + "mcp/sdk": "^0.7.0" }, "require-dev": { "ext-pdo": "*", diff --git a/src/Http/StreamableTransportFactory.php b/src/Http/StreamableTransportFactory.php index 11a07a6..cdd8527 100644 --- a/src/Http/StreamableTransportFactory.php +++ b/src/Http/StreamableTransportFactory.php @@ -9,6 +9,7 @@ use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\StreamFactoryInterface; +use Psr\Http\Server\MiddlewareInterface; use Psr\Log\LoggerInterface; final class StreamableTransportFactory implements TransportFactoryInterface @@ -43,13 +44,13 @@ public function create(mixed ...$args): TransportInterface } /** - * @return list<\Psr\Http\Server\MiddlewareInterface> + * @return list */ private function createMiddleware(): array { $middleware = StreamableHttpTransport::defaultMiddleware(); - return array_values(array_filter($middleware, static fn($item) => !$item instanceof DnsRebindingProtectionMiddleware)); + return array_values(array_filter($middleware, static fn ($item) => !$item instanceof DnsRebindingProtectionMiddleware)); } } From 174dfff35dfd480fdc8d7a863495142b3f89ddba Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Mon, 10 Aug 2026 12:19:04 +0200 Subject: [PATCH 11/12] feat: Added `paginationLimit` configuration to server settings and DI setup --- .docs/README.md | 1 + src/DI/McpExtension.php | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.docs/README.md b/.docs/README.md index e304553..dc1226f 100644 --- a/.docs/README.md +++ b/.docs/README.md @@ -60,6 +60,7 @@ mcp: # Server information name: # Server name (default: 'MCP') version: # Server version (default: '1.0.0') + paginationLimit: # Max items returned per page for list methods (default: 50) # Discovery configuration discovery: diff --git a/src/DI/McpExtension.php b/src/DI/McpExtension.php index c14f90f..e216fe4 100644 --- a/src/DI/McpExtension.php +++ b/src/DI/McpExtension.php @@ -55,6 +55,7 @@ public function getConfigSchema(): Schema Expect::structure([ 'name' => Expect::string()->default('MCP'), 'version' => Expect::string()->default('1.0.0'), + 'paginationLimit' => Expect::int(50), 'discovery' => Expect::structure([ 'enabled' => Expect::bool(true), 'basePath' => Expect::string()->default($parameters['appDir'] ?? getcwd()), @@ -154,6 +155,11 @@ private function loadServerConfiguration(string $serverName, stdClass $serverCon $serverConfig->version, ]); + // Server:PaginationLimit + $builderDef->addSetup('setPaginationLimit', [ + $serverConfig->paginationLimit, + ]); + // Server:Container if ($serverConfig->container === null) { $builderDef->addSetup('setContainer', [new Statement(NetteContainer::class, ['@container'])]); From 23718dfa884acaa92d42da0ef4e33c54929d92a1 Mon Sep 17 00:00:00 2001 From: Petr Hahn Date: Fri, 21 Aug 2026 10:43:01 +0200 Subject: [PATCH 12/12] feat: Add `mergeDefaults` option to `scanDirs` and `excludeDirs` in DI configuration --- src/DI/McpExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DI/McpExtension.php b/src/DI/McpExtension.php index e216fe4..bbcda07 100644 --- a/src/DI/McpExtension.php +++ b/src/DI/McpExtension.php @@ -59,8 +59,8 @@ public function getConfigSchema(): Schema 'discovery' => Expect::structure([ 'enabled' => Expect::bool(true), 'basePath' => Expect::string()->default($parameters['appDir'] ?? getcwd()), - 'scanDirs' => Expect::arrayOf(Expect::string())->default(['.']), - 'excludeDirs' => Expect::arrayOf(Expect::string())->default([]), + 'scanDirs' => Expect::arrayOf(Expect::string())->default(['.'])->mergeDefaults(false), + 'excludeDirs' => Expect::arrayOf(Expect::string())->default([])->mergeDefaults(false), 'cache' => Expect::anyOf($expectService, null)->default(null), ])->required(), 'session' => Expect::structure([