fix: tolerate numeric args from MCP JSON in all kanban commands
Replace fragile 'as String'/'as String?' casts in callAsTool() with string interpolation so that numeric values sent by MCP JSON clients (double instead of String) no longer throw a type cast error. Fixes: type 'double' is not a subtype of type 'String' in type cast Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
0ad1fae213
commit
1e263f08c0
13 changed files with 41 additions and 35 deletions
|
|
@ -35,8 +35,8 @@ class AddCommentCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final id = (args['id'] as String).toUpperCase();
|
final id = '${args['id']}'.toUpperCase();
|
||||||
final comment = args['comment'] as String;
|
final comment = '${args['comment']}';
|
||||||
|
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
final store = TicketStore(
|
final store = TicketStore(
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class ArchiveCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final id = (args['id'] as String).toUpperCase();
|
final id = '${args['id']}'.toUpperCase();
|
||||||
|
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
final config = context.config.kanban;
|
final config = context.config.kanban;
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,11 @@ class BoardCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final typeFilter = args['type'] as String?;
|
final typeFilter = args['type'] != null ? '${args['type']}' : null;
|
||||||
final labelFilter = args['label'] as String?;
|
final labelFilter = args['label'] != null ? '${args['label']}' : null;
|
||||||
final milestoneFilter = args['milestone'] as String?;
|
final milestoneFilter = args['milestone'] != null
|
||||||
|
? '${args['milestone']}'
|
||||||
|
: null;
|
||||||
|
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
final config = context.config.kanban;
|
final config = context.config.kanban;
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,10 @@ class CreateCommand extends DewCommand with DewToolCommand {
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
final config = context.config.kanban;
|
final config = context.config.kanban;
|
||||||
|
|
||||||
final title = args['title'] as String;
|
final title = '${args['title']}';
|
||||||
final typeId = args['type'] as String;
|
final typeId = '${args['type']}';
|
||||||
final columnArg = args['column'] as String?;
|
final columnArg = args['column'] != null ? '${args['column']}' : null;
|
||||||
final body = args['body'] as String? ?? '';
|
final body = args['body'] != null ? '${args['body']}' : '';
|
||||||
final milestones = _toStringList(args['milestone']);
|
final milestones = _toStringList(args['milestone']);
|
||||||
final labels = _toStringList(args['label']);
|
final labels = _toStringList(args['label']);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class DeleteCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final id = (args['id'] as String).toUpperCase();
|
final id = '${args['id']}'.toUpperCase();
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
final store = TicketStore(
|
final store = TicketStore(
|
||||||
kanbanDir: context.dirs.kanban,
|
kanbanDir: context.dirs.kanban,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class GetCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final id = (args['id'] as String).toUpperCase();
|
final id = '${args['id']}'.toUpperCase();
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
final store = TicketStore(
|
final store = TicketStore(
|
||||||
kanbanDir: context.dirs.kanban,
|
kanbanDir: context.dirs.kanban,
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,9 @@ class LinkCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final id = (args['id'] as String).toUpperCase();
|
final id = '${args['id']}'.toUpperCase();
|
||||||
final targetId = (args['target'] as String).toUpperCase();
|
final targetId = '${args['target']}'.toUpperCase();
|
||||||
final type = args['type'] as String;
|
final type = '${args['type']}';
|
||||||
|
|
||||||
if (id == targetId)
|
if (id == targetId)
|
||||||
throw ArgumentError('A ticket cannot be linked to itself.');
|
throw ArgumentError('A ticket cannot be linked to itself.');
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,12 @@ class ListCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final columnFilter = args['column'] as String?;
|
final columnFilter = args['column'] != null ? '${args['column']}' : null;
|
||||||
final typeFilter = args['type'] as String?;
|
final typeFilter = args['type'] != null ? '${args['type']}' : null;
|
||||||
final labelFilter = args['label'] as String?;
|
final labelFilter = args['label'] != null ? '${args['label']}' : null;
|
||||||
final milestoneFilter = args['milestone'] as String?;
|
final milestoneFilter = args['milestone'] != null
|
||||||
|
? '${args['milestone']}'
|
||||||
|
: null;
|
||||||
final includeArchived = args['include-archived'] as bool? ?? false;
|
final includeArchived = args['include-archived'] as bool? ?? false;
|
||||||
|
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ class MoveCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final id = (args['id'] as String).toUpperCase();
|
final id = '${args['id']}'.toUpperCase();
|
||||||
final column = args['column'] as String;
|
final column = '${args['column']}';
|
||||||
|
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
final config = context.config.kanban;
|
final config = context.config.kanban;
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,13 @@ class SearchCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final query = (args['query'] as String).toLowerCase();
|
final query = '${args['query']}'.toLowerCase();
|
||||||
final columnFilter = args['column'] as String?;
|
final columnFilter = args['column'] != null ? '${args['column']}' : null;
|
||||||
final typeFilter = args['type'] as String?;
|
final typeFilter = args['type'] != null ? '${args['type']}' : null;
|
||||||
final labelFilter = args['label'] as String?;
|
final labelFilter = args['label'] != null ? '${args['label']}' : null;
|
||||||
final milestoneFilter = args['milestone'] as String?;
|
final milestoneFilter = args['milestone'] != null
|
||||||
|
? '${args['milestone']}'
|
||||||
|
: null;
|
||||||
final includeArchived = args['include-archived'] as bool? ?? false;
|
final includeArchived = args['include-archived'] as bool? ?? false;
|
||||||
|
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class UnarchiveCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final id = (args['id'] as String).toUpperCase();
|
final id = '${args['id']}'.toUpperCase();
|
||||||
|
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
final config = context.config.kanban;
|
final config = context.config.kanban;
|
||||||
|
|
@ -50,7 +50,7 @@ class UnarchiveCommand extends DewCommand with DewToolCommand {
|
||||||
if (ticket == null) throw ArgumentError('Ticket $id not found.');
|
if (ticket == null) throw ArgumentError('Ticket $id not found.');
|
||||||
if (ticket.column != 'archive') return '$id is not archived.';
|
if (ticket.column != 'archive') return '$id is not archived.';
|
||||||
|
|
||||||
final columnArg = args['column'] as String?;
|
final columnArg = args['column'] != null ? '${args['column']}' : null;
|
||||||
final targetColumn = columnArg ?? config.columns.first.id;
|
final targetColumn = columnArg ?? config.columns.first.id;
|
||||||
if (!config.columns.any((c) => c.id == targetColumn)) {
|
if (!config.columns.any((c) => c.id == targetColumn)) {
|
||||||
throw ArgumentError(
|
throw ArgumentError(
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ class UnlinkCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final id = (args['id'] as String).toUpperCase();
|
final id = '${args['id']}'.toUpperCase();
|
||||||
final targetId = (args['target'] as String).toUpperCase();
|
final targetId = '${args['target']}'.toUpperCase();
|
||||||
|
|
||||||
final context = await ProjectContext.find(fs: _fs);
|
final context = await ProjectContext.find(fs: _fs);
|
||||||
final store = TicketStore(
|
final store = TicketStore(
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,11 @@ class UpdateCommand extends DewCommand with DewToolCommand {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> callAsTool(Map<String, dynamic> args) async {
|
Future<String> callAsTool(Map<String, dynamic> args) async {
|
||||||
final id = (args['id'] as String).toUpperCase();
|
final id = '${args['id']}'.toUpperCase();
|
||||||
final title = args['title'] as String?;
|
final title = args['title'] != null ? '${args['title']}' : null;
|
||||||
final typeId = args['type'] as String?;
|
final typeId = args['type'] != null ? '${args['type']}' : null;
|
||||||
final column = args['column'] as String?;
|
final column = args['column'] != null ? '${args['column']}' : null;
|
||||||
final body = args['body'] as String?;
|
final body = args['body'] != null ? '${args['body']}' : null;
|
||||||
final rawMilestones = args['milestone'] as List?;
|
final rawMilestones = args['milestone'] as List?;
|
||||||
final milestones = rawMilestones != null && rawMilestones.isNotEmpty
|
final milestones = rawMilestones != null && rawMilestones.isNotEmpty
|
||||||
? rawMilestones.cast<String>().where((s) => s.isNotEmpty).toList()
|
? rawMilestones.cast<String>().where((s) => s.isNotEmpty).toList()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue