diff --git a/config/services.yaml b/config/services.yaml index b0f5649..497cda6 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -45,6 +45,10 @@ services: arguments: $uploadDir: '%task_document_upload_dir%' + App\Mcp\Tool\Task\AddTaskDocumentTool: + arguments: + $uploadDir: '%task_document_upload_dir%' + App\Controller\UserAvatarController: arguments: $avatarUploadDir: '%avatar_upload_dir%' diff --git a/src/Mcp/Tool/Task/AddTaskDocumentTool.php b/src/Mcp/Tool/Task/AddTaskDocumentTool.php new file mode 100644 index 0000000..e879d3b --- /dev/null +++ b/src/Mcp/Tool/Task/AddTaskDocumentTool.php @@ -0,0 +1,110 @@ + 'text/markdown', + 'markdown' => 'text/markdown', + 'txt' => 'text/plain', + 'csv' => 'text/csv', + 'json' => 'application/json', + 'xml' => 'text/xml', + ]; + + public function __construct( + private readonly EntityManagerInterface $entityManager, + private readonly TaskRepository $taskRepository, + private readonly Security $security, + private readonly string $uploadDir, + ) {} + + /** + * @param int $taskId ID of the task to attach the document to + * @param string $content Raw text content of the document (e.g. Markdown) + * @param string $fileName Display name of the document, including extension (defaults to "document.md") + */ + public function __invoke( + int $taskId, + string $content, + string $fileName = 'document.md', + ): string { + if (!$this->security->isGranted('ROLE_USER')) { + throw new AccessDeniedException('Access denied: ROLE_USER required.'); + } + + $task = $this->taskRepository->find($taskId); + if (null === $task) { + throw new InvalidArgumentException(sprintf('Task with ID %d not found.', $taskId)); + } + + if ('' === $content) { + throw new InvalidArgumentException('Document content cannot be empty.'); + } + + $size = strlen($content); + if ($size > self::MAX_CONTENT_SIZE) { + throw new InvalidArgumentException('Content size exceeds 5 MB limit.'); + } + + $originalName = '' !== trim($fileName) ? trim($fileName) : 'document.md'; + + $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); + $mimeType = self::EXTENSION_TO_MIME[$extension] ?? 'text/markdown'; + if ('' === $extension) { + $originalName .= '.md'; + $extension = 'md'; + } + + $storedName = Uuid::v4()->toRfc4122().'.'.$extension; + + if (!is_dir($this->uploadDir) && !mkdir($this->uploadDir, 0o775, true) && !is_dir($this->uploadDir)) { + throw new InvalidArgumentException(sprintf('Upload directory "%s" could not be created.', $this->uploadDir)); + } + + if (false === file_put_contents($this->uploadDir.'/'.$storedName, $content)) { + throw new InvalidArgumentException('Failed to write document to disk.'); + } + + $document = new TaskDocument(); + $document->setTask($task); + $document->setOriginalName($originalName); + $document->setFileName($storedName); + $document->setMimeType($mimeType); + $document->setSize($size); + $document->setCreatedAt(new DateTimeImmutable()); + $document->setUploadedBy($this->security->getUser()); + + $this->entityManager->persist($document); + $this->entityManager->flush(); + + return json_encode([ + 'id' => $document->getId(), + 'taskId' => $task->getId(), + 'originalName' => $document->getOriginalName(), + 'mimeType' => $document->getMimeType(), + 'size' => $document->getSize(), + 'createdAt' => $document->getCreatedAt()?->format('c'), + 'uploadedBy' => $document->getUploadedBy()?->getUsername(), + ]); + } +}