Files
Inventory/src/Mcp/Tool/CustomField/UpsertCustomFieldValuesTool.php
Matthieu 4340a0e13e feat(mcp) : add business tools — search, history, comments, custom fields, documents, model types
- search_inventory: global search across all 6 entity types
- get_entity_history + get_activity_log: audit trail access
- 4 comment tools: list, create, resolve, unresolved count
- 3 custom field tools: list values, upsert, delete
- 2 document tools: list, delete (upload via REST only)
- 6 model type tools: list, get, create, update, delete, sync
- 69 MCP tests pass total

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 15:00:37 +01:00

115 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Mcp\Tool\CustomField;
use App\Entity\Composant;
use App\Entity\CustomField;
use App\Entity\CustomFieldValue;
use App\Entity\Machine;
use App\Entity\Piece;
use App\Entity\Product;
use App\Mcp\Tool\McpToolHelper;
use Doctrine\ORM\EntityManagerInterface;
use Mcp\Capability\Attribute\McpTool;
use Symfony\Bundle\SecurityBundle\Security;
#[McpTool(
name: 'upsert_custom_field_values',
description: 'Create or update custom field values for a given entity. Each entry in the fields array needs customFieldId and value. If a value already exists for that custom field + entity, it is updated; otherwise a new one is created. Requires ROLE_GESTIONNAIRE.',
)]
class UpsertCustomFieldValuesTool
{
use McpToolHelper;
private const ALLOWED_TYPES = ['machine', 'composant', 'piece', 'product'];
public function __construct(
private readonly EntityManagerInterface $em,
private readonly Security $security,
) {}
public function __invoke(string $entityType, string $entityId, array $fields): array
{
$this->requireRole($this->security, 'ROLE_GESTIONNAIRE');
$entityType = strtolower($entityType);
if (!in_array($entityType, self::ALLOWED_TYPES, true)) {
$this->mcpError('validation', "entityType must be one of: machine, composant, piece, product. Got '{$entityType}'.");
}
$entityClass = match ($entityType) {
'machine' => Machine::class,
'composant' => Composant::class,
'piece' => Piece::class,
'product' => Product::class,
};
$entity = $this->em->getRepository($entityClass)->find($entityId);
if (null === $entity) {
$this->mcpError('not_found', ucfirst($entityType)." not found: {$entityId}");
}
$results = [];
foreach ($fields as $fieldEntry) {
$customFieldId = $fieldEntry['customFieldId'] ?? null;
$value = $fieldEntry['value'] ?? '';
if (null === $customFieldId) {
$this->mcpError('validation', 'Each field entry must have a customFieldId.');
}
$customField = $this->em->getRepository(CustomField::class)->find($customFieldId);
if (null === $customField) {
$this->mcpError('not_found', "CustomField not found: {$customFieldId}");
}
$existing = $this->em->getRepository(CustomFieldValue::class)->findOneBy([
'customField' => $customField,
$entityType => $entity,
]);
if (null !== $existing) {
$existing->setValue((string) $value);
$results[] = [
'id' => $existing->getId(),
'customFieldId' => $customField->getId(),
'value' => (string) $value,
'action' => 'updated',
];
} else {
$cfv = new CustomFieldValue();
$cfv->setCustomField($customField);
$cfv->setValue((string) $value);
$setter = 'set'.ucfirst($entityType);
$cfv->{$setter}($entity);
$this->em->persist($cfv);
$this->em->flush();
$results[] = [
'id' => $cfv->getId(),
'customFieldId' => $customField->getId(),
'value' => (string) $value,
'action' => 'created',
];
}
}
$this->em->flush();
return $this->jsonResponse([
'entityType' => $entityType,
'entityId' => $entityId,
'results' => $results,
'total' => count($results),
]);
}
}