feat: support multi-status filtering and includeAllStatuses for task listing#69
Conversation
…listing Add two mechanisms to include completed/resolved tasks when listing: 1. `includeAllStatuses` boolean — passes through to Motion API to return tasks across all statuses in one call 2. `status` as array — accepts an array of status strings to filter by multiple specific statuses (e.g., ["Todo", "Completed"]) Adds validation that status and includeAllStatuses cannot be combined. Updates tool schema descriptions to clarify default filtering behavior. Closes #68
PR Review: feat: support multi-status filtering and includeAllStatuses for task listingOverall this is a clean, well-scoped addition. The core logic is sound and the backward-compatibility story is good. I found a few issues worth addressing before merge. Bugs / Issues1.
|
There was a problem hiding this comment.
Pull request overview
This PR adds support for multi-status filtering and the includeAllStatuses parameter to the motion_tasks list operation, addressing issue #68. The changes enable users to retrieve tasks across multiple statuses (e.g., both "Todo" and "Completed") or all statuses in a single API call, which was previously not possible.
Changes:
- Added
includeAllStatusesboolean parameter to list tasks across all statuses including completed/resolved tasks - Updated
statusparameter to accept either a single string or an array of strings for multi-status filtering - Added validation to prevent combining
statusandincludeAllStatusesparameters per API constraints
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| src/tools/ToolDefinitions.ts | Updated tool schema to support status as string or array using oneOf, and added includeAllStatuses boolean parameter |
| src/services/motionApi.ts | Modified GetTasksOptions interface and getTasks method to handle array status values and includeAllStatuses parameter |
| src/handlers/TaskHandler.ts | Updated ListTaskParams interface, added validation for parameter conflicts, and enhanced status display formatting |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (params.includeAllStatuses && params.status) { | ||
| return this.handleError(new Error( | ||
| "Cannot combine 'includeAllStatuses' with 'status' filter. Use one or the other." | ||
| )); | ||
| } |
There was a problem hiding this comment.
The validation incorrectly rejects the combination of includeAllStatuses with an empty status array. An empty array is semantically equivalent to no status filter and should not conflict with includeAllStatuses. The condition should check for non-empty values: if (params.includeAllStatuses && params.status && (typeof params.status === 'string' || params.status.length > 0))
| @@ -238,6 +239,13 @@ export class TaskHandler extends BaseHandler { | |||
| } | |||
| } | |||
|
|
|||
There was a problem hiding this comment.
Missing validation for status array elements. When status is an array, each element should be validated to ensure it's a non-empty string (similar to the labels validation on line 269). Without this, empty strings or non-string values in the array could be passed to the API. Add validation before the includeAllStatuses check: if (Array.isArray(params.status) && params.status.some(s => !s || typeof s !== 'string')) { return this.handleError(new Error('Status array must contain only non-empty strings')); }
| // Validate status array elements if provided | |
| if (Array.isArray(params.status) && params.status.some(s => !s || typeof s !== 'string')) { | |
| return this.handleError(new Error('Status array must contain only non-empty strings')); | |
| } |
| @@ -269,17 +277,24 @@ export class TaskHandler extends BaseHandler { | |||
| workspaceId: workspace.id, | |||
| projectId: resolvedProjectId, | |||
| status: params.status, | |||
| includeAllStatuses: params.includeAllStatuses, | |||
There was a problem hiding this comment.
The new multi-status filtering and includeAllStatuses functionality lacks test coverage. Based on the test plan in the PR description, there should be tests verifying: 1) status as an array works correctly, 2) includeAllStatuses parameter works, 3) combining status and includeAllStatuses returns a validation error, and 4) backward compatibility with single status strings. Consider adding tests for these scenarios in tests/handlers.task.spec.ts.
|
|
||
| const statusDisplay = params.includeAllStatuses | ||
| ? 'all statuses' | ||
| : Array.isArray(params.status) |
There was a problem hiding this comment.
The statusDisplay logic doesn't handle empty arrays correctly. If params.status is an empty array, params.status.join(', ') returns an empty string, which would result in the display text showing with status "". Consider adding a check: Array.isArray(params.status) && params.status.length > 0 ? params.status.join(', ') : params.status
| : Array.isArray(params.status) | |
| : Array.isArray(params.status) && params.status.length > 0 |
| params.append('status', s); | ||
| } | ||
| } else { |
There was a problem hiding this comment.
Empty strings in the status array are not filtered before appending to query parameters. Unlike the labels handling (lines 628-629) which checks if (label) before appending, status values are appended without validation. If an empty string exists in the status array, it will be sent to the API as status=. Consider adding validation: if (s) { params.append('status', s); }
| params.append('status', s); | |
| } | |
| } else { | |
| if (s) { | |
| params.append('status', s); | |
| } | |
| } | |
| } else if (status) { |
| if (status) { | ||
| params.append('status', status); | ||
| if (Array.isArray(status)) { | ||
| for (const s of status) { |
There was a problem hiding this comment.
The implementation doesn't check for or filter duplicate status values in the array. While not necessarily wrong (the API may handle duplicates gracefully), it could result in redundant query parameters like status=Todo&status=Todo. Consider deduplicating the array before iteration for cleaner API calls, though this is a minor optimization.
| for (const s of status) { | |
| const uniqueStatuses = Array.from(new Set(status)); | |
| for (const s of uniqueStatuses) { |
- Reject status arrays in create/update operations with clear error; arrays are only valid for list (fixes schema-wide permissiveness bug) - Add status array element validation (non-empty strings only), mirroring the existing labels validation pattern - Fix empty-array edge case in includeAllStatuses conflict check; [] is semantically "no filter" and should not trigger the validation error - Fix statusDisplay empty-array edge case (avoid empty string label) - Guard empty strings in status array before URLSearchParams.append() - Deduplicate status array before iterating to avoid redundant query params - Update MotionTasksArgs to widen status to string | string[] and add includeAllStatuses, aligning compile-time types with runtime schema Deferred: test coverage tracked in #70
PR Review: feat: support multi-status filtering and includeAllStatuses for task listingThe prior claude review was written against an earlier version of this PR. The current diff has already resolved all three issues it raised:
Good to see those addressed. Here's what remains. One real concern: Motion API multi-value
|
Verified: Motion API supports repeated
|
| Test | Query | Task Count | Statuses Returned |
|---|---|---|---|
Single status=Todo |
baseline | 35 | Todo: 35 |
Single status=In Progress |
baseline | 1 | In Progress: 1 |
Repeated status=Todo&status=In Progress |
PR approach | 36 | Todo: 35, In Progress: 1 |
Three statuses Todo + In Progress + Backlog |
PR approach | 36 | Todo: 35, In Progress: 1 (0 Backlog tasks exist) |
Comma-separated status=Todo,In Progress |
alternative | 0 | No match |
Conclusions
- Repeated
status=params work correctly — the API returns the union of tasks matching any provided status (35 + 1 = 36, exactly matching individual baselines). - Comma-separated values do NOT work — returns 0 results, confirming
URLSearchParams.append()in a loop is the correct approach. - The
status=Todo&status=In%20Progresspattern is the right way to do multi-status filtering with the Motion API.
Summary
includeAllStatusesboolean parameter to themotion_taskslist operation, passing through to the Motion API to return tasks across all statuses (including completed/resolved) in one callstatusparameter to accept either a single string or an array of strings, enabling multi-status filtering (e.g.,["Todo", "Completed"])statusandincludeAllStatusescannot be combined (per API constraints)statusfield description to clarify default filtering behavior and referencemotion_statusestoolCloses #68
Test plan
npm run buildpasses cleanlytimeout 3s npm run mcpincludeAllStatuses: truereturns completed tasksstatus: ["Todo", "Completed"]returns tasks matching either statusstatus: "Todo"still works (backward compatible)statusandincludeAllStatusesreturns a validation error