OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
73
app/Domain/Tickets/Tools/AddMilestoneTool.php
Normal file
73
app/Domain/Tickets/Tools/AddMilestoneTool.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Add a new milestone to a project.
|
||||
*/
|
||||
class AddMilestoneTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'addMilestone';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Adds a new milestone to the project. Milestones are used as hierarchical element to group tasks.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->string('headline')->description('Title of the milestone.')->required()
|
||||
->string('color')->description('Choose a color for this milestone using a hex code.')->required()
|
||||
->string('editFrom')->description('Start date of the milestone in ISO8601 format (example: 2024-04-30T15:00:00-04:00).')->required()
|
||||
->string('editTo')->description('End date of the milestone in ISO8601 format (example: 2024-04-30T15:00:00-04:00).')->required()
|
||||
->integer('projectId')->description('Project ID.')->required()
|
||||
->integer('editorId')->description('Editor ID.')->required()
|
||||
->integer('dependentMilestone')->description('Dependent milestone ID.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$params = [
|
||||
'headline' => $arguments['headline'],
|
||||
'projectId' => (int) ($arguments['projectId'] ?? 0),
|
||||
'editorId' => (int) ($arguments['editorId'] ?? 0),
|
||||
'dependentMilestone' => ($arguments['dependentMilestone'] ?? null),
|
||||
'tags' => $arguments['color'],
|
||||
'editFrom' => $arguments['editFrom'],
|
||||
'editTo' => $arguments['editTo'],
|
||||
];
|
||||
|
||||
$result = $this->ticketsService->quickAddMilestone($params);
|
||||
|
||||
if ($result) {
|
||||
return ToolResult::text("Milestone created successfully with ID: {$result}");
|
||||
}
|
||||
|
||||
return ToolResult::error('Failed to create milestone.');
|
||||
}
|
||||
}
|
||||
116
app/Domain/Tickets/Tools/AddMilestonesForProjectTool.php
Normal file
116
app/Domain/Tickets/Tools/AddMilestonesForProjectTool.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Create multiple milestones for a project in a single operation.
|
||||
*/
|
||||
class AddMilestonesForProjectTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'addMilestonesForProject';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Creates multiple milestones for a project in a single operation. This is the primary tool for project setup and should be used instead of multiple addMilestone calls. Each milestone should include headline, color, editFrom, editTo, and optionally dependentMilestone. Much more efficient than individual milestone creation for project planning phases.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('projectId')->description('Project ID where milestones will be created.')->required()
|
||||
->integer('editorId')->description('User ID who will be the editor/creator of these milestones.')->required()
|
||||
->raw('milestones', ['type' => 'array', 'description' => 'Array of milestone definitions. Each element should contain: headline, color (hex), editFrom (ISO8601), editTo (ISO8601), and optionally dependentMilestone (ID). Example: [{"headline": "Phase 1", "color": "#FF0000", "editFrom": "2024-01-01T09:00:00Z", "editTo": "2024-01-31T17:00:00Z"}]'])->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$projectId = (int) ($arguments['projectId'] ?? 0);
|
||||
$editorId = (int) ($arguments['editorId'] ?? 0);
|
||||
$milestones = ($arguments['milestones'] ?? []);
|
||||
|
||||
$results = [];
|
||||
$successCount = 0;
|
||||
$failureCount = 0;
|
||||
|
||||
foreach ($milestones as $milestoneData) {
|
||||
try {
|
||||
if (! isset($milestoneData['headline']) || ! isset($milestoneData['color']) ||
|
||||
! isset($milestoneData['editFrom']) || ! isset($milestoneData['editTo'])) {
|
||||
$failureCount++;
|
||||
$results[] = [
|
||||
'headline' => $milestoneData['headline'] ?? 'Unknown',
|
||||
'status' => 'error',
|
||||
'message' => 'Missing required fields (headline, color, editFrom, editTo)',
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$params = [
|
||||
'headline' => $milestoneData['headline'],
|
||||
'projectId' => $projectId,
|
||||
'editorId' => $editorId,
|
||||
'dependentMilestone' => $milestoneData['dependentMilestone'] ?? null,
|
||||
'tags' => $milestoneData['color'],
|
||||
'editFrom' => $milestoneData['editFrom'],
|
||||
'editTo' => $milestoneData['editTo'],
|
||||
];
|
||||
|
||||
$result = $this->ticketsService->quickAddMilestone($params);
|
||||
|
||||
if ($result) {
|
||||
$successCount++;
|
||||
$results[] = [
|
||||
'headline' => $milestoneData['headline'],
|
||||
'status' => 'success',
|
||||
'id' => $result,
|
||||
];
|
||||
} else {
|
||||
$failureCount++;
|
||||
$results[] = [
|
||||
'headline' => $milestoneData['headline'],
|
||||
'status' => 'error',
|
||||
'message' => 'Failed to create milestone',
|
||||
];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$failureCount++;
|
||||
$results[] = [
|
||||
'headline' => $milestoneData['headline'] ?? 'Unknown',
|
||||
'status' => 'error',
|
||||
'message' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return ToolResult::text(
|
||||
"Milestone creation completed. Success: {$successCount}, Failed: {$failureCount}\n\n".
|
||||
Str::toMarkdown($results)
|
||||
);
|
||||
}
|
||||
}
|
||||
88
app/Domain/Tickets/Tools/AddSubtaskTool.php
Normal file
88
app/Domain/Tickets/Tools/AddSubtaskTool.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Create a subtask for an existing task.
|
||||
*/
|
||||
class AddSubtaskTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'addSubtask';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Creates a new subtask of another existing task. Tool to break down large tasks into smaller elements.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('parentTicket')->description('ID of the parent ticket.')->required()
|
||||
->string('headline')->description('Title of the subtask.')->required()
|
||||
->string('description')->description('Subtask description.')
|
||||
->integer('projectId')->description('Project ID.')
|
||||
->integer('editorId')->description('Assigned user ID.')
|
||||
->integer('userId')->description('Creator user ID.')
|
||||
->string('dateToFinish')->description('Due date in ISO8601 format.')
|
||||
->integer('status')->description('Status ID.')
|
||||
->string('editFrom')->description('Scheduled start date in ISO8601 format.')
|
||||
->string('editTo')->description('Scheduled end date in ISO8601 format.')
|
||||
->integer('effort')->description('Effort T-shirt size: 1=XS, 2=S, 3=M, 5=L, 8=XL, 13=XXL.')
|
||||
->integer('planHours')->description('Planned hours for this subtask.')
|
||||
->integer('priority')->description('Priority: 1=Critical, 2=High, 3=Medium, 4=Low, 5=Lowest.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$params = [
|
||||
'headline' => $arguments['headline'],
|
||||
'description' => ($arguments['description'] ?? ''),
|
||||
'projectId' => ($arguments['projectId'] ?? null),
|
||||
'editorId' => ($arguments['editorId'] ?? null),
|
||||
'userId' => ($arguments['userId'] ?? null),
|
||||
'dateToFinish' => ($arguments['dateToFinish'] ?? null),
|
||||
'status' => (int) ($arguments['status'] ?? 3),
|
||||
'sprint' => null,
|
||||
'editFrom' => ($arguments['editFrom'] ?? null),
|
||||
'editTo' => ($arguments['editTo'] ?? null),
|
||||
'milestone' => null,
|
||||
'type' => 'subtask',
|
||||
'dependingTicketId' => (int) ($arguments['parentTicket'] ?? 0),
|
||||
'storypoints' => (int) ($arguments['effort'] ?? 3),
|
||||
'priority' => (int) ($arguments['priority'] ?? 3),
|
||||
'planHours' => (int) ($arguments['planHours'] ?? 0),
|
||||
];
|
||||
|
||||
$result = $this->ticketsService->quickAddTicket($params);
|
||||
|
||||
if ($result) {
|
||||
return ToolResult::text("Subtask created successfully. ID: {$result}");
|
||||
}
|
||||
|
||||
return ToolResult::error('Failed to create subtask.');
|
||||
}
|
||||
}
|
||||
82
app/Domain/Tickets/Tools/AddTaskTool.php
Normal file
82
app/Domain/Tickets/Tools/AddTaskTool.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Create a new task.
|
||||
*/
|
||||
class AddTaskTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'addTask';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Adds a new task quickly based on the provided parameters.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->string('headline')->description('Title of the task.')->required()
|
||||
->string('description')->description('Task description.')
|
||||
->integer('projectId')->description('Project ID.')
|
||||
->integer('editorId')->description('Assigned user ID.')
|
||||
->integer('userId')->description('Creator user ID.')
|
||||
->string('dateToFinish')->description('Due date in ISO8601 format.')
|
||||
->integer('status')->description('Status ID.')
|
||||
->integer('sprint')->description('Sprint ID.')
|
||||
->string('editFrom')->description('Scheduled start date in ISO8601 format.')
|
||||
->string('editTo')->description('Scheduled end date in ISO8601 format.')
|
||||
->integer('milestone')->description('Milestone ID.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$params = [
|
||||
'headline' => $arguments['headline'],
|
||||
'description' => ($arguments['description'] ?? ''),
|
||||
'projectId' => ($arguments['projectId'] ?? null),
|
||||
'editorId' => ($arguments['editorId'] ?? null),
|
||||
'userId' => ($arguments['userId'] ?? null),
|
||||
'dateToFinish' => ($arguments['dateToFinish'] ?? null),
|
||||
'status' => (int) ($arguments['status'] ?? 3),
|
||||
'sprint' => ($arguments['sprint'] ?? null),
|
||||
'editFrom' => ($arguments['editFrom'] ?? null),
|
||||
'editTo' => ($arguments['editTo'] ?? null),
|
||||
'milestone' => ($arguments['milestone'] ?? null),
|
||||
'type' => 'task',
|
||||
];
|
||||
|
||||
$result = $this->ticketsService->quickAddTicket($params);
|
||||
|
||||
if ($result) {
|
||||
return ToolResult::text("Task created successfully. ID: {$result}");
|
||||
}
|
||||
|
||||
return ToolResult::error('Failed to create task.');
|
||||
}
|
||||
}
|
||||
92
app/Domain/Tickets/Tools/BulkAddTasksTool.php
Normal file
92
app/Domain/Tickets/Tools/BulkAddTasksTool.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Create multiple tasks in a single operation.
|
||||
*/
|
||||
class BulkAddTasksTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'bulkAddTasks';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Adds multiple task in a single operation. Expects an array of task data where each element contains the same fields as addTicket. Required fields for each ticket: headline, projectId. Optional fields: description, editorId, userId, dateToFinish, status, sprint, editFrom, editTo, milestone.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->raw('tasks', ['type' => 'array', 'description' => 'Array of task objects. Each must contain headline and projectId.'])->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$tasks = ($arguments['tasks'] ?? []);
|
||||
$results = [];
|
||||
$successCount = 0;
|
||||
$failureCount = 0;
|
||||
|
||||
foreach ($tasks as $taskData) {
|
||||
try {
|
||||
$params = [
|
||||
'headline' => $taskData['headline'] ?? '',
|
||||
'description' => $taskData['description'] ?? '',
|
||||
'projectId' => $taskData['projectId'] ?? null,
|
||||
'editorId' => $taskData['editorId'] ?? null,
|
||||
'userId' => $taskData['userId'] ?? null,
|
||||
'dateToFinish' => $taskData['dateToFinish'] ?? null,
|
||||
'status' => $taskData['status'] ?? 3,
|
||||
'sprint' => $taskData['sprint'] ?? null,
|
||||
'editFrom' => $taskData['editFrom'] ?? null,
|
||||
'editTo' => $taskData['editTo'] ?? null,
|
||||
'milestone' => $taskData['milestone'] ?? null,
|
||||
'type' => 'task',
|
||||
];
|
||||
|
||||
$result = $this->ticketsService->quickAddTicket($params);
|
||||
|
||||
if ($result) {
|
||||
$successCount++;
|
||||
$results[] = ['headline' => $taskData['headline'], 'status' => 'success', 'id' => $result];
|
||||
} else {
|
||||
$failureCount++;
|
||||
$results[] = ['headline' => $taskData['headline'], 'status' => 'error', 'message' => 'Failed to create task'];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$failureCount++;
|
||||
$results[] = ['headline' => $taskData['headline'] ?? 'Unknown', 'status' => 'error', 'message' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
return ToolResult::text(
|
||||
"Bulk task creation completed. Success: {$successCount}, Failed: {$failureCount}\n\n".
|
||||
Str::toMarkdown($results)
|
||||
);
|
||||
}
|
||||
}
|
||||
80
app/Domain/Tickets/Tools/BulkEditTasksTool.php
Normal file
80
app/Domain/Tickets/Tools/BulkEditTasksTool.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Update multiple tasks in a single operation.
|
||||
*/
|
||||
class BulkEditTasksTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'bulkEditTasks';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Updates multiple tasks in a single operation. Expects an array where each element contains ticketId and the fields to update. Field names that can be updated are: headline, description, projectId, editorId, userId, dateToFinish, status, editFrom, editTo, milestoneId';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->raw('updates', ['type' => 'array', 'description' => 'Array of update objects. Each must have id and the fields to update.'])->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$updates = ($arguments['updates'] ?? []);
|
||||
$results = [];
|
||||
$successCount = 0;
|
||||
$failureCount = 0;
|
||||
|
||||
foreach ($updates as $update) {
|
||||
if (! isset($update['id'])) {
|
||||
$failureCount++;
|
||||
$results[] = ['status' => 'error', 'message' => 'Missing task ID'];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$id = $update['id'];
|
||||
unset($update['id']);
|
||||
|
||||
if ($this->ticketsService->patch($id, $update)) {
|
||||
$successCount++;
|
||||
$results[] = ['id' => $id, 'status' => 'success'];
|
||||
} else {
|
||||
$failureCount++;
|
||||
$results[] = ['id' => $id, 'status' => 'error', 'message' => 'Failed to update task'];
|
||||
}
|
||||
}
|
||||
|
||||
return ToolResult::text(
|
||||
"Bulk task update completed. Success: {$successCount}, Failed: {$failureCount}\n\n".
|
||||
Str::toMarkdown($results)
|
||||
);
|
||||
}
|
||||
}
|
||||
104
app/Domain/Tickets/Tools/BulkScheduleTasksTool.php
Normal file
104
app/Domain/Tickets/Tools/BulkScheduleTasksTool.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Schedule multiple tasks by setting editFrom and editTo dates.
|
||||
*/
|
||||
class BulkScheduleTasksTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'bulkScheduleTasks';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Schedules multiple tasks by setting editFrom and editTo dates in a single operation. This allows timeboxing multiple tasks efficiently. All tasks must have at least 15 minutes duration. Expects an array where each element contains taskId, editFrom, and editTo.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->raw('schedules', ['type' => 'array', 'description' => 'Array of schedule objects. Each must have taskId, editFrom (ISO8601), and editTo (ISO8601).'])->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$schedules = ($arguments['schedules'] ?? []);
|
||||
$results = [];
|
||||
$successCount = 0;
|
||||
$failureCount = 0;
|
||||
$validationErrors = [];
|
||||
|
||||
foreach ($schedules as $index => $schedule) {
|
||||
if (! isset($schedule['taskId']) || ! isset($schedule['editFrom']) || ! isset($schedule['editTo'])) {
|
||||
$validationErrors[] = "Schedule #{$index} is missing required fields (taskId, editFrom, editTo)";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$editFrom = new \DateTime($schedule['editFrom']);
|
||||
$editTo = new \DateTime($schedule['editTo']);
|
||||
} catch (\Exception $e) {
|
||||
$validationErrors[] = "Schedule #{$index} has invalid date format. Use ISO8601 (e.g. 2024-04-30T15:00:00-04:00)";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$duration = $editTo->getTimestamp() - $editFrom->getTimestamp();
|
||||
|
||||
if ($duration < 900) {
|
||||
$validationErrors[] = "Schedule #{$index} is shorter than the minimum 15 minute duration";
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($validationErrors)) {
|
||||
return ToolResult::error("Validation failed:\n- ".implode("\n- ", $validationErrors));
|
||||
}
|
||||
|
||||
foreach ($schedules as $schedule) {
|
||||
$taskId = $schedule['taskId'];
|
||||
$params = [
|
||||
'editFrom' => $schedule['editFrom'],
|
||||
'editTo' => $schedule['editTo'],
|
||||
];
|
||||
|
||||
if ($this->ticketsService->patch($taskId, $params)) {
|
||||
$successCount++;
|
||||
$results[] = ['taskId' => $taskId, 'status' => 'success'];
|
||||
} else {
|
||||
$failureCount++;
|
||||
$results[] = ['taskId' => $taskId, 'status' => 'error', 'message' => 'Failed to schedule task'];
|
||||
}
|
||||
}
|
||||
|
||||
return ToolResult::text(
|
||||
"Bulk task scheduling completed. Success: {$successCount}, Failed: {$failureCount}\n\n".
|
||||
Str::toMarkdown($results)
|
||||
);
|
||||
}
|
||||
}
|
||||
105
app/Domain/Tickets/Tools/CreateSubtasksForTaskTool.php
Normal file
105
app/Domain/Tickets/Tools/CreateSubtasksForTaskTool.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Create multiple subtasks for a parent task.
|
||||
*/
|
||||
class CreateSubtasksForTaskTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'createSubtasksForTask';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Creates multiple subtasks for a parent task in a single operation. This is useful for breaking down a large task into smaller components. Optionally schedules these subtasks with editFrom/editTo dates. Expects a parent task ID and an array of subtask definitions.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('parentTaskId')->description('ID of the parent task.')->required()
|
||||
->raw('subtasks', ['type' => 'array', 'description' => 'Array of subtask objects. Each needs headline. Optional: description, userId, editFrom, editTo, dateToFinish (all ISO8601), priority (1=Critical to 5=Lowest), planHours, effort (1=XS to 13=XXL).'])->required()
|
||||
->integer('projectId')->description('Project ID (defaults to parent task\'s project).');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$parentTaskId = (int) ($arguments['parentTaskId'] ?? 0);
|
||||
$subtasks = ($arguments['subtasks'] ?? []);
|
||||
$projectId = ($arguments['projectId'] ?? null);
|
||||
|
||||
$parentTask = $this->ticketsService->getTicket($parentTaskId);
|
||||
if (! $parentTask) {
|
||||
return ToolResult::error("Parent task with ID {$parentTaskId} not found.");
|
||||
}
|
||||
|
||||
if ($projectId === null) {
|
||||
$projectId = $parentTask->projectId;
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$successCount = 0;
|
||||
$failureCount = 0;
|
||||
|
||||
foreach ($subtasks as $subtaskData) {
|
||||
$params = [
|
||||
'headline' => $subtaskData['headline'] ?? '',
|
||||
'description' => $subtaskData['description'] ?? '',
|
||||
'projectId' => $projectId,
|
||||
'editorId' => null,
|
||||
'userId' => $subtaskData['userId'] ?? session('userdata.id'),
|
||||
'dateToFinish' => $subtaskData['dateToFinish'] ?? null,
|
||||
'status' => $subtaskData['status'] ?? 3,
|
||||
'sprint' => null,
|
||||
'editFrom' => $subtaskData['editFrom'] ?? null,
|
||||
'editTo' => $subtaskData['editTo'] ?? null,
|
||||
'milestone' => null,
|
||||
'type' => 'subtask',
|
||||
'dependingTicketId' => $parentTaskId,
|
||||
'storypoints' => $subtaskData['effort'] ?? 2,
|
||||
'priority' => $subtaskData['priority'] ?? 3,
|
||||
'planHours' => $subtaskData['planHours'] ?? 1,
|
||||
];
|
||||
|
||||
$result = $this->ticketsService->quickAddTicket($params);
|
||||
|
||||
if ($result) {
|
||||
$successCount++;
|
||||
$results[] = ['headline' => $subtaskData['headline'], 'status' => 'success', 'id' => $result];
|
||||
} else {
|
||||
$failureCount++;
|
||||
$results[] = ['headline' => $subtaskData['headline'] ?? 'Unknown', 'status' => 'error', 'message' => 'Failed to create subtask'];
|
||||
}
|
||||
}
|
||||
|
||||
return ToolResult::text(
|
||||
"Subtask creation completed. Success: {$successCount}, Failed: {$failureCount}\n\n".
|
||||
Str::toMarkdown($results)
|
||||
);
|
||||
}
|
||||
}
|
||||
67
app/Domain/Tickets/Tools/EditMilestoneTool.php
Normal file
67
app/Domain/Tickets/Tools/EditMilestoneTool.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Update an existing milestone.
|
||||
*/
|
||||
class EditMilestoneTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'editMilestone';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Updates an existing milestone as defined by the `id` parameter and using an array `params` where they key is the column name and the value is the value that it should be updated to. Dates need to be provided as iso8601 strings (example: 2024-04-30T15:00:00-04:00). Commonly updated fields are: headline, type, description, projectId, editFrom, editTo, planHours.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('id')->description('ID of the milestone to update.')->required()
|
||||
->raw('params', ['type' => 'object', 'description' => 'Key-value pairs of fields to update. Example: {"headline": "New title", "editFrom": "2024-04-30T15:00:00-04:00"}'])->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$id = (int) ($arguments['id'] ?? 0);
|
||||
$params = ($arguments['params'] ?? null);
|
||||
|
||||
if (is_array($params) && ! empty($params) && isset($params[0]) && is_array($params[0])) {
|
||||
$params = $params[0];
|
||||
}
|
||||
|
||||
if (! is_array($params)) {
|
||||
return ToolResult::error('The params parameter is not a valid object. Provide key-value pairs.');
|
||||
}
|
||||
|
||||
if ($this->ticketsService->patch($id, $params)) {
|
||||
return ToolResult::text('Milestone updated successfully.');
|
||||
}
|
||||
|
||||
return ToolResult::error('Failed to update milestone.');
|
||||
}
|
||||
}
|
||||
67
app/Domain/Tickets/Tools/EditTaskTool.php
Normal file
67
app/Domain/Tickets/Tools/EditTaskTool.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Update an existing task.
|
||||
*/
|
||||
class EditTaskTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'editTask';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Updates an existing task as defined by the `id` parameter and using an array `params` where they key is the column name and the value is the value that it should be updated to. Dates need to be provided as iso8601 strings (example: 2024-04-30T15:00:00-04:00). Commonly updated fields are: headline, type, description, projectId, status (needs to be a status id, see the getStatusLabel tool for further information), storypoints (often called effort), dateToFinish (due date), planHours. Due dates should not be used as a form of timeboxing since they may represent client due dates. Instead use editFrom and editTo dates to schedule a task for a specific user.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('id')->description('ID of the task to update.')->required()
|
||||
->raw('params', ['type' => 'object', 'description' => 'Key-value pairs of fields to update. Example: {"headline": "New title", "status": 3}'])->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$id = (int) ($arguments['id'] ?? 0);
|
||||
$params = ($arguments['params'] ?? null);
|
||||
|
||||
if (is_array($params) && ! empty($params) && isset($params[0]) && is_array($params[0])) {
|
||||
$params = $params[0];
|
||||
}
|
||||
|
||||
if (! is_array($params)) {
|
||||
return ToolResult::error('The params parameter is not a valid object. Provide key-value pairs.');
|
||||
}
|
||||
|
||||
if ($this->ticketsService->patch($id, $params)) {
|
||||
return ToolResult::text('Task updated successfully.');
|
||||
}
|
||||
|
||||
return ToolResult::error('Failed to update task.');
|
||||
}
|
||||
}
|
||||
73
app/Domain/Tickets/Tools/FindMilestonesTool.php
Normal file
73
app/Domain/Tickets/Tools/FindMilestonesTool.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Search for milestones in a project.
|
||||
*/
|
||||
#[IsReadOnly]
|
||||
class FindMilestonesTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'findMilestones';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Gets all milestones from the database given search criteria array. All dates are returned in the format YYYY-MM-DD hh:mm:ss in the UTC timezone';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('projectId')->description('Project ID of the milestones to retrieve.')->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$projectId = (int) ($arguments['projectId'] ?? 0);
|
||||
$milestones = $this->ticketsService->getAllMilestones(['currentProject' => $projectId, 'type' => 'milestone']);
|
||||
|
||||
$results = "## MILESTONES \n";
|
||||
|
||||
if (empty($milestones)) {
|
||||
$results .= 'No Milestones found for this project.';
|
||||
|
||||
return ToolResult::text($results);
|
||||
}
|
||||
|
||||
foreach ($milestones as $milestone) {
|
||||
$progress = $this->ticketsService->getMilestoneProgress($milestone->id);
|
||||
$results .= 'Title: '.$milestone->headline."\n";
|
||||
$results .= 'Start Date: '.$milestone->editFrom."\n";
|
||||
$results .= 'End Date: '.$milestone->editTo."\n";
|
||||
$results .= 'Color: '.$milestone->tags."\n";
|
||||
$results .= 'Progress: '.$progress."% completed\n";
|
||||
}
|
||||
|
||||
return ToolResult::text($results);
|
||||
}
|
||||
}
|
||||
119
app/Domain/Tickets/Tools/FindTasksTool.php
Normal file
119
app/Domain/Tickets/Tools/FindTasksTool.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Models\Tickets as TicketModel;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
use Leantime\Domain\Tickets\Support\TicketFormatter;
|
||||
|
||||
/**
|
||||
* Search for tasks across multiple projects efficiently.
|
||||
*/
|
||||
#[IsReadOnly]
|
||||
class FindTasksTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'findTasks';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Search for tasks across multiple projects efficiently. This is the primary tool for task discovery and should be used for ALL task searches, whether for single projects or multiple projects. Use this instead of separate project queries. Supports filtering by user, status, and date ranges. Important: Execute this tool only ONCE. Ensure you have all project ids you want to query ready and in this array.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->raw('projectIds', ['type' => 'array', 'description' => 'Array of project IDs (numbers) to search. For multiple projects use [1,3,4,5]. This is more efficient than separate calls.'])->required()
|
||||
->string('dateRangeFrom')->description('Modified date range from filter. ISO8601 format (e.g. 2024-04-30T15:00:00-04:00).')
|
||||
->string('dateRangeTo')->description('Modified date range to filter. ISO8601 format.')
|
||||
->integer('userId')->description('User ID to filter by. Empty for all users. 0 for current user.')
|
||||
->string('status')->description('Status filter: open (not completed), done (completed), all (everything). Default is all.')
|
||||
->integer('limit')->description('Maximum tasks per project. Default 20.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$projectIds = ($arguments['projectIds'] ?? []);
|
||||
$userId = ($arguments['userId'] ?? null);
|
||||
$status = ($arguments['status'] ?? 'all');
|
||||
$limit = (int) ($arguments['limit'] ?? 20);
|
||||
|
||||
$allResults = [];
|
||||
$totalTasks = 0;
|
||||
|
||||
foreach ($projectIds as $projectId) {
|
||||
$effectiveUserId = $userId;
|
||||
if ($effectiveUserId === null) {
|
||||
$effectiveUserId = '';
|
||||
}
|
||||
if ($effectiveUserId === 0) {
|
||||
$effectiveUserId = session('userdata.id') ?? '';
|
||||
}
|
||||
|
||||
$searchCriteria = [
|
||||
'users' => $effectiveUserId,
|
||||
'currentProject' => $projectId,
|
||||
];
|
||||
|
||||
if ($status === 'open') {
|
||||
$searchCriteria['status'] = 'not_done';
|
||||
} elseif ($status === 'done') {
|
||||
$searchCriteria['status'] = 'done';
|
||||
}
|
||||
|
||||
$tickets = $this->ticketsService->getAll($searchCriteria, $limit);
|
||||
|
||||
if (! empty($tickets)) {
|
||||
$allResults[$projectId] = $tickets;
|
||||
$totalTasks += count($tickets);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($allResults)) {
|
||||
return ToolResult::text('No tasks found for the specified criteria.');
|
||||
}
|
||||
|
||||
$response = "## TASK RESULTS ACROSS PROJECTS\n";
|
||||
if ($totalTasks >= ($limit * count($projectIds))) {
|
||||
$response .= "**Showing first {$limit} results per project. Use more specific filters to reduce results.**\n\n";
|
||||
}
|
||||
|
||||
foreach ($allResults as $projectId => $tickets) {
|
||||
$projectName = $tickets[0]['projectName'] ?? "Project {$projectId}";
|
||||
$response .= "## {$projectName} (Project ID: {$projectId})\n";
|
||||
$response .= '**Found '.count($tickets)." tasks**\n\n";
|
||||
|
||||
foreach ($tickets as $ticket) {
|
||||
$ticketModel = new TicketModel($ticket);
|
||||
$formatter = new TicketFormatter($ticketModel);
|
||||
$response .= $formatter->format()."\n\n";
|
||||
}
|
||||
|
||||
$response .= "---\n\n";
|
||||
}
|
||||
|
||||
return ToolResult::text($response);
|
||||
}
|
||||
}
|
||||
76
app/Domain/Tickets/Tools/GetAllStatusLabelsByUserIdTool.php
Normal file
76
app/Domain/Tickets/Tools/GetAllStatusLabelsByUserIdTool.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Get status labels across all projects for a user.
|
||||
*/
|
||||
#[IsReadOnly]
|
||||
class GetAllStatusLabelsByUserIdTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'getAllStatusLabelsByUserId';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Get the status labels available for a user across multiple projects This can help get the statuses in a human readable format for a given project.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('userId')->description('User ID to get status labels for.')->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$userId = (int) ($arguments['userId'] ?? 0);
|
||||
if ($userId === 0) {
|
||||
$userId = session('userdata.id');
|
||||
}
|
||||
|
||||
$status = $this->ticketsService->getAllStatusLabelsByUserId($userId);
|
||||
|
||||
$statusAIString = '## Status Labels';
|
||||
foreach ($status as $projectKey => $projectStatus) {
|
||||
foreach ($projectStatus as $key => $value) {
|
||||
$result = [
|
||||
'id' => $key,
|
||||
'projectId' => $projectKey,
|
||||
'name' => $value['name'],
|
||||
'statusType' => $value['statusType'],
|
||||
'isKanbanColumn' => $value['kanbanCol'] === '1' ? 'yes' : 'no',
|
||||
];
|
||||
|
||||
$statusAIString .= Str::toMarkdown($result)."\n";
|
||||
}
|
||||
}
|
||||
|
||||
return ToolResult::text($statusAIString);
|
||||
}
|
||||
}
|
||||
61
app/Domain/Tickets/Tools/GetMilestoneTool.php
Normal file
61
app/Domain/Tickets/Tools/GetMilestoneTool.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Get a single milestone by ID.
|
||||
*/
|
||||
#[IsReadOnly]
|
||||
class GetMilestoneTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'getMilestone';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Gets one individual milestone by milestone id. If the user is not allowed to see the task, false is returned. All dates are returned in the format YYYY-MM-DD hh:mm:ss in the UTC timezone';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('id')->description('ID of the milestone to retrieve.')->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$id = (int) ($arguments['id'] ?? 0);
|
||||
$ticket = $this->ticketsService->getTicket($id);
|
||||
|
||||
if (! $ticket) {
|
||||
return ToolResult::error("Milestone with ID {$id} not found.");
|
||||
}
|
||||
|
||||
return ToolResult::text(Str::toMarkdown($ticket)."\n");
|
||||
}
|
||||
}
|
||||
69
app/Domain/Tickets/Tools/GetStatusLabelsTool.php
Normal file
69
app/Domain/Tickets/Tools/GetStatusLabelsTool.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
|
||||
/**
|
||||
* Get status labels for a project.
|
||||
*/
|
||||
#[IsReadOnly]
|
||||
class GetStatusLabelsTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'getStatusLabels';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Get the status labels available for a project. This can help get the statuses in a human readable format for a given project, since the database only stores the status id and each project can define its own statuses. The array is keyed by the status id and returns an array with name (language string), class (css class), statusType (INPROGRESS, DONE, NEW) and whether its available in a kanbanCol (true/false).';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('projectId')->description('Project ID to get status labels for.')->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$projectId = (int) ($arguments['projectId'] ?? 0);
|
||||
$status = $this->ticketsService->getStatusLabels($projectId);
|
||||
|
||||
$statusAIString = '## Status Labels';
|
||||
foreach ($status as $key => $value) {
|
||||
$result = [
|
||||
'id' => $key,
|
||||
'name' => $value['name'],
|
||||
'statusType' => $value['statusType'],
|
||||
'isKanbanColumn' => $value['kanbanCol'] === '1' ? 'yes' : 'no',
|
||||
];
|
||||
|
||||
$statusAIString .= Str::toMarkdown($result)."\n";
|
||||
}
|
||||
|
||||
return ToolResult::text($statusAIString);
|
||||
}
|
||||
}
|
||||
63
app/Domain/Tickets/Tools/GetTaskTool.php
Normal file
63
app/Domain/Tickets/Tools/GetTaskTool.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Tools;
|
||||
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Tools\Annotations\IsReadOnly;
|
||||
use Laravel\Mcp\Server\Tools\ToolInputSchema;
|
||||
use Laravel\Mcp\Server\Tools\ToolResult;
|
||||
use Leantime\Domain\Tickets\Services\Tickets;
|
||||
use Leantime\Domain\Tickets\Support\TicketFormatter;
|
||||
|
||||
/**
|
||||
* Get a single task by ID.
|
||||
*/
|
||||
#[IsReadOnly]
|
||||
class GetTaskTool extends Tool
|
||||
{
|
||||
public function __construct(
|
||||
private Tickets $ticketsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the tool name.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return 'getTicket';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tool description.
|
||||
*/
|
||||
public function description(): string
|
||||
{
|
||||
return 'Gets one individual task by task id. If the user is not allowed to see the task, false is returned. All dates are returned in the format YYYY-MM-DD hh:mm:ss in the UTC timezone';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the tool input schema.
|
||||
*/
|
||||
public function schema(ToolInputSchema $schema): ToolInputSchema
|
||||
{
|
||||
return $schema
|
||||
->integer('id')->description('ID of the task to retrieve.')->required();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the tool request.
|
||||
*/
|
||||
public function handle(array $arguments): ToolResult
|
||||
{
|
||||
$id = (int) ($arguments['id'] ?? 0);
|
||||
$ticket = $this->ticketsService->getTicket($id);
|
||||
|
||||
if ($ticket) {
|
||||
$formatter = new TicketFormatter($ticket);
|
||||
|
||||
return ToolResult::text($formatter->format());
|
||||
}
|
||||
|
||||
return ToolResult::error("Could not find task with id {$id}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user