Commitf793c722Recorded22 Apr 2026Repositorybureau
email,calendar: validate required MCP args with require-arg helper
Message
Mirror the folio fix: replace undefaulted (dict-ref args KEY:) with a require-arg helper that raises 'Missing required argument: KEY' when the key is absent.
Closes t-a06a (bureau email-read crashing on every call with 'dict-ref: key not found'): the handler at email.sgl:276 was reading id without a default, so any request whose arguments dict was missing 'id' (or named the field differently) crashed into sigil's internal error instead of a user-facing one.
Changed
src/bureau/calendar.sgl | 24 ++++++++++++------------
src/bureau/email.sgl | 50 +++++++++++++++++++++++++++++++-------------------
2 files changed, 43 insertions(+), 31 deletions(-)Diff
src/bureau/calendar.sglmodified
@@ -164,8 +164,8 @@
164
165
(define (tool-events args) 166
(let* ((client (ensure-caldav-client))−167
(start (parse-iso8601 (dict-ref args start:)))−168
(end (parse-iso8601 (dict-ref args end:)))+167
(start (parse-iso8601 (require-arg args start: "start")))+168
(end (parse-iso8601 (require-arg args end: "end"))) 169
(calendar-name (dict-ref args calendar: #f)) 170
(expand? (dict-ref args expand-recurring: #t)) 171
(calendars (resolve-calendars client calendar-name))@@ -178,7 +178,7 @@
178
179
(define (tool-event-details args) 180
(let* ((client (ensure-caldav-client))−181
(url (dict-ref args url:))+181
(url (require-arg args url: "url")) 182
(event (caldav-event-get client url))) 183
(if event 184
(format-event-full event)@@ -192,9 +192,9 @@
192
193
(define (tool-event-create args) 194
(let* ((client (ensure-caldav-client))−195
(summary (dict-ref args summary:))−196
(start (parse-iso8601 (dict-ref args start:)))−197
(end (parse-iso8601 (dict-ref args end:)))+195
(summary (require-arg args summary: "summary"))+196
(start (parse-iso8601 (require-arg args start: "start")))+197
(end (parse-iso8601 (require-arg args end: "end"))) 198
(calendar-name (dict-ref args calendar: #f)) 199
(description (dict-ref args description: #f)) 200
(location (dict-ref args location: #f))@@ -231,7 +231,7 @@
231
232
(define (tool-event-update args) 233
(let* ((client (ensure-caldav-client))−234
(url (dict-ref args url:))+234
(url (require-arg args url: "url")) 235
(event (caldav-event-get client url)) 236
(_ (unless event (error (string-append "Event not found: " url)))) 237
;; Apply updates@@ -250,8 +250,8 @@
250
251
(define (tool-event-add-attendees args) 252
(let* ((client (ensure-caldav-client))−253
(url (dict-ref args url:))−254
(attendees-str (dict-ref args attendees:))+253
(url (require-arg args url: "url"))+254
(attendees-str (require-arg args attendees: "attendees")) 255
(new-attendees (parse-attendee-list attendees-str)) 256
(event (caldav-event-get client url)) 257
(_ (unless event (error (string-append "Event not found: " url))))@@ -272,14 +272,14 @@
272
273
(define (tool-event-delete args) 274
(let* ((client (ensure-caldav-client))−275
(url (dict-ref args url:)))+275
(url (require-arg args url: "url"))) 276
(caldav-event-delete! client url) 277
(string-append "Event deleted: " url))) 278
279
(define (tool-free-slots args) 280
(let* ((client (ensure-caldav-client))−281
(start (parse-iso8601 (dict-ref args start:)))−282
(end (parse-iso8601 (dict-ref args end:)))+281
(start (parse-iso8601 (require-arg args start: "start")))+282
(end (parse-iso8601 (require-arg args end: "end"))) 283
(calendar-name (dict-ref args calendar: #f)) 284
(min-duration (* (dict-ref args min-duration: 30) 60)) ;; convert to seconds 285
(working-hours? (dict-ref args working-hours-only: #t))src/bureau/email.sglmodified
@@ -16,6 +16,18 @@
16
normalize-utc-date) 17
(begin 18
+19
;; ============================================================+20
;; Argument validation+21
;; ============================================================+22
+23
;; Look up a required argument in a tool's args dict. Raises a+24
;; user-facing error naming the missing key instead of sigil's+25
;; opaque `dict-ref: key not found`.+26
(define (require-arg args key name)+27
(if (dict-contains? args key)+28
(dict-ref args key)+29
(error (string-append "Missing required argument: " name))))+30
31
;; ============================================================ 32
;; Mailbox Resolution 33
;; ============================================================@@ -273,7 +285,7 @@
285
286
(define (tool-email-read args) 287
(let* ((client (ensure-jmap-client))−276
(id (dict-ref args id:))+288
(id (require-arg args id: "id")) 289
(email (jmap-email-get client id))) 290
(if email 291
(format-email-full email)@@ -281,8 +293,8 @@
293
294
(define (tool-email-send args) 295
(let* ((client (ensure-jmap-client))−284
(to-str (dict-ref args to:))−285
(subject (dict-ref args subject:))+296
(to-str (require-arg args to: "to"))+297
(subject (require-arg args subject: "subject")) 298
(body (dict-ref args body: #f)) 299
(html-body (dict-ref args html-body: #f)) 300
(cc-str (dict-ref args cc: #f))@@ -316,8 +328,8 @@
328
329
(define (tool-email-reply args) 330
(let* ((client (ensure-jmap-client))−319
(id (dict-ref args id:))−320
(body (dict-ref args body:))+331
(id (require-arg args id: "id"))+332
(body (require-arg args body: "body")) 333
(reply-all? (dict-ref args reply-all: #f)) 334
(html-body (dict-ref args html-body: #f)) 335
(identity-email (dict-ref args identity: #f))@@ -403,8 +415,8 @@
415
416
(define (tool-email-move args) 417
(let* ((client (ensure-jmap-client))−406
(id (dict-ref args id:))−407
(mailbox-name (dict-ref args mailbox:))+418
(id (require-arg args id: "id"))+419
(mailbox-name (require-arg args mailbox: "mailbox")) 420
(mb (resolve-mailbox client mailbox-name)) 421
(mb-id (dict-ref mb id:))) 422
(jmap-email-move! client id mb-id)@@ -412,7 +424,7 @@
424
425
(define (tool-email-archive args) 426
(let* ((client (ensure-jmap-client))−415
(id (dict-ref args id:))+427
(id (require-arg args id: "id")) 428
(archive (or (jmap-mailbox-by-role client "archive") 429
(error "Archive mailbox not found"))) 430
(archive-id (dict-ref archive id:)))@@ -421,7 +433,7 @@
433
434
(define (tool-email-trash args) 435
(let* ((client (ensure-jmap-client))−424
(id (dict-ref args id:))+436
(id (require-arg args id: "id")) 437
(trash (or (jmap-mailbox-by-role client "trash") 438
(error "Trash mailbox not found"))) 439
(trash-id (dict-ref trash id:)))@@ -430,7 +442,7 @@
442
443
(define (tool-email-trash-batch args) 444
(let* ((client (ensure-jmap-client))−433
(ids-str (dict-ref args ids:))+445
(ids-str (require-arg args ids: "ids")) 446
(ids (parse-id-list ids-str)) 447
(trash (or (jmap-mailbox-by-role client "trash") 448
(error "Trash mailbox not found")))@@ -440,7 +452,7 @@
452
453
(define (tool-email-archive-batch args) 454
(let* ((client (ensure-jmap-client))−443
(ids-str (dict-ref args ids:))+455
(ids-str (require-arg args ids: "ids")) 456
(ids (parse-id-list ids-str)) 457
(archive (or (jmap-mailbox-by-role client "archive") 458
(error "Archive mailbox not found")))@@ -450,9 +462,9 @@
462
463
(define (tool-email-move-batch args) 464
(let* ((client (ensure-jmap-client))−453
(ids-str (dict-ref args ids:))+465
(ids-str (require-arg args ids: "ids")) 466
(ids (parse-id-list ids-str))−455
(mailbox-name (dict-ref args mailbox:))+467
(mailbox-name (require-arg args mailbox: "mailbox")) 468
(mb (resolve-mailbox client mailbox-name)) 469
(mb-id (dict-ref mb id:))) 470
(jmap-email-move-many! client ids mb-id)@@ -460,7 +472,7 @@
472
473
(define (tool-email-flag args) 474
(let* ((client (ensure-jmap-client))−463
(id (dict-ref args id:))+475
(id (require-arg args id: "id")) 476
(unflag? (dict-ref args unflag: #f))) 477
(if unflag? 478
(begin (jmap-email-remove-keyword! client id "$flagged")@@ -470,7 +482,7 @@
482
483
(define (tool-email-mark-read args) 484
(let* ((client (ensure-jmap-client))−473
(id (dict-ref args id:))+485
(id (require-arg args id: "id")) 486
(unread? (dict-ref args unread: #f))) 487
(if unread? 488
(begin (jmap-email-remove-keyword! client id "$seen")@@ -481,7 +493,7 @@
493
494
(define (tool-email-list-attachments args) 495
(let* ((client (ensure-jmap-client))−484
(id (dict-ref args id:))+496
(id (require-arg args id: "id")) 497
(email (jmap-email-get client id))) 498
(unless email (error (string-append "Email not found: " id))) 499
(let ((attachments (dict-ref email attachments: #f)))@@ -510,9 +522,9 @@
522
523
(define (tool-email-download-attachment args) 524
(let* ((client (ensure-jmap-client))−513
(id (dict-ref args id:))−514
(att-name (dict-ref args name:))−515
(dest-dir (dict-ref args path:))+525
(id (require-arg args id: "id"))+526
(att-name (require-arg args name: "name"))+527
(dest-dir (require-arg args path: "path")) 528
(email (jmap-email-get client id))) 529
(unless email (error (string-append "Email not found: " id))) 530
(let ((attachments (dict-ref email attachments: #f)))