* [Tests] Bump MCP Inspector to 0.22.0 for 2025-11-25 protocol The pinned inspector 0.17.2 predates the protocol version bump to 2025-11-25 (v0.6.0) and rejects the handshake with "Server's protocol version is not supported", failing every inspector snapshot test. 0.22.0 negotiates the current protocol; the full inspector suite passes against it (95 tests, 7 pre-existing skips). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [Examples] Demo pre-built instance handler in combined-registration Extends the combined-registration example with a third handler style: a pre-built object instance `[$instance, 'method']`, alongside the existing discovered and manual class-string handlers. PreconfiguredGreeter takes a scalar constructor argument neither the container-less `new $className()` fallback nor the auto-wiring container can build, so it can only be registered as an already-constructed instance — exercising the resolver path added in the parent branch. Adds an inspector snapshot case (tools/call instance_greeter) and updates the tools/list snapshot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
137 lines
4.5 KiB
PHP
137 lines
4.5 KiB
PHP
<?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\Inspector;
|
|
|
|
use Mcp\Schema\Enum\LoggingLevel;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Component\Process\Process;
|
|
|
|
abstract class InspectorSnapshotTestCase extends TestCase
|
|
{
|
|
private const INSPECTOR_VERSION = '0.22.0';
|
|
|
|
/** @param array<string, mixed> $options */
|
|
#[DataProvider('provideMethods')]
|
|
public function testOutputMatchesSnapshot(
|
|
string $method,
|
|
array $options = [],
|
|
?string $testName = null,
|
|
): void {
|
|
$inspector = \sprintf('@modelcontextprotocol/inspector@%s', self::INSPECTOR_VERSION);
|
|
|
|
$args = [
|
|
'npx',
|
|
$inspector,
|
|
'--cli',
|
|
...$this->getServerConnectionArgs(),
|
|
'--transport',
|
|
$this->getTransport(),
|
|
'--method',
|
|
$method,
|
|
];
|
|
|
|
// Options for tools/call
|
|
if (isset($options['toolName'])) {
|
|
$args[] = '--tool-name';
|
|
$args[] = $options['toolName'];
|
|
|
|
foreach ($options['toolArgs'] ?? [] as $key => $value) {
|
|
$args[] = '--tool-arg';
|
|
if (\is_array($value)) {
|
|
$args[] = \sprintf('%s=%s', $key, json_encode($value));
|
|
} elseif (\is_bool($value)) {
|
|
$args[] = \sprintf('%s=%s', $key, $value ? 'true' : 'false');
|
|
} else {
|
|
$args[] = \sprintf('%s=%s', $key, $value);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Options for resources/read
|
|
if (isset($options['uri'])) {
|
|
$args[] = '--uri';
|
|
$args[] = $options['uri'];
|
|
}
|
|
|
|
// Options for prompts/get
|
|
if (isset($options['promptName'])) {
|
|
$args[] = '--prompt-name';
|
|
$args[] = $options['promptName'];
|
|
|
|
foreach ($options['promptArgs'] ?? [] as $key => $value) {
|
|
$args[] = '--prompt-args';
|
|
if (\is_array($value)) {
|
|
$args[] = \sprintf('%s=%s', $key, json_encode($value));
|
|
} elseif (\is_bool($value)) {
|
|
$args[] = \sprintf('%s=%s', $key, $value ? 'true' : 'false');
|
|
} else {
|
|
$args[] = \sprintf('%s=%s', $key, $value);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Options for logging/setLevel
|
|
if (isset($options['logLevel'])) {
|
|
$args[] = '--log-level';
|
|
$args[] = $options['logLevel'] instanceof LoggingLevel ? $options['logLevel']->value : $options['logLevel'];
|
|
}
|
|
|
|
// Options for env variables
|
|
if (isset($options['envVars'])) {
|
|
foreach ($options['envVars'] as $key => $value) {
|
|
$args[] = '-e';
|
|
$args[] = \sprintf('%s=%s', $key, $value);
|
|
}
|
|
}
|
|
|
|
$output = (new Process(command: $args))
|
|
->mustRun()
|
|
->getOutput();
|
|
|
|
$snapshotFile = $this->getSnapshotFilePath($method, $testName);
|
|
|
|
$normalizedOutput = $this->normalizeTestOutput($output, $testName);
|
|
|
|
if (!file_exists($snapshotFile)) {
|
|
file_put_contents($snapshotFile, $normalizedOutput.\PHP_EOL);
|
|
$this->markTestIncomplete("Snapshot created at $snapshotFile, please re-run tests.");
|
|
}
|
|
|
|
$expected = file_get_contents($snapshotFile);
|
|
|
|
$message = \sprintf('Output does not match snapshot "%s".', $snapshotFile);
|
|
$this->assertJsonStringEqualsJsonString($expected, $normalizedOutput, $message);
|
|
}
|
|
|
|
protected function normalizeTestOutput(string $output, ?string $testName = null): string
|
|
{
|
|
return $output;
|
|
}
|
|
|
|
public static function provideMethods(): array
|
|
{
|
|
return [
|
|
'Prompt Listing' => ['method' => 'prompts/list'],
|
|
'Resource Listing' => ['method' => 'resources/list'],
|
|
'Resource Template Listing' => ['method' => 'resources/templates/list'],
|
|
'Tool Listing' => ['method' => 'tools/list'],
|
|
];
|
|
}
|
|
|
|
abstract protected function getSnapshotFilePath(string $method, ?string $testName = null): string;
|
|
|
|
/** @return array<string> */
|
|
abstract protected function getServerConnectionArgs(): array;
|
|
|
|
abstract protected function getTransport(): string;
|
|
}
|