feat : add 22 MCP tool classes for projects, tasks, and time tracking
Tools: list-users, list-clients, list/get/create/update-project, list/get/create/update/delete-task, list-statuses/priorities/efforts/tags, list/create/update-group, list/create/update/delete-time-entry. Attribute moved to class level for SDK discovery compatibility. Install nyholm/psr7 for HTTP transport PSR-17 support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
88
src/Mcp/Tool/TimeEntry/ListTimeEntriesTool.php
Normal file
88
src/Mcp/Tool/TimeEntry/ListTimeEntriesTool.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Mcp\Tool\TimeEntry;
|
||||
|
||||
use App\Repository\TimeEntryRepository;
|
||||
use DateTimeImmutable;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
#[McpTool(name: 'list-time-entries', description: 'List time entries with optional filters. Duration is computed in minutes and null for active timers.')]
|
||||
class ListTimeEntriesTool
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TimeEntryRepository $timeEntryRepository,
|
||||
) {}
|
||||
|
||||
public function __invoke(
|
||||
?int $userId = null,
|
||||
?int $projectId = null,
|
||||
?int $taskId = null,
|
||||
?string $startDate = null,
|
||||
?string $endDate = null,
|
||||
int $limit = 100,
|
||||
): string {
|
||||
$limit = min($limit, 200);
|
||||
|
||||
$qb = $this->timeEntryRepository->createQueryBuilder('te')
|
||||
->leftJoin('te.user', 'u')->addSelect('u')
|
||||
->leftJoin('te.project', 'p')->addSelect('p')
|
||||
->leftJoin('te.task', 't')->addSelect('t')
|
||||
->leftJoin('te.tags', 'tg')->addSelect('tg')
|
||||
->orderBy('te.startedAt', 'DESC')
|
||||
->setMaxResults($limit)
|
||||
;
|
||||
|
||||
if (null !== $userId) {
|
||||
$qb->andWhere('u.id = :userId')->setParameter('userId', $userId);
|
||||
}
|
||||
if (null !== $projectId) {
|
||||
$qb->andWhere('p.id = :projectId')->setParameter('projectId', $projectId);
|
||||
}
|
||||
if (null !== $taskId) {
|
||||
$qb->andWhere('t.id = :taskId')->setParameter('taskId', $taskId);
|
||||
}
|
||||
if (null !== $startDate) {
|
||||
$qb->andWhere('te.startedAt >= :startDate')
|
||||
->setParameter('startDate', new DateTimeImmutable($startDate.' 00:00:00'))
|
||||
;
|
||||
}
|
||||
if (null !== $endDate) {
|
||||
$qb->andWhere('te.startedAt <= :endDate')
|
||||
->setParameter('endDate', new DateTimeImmutable($endDate.' 23:59:59'))
|
||||
;
|
||||
}
|
||||
|
||||
$entries = $qb->getQuery()->getResult();
|
||||
|
||||
return json_encode(array_map(fn ($entry) => [
|
||||
'id' => $entry->getId(),
|
||||
'title' => $entry->getTitle(),
|
||||
'description' => $entry->getDescription(),
|
||||
'startedAt' => $entry->getStartedAt()?->format('c'),
|
||||
'stoppedAt' => $entry->getStoppedAt()?->format('c'),
|
||||
'duration' => $entry->getStoppedAt() && $entry->getStartedAt()
|
||||
? (int) round(($entry->getStoppedAt()->getTimestamp() - $entry->getStartedAt()->getTimestamp()) / 60)
|
||||
: null,
|
||||
'user' => [
|
||||
'id' => $entry->getUser()->getId(),
|
||||
'username' => $entry->getUser()->getUsername(),
|
||||
],
|
||||
'project' => $entry->getProject() ? [
|
||||
'id' => $entry->getProject()->getId(),
|
||||
'code' => $entry->getProject()->getCode(),
|
||||
'name' => $entry->getProject()->getName(),
|
||||
] : null,
|
||||
'task' => $entry->getTask() ? [
|
||||
'id' => $entry->getTask()->getId(),
|
||||
'number' => $entry->getTask()->getNumber(),
|
||||
'title' => $entry->getTask()->getTitle(),
|
||||
] : null,
|
||||
'tags' => $entry->getTags()->map(fn ($t) => [
|
||||
'id' => $t->getId(),
|
||||
'label' => $t->getLabel(),
|
||||
])->toArray(),
|
||||
], $entries));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user