Added MCP tools to access info provider features

This commit is contained in:
Jan Böhmer 2026-07-20 22:07:06 +02:00
parent a3fe7ea591
commit 7e53c7ae9a
14 changed files with 708 additions and 0 deletions

View file

@ -0,0 +1,63 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Services\InfoProviderSystem\DTOs;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\McpToolCollection;
use App\Mcp\DTO\ListInfoProvidersInput;
use App\State\Mcp\ListInfoProvidersProcessor;
/**
* This DTO represents an available info provider (a distributor or manufacturer catalog which can be
* searched via search_info_providers / get_info_provider_part_details).
*/
#[ApiResource(
description: 'An info provider which can be used to search for parts and retrieve part details.',
operations: [],
mcp: [
'list_info_providers' => new McpToolCollection(
title: 'List available info providers',
description: 'List the info providers (e.g. distributors like Digikey, Mouser, LCSC) which are currently active and can be used with search_info_providers and get_info_provider_part_details.',
annotations: ['readOnlyHint' => true, 'destructiveHint' => false, 'idempotentHint' => true, 'openWorldHint' => false],
input: ListInfoProvidersInput::class,
security: 'is_granted("@info_providers.create_parts")',
processor: ListInfoProvidersProcessor::class,
),
],
)]
readonly class InfoProviderDTO
{
public function __construct(
/** @var string The unique key of the provider, to be used as provider_key in the other info provider tools */
public string $key,
/** @var string The (user friendly) name of the provider (e.g. "Digikey") */
public string $name,
/** @var string|null A short description of the provider */
public ?string $description = null,
/** @var string|null The url of the provider (e.g. "https://www.digikey.com") */
public ?string $url = null,
/** @var string[] The kinds of data this provider can supply (e.g. "PRICE", "DATASHEET", "PICTURE") */
public array $capabilities = [],
) {
}
}

View file

@ -23,11 +23,30 @@ declare(strict_types=1);
namespace App\Services\InfoProviderSystem\DTOs;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\McpTool;
use App\Entity\Parts\ManufacturingStatus;
use App\Mcp\DTO\InfoProviderPartDetailsInput;
use App\State\Mcp\GetInfoProviderPartDetailsProcessor;
/**
* This DTO represents a part with all its details.
*/
#[ApiResource(
description: 'Detailed information about a part from an external info provider (e.g. a distributor or manufacturer catalog), including datasheets, images, parameters and purchase information.',
operations: [],
mcp: [
'get_info_provider_part_details' => new McpTool(
title: 'Get part details from an info provider',
description: 'Get full detailed information (datasheets, images, parameters, prices, ...) about a specific part from an external info provider, identified by the provider key and the provider-specific part ID (both returned by search_info_providers).',
annotations: ['readOnlyHint' => true, 'destructiveHint' => false, 'idempotentHint' => true, 'openWorldHint' => true],
input: InfoProviderPartDetailsInput::class,
security: 'is_granted("@info_providers.create_parts")',
validate: true,
processor: GetInfoProviderPartDetailsProcessor::class,
),
],
)]
class PartDetailDTO extends SearchResultDTO
{
public function __construct(

View file

@ -24,12 +24,14 @@ declare(strict_types=1);
namespace App\Services\InfoProviderSystem\DTOs;
use Brick\Math\BigDecimal;
use Symfony\Component\Serializer\Attribute\Ignore;
/**
* This DTO represents a price for a single unit in a certain discount range
*/
readonly class PriceDTO
{
#[Ignore]
private BigDecimal $price_as_big_decimal;
public function __construct(
@ -54,6 +56,7 @@ readonly class PriceDTO
* Gets the price as BigDecimal
* @return BigDecimal
*/
#[Ignore]
public function getPriceAsBigDecimal(): BigDecimal
{
return $this->price_as_big_decimal;

View file

@ -23,12 +23,31 @@ declare(strict_types=1);
namespace App\Services\InfoProviderSystem\DTOs;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\McpToolCollection;
use App\Entity\Parts\ManufacturingStatus;
use App\Mcp\DTO\InfoProviderSearchInput;
use App\State\Mcp\SearchInfoProvidersProcessor;
/**
* This DTO represents a search result for a part.
* @see \App\Tests\Services\InfoProviderSystem\DTOs\SearchResultDTOTest
*/
#[ApiResource(
description: 'A search result for a part from an external info provider (e.g. a distributor or manufacturer catalog).',
operations: [],
mcp: [
'search_info_providers' => new McpToolCollection(
title: 'Search external info providers',
description: 'Search external info providers (e.g. distributors like Digikey, Mouser, LCSC) for parts matching a keyword. Returns a list of search results, which can be passed to get_info_provider_part_details to retrieve full details for a specific result.',
annotations: ['readOnlyHint' => true, 'destructiveHint' => false, 'idempotentHint' => true, 'openWorldHint' => true],
input: InfoProviderSearchInput::class,
security: 'is_granted("@info_providers.create_parts")',
validate: true,
processor: SearchInfoProvidersProcessor::class,
),
],
)]
class SearchResultDTO
{
/** @var string|null An URL to a preview image */

View file

@ -126,6 +126,7 @@ final class PartInfoRetriever
* @param array<string, mixed> $options An associative array of options which can be used to modify the search behavior. The supported options depend on the provider and should be documented in the provider's documentation.
* @return PartDetailDTO
* @throws InfoProviderNotActiveException if the the given providers is not active
* @throws \InvalidArgumentException if the given provider key does not match any registered provider
*/
public function getDetails(string $provider_key, string $part_id, array $options = []): PartDetailDTO
{