From abe235cd7d3fc4d3773b22ab3d6b0e85aabdcbd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Fri, 2 Oct 2026 23:29:04 +0200 Subject: [PATCH 1/7] Composer: fix package description, keywords and homepage --- composer.json | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index fc902ec..0b4ceae 100644 --- a/composer.json +++ b/composer.json @@ -1,18 +1,17 @@ { "name": "contributte/mcp", - "description": "Doctrine DBAL for Nette Framework", + "description": "Model Context Protocol (MCP) server integration for Nette Framework", "keywords": [ "nette", - "doctrine", - "dbal", - "database", - "mysql", - "postgres", - "sqlite" + "mcp", + "model-context-protocol", + "ai", + "llm", + "server" ], "type": "library", "license": "MIT", - "homepage": "https://github.com/contributte/doctrine-dbal", + "homepage": "https://github.com/contributte/mcp", "authors": [ { "name": "Milan Felix Ć ulc", From 798c11cdd560d25045c77b6d0b7f0b92b4791650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Fri, 2 Oct 2026 23:29:05 +0200 Subject: [PATCH 2/7] DI: validate tag values in BuilderMan --- src/DI/Helpers/BuilderMan.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/DI/Helpers/BuilderMan.php b/src/DI/Helpers/BuilderMan.php index 233b183..a7fa7d1 100644 --- a/src/DI/Helpers/BuilderMan.php +++ b/src/DI/Helpers/BuilderMan.php @@ -2,6 +2,7 @@ namespace Contributte\Mcp\DI\Helpers; +use Contributte\Mcp\Exception\LogicalException; use Nette\DI\CompilerExtension; use Nette\DI\Definitions\Definition; use Nette\DI\Definitions\Reference; @@ -31,6 +32,10 @@ public function getServiceDefinitionsByTag(string $tag): array $definitions = []; foreach ($builder->findByTag($tag) as $serviceName => $tagValue) { + if (!is_string($tagValue) && !is_int($tagValue)) { + throw new LogicalException(sprintf('Tag "%s" of service "%s" must have a string value', $tag, $serviceName)); + } + $definitions[(string) $tagValue] = $builder->getDefinition($serviceName); } From e7e3bceb9a50fb6a3e29045cf4a6a5a6dba043ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Fri, 2 Oct 2026 23:29:05 +0200 Subject: [PATCH 3/7] Composer: require stable mcp/sdk ^0.8.1 TraceableRegistry follows the new RegistryInterface, Psr16StoreSession is renamed to Psr16SessionStore, Resource to ResourceDefinition and StreamableHttpTransport no longer takes a CORS headers argument. --- composer.json | 2 +- src/DI/McpExtension.php | 4 +- src/Http/StreamableTransportFactory.php | 1 - src/Registry/TraceableRegistry.php | 58 ++++++++++++++++-------- tests/Cases/Tracy/TraceableRegistry.phpt | 13 +++++- tests/Toolkit/ServerInspector.php | 4 +- 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index 0b4ceae..04da131 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "psr/container": "^2.0.2", "psr/cache": "^3.0.0", "psr/simple-cache": "^3.0.0", - "mcp/sdk": "dev-main#369932378f7bcfa29a00efc1530b70c4d501a24b" + "mcp/sdk": "^0.8.1" }, "require-dev": { "ext-pdo": "*", diff --git a/src/DI/McpExtension.php b/src/DI/McpExtension.php index ff7f127..32f6236 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; @@ -198,7 +198,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/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/Cases/Tracy/TraceableRegistry.phpt b/tests/Cases/Tracy/TraceableRegistry.phpt index ae948f8..3afefc4 100644 --- a/tests/Cases/Tracy/TraceableRegistry.phpt +++ b/tests/Cases/Tracy/TraceableRegistry.phpt @@ -5,6 +5,7 @@ namespace Tests\Cases\Tracy; use Contributte\Mcp\Registry\TraceableRegistry; use Contributte\Tester\Toolkit; use Mcp\Capability\Registry; +use Mcp\Capability\Registry\ToolReference; use Mcp\Capability\RegistryInterface; use Mcp\Schema\Page; use Mcp\Schema\Tool; @@ -71,14 +72,24 @@ Toolkit::test(function (): void { $tool = new Tool( name: 'test-tool', + title: null, inputSchema: ['type' => 'object', 'properties' => [], 'required' => []], description: 'A test tool', annotations: null, ); - $traceableRegistry->registerTool($tool, fn () => 'result'); + $reference = $traceableRegistry->registerTool($tool, fn () => 'result'); + Assert::type(ToolReference::class, $reference); + Assert::same($tool, $reference->tool); Assert::true($traceableRegistry->hasTools()); Assert::true($innerRegistry->hasTools()); + Assert::true($traceableRegistry->hasTool('test-tool')); + Assert::false($traceableRegistry->hasTool('missing-tool')); + + $traceableRegistry->unregisterTool('test-tool'); + + Assert::false($traceableRegistry->hasTool('test-tool')); + Assert::false($innerRegistry->hasTool('test-tool')); }); // Test: TraceableRegistry accumulates multiple calls 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 db0916c83e2cf88cfb50874fd580d1defa6bc99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Fri, 2 Oct 2026 23:29:05 +0200 Subject: [PATCH 4/7] Code: tighten types reported by phpstan --- src/DI/McpExtension.php | 3 ++- src/Http/GuzzleBridge.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/DI/McpExtension.php b/src/DI/McpExtension.php index 32f6236..426de0b 100644 --- a/src/DI/McpExtension.php +++ b/src/DI/McpExtension.php @@ -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) 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 4b5e53f8aa3c8a4b233bc9c2d6dd03f052e1b0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Fri, 2 Oct 2026 23:29:05 +0200 Subject: [PATCH 5/7] Composer: require nette/di and symfony/finder, suggest optional integrations --- composer.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 04da131..1ed6aed 100644 --- a/composer.json +++ b/composer.json @@ -21,10 +21,12 @@ "require": { "php": ">=8.4", "guzzlehttp/guzzle": "^7.10.0", - "psr/container": "^2.0.2", + "mcp/sdk": "^0.8.1", + "nette/di": "^3.2.5", "psr/cache": "^3.0.0", + "psr/container": "^2.0.2", "psr/simple-cache": "^3.0.0", - "mcp/sdk": "^0.8.1" + "symfony/finder": "^7.4 || ^8.0" }, "require-dev": { "ext-pdo": "*", @@ -33,11 +35,16 @@ "contributte/tester": "^0.5.0", "mockery/mockery": "^1.6.12", "nette/application": "^3.2.8", - "nette/di": "^3.2.5", "nette/http": "^3.3.3", "symfony/console": "^7.4.1 || ^8.0.1", "tracy/tracy": "^2.11.0" }, + "suggest": { + "nette/application": "Required for McpPresenter and McpRouter (HTTP transport)", + "nette/http": "Required for McpPresenter and GuzzleBridge (HTTP transport)", + "symfony/console": "Required for McpCommand (stdio transport)", + "tracy/tracy": "Required for the Tracy debug panel" + }, "autoload": { "psr-4": { "Contributte\\Mcp\\": "src/" From a62a74a3447848a6f683a5387d637e1f17cf9347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Fri, 2 Oct 2026 23:29:05 +0200 Subject: [PATCH 6/7] Composer: bump branch alias to 0.3.x-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1ed6aed..e9881b4 100644 --- a/composer.json +++ b/composer.json @@ -69,7 +69,7 @@ }, "extra": { "branch-alias": { - "dev-master": "0.1.x-dev" + "dev-master": "0.3.x-dev" } } } From 283f2b6b0b6dc979d3d741b568e46773bc6a4ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Felix=20=C5=A0ulc?= Date: Fri, 2 Oct 2026 23:29:12 +0200 Subject: [PATCH 7/7] Docs: update versions table --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d75e022..932ace3 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,10 @@ For details on how to use this package, check out our [documentation](.docs). ## Versions -| State | Version | Branch | Nette | PHP | -|-------------|---------|----------|--------|---------| -| dev | `^0.1` | `master` | `3.2+` | `>=8.4` | +| State | Version | Branch | mcp/sdk | Nette | PHP | +|-------------|---------|----------|------------|--------|---------| +| dev | `^0.3` | `master` | `^0.8` | `3.2+` | `>=8.4` | +| stable | `^0.2` | `master` | `dev-main` | `3.2+` | `>=8.4` | ## Development