shipmail-mcp 0.8.3 → 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipmail",
3
- "version": "0.8.3",
3
+ "version": "0.8.7",
4
4
  "description": "Operate secure custom-domain business email, calendars, and agent inboxes with Shipmail.",
5
5
  "author": {
6
6
  "name": "Shipmail",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipmail",
3
- "version": "0.8.3",
3
+ "version": "0.8.7",
4
4
  "description": "Operate secure custom-domain business email, calendars, and agent inboxes with Shipmail.",
5
5
  "author": {
6
6
  "name": "Shipmail",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipmail",
3
- "version": "0.8.3",
3
+ "version": "0.8.7",
4
4
  "description": "Operate secure custom-domain business email, calendars, and agent inboxes with Shipmail.",
5
5
  "author": {
6
6
  "name": "Shipmail",
package/README.md CHANGED
@@ -261,7 +261,7 @@ All tools are namespaced with `shipmail_` to avoid collisions with peer MCP serv
261
261
  | Status | `shipmail_status` |
262
262
  | Domains | `shipmail_list_domains`, `shipmail_get_domain`, `shipmail_get_domain_dns_records`, `shipmail_create_domain`, `shipmail_update_domain`, `shipmail_delete_domain`, `shipmail_verify_domain`, `shipmail_search_domains` |
263
263
  | Mailboxes | `shipmail_list_mailboxes`, `shipmail_get_mailbox`, `shipmail_create_mailbox`, `shipmail_update_mailbox`, `shipmail_delete_mailbox`, `shipmail_suspend_mailbox`, `shipmail_resume_mailbox`, `shipmail_list_mailbox_app_passwords`, `shipmail_create_mailbox_app_password`, `shipmail_revoke_mailbox_app_password`, `shipmail_list_mailbox_forwarding`, `shipmail_create_mailbox_forwarding`, `shipmail_delete_mailbox_forwarding`, `shipmail_list_mailbox_folders`, `shipmail_create_mailbox_folder`, `shipmail_update_mailbox_folder`, `shipmail_delete_mailbox_folder`, `shipmail_list_mailbox_rules`, `shipmail_get_mailbox_rule`, `shipmail_create_mailbox_rule`, `shipmail_update_mailbox_rule`, `shipmail_delete_mailbox_rule`, `shipmail_list_mailbox_identities`, `shipmail_reset_mailbox_password`, `shipmail_set_auto_reply`, `shipmail_inject_sandbox_inbound` |
264
- | Mailbox inbox | `shipmail_list_mailbox_inbox_messages`, `shipmail_get_mailbox_inbox_message`, `shipmail_read_mailbox_inbox_attachment`, `shipmail_get_mailbox_inbox_thread`, `shipmail_list_mailbox_inbox_threads`, `shipmail_update_inbox_thread_attention`, `shipmail_create_inbox_reply_draft`, `shipmail_send_inbox_reply_draft`, `shipmail_update_inbox_message`, `shipmail_move_inbox_message`, `shipmail_delete_inbox_message` |
264
+ | Mailbox inbox | `shipmail_list_mailbox_inbox_messages`, `shipmail_get_mailbox_inbox_message`, `shipmail_read_mailbox_inbox_attachment`, `shipmail_get_mailbox_inbox_thread`, `shipmail_list_mailbox_inbox_threads`, `shipmail_update_inbox_thread_attention`, `shipmail_create_draft`, `shipmail_create_inbox_reply_draft`, `shipmail_send_inbox_reply_draft`, `shipmail_update_inbox_message`, `shipmail_move_inbox_message`, `shipmail_delete_inbox_message` |
265
265
  | Reply scans | `shipmail_create_reply_scan`, `shipmail_get_reply_scan`, `shipmail_list_reply_scan_results` |
266
266
  | Messages and threads | `shipmail_list_messages`, `shipmail_list_message_analytics`, `shipmail_get_message`, `shipmail_compose_message_with_file` (hosted MCP Apps), `shipmail_prepare_staged_attachment_upload`, `shipmail_send_message`, `shipmail_list_scheduled_messages`, `shipmail_get_scheduled_message`, `shipmail_update_scheduled_message`, `shipmail_cancel_scheduled_message`, `shipmail_reply_to_message`, `shipmail_list_threads`, `shipmail_get_thread`, `shipmail_reply_to_thread`, `shipmail_reply_to_inbox_message`, `shipmail_reply_to_inbox_thread` |
267
267
  | Webhooks | `shipmail_list_webhooks`, `shipmail_get_webhook`, `shipmail_create_webhook`, `shipmail_update_webhook`, `shipmail_delete_webhook`, `shipmail_rotate_webhook_secret`, `shipmail_test_webhook`, `shipmail_list_webhook_deliveries`, `shipmail_get_webhook_delivery`, `shipmail_replay_webhook_delivery` |
@@ -321,6 +321,10 @@ thread version used to create the draft. `shipmail_send_inbox_reply_draft` rejec
321
321
  the conversation changed before approval. The direct reply tools remain available as lower-level
322
322
  operations for callers that already provide their own review and concurrency controls.
323
323
 
324
+ To start a new conversation without send access, use `shipmail_create_draft`. It saves the message
325
+ to the mailbox's Drafts folder and sends nothing; the user reviews, edits, and sends it from Drafts
326
+ in Shipmail or any mail client. It needs only the `drafts:compose` scope.
327
+
324
328
  With an `sm_test_...` API key, send and reply tools accept `sandbox_outcome` and the sandbox inbound tool creates fake inbound mail. The API keeps test storage and events isolated and never delivers sandbox mail to real recipients.
325
329
 
326
330
  App-password creation, revocation, and partner grant consumption are destructive/high-risk tools.
@@ -387,7 +391,7 @@ Shipmail generates the primary credential and never returns it to the partner.
387
391
  - **Input sanitization**: Email content, addresses, and error text are stripped of ASCII control characters, DEL, and Unicode directional or BiDi markers (U+061C, U+200E/F, U+202A-E, U+2066-9). Long strings are truncated.
388
392
  - **Error redaction**: 5xx and unexpected Shipmail errors are redacted to a generic message; the original `request_id` is preserved for support. Generic `Error` thrown values (network errors, deserialization) are redacted to "Internal MCP error" before reaching the LLM. Detail lands on stderr.
389
393
  - **Circuit breaker**: Each session enforces per-tool rate limits and a hard total-call ceiling as a runaway-agent guard. These are not abuse controls. Real abuse limits live at the API per API key. Restart the server to reset.
390
- - **Least-privilege reply workflow**: Use `messages:read` for discovery, add `drafts:write` for reviewable draft creation, and grant `messages:send` only to agents that are allowed to transmit approved mail. A review-only agent does not need send access.
394
+ - **Least-privilege draft workflow**: Use `messages:read` for discovery, add `drafts:write` for reviewable reply drafts and `drafts:compose` for new-message drafts saved to Drafts, and grant `messages:send` only to agents that are allowed to transmit approved mail. A review-only agent does not need send access: `drafts:compose` saves a draft but can never send it.
391
395
  - **Webhook URL validation**: Webhook URLs must be public https endpoints. Localhost, RFC1918, link-local, ULA, IPv4-mapped IPv6, `0.0.0.0`, decimal-int IPs, `.local`, and `.internal` hosts are rejected at input time.
392
396
  - **Destructive annotations**: Tools that delete, retarget, change persistent rules, rotate, reset credentials, or create automatic outbound responses are annotated with `destructiveHint`. Hosts that gate on this annotation will prompt the user. Annotated tools include `shipmail_update_domain`, `shipmail_update_webhook`, `shipmail_rotate_webhook_secret`, `shipmail_delete_mailbox_rule`, `shipmail_delete_mailbox_folder`, `shipmail_reset_mailbox_password`, and `shipmail_set_auto_reply` in addition to obvious deletes.
393
397
 
@@ -1,9 +1,9 @@
1
1
  import { ApiKeyScope } from 'shipmail/api-key-scopes';
2
2
 
3
3
  declare const MCP_CAPABILITY_VERSION = "1.1.0";
4
- declare const MCP_PERMISSION_GROUP_NAMES: readonly ["account_status", "automations", "mail_read", "drafts", "mail_organize", "send_mail", "calendar_read", "calendar_write", "booking_pages", "forwarding", "mailbox_rules", "members", "webhooks", "credentials", "mailbox_export", "domain_admin", "mailbox_admin", "newsletters", "audiences", "suppressions", "partner_admin"];
4
+ declare const MCP_PERMISSION_GROUP_NAMES: readonly ["account_status", "automations", "mail_read", "drafts", "compose_drafts", "mail_organize", "send_mail", "calendar_read", "calendar_write", "booking_pages", "forwarding", "mailbox_rules", "members", "webhooks", "credentials", "mailbox_export", "domain_admin", "mailbox_admin", "newsletters", "audiences", "suppressions", "partner_admin"];
5
5
  type McpPermissionGroupName = (typeof MCP_PERMISSION_GROUP_NAMES)[number];
6
- declare const MCP_MEMBER_PERMISSION_GROUP_NAMES: readonly ["account_status", "mail_read", "drafts", "mail_organize", "mailbox_rules", "send_mail", "calendar_read", "calendar_write", "automations"];
6
+ declare const MCP_MEMBER_PERMISSION_GROUP_NAMES: readonly ["account_status", "mail_read", "drafts", "compose_drafts", "mail_organize", "mailbox_rules", "send_mail", "calendar_read", "calendar_write", "automations"];
7
7
  type McpRole = "owner" | "member";
8
8
  type McpCapabilityEffect = "read" | "local_write" | "external_write" | "destructive";
9
9
  type McpRecipientControl = "none" | "server_derived" | "caller_supplied";
@@ -40,6 +40,12 @@ declare const MCP_PERMISSION_GROUPS: readonly [{
40
40
  readonly description: "Create reply drafts whose recipients are derived by Shipmail.";
41
41
  readonly scopes: readonly ["drafts:write"];
42
42
  readonly persistent: false;
43
+ }, {
44
+ readonly name: "compose_drafts";
45
+ readonly label: "Compose drafts";
46
+ readonly description: "Save new messages to Drafts for review. Cannot send.";
47
+ readonly scopes: readonly ["drafts:compose"];
48
+ readonly persistent: false;
43
49
  }, {
44
50
  readonly name: "mail_organize";
45
51
  readonly label: "Organize mail";
@@ -143,7 +149,7 @@ declare const MCP_PERMISSION_GROUPS: readonly [{
143
149
  readonly scopes: readonly ["partner:organizations:read", "partner:organizations:write", "partner:organizations:access", "partner:mailbox_credentials:issue", "partner:usage:read"];
144
150
  readonly persistent: true;
145
151
  }];
146
- declare const CAPABILITY_ROWS: readonly [readonly ["shipmail_status", "getStatus", "public"], readonly ["shipmail_list_automations", "listAutomations", "automations:read"], readonly ["shipmail_create_automation", "createAutomation", "automations:write"], readonly ["shipmail_get_automation", "getAutomation", "automations:read"], readonly ["shipmail_update_automation", "updateAutomation", "automations:write"], readonly ["shipmail_run_automation", "runAutomation", "automations:write"], readonly ["shipmail_create_domain", "createDomain", "domains:write"], readonly ["shipmail_list_domains", "listDomains", "domains:read"], readonly ["shipmail_get_domain", "getDomain", "domains:read"], readonly ["shipmail_get_domain_dns_records", "getDomainDnsRecords", "domains:read"], readonly ["shipmail_update_domain", "updateDomain", "domains:write"], readonly ["shipmail_delete_domain", "deleteDomain", "domains:write"], readonly ["shipmail_verify_domain", "verifyDomain", "domains:write"], readonly ["shipmail_search_domains", "searchDomains", "domains:read"], readonly ["shipmail_create_mailbox", "createMailbox", "mailboxes:write"], readonly ["shipmail_list_mailboxes", "listMailboxes", "mailboxes:read"], readonly ["shipmail_get_mailbox", "getMailbox", "mailboxes:read"], readonly ["shipmail_list_mailbox_app_passwords", "listMailboxAppPasswords", "mailbox_credentials:read"], readonly ["shipmail_create_mailbox_app_password", "createMailboxAppPassword", "mailbox_credentials:write"], readonly ["shipmail_revoke_mailbox_app_password", "revokeMailboxAppPassword", "mailbox_credentials:write"], readonly ["shipmail_suspend_mailbox", "suspendMailbox", "mailboxes:write"], readonly ["shipmail_resume_mailbox", "resumeMailbox", "mailboxes:write"], readonly ["shipmail_create_mailbox_export", "createMailboxExport", "mailboxes:export"], readonly ["shipmail_get_mailbox_export", "getMailboxExport", "mailboxes:export"], readonly ["shipmail_update_mailbox", "updateMailbox", "mailboxes:write"], readonly ["shipmail_delete_mailbox", "deleteMailbox", "mailboxes:write"], readonly ["shipmail_list_mailbox_folders", "listMailboxFolders", "mailboxes:read"], readonly ["shipmail_create_mailbox_folder", "createMailboxFolder", "messages:write"], readonly ["shipmail_update_mailbox_folder", "updateMailboxFolder", "messages:write"], readonly ["shipmail_delete_mailbox_folder", "deleteMailboxFolder", "messages:write"], readonly ["shipmail_list_mailbox_rules", "getMailboxRules", "mailbox_rules:read"], readonly ["shipmail_get_mailbox_rule", "getMailboxRules", "mailbox_rules:read"], readonly ["shipmail_create_mailbox_rule", "updateMailboxRules", "mailbox_rules:write"], readonly ["shipmail_update_mailbox_rule", "updateMailboxRules", "mailbox_rules:write"], readonly ["shipmail_delete_mailbox_rule", "updateMailboxRules", "mailbox_rules:write"], readonly ["shipmail_list_mailbox_identities", "listMailboxIdentities", "mailboxes:read"], readonly ["shipmail_list_mailbox_inbox_messages", "listMailboxInboxMessages", "messages:read"], readonly ["shipmail_get_mailbox_inbox_message", "getMailboxInboxMessage", "messages:read"], readonly ["shipmail_read_mailbox_inbox_attachment", "downloadMailboxInboxAttachment", "messages:read"], readonly ["shipmail_list_mailbox_inbox_threads", "listMailboxInboxThreads", "messages:read"], readonly ["shipmail_get_mailbox_inbox_thread", "getMailboxInboxThread", "messages:read"], readonly ["shipmail_update_inbox_thread_attention", "updateMailboxInboxThreadAttention", "messages:write"], readonly ["shipmail_create_inbox_reply_draft", "createMailboxInboxReplyDraft", "drafts:write"], readonly ["shipmail_send_inbox_reply_draft", "sendMailboxInboxReplyDraft", "messages:send"], readonly ["shipmail_reply_to_inbox_message", "replyToMailboxInboxMessage", "messages:send"], readonly ["shipmail_reply_to_inbox_thread", "replyToMailboxInboxThread", "messages:send"], readonly ["shipmail_update_inbox_message", "updateMailboxInboxMessage", "messages:write"], readonly ["shipmail_move_inbox_message", "moveMailboxInboxMessage", "messages:write"], readonly ["shipmail_delete_inbox_message", "deleteMailboxInboxMessage", "messages:write"], readonly ["shipmail_list_mailbox_forwarding", "listMailboxForwarding", "mailbox_forwarding:read"], readonly ["shipmail_create_mailbox_forwarding", "createMailboxForwarding", "mailbox_forwarding:write"], readonly ["shipmail_delete_mailbox_forwarding", "deleteMailboxForwarding", "mailbox_forwarding:write"], readonly ["shipmail_reset_mailbox_password", "resetMailboxPassword", "mailboxes:write"], readonly ["shipmail_set_auto_reply", "updateAutoReply", "mailboxes:write"], readonly ["shipmail_get_mailbox_delivery_routing", "getMailboxDeliveryRouting", "mailboxes:read"], readonly ["shipmail_update_mailbox_delivery_routing", "updateMailboxDeliveryRouting", "mailboxes:write"], readonly ["shipmail_create_mailbox_import", "createMailboxImport", "mailboxes:write"], readonly ["shipmail_list_mailbox_imports", "listMailboxImports", "mailboxes:read"], readonly ["shipmail_get_mailbox_import", "getMailboxImport", "mailboxes:read"], readonly ["shipmail_cancel_mailbox_import", "cancelMailboxImport", "mailboxes:write"], readonly ["shipmail_resume_mailbox_import", "resumeMailboxImport", "mailboxes:write"], readonly ["shipmail_restore_mailbox_import", "restoreMailboxImport", "mailboxes:write"], readonly ["shipmail_undo_mailbox_import", "undoMailboxImport", "mailboxes:write"], readonly ["shipmail_inject_sandbox_inbound", "injectSandboxInbound", "messages:write"], readonly ["shipmail_list_messages", "listMessages", "messages:read"], readonly ["shipmail_list_message_analytics", "listMessageAnalytics", "messages:read"], readonly ["shipmail_compose_message_with_file", "prepareStagedAttachmentUpload", "messages:send"], readonly ["shipmail_prepare_staged_attachment_upload", "prepareStagedAttachmentUpload", "messages:send"], readonly ["shipmail_send_message", "sendMessage", "messages:send"], readonly ["shipmail_list_scheduled_messages", "listScheduledMessages", "messages:read"], readonly ["shipmail_get_scheduled_message", "getScheduledMessage", "messages:read"], readonly ["shipmail_update_scheduled_message", "updateScheduledMessage", "messages:send"], readonly ["shipmail_cancel_scheduled_message", "cancelScheduledMessage", "messages:send"], readonly ["shipmail_get_message", "getMessage", "messages:read"], readonly ["shipmail_reply_to_message", "replyToMessage", "messages:send"], readonly ["shipmail_list_threads", "listThreads", "threads:read"], readonly ["shipmail_get_thread", "getThread", "threads:read"], readonly ["shipmail_reply_to_thread", "replyToThread", "messages:send"], readonly ["shipmail_create_reply_scan", "createReplyScan", "messages:write"], readonly ["shipmail_get_reply_scan", "getReplyScan", "messages:read"], readonly ["shipmail_list_reply_scan_results", "listReplyScanResults", "messages:read"], readonly ["shipmail_list_members", "listMembers", "members:read"], readonly ["shipmail_get_member", "getMember", "members:read"], readonly ["shipmail_create_webhook", "createWebhook", "webhooks:write"], readonly ["shipmail_list_webhooks", "listWebhooks", "webhooks:read"], readonly ["shipmail_get_webhook", "getWebhook", "webhooks:read"], readonly ["shipmail_update_webhook", "updateWebhook", "webhooks:write"], readonly ["shipmail_delete_webhook", "deleteWebhook", "webhooks:write"], readonly ["shipmail_rotate_webhook_secret", "rotateWebhookSecret", "webhooks:write"], readonly ["shipmail_test_webhook", "testWebhook", "webhooks:write"], readonly ["shipmail_list_webhook_deliveries", "listWebhookDeliveries", "webhooks:read"], readonly ["shipmail_get_webhook_delivery", "getWebhookDelivery", "webhooks:read"], readonly ["shipmail_replay_webhook_delivery", "replayWebhookDelivery", "webhooks:write"], readonly ["shipmail_list_suppressions", "listSuppressions", "suppressions:read"], readonly ["shipmail_remove_suppression", "removeSuppression", "suppressions:write"], readonly ["shipmail_create_audience", "createAudience", "audiences:write"], readonly ["shipmail_list_audiences", "listAudiences", "audiences:read"], readonly ["shipmail_get_audience", "getAudience", "audiences:read"], readonly ["shipmail_update_audience", "updateAudience", "audiences:write"], readonly ["shipmail_delete_audience", "deleteAudience", "audiences:write"], readonly ["shipmail_get_audience_feed", "getAudienceFeed", "audiences:read"], readonly ["shipmail_update_audience_feed", "updateAudienceFeed", "audiences:write"], readonly ["shipmail_rotate_audience_feed", "rotateAudienceFeedToken", "audiences:write"], readonly ["shipmail_revoke_audience_feed", "revokeAudienceFeedToken", "audiences:write"], readonly ["shipmail_add_subscriber", "addSubscriber", "audiences:write"], readonly ["shipmail_add_subscribers_batch", "addSubscribersBatch", "audiences:write"], readonly ["shipmail_list_subscribers", "listSubscribers", "audiences:read"], readonly ["shipmail_get_subscriber", "getSubscriber", "audiences:read"], readonly ["shipmail_get_subscriber_by_email", "getSubscriberByEmail", "audiences:read"], readonly ["shipmail_update_subscriber", "updateSubscriber", "audiences:write"], readonly ["shipmail_unsubscribe_subscriber", "unsubscribeSubscriber", "audiences:write"], readonly ["shipmail_resubscribe_subscriber", "resubscribeSubscriber", "audiences:write"], readonly ["shipmail_remove_subscriber", "removeSubscriber", "audiences:write"], readonly ["shipmail_list_newsletter_domains", "listNewsletterDomains", "newsletters:read"], readonly ["shipmail_list_newsletter_sender_identities", "listNewsletterSenderIdentities", "newsletters:read"], readonly ["shipmail_create_newsletter", "createNewsletter", "newsletters:write"], readonly ["shipmail_create_newsletter_from_changelog", "createNewsletterFromChangelog", "newsletters:write"], readonly ["shipmail_list_newsletters", "listNewsletters", "newsletters:read"], readonly ["shipmail_get_newsletter", "getNewsletter", "newsletters:read"], readonly ["shipmail_update_newsletter", "updateNewsletter", "newsletters:write"], readonly ["shipmail_list_newsletter_assets", "listNewsletterAssets", "newsletters:read"], readonly ["shipmail_upload_newsletter_asset_with_file", "prepareNewsletterAssetUpload", "newsletters:write"], readonly ["shipmail_prepare_newsletter_asset_upload", "prepareNewsletterAssetUpload", "newsletters:write"], readonly ["shipmail_register_newsletter_asset", "uploadNewsletterAsset", "newsletters:write"], readonly ["shipmail_preview_newsletter", "previewNewsletter", "newsletters:read"], readonly ["shipmail_run_newsletter_preflight", "runNewsletterPreflight", "newsletters:write"], readonly ["shipmail_send_newsletter_test", "sendNewsletterTest", "newsletters:write"], readonly ["shipmail_schedule_newsletter", "scheduleNewsletter", "newsletters:write"], readonly ["shipmail_cancel_newsletter", "cancelNewsletter", "newsletters:write"], readonly ["shipmail_resume_newsletter", "resumeNewsletter", "newsletters:write"], readonly ["shipmail_list_calendar_events", "listCalendarEvents", "calendar:read"], readonly ["shipmail_create_calendar_event", "createCalendarEvent", "calendar:write"], readonly ["shipmail_get_calendar_event", "getCalendarEvent", "calendar:read"], readonly ["shipmail_update_calendar_event", "updateCalendarEvent", "calendar:write"], readonly ["shipmail_delete_calendar_event", "deleteCalendarEvent", "calendar:write"], readonly ["shipmail_get_calendar_availability", "getCalendarAvailability", "calendar:read"], readonly ["shipmail_list_booking_pages", "listBookingPages", "booking_pages:read"], readonly ["shipmail_create_booking_page", "createBookingPage", "booking_pages:write"], readonly ["shipmail_get_booking_page", "getBookingPage", "booking_pages:read"], readonly ["shipmail_update_booking_page", "updateBookingPage", "booking_pages:write"], readonly ["shipmail_delete_booking_page", "deleteBookingPage", "booking_pages:write"], readonly ["shipmail_list_partner_organizations", "listPartnerOrganizations", "partner:organizations:read"], readonly ["shipmail_create_partner_organization", "createPartnerOrganization", "partner:organizations:write"], readonly ["shipmail_get_partner_organization", "getPartnerOrganization", "partner:organizations:read"], readonly ["shipmail_update_partner_organization", "updatePartnerOrganization", "partner:organizations:write"], readonly ["shipmail_resend_partner_ownership_invitation", "resendPartnerOwnershipInvitation", "partner:organizations:write"], readonly ["shipmail_suspend_partner_organization", "suspendPartnerOrganization", "partner:organizations:write"], readonly ["shipmail_resume_partner_organization", "resumePartnerOrganization", "partner:organizations:write"], readonly ["shipmail_offboard_partner_organization", "offboardPartnerOrganization", "partner:organizations:write"], readonly ["shipmail_list_partner_mailbox_credential_grants", "listPartnerMailboxCredentialGrants", "partner:mailbox_credentials:issue"], readonly ["shipmail_consume_partner_mailbox_credential_grant", "consumePartnerMailboxCredentialGrant", "partner:mailbox_credentials:issue"], readonly ["shipmail_get_partner_usage", "getPartnerUsage", "partner:usage:read"]];
152
+ declare const CAPABILITY_ROWS: readonly [readonly ["shipmail_status", "getStatus", "public"], readonly ["shipmail_list_automations", "listAutomations", "automations:read"], readonly ["shipmail_create_automation", "createAutomation", "automations:write"], readonly ["shipmail_get_automation", "getAutomation", "automations:read"], readonly ["shipmail_update_automation", "updateAutomation", "automations:write"], readonly ["shipmail_run_automation", "runAutomation", "automations:write"], readonly ["shipmail_create_domain", "createDomain", "domains:write"], readonly ["shipmail_list_domains", "listDomains", "domains:read"], readonly ["shipmail_get_domain", "getDomain", "domains:read"], readonly ["shipmail_get_domain_dns_records", "getDomainDnsRecords", "domains:read"], readonly ["shipmail_update_domain", "updateDomain", "domains:write"], readonly ["shipmail_delete_domain", "deleteDomain", "domains:write"], readonly ["shipmail_verify_domain", "verifyDomain", "domains:write"], readonly ["shipmail_search_domains", "searchDomains", "domains:read"], readonly ["shipmail_create_mailbox", "createMailbox", "mailboxes:write"], readonly ["shipmail_list_mailboxes", "listMailboxes", "mailboxes:read"], readonly ["shipmail_get_mailbox", "getMailbox", "mailboxes:read"], readonly ["shipmail_list_mailbox_app_passwords", "listMailboxAppPasswords", "mailbox_credentials:read"], readonly ["shipmail_create_mailbox_app_password", "createMailboxAppPassword", "mailbox_credentials:write"], readonly ["shipmail_revoke_mailbox_app_password", "revokeMailboxAppPassword", "mailbox_credentials:write"], readonly ["shipmail_suspend_mailbox", "suspendMailbox", "mailboxes:write"], readonly ["shipmail_resume_mailbox", "resumeMailbox", "mailboxes:write"], readonly ["shipmail_create_mailbox_export", "createMailboxExport", "mailboxes:export"], readonly ["shipmail_get_mailbox_export", "getMailboxExport", "mailboxes:export"], readonly ["shipmail_update_mailbox", "updateMailbox", "mailboxes:write"], readonly ["shipmail_delete_mailbox", "deleteMailbox", "mailboxes:write"], readonly ["shipmail_list_mailbox_folders", "listMailboxFolders", "mailboxes:read"], readonly ["shipmail_create_mailbox_folder", "createMailboxFolder", "messages:write"], readonly ["shipmail_update_mailbox_folder", "updateMailboxFolder", "messages:write"], readonly ["shipmail_delete_mailbox_folder", "deleteMailboxFolder", "messages:write"], readonly ["shipmail_list_mailbox_rules", "getMailboxRules", "mailbox_rules:read"], readonly ["shipmail_get_mailbox_rule", "getMailboxRules", "mailbox_rules:read"], readonly ["shipmail_create_mailbox_rule", "updateMailboxRules", "mailbox_rules:write"], readonly ["shipmail_update_mailbox_rule", "updateMailboxRules", "mailbox_rules:write"], readonly ["shipmail_delete_mailbox_rule", "updateMailboxRules", "mailbox_rules:write"], readonly ["shipmail_list_mailbox_identities", "listMailboxIdentities", "mailboxes:read"], readonly ["shipmail_list_mailbox_inbox_messages", "listMailboxInboxMessages", "messages:read"], readonly ["shipmail_get_mailbox_inbox_message", "getMailboxInboxMessage", "messages:read"], readonly ["shipmail_read_mailbox_inbox_attachment", "downloadMailboxInboxAttachment", "messages:read"], readonly ["shipmail_list_mailbox_inbox_threads", "listMailboxInboxThreads", "messages:read"], readonly ["shipmail_get_mailbox_inbox_thread", "getMailboxInboxThread", "messages:read"], readonly ["shipmail_update_inbox_thread_attention", "updateMailboxInboxThreadAttention", "messages:write"], readonly ["shipmail_create_draft", "createMailboxInboxDraft", "drafts:compose"], readonly ["shipmail_create_inbox_reply_draft", "createMailboxInboxReplyDraft", "drafts:write"], readonly ["shipmail_send_inbox_reply_draft", "sendMailboxInboxReplyDraft", "messages:send"], readonly ["shipmail_reply_to_inbox_message", "replyToMailboxInboxMessage", "messages:send"], readonly ["shipmail_reply_to_inbox_thread", "replyToMailboxInboxThread", "messages:send"], readonly ["shipmail_update_inbox_message", "updateMailboxInboxMessage", "messages:write"], readonly ["shipmail_move_inbox_message", "moveMailboxInboxMessage", "messages:write"], readonly ["shipmail_delete_inbox_message", "deleteMailboxInboxMessage", "messages:write"], readonly ["shipmail_list_mailbox_forwarding", "listMailboxForwarding", "mailbox_forwarding:read"], readonly ["shipmail_create_mailbox_forwarding", "createMailboxForwarding", "mailbox_forwarding:write"], readonly ["shipmail_delete_mailbox_forwarding", "deleteMailboxForwarding", "mailbox_forwarding:write"], readonly ["shipmail_reset_mailbox_password", "resetMailboxPassword", "mailboxes:write"], readonly ["shipmail_set_auto_reply", "updateAutoReply", "mailboxes:write"], readonly ["shipmail_get_mailbox_delivery_routing", "getMailboxDeliveryRouting", "mailboxes:read"], readonly ["shipmail_update_mailbox_delivery_routing", "updateMailboxDeliveryRouting", "mailboxes:write"], readonly ["shipmail_create_mailbox_import", "createMailboxImport", "mailboxes:write"], readonly ["shipmail_list_mailbox_imports", "listMailboxImports", "mailboxes:read"], readonly ["shipmail_get_mailbox_import", "getMailboxImport", "mailboxes:read"], readonly ["shipmail_cancel_mailbox_import", "cancelMailboxImport", "mailboxes:write"], readonly ["shipmail_resume_mailbox_import", "resumeMailboxImport", "mailboxes:write"], readonly ["shipmail_restore_mailbox_import", "restoreMailboxImport", "mailboxes:write"], readonly ["shipmail_undo_mailbox_import", "undoMailboxImport", "mailboxes:write"], readonly ["shipmail_inject_sandbox_inbound", "injectSandboxInbound", "messages:write"], readonly ["shipmail_list_messages", "listMessages", "messages:read"], readonly ["shipmail_list_message_analytics", "listMessageAnalytics", "messages:read"], readonly ["shipmail_compose_message_with_file", "prepareStagedAttachmentUpload", "messages:send"], readonly ["shipmail_prepare_staged_attachment_upload", "prepareStagedAttachmentUpload", "messages:send"], readonly ["shipmail_send_message", "sendMessage", "messages:send"], readonly ["shipmail_list_scheduled_messages", "listScheduledMessages", "messages:read"], readonly ["shipmail_get_scheduled_message", "getScheduledMessage", "messages:read"], readonly ["shipmail_update_scheduled_message", "updateScheduledMessage", "messages:send"], readonly ["shipmail_cancel_scheduled_message", "cancelScheduledMessage", "messages:send"], readonly ["shipmail_get_message", "getMessage", "messages:read"], readonly ["shipmail_reply_to_message", "replyToMessage", "messages:send"], readonly ["shipmail_list_threads", "listThreads", "threads:read"], readonly ["shipmail_get_thread", "getThread", "threads:read"], readonly ["shipmail_reply_to_thread", "replyToThread", "messages:send"], readonly ["shipmail_create_reply_scan", "createReplyScan", "messages:write"], readonly ["shipmail_get_reply_scan", "getReplyScan", "messages:read"], readonly ["shipmail_list_reply_scan_results", "listReplyScanResults", "messages:read"], readonly ["shipmail_list_members", "listMembers", "members:read"], readonly ["shipmail_get_member", "getMember", "members:read"], readonly ["shipmail_create_webhook", "createWebhook", "webhooks:write"], readonly ["shipmail_list_webhooks", "listWebhooks", "webhooks:read"], readonly ["shipmail_get_webhook", "getWebhook", "webhooks:read"], readonly ["shipmail_update_webhook", "updateWebhook", "webhooks:write"], readonly ["shipmail_delete_webhook", "deleteWebhook", "webhooks:write"], readonly ["shipmail_rotate_webhook_secret", "rotateWebhookSecret", "webhooks:write"], readonly ["shipmail_test_webhook", "testWebhook", "webhooks:write"], readonly ["shipmail_list_webhook_deliveries", "listWebhookDeliveries", "webhooks:read"], readonly ["shipmail_get_webhook_delivery", "getWebhookDelivery", "webhooks:read"], readonly ["shipmail_replay_webhook_delivery", "replayWebhookDelivery", "webhooks:write"], readonly ["shipmail_list_suppressions", "listSuppressions", "suppressions:read"], readonly ["shipmail_remove_suppression", "removeSuppression", "suppressions:write"], readonly ["shipmail_create_audience", "createAudience", "audiences:write"], readonly ["shipmail_list_audiences", "listAudiences", "audiences:read"], readonly ["shipmail_get_audience", "getAudience", "audiences:read"], readonly ["shipmail_update_audience", "updateAudience", "audiences:write"], readonly ["shipmail_delete_audience", "deleteAudience", "audiences:write"], readonly ["shipmail_get_audience_feed", "getAudienceFeed", "audiences:read"], readonly ["shipmail_update_audience_feed", "updateAudienceFeed", "audiences:write"], readonly ["shipmail_rotate_audience_feed", "rotateAudienceFeedToken", "audiences:write"], readonly ["shipmail_revoke_audience_feed", "revokeAudienceFeedToken", "audiences:write"], readonly ["shipmail_add_subscriber", "addSubscriber", "audiences:write"], readonly ["shipmail_add_subscribers_batch", "addSubscribersBatch", "audiences:write"], readonly ["shipmail_list_subscribers", "listSubscribers", "audiences:read"], readonly ["shipmail_get_subscriber", "getSubscriber", "audiences:read"], readonly ["shipmail_get_subscriber_by_email", "getSubscriberByEmail", "audiences:read"], readonly ["shipmail_update_subscriber", "updateSubscriber", "audiences:write"], readonly ["shipmail_unsubscribe_subscriber", "unsubscribeSubscriber", "audiences:write"], readonly ["shipmail_resubscribe_subscriber", "resubscribeSubscriber", "audiences:write"], readonly ["shipmail_remove_subscriber", "removeSubscriber", "audiences:write"], readonly ["shipmail_list_newsletter_domains", "listNewsletterDomains", "newsletters:read"], readonly ["shipmail_list_newsletter_sender_identities", "listNewsletterSenderIdentities", "newsletters:read"], readonly ["shipmail_create_newsletter", "createNewsletter", "newsletters:write"], readonly ["shipmail_create_newsletter_from_changelog", "createNewsletterFromChangelog", "newsletters:write"], readonly ["shipmail_list_newsletters", "listNewsletters", "newsletters:read"], readonly ["shipmail_get_newsletter", "getNewsletter", "newsletters:read"], readonly ["shipmail_update_newsletter", "updateNewsletter", "newsletters:write"], readonly ["shipmail_list_newsletter_assets", "listNewsletterAssets", "newsletters:read"], readonly ["shipmail_upload_newsletter_asset_with_file", "prepareNewsletterAssetUpload", "newsletters:write"], readonly ["shipmail_prepare_newsletter_asset_upload", "prepareNewsletterAssetUpload", "newsletters:write"], readonly ["shipmail_register_newsletter_asset", "uploadNewsletterAsset", "newsletters:write"], readonly ["shipmail_preview_newsletter", "previewNewsletter", "newsletters:read"], readonly ["shipmail_run_newsletter_preflight", "runNewsletterPreflight", "newsletters:write"], readonly ["shipmail_send_newsletter_test", "sendNewsletterTest", "newsletters:write"], readonly ["shipmail_schedule_newsletter", "scheduleNewsletter", "newsletters:write"], readonly ["shipmail_cancel_newsletter", "cancelNewsletter", "newsletters:write"], readonly ["shipmail_resume_newsletter", "resumeNewsletter", "newsletters:write"], readonly ["shipmail_list_calendar_events", "listCalendarEvents", "calendar:read"], readonly ["shipmail_create_calendar_event", "createCalendarEvent", "calendar:write"], readonly ["shipmail_get_calendar_event", "getCalendarEvent", "calendar:read"], readonly ["shipmail_update_calendar_event", "updateCalendarEvent", "calendar:write"], readonly ["shipmail_delete_calendar_event", "deleteCalendarEvent", "calendar:write"], readonly ["shipmail_get_calendar_availability", "getCalendarAvailability", "calendar:read"], readonly ["shipmail_list_booking_pages", "listBookingPages", "booking_pages:read"], readonly ["shipmail_create_booking_page", "createBookingPage", "booking_pages:write"], readonly ["shipmail_get_booking_page", "getBookingPage", "booking_pages:read"], readonly ["shipmail_update_booking_page", "updateBookingPage", "booking_pages:write"], readonly ["shipmail_delete_booking_page", "deleteBookingPage", "booking_pages:write"], readonly ["shipmail_list_partner_organizations", "listPartnerOrganizations", "partner:organizations:read"], readonly ["shipmail_create_partner_organization", "createPartnerOrganization", "partner:organizations:write"], readonly ["shipmail_get_partner_organization", "getPartnerOrganization", "partner:organizations:read"], readonly ["shipmail_update_partner_organization", "updatePartnerOrganization", "partner:organizations:write"], readonly ["shipmail_resend_partner_ownership_invitation", "resendPartnerOwnershipInvitation", "partner:organizations:write"], readonly ["shipmail_suspend_partner_organization", "suspendPartnerOrganization", "partner:organizations:write"], readonly ["shipmail_resume_partner_organization", "resumePartnerOrganization", "partner:organizations:write"], readonly ["shipmail_offboard_partner_organization", "offboardPartnerOrganization", "partner:organizations:write"], readonly ["shipmail_list_partner_mailbox_credential_grants", "listPartnerMailboxCredentialGrants", "partner:mailbox_credentials:issue"], readonly ["shipmail_consume_partner_mailbox_credential_grant", "consumePartnerMailboxCredentialGrant", "partner:mailbox_credentials:issue"], readonly ["shipmail_get_partner_usage", "getPartnerUsage", "partner:usage:read"]];
147
153
  type McpToolName = (typeof CAPABILITY_ROWS)[number][0];
148
154
  type McpCapability = {
149
155
  readonly toolName: McpToolName;
@@ -6,6 +6,7 @@ var MCP_PERMISSION_GROUP_NAMES = [
6
6
  "automations",
7
7
  "mail_read",
8
8
  "drafts",
9
+ "compose_drafts",
9
10
  "mail_organize",
10
11
  "send_mail",
11
12
  "calendar_read",
@@ -28,6 +29,7 @@ var MCP_MEMBER_PERMISSION_GROUP_NAMES = [
28
29
  "account_status",
29
30
  "mail_read",
30
31
  "drafts",
32
+ "compose_drafts",
31
33
  "mail_organize",
32
34
  "mailbox_rules",
33
35
  "send_mail",
@@ -64,6 +66,13 @@ var MCP_PERMISSION_GROUPS = [
64
66
  scopes: ["drafts:write"],
65
67
  persistent: false
66
68
  },
69
+ {
70
+ name: "compose_drafts",
71
+ label: "Compose drafts",
72
+ description: "Save new messages to Drafts for review. Cannot send.",
73
+ scopes: ["drafts:compose"],
74
+ persistent: false
75
+ },
67
76
  {
68
77
  name: "mail_organize",
69
78
  label: "Organize mail",
@@ -233,6 +242,7 @@ var CAPABILITY_ROWS = [
233
242
  ["shipmail_list_mailbox_inbox_threads", "listMailboxInboxThreads", "messages:read"],
234
243
  ["shipmail_get_mailbox_inbox_thread", "getMailboxInboxThread", "messages:read"],
235
244
  ["shipmail_update_inbox_thread_attention", "updateMailboxInboxThreadAttention", "messages:write"],
245
+ ["shipmail_create_draft", "createMailboxInboxDraft", "drafts:compose"],
236
246
  ["shipmail_create_inbox_reply_draft", "createMailboxInboxReplyDraft", "drafts:write"],
237
247
  ["shipmail_send_inbox_reply_draft", "sendMailboxInboxReplyDraft", "messages:send"],
238
248
  ["shipmail_reply_to_inbox_message", "replyToMailboxInboxMessage", "messages:send"],
@@ -433,6 +443,7 @@ var SERVER_DERIVED_RECIPIENT_TOOLS = /* @__PURE__ */ new Set([
433
443
  ]);
434
444
  var CALLER_SUPPLIED_RECIPIENT_TOOLS = /* @__PURE__ */ new Set([
435
445
  "shipmail_send_message",
446
+ "shipmail_create_draft",
436
447
  "shipmail_create_mailbox_forwarding",
437
448
  "shipmail_create_webhook",
438
449
  "shipmail_update_webhook",
@@ -455,6 +466,7 @@ function permissionGroupFor(toolName, requiredScope) {
455
466
  if (requiredScope.startsWith("mailbox_rules:")) return "mailbox_rules";
456
467
  if (requiredScope === "mailboxes:export") return "mailbox_export";
457
468
  if (requiredScope === "drafts:write") return "drafts";
469
+ if (requiredScope === "drafts:compose") return "compose_drafts";
458
470
  if (requiredScope === "messages:send") return "send_mail";
459
471
  if (requiredScope === "messages:write") return "mail_organize";
460
472
  if (requiredScope === "messages:read" || requiredScope === "threads:read") return "mail_read";
package/dist/index.js CHANGED
@@ -111,6 +111,7 @@ var MCP_MEMBER_PERMISSION_GROUP_NAMES = [
111
111
  "account_status",
112
112
  "mail_read",
113
113
  "drafts",
114
+ "compose_drafts",
114
115
  "mail_organize",
115
116
  "mailbox_rules",
116
117
  "send_mail",
@@ -147,6 +148,13 @@ var MCP_PERMISSION_GROUPS = [
147
148
  scopes: ["drafts:write"],
148
149
  persistent: false
149
150
  },
151
+ {
152
+ name: "compose_drafts",
153
+ label: "Compose drafts",
154
+ description: "Save new messages to Drafts for review. Cannot send.",
155
+ scopes: ["drafts:compose"],
156
+ persistent: false
157
+ },
150
158
  {
151
159
  name: "mail_organize",
152
160
  label: "Organize mail",
@@ -316,6 +324,7 @@ var CAPABILITY_ROWS = [
316
324
  ["shipmail_list_mailbox_inbox_threads", "listMailboxInboxThreads", "messages:read"],
317
325
  ["shipmail_get_mailbox_inbox_thread", "getMailboxInboxThread", "messages:read"],
318
326
  ["shipmail_update_inbox_thread_attention", "updateMailboxInboxThreadAttention", "messages:write"],
327
+ ["shipmail_create_draft", "createMailboxInboxDraft", "drafts:compose"],
319
328
  ["shipmail_create_inbox_reply_draft", "createMailboxInboxReplyDraft", "drafts:write"],
320
329
  ["shipmail_send_inbox_reply_draft", "sendMailboxInboxReplyDraft", "messages:send"],
321
330
  ["shipmail_reply_to_inbox_message", "replyToMailboxInboxMessage", "messages:send"],
@@ -516,6 +525,7 @@ var SERVER_DERIVED_RECIPIENT_TOOLS = /* @__PURE__ */ new Set([
516
525
  ]);
517
526
  var CALLER_SUPPLIED_RECIPIENT_TOOLS = /* @__PURE__ */ new Set([
518
527
  "shipmail_send_message",
528
+ "shipmail_create_draft",
519
529
  "shipmail_create_mailbox_forwarding",
520
530
  "shipmail_create_webhook",
521
531
  "shipmail_update_webhook",
@@ -538,6 +548,7 @@ function permissionGroupFor(toolName, requiredScope) {
538
548
  if (requiredScope.startsWith("mailbox_rules:")) return "mailbox_rules";
539
549
  if (requiredScope === "mailboxes:export") return "mailbox_export";
540
550
  if (requiredScope === "drafts:write") return "drafts";
551
+ if (requiredScope === "drafts:compose") return "compose_drafts";
541
552
  if (requiredScope === "messages:send") return "send_mail";
542
553
  if (requiredScope === "messages:write") return "mail_organize";
543
554
  if (requiredScope === "messages:read" || requiredScope === "threads:read") return "mail_read";
@@ -661,6 +672,15 @@ var CLOSING_KEYWORDS = /* @__PURE__ */ new Set(["additionalProperties", "unevalu
661
672
  function isSchemaNode(value) {
662
673
  return typeof value === "object" && value !== null && !Array.isArray(value);
663
674
  }
675
+ function isStringTyped(node) {
676
+ const type = node["type"];
677
+ if (type === void 0) return true;
678
+ if (Array.isArray(type)) return type.includes("string");
679
+ return type === "string";
680
+ }
681
+ function isStringEnum(value) {
682
+ return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "string");
683
+ }
664
684
  function openSchemaValue(value) {
665
685
  if (Array.isArray(value)) return value.map((entry) => openSchemaValue(entry));
666
686
  return isSchemaNode(value) ? openSchemaNode(value) : value;
@@ -675,15 +695,25 @@ function openSchemaRecord(value) {
675
695
  }
676
696
  function openSchemaNode(node) {
677
697
  const opened = {};
698
+ const stringTyped = isStringTyped(node);
699
+ const mergeUnion = Object.hasOwn(node, "oneOf");
700
+ const union = [];
678
701
  for (const keyword of Object.getOwnPropertyNames(node)) {
679
702
  const value = node[keyword];
680
703
  if (value === false && CLOSING_KEYWORDS.has(keyword)) continue;
704
+ if (stringTyped && keyword === "enum" && isStringEnum(value)) continue;
705
+ if (stringTyped && keyword === "const" && typeof value === "string") continue;
706
+ if (mergeUnion && (keyword === "oneOf" || keyword === "anyOf") && Array.isArray(value)) {
707
+ union.push(...value.map((branch) => openSchemaValue(branch)));
708
+ continue;
709
+ }
681
710
  if (SCHEMA_RECORD_KEYWORDS.has(keyword)) {
682
711
  opened[keyword] = openSchemaRecord(value);
683
712
  continue;
684
713
  }
685
714
  opened[keyword] = SCHEMA_KEYWORDS.has(keyword) ? openSchemaValue(value) : value;
686
715
  }
716
+ if (union.length > 0) opened["anyOf"] = union;
687
717
  return opened;
688
718
  }
689
719
  function openJsonSchemaObjects(schema) {
@@ -1518,6 +1548,19 @@ var inboxReplyDraftSchema = z.object({
1518
1548
  cc: z.array(replyDraftRecipientSchema),
1519
1549
  created_at: z.string()
1520
1550
  });
1551
+ var inboxDraftSchema = z.object({
1552
+ object: z.literal("inbox_draft"),
1553
+ id: z.string().describe(
1554
+ "Inbox message ID of the saved draft. Read it with shipmail_get_mailbox_inbox_message."
1555
+ ),
1556
+ mailbox_id: z.string(),
1557
+ from: replyDraftRecipientSchema,
1558
+ to: z.array(replyDraftRecipientSchema),
1559
+ cc: z.array(replyDraftRecipientSchema),
1560
+ bcc: z.array(replyDraftRecipientSchema),
1561
+ subject: z.string(),
1562
+ created_at: z.string()
1563
+ });
1521
1564
  var inboxReplyDraftSendSchema = z.object({
1522
1565
  object: z.literal("inbox_reply_draft_send"),
1523
1566
  draft_id: z.string(),
@@ -1890,6 +1933,7 @@ var inboxThreadOutputSchema = z.object({ inbox_thread: inboxThreadSchema });
1890
1933
  var inboxThreadAttentionOutputSchema = z.object({
1891
1934
  inbox_thread_attention: inboxThreadAttentionResultSchema
1892
1935
  });
1936
+ var inboxDraftOutputSchema = z.object({ inbox_draft: inboxDraftSchema });
1893
1937
  var inboxReplyDraftOutputSchema = z.object({ inbox_reply_draft: inboxReplyDraftSchema });
1894
1938
  var inboxReplyDraftSendOutputSchema = z.object({
1895
1939
  inbox_reply_draft_send: inboxReplyDraftSendSchema
@@ -2032,7 +2076,8 @@ var importFolderSchema = z.object({
2032
2076
  source_folder: z.string(),
2033
2077
  target_folder: z.string().nullable(),
2034
2078
  role: z.string().nullable(),
2035
- state: z.enum(["pending", "importing", "completed", "failed"]),
2079
+ // "skipped": the source server refused the folder; the import finished without it.
2080
+ state: z.enum(["pending", "importing", "completed", "failed", "skipped"]),
2036
2081
  found: z.number(),
2037
2082
  imported: z.number(),
2038
2083
  duplicates: z.number(),
@@ -2300,6 +2345,20 @@ var updateInboxThreadAttentionInputSchema = z.object({
2300
2345
  }).refine((value) => value.done !== void 0 || value.follow_up_at !== void 0, {
2301
2346
  message: "Provide done or follow_up_at."
2302
2347
  });
2348
+ var createInboxDraftInputSchema = z.object({
2349
+ id: idSchema.describe("Mailbox ID whose Drafts folder receives the draft."),
2350
+ to: z.array(recipientInputSchema).min(1).max(50),
2351
+ cc: z.array(recipientInputSchema).max(50).optional(),
2352
+ bcc: z.array(recipientInputSchema).max(50).optional(),
2353
+ subject: noControlString(998, "subject").optional(),
2354
+ text: z.string().max(256e3).optional(),
2355
+ html: z.string().max(512e3).optional(),
2356
+ idempotency_key: idempotencyKeySchema
2357
+ }).strict().refine((value) => Boolean(value.text || value.html), {
2358
+ message: "At least one of html or text is required."
2359
+ }).refine((value) => value.to.length + (value.cc?.length ?? 0) + (value.bcc?.length ?? 0) <= 50, {
2360
+ message: "Total recipients (to + cc + bcc) must not exceed 50."
2361
+ });
2303
2362
  var createInboxReplyDraftInputSchema = z.object({
2304
2363
  id: idSchema.describe("Mailbox ID."),
2305
2364
  thread_id: noControlString(256, "thread_id").min(1),
@@ -2510,9 +2569,12 @@ var updateScheduledMessageInputSchema = z.object({
2510
2569
  staged_attachment_ids: z.array(z.string().regex(/^sat_[0-9a-z]+$/, "Invalid staged attachment ID.")).max(20).optional(),
2511
2570
  scheduled_at: z.iso.datetime(),
2512
2571
  idempotency_key: idempotencyKeySchema
2513
- }).strict().refine((value) => Boolean(value.html || value.text), {
2514
- message: "At least one of html or text is required."
2515
- });
2572
+ }).strict().refine(
2573
+ (value) => value.html === void 0 && value.text === void 0 || Boolean(value.html) || Boolean(value.text),
2574
+ {
2575
+ message: "Provide a non-empty html or text, or omit both to keep the stored body."
2576
+ }
2577
+ );
2516
2578
  var replyToMessageInputSchema = z.object({
2517
2579
  id: idSchema.describe("Message ID to reply to."),
2518
2580
  to: z.array(recipientInputSchema).min(1).max(50),
@@ -5129,6 +5191,7 @@ var SESSION_LIMITS = {
5129
5191
  shipmail_reply_to_thread: 10,
5130
5192
  shipmail_reply_to_inbox_message: 10,
5131
5193
  shipmail_reply_to_inbox_thread: 10,
5194
+ shipmail_create_draft: 20,
5132
5195
  shipmail_create_inbox_reply_draft: 20,
5133
5196
  shipmail_send_inbox_reply_draft: 10,
5134
5197
  shipmail_update_inbox_thread_attention: 50,
@@ -6379,6 +6442,29 @@ function registerTools(rawServer, client, allowedTools, grantedOrganizations = [
6379
6442
  )
6380
6443
  );
6381
6444
  });
6445
+ registerIfAllowed("shipmail_create_draft", () => {
6446
+ server.registerTool(
6447
+ "shipmail_create_draft",
6448
+ {
6449
+ title: "Save New Email Draft",
6450
+ description: "Save a new email (not a reply) to the mailbox's Drafts folder. This only saves a draft: it sends nothing, and there is no tool to send it. The user reviews, edits, and sends it from Drafts in Shipmail or any mail client. Use shipmail_create_inbox_reply_draft to reply to an existing conversation.",
6451
+ inputSchema: createInboxDraftInputSchema,
6452
+ outputSchema: inboxDraftOutputSchema,
6453
+ annotations: {
6454
+ readOnlyHint: false,
6455
+ destructiveHint: false,
6456
+ idempotentHint: true,
6457
+ openWorldHint: false
6458
+ }
6459
+ },
6460
+ async (args) => runTool("shipmail_create_draft", inboxDraftOutputSchema, async () => {
6461
+ const { id, ...params } = stripIdempotencyKey(args);
6462
+ return {
6463
+ inbox_draft: await client.mailboxes.createInboxDraft(id, params, mutationOptions(args))
6464
+ };
6465
+ })
6466
+ );
6467
+ });
6382
6468
  registerIfAllowed("shipmail_create_inbox_reply_draft", () => {
6383
6469
  server.registerTool(
6384
6470
  "shipmail_create_inbox_reply_draft",
@@ -8727,7 +8813,7 @@ function registerTools(rawServer, client, allowedTools, grantedOrganizations = [
8727
8813
  }
8728
8814
 
8729
8815
  // src/version.ts
8730
- var VERSION = "0.8.3";
8816
+ var VERSION = "0.8.7";
8731
8817
 
8732
8818
  // src/server.ts
8733
8819
  var INSTRUCTIONS = `Shipmail MCP exposes the business email and calendar tools authorized by the connection's current Shipmail permissions.
package/dist/server.js CHANGED
@@ -14,6 +14,7 @@ var MCP_MEMBER_PERMISSION_GROUP_NAMES = [
14
14
  "account_status",
15
15
  "mail_read",
16
16
  "drafts",
17
+ "compose_drafts",
17
18
  "mail_organize",
18
19
  "mailbox_rules",
19
20
  "send_mail",
@@ -50,6 +51,13 @@ var MCP_PERMISSION_GROUPS = [
50
51
  scopes: ["drafts:write"],
51
52
  persistent: false
52
53
  },
54
+ {
55
+ name: "compose_drafts",
56
+ label: "Compose drafts",
57
+ description: "Save new messages to Drafts for review. Cannot send.",
58
+ scopes: ["drafts:compose"],
59
+ persistent: false
60
+ },
53
61
  {
54
62
  name: "mail_organize",
55
63
  label: "Organize mail",
@@ -219,6 +227,7 @@ var CAPABILITY_ROWS = [
219
227
  ["shipmail_list_mailbox_inbox_threads", "listMailboxInboxThreads", "messages:read"],
220
228
  ["shipmail_get_mailbox_inbox_thread", "getMailboxInboxThread", "messages:read"],
221
229
  ["shipmail_update_inbox_thread_attention", "updateMailboxInboxThreadAttention", "messages:write"],
230
+ ["shipmail_create_draft", "createMailboxInboxDraft", "drafts:compose"],
222
231
  ["shipmail_create_inbox_reply_draft", "createMailboxInboxReplyDraft", "drafts:write"],
223
232
  ["shipmail_send_inbox_reply_draft", "sendMailboxInboxReplyDraft", "messages:send"],
224
233
  ["shipmail_reply_to_inbox_message", "replyToMailboxInboxMessage", "messages:send"],
@@ -419,6 +428,7 @@ var SERVER_DERIVED_RECIPIENT_TOOLS = /* @__PURE__ */ new Set([
419
428
  ]);
420
429
  var CALLER_SUPPLIED_RECIPIENT_TOOLS = /* @__PURE__ */ new Set([
421
430
  "shipmail_send_message",
431
+ "shipmail_create_draft",
422
432
  "shipmail_create_mailbox_forwarding",
423
433
  "shipmail_create_webhook",
424
434
  "shipmail_update_webhook",
@@ -441,6 +451,7 @@ function permissionGroupFor(toolName, requiredScope) {
441
451
  if (requiredScope.startsWith("mailbox_rules:")) return "mailbox_rules";
442
452
  if (requiredScope === "mailboxes:export") return "mailbox_export";
443
453
  if (requiredScope === "drafts:write") return "drafts";
454
+ if (requiredScope === "drafts:compose") return "compose_drafts";
444
455
  if (requiredScope === "messages:send") return "send_mail";
445
456
  if (requiredScope === "messages:write") return "mail_organize";
446
457
  if (requiredScope === "messages:read" || requiredScope === "threads:read") return "mail_read";
@@ -564,6 +575,15 @@ var CLOSING_KEYWORDS = /* @__PURE__ */ new Set(["additionalProperties", "unevalu
564
575
  function isSchemaNode(value) {
565
576
  return typeof value === "object" && value !== null && !Array.isArray(value);
566
577
  }
578
+ function isStringTyped(node) {
579
+ const type = node["type"];
580
+ if (type === void 0) return true;
581
+ if (Array.isArray(type)) return type.includes("string");
582
+ return type === "string";
583
+ }
584
+ function isStringEnum(value) {
585
+ return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "string");
586
+ }
567
587
  function openSchemaValue(value) {
568
588
  if (Array.isArray(value)) return value.map((entry) => openSchemaValue(entry));
569
589
  return isSchemaNode(value) ? openSchemaNode(value) : value;
@@ -578,15 +598,25 @@ function openSchemaRecord(value) {
578
598
  }
579
599
  function openSchemaNode(node) {
580
600
  const opened = {};
601
+ const stringTyped = isStringTyped(node);
602
+ const mergeUnion = Object.hasOwn(node, "oneOf");
603
+ const union = [];
581
604
  for (const keyword of Object.getOwnPropertyNames(node)) {
582
605
  const value = node[keyword];
583
606
  if (value === false && CLOSING_KEYWORDS.has(keyword)) continue;
607
+ if (stringTyped && keyword === "enum" && isStringEnum(value)) continue;
608
+ if (stringTyped && keyword === "const" && typeof value === "string") continue;
609
+ if (mergeUnion && (keyword === "oneOf" || keyword === "anyOf") && Array.isArray(value)) {
610
+ union.push(...value.map((branch) => openSchemaValue(branch)));
611
+ continue;
612
+ }
584
613
  if (SCHEMA_RECORD_KEYWORDS.has(keyword)) {
585
614
  opened[keyword] = openSchemaRecord(value);
586
615
  continue;
587
616
  }
588
617
  opened[keyword] = SCHEMA_KEYWORDS.has(keyword) ? openSchemaValue(value) : value;
589
618
  }
619
+ if (union.length > 0) opened["anyOf"] = union;
590
620
  return opened;
591
621
  }
592
622
  function openJsonSchemaObjects(schema) {
@@ -1421,6 +1451,19 @@ var inboxReplyDraftSchema = z.object({
1421
1451
  cc: z.array(replyDraftRecipientSchema),
1422
1452
  created_at: z.string()
1423
1453
  });
1454
+ var inboxDraftSchema = z.object({
1455
+ object: z.literal("inbox_draft"),
1456
+ id: z.string().describe(
1457
+ "Inbox message ID of the saved draft. Read it with shipmail_get_mailbox_inbox_message."
1458
+ ),
1459
+ mailbox_id: z.string(),
1460
+ from: replyDraftRecipientSchema,
1461
+ to: z.array(replyDraftRecipientSchema),
1462
+ cc: z.array(replyDraftRecipientSchema),
1463
+ bcc: z.array(replyDraftRecipientSchema),
1464
+ subject: z.string(),
1465
+ created_at: z.string()
1466
+ });
1424
1467
  var inboxReplyDraftSendSchema = z.object({
1425
1468
  object: z.literal("inbox_reply_draft_send"),
1426
1469
  draft_id: z.string(),
@@ -1793,6 +1836,7 @@ var inboxThreadOutputSchema = z.object({ inbox_thread: inboxThreadSchema });
1793
1836
  var inboxThreadAttentionOutputSchema = z.object({
1794
1837
  inbox_thread_attention: inboxThreadAttentionResultSchema
1795
1838
  });
1839
+ var inboxDraftOutputSchema = z.object({ inbox_draft: inboxDraftSchema });
1796
1840
  var inboxReplyDraftOutputSchema = z.object({ inbox_reply_draft: inboxReplyDraftSchema });
1797
1841
  var inboxReplyDraftSendOutputSchema = z.object({
1798
1842
  inbox_reply_draft_send: inboxReplyDraftSendSchema
@@ -1935,7 +1979,8 @@ var importFolderSchema = z.object({
1935
1979
  source_folder: z.string(),
1936
1980
  target_folder: z.string().nullable(),
1937
1981
  role: z.string().nullable(),
1938
- state: z.enum(["pending", "importing", "completed", "failed"]),
1982
+ // "skipped": the source server refused the folder; the import finished without it.
1983
+ state: z.enum(["pending", "importing", "completed", "failed", "skipped"]),
1939
1984
  found: z.number(),
1940
1985
  imported: z.number(),
1941
1986
  duplicates: z.number(),
@@ -2203,6 +2248,20 @@ var updateInboxThreadAttentionInputSchema = z.object({
2203
2248
  }).refine((value) => value.done !== void 0 || value.follow_up_at !== void 0, {
2204
2249
  message: "Provide done or follow_up_at."
2205
2250
  });
2251
+ var createInboxDraftInputSchema = z.object({
2252
+ id: idSchema.describe("Mailbox ID whose Drafts folder receives the draft."),
2253
+ to: z.array(recipientInputSchema).min(1).max(50),
2254
+ cc: z.array(recipientInputSchema).max(50).optional(),
2255
+ bcc: z.array(recipientInputSchema).max(50).optional(),
2256
+ subject: noControlString(998, "subject").optional(),
2257
+ text: z.string().max(256e3).optional(),
2258
+ html: z.string().max(512e3).optional(),
2259
+ idempotency_key: idempotencyKeySchema
2260
+ }).strict().refine((value) => Boolean(value.text || value.html), {
2261
+ message: "At least one of html or text is required."
2262
+ }).refine((value) => value.to.length + (value.cc?.length ?? 0) + (value.bcc?.length ?? 0) <= 50, {
2263
+ message: "Total recipients (to + cc + bcc) must not exceed 50."
2264
+ });
2206
2265
  var createInboxReplyDraftInputSchema = z.object({
2207
2266
  id: idSchema.describe("Mailbox ID."),
2208
2267
  thread_id: noControlString(256, "thread_id").min(1),
@@ -2413,9 +2472,12 @@ var updateScheduledMessageInputSchema = z.object({
2413
2472
  staged_attachment_ids: z.array(z.string().regex(/^sat_[0-9a-z]+$/, "Invalid staged attachment ID.")).max(20).optional(),
2414
2473
  scheduled_at: z.iso.datetime(),
2415
2474
  idempotency_key: idempotencyKeySchema
2416
- }).strict().refine((value) => Boolean(value.html || value.text), {
2417
- message: "At least one of html or text is required."
2418
- });
2475
+ }).strict().refine(
2476
+ (value) => value.html === void 0 && value.text === void 0 || Boolean(value.html) || Boolean(value.text),
2477
+ {
2478
+ message: "Provide a non-empty html or text, or omit both to keep the stored body."
2479
+ }
2480
+ );
2419
2481
  var replyToMessageInputSchema = z.object({
2420
2482
  id: idSchema.describe("Message ID to reply to."),
2421
2483
  to: z.array(recipientInputSchema).min(1).max(50),
@@ -5039,6 +5101,7 @@ var SESSION_LIMITS = {
5039
5101
  shipmail_reply_to_thread: 10,
5040
5102
  shipmail_reply_to_inbox_message: 10,
5041
5103
  shipmail_reply_to_inbox_thread: 10,
5104
+ shipmail_create_draft: 20,
5042
5105
  shipmail_create_inbox_reply_draft: 20,
5043
5106
  shipmail_send_inbox_reply_draft: 10,
5044
5107
  shipmail_update_inbox_thread_attention: 50,
@@ -6289,6 +6352,29 @@ function registerTools(rawServer, client, allowedTools, grantedOrganizations = [
6289
6352
  )
6290
6353
  );
6291
6354
  });
6355
+ registerIfAllowed("shipmail_create_draft", () => {
6356
+ server.registerTool(
6357
+ "shipmail_create_draft",
6358
+ {
6359
+ title: "Save New Email Draft",
6360
+ description: "Save a new email (not a reply) to the mailbox's Drafts folder. This only saves a draft: it sends nothing, and there is no tool to send it. The user reviews, edits, and sends it from Drafts in Shipmail or any mail client. Use shipmail_create_inbox_reply_draft to reply to an existing conversation.",
6361
+ inputSchema: createInboxDraftInputSchema,
6362
+ outputSchema: inboxDraftOutputSchema,
6363
+ annotations: {
6364
+ readOnlyHint: false,
6365
+ destructiveHint: false,
6366
+ idempotentHint: true,
6367
+ openWorldHint: false
6368
+ }
6369
+ },
6370
+ async (args) => runTool("shipmail_create_draft", inboxDraftOutputSchema, async () => {
6371
+ const { id, ...params } = stripIdempotencyKey(args);
6372
+ return {
6373
+ inbox_draft: await client.mailboxes.createInboxDraft(id, params, mutationOptions(args))
6374
+ };
6375
+ })
6376
+ );
6377
+ });
6292
6378
  registerIfAllowed("shipmail_create_inbox_reply_draft", () => {
6293
6379
  server.registerTool(
6294
6380
  "shipmail_create_inbox_reply_draft",
@@ -8637,7 +8723,7 @@ function registerTools(rawServer, client, allowedTools, grantedOrganizations = [
8637
8723
  }
8638
8724
 
8639
8725
  // src/version.ts
8640
- var VERSION = "0.8.3";
8726
+ var VERSION = "0.8.7";
8641
8727
 
8642
8728
  // src/server.ts
8643
8729
  var INSTRUCTIONS = `Shipmail MCP exposes the business email and calendar tools authorized by the connection's current Shipmail permissions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipmail-mcp",
3
- "version": "0.8.3",
3
+ "version": "0.8.7",
4
4
  "mcpName": "io.github.shipmail-to/shipmail-mcp",
5
5
  "description": "Official Model Context Protocol (MCP) server for Shipmail, a business email provider with REST API, webhooks, and custom-domain inboxes for AI agents.",
6
6
  "type": "module",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@modelcontextprotocol/server": "2.0.0",
70
- "shipmail": "0.5.3",
70
+ "shipmail": "0.5.8",
71
71
  "zod": "4.4.3"
72
72
  },
73
73
  "devDependencies": {
package/server.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/shipmail-to/shipmail-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.8.3",
9
+ "version": "0.8.7",
10
10
  "websiteUrl": "https://shipmail.to/docs/mcp",
11
11
  "remotes": [
12
12
  {
@@ -27,7 +27,7 @@
27
27
  "registryType": "npm",
28
28
  "registryBaseUrl": "https://registry.npmjs.org",
29
29
  "identifier": "shipmail-mcp",
30
- "version": "0.8.3",
30
+ "version": "0.8.7",
31
31
  "transport": {
32
32
  "type": "stdio"
33
33
  },
@@ -30,7 +30,7 @@ Use Shipmail's hosted MCP server at `https://shipmail.to/api/mcp`. Prefer the na
30
30
 
31
31
  For inbox replies, prefer `shipmail_create_inbox_reply_draft`. Present the draft to the user and stop. After explicit approval, use `shipmail_send_inbox_reply_draft`.
32
32
 
33
- For a new message, assemble the sender identity, To/Cc/Bcc recipients, subject, text or HTML body, and attachments. Present those fields for approval before `shipmail_send_message`. Use `shipmail_reply_to_inbox_message`, `shipmail_reply_to_inbox_thread`, `shipmail_reply_to_message`, or `shipmail_reply_to_thread` only after the user approves the exact reply.
33
+ For a new message, assemble the sender identity, To/Cc/Bcc recipients, subject, text or HTML body, and attachments. Present those fields for approval before `shipmail_send_message`. When the user wants a new message to review and send themselves, or the connection cannot send, use `shipmail_create_draft`: it saves the message to the mailbox's Drafts folder and sends nothing. Use `shipmail_reply_to_inbox_message`, `shipmail_reply_to_inbox_thread`, `shipmail_reply_to_message`, or `shipmail_reply_to_thread` only after the user approves the exact reply.
34
34
 
35
35
  When the user asks to schedule a message, state the exact delivery time and timezone before approval.
36
36
 
package/smithery.yaml CHANGED
@@ -1,6 +1,6 @@
1
1
  # Smithery configuration file: https://smithery.ai/docs/build/project-config/smithery-yaml
2
2
  name: shipmail-mcp
3
- version: 0.8.3
3
+ version: 0.8.7
4
4
  description: Official Shipmail MCP server for AI-agent custom-domain business email inboxes with REST API and webhooks.
5
5
  author: Shipmail
6
6
  repository: https://github.com/shipmail-to/shipmail-mcp