Documentation Index
Fetch the complete documentation index at: https://docs.mirage.strukto.ai/llms.txt
Use this file to discover all available pages before exploring further.
The Gmail resource exposes a Gmail account as a virtual filesystem
mounted at some prefix such as /gmail/.
For Google OAuth setup, see Google Workspace Setup.
Config
import os
from mirage import MountMode, Workspace
from mirage.resource.gmail import GmailConfig, GmailResource
config = GmailConfig(
client_id=os.environ["GOOGLE_CLIENT_ID"],
client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
refresh_token=os.environ["GOOGLE_REFRESH_TOKEN"],
)
resource = GmailResource(config=config)
ws = Workspace({"/gmail": resource}, mode=MountMode.READ)
Filesystem Layout
/gmail/
<label>/
<yyyy-mm-dd>/
<sanitized-subject>__<message-id>.gmail.json
<sanitized-subject>__<message-id>/ # only if message has attachments
<attachment-filename>
...
Example:
/gmail/
INBOX/
2026-04-12/
Meeting_Notes__msg123.gmail.json
Meeting_Notes__msg123/
report.pdf
screenshot.png
Simple_Email__msg456.gmail.json
2026-04-11/
Hello__msg789.gmail.json
SENT/
2026-04-12/
Reply__msg012.gmail.json
STARRED/
My_Custom_Label/
Label directories appear at the root. System labels use the Gmail
label ID (e.g., INBOX, SENT, STARRED). User-created labels
use their label name with spaces replaced by underscores.
Date Directories
Inside each label, messages are grouped into date subdirectories
formatted as YYYY-MM-DD. The date is derived from the message’s
internalDate (epoch milliseconds) converted to a calendar date.
Message Files
Each message is stored as a .gmail.json file. The filename shape is:
<sanitized-subject>__<message-id>.gmail.json
Subjects are sanitized for filesystem safety and truncated when
necessary. The message ID is embedded after __ and before
.gmail.json.
Attachments
Messages that have attachments get a companion subdirectory with
the same base name (without .gmail.json). Decoded attachment
files are placed inside:
Meeting_Notes__msg123.gmail.json # message JSON
Meeting_Notes__msg123/ # attachment directory
report.pdf
screenshot.png
Cache
The Gmail resource uses IndexCacheStore (same as Slack, Discord,
and other resources). Index entries store label IDs, message IDs,
and message metadata. There is no separate content cache — file
content caching is handled by the workspace IOResult mechanism.
Example
import asyncio
import os
from dotenv import load_dotenv
from mirage import MountMode, Workspace
from mirage.resource.gmail import GmailConfig, GmailResource
load_dotenv(".env.development")
config = GmailConfig(
client_id=os.environ["GOOGLE_CLIENT_ID"],
client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
refresh_token=os.environ["GOOGLE_REFRESH_TOKEN"],
)
resource = GmailResource(config=config)
async def main():
ws = Workspace({"/gmail": resource}, mode=MountMode.READ)
# List labels
r = await ws.execute("ls /gmail/")
print(await r.stdout_str())
# List date directories in INBOX
r = await ws.execute("ls /gmail/INBOX/")
print(await r.stdout_str())
# List messages for a specific date
r = await ws.execute("ls /gmail/INBOX/2026-04-12/")
print(await r.stdout_str())
# Read a message
r = await ws.execute(
"cat /gmail/INBOX/2026-04-12/Meeting_Notes__msg123.gmail.json")
print(await r.stdout_str())
# Extract subject with jq
r = await ws.execute(
'jq ".subject"'
" /gmail/INBOX/2026-04-12/Meeting_Notes__msg123.gmail.json")
print(await r.stdout_str())
# List attachments
r = await ws.execute("ls /gmail/INBOX/2026-04-12/Meeting_Notes__msg123/")
print(await r.stdout_str())
# Search across all messages
r = await ws.execute('rg "quarterly" /gmail/INBOX/')
print(await r.stdout_str())
# Tree view
r = await ws.execute("tree -L 2 /gmail/INBOX/")
print(await r.stdout_str())
# Triage unread messages
r = await ws.execute('gws-gmail-triage --query "is:unread" --max 5')
print(await r.stdout_str())
# Send an email
r = await ws.execute(
'gws-gmail-send --to "user@example.com"'
' --subject "Hello from MIRAGE"'
' --body "This email was sent via the MIRAGE Gmail resource."')
print(await r.stdout_str())
if __name__ == "__main__":
asyncio.run(main())
See examples/gmail/gmail.py for the full working example.
Finding IDs
Resource-specific commands require message IDs. These can be
extracted from the filesystem:
# Message ID -- embedded in filename after "__"
ls /gmail/INBOX/2026-04-12/
# -> Meeting_Notes__msg123.gmail.json <- message_id = msg123
# Extract message ID from filename
basename /gmail/INBOX/2026-04-12/Meeting_Notes__msg123.gmail.json .gmail.json
# -> Meeting_Notes__msg123
# The part after "__" is the message ID: msg123
# Read a message then reply
gws-gmail-read --id msg123
gws-gmail-reply --message-id msg123 --body "Thanks for the notes"
Working with Large Labels
Labels with many messages are split into date directories. Tips
for efficient access:
# List available dates
ls /gmail/INBOX/
# Check message count for a specific date
ls /gmail/INBOX/2026-04-12/ | wc -l
# Read only the most recent date
ls /gmail/INBOX/ | tail -n 1
# Search across all dates in a label
rg "keyword" /gmail/INBOX/
# Extract specific fields to reduce output
jq -r '.subject' /gmail/INBOX/2026-04-12/*.gmail.json | head -n 20
# Find messages with attachments
find /gmail/INBOX/2026-04-12/ -type d
# Tree view of a label
tree -L 2 /gmail/INBOX/
Shell Commands
Standard commands available on the mounted Gmail tree:
| Command | Notes |
|---|
ls | List labels, dates, messages, attachments |
cat | Read message JSON or attachment content |
head / tail | First/last N lines |
grep / rg | Pattern search (file or directory level) |
jq | Query message JSON fields |
wc | Line/word/byte counts |
stat | File metadata (name, size, type) |
find | Recursive search with -name, -maxdepth |
tree | Directory tree view |
basename | Extract filename from path |
dirname | Extract directory from path |
realpath | Resolve path to absolute form |
nl | Number lines of output |
Resource-specific commands:
gws-gmail-send
Send a new email.
gws-gmail-send --to "user@example.com" --subject "Hello" --body "Hi there"
| Option | Required | Description |
|---|
--to | yes | Recipient email address |
--subject | yes | Email subject line |
--body | yes | Email body text |
Returns the sent message JSON.
gws-gmail-reply
Reply to a message.
gws-gmail-reply --message-id msg123 --body "Thanks for the update"
| Option | Required | Description |
|---|
--message-id | yes | Gmail message ID |
--body | yes | Reply body text |
Returns the sent reply JSON.
gws-gmail-reply-all
Reply-all to a message.
gws-gmail-reply-all --message-id msg123 --body "Acknowledged by the team"
| Option | Required | Description |
|---|
--message-id | yes | Gmail message ID |
--body | yes | Reply body text |
Returns the sent reply JSON.
gws-gmail-forward
Forward a message to another recipient.
gws-gmail-forward --message-id msg123 --to "colleague@example.com"
| Option | Required | Description |
|---|
--message-id | yes | Gmail message ID |
--to | yes | Recipient email address |
Returns the forwarded message JSON.
gws-gmail-triage
Search and triage emails using Gmail query syntax.
gws-gmail-triage --query "is:unread" --max 10
| Option | Required | Description |
|---|
--query | yes | Gmail search query |
--max | no | Maximum number of results to return |
Returns matching messages as JSON.
gws-gmail-read
Read a message by its ID.
gws-gmail-read --id msg123
| Option | Required | Description |
|---|
--id | yes | Gmail message ID |
Returns the full message JSON.