feat(gmail,outlook): add track_delivery with consistent tracking IDs#112
Merged
dimavedenyapin merged 10 commits intomainfrom Mar 29, 2026
Merged
feat(gmail,outlook): add track_delivery with consistent tracking IDs#112dimavedenyapin merged 10 commits intomainfrom
dimavedenyapin merged 10 commits intomainfrom
Conversation
When track_delivery=true, returns structured JSON with messageId, threadId, labelIds, and recipient info instead of plain text — enables delivery tracking in workflow-builder's McpToolNode without requiring a separate Gmail send node. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s Gmail and Outlook
Gmail: returns tracking_message_id (Gmail message id) and tracking_thread_id (threadId)
Outlook: uses draft-then-send flow when track_delivery=true to capture message id
and conversationId before sending, returns same tracking_message_id/tracking_thread_id shape
Both providers return identical JSON shape:
{ "status": "sent", "tracking_message_id": "...", "tracking_thread_id": "..." }
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both Gmail and Outlook send_email now return:
{ "status": "sent", "channelMessageId": "..." }
This maps directly to workflo's messages.channel_message_id column,
which the AddConversationMessageNode uses to store the external message ID.
Dropped tracking_thread_id — workflo uses sessionId (managed by the
workflow), not provider-side thread/conversation IDs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both send_email tools now return conversationId alongside channelMessageId when track_delivery is true. Gmail maps threadId → conversationId, Outlook returns its native conversationId from the draft. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gmail: calls users().watch() with GMAIL_PUBSUB_TOPIC env var on each tracked send (idempotent, safe to repeat). Watches SENT + INBOX labels. Outlook: checks for existing Graph subscription before creating one. Uses OUTLOOK_WEBHOOK_URL env var. Subscription covers me/messages with created+updated change types, max 4230-minute expiry. Both are non-blocking — failures are logged but don't prevent the email send or tracking response. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire both env vars into stage and production deploy workflows via GitHub vars (INT_/PROD_ prefixed). Also add to .env.example for local development reference. These are optional — watch/webhook setup is skipped when unset. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply black formatter to src/servers/gmail/main.py and src/servers/outlook/main.py to pass the format-check CI job. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These files were unintentionally changed — .dockerignore was deleted and remote.py had debug=True hardcoded + server names exposed in health checks. Restoring both to their state on main. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Graph object ID changes when a message moves from Drafts to Sent Items after send. internetMessageId (RFC 2822 Message-ID header) is stable across this transition, so webhook notifications can be matched back to the sent message in our DB. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dimavedenyapin
commented
Mar 29, 2026
…_delivery Gmail: structured JSON with channelMessageId/conversationId is now always returned. track_delivery only controls whether Gmail Watch (Pub/Sub) is set up. Outlook: always uses draft-then-send flow (since /me/sendMail returns 202 with empty body). track_delivery only controls webhook subscription setup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
track_deliveryboolean parameter to both Gmail and Outlooksend_emailtoolstrue, both return the same JSON shape for downstream delivery tracking:{ "status": "sent", "tracking_message_id": "...", "tracking_thread_id": "..." }tracking_message_id= Gmail message ID,tracking_thread_id= Gmail threadIdPOST /me/messages→POST /me/messages/{id}/send) to capture the message ID and conversationId before sendingtrack_deliveryare unaffectedTest plan
test_send_emailtest passes (notrack_delivery= old behavior)send_emailwithtrack_delivery: true→ verify JSON withtracking_message_id,tracking_thread_idsend_emailwithouttrack_delivery→ same "Email sent successfully" textsend_emailwithtrack_delivery: true→ verify draft-then-send returns tracking JSON🤖 Generated with Claude Code