Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ mcp:
# Server information
name: <string> # Server name (default: 'MCP')
version: <string> # Server version (default: '1.0.0')
paginationLimit: <int> # Max items returned per page for list methods (default: 50)

# Discovery configuration
discovery:
Expand Down
29 changes: 11 additions & 18 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,42 +1,38 @@
{
"name": "contributte/mcp",
"description": "Doctrine DBAL for Nette Framework",
"name": "demmmmios/mcp",
"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",
"homepage": "https://f3l1x.io"
}
],
"require": {
"php": ">=8.4",
"php": ">=8.2",
"guzzlehttp/guzzle": "^7.10.0",
"psr/container": "^2.0.2",
"psr/cache": "^3.0.0",
"psr/simple-cache": "^3.0.0",
"mcp/sdk": "dev-main#369932378f7bcfa29a00efc1530b70c4d501a24b"
"psr/container": "^1.0 || ^2.0",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"psr/simple-cache": "^2.0 || ^3.0",
"mcp/sdk": "^0.7.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",
"nette/application": "^3.2.8",
"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": {
Expand All @@ -49,9 +45,6 @@
"Tests\\": "tests"
}
},
"conflict": {
"nette/neon": "<3.4.5"
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ includes:

parameters:
level: 9
phpVersion: 80400
phpVersion: 80200

scanDirectories:
- src
Expand Down
6 changes: 5 additions & 1 deletion src/DI/Helpers/BuilderMan.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
23 changes: 15 additions & 8 deletions src/DI/McpExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
{
Expand All @@ -55,11 +55,12 @@ 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()),
'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([
Expand Down Expand Up @@ -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'])]);
Expand All @@ -178,7 +184,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)
Expand All @@ -198,7 +205,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;
Expand Down
2 changes: 1 addition & 1 deletion src/Http/GuzzleBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
14 changes: 13 additions & 1 deletion src/Http/StreamableTransportFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
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;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Log\LoggerInterface;

final class StreamableTransportFactory implements TransportFactoryInterface
Expand Down Expand Up @@ -36,9 +38,19 @@ public function create(mixed ...$args): TransportInterface
$serverRequest,
$this->responseFactory,
$this->streamFactory,
[],
$this->logger,
$this->createMiddleware(),
);
}

/**
* @return list<MiddlewareInterface>
*/
private function createMiddleware(): array
{
$middleware = StreamableHttpTransport::defaultMiddleware();

return array_values(array_filter($middleware, static fn ($item) => !$item instanceof DnsRebindingProtectionMiddleware));
}

}
58 changes: 40 additions & 18 deletions src/Registry/TraceableRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

namespace Contributte\Mcp\Registry;

use Mcp\Capability\Discovery\DiscoveryState;
use Mcp\Capability\Registry\PromptReference;
use Mcp\Capability\Registry\ResourceReference;
use Mcp\Capability\Registry\ResourceTemplateReference;
use Mcp\Capability\Registry\ToolReference;
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;

Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/Tracy/McpPanel.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,7 +52,7 @@ public function getPanel(): string
ob_start();
require __DIR__ . '/templates/panel.phtml';

return (string) ob_get_clean();
return ob_get_clean();
}

}
1 change: 1 addition & 0 deletions tests/Cases/Tracy/TraceableRegistry.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions tests/Toolkit/ServerInspector.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -43,7 +43,7 @@ public static function hasTools(Server $server): bool
}

/**
* @return array<string, Resource>
* @return array<string, ResourceDefinition>
*/
public static function getResources(Server $server): array
{
Expand Down