fix(worker): make external MCP filter params optional, surface tool errors
Nullable filter/patch params across the External/ MCP tool classes (ListTasks, UpdateTask, AddSubtask, ReviewTask, SetMyDay, SetListConfig/SetTaskConfig, CreateList/UpdateList) lacked C# default values, so the generated tool schema marked them required — MCP clients omitting them (the common case) failed. Gave every such parameter a default value. Also registered a call-tool filter (ExternalMcpExceptionFilter) on the external MCP host that translates InvalidOperationException/ArgumentException into McpException, since the SDK's own catch-all discards ex.Message for any other exception type and returns a generic "An error occurred invoking 'X'." string. Added a reflection-based schema test sweeping every [McpServerToolType] class to guard against reintroducing a required-but-nullable parameter.
This commit is contained in:
+13
-13
@@ -107,9 +107,9 @@ public sealed class ExternalMcpService
|
||||
"Valid status values: Idle, Queued, Running, WaitingForReview, WaitingForChildren, Done, Failed, Cancelled.")]
|
||||
public async Task<IReadOnlyList<TaskDto>> ListTasks(
|
||||
string listId,
|
||||
string? createdBy,
|
||||
string? status,
|
||||
CancellationToken cancellationToken)
|
||||
string? createdBy = null,
|
||||
string? status = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
TaskStatus? statusFilter = null;
|
||||
if (!string.IsNullOrWhiteSpace(status))
|
||||
@@ -193,10 +193,10 @@ public sealed class ExternalMcpService
|
||||
[McpServerTool, Description("Update an existing task's title, description, and/or commit type. Pass null to leave a field unchanged. Refuses if the task is currently Running.")]
|
||||
public async Task<TaskDto> UpdateTask(
|
||||
string taskId,
|
||||
string? title,
|
||||
string? description,
|
||||
string? commitType,
|
||||
CancellationToken cancellationToken)
|
||||
string? title = null,
|
||||
string? description = null,
|
||||
string? commitType = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var task = await _tasks.GetByIdAsync(taskId, cancellationToken)
|
||||
?? throw new InvalidOperationException($"Task {taskId} not found.");
|
||||
@@ -219,8 +219,8 @@ public sealed class ExternalMcpService
|
||||
public async Task<TaskDto> AddSubtask(
|
||||
string taskId,
|
||||
string title,
|
||||
int? orderNum,
|
||||
CancellationToken cancellationToken)
|
||||
int? orderNum = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
throw new InvalidOperationException("title is required.");
|
||||
@@ -300,8 +300,8 @@ public sealed class ExternalMcpService
|
||||
public async Task<TaskDto> ReviewTask(
|
||||
string taskId,
|
||||
string decision,
|
||||
string? feedback,
|
||||
CancellationToken cancellationToken)
|
||||
string? feedback = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_ = await _tasks.GetByIdAsync(taskId, cancellationToken)
|
||||
?? throw new InvalidOperationException($"Task {taskId} not found.");
|
||||
@@ -619,8 +619,8 @@ public sealed class ExternalMcpService
|
||||
public async Task<TaskDto> SetMyDay(
|
||||
string taskId,
|
||||
bool isMyDay,
|
||||
int? sortOrder,
|
||||
CancellationToken cancellationToken)
|
||||
int? sortOrder = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var ctx = await _dbFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user