MCP server › Sample workflows
Export a message for legal hold
Produce a custodian's message for a legal matter as an RFC822 file, and prove the export matches what the index says about it. This is the one workflow that crosses from the search index into the archive — every other search sample stops at the index record itself. The sample runs against the DataTap-hosted Enron corpus. Read-only throughout.
The tool sequence
Step 1 finds a candidate message by search. Step 2 resolves its archive coordinates. Step 3 browses the archive folder to get the composite Id the download call needs. Steps 4–5 preview the same message as JSON and export it as a standalone .eml file:
// 1 · Find the message in the search index.
{"tool": "search_content", "arguments": {
"query": "*",
"filter": "fromAddress eq '<custodian-address>'",
"top": 1
}}
// → Items[0].uniqueKey
// 2 · Pull the full index record for that hit, to get its archive
// coordinates: ownerId, parentId (the containing folder) and
// fragmentId.
{"tool": "get_item", "arguments": { "uniqueKey": "<uniqueKey-from-step-1>" }}
// 3 · Browse the archive copy of that folder to resolve the message's
// archived component Id — the index alone has no contentHash or
// versionNumber, so a search hit cannot build a download call by
// itself. connectionId here is the BLOB (archive) connection, not
// the source Exchange connection.
{"tool": "browse_source_items", "arguments": {
"connectionId": "<blob-connection-id>",
"sourceId": "<ownerId-from-step-2>",
"nodeId": "<parentId-from-step-2>",
"types": "MessageItem",
"pageSize": 5
}}
// → Items[0].Id is a composite key:
// {contentHash}|{graphItemId}|{versionDate}|{versionNumber}|{tag}
// 4 · Preview the archived message as JSON.
{"tool": "preview_item", "arguments": {
"connectionId": "<blob-connection-id>",
"sourceId": "<ownerId>",
"itemId": "<Id-from-step-3>",
"fragmentId": "<FragmentId-from-step-3>",
"format": "json"
}}
// 5 · Export the same message as a standalone RFC822 file.
{"tool": "preview_item", "arguments": {
"connectionId": "<blob-connection-id>",
"sourceId": "<ownerId>",
"itemId": "<Id-from-step-3>",
"fragmentId": "<FragmentId-from-step-3>",
"format": "eml"
}}
// → a raw RFC822 string (From/Subject/Date/Message-Id headers, full body)Archive reads use the BLOB connection, not the source
preview_item is an archive read — it serves content out of the tenant's Blob storage destination, by design, regardless of which source connector originally collected the item. Passing the Exchange source connection's connectionId instead of the Blob destination's is rejected outright, with a message naming the way out:
{"error": "preview_item reads from the BLOB destination, not the source connection. Call list_connections to find the Blob connectionId for this tenant."}list_connections (used in the files-in-folder sample) is the tool that resolves the correct Blob connectionId up front.
JSON preview vs. EML export
format="json" returns a structured record — Subject, SenderEmail, BodyContentType, InternetMessageId — that agrees with what browse_source_items reported for the same item. format="eml" returns the message as a raw RFC822 string instead of JSON: a complete, standalone file with From:, Subject:, Date: and Message-Id: headers, suitable for handing to a legal review platform as-is.
search_content_advanced query (e.g. everything matching a case number or a date range) to build a full legal-hold export set rather than a single message.← Previous sample: Audit the collection estate · Back to MCP server →