with one click
joshu-mail
Find/search mail; local cache first, deep server Gmail.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Find/search mail; local cache first, deep server Gmail.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Search Desktop files via gbrain; mail use joshu-mail.
Meeting-mail scheduling. Kanban ea-sched-*; Calendly fallback.
jMail desktop UI workflows — compose drafts, thread navigation, voice fast paths.
Daily handoff — prep yesterday carryover, interactive morning review, finalize today's time block.
Triage mail to Projects. Not drips—use ea-project-kanban.
Drips, pipelines, HITL: Kanban project boards, not todo.md.
| name | joshu-mail |
| description | Find/search mail; local cache first, deep server Gmail. |
| version | 1.3.0 |
| metadata | {"hermes":{"category":"mail","version":"1.3.0"}} |
Platform mechanics: Apps use
@joshu/platform-data(mail.search,mail.sync,connections.status). Agents use MCP tools below orPOST /joshu/api/apps/jmail/invokefor headless actions. This skill covers workflow and escalation — not REST path details. When the jMail GUI is open, loadjmail-guifirst — list/read visible inbox from GUI snapshot; usejoshu-mailonly when mail is not in the loaded list or user wants deep/live search.
General-purpose mail read/search on the Joshu box. Not triage (ea-playbook), not meeting scheduling (ea-scheduling), not multi-step campaigns (ea-project-kanban).
skill_view('joshu-mail') when the user asks to:
owner work email vs agent Nylas)Also load before Composio live Gmail — even if you already ran gbrain.
| Task | Skill |
|---|---|
Process Triage/*.stub.md into Projects | ea-playbook |
| Meeting-mail scheduling | ea-scheduling |
| Drip / Kanban / HITL project work | ea-project-kanban |
| Desktop files, journals (non-mail) | joshu-brain |
${JOSHU_FILES_ROOT}/connectors/mail/gmail/{account_key}/threads/ # owner Gmail (Composio)
${JOSHU_FILES_ROOT}/connectors/mail/nylas/threads/ # agent inbox (Nylas)
Multiple Gmail accounts → match account_key / account_email in frontmatter to the inbox the user named. Agent Nylas mail ≠ principal Gmail unless they asked for it.
| Tier | What | When |
|---|---|---|
| Local cache (fast) | Synced mirror markdown on the box + gbrain | Always start here — steps 1–4 below |
| Deep server-side search | Composio live Gmail against Google’s servers | Only when the local cache does not hit (miss after 1–4, pre-connect mail never mirrored, or user asks to search live) |
Do not treat Composio as “another local grep.” It is a remote search path — heavier, session-based, and correct when mirrors lag or never backfilled.
mcp_gbrain_query — source_id: "__all__", recency: "on", since: "90d", mail keywordsmcp_joshu_connectors_connectors_status — which accounts exist; mirror countsconnectors_sync_now (provider: "gmail") then re-query — not on every turnsearch_files / grep on connectors/mail/gmail/{account_key}/ for exact subjectreferences/composio-gmail-live.mdDetails + MCP tables: references/mail-search-order.md
When the local cache does not hit: mirrors can lag, omit pre-connect history, or simply not contain the thread yet. Deep server-side search via Composio is then correct — not more local grep or another sync loop.
Commit to the dynamic session loop (do not mix with box terminal after step 5):
COMPOSIO_SEARCH_TOOLS → session_id, plan, workbench snippets
COMPOSIO_MULTI_EXECUTE_TOOL → one precise GMAIL_FETCH_EMAILS first
→ inline preview OK? answer user
→ remote_file_info? COMPOSIO_REMOTE_BASH or COMPOSIO_REMOTE_WORKBENCH (mandatory, same session_id)
→ empty/ambiguous inline? WORKBENCH/BASH to parse JSON — not proof mail is absent
Hermes tool names: mcp_composio_COMPOSIO_*.
connectors_status → gmail.accounts[] → email, accountKey, connectedAccountIdowner work email → db_at_project_aeon_com)GMAIL_FETCH_EMAILS: always user_id: "me" — never an email address (delegation denied)connected_account_id: ca_… in SEARCH_TOOLS known_fields; scope OAuth to that account per Composio planuser_id: me hits principal mail if the Composio session is bound to another account| Path | Where | Tool |
|---|---|---|
${JOSHU_FILES_ROOT}/connectors/mail/… | Joshu box | gbrain, search_files, local terminal |
/mnt/files/… in Composio responses | Remote sandbox | COMPOSIO_REMOTE_BASH or COMPOSIO_REMOTE_WORKBENCH only |
After remote_file_info, the next tool call must be BASH or WORKBENCH — never local grep for pick.json.
Use sync_response_to_workbench: true when include_payload: true or many messages expected.
Full session steps, examples, pitfalls: references/composio-gmail-live.md
When the user asks you to send (not find) mail:
mcp_joshu_connectors_nylas_send_message from the agent Nylas mailboxnylas_get_profile for timezone / primaryWorkEmailPOST /joshu/api/nylas/messages/send (action-guard gated)Reply subjects: when replyToMessageId is set, copy the parent subject from the thread mirror exactly (only Re:/Fwd: prefix differences allowed). Decorating the subject (availability, names, task titles) returns reply_subject_mismatch and forks Gmail threading — retry with expectedSubject from the error.
Principal Gmail send/reply: owner jMail only — agent blocked.
When the user says "add X to the email I just sent" or similar:
Find your sent message — list Nylas mirror files sorted by mtime:
ls -lt ${JOSHU_FILES_ROOT}/connectors/mail/nylas/threads/ | head
Read the newest threads; look for ones where from: matches the agent mailbox and the subject/tos match.
Identify the sent message ID — read the mirror file's frontmatter for the external_id (the Nylas message id). Or use nylas_get_message() with a known message id.
Reply-all with the new person — use nylas_send_message with:
replyToMessageId: the sent message's id from step 2cc: include the original recipients PLUS the new person — replying with replyToMessageId threads into the same conversation but doesn't auto-populate the old CC list; you must re-list everyoneto: the original primary recipientPitfall: replyToMessageId only threads the reply — it does NOT preserve the previous To/CC list. You must manually repopulate the CC list with everyone for a true reply-all.
joshu-braindocs/connectors.md