Every tool description now leads with what the tool does AND when to reach for it, since MCP clients rank tools by that text. Per-parameter prose moved onto the parameters as [Description], exhaustive result-shape enumerations and design/history rationale dropped, and the repeated boilerplate clauses (lean-task-ref, batch cap, refused-while-Running) pulled into McpToolDocs, which also documents the style for future tools. Tool-level description text: 20494 -> 13605 chars (-34%); combined with the new parameter descriptions 18517 (-10%). Closes gaps that caused wrong calls rather than just verbose ones: - list_task_attachments returns metadata only, no file content - run_task_now shares continue_task's single override slot and throws when busy - list_runs is ordered oldest-first and feeds get_run - workingDir on create_list/update_list is an existing local git repo path, unvalidated until the first task run - get_task_worktree's behind=0 also means the main ref was unreachable Removes get_task_status_values: a whole tool entry for static reference text. GetTask's description is now the canonical place for status meanings.
59 lines
2.4 KiB
C#
59 lines
2.4 KiB
C#
using System.ComponentModel;
|
|
using ClaudeDo.Data;
|
|
using ClaudeDo.Worker.Queue;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using ModelContextProtocol.Server;
|
|
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
|
|
|
namespace ClaudeDo.Worker.External;
|
|
|
|
public sealed record QueueSlotDto(string Slot, string TaskId, DateTime StartedAt);
|
|
|
|
public sealed record GetQueueStateResult(
|
|
int ConfiguredSlots,
|
|
int EffectiveSlots,
|
|
IReadOnlyList<QueueSlotDto> ActiveSlots,
|
|
IReadOnlyList<string> WaitingTaskIds);
|
|
|
|
[McpServerToolType]
|
|
public sealed class QueueStateMcpTools
|
|
{
|
|
private readonly QueueService _queue;
|
|
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
|
|
|
|
public QueueStateMcpTools(QueueService queue, IDbContextFactory<ClaudeDoDbContext> dbFactory)
|
|
{
|
|
_queue = queue;
|
|
_dbFactory = dbFactory;
|
|
}
|
|
|
|
[McpServerTool, Description(
|
|
"Read-only snapshot of the execution queue -- call this to observe slot occupancy instead " +
|
|
"of inferring it from maxParallelExecutions. effectiveSlots is configuredSlots stepped down " +
|
|
"by the usage throttle (lower when the 5h/7d usage window fills up), so comparing the two " +
|
|
"shows whether throttling is currently active. Each active slot is \"queue\" (a normal " +
|
|
"queue slot) or \"override\" (the single run_task_now/continue_task slot). waitingTaskIds " +
|
|
"lists queued, unblocked, non-manual, due tasks in the order the queue would pick them next.")]
|
|
public async Task<GetQueueStateResult> GetQueueState(CancellationToken cancellationToken = default)
|
|
{
|
|
var (configured, effective) = await _queue.GetSlotCountsAsync(cancellationToken);
|
|
|
|
var activeSlots = _queue.GetActive()
|
|
.Select(a => new QueueSlotDto(a.slot, a.taskId, a.startedAt))
|
|
.ToList();
|
|
|
|
await using var ctx = await _dbFactory.CreateDbContextAsync(cancellationToken);
|
|
var now = DateTime.UtcNow;
|
|
var waitingTaskIds = await ctx.Tasks
|
|
.Where(t => t.Status == TaskStatus.Queued
|
|
&& t.BlockedByTaskId == null
|
|
&& !t.IsManual
|
|
&& (t.ScheduledFor == null || t.ScheduledFor <= now))
|
|
.OrderBy(t => t.SortOrder).ThenBy(t => t.CreatedAt)
|
|
.Select(t => t.Id)
|
|
.ToListAsync(cancellationToken);
|
|
|
|
return new GetQueueStateResult(configured, effective, activeSlots, waitingTaskIds);
|
|
}
|
|
}
|