@@ -362,11 +362,19 @@ func (*RootCmd) mcpConfigureCursor() *serpent.Command {
362
362
}
363
363
364
364
type taskReport struct {
365
- link string
366
- messageID int64
365
+ // link is optional.
366
+ link string
367
+ // messageID must be set if this update is from a *user* message. A user
368
+ // message only happens when interacting via the AI AgentAPI (as opposed to
369
+ // interacting with the terminal directly).
370
+ messageID * int64
371
+ // selfReported must be set if the update is directly from the AI agent
372
+ // (as opposed to the screen watcher).
367
373
selfReported bool
368
- state codersdk.WorkspaceAppStatusState
369
- summary string
374
+ // state must always be set.
375
+ state codersdk.WorkspaceAppStatusState
376
+ // summary is optional.
377
+ summary string
370
378
}
371
379
372
380
type mcpServer struct {
@@ -388,17 +396,17 @@ func (r *RootCmd) mcpServer() *serpent.Command {
388
396
return & serpent.Command {
389
397
Use :"server" ,
390
398
Handler :func (inv * serpent.Invocation )error {
391
- // lastUserMessageID is the ID of the last *user* message that we saw. A
392
- // user message only happens when interacting via the AI AgentAPI (as
393
- // opposed to interacting with the terminal directly).
394
- var lastUserMessageID int64
395
399
var lastReport taskReport
396
400
// Create a queue that skips duplicates and preserves summaries.
397
401
queue := cliutil.NewQueue [taskReport ](512 ).WithPredicate (func (report taskReport ) (taskReport ,bool ) {
398
- // Use "working" status if this is a new user message. If this is not a
399
- // new user message, and the status is "working" and not self-reported
400
- // (meaning it came from the screen watcher), then it means one of two
401
- // things:
402
+ // If this is a user message, discard if it is not new.
403
+ if report .messageID != nil &&
404
+ lastReport .messageID != nil && * lastReport .messageID >= * report .messageID {
405
+ return report ,false
406
+ }
407
+ // If this is not a user message, and the status is "working" and not
408
+ // self-reported (meaning it came from the screen watcher), then it
409
+ // means one of two things:
402
410
//
403
411
// 1. The AI agent is not working; the user is interacting with the
404
412
// terminal directly.
@@ -415,18 +423,27 @@ func (r *RootCmd) mcpServer() *serpent.Command {
415
423
// user manually submits a new prompt and the AI agent becomes active
416
424
// (and does not update itself), but it avoids spamming useless status
417
425
// updates as the user is typing, so the tradeoff is worth it.
418
- if report .messageID > lastUserMessageID {
419
- report .state = codersdk .WorkspaceAppStatusStateWorking
420
- } else if report . state == codersdk . WorkspaceAppStatusStateWorking && ! report .selfReported && lastReport .state != "" {
426
+ if report .messageID == nil &&
427
+ report .state == codersdk .WorkspaceAppStatusStateWorking &&
428
+ ! report .selfReported && lastReport .state != "" {
421
429
return report ,false
422
430
}
431
+ // Keep track of the last message ID.
432
+ if report .messageID == nil {
433
+ report .messageID = lastReport .messageID
434
+ }
423
435
// Preserve previous message and URI if there was no message.
424
436
if report .summary == "" {
425
437
report .summary = lastReport .summary
426
438
if report .link == "" {
427
439
report .link = lastReport .link
428
440
}
429
441
}
442
+ // Avoid queuing empty statuses (this would probably indicate a
443
+ // developer error)
444
+ if report .state == "" {
445
+ return report ,false
446
+ }
430
447
// Avoid queueing duplicate updates.
431
448
if report .state == lastReport .state &&
432
449
report .link == lastReport .link &&
@@ -607,7 +624,8 @@ func (s *mcpServer) startWatcher(ctx context.Context, inv *serpent.Invocation) {
607
624
case agentapi.EventMessageUpdate :
608
625
if ev .Role == agentapi .RoleUser {
609
626
err := s .queue .Push (taskReport {
610
- messageID :ev .Id ,
627
+ messageID :& ev .Id ,
628
+ state :codersdk .WorkspaceAppStatusStateWorking ,
611
629
})
612
630
if err != nil {
613
631
cliui .Warnf (inv .Stderr ,"Failed to queue update: %s" ,err )