feat: add MCP server for the website chatbot over a mirrored TYPO3 content feed

This commit is contained in:
fromme committed 2026-10-08 11:46:58 +02:00
1 parent ff7ddf7a9e
commit 1b4384e3f3
52 files changed
+5117 -4

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Chatbot\Mcp\Tool;
use App\Chatbot\Mcp\ToolCallRunner;
use App\Chatbot\TripCatalog;
use Mcp\Capability\Attribute\McpTool;
use Mcp\Capability\Attribute\Schema;
use Mcp\Exception\ToolCallException;
use Mcp\Schema\Result\CallToolResult;
use Mcp\Schema\ToolAnnotations;
#[McpTool(
name: 'get_trip',
description: 'Full details of one trip: description, included services, programme, hotel, all upcoming dates with rooms, prices per person in EUR and availability, pickup points and departure times, FAQs. Use after search_trips, or when the user names a specific trip. Prices and availability are a snapshot as of dataAsOf; the booking page is binding.',
annotations: new ToolAnnotations(readOnlyHint: true, openWorldHint: false),
)]
final class GetTripTool
{
public function __construct(
private readonly TripCatalog $catalog,
private readonly ToolCallRunner $runner,
) {
}
public function __invoke(
#[Schema(description: 'Trip code, as returned by search_trips.', minLength: 1)]
string $code,
#[Schema(description: 'Narrows the dates to one hotel when a trip has several.')]
?string $hotelCode = null,
): CallToolResult {
return $this->runner->run('get_trip', get_defined_vars(), function () use ($code, $hotelCode): array {
$trip = $this->catalog->get(trim($code), $hotelCode);
if (null === $trip) {
throw new ToolCallException(sprintf('No trip with code "%s". Use search_trips to find trip codes.', $code));
}
return $trip;
});
}
}