From add3a9a21f4d11b30272e0ccdf01df7d6eb555dd Mon Sep 17 00:00:00 2001 From: Matthieu Date: Mon, 16 Mar 2026 17:24:04 +0100 Subject: [PATCH] fix(mcp) : return CallToolResult to prevent structuredContent serialization issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tools now return CallToolResult directly instead of Content arrays, preventing the MCP SDK from auto-generating structuredContent as a JSON array (which Claude Code rejects — expects a JSON object/record). Also adds Accept header to test helpers and SSE response parsing. Co-Authored-By: Claude Opus 4.6 (1M context) --- .mcp.json | 20 ++++++++--------- src/Mcp/Tool/ActivityLogTool.php | 3 ++- src/Mcp/Tool/Comment/CreateCommentTool.php | 3 ++- src/Mcp/Tool/Comment/ListCommentsTool.php | 3 ++- src/Mcp/Tool/Comment/ResolveCommentTool.php | 3 ++- src/Mcp/Tool/Comment/UnresolvedCountTool.php | 3 ++- .../Tool/Composant/CreateComposantTool.php | 3 ++- .../Tool/Composant/DeleteComposantTool.php | 3 ++- src/Mcp/Tool/Composant/GetComposantTool.php | 3 ++- src/Mcp/Tool/Composant/ListComposantsTool.php | 3 ++- .../Tool/Composant/UpdateComposantTool.php | 3 ++- .../Constructeur/CreateConstructeurTool.php | 3 ++- .../Constructeur/DeleteConstructeurTool.php | 3 ++- .../Tool/Constructeur/GetConstructeurTool.php | 3 ++- .../Constructeur/ListConstructeursTool.php | 3 ++- .../Constructeur/UpdateConstructeurTool.php | 3 ++- .../DeleteCustomFieldValueTool.php | 3 ++- .../CustomField/ListCustomFieldValuesTool.php | 3 ++- .../UpsertCustomFieldValuesTool.php | 3 ++- src/Mcp/Tool/DashboardStatsTool.php | 11 +++++----- src/Mcp/Tool/Document/DeleteDocumentTool.php | 3 ++- src/Mcp/Tool/Document/ListDocumentsTool.php | 3 ++- src/Mcp/Tool/EntityHistoryTool.php | 3 ++- src/Mcp/Tool/Machine/AddMachineLinksTool.php | 3 ++- src/Mcp/Tool/Machine/CloneMachineTool.php | 3 ++- src/Mcp/Tool/Machine/CreateMachineTool.php | 3 ++- src/Mcp/Tool/Machine/DeleteMachineTool.php | 3 ++- src/Mcp/Tool/Machine/GetMachineTool.php | 3 ++- src/Mcp/Tool/Machine/ListMachineLinksTool.php | 3 ++- src/Mcp/Tool/Machine/ListMachinesTool.php | 3 ++- src/Mcp/Tool/Machine/MachineStructureTool.php | 3 ++- .../Tool/Machine/RemoveMachineLinkTool.php | 3 ++- .../Tool/Machine/UpdateMachineLinkTool.php | 3 ++- src/Mcp/Tool/Machine/UpdateMachineTool.php | 3 ++- src/Mcp/Tool/McpToolHelper.php | 15 +++++-------- .../Tool/ModelType/CreateModelTypeTool.php | 3 ++- .../Tool/ModelType/DeleteModelTypeTool.php | 3 ++- src/Mcp/Tool/ModelType/GetModelTypeTool.php | 3 ++- src/Mcp/Tool/ModelType/ListModelTypesTool.php | 3 ++- src/Mcp/Tool/ModelType/SyncModelTypeTool.php | 3 ++- .../Tool/ModelType/UpdateModelTypeTool.php | 3 ++- src/Mcp/Tool/Piece/CreatePieceTool.php | 3 ++- src/Mcp/Tool/Piece/DeletePieceTool.php | 3 ++- src/Mcp/Tool/Piece/GetPieceTool.php | 3 ++- src/Mcp/Tool/Piece/ListPiecesTool.php | 3 ++- src/Mcp/Tool/Piece/UpdatePieceTool.php | 3 ++- src/Mcp/Tool/Product/CreateProductTool.php | 3 ++- src/Mcp/Tool/Product/DeleteProductTool.php | 3 ++- src/Mcp/Tool/Product/GetProductTool.php | 3 ++- src/Mcp/Tool/Product/ListProductsTool.php | 3 ++- src/Mcp/Tool/Product/UpdateProductTool.php | 3 ++- src/Mcp/Tool/SearchInventoryTool.php | 3 ++- src/Mcp/Tool/Site/CreateSiteTool.php | 3 ++- src/Mcp/Tool/Site/DeleteSiteTool.php | 3 ++- src/Mcp/Tool/Site/GetSiteTool.php | 3 ++- src/Mcp/Tool/Site/ListSitesTool.php | 3 ++- src/Mcp/Tool/Site/UpdateSiteTool.php | 3 ++- src/Mcp/Tool/Slot/ListSlotsTool.php | 7 +++--- src/Mcp/Tool/Slot/UpdateSlotsTool.php | 3 ++- tests/AbstractApiTestCase.php | 22 ++++++++++++++++++- 60 files changed, 156 insertions(+), 84 deletions(-) diff --git a/.mcp.json b/.mcp.json index 1b7c056..20aaa50 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,14 +1,12 @@ { - "mcpServers": { - "inventory": { - "command": "docker", - "args": [ - "exec", "-i", - "-e", "MCP_PROFILE_ID=REPLACE_WITH_YOUR_PROFILE_ID", - "-e", "MCP_PROFILE_PASSWORD=REPLACE_WITH_YOUR_PASSWORD", - "php-inventory-apache", - "php", "bin/console", "mcp:server" - ] + "mcpServers": { + "inventory": { + "type": "http", + "url": "http://inventory.malio-dev.fr/_mcp", + "headers": { + "X-Profile-Id": "admin-default-profile", + "X-Profile-Password": "A123" + } + } } - } } diff --git a/src/Mcp/Tool/ActivityLogTool.php b/src/Mcp/Tool/ActivityLogTool.php index 868c7be..14b716a 100644 --- a/src/Mcp/Tool/ActivityLogTool.php +++ b/src/Mcp/Tool/ActivityLogTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool; use App\Repository\AuditLogRepository; use DateTimeInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -22,7 +23,7 @@ class ActivityLogTool private readonly Security $security, ) {} - public function __invoke(int $page = 1, int $limit = 30, string $entityType = '', string $action = ''): array + public function __invoke(int $page = 1, int $limit = 30, string $entityType = '', string $action = ''): CallToolResult { $this->requireRole($this->security, 'ROLE_VIEWER'); diff --git a/src/Mcp/Tool/Comment/CreateCommentTool.php b/src/Mcp/Tool/Comment/CreateCommentTool.php index 25b4247..469e6c2 100644 --- a/src/Mcp/Tool/Comment/CreateCommentTool.php +++ b/src/Mcp/Tool/Comment/CreateCommentTool.php @@ -9,6 +9,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\ProfileRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -30,7 +31,7 @@ class CreateCommentTool string $entityType, string $entityId, string $entityName = '', - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_VIEWER'); $content = trim($content); diff --git a/src/Mcp/Tool/Comment/ListCommentsTool.php b/src/Mcp/Tool/Comment/ListCommentsTool.php index 55a002a..a0d14fe 100644 --- a/src/Mcp/Tool/Comment/ListCommentsTool.php +++ b/src/Mcp/Tool/Comment/ListCommentsTool.php @@ -8,6 +8,7 @@ use App\Entity\Comment; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_comments', @@ -21,7 +22,7 @@ class ListCommentsTool private readonly EntityManagerInterface $em, ) {} - public function __invoke(string $entityType, string $entityId, int $page = 1, int $limit = 30): array + public function __invoke(string $entityType, string $entityId, int $page = 1, int $limit = 30): CallToolResult { $p = $this->paginationParams($page, $limit); diff --git a/src/Mcp/Tool/Comment/ResolveCommentTool.php b/src/Mcp/Tool/Comment/ResolveCommentTool.php index 5f1eb81..454f36b 100644 --- a/src/Mcp/Tool/Comment/ResolveCommentTool.php +++ b/src/Mcp/Tool/Comment/ResolveCommentTool.php @@ -10,6 +10,7 @@ use App\Repository\ProfileRepository; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -26,7 +27,7 @@ class ResolveCommentTool private readonly ProfileRepository $profiles, ) {} - public function __invoke(string $commentId): array + public function __invoke(string $commentId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Comment/UnresolvedCountTool.php b/src/Mcp/Tool/Comment/UnresolvedCountTool.php index 0859b50..97c652b 100644 --- a/src/Mcp/Tool/Comment/UnresolvedCountTool.php +++ b/src/Mcp/Tool/Comment/UnresolvedCountTool.php @@ -8,6 +8,7 @@ use App\Entity\Comment; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_unresolved_comments_count', @@ -21,7 +22,7 @@ class UnresolvedCountTool private readonly EntityManagerInterface $em, ) {} - public function __invoke(): array + public function __invoke(): CallToolResult { $count = (int) $this->em->getRepository(Comment::class) ->createQueryBuilder('c') diff --git a/src/Mcp/Tool/Composant/CreateComposantTool.php b/src/Mcp/Tool/Composant/CreateComposantTool.php index adcd93e..290760c 100644 --- a/src/Mcp/Tool/Composant/CreateComposantTool.php +++ b/src/Mcp/Tool/Composant/CreateComposantTool.php @@ -10,6 +10,7 @@ use App\Repository\ConstructeurRepository; use App\Repository\ModelTypeRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -37,7 +38,7 @@ class CreateComposantTool string $prix = '', string $modelTypeId = '', array $constructeurIds = [], - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $composant = new Composant(); diff --git a/src/Mcp/Tool/Composant/DeleteComposantTool.php b/src/Mcp/Tool/Composant/DeleteComposantTool.php index dff0c06..08d1b9c 100644 --- a/src/Mcp/Tool/Composant/DeleteComposantTool.php +++ b/src/Mcp/Tool/Composant/DeleteComposantTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\ComposantRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class DeleteComposantTool private readonly Security $security, ) {} - public function __invoke(string $composantId): array + public function __invoke(string $composantId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Composant/GetComposantTool.php b/src/Mcp/Tool/Composant/GetComposantTool.php index ef74ba0..c3a6b94 100644 --- a/src/Mcp/Tool/Composant/GetComposantTool.php +++ b/src/Mcp/Tool/Composant/GetComposantTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Composant; use App\Mcp\Tool\McpToolHelper; use App\Repository\ComposantRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_composant', @@ -20,7 +21,7 @@ class GetComposantTool private readonly ComposantRepository $composants, ) {} - public function __invoke(string $composantId): array + public function __invoke(string $composantId): CallToolResult { $composant = $this->composants->find($composantId); diff --git a/src/Mcp/Tool/Composant/ListComposantsTool.php b/src/Mcp/Tool/Composant/ListComposantsTool.php index 3ab6051..3410ca0 100644 --- a/src/Mcp/Tool/Composant/ListComposantsTool.php +++ b/src/Mcp/Tool/Composant/ListComposantsTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Composant; use App\Mcp\Tool\McpToolHelper; use App\Repository\ComposantRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_composants', @@ -20,7 +21,7 @@ class ListComposantsTool private readonly ComposantRepository $composants, ) {} - public function __invoke(int $page = 1, int $limit = 30, string $search = ''): array + public function __invoke(int $page = 1, int $limit = 30, string $search = ''): CallToolResult { $p = $this->paginationParams($page, $limit); diff --git a/src/Mcp/Tool/Composant/UpdateComposantTool.php b/src/Mcp/Tool/Composant/UpdateComposantTool.php index e174441..c7c7951 100644 --- a/src/Mcp/Tool/Composant/UpdateComposantTool.php +++ b/src/Mcp/Tool/Composant/UpdateComposantTool.php @@ -10,6 +10,7 @@ use App\Repository\ConstructeurRepository; use App\Repository\ModelTypeRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -39,7 +40,7 @@ class UpdateComposantTool ?string $prix = null, ?string $modelTypeId = null, ?array $constructeurIds = null, - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $composant = $this->composants->find($composantId); diff --git a/src/Mcp/Tool/Constructeur/CreateConstructeurTool.php b/src/Mcp/Tool/Constructeur/CreateConstructeurTool.php index 78ad2e3..20de306 100644 --- a/src/Mcp/Tool/Constructeur/CreateConstructeurTool.php +++ b/src/Mcp/Tool/Constructeur/CreateConstructeurTool.php @@ -8,6 +8,7 @@ use App\Entity\Constructeur; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -27,7 +28,7 @@ class CreateConstructeurTool string $name, string $email = '', string $phone = '', - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $constructeur = new Constructeur(); diff --git a/src/Mcp/Tool/Constructeur/DeleteConstructeurTool.php b/src/Mcp/Tool/Constructeur/DeleteConstructeurTool.php index 2aa07ce..c33910d 100644 --- a/src/Mcp/Tool/Constructeur/DeleteConstructeurTool.php +++ b/src/Mcp/Tool/Constructeur/DeleteConstructeurTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\ConstructeurRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class DeleteConstructeurTool private readonly Security $security, ) {} - public function __invoke(string $constructeurId): array + public function __invoke(string $constructeurId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Constructeur/GetConstructeurTool.php b/src/Mcp/Tool/Constructeur/GetConstructeurTool.php index 69647f0..c59d43f 100644 --- a/src/Mcp/Tool/Constructeur/GetConstructeurTool.php +++ b/src/Mcp/Tool/Constructeur/GetConstructeurTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Constructeur; use App\Mcp\Tool\McpToolHelper; use App\Repository\ConstructeurRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_constructeur', @@ -20,7 +21,7 @@ class GetConstructeurTool private readonly ConstructeurRepository $constructeurs, ) {} - public function __invoke(string $constructeurId): array + public function __invoke(string $constructeurId): CallToolResult { $constructeur = $this->constructeurs->find($constructeurId); diff --git a/src/Mcp/Tool/Constructeur/ListConstructeursTool.php b/src/Mcp/Tool/Constructeur/ListConstructeursTool.php index 6ebe64d..26df639 100644 --- a/src/Mcp/Tool/Constructeur/ListConstructeursTool.php +++ b/src/Mcp/Tool/Constructeur/ListConstructeursTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Constructeur; use App\Mcp\Tool\McpToolHelper; use App\Repository\ConstructeurRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_constructeurs', @@ -20,7 +21,7 @@ class ListConstructeursTool private readonly ConstructeurRepository $constructeurs, ) {} - public function __invoke(int $page = 1, int $limit = 30, string $search = ''): array + public function __invoke(int $page = 1, int $limit = 30, string $search = ''): CallToolResult { $p = $this->paginationParams($page, $limit); diff --git a/src/Mcp/Tool/Constructeur/UpdateConstructeurTool.php b/src/Mcp/Tool/Constructeur/UpdateConstructeurTool.php index d136493..51efb18 100644 --- a/src/Mcp/Tool/Constructeur/UpdateConstructeurTool.php +++ b/src/Mcp/Tool/Constructeur/UpdateConstructeurTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\ConstructeurRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -29,7 +30,7 @@ class UpdateConstructeurTool ?string $name = null, ?string $email = null, ?string $phone = null, - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $constructeur = $this->constructeurs->find($constructeurId); diff --git a/src/Mcp/Tool/CustomField/DeleteCustomFieldValueTool.php b/src/Mcp/Tool/CustomField/DeleteCustomFieldValueTool.php index dc1b735..e5a2a9f 100644 --- a/src/Mcp/Tool/CustomField/DeleteCustomFieldValueTool.php +++ b/src/Mcp/Tool/CustomField/DeleteCustomFieldValueTool.php @@ -8,6 +8,7 @@ use App\Entity\CustomFieldValue; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -23,7 +24,7 @@ class DeleteCustomFieldValueTool private readonly Security $security, ) {} - public function __invoke(string $customFieldValueId): array + public function __invoke(string $customFieldValueId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/CustomField/ListCustomFieldValuesTool.php b/src/Mcp/Tool/CustomField/ListCustomFieldValuesTool.php index bd2b5b8..10441aa 100644 --- a/src/Mcp/Tool/CustomField/ListCustomFieldValuesTool.php +++ b/src/Mcp/Tool/CustomField/ListCustomFieldValuesTool.php @@ -8,6 +8,7 @@ use App\Entity\CustomFieldValue; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_custom_field_values', @@ -23,7 +24,7 @@ class ListCustomFieldValuesTool private readonly EntityManagerInterface $em, ) {} - public function __invoke(string $entityType, string $entityId): array + public function __invoke(string $entityType, string $entityId): CallToolResult { $entityType = strtolower($entityType); diff --git a/src/Mcp/Tool/CustomField/UpsertCustomFieldValuesTool.php b/src/Mcp/Tool/CustomField/UpsertCustomFieldValuesTool.php index 76d2016..f714310 100644 --- a/src/Mcp/Tool/CustomField/UpsertCustomFieldValuesTool.php +++ b/src/Mcp/Tool/CustomField/UpsertCustomFieldValuesTool.php @@ -13,6 +13,7 @@ use App\Entity\Product; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -30,7 +31,7 @@ class UpsertCustomFieldValuesTool private readonly Security $security, ) {} - public function __invoke(string $entityType, string $entityId, array $fields): array + public function __invoke(string $entityType, string $entityId, array $fields): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/DashboardStatsTool.php b/src/Mcp/Tool/DashboardStatsTool.php index f592fb8..41c214f 100644 --- a/src/Mcp/Tool/DashboardStatsTool.php +++ b/src/Mcp/Tool/DashboardStatsTool.php @@ -12,6 +12,7 @@ use App\Repository\SiteRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; use Mcp\Schema\Content\TextContent; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_dashboard_stats', @@ -28,14 +29,14 @@ class DashboardStatsTool private readonly EntityManagerInterface $em, ) {} - public function __invoke(): array + public function __invoke(): CallToolResult { $unresolvedComments = (int) $this->em->createQuery( "SELECT COUNT(c.id) FROM App\\Entity\\Comment c WHERE c.status = 'open'" )->getSingleScalarResult(); - return [ - new TextContent( + return new CallToolResult( + content: [new TextContent( text: json_encode([ 'machines' => $this->machines->count([]), 'pieces' => $this->pieces->count([]), @@ -44,7 +45,7 @@ class DashboardStatsTool 'sites' => $this->sites->count([]), 'unresolvedComments' => $unresolvedComments, ], JSON_THROW_ON_ERROR) - ), - ]; + )], + ); } } diff --git a/src/Mcp/Tool/Document/DeleteDocumentTool.php b/src/Mcp/Tool/Document/DeleteDocumentTool.php index 41de1c9..5d24161 100644 --- a/src/Mcp/Tool/Document/DeleteDocumentTool.php +++ b/src/Mcp/Tool/Document/DeleteDocumentTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\DocumentRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class DeleteDocumentTool private readonly Security $security, ) {} - public function __invoke(string $documentId): array + public function __invoke(string $documentId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Document/ListDocumentsTool.php b/src/Mcp/Tool/Document/ListDocumentsTool.php index b17364d..7acd638 100644 --- a/src/Mcp/Tool/Document/ListDocumentsTool.php +++ b/src/Mcp/Tool/Document/ListDocumentsTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Document; use App\Mcp\Tool\McpToolHelper; use App\Repository\DocumentRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_documents', @@ -28,7 +29,7 @@ class ListDocumentsTool private readonly DocumentRepository $documents, ) {} - public function __invoke(string $entityType, string $entityId): array + public function __invoke(string $entityType, string $entityId): CallToolResult { if (!isset(self::ENTITY_FIELDS[$entityType])) { $this->mcpError('validation', "Invalid entityType '{$entityType}'. Must be one of: site, machine, composant, piece, product."); diff --git a/src/Mcp/Tool/EntityHistoryTool.php b/src/Mcp/Tool/EntityHistoryTool.php index ba9cf46..35dcef8 100644 --- a/src/Mcp/Tool/EntityHistoryTool.php +++ b/src/Mcp/Tool/EntityHistoryTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool; use App\Repository\AuditLogRepository; use DateTimeInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class EntityHistoryTool private readonly Security $security, ) {} - public function __invoke(string $entityType, string $entityId): array + public function __invoke(string $entityType, string $entityId): CallToolResult { $this->requireRole($this->security, 'ROLE_VIEWER'); diff --git a/src/Mcp/Tool/Machine/AddMachineLinksTool.php b/src/Mcp/Tool/Machine/AddMachineLinksTool.php index f0b9f6b..9243214 100644 --- a/src/Mcp/Tool/Machine/AddMachineLinksTool.php +++ b/src/Mcp/Tool/Machine/AddMachineLinksTool.php @@ -16,6 +16,7 @@ use App\Repository\PieceRepository; use App\Repository\ProductRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -37,7 +38,7 @@ class AddMachineLinksTool private readonly MachinePieceLinkRepository $pieceLinks, ) {} - public function __invoke(string $machineId, array $links): array + public function __invoke(string $machineId, array $links): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Machine/CloneMachineTool.php b/src/Mcp/Tool/Machine/CloneMachineTool.php index fb09283..8533569 100644 --- a/src/Mcp/Tool/Machine/CloneMachineTool.php +++ b/src/Mcp/Tool/Machine/CloneMachineTool.php @@ -18,6 +18,7 @@ use App\Repository\MachineProductLinkRepository; use App\Repository\MachineRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -42,7 +43,7 @@ class CloneMachineTool string $name, string $siteId, string $reference = '', - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $source = $this->machineRepository->find($machineId); diff --git a/src/Mcp/Tool/Machine/CreateMachineTool.php b/src/Mcp/Tool/Machine/CreateMachineTool.php index fc31f42..93f3037 100644 --- a/src/Mcp/Tool/Machine/CreateMachineTool.php +++ b/src/Mcp/Tool/Machine/CreateMachineTool.php @@ -10,6 +10,7 @@ use App\Repository\ConstructeurRepository; use App\Repository\SiteRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -36,7 +37,7 @@ class CreateMachineTool string $reference = '', string $prix = '', array $constructeurIds = [], - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $site = $this->sites->find($siteId); diff --git a/src/Mcp/Tool/Machine/DeleteMachineTool.php b/src/Mcp/Tool/Machine/DeleteMachineTool.php index 61988d2..78e427a 100644 --- a/src/Mcp/Tool/Machine/DeleteMachineTool.php +++ b/src/Mcp/Tool/Machine/DeleteMachineTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\MachineRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class DeleteMachineTool private readonly Security $security, ) {} - public function __invoke(string $machineId): array + public function __invoke(string $machineId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Machine/GetMachineTool.php b/src/Mcp/Tool/Machine/GetMachineTool.php index ebea397..8dcd921 100644 --- a/src/Mcp/Tool/Machine/GetMachineTool.php +++ b/src/Mcp/Tool/Machine/GetMachineTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Machine; use App\Mcp\Tool\McpToolHelper; use App\Repository\MachineRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_machine', @@ -20,7 +21,7 @@ class GetMachineTool private readonly MachineRepository $machines, ) {} - public function __invoke(string $machineId): array + public function __invoke(string $machineId): CallToolResult { $machine = $this->machines->find($machineId); diff --git a/src/Mcp/Tool/Machine/ListMachineLinksTool.php b/src/Mcp/Tool/Machine/ListMachineLinksTool.php index 907fb5c..95a957f 100644 --- a/src/Mcp/Tool/Machine/ListMachineLinksTool.php +++ b/src/Mcp/Tool/Machine/ListMachineLinksTool.php @@ -10,6 +10,7 @@ use App\Repository\MachinePieceLinkRepository; use App\Repository\MachineProductLinkRepository; use App\Repository\MachineRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_machine_links', @@ -26,7 +27,7 @@ class ListMachineLinksTool private readonly MachineProductLinkRepository $productLinks, ) {} - public function __invoke(string $machineId): array + public function __invoke(string $machineId): CallToolResult { $machine = $this->machines->find($machineId); if (null === $machine) { diff --git a/src/Mcp/Tool/Machine/ListMachinesTool.php b/src/Mcp/Tool/Machine/ListMachinesTool.php index 9513f05..ab7c12f 100644 --- a/src/Mcp/Tool/Machine/ListMachinesTool.php +++ b/src/Mcp/Tool/Machine/ListMachinesTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Machine; use App\Mcp\Tool\McpToolHelper; use App\Repository\MachineRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_machines', @@ -20,7 +21,7 @@ class ListMachinesTool private readonly MachineRepository $machines, ) {} - public function __invoke(int $page = 1, int $limit = 30, string $search = ''): array + public function __invoke(int $page = 1, int $limit = 30, string $search = ''): CallToolResult { $p = $this->paginationParams($page, $limit); diff --git a/src/Mcp/Tool/Machine/MachineStructureTool.php b/src/Mcp/Tool/Machine/MachineStructureTool.php index ed44bd5..0cd0dbb 100644 --- a/src/Mcp/Tool/Machine/MachineStructureTool.php +++ b/src/Mcp/Tool/Machine/MachineStructureTool.php @@ -21,6 +21,7 @@ use App\Repository\MachineProductLinkRepository; use App\Repository\MachineRepository; use Doctrine\Common\Collections\Collection; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_machine_structure', @@ -37,7 +38,7 @@ class MachineStructureTool private readonly MachineProductLinkRepository $machineProductLinkRepository, ) {} - public function __invoke(string $machineId): array + public function __invoke(string $machineId): CallToolResult { $machine = $this->machineRepository->find($machineId); diff --git a/src/Mcp/Tool/Machine/RemoveMachineLinkTool.php b/src/Mcp/Tool/Machine/RemoveMachineLinkTool.php index c28c215..700957a 100644 --- a/src/Mcp/Tool/Machine/RemoveMachineLinkTool.php +++ b/src/Mcp/Tool/Machine/RemoveMachineLinkTool.php @@ -10,6 +10,7 @@ use App\Repository\MachinePieceLinkRepository; use App\Repository\MachineProductLinkRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -28,7 +29,7 @@ class RemoveMachineLinkTool private readonly MachineProductLinkRepository $productLinks, ) {} - public function __invoke(string $linkId, string $linkType): array + public function __invoke(string $linkId, string $linkType): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Machine/UpdateMachineLinkTool.php b/src/Mcp/Tool/Machine/UpdateMachineLinkTool.php index e447d37..3427fbd 100644 --- a/src/Mcp/Tool/Machine/UpdateMachineLinkTool.php +++ b/src/Mcp/Tool/Machine/UpdateMachineLinkTool.php @@ -11,6 +11,7 @@ use App\Repository\MachineComponentLinkRepository; use App\Repository\MachinePieceLinkRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -35,7 +36,7 @@ class UpdateMachineLinkTool ?string $referenceOverride = null, ?string $prixOverride = null, ?int $quantity = null, - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); switch ($linkType) { diff --git a/src/Mcp/Tool/Machine/UpdateMachineTool.php b/src/Mcp/Tool/Machine/UpdateMachineTool.php index 27e01fe..2e00aa5 100644 --- a/src/Mcp/Tool/Machine/UpdateMachineTool.php +++ b/src/Mcp/Tool/Machine/UpdateMachineTool.php @@ -10,6 +10,7 @@ use App\Repository\MachineRepository; use App\Repository\SiteRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -38,7 +39,7 @@ class UpdateMachineTool ?string $prix = null, ?string $siteId = null, ?array $constructeurIds = null, - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $machine = $this->machines->find($machineId); diff --git a/src/Mcp/Tool/McpToolHelper.php b/src/Mcp/Tool/McpToolHelper.php index 96c3afd..3cec1eb 100644 --- a/src/Mcp/Tool/McpToolHelper.php +++ b/src/Mcp/Tool/McpToolHelper.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Mcp\Tool; use Mcp\Schema\Content\TextContent; +use Mcp\Schema\Result\CallToolResult; use RuntimeException; use Symfony\Bundle\SecurityBundle\Security; @@ -17,12 +18,11 @@ trait McpToolHelper } } - /** - * @return array{TextContent} - */ - private function jsonResponse(array $data): array + private function jsonResponse(array $data): CallToolResult { - return [new TextContent(text: json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE))]; + return new CallToolResult( + content: [new TextContent(text: json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE))], + ); } private function mcpError(string $category, string $message): never @@ -42,10 +42,7 @@ trait McpToolHelper return ['page' => $page, 'limit' => $limit, 'offset' => $offset]; } - /** - * @return array{TextContent} - */ - private function paginatedResponse(array $items, int $total, int $page, int $limit): array + private function paginatedResponse(array $items, int $total, int $page, int $limit): CallToolResult { return $this->jsonResponse([ 'items' => $items, diff --git a/src/Mcp/Tool/ModelType/CreateModelTypeTool.php b/src/Mcp/Tool/ModelType/CreateModelTypeTool.php index 3c9c731..9a2a771 100644 --- a/src/Mcp/Tool/ModelType/CreateModelTypeTool.php +++ b/src/Mcp/Tool/ModelType/CreateModelTypeTool.php @@ -9,6 +9,7 @@ use App\Enum\ModelCategory; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class CreateModelTypeTool private readonly Security $security, ) {} - public function __invoke(string $name, string $category, string $code = ''): array + public function __invoke(string $name, string $category, string $code = ''): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/ModelType/DeleteModelTypeTool.php b/src/Mcp/Tool/ModelType/DeleteModelTypeTool.php index 7e384a0..d53bd0b 100644 --- a/src/Mcp/Tool/ModelType/DeleteModelTypeTool.php +++ b/src/Mcp/Tool/ModelType/DeleteModelTypeTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\ModelTypeRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class DeleteModelTypeTool private readonly Security $security, ) {} - public function __invoke(string $modelTypeId): array + public function __invoke(string $modelTypeId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/ModelType/GetModelTypeTool.php b/src/Mcp/Tool/ModelType/GetModelTypeTool.php index 989a9d2..fa27432 100644 --- a/src/Mcp/Tool/ModelType/GetModelTypeTool.php +++ b/src/Mcp/Tool/ModelType/GetModelTypeTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\ModelType; use App\Mcp\Tool\McpToolHelper; use App\Repository\ModelTypeRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_model_type', @@ -20,7 +21,7 @@ class GetModelTypeTool private readonly ModelTypeRepository $modelTypes, ) {} - public function __invoke(string $modelTypeId): array + public function __invoke(string $modelTypeId): CallToolResult { $mt = $this->modelTypes->find($modelTypeId); diff --git a/src/Mcp/Tool/ModelType/ListModelTypesTool.php b/src/Mcp/Tool/ModelType/ListModelTypesTool.php index 440bfe2..7c37bf6 100644 --- a/src/Mcp/Tool/ModelType/ListModelTypesTool.php +++ b/src/Mcp/Tool/ModelType/ListModelTypesTool.php @@ -8,6 +8,7 @@ use App\Enum\ModelCategory; use App\Mcp\Tool\McpToolHelper; use App\Repository\ModelTypeRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_model_types', @@ -21,7 +22,7 @@ class ListModelTypesTool private readonly ModelTypeRepository $modelTypes, ) {} - public function __invoke(int $page = 1, int $limit = 30, string $category = ''): array + public function __invoke(int $page = 1, int $limit = 30, string $category = ''): CallToolResult { $p = $this->paginationParams($page, $limit); diff --git a/src/Mcp/Tool/ModelType/SyncModelTypeTool.php b/src/Mcp/Tool/ModelType/SyncModelTypeTool.php index 3c58e12..d49c637 100644 --- a/src/Mcp/Tool/ModelType/SyncModelTypeTool.php +++ b/src/Mcp/Tool/ModelType/SyncModelTypeTool.php @@ -10,6 +10,7 @@ use App\Repository\ModelTypeRepository; use App\Service\ModelTypeSyncService; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -33,7 +34,7 @@ class SyncModelTypeTool ?array $structure = null, bool $confirmDeletions = false, bool $confirmTypeChanges = false, - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); if (!in_array($action, ['preview', 'sync'], true)) { diff --git a/src/Mcp/Tool/ModelType/UpdateModelTypeTool.php b/src/Mcp/Tool/ModelType/UpdateModelTypeTool.php index 4520dc3..d59036b 100644 --- a/src/Mcp/Tool/ModelType/UpdateModelTypeTool.php +++ b/src/Mcp/Tool/ModelType/UpdateModelTypeTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\ModelTypeRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class UpdateModelTypeTool private readonly Security $security, ) {} - public function __invoke(string $modelTypeId, ?string $name = null, ?string $code = null): array + public function __invoke(string $modelTypeId, ?string $name = null, ?string $code = null): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Piece/CreatePieceTool.php b/src/Mcp/Tool/Piece/CreatePieceTool.php index 7207d20..6286404 100644 --- a/src/Mcp/Tool/Piece/CreatePieceTool.php +++ b/src/Mcp/Tool/Piece/CreatePieceTool.php @@ -10,6 +10,7 @@ use App\Repository\ConstructeurRepository; use App\Repository\ModelTypeRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -37,7 +38,7 @@ class CreatePieceTool string $prix = '', string $modelTypeId = '', array $constructeurIds = [], - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $piece = new Piece(); diff --git a/src/Mcp/Tool/Piece/DeletePieceTool.php b/src/Mcp/Tool/Piece/DeletePieceTool.php index 384d5df..b354307 100644 --- a/src/Mcp/Tool/Piece/DeletePieceTool.php +++ b/src/Mcp/Tool/Piece/DeletePieceTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\PieceRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class DeletePieceTool private readonly Security $security, ) {} - public function __invoke(string $pieceId): array + public function __invoke(string $pieceId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Piece/GetPieceTool.php b/src/Mcp/Tool/Piece/GetPieceTool.php index be43163..5eab4ea 100644 --- a/src/Mcp/Tool/Piece/GetPieceTool.php +++ b/src/Mcp/Tool/Piece/GetPieceTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Piece; use App\Mcp\Tool\McpToolHelper; use App\Repository\PieceRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_piece', @@ -20,7 +21,7 @@ class GetPieceTool private readonly PieceRepository $pieces, ) {} - public function __invoke(string $pieceId): array + public function __invoke(string $pieceId): CallToolResult { $piece = $this->pieces->find($pieceId); diff --git a/src/Mcp/Tool/Piece/ListPiecesTool.php b/src/Mcp/Tool/Piece/ListPiecesTool.php index ffd913b..2fa1e33 100644 --- a/src/Mcp/Tool/Piece/ListPiecesTool.php +++ b/src/Mcp/Tool/Piece/ListPiecesTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Piece; use App\Mcp\Tool\McpToolHelper; use App\Repository\PieceRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_pieces', @@ -20,7 +21,7 @@ class ListPiecesTool private readonly PieceRepository $pieces, ) {} - public function __invoke(int $page = 1, int $limit = 30, string $search = ''): array + public function __invoke(int $page = 1, int $limit = 30, string $search = ''): CallToolResult { $p = $this->paginationParams($page, $limit); diff --git a/src/Mcp/Tool/Piece/UpdatePieceTool.php b/src/Mcp/Tool/Piece/UpdatePieceTool.php index 8d76175..19440cb 100644 --- a/src/Mcp/Tool/Piece/UpdatePieceTool.php +++ b/src/Mcp/Tool/Piece/UpdatePieceTool.php @@ -10,6 +10,7 @@ use App\Repository\ModelTypeRepository; use App\Repository\PieceRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -39,7 +40,7 @@ class UpdatePieceTool ?string $prix = null, ?string $modelTypeId = null, ?array $constructeurIds = null, - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $piece = $this->pieces->find($pieceId); diff --git a/src/Mcp/Tool/Product/CreateProductTool.php b/src/Mcp/Tool/Product/CreateProductTool.php index 22fbcdb..0d4c523 100644 --- a/src/Mcp/Tool/Product/CreateProductTool.php +++ b/src/Mcp/Tool/Product/CreateProductTool.php @@ -10,6 +10,7 @@ use App\Repository\ConstructeurRepository; use App\Repository\ModelTypeRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -36,7 +37,7 @@ class CreateProductTool string $supplierPrice = '', string $modelTypeId = '', array $constructeurIds = [], - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $product = new Product(); diff --git a/src/Mcp/Tool/Product/DeleteProductTool.php b/src/Mcp/Tool/Product/DeleteProductTool.php index 76395bc..7debf8b 100644 --- a/src/Mcp/Tool/Product/DeleteProductTool.php +++ b/src/Mcp/Tool/Product/DeleteProductTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\ProductRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class DeleteProductTool private readonly Security $security, ) {} - public function __invoke(string $productId): array + public function __invoke(string $productId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Product/GetProductTool.php b/src/Mcp/Tool/Product/GetProductTool.php index f3cf6ba..f003d0f 100644 --- a/src/Mcp/Tool/Product/GetProductTool.php +++ b/src/Mcp/Tool/Product/GetProductTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Product; use App\Mcp\Tool\McpToolHelper; use App\Repository\ProductRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_product', @@ -20,7 +21,7 @@ class GetProductTool private readonly ProductRepository $products, ) {} - public function __invoke(string $productId): array + public function __invoke(string $productId): CallToolResult { $product = $this->products->find($productId); diff --git a/src/Mcp/Tool/Product/ListProductsTool.php b/src/Mcp/Tool/Product/ListProductsTool.php index baf68ca..4a7e649 100644 --- a/src/Mcp/Tool/Product/ListProductsTool.php +++ b/src/Mcp/Tool/Product/ListProductsTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Product; use App\Mcp\Tool\McpToolHelper; use App\Repository\ProductRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_products', @@ -20,7 +21,7 @@ class ListProductsTool private readonly ProductRepository $products, ) {} - public function __invoke(int $page = 1, int $limit = 30, string $search = ''): array + public function __invoke(int $page = 1, int $limit = 30, string $search = ''): CallToolResult { $p = $this->paginationParams($page, $limit); diff --git a/src/Mcp/Tool/Product/UpdateProductTool.php b/src/Mcp/Tool/Product/UpdateProductTool.php index c3fc9b9..2e25777 100644 --- a/src/Mcp/Tool/Product/UpdateProductTool.php +++ b/src/Mcp/Tool/Product/UpdateProductTool.php @@ -10,6 +10,7 @@ use App\Repository\ModelTypeRepository; use App\Repository\ProductRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -38,7 +39,7 @@ class UpdateProductTool ?string $supplierPrice = null, ?string $modelTypeId = null, ?array $constructeurIds = null, - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $product = $this->products->find($productId); diff --git a/src/Mcp/Tool/SearchInventoryTool.php b/src/Mcp/Tool/SearchInventoryTool.php index f7db620..6126cd1 100644 --- a/src/Mcp/Tool/SearchInventoryTool.php +++ b/src/Mcp/Tool/SearchInventoryTool.php @@ -11,6 +11,7 @@ use App\Repository\PieceRepository; use App\Repository\ProductRepository; use App\Repository\SiteRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'search_inventory', @@ -31,7 +32,7 @@ class SearchInventoryTool private readonly ConstructeurRepository $constructeurs, ) {} - public function __invoke(string $query, string $types = '', int $limit = 20): array + public function __invoke(string $query, string $types = '', int $limit = 20): CallToolResult { $query = trim($query); if ('' === $query) { diff --git a/src/Mcp/Tool/Site/CreateSiteTool.php b/src/Mcp/Tool/Site/CreateSiteTool.php index 0272239..3f1ae7c 100644 --- a/src/Mcp/Tool/Site/CreateSiteTool.php +++ b/src/Mcp/Tool/Site/CreateSiteTool.php @@ -8,6 +8,7 @@ use App\Entity\Site; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -31,7 +32,7 @@ class CreateSiteTool string $contactPostalCode = '', string $contactCity = '', string $color = '', - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $site = new Site(); diff --git a/src/Mcp/Tool/Site/DeleteSiteTool.php b/src/Mcp/Tool/Site/DeleteSiteTool.php index c611684..c112a71 100644 --- a/src/Mcp/Tool/Site/DeleteSiteTool.php +++ b/src/Mcp/Tool/Site/DeleteSiteTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\SiteRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -24,7 +25,7 @@ class DeleteSiteTool private readonly Security $security, ) {} - public function __invoke(string $siteId): array + public function __invoke(string $siteId): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/src/Mcp/Tool/Site/GetSiteTool.php b/src/Mcp/Tool/Site/GetSiteTool.php index 233a354..d185572 100644 --- a/src/Mcp/Tool/Site/GetSiteTool.php +++ b/src/Mcp/Tool/Site/GetSiteTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Site; use App\Mcp\Tool\McpToolHelper; use App\Repository\SiteRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'get_site', @@ -20,7 +21,7 @@ class GetSiteTool private readonly SiteRepository $sites, ) {} - public function __invoke(string $siteId): array + public function __invoke(string $siteId): CallToolResult { $site = $this->sites->find($siteId); diff --git a/src/Mcp/Tool/Site/ListSitesTool.php b/src/Mcp/Tool/Site/ListSitesTool.php index f99367f..35fc28c 100644 --- a/src/Mcp/Tool/Site/ListSitesTool.php +++ b/src/Mcp/Tool/Site/ListSitesTool.php @@ -7,6 +7,7 @@ namespace App\Mcp\Tool\Site; use App\Mcp\Tool\McpToolHelper; use App\Repository\SiteRepository; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_sites', @@ -20,7 +21,7 @@ class ListSitesTool private readonly SiteRepository $sites, ) {} - public function __invoke(int $page = 1, int $limit = 30, string $search = ''): array + public function __invoke(int $page = 1, int $limit = 30, string $search = ''): CallToolResult { $p = $this->paginationParams($page, $limit); diff --git a/src/Mcp/Tool/Site/UpdateSiteTool.php b/src/Mcp/Tool/Site/UpdateSiteTool.php index 010881d..d16f419 100644 --- a/src/Mcp/Tool/Site/UpdateSiteTool.php +++ b/src/Mcp/Tool/Site/UpdateSiteTool.php @@ -8,6 +8,7 @@ use App\Mcp\Tool\McpToolHelper; use App\Repository\SiteRepository; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -33,7 +34,7 @@ class UpdateSiteTool ?string $contactPostalCode = null, ?string $contactCity = null, ?string $color = null, - ): array { + ): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); $site = $this->sites->find($siteId); diff --git a/src/Mcp/Tool/Slot/ListSlotsTool.php b/src/Mcp/Tool/Slot/ListSlotsTool.php index 3fa8078..4df598d 100644 --- a/src/Mcp/Tool/Slot/ListSlotsTool.php +++ b/src/Mcp/Tool/Slot/ListSlotsTool.php @@ -11,6 +11,7 @@ use App\Entity\PieceProductSlot; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; #[McpTool( name: 'list_slots', @@ -24,7 +25,7 @@ class ListSlotsTool private readonly EntityManagerInterface $em, ) {} - public function __invoke(string $entityType, string $entityId): array + public function __invoke(string $entityType, string $entityId): CallToolResult { if ('composant' === $entityType) { return $this->listComposantSlots($entityId); @@ -37,7 +38,7 @@ class ListSlotsTool $this->mcpError('validation', "entityType must be 'composant' or 'piece', got '{$entityType}'."); } - private function listComposantSlots(string $composantId): array + private function listComposantSlots(string $composantId): CallToolResult { $pieceSlots = $this->em->createQueryBuilder() ->select( @@ -108,7 +109,7 @@ class ListSlotsTool ]); } - private function listPieceSlots(string $pieceId): array + private function listPieceSlots(string $pieceId): CallToolResult { $slots = $this->em->createQueryBuilder() ->select( diff --git a/src/Mcp/Tool/Slot/UpdateSlotsTool.php b/src/Mcp/Tool/Slot/UpdateSlotsTool.php index 5912662..72044ec 100644 --- a/src/Mcp/Tool/Slot/UpdateSlotsTool.php +++ b/src/Mcp/Tool/Slot/UpdateSlotsTool.php @@ -14,6 +14,7 @@ use App\Entity\Product; use App\Mcp\Tool\McpToolHelper; use Doctrine\ORM\EntityManagerInterface; use Mcp\Capability\Attribute\McpTool; +use Mcp\Schema\Result\CallToolResult; use Symfony\Bundle\SecurityBundle\Security; #[McpTool( @@ -29,7 +30,7 @@ class UpdateSlotsTool private readonly Security $security, ) {} - public function __invoke(array $slots): array + public function __invoke(array $slots): CallToolResult { $this->requireRole($this->security, 'ROLE_GESTIONNAIRE'); diff --git a/tests/AbstractApiTestCase.php b/tests/AbstractApiTestCase.php index 6e66eb4..6fddab5 100644 --- a/tests/AbstractApiTestCase.php +++ b/tests/AbstractApiTestCase.php @@ -108,6 +108,7 @@ abstract class AbstractApiTestCase extends ApiTestCase $response = $client->request('POST', '/_mcp', [ 'headers' => [ 'Content-Type' => 'application/json', + 'Accept' => 'application/json, text/event-stream', 'X-Profile-Id' => $profileId, 'X-Profile-Password' => $password, ], @@ -128,6 +129,7 @@ abstract class AbstractApiTestCase extends ApiTestCase $client->request('POST', '/_mcp', [ 'headers' => [ 'Content-Type' => 'application/json', + 'Accept' => 'application/json, text/event-stream', 'X-Profile-Id' => $profileId, 'X-Profile-Password' => $password, 'Mcp-Session-Id' => $sessionId, @@ -149,6 +151,7 @@ abstract class AbstractApiTestCase extends ApiTestCase $response = $session['client']->request('POST', '/_mcp', [ 'headers' => [ 'Content-Type' => 'application/json', + 'Accept' => 'application/json, text/event-stream', 'X-Profile-Id' => $session['profileId'], 'X-Profile-Password' => $session['password'], 'Mcp-Session-Id' => $session['sessionId'], @@ -164,7 +167,24 @@ abstract class AbstractApiTestCase extends ApiTestCase ]), ]); - $data = $response->toArray(false); + $raw = $response->getContent(false); + $data = json_decode($raw, true); + + if (null === $data) { + // SSE format: parse "data: {...}" lines + foreach (explode("\n", $raw) as $line) { + if (str_starts_with($line, 'data: ')) { + $parsed = json_decode(substr($line, 6), true); + if ($parsed && (isset($parsed['result']) || isset($parsed['error']))) { + $data = $parsed; + + break; + } + } + } + } + + $data ??= []; if (isset($data['result']['content'][0]['text'])) { $data['_parsed'] = json_decode($data['result']['content'][0]['text'], true);