[Examples][Tests] Make the MCP Apps example work over HTTP, and pin it (#439)
* [Examples][Tests] Make the MCP Apps example work over HTTP, and pin it It was the only example without a session store, so every request under `php -S` or php-fpm - each its own process - was answered "Session not found or has expired". It now uses a FileSessionStore like the others. Inspector snapshots pin what the extension actually puts on the wire: the ui:// resource's `text/html;profile=mcp-app` MIME type and `ui` marker, and the `_meta.ui` on the tool that links the two. * Apply suggestion from @chr-hertel * Apply suggestion from @chr-hertel
This commit is contained in:
parent
7407661260
commit
0751a83658
8 changed files with 130 additions and 0 deletions
|
|
@ -18,12 +18,14 @@ use Mcp\Schema\Extension\Apps\McpApps;
|
||||||
use Mcp\Schema\Extension\Apps\ToolVisibility;
|
use Mcp\Schema\Extension\Apps\ToolVisibility;
|
||||||
use Mcp\Schema\Extension\Apps\UiToolMeta;
|
use Mcp\Schema\Extension\Apps\UiToolMeta;
|
||||||
use Mcp\Server;
|
use Mcp\Server;
|
||||||
|
use Mcp\Server\Session\FileSessionStore;
|
||||||
|
|
||||||
logger()->info('Starting MCP Apps Example Server...');
|
logger()->info('Starting MCP Apps Example Server...');
|
||||||
|
|
||||||
$server = Server::builder()
|
$server = Server::builder()
|
||||||
->setServerInfo('MCP Apps Weather Example', '1.0.0')
|
->setServerInfo('MCP Apps Weather Example', '1.0.0')
|
||||||
->setLogger(logger())
|
->setLogger(logger())
|
||||||
|
->setSession(new FileSessionStore(__DIR__.'/sessions'))
|
||||||
->enableExtension(new McpApps())
|
->enableExtension(new McpApps())
|
||||||
->addResource(
|
->addResource(
|
||||||
[WeatherApp::class, 'getWeatherApp'],
|
[WeatherApp::class, 'getWeatherApp'],
|
||||||
|
|
|
||||||
50
tests/Inspector/Http/HttpMcpAppsTest.php
Normal file
50
tests/Inspector/Http/HttpMcpAppsTest.php
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?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\Http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snapshots the MCP Apps example as the Inspector sees it.
|
||||||
|
*
|
||||||
|
* What the snapshots are actually pinning is the extension's metadata surviving
|
||||||
|
* registration: the `ui://` resource carrying `text/html;profile=mcp-app` and
|
||||||
|
* the `ui` marker in its `_meta`, and the tool carrying the `UiToolMeta` that
|
||||||
|
* links it to that resource. Those are the two halves a host needs to render an
|
||||||
|
* app, and both travel in `_meta`, where a silent drop would otherwise go
|
||||||
|
* unnoticed until a host failed to render anything.
|
||||||
|
*/
|
||||||
|
final class HttpMcpAppsTest extends HttpInspectorSnapshotTestCase
|
||||||
|
{
|
||||||
|
public static function provideMethods(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
...parent::provideMethods(),
|
||||||
|
'Read the UI resource' => [
|
||||||
|
'method' => 'resources/read',
|
||||||
|
'options' => ['uri' => 'ui://weather-app'],
|
||||||
|
'testName' => 'weather_app',
|
||||||
|
],
|
||||||
|
'Call the linked tool' => [
|
||||||
|
'method' => 'tools/call',
|
||||||
|
'options' => [
|
||||||
|
'toolName' => 'get_weather',
|
||||||
|
'toolArgs' => ['city' => 'london'],
|
||||||
|
],
|
||||||
|
'testName' => 'get_weather',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getServerScript(): string
|
||||||
|
{
|
||||||
|
return \dirname(__DIR__, 3).'/examples/server/mcp-apps/server.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"prompts": []
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"name": "weather-app",
|
||||||
|
"uri": "ui://weather-app",
|
||||||
|
"description": "Interactive weather dashboard",
|
||||||
|
"mimeType": "text/html;profile=mcp-app",
|
||||||
|
"_meta": {
|
||||||
|
"ui": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"resourceTemplates": []
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": "Weather in london: 15°C, Cloudy, Humidity: 78%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isError": false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"name": "get_weather",
|
||||||
|
"description": "Get current weather for a city",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"city": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"city"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_meta": {
|
||||||
|
"ui": {
|
||||||
|
"resourceUri": "ui://weather-app",
|
||||||
|
"visibility": [
|
||||||
|
"model",
|
||||||
|
"app"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue