{"id":"34ddc7fa-72f2-4a28-beb5-2ab83767efed","entityType":"agent","slug":"clawhub-globalcaos-outlook-hack","name":"TinkerClaw Outlook","canonicalUrl":"https://www.xpersona.co/agent/clawhub-globalcaos-outlook-hack","canonicalPath":"/agent/clawhub-globalcaos-outlook-hack","generatedAt":"2026-10-11T04:33:06.364Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T02:22:58.506Z","emptyReason":null},"description":"Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in. Skill: TinkerClaw Outlook Owner: globalcaos Summary: Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in. Tags: latest:3.4.2 Version history: v3.4.2 | 2026-09-07T13:06:46.696Z | user Closes remaining scanner findings on 3.4.1: Graph","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.2K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s17324vfeqe0ptzp84z2z9vttx883zdg:outlook-hack","sourceUrl":"https://clawhub.ai/globalcaos/outlook-hack","homepage":"https://clawhub.ai/globalcaos/skills/outlook-hack","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/globalcaos/outlook-hack","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/globalcaos/skills/outlook-hack","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":62,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T02:22:58.506Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T02:22:58.506Z","emptyReason":null},"stars":null,"forks":null,"downloads":1190,"packageName":null,"latestVersion":"3.4.2","tractionLabel":"1.2K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T02:22:58.493Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T02:22:58.506Z","lastCrawledAt":"2026-10-11T02:22:58.493Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T02:22:58.493Z","lastVerifiedAt":null,"highlights":[{"version":"3.4.2","createdAt":"2026-09-07T13:06:46.696Z","changelog":"Closes remaining scanner findings on 3.4.1: Graph message IDs are character-validated and URL-encoded, Graph requests are confined to /me/messages and drafts, and attachment downloads refuse overwrites via exclusive wx create plus unique filenames.","fileCount":4,"zipByteSize":10257},{"version":"3.4.1","createdAt":"2026-09-07T12:55:37.039Z","changelog":"Hardens local output handling after scan feedback: rejects symlink directories and files, forces existing export directories to mode 0700, and forces every mailbox body, summary, index, and attachment file to mode 0600. Retains the 3.4 transient-token redesign: no credential storage, no subprocesses, no token extraction, exact Graph host allowlist, mail read plus drafts only.","fileCount":4,"zipByteSize":9601},{"version":"3.4.0","createdAt":"2026-09-07T12:47:39.550Z","changelog":"Security redesign: keeps Outlook reading, attachments, and draft creation/editing while removing Teams token extraction, refresh-token persistence, subprocess execution, persistent SQLite sync, calendar/contact access, move/flag/delete operations, and command-line secrets. Uses one short-lived Graph access token via stdin per run, exact HTTPS graph.microsoft.com destination validation, no credential storage, and restrictive modes for opt-in exports.","fileCount":4,"zipByteSize":9458},{"version":"3.3.0","createdAt":"2026-09-07T12:10:56.207Z","changelog":"Addresses the credential-storage and disclosure findings. Refresh token is now stored in the OS keychain (secret-tool/libsecret or macOS Keychain) when available, falling back to a 0600 file with a printed warning only when no keychain exists — previously it always went to a plain file. Adds --logout to remove the token from both stores. Removes wording that told the agent to delay reporting a blocked state. Documents the exact write capabilities (create/patch drafts, move/flag/archive) instead of implying read-only, while sending stays hard-blocked in code.","fileCount":6,"zipByteSize":33435},{"version":"3.2.0","createdAt":"2026-09-07T10:49:31.278Z","changelog":"Corrects a false safety claim and declares the real permission surface. The outlook-mail-fetch.mjs header asserted READ-ONLY + DRAFT-ONLY while the file has always issued Graph PATCH to overwrite drafts; the header now states exactly what it can change. Adds a machine-readable permissions block covering network, shell, browser, file_write, credentials and mail_write, and an explicit pre-install warning that the skill borrows and persists a Microsoft refresh token from your Teams session, what that grants, that it is not in an OS keychain, that it may breach a work acceptable-use policy, and how to revoke it. Sending remains absent from the codebase.","fileCount":6,"zipByteSize":31842},{"version":"3.1.0","createdAt":"2026-09-07T09:40:33.105Z","changelog":"Security and disclosure pass. Adds a Permissions, Data Flow & Consent section declaring every capability, what data is touched, where it is written and what leaves the machine. Documents the off-switch and requires explicit opt-in for privacy-affecting or destructive actions. Removes documentation claims the shipped code did not implement. No functionality removed.","fileCount":6,"zipByteSize":30534},{"version":"1.0.2","createdAt":"2026-06-06T16:51:10.299Z","changelog":"TinkerClaw rebrand + funnel to github.com/globalcaos/tinkerclaw","fileCount":4,"zipByteSize":14198},{"version":"1.0.1","createdAt":"2026-03-08T12:55:29.849Z","changelog":"test","fileCount":4,"zipByteSize":14198}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17324vfeqe0ptzp84z2z9vttx883zdg:outlook-hack","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T04:33:06.359Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-globalcaos-outlook-hack/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-11T02:22:58.506Z","emptyReason":null},"readme":"Skill: TinkerClaw Outlook\n\nOwner: globalcaos\n\nSummary: Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in.\n\nTags: latest:3.4.2\n\nVersion history:\n\nv3.4.2 | 2026-09-07T13:06:46.696Z | user\n\nCloses remaining scanner findings on 3.4.1: Graph message IDs are character-validated and URL-encoded, Graph requests are confined to /me/messages and drafts, and attachment downloads refuse overwrites via exclusive wx create plus unique filenames.\n\nv3.4.1 | 2026-09-07T12:55:37.039Z | user\n\nHardens local output handling after scan feedback: rejects symlink directories and files, forces existing export directories to mode 0700, and forces every mailbox body, summary, index, and attachment file to mode 0600. Retains the 3.4 transient-token redesign: no credential storage, no subprocesses, no token extraction, exact Graph host allowlist, mail read plus drafts only.\n\nv3.4.0 | 2026-09-07T12:47:39.550Z | user\n\nSecurity redesign: keeps Outlook reading, attachments, and draft creation/editing while removing Teams token extraction, refresh-token persistence, subprocess execution, persistent SQLite sync, calendar/contact access, move/flag/delete operations, and command-line secrets. Uses one short-lived Graph access token via stdin per run, exact HTTPS graph.microsoft.com destination validation, no credential storage, and restrictive modes for opt-in exports.\n\nv3.3.0 | 2026-09-07T12:10:56.207Z | user\n\nAddresses the credential-storage and disclosure findings. Refresh token is now stored in the OS keychain (secret-tool/libsecret or macOS Keychain) when available, falling back to a 0600 file with a printed warning only when no keychain exists — previously it always went to a plain file. Adds --logout to remove the token from both stores. Removes wording that told the agent to delay reporting a blocked state. Documents the exact write capabilities (create/patch drafts, move/flag/archive) instead of implying read-only, while sending stays hard-blocked in code.\n\nv3.2.0 | 2026-09-07T10:49:31.278Z | user\n\nCorrects a false safety claim and declares the real permission surface. The outlook-mail-fetch.mjs header asserted READ-ONLY + DRAFT-ONLY while the file has always issued Graph PATCH to overwrite drafts; the header now states exactly what it can change. Adds a machine-readable permissions block covering network, shell, browser, file_write, credentials and mail_write, and an explicit pre-install warning that the skill borrows and persists a Microsoft refresh token from your Teams session, what that grants, that it is not in an OS keychain, that it may breach a work acceptable-use policy, and how to revoke it. Sending remains absent from the codebase.\n\nv3.1.0 | 2026-09-07T09:40:33.105Z | user\n\nSecurity and disclosure pass. Adds a Permissions, Data Flow & Consent section declaring every capability, what data is touched, where it is written and what leaves the machine. Documents the off-switch and requires explicit opt-in for privacy-affecting or destructive actions. Removes documentation claims the shipped code did not implement. No functionality removed.\n\nv1.0.2 | 2026-06-06T16:51:10.299Z | user\n\nTinkerClaw rebrand + funnel to github.com/globalcaos/tinkerclaw\n\nv1.0.1 | 2026-03-08T12:55:29.849Z | user\n\ntest\n\nv5.0.0 | 2026-02-24T19:05:55.521Z | user\n\nFull CLI rewrite: outlook command with mail list/read/search/draft/reply-draft/forward-draft, calendar, contacts, folders, attachments. Shared MSAL token with teams-hack. Cross-referenced sibling skills.\n\nv4.0.0 | 2026-02-24T18:08:10.805Z | auto\n\n**Major update: Now uses Teams refresh token for seamless, long-term Outlook API access (no more daily re-auth).**\n\n- Uses MSAL refresh token from Microsoft Teams localStorage for full Outlook API access, lasting 90+ days per extraction.\n- Switches backend to Microsoft Graph API v1.0 (modern & supported).\n- Browser re-auth only required once per quarter—or less.\n- Send, reply, and forward remain code-blocked for safety; drafts only.\n- Updated documentation to reflect Teams/Graph workflow and new setup.\n\nv3.0.0 | 2026-02-22T21:23:06.011Z | user\n\nv3.0.0: Added outlook-mail-fetch.mjs script for autonomous bulk email fetching via Outlook REST API v2.0. Classic Outlook (office.com) bearer token extraction from localStorage. Full body text + attachment indexing. Zero npm dependencies. Critical discovery: new Outlook (cloud.microsoft) uses PoP tokens that cannot be extracted — always use classic tab.\n\nv2.6.0 | 2026-02-20T23:34:55.402Z | user\n\nComplete rewrite: Marketia copy with humor-driven description, code-enforced no-send safety, browser session auth explanation, proper metadata with security notes.\n\nv2.5.1 | 2026-02-20T23:13:00.245Z | user\n\nRepublish with updated metadata after ClawHavoc sweep delisting.\n\nv1.0.0 | 2026-02-15T01:17:35.011Z | user\n\nInitial release: full Outlook email access via browser relay — read, search, send, reply, calendar, attachments. Zero API keys, zero admin consent.\n\nArchive index:\n\nArchive v3.4.2: 4 files, 10257 bytes\n\nFiles: scripts/outlook-mail-fetch.mjs (19200b), skill-card.md (2449b), SKILL.md (4445b), _meta.json (131b)\n\nFile v3.4.2:SKILL.md\n\n---\nname: outlook-hack\nversion: 3.4.2\ndescription: \"Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in.\"\nmetadata:\n  openclaw:\n    emoji: \"📧\"\n    os: [\"linux\", \"darwin\"]\n    requires:\n      capabilities: [\"network\", \"file_write\"]\n    permissions:\n      network: \"Credentialed HTTPS requests only to graph.microsoft.com; enforced by a runtime allowlist.\"\n      file_write: \"No credential storage. Optional mailbox exports require --yes and use mode 0600 files inside a mode 0700 directory.\"\n      mail_write: \"Can create and patch drafts. No send, reply-to-network, forward-to-network, move, flag, or delete operation is shipped.\"\n---\n\n> Part of **[TinkerClaw](https://github.com/globalcaos/tinkerclaw)**.\n\n# Outlook — read, search, and draft; never send\n\nThis skill reads Outlook mail and creates or edits drafts through Microsoft Graph. The shipped client has no send endpoint. It does not install packages, run subprocesses, store credentials, contact telemetry services, or transmit mailbox data anywhere except Microsoft Graph.\n\n## What ships\n\nOne zero-dependency Node.js client: `scripts/outlook-mail-fetch.mjs`.\n\nIt can:\n- list recent messages and drafts;\n- read a complete message body;\n- download attachments to a directory you choose;\n- create or patch drafts while preserving the existing signature;\n- export mailbox bodies and attachment metadata locally, only with `--fetch-all --yes`.\n\nIt cannot send, reply, forward, permanently delete, move, flag, read contacts, or read calendars. Those capabilities are intentionally outside this public package.\n\n## Permissions, data flow, and consent\n\n**Authentication.** Supply a short-lived Microsoft Graph access token on standard input for each invocation. This package does not extract, refresh, print, or store tokens. Obtain the token through a Microsoft-supported login tool or identity flow approved by your organisation, requesting only the mail scopes needed for the command.\n\n**Network boundary.** Every credentialed request is runtime-checked before the token is read. The destination must be exactly `https://graph.microsoft.com` with no user information, custom port, HTTP downgrade, or lookalike subdomain. Pagination URLs are checked by the same function.\n\n**Local mailbox data.** Normal list/read commands print to the current process and do not create a mailbox mirror. Bulk export refuses to run without `--yes`. Export directories are rejected if they are symlinks and forced to mode `0700`; exported bodies, summaries, indexes, and downloaded attachments reject symlink targets and are forced to mode `0600`, including pre-existing paths. Attachment downloads never overwrite an existing file: they allocate a unique name and create it with exclusive `wx`. Message IDs are restricted to Graph-safe characters and encoded before they enter a URL. Data is plaintext at rest.\n\n**No command-line secrets.** The client refuses tokens as command-line arguments. Pipe the access token on stdin so it does not appear in process listings or shell history.\n\n## Commands\n\n```bash\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --test --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --list-drafts --access-token-stdin --limit 15\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get '<message-id>' --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get-attachments '<message-id>' --access-token-stdin --out '<private-dir>'\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --patch-draft '<draft-id>' --body-file body.html --keep-signature --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes --months 6 --access-token-stdin\n```\n\nReview every draft in Outlook before sending it manually. This skill never sends on your behalf.\n\n## Cleanup\n\nOptional exports live under `~/.openclaw/workspace/data/outlook-emails/`. Inspect that directory and remove it with your normal file manager or recovery-aware deletion tool when no longer needed.\n\nSource and issues: **https://github.com/globalcaos/tinkerclaw**\n\nFile v3.4.2:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"3.4.2\",\n  \"publishedAt\": 1788786406696\n}\n\nFile v3.4.2:skill-card.md\n\n## Description:\n\nRead and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[globalcaos](https://clawhub.ai/user/globalcaos)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to inspect Outlook mail, download selected attachments, and prepare or update drafts through Microsoft Graph while leaving final sending to a human in Outlook.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: A Microsoft Graph token grants access to sensitive mailbox data and draft operations.\n\nMitigation: Use a short-lived token scoped only to the mail actions needed for the command.\n\nRisk: Email content and generated summaries may contain deceptive or untrusted instructions.\n\nMitigation: Treat message content and summaries as untrusted context and do not follow instructions found in mail without independent review.\n\nRisk: Optional mailbox exports and attachment downloads write plaintext local files.\n\nMitigation: Use exports only when needed, keep them in private directories, and remove local plaintext data when it is no longer required.\n\nRisk: Draft creation or patching can alter mailbox state even though the skill does not send mail.\n\nMitigation: Review every draft manually in Outlook before sending or relying on it.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/globalcaos/skills/outlook-hack)\n- [Microsoft Graph endpoint](https://graph.microsoft.com)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, code, configuration, guidance]\n\n**Output Format:** [Markdown guidance with shell command examples and command output from Microsoft Graph mail operations]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May produce local plaintext mailbox exports and attachment files only when explicitly requested with the consent flag.]\n\n## Skill Version(s):\n\n3.4.2 (source: frontmatter and server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v3.4.1: 4 files, 9601 bytes\n\nFiles: scripts/outlook-mail-fetch.mjs (17761b), skill-card.md (2279b), SKILL.md (4238b), _meta.json (131b)\n\nFile v3.4.1:SKILL.md\n\n---\nname: outlook-hack\nversion: 3.4.1\ndescription: \"Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in.\"\nmetadata:\n  openclaw:\n    emoji: \"📧\"\n    os: [\"linux\", \"darwin\"]\n    requires:\n      capabilities: [\"network\", \"file_write\"]\n    permissions:\n      network: \"Credentialed HTTPS requests only to graph.microsoft.com; enforced by a runtime allowlist.\"\n      file_write: \"No credential storage. Optional mailbox exports require --yes and use mode 0600 files inside a mode 0700 directory.\"\n      mail_write: \"Can create and patch drafts. No send, reply-to-network, forward-to-network, move, flag, or delete operation is shipped.\"\n---\n\n> Part of **[TinkerClaw](https://github.com/globalcaos/tinkerclaw)**.\n\n# Outlook — read, search, and draft; never send\n\nThis skill reads Outlook mail and creates or edits drafts through Microsoft Graph. The shipped client has no send endpoint. It does not install packages, run subprocesses, store credentials, contact telemetry services, or transmit mailbox data anywhere except Microsoft Graph.\n\n## What ships\n\nOne zero-dependency Node.js client: `scripts/outlook-mail-fetch.mjs`.\n\nIt can:\n- list recent messages and drafts;\n- read a complete message body;\n- download attachments to a directory you choose;\n- create or patch drafts while preserving the existing signature;\n- export mailbox bodies and attachment metadata locally, only with `--fetch-all --yes`.\n\nIt cannot send, reply, forward, permanently delete, move, flag, read contacts, or read calendars. Those capabilities are intentionally outside this public package.\n\n## Permissions, data flow, and consent\n\n**Authentication.** Supply a short-lived Microsoft Graph access token on standard input for each invocation. This package does not extract, refresh, print, or store tokens. Obtain the token through a Microsoft-supported login tool or identity flow approved by your organisation, requesting only the mail scopes needed for the command.\n\n**Network boundary.** Every credentialed request is runtime-checked before the token is read. The destination must be exactly `https://graph.microsoft.com` with no user information, custom port, HTTP downgrade, or lookalike subdomain. Pagination URLs are checked by the same function.\n\n**Local mailbox data.** Normal list/read commands print to the current process and do not create a mailbox mirror. Bulk export refuses to run without `--yes`. Export directories are rejected if they are symlinks and forced to mode `0700`; exported bodies, summaries, indexes, and downloaded attachments reject symlink targets and are forced to mode `0600`, including pre-existing paths. Data is plaintext at rest.\n\n**No command-line secrets.** The client refuses tokens as command-line arguments. Pipe the access token on stdin so it does not appear in process listings or shell history.\n\n## Commands\n\n```bash\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --test --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --list-drafts --access-token-stdin --limit 15\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get '<message-id>' --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get-attachments '<message-id>' --access-token-stdin --out '<private-dir>'\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --patch-draft '<draft-id>' --body-file body.html --keep-signature --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes --months 6 --access-token-stdin\n```\n\nReview every draft in Outlook before sending it manually. This skill never sends on your behalf.\n\n## Cleanup\n\nOptional exports live under `~/.openclaw/workspace/data/outlook-emails/`. Inspect that directory and remove it with your normal file manager or recovery-aware deletion tool when no longer needed.\n\nSource and issues: **https://github.com/globalcaos/tinkerclaw**\n\nFile v3.4.1:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"3.4.1\",\n  \"publishedAt\": 1788785737039\n}\n\nFile v3.4.1:skill-card.md\n\n## Description:\n\nTinkerClaw Outlook helps agents read and search Outlook mail, inspect attachments, and create or edit drafts through Microsoft Graph without sending messages or storing credentials.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[globalcaos](https://clawhub.ai/user/globalcaos)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nExternal users and developers use this skill to inspect Outlook mailbox content, retrieve attachments, and prepare draft edits through Microsoft Graph while keeping message sending manual.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: A mail token or weakly scoped Graph permission could allow broader mailbox access than intended.\n\nMitigation: Use a short-lived, least-privilege Microsoft Graph token and avoid broad Graph scopes.\n\nRisk: Draft edits could affect an unintended message if untrusted content controls message or draft IDs.\n\nMitigation: Keep message and draft IDs under user control, and review every edited draft manually in Outlook before sending.\n\nRisk: Mailbox exports and attachment downloads can create plaintext local files containing sensitive mail data.\n\nMitigation: Run bulk export only after explicit confirmation, download attachments into an empty private directory, and remove exported data when no longer needed.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/globalcaos/skills/outlook-hack)\n- [Microsoft Graph API endpoint](https://graph.microsoft.com/v1.0)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, files, guidance]\n\n**Output Format:** [Markdown guidance with shell command examples; command output may be plain text, JSONL, Markdown files, or draft changes in Outlook.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [No credential storage; optional mailbox exports are plaintext local files and require explicit confirmation.]\n\n## Skill Version(s):\n\n3.4.1 (source: frontmatter and server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v3.4.0: 4 files, 9458 bytes\n\nFiles: scripts/outlook-mail-fetch.mjs (17171b), skill-card.md (2398b), SKILL.md (4127b), _meta.json (131b)\n\nFile v3.4.0:SKILL.md\n\n---\nname: outlook-hack\nversion: 3.4.0\ndescription: \"Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in.\"\nmetadata:\n  openclaw:\n    emoji: \"📧\"\n    os: [\"linux\", \"darwin\"]\n    requires:\n      capabilities: [\"network\", \"file_write\"]\n    permissions:\n      network: \"Credentialed HTTPS requests only to graph.microsoft.com; enforced by a runtime allowlist.\"\n      file_write: \"No credential storage. Optional mailbox exports require --yes and use mode 0600 files inside a mode 0700 directory.\"\n      mail_write: \"Can create and patch drafts. No send, reply-to-network, forward-to-network, move, flag, or delete operation is shipped.\"\n---\n\n> Part of **[TinkerClaw](https://github.com/globalcaos/tinkerclaw)**.\n\n# Outlook — read, search, and draft; never send\n\nThis skill reads Outlook mail and creates or edits drafts through Microsoft Graph. The shipped client has no send endpoint. It does not install packages, run subprocesses, store credentials, contact telemetry services, or transmit mailbox data anywhere except Microsoft Graph.\n\n## What ships\n\nOne zero-dependency Node.js client: `scripts/outlook-mail-fetch.mjs`.\n\nIt can:\n- list recent messages and drafts;\n- read a complete message body;\n- download attachments to a directory you choose;\n- create or patch drafts while preserving the existing signature;\n- export mailbox bodies and attachment metadata locally, only with `--fetch-all --yes`.\n\nIt cannot send, reply, forward, permanently delete, move, flag, read contacts, or read calendars. Those capabilities are intentionally outside this public package.\n\n## Permissions, data flow, and consent\n\n**Authentication.** Supply a short-lived Microsoft Graph access token on standard input for each invocation. This package does not extract, refresh, print, or store tokens. Obtain the token through a Microsoft-supported login tool or identity flow approved by your organisation, requesting only the mail scopes needed for the command.\n\n**Network boundary.** Every credentialed request is runtime-checked before the token is read. The destination must be exactly `https://graph.microsoft.com` with no user information, custom port, HTTP downgrade, or lookalike subdomain. Pagination URLs are checked by the same function.\n\n**Local mailbox data.** Normal list/read commands print to the current process and do not create a mailbox mirror. Bulk export refuses to run without `--yes`. Export directories use mode `0700`; exported bodies, summaries, indexes, and downloaded attachments use mode `0600`. Data is plaintext at rest.\n\n**No command-line secrets.** The client refuses tokens as command-line arguments. Pipe the access token on stdin so it does not appear in process listings or shell history.\n\n## Commands\n\n```bash\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --test --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --list-drafts --access-token-stdin --limit 15\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get '<message-id>' --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get-attachments '<message-id>' --access-token-stdin --out '<private-dir>'\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --patch-draft '<draft-id>' --body-file body.html --keep-signature --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes --months 6 --access-token-stdin\n```\n\nReview every draft in Outlook before sending it manually. This skill never sends on your behalf.\n\n## Cleanup\n\nOptional exports live under `~/.openclaw/workspace/data/outlook-emails/`. Inspect that directory and remove it with your normal file manager or recovery-aware deletion tool when no longer needed.\n\nSource and issues: **https://github.com/globalcaos/tinkerclaw**\n\nFile v3.4.0:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"3.4.0\",\n  \"publishedAt\": 1788785259550\n}\n\nFile v3.4.0:skill-card.md\n\n## Description:\n\nRead and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[globalcaos](https://clawhub.ai/user/globalcaos)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nExternal users and developers use this skill to inspect Outlook messages and attachments, export mailbox summaries when explicitly requested, and create or update Outlook drafts for manual review before sending.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Mailbox export and attachment download paths can overwrite predictable local files and may not reliably enforce private permissions.\n\nMitigation: Run exports only after review, avoid shared systems, choose a new private attachment directory, and verify file permissions on generated mailbox data.\n\nRisk: Exported mailbox bodies, summaries, indexes, and attachments are plaintext local files.\n\nMitigation: Use --fetch-all only when needed, delete exports when no longer required, and handle downloaded attachments as sensitive data.\n\nRisk: The skill can create or patch Outlook drafts even though it does not send mail.\n\nMitigation: Review each draft manually in Outlook before sending and use narrowly scoped, short-lived Microsoft Graph tokens.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/globalcaos/skills/outlook-hack)\n- [Microsoft Graph](https://graph.microsoft.com)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, files, guidance]\n\n**Output Format:** [Markdown guidance with inline shell commands; runtime commands may print text or write local JSONL, Markdown, and attachment files.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Uses a short-lived Microsoft Graph token from stdin; opt-in bulk exports and attachment downloads write plaintext local files.]\n\n## Skill Version(s):\n\n3.4.0 (source: frontmatter and server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v3.3.0: 6 files, 33435 bytes\n\nFiles: scripts/outlook-mail-fetch.mjs (21956b), scripts/outlook-sync.py (15146b), scripts/outlook.mjs (26327b), skill-card.md (3089b), SKILL.md (27382b), _meta.json (131b)\n\nFile v3.3.0:SKILL.md\n\n---\nname: outlook-hack\nversion: 3.3.0\ndescription: \"Your agent reads Outlook email all day. Drafts replies for you. Won't send a single one. Not even if you ask nicely. Reads mail, calendar and contacts and (opt-in, --yes) exports your mailbox to disk; it borrows your signed-in Teams session token and stores it locally. Sending is hard-blocked. See Permissions, Data Flow & Consent.\"\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📧\",\n        \"os\": [\"linux\", \"darwin\"],\n        \"requires\": { \"capabilities\": [\"browser\", \"shell\", \"network\", \"file_write\"] },\n        \"permissions\":\n          {\n            \"network\": \"HTTPS to login.microsoftonline.com and graph.microsoft.com only. No telemetry, no third parties.\",\n            \"shell\": \"Runs node/python3 for the Graph client and the local sync database.\",\n            \"browser\": \"Reads the Microsoft refresh token from a Teams web tab you have actively shared with the relay.\",\n            \"file_write\": \"Token at ~/.openclaw/credentials/outlook-msal.json (0600); opt-in mailbox cache/exports under ~/.openclaw/workspace/data/outlook-emails/ (--yes required).\",\n            \"credentials\": \"A Microsoft refresh token BORROWED from your existing Teams session and persisted locally. Durable delegated access to mail, calendar and contacts. Revoke: myaccount.microsoft.com -> Sign out everywhere.\",\n            \"mail_write\": \"Creates and OVERWRITES drafts (Graph PATCH); can move/flag/archive. NEVER sends - no send/reply/forward call exists. Permanent delete is rewritten to archive.\",\n          },\n        \"notes\":\n          {\n            \"security\": \"This skill borrows the Microsoft Graph access token from a Teams Web tab you have actively Shared with the OpenClaw browser relay, then calls Graph directly — no API key, no admin consent. That token is broad: it can reach your MAIL (all folders), CALENDAR, CONTACTS and PROFILE, and this package uses all four. It can READ, SEARCH and BULK-EXPORT mail; LIST/READ/CREATE/EDIT drafts; DOWNLOAD attachments; MOVE and FLAG messages; and 'delete' — which is CODE-REWRITTEN to a reversible ARCHIVE (move to the Archive folder), never a hard delete. SENDING IS ABSENT/BLOCKED: /sendmail, /send, /reply, /replyall and /forward are hard-blocked in outlook.mjs and simply do not exist in outlook-mail-fetch.mjs; reply/forward only ever produce DRAFTS. DISK WRITES: the borrowed refresh + access token live at ~/.openclaw/credentials/outlook-msal.json (mode 0600). Bulk mailbox exports (raw bodies, attachment index, digest, SQLite DB) are written to ~/.openclaw/workspace/data/outlook-emails/ UNENCRYPTED and ONLY when you pass --yes. Network egress is limited to login.microsoftonline.com and graph.microsoft.com — no telemetry, no third parties. Full disclosure, capability table and off-switches: the 'Permissions, Data Flow & Consent' section.\",\n          },\n      },\n  }\n---\n\n> One of dozens of skills and plugins in **[TinkerClaw](https://github.com/globalcaos/tinkerclaw)** — a self-improving OpenClaw fork that's been running 24/7 for months.\n\nWon't send a single one. Not even if you ask nicely.\n\nYour agent reads the whole inbox all day, digests it, and drafts your replies — then stops cold at the one thing that could embarrass you. The brake isn't a setting you trusted someone to leave on. The send button simply was never built into the code.\n\nIt reads, searches, and summarizes your entire Outlook mailbox with no API key and no admin sign-off — it just borrows the session your browser already has. It can write a reply and drop it in your Drafts for you to look over. What it can't do is mail anyone: the send, reply, and forward functions don't exist in the code, so there's nothing to disable and nothing to slip through.\n\n**Part of [TinkerClaw](https://github.com/globalcaos/tinkerclaw)** — real-time token tracking, self-improving crons, persistent cognitive memory. This is one piece of that stack; the repo has dozens more.\n\n👉 **https://github.com/globalcaos/tinkerclaw**\n\n_Clone it. Fork it. Break it. Make it yours._\n\n# Outlook Hack\n\n<why_this_matters>\nYour AI agent won't email the CEO at 3am. Not because of a setting, not because of a policy — because the code physically cannot send email. The send/reply/forward Graph endpoints are hard-blocked in one script and were never written into the other; drafts land in the Drafts folder for manual review. And `delete` is rewritten to a reversible **archive**, so an agent can't destroy a message even if asked.\n</why_this_matters>\n\n<capabilities>\nThis is honest and complete — the scanner reads the code, and so should you. The package ships two Node CLIs and one Python sync tool:\n\n**`outlook-mail-fetch.mjs`** (mail + drafts)\n- Read, search, and **bulk-fetch** emails across all folders (`--fetch-all`, opt-in, writes to disk)\n- Index all attachments (name, type, size) per message\n- Generate digest summaries with top senders, unread counts, body text\n- Create/list drafts, read any single message's **complete body** (text or raw HTML), download a message's file attachments, and edit a draft's body in place preserving the live signature + quoted thread (`--patch-draft --keep-signature`)\n- **Never sends** — there is no send/reply/forward function in this file\n\n**`outlook.mjs`** (fuller Graph CLI)\n- `mail list | read | search | draft | reply-draft | forward-draft | move | flag | attachments | attachment | fetch-all`\n- `folders list`, `calendar`, `contacts`, `me` (profile)\n- `mail delete` is **rewritten to archive** (reversible) — a real hard delete is never issued\n- Send/reply/forward-to-network are **hard-blocked** at the HTTP layer (`BLOCKED_PATHS`); reply-draft/forward-draft only create DRAFTS\n\n**`outlook-sync.py`** (local index)\n- Incremental sync into a local SQLite + FTS5 database, offline full-text search\n</capabilities>\n\n\n## ⚠️ Read this before installing — the honest version\n\nThis skill works by **borrowing the Microsoft refresh token from your already-signed-in Teams\nweb session** and storing it on local disk. That is the entire trick, and it is why no app\nregistration or admin consent is needed.\n\nBe clear-eyed about what that means:\n\n- A refresh token is **durable delegated access** to your mail, calendar and contacts. Anyone\n  who obtains that file has your mailbox until the token is revoked.\n- It is stored in a plain file, **not** in an OS keychain.\n- Microsoft did not issue this token to this tool. You are re-using a credential minted for a\n  different client. Depending on your organisation, that may breach your acceptable-use policy.\n  **If this is a work account, ask before you use it.**\n- ClawHub's scanner flags this as credential re-use, and **it is right to**. We are not going to\n  argue otherwise or hide it behind softer wording.\n\n**Where the token lives.** It is stored in your **OS keychain** when one is available\n(`secret-tool`/libsecret on Linux, the Keychain on macOS). Only if no keychain is present does it\nfall back to a `0600` file at `~/.openclaw/credentials/outlook-msal.json`, and the tool prints a\nwarning when it does. Install `libsecret-tools` to get keychain storage on Linux.\n\n**Log out with one command:** `node scripts/outlook-mail-fetch.mjs --logout` removes the token\nfrom both the keychain and the file. To revoke it server-side as well:\n<https://myaccount.microsoft.com> → *Sign out everywhere*.\n\n**Writes:** it creates and overwrites *drafts*, and can move/flag/archive messages. It **never\nsends** — there is no send/reply/forward call anywhere in the codebase, and permanent delete is\nrewritten to archive. Mailbox sync and export are opt-in and require an explicit `--yes`.\n\nIf any of the above is not acceptable for your account, **do not install this.** That is a\nlegitimate outcome and we would rather say it here than have you find out later.\n\n\n## Permissions, Data Flow & Consent\n\nShort version: this skill borrows the Graph token from a Teams tab you Shared, reads your\nmailbox/calendar/contacts through Microsoft's own API, and can — only when you say `--yes` —\nwrite an unencrypted copy of your mail to disk. It never sends mail. Longer version, because\nyou should not take that on trust:\n\n**What data it touches.** Your Outlook **mail** (all folders), **calendar**, **contacts**, and\n**profile**, via Microsoft Graph. It reads what those APIs return; it does not scrape the page.\n\n**Where it goes / what it writes to disk.**\n\n| Path | What | When | Sensitivity |\n| --- | --- | --- | --- |\n| `~/.openclaw/credentials/outlook-msal.json` (0600) | The borrowed refresh token + rotating access token | On token store, refreshed on use | **High** — this is a live account credential |\n| `~/.openclaw/workspace/data/outlook-emails/raw-emails.jsonl` | Subjects, senders, recipients, body text | Only on `--fetch-all --yes` / `--full --yes` | High — plaintext mail |\n| `…/attachments-index.jsonl`, `…/email-summary.md`, `…/outlook.db` | Attachment metadata, digest, SQLite mirror | Same | High — plaintext mail |\n| A folder you name | Downloaded attachment bytes | Only on `--get-attachments` / `mail attachment` | As sensitive as the files |\n\n**Network.** Exactly two hosts: `login.microsoftonline.com` (token exchange) and\n`graph.microsoft.com` (all mail/calendar/contact calls). No telemetry, no third parties.\n\n**Credentials it reads.** The MSAL **refresh token** from the Teams tab's `localStorage` — read\nthrough the OpenClaw browser relay, and only from a tab you actively **Shared**. Thereafter it\nreads the token file above. It reads no other keys or auth files.\n\n| Capability | Why | Scope |\n| --- | --- | --- |\n| Browser relay read (localStorage) | One-time token extraction from your Shared Teams tab | Only tabs you clicked **Share** on |\n| Token exchange (`login.microsoftonline.com`) | Turn the refresh token into ~1h access tokens | The scopes the Teams token already carries — see below |\n| Graph read (`graph.microsoft.com`) | Read/search mail, calendar, contacts, profile, attachments | Read-only reads |\n| Graph write (Graph) | Create/edit **drafts**, move, flag, archive | Draft + non-destructive mailbox ops only; **no send** |\n| File write | Token store (0600); opt-in mail export | Paths in the table above; export gated by `--yes` |\n\n**About the refresh token — read this, it's the sharp edge.** The skill's whole trick is\nreusing the token your Teams session already holds, so no API key or admin approval is needed.\nThat token is powerful: it is a Microsoft account credential, it inherits Teams' broad scopes\n(Mail, Calendars, Files, ChannelMessage… — this skill uses only Mail/Calendar/Contacts, but the\ntoken can do more), and it auto-rotates within a **fixed ~24 h lifetime**. It is stored in a\nflat file (`outlook-msal.json`, mode 0600), **not** in an OS keychain — treat that file like a\npassword. We disclose this rather than pretend it's a standard consent-screen OAuth flow, because\nit isn't: it is a deliberate session-token reuse, and that is the feature. If you are not\ncomfortable with an agent holding a 24 h Graph credential in a local file, do not install this.\n\n**Off switches — all real, all in the shipped code:**\n\n```bash\n# Revoke this skill's access (delete the stored token). For full revocation, also sign out of\n# Teams / revoke sessions in your Microsoft account security page.\nrm ~/.openclaw/credentials/outlook-msal.json\n\n# Never write mail to disk: just don't pass --yes. --test, --list-drafts, `mail list`,\n# `mail read` all keep data in memory. Bulk export refuses to run without --yes.\n\n# Delete everything already exported to disk (bodies, index, digest, SQLite DB):\nrm -rf ~/.openclaw/workspace/data/outlook-emails\n```\n\n**Sending has no off switch because it was never built.** `outlook.mjs` blocks `/sendmail`,\n`/send`, `/reply`, `/replyall`, `/forward` before any request leaves; `outlook-mail-fetch.mjs`\nhas no send code at all. `delete` is rewritten to a reversible archive. These are enforced in\ncode, not just described here — read the two `scripts/*.mjs`, they are short.\n\n**Consent, explicitly.** (1) Token extraction only works on a Teams tab you actively **Shared**.\n(2) Bulk mailbox export (`--fetch-all`, `--full`) refuses to run without `--yes` and first prints\nexactly what it will write and where. (3) `delete` is downgraded to archive; a real delete stays\nin your hands, in Outlook.\n\n\n### What it can change (so nothing here is a surprise)\n\nThis is not a read-only tool, and the docs no longer pretend it is. Precisely:\n\n- **Drafts:** it can CREATE a draft and PATCH (overwrite) an existing draft's body. This is how\n  \"draft a reply\" works. It can change draft content you wrote — review drafts before sending.\n- **Mailbox state:** it can MOVE, FLAG and ARCHIVE messages. `delete` is deliberately rewritten\n  to a reversible archive; there is no hard-delete path.\n- **What it will NEVER do:** SEND. `/sendmail`, `/send`, `/reply`, `/replyall` and `/forward` are\n  hard-blocked in `outlook.mjs` and absent from `outlook-mail-fetch.mjs`. Reply and forward only\n  ever produce drafts.\n\nMailbox export and the local SQLite cache are opt-in and require an explicit `--yes`.\n\n## Quick Start\n\n### 0. Relay Preflight — RUN IT, don't ask about it\n\n> **Check before you report a state, either way.** The Teams tab is often already shared, so a\n> working setup can look broken if you guess. Run the three commands below, then tell the user\n> what you actually found — including, immediately, if you find you have no access. Never delay\n> telling someone they are blocked; the point of checking first is to be accurate, not quiet.\n>\n> ```bash\n> PORT=18792   # browser.profiles.chrome-relay.cdpUrl in ~/.openclaw/openclaw.json\n> curl -s \"http://127.0.0.1:$PORT/extension/status\"   # want {\"connected\":true,\"count\":>=1}\n> curl -s \"http://127.0.0.1:$PORT/json/version\"       # want \"Browser\":\"OpenClaw/extension-relay\"\n> curl -s \"http://127.0.0.1:$PORT/json/list\"          # want a page whose url is teams.cloud.microsoft\n> ```\n> `/json/list` is the endpoint that works — **`/tabs` returns the literal string `not found`** on\n> some builds, which reads like a broken relay and is not. Only if `count` is 0 or no Teams page\n> appears do you ask the user to Share the tab.\n>\n> **A dead stored token is NOT a reason to ask either.** `AADSTS700084` just means the ~24 h SPA\n> refresh token expired; the fix is to re-extract from the already-shared tab (§1).\n\n### The original preflight notes\n\nToken extraction runs **through the OpenClaw browser relay**, which only exposes tabs the user has actively clicked **Share** on. Each browser's extension is a *separate, independent connection* that must be live. An empty tab list or a 404 almost never means \"broken code\" — it means no tab is currently shared, or the extension's background socket dropped (common right after a gateway restart, because an MV3 service worker has to re-dial). **Verify these two things before concluding you can't read mail:**\n\n1. **An extension is connected.** `GET http://127.0.0.1:<relayPort>/extension/status` → expect `{\"connected\":true,\"count\":>=1}`. `<relayPort>` is the port of `browser.profiles.chrome-relay.cdpUrl` in `~/.openclaw/openclaw.json` (typically `18792`). Confirm it's the relay via `GET /json/version` → `\"Browser\":\"OpenClaw/extension-relay\"`.\n2. **The Teams tab is in the shared list.** List shared tabs (`GET /json/list`) and confirm a tab whose url is `teams.cloud.microsoft` appears, with its `targetId`.\n\n**If count is 0, or Teams isn't listed:** ask the user to (a) reload the OpenClaw extension in the browser holding Teams (`chrome://extensions` → reload), then (b) click **Share** on the Teams tab. A full gateway restart also reconnects every extension. Re-run the two checks.\n\n**Two-browser setups:** if Teams lives in one browser and another tab in a second browser, each browser's extension is its own connection — reloading one does **not** connect the other. Check the count, not just one tab.\n\n**Running the extraction JS:** evaluate the localStorage snippet below against the Teams tab **through the relay's CDP channel targeting that tab's `targetId`** (the Teams tab should be foreground/active). Capture the returned `secret` straight into a variable/file — never echo it to stdout/transcript.\n\n### 1. Token Extraction (one-time, ~30 seconds)\n\nExtract from the **Teams** tab, not Outlook. Classic Outlook no longer exists in most orgs, and new Outlook uses PoP tokens that can't be extracted. The Teams tab provides an MSAL refresh token (auto-rotating, capped at a **fixed ~24 h lifetime** — see Token Lifetime) that powers both this skill and the `teams-hack` skill.\n\n> ⚠️ **The refresh-token snippet below may return an empty `secret`** on newer MSAL builds that\n> encrypt the refresh-token cache entry (key `msal.2|<clientId>.<tenantId>`, value has no\n> `secret` field). When that happens, use the plaintext **access token** instead — Teams keeps\n> ~23 access-token entries in localStorage, each with a real `secret` (the bearer JWT), a\n> `target` (scopes) and `expiresOn`. One is scoped to `graph.microsoft.com` with `Mail.Read` +\n> `Mail.ReadWrite`. Pull that `secret` and call Graph directly — it lives only ~1 h, so re-pull\n> per session:\n>\n> ```javascript\n> (() => {\n>   for (const k of Object.keys(localStorage)) {\n>     if (!/accesstoken/i.test(k)) continue;\n>     let v; try { v = JSON.parse(localStorage.getItem(k)); } catch { continue; }\n>     if (/graph\\.microsoft\\.com/i.test(v.target || '') && /Mail\\.ReadWrite/i.test(v.target))\n>       return { secret: v.secret, expiresOn: v.expiresOn, scopes: v.target };\n>   }\n>   return { err: 'no graph mail access token' };\n> })()\n> ```\n>\n> Then `fetch('https://graph.microsoft.com/v1.0/me/...', {headers:{Authorization:'Bearer '+secret}})`\n> straight from the relay's Node context. Message ids contain `+`/`=`/`/`; pass them **raw** in\n> the path (do NOT `encodeURIComponent`, that 400s).\n\nOpen Microsoft Teams (`teams.cloud.microsoft`) in Chrome with the OpenClaw browser relay attached. Then run this in-browser evaluation:\n\n```javascript\n(() => {\n  const keys = Object.keys(localStorage).filter(\n    (k) => k.includes(\"refreshtoken\") || k.includes(\"RefreshToken\"),\n  );\n  const parsed = JSON.parse(localStorage.getItem(keys[0]));\n  const accountKeys = Object.keys(localStorage).filter((k) => {\n    try {\n      return JSON.parse(localStorage.getItem(k)).tenantId;\n    } catch {\n      return false;\n    }\n  });\n  let tenantId = null;\n  for (const k of accountKeys) {\n    try {\n      tenantId = JSON.parse(localStorage.getItem(k)).tenantId;\n      break;\n    } catch {}\n  }\n  return { secret: parsed.secret, tenantId };\n})();\n```\n\nSave the token via the `teams` CLI (not the outlook-mail-fetch script):\n\n```bash\nteams token store --refresh-token \"<secret>\" --tenant-id \"<tenantId>\"\n```\n\n### 2. Verify Access\n\n```bash\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --test\n```\n\n### 3. Bulk Fetch (opt-in — writes your mailbox to disk)\n\n`--fetch-all` exports raw bodies, an attachment index and a digest to\n`~/.openclaw/workspace/data/outlook-emails/` **unencrypted**. It refuses to run without `--yes`\nand first prints exactly what it will write. If you only need to answer a question, prefer\n`--test` / `--list-drafts`, which stay in memory.\n\n```bash\n# Last 6 months (default) — --yes is required\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes\n\n# Custom range\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes --months 12\n```\n\nOutput: `~/.openclaw/workspace/data/outlook-emails/`\n\n- `raw-emails.jsonl` — full email data (subject, from, to, body text, preview)\n- `attachments-index.jsonl` — every attachment per message\n- `email-summary.md` — readable digest with stats and per-email summaries\n\nDelete it all with `rm -rf ~/.openclaw/workspace/data/outlook-emails`.\n\n<token_source>\nAlways extract from the Teams tab — that's the only working source.\n\n| Source                                  | Token Type                      | Extractable?       | Lifetime                |\n| --------------------------------------- | ------------------------------- | ------------------ | ----------------------- |\n| Teams (`teams.cloud.microsoft`)         | MSAL refresh token              | Yes                | **~24 h**, auto-rotates |\n| New Outlook (`outlook.cloud.microsoft`) | PoP token (Proof-of-Possession) | No                 | Crypto-bound to browser |\n| Classic Outlook (`outlook.office.com`)  | Bearer access token             | Deprecated/gone    | Most orgs migrated      |\n\nNew Outlook uses Proof-of-Possession tokens that cannot be extracted or replayed. Classic Outlook is deprecated and no longer available in most orgs.\n</token_source>\n\n### When someone says a draft already exists — LIST it, don't invent a file\n\n1. **`--list-drafts` is the first move**, not a last resort, whenever a draft is said to exist,\n   be unfinished, or need replacing. Don't report it missing until that command returns empty.\n2. **The artifact is an Outlook draft.** Not a Word file, not a chat paste, not an attachment to\n   upload. Create it, or `--patch-draft` it, then `--list-drafts` again and report subject + time.\n3. Relay preflight (§0) still runs first. A listed draft with a dead token is a re-extract, not a\n   missing draft.\n\n## Drafts & Reading a Full Message\n\n`--test`/`--fetch-all` are for bulk digests. To work with one specific message or a draft, use these (all read/edit only — sending stays code-disabled):\n\n```bash\n# List your drafts (subject, to/cc, lastModified, id)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --list-drafts [--limit 15]\n\n# Print the COMPLETE body of any message or draft (not the truncated preview)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --get <id>          # cleaned plain text\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --get <id> --raw    # raw HTML (for editing)\n\n# Download a message's file attachments (plans, photos, PDFs) to a folder — read-only, never sends.\n# Defaults to ~/.openclaw/workspace/data/outlook-emails/attachments/<id> if --out is omitted.\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --get-attachments <id> --out \"<dir>\"\n\n# Replace a draft's body from an HTML fragment file.\n# --keep-signature splices your new HTML BEFORE the draft's EXISTING signature block,\n# preserving that signature (incl. its inline logo) AND the quoted thread underneath.\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --patch-draft <id> --body-file new-body.html --keep-signature\n```\n\n<why_these_exist>\n`--get` solves the \"the preview cuts off the email\" problem — Graph's `bodyPreview` and the bulk digest truncate; `--get` returns the full body. `--patch-draft --keep-signature` reads the signature from the **existing draft** (cut at `<div id=\"Signature\">`, keep head + signature + quoted thread, replace only the top), so you edit a draft without clobbering its signature/logo/quote — and without any bundled personal signature file.\n</why_these_exist>\n\n## How It Works (Technical)\n\n1. Share your Microsoft Teams tab with OpenClaw via the Browser Relay\n2. The agent reads `localStorage` to extract the MSAL refresh token\n3. Token is stored (0600) and exchanged for a Graph API access token via `teams token store`\n4. Both this skill and `teams-hack` share `~/.openclaw/credentials/outlook-msal.json`\n5. The scripts use the Graph access token for read/draft operations\n6. Refresh token auto-rotates on each use, but has a **fixed ~24 h ceiling** (SPA-issued)\n\nThe skill is not scraping the page. It speaks Outlook's own REST API, authenticated through your existing browser session.\n\n## Token Lifetime & Refresh\n\n- Refresh token: **hard ~24 h lifetime** — Entra issues SPA tokens with a fixed, non-extendable window. Rotation on use does NOT reset the clock; once ~24 h from first issue elapse, refresh fails with `AADSTS700084`.\n- Access token: ~1 hour, automatically refreshed by the scripts\n- Practical consequence: **any sync gap > 24 h needs a fresh extraction.** Plan on re-extracting from the Teams tab roughly daily.\n- When expired: re-extract from Teams tab (relay preflight §0 → localStorage snippet → `teams token store`). Requires the Teams tab to be shared.\n- Meanwhile the local SQLite DB still answers historical questions offline — a dead token blocks *new* mail, not the archive.\n\n## Local Database (SQLite + FTS5)\n\n`outlook-sync.py` keeps a local searchable mirror of your mail at\n`~/.openclaw/workspace/data/outlook-emails/outlook.db`. This is persistent, **unencrypted** mail\nstorage — the same privacy note as the bulk export applies. Incremental sync (no flag) only\nfetches what's new; a `--full` re-sync bulk-copies months of bodies and requires `--yes`. Delete\nthe store with `rm -rf ~/.openclaw/workspace/data/outlook-emails`.\n\n<sync_first>\nBefore answering an Outlook question against the local mirror, run incremental sync:\n\n```bash\npython3 {baseDir}/scripts/outlook-sync.py\n```\n\nIt checks the DB for the latest email date and fetches only what's new (with 1-day overlap). Takes seconds for a caught-up DB.\n</sync_first>\n\n### Commands\n\n```bash\n# Incremental sync (default — fetches only new emails)\npython3 {baseDir}/scripts/outlook-sync.py\n\n# Full re-sync (e.g., 36 months) — --yes required (bulk copy of mail bodies to disk)\npython3 {baseDir}/scripts/outlook-sync.py --full 36 --yes\n\n# Check DB stats\npython3 {baseDir}/scripts/outlook-sync.py --status\n\n# Full-text search\npython3 {baseDir}/scripts/outlook-sync.py --query \"invoice\"\npython3 {baseDir}/scripts/outlook-sync.py --query \"project kickoff\" --limit 10\n```\n\n### Query the DB directly (for complex queries)\n\n```python\nimport sqlite3\nfrom pathlib import Path\ndb = sqlite3.connect(str(Path.home() / \".openclaw/workspace/data/outlook-emails/outlook.db\"))\n\n# FTS search\ndb.execute('SELECT date, \"from\", subject FROM emails WHERE rowid IN (SELECT rowid FROM emails_fts WHERE emails_fts MATCH ?) ORDER BY date DESC LIMIT 10', (\"search terms\",))\n\n# By sender\ndb.execute('SELECT date, subject FROM emails WHERE \"from\" = ? ORDER BY date DESC LIMIT 10', (\"someone@example.com\",))\n\n# Attachments for a message\ndb.execute('SELECT name, content_type, size FROM attachments WHERE message_id = ?', (msg_id,))\n```\n\n<architecture_notes>\n- Zero external dependencies — pure Node.js/Python (v18+/3.10+), no npm/pip packages\n- Send-blocked — `/sendmail`, `/send`, `/reply`, `/replyall`, `/forward` are hard-blocked in `outlook.mjs`, and absent from `outlook-mail-fetch.mjs`\n- Delete-blocked — `mail delete` is rewritten to a reversible archive\n- Rate-limited — fetches 50 emails per page with automatic pagination + 429 retry\n- Body text cleaned — HTML stripped, whitespace normalized, truncated per email\n- Incremental sync — only fetches emails newer than the latest in the local DB\n</architecture_notes>\n\n## The Full Stack\n\nPair with [whatsapp-ultimate](https://clawhub.ai/globalcaos/whatsapp-ultimate) for messaging and [jarvis-voice](https://clawhub.ai/globalcaos/jarvis-voice) for voice.\n\nEverything the documentation above describes is in this package. If you find a claim here that the code does not do, that is a bug — open an issue on [the repo](https://github.com/globalcaos/tinkerclaw/issues).\n\n[Clone it. Fork it. Break it. Make it yours.](https://github.com/globalcaos/tinkerclaw)\n\nFile v3.3.0:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"3.3.0\",\n  \"publishedAt\": 1788783056207\n}\n\nFile v3.3.0:skill-card.md\n\n## Description:\n\nYour agent reads Outlook email all day. Drafts replies for you. Won't send a single one. Not even if you ask nicely. Reads mail, calendar and contacts and (opt-in, --yes) exports your mailbox to disk; it borrows your signed-in Teams session token and stores it locally. Sending is hard-blocked. See Permissions, Data Flow & Consent.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[globalcaos](https://clawhub.ai/user/globalcaos)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agent users use this skill to let an OpenClaw-style agent inspect Outlook mail, calendar, contacts, attachments, and local mailbox indexes, then prepare draft replies for manual review. It is intended for users who accept delegated Microsoft Graph access, local credential storage, and opt-in local mailbox export.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill reuses a signed-in Teams session as a Microsoft Graph credential, giving an agent delegated access to Outlook mail, calendar, contacts, and profile data.\n\nMitigation: Install only when this credential reuse is acceptable, get approval before using work or school accounts, and revoke Microsoft sessions when access should end.\n\nRisk: Broad Microsoft account credentials and plaintext mailbox copies can be stored locally under ~/.openclaw.\n\nMitigation: Prefer OS keychain storage, install libsecret-tools on Linux when available, avoid bulk export unless needed, and remove local credentials and exported mailbox data when finished.\n\nRisk: The send block does not make the skill read-only; it can create or overwrite drafts and move, flag, or archive messages.\n\nMitigation: Review drafts before sending manually and treat archive, move, and flag operations as mailbox changes that should be checked after agent use.\n\nRisk: Opt-in bulk sync and fetch commands can write unencrypted mailbox bodies, attachment metadata, digests, and SQLite indexes to disk.\n\nMitigation: Run bulk export only with explicit consent, keep exported data in a protected local account, and delete ~/.openclaw/workspace/data/outlook-emails/ when the local copy is no longer needed.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/globalcaos/skills/outlook-hack)\n- [Publisher profile](https://clawhub.ai/user/globalcaos)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with shell command examples and JSON or text CLI output]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May create or update local credential, mailbox export, attachment, and SQLite files when the user runs the documented commands.]\n\n## Skill Version(s):\n\n3.3.0 (source: frontmatter and server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v3.2.0: 6 files, 31842 bytes\n\nFiles: scripts/outlook-mail-fetch.mjs (18533b), scripts/outlook-sync.py (15146b), scripts/outlook.mjs (26327b), skill-card.md (3019b), SKILL.md (26055b), _meta.json (131b)\n\nFile v3.2.0:SKILL.md\n\n---\nname: outlook-hack\nversion: 3.2.0\ndescription: \"Your agent reads Outlook email all day. Drafts replies for you. Won't send a single one. Not even if you ask nicely. Reads mail, calendar and contacts and (opt-in, --yes) exports your mailbox to disk; it borrows your signed-in Teams session token and stores it locally. Sending is hard-blocked. See Permissions, Data Flow & Consent.\"\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📧\",\n        \"os\": [\"linux\", \"darwin\"],\n        \"requires\": { \"capabilities\": [\"browser\", \"shell\", \"network\", \"file_write\"] },\n        \"permissions\":\n          {\n            \"network\": \"HTTPS to login.microsoftonline.com and graph.microsoft.com only. No telemetry, no third parties.\",\n            \"shell\": \"Runs node/python3 for the Graph client and the local sync database.\",\n            \"browser\": \"Reads the Microsoft refresh token from a Teams web tab you have actively shared with the relay.\",\n            \"file_write\": \"Token at ~/.openclaw/credentials/outlook-msal.json (0600); opt-in mailbox cache/exports under ~/.openclaw/workspace/data/outlook-emails/ (--yes required).\",\n            \"credentials\": \"A Microsoft refresh token BORROWED from your existing Teams session and persisted locally. Durable delegated access to mail, calendar and contacts. Revoke: myaccount.microsoft.com -> Sign out everywhere.\",\n            \"mail_write\": \"Creates and OVERWRITES drafts (Graph PATCH); can move/flag/archive. NEVER sends - no send/reply/forward call exists. Permanent delete is rewritten to archive.\",\n          },\n        \"notes\":\n          {\n            \"security\": \"This skill borrows the Microsoft Graph access token from a Teams Web tab you have actively Shared with the OpenClaw browser relay, then calls Graph directly — no API key, no admin consent. That token is broad: it can reach your MAIL (all folders), CALENDAR, CONTACTS and PROFILE, and this package uses all four. It can READ, SEARCH and BULK-EXPORT mail; LIST/READ/CREATE/EDIT drafts; DOWNLOAD attachments; MOVE and FLAG messages; and 'delete' — which is CODE-REWRITTEN to a reversible ARCHIVE (move to the Archive folder), never a hard delete. SENDING IS ABSENT/BLOCKED: /sendmail, /send, /reply, /replyall and /forward are hard-blocked in outlook.mjs and simply do not exist in outlook-mail-fetch.mjs; reply/forward only ever produce DRAFTS. DISK WRITES: the borrowed refresh + access token live at ~/.openclaw/credentials/outlook-msal.json (mode 0600). Bulk mailbox exports (raw bodies, attachment index, digest, SQLite DB) are written to ~/.openclaw/workspace/data/outlook-emails/ UNENCRYPTED and ONLY when you pass --yes. Network egress is limited to login.microsoftonline.com and graph.microsoft.com — no telemetry, no third parties. Full disclosure, capability table and off-switches: the 'Permissions, Data Flow & Consent' section.\",\n          },\n      },\n  }\n---\n\n> One of dozens of skills and plugins in **[TinkerClaw](https://github.com/globalcaos/tinkerclaw)** — a self-improving OpenClaw fork that's been running 24/7 for months.\n\nWon't send a single one. Not even if you ask nicely.\n\nYour agent reads the whole inbox all day, digests it, and drafts your replies — then stops cold at the one thing that could embarrass you. The brake isn't a setting you trusted someone to leave on. The send button simply was never built into the code.\n\nIt reads, searches, and summarizes your entire Outlook mailbox with no API key and no admin sign-off — it just borrows the session your browser already has. It can write a reply and drop it in your Drafts for you to look over. What it can't do is mail anyone: the send, reply, and forward functions don't exist in the code, so there's nothing to disable and nothing to slip through.\n\n**Part of [TinkerClaw](https://github.com/globalcaos/tinkerclaw)** — real-time token tracking, self-improving crons, persistent cognitive memory. This is one piece of that stack; the repo has dozens more.\n\n👉 **https://github.com/globalcaos/tinkerclaw**\n\n_Clone it. Fork it. Break it. Make it yours._\n\n# Outlook Hack\n\n<why_this_matters>\nYour AI agent won't email the CEO at 3am. Not because of a setting, not because of a policy — because the code physically cannot send email. The send/reply/forward Graph endpoints are hard-blocked in one script and were never written into the other; drafts land in the Drafts folder for manual review. And `delete` is rewritten to a reversible **archive**, so an agent can't destroy a message even if asked.\n</why_this_matters>\n\n<capabilities>\nThis is honest and complete — the scanner reads the code, and so should you. The package ships two Node CLIs and one Python sync tool:\n\n**`outlook-mail-fetch.mjs`** (mail + drafts)\n- Read, search, and **bulk-fetch** emails across all folders (`--fetch-all`, opt-in, writes to disk)\n- Index all attachments (name, type, size) per message\n- Generate digest summaries with top senders, unread counts, body text\n- Create/list drafts, read any single message's **complete body** (text or raw HTML), download a message's file attachments, and edit a draft's body in place preserving the live signature + quoted thread (`--patch-draft --keep-signature`)\n- **Never sends** — there is no send/reply/forward function in this file\n\n**`outlook.mjs`** (fuller Graph CLI)\n- `mail list | read | search | draft | reply-draft | forward-draft | move | flag | attachments | attachment | fetch-all`\n- `folders list`, `calendar`, `contacts`, `me` (profile)\n- `mail delete` is **rewritten to archive** (reversible) — a real hard delete is never issued\n- Send/reply/forward-to-network are **hard-blocked** at the HTTP layer (`BLOCKED_PATHS`); reply-draft/forward-draft only create DRAFTS\n\n**`outlook-sync.py`** (local index)\n- Incremental sync into a local SQLite + FTS5 database, offline full-text search\n</capabilities>\n\n\n## ⚠️ Read this before installing — the honest version\n\nThis skill works by **borrowing the Microsoft refresh token from your already-signed-in Teams\nweb session** and storing it on local disk. That is the entire trick, and it is why no app\nregistration or admin consent is needed.\n\nBe clear-eyed about what that means:\n\n- A refresh token is **durable delegated access** to your mail, calendar and contacts. Anyone\n  who obtains that file has your mailbox until the token is revoked.\n- It is stored in a plain file, **not** in an OS keychain.\n- Microsoft did not issue this token to this tool. You are re-using a credential minted for a\n  different client. Depending on your organisation, that may breach your acceptable-use policy.\n  **If this is a work account, ask before you use it.**\n- ClawHub's scanner flags this as credential re-use, and **it is right to**. We are not going to\n  argue otherwise or hide it behind softer wording.\n\n**Revoke access at any time:** <https://myaccount.microsoft.com> → *Sign out everywhere*, then\ndelete the stored token file. That immediately kills this skill's access.\n\n**Writes:** it creates and overwrites *drafts*, and can move/flag/archive messages. It **never\nsends** — there is no send/reply/forward call anywhere in the codebase, and permanent delete is\nrewritten to archive. Mailbox sync and export are opt-in and require an explicit `--yes`.\n\nIf any of the above is not acceptable for your account, **do not install this.** That is a\nlegitimate outcome and we would rather say it here than have you find out later.\n\n\n## Permissions, Data Flow & Consent\n\nShort version: this skill borrows the Graph token from a Teams tab you Shared, reads your\nmailbox/calendar/contacts through Microsoft's own API, and can — only when you say `--yes` —\nwrite an unencrypted copy of your mail to disk. It never sends mail. Longer version, because\nyou should not take that on trust:\n\n**What data it touches.** Your Outlook **mail** (all folders), **calendar**, **contacts**, and\n**profile**, via Microsoft Graph. It reads what those APIs return; it does not scrape the page.\n\n**Where it goes / what it writes to disk.**\n\n| Path | What | When | Sensitivity |\n| --- | --- | --- | --- |\n| `~/.openclaw/credentials/outlook-msal.json` (0600) | The borrowed refresh token + rotating access token | On token store, refreshed on use | **High** — this is a live account credential |\n| `~/.openclaw/workspace/data/outlook-emails/raw-emails.jsonl` | Subjects, senders, recipients, body text | Only on `--fetch-all --yes` / `--full --yes` | High — plaintext mail |\n| `…/attachments-index.jsonl`, `…/email-summary.md`, `…/outlook.db` | Attachment metadata, digest, SQLite mirror | Same | High — plaintext mail |\n| A folder you name | Downloaded attachment bytes | Only on `--get-attachments` / `mail attachment` | As sensitive as the files |\n\n**Network.** Exactly two hosts: `login.microsoftonline.com` (token exchange) and\n`graph.microsoft.com` (all mail/calendar/contact calls). No telemetry, no third parties.\n\n**Credentials it reads.** The MSAL **refresh token** from the Teams tab's `localStorage` — read\nthrough the OpenClaw browser relay, and only from a tab you actively **Shared**. Thereafter it\nreads the token file above. It reads no other keys or auth files.\n\n| Capability | Why | Scope |\n| --- | --- | --- |\n| Browser relay read (localStorage) | One-time token extraction from your Shared Teams tab | Only tabs you clicked **Share** on |\n| Token exchange (`login.microsoftonline.com`) | Turn the refresh token into ~1h access tokens | The scopes the Teams token already carries — see below |\n| Graph read (`graph.microsoft.com`) | Read/search mail, calendar, contacts, profile, attachments | Read-only reads |\n| Graph write (Graph) | Create/edit **drafts**, move, flag, archive | Draft + non-destructive mailbox ops only; **no send** |\n| File write | Token store (0600); opt-in mail export | Paths in the table above; export gated by `--yes` |\n\n**About the refresh token — read this, it's the sharp edge.** The skill's whole trick is\nreusing the token your Teams session already holds, so no API key or admin approval is needed.\nThat token is powerful: it is a Microsoft account credential, it inherits Teams' broad scopes\n(Mail, Calendars, Files, ChannelMessage… — this skill uses only Mail/Calendar/Contacts, but the\ntoken can do more), and it auto-rotates within a **fixed ~24 h lifetime**. It is stored in a\nflat file (`outlook-msal.json`, mode 0600), **not** in an OS keychain — treat that file like a\npassword. We disclose this rather than pretend it's a standard consent-screen OAuth flow, because\nit isn't: it is a deliberate session-token reuse, and that is the feature. If you are not\ncomfortable with an agent holding a 24 h Graph credential in a local file, do not install this.\n\n**Off switches — all real, all in the shipped code:**\n\n```bash\n# Revoke this skill's access (delete the stored token). For full revocation, also sign out of\n# Teams / revoke sessions in your Microsoft account security page.\nrm ~/.openclaw/credentials/outlook-msal.json\n\n# Never write mail to disk: just don't pass --yes. --test, --list-drafts, `mail list`,\n# `mail read` all keep data in memory. Bulk export refuses to run without --yes.\n\n# Delete everything already exported to disk (bodies, index, digest, SQLite DB):\nrm -rf ~/.openclaw/workspace/data/outlook-emails\n```\n\n**Sending has no off switch because it was never built.** `outlook.mjs` blocks `/sendmail`,\n`/send`, `/reply`, `/replyall`, `/forward` before any request leaves; `outlook-mail-fetch.mjs`\nhas no send code at all. `delete` is rewritten to a reversible archive. These are enforced in\ncode, not just described here — read the two `scripts/*.mjs`, they are short.\n\n**Consent, explicitly.** (1) Token extraction only works on a Teams tab you actively **Shared**.\n(2) Bulk mailbox export (`--fetch-all`, `--full`) refuses to run without `--yes` and first prints\nexactly what it will write and where. (3) `delete` is downgraded to archive; a real delete stays\nin your hands, in Outlook.\n\n## Quick Start\n\n### 0. Relay Preflight — RUN IT, don't ask about it\n\n> **Don't tell the user you're blocked before you've checked.** The Teams tab is often already\n> shared; a working setup can look broken if you ask first and check later. Run the three\n> commands below before reporting \"no access\" or asking anyone to share a tab.\n>\n> ```bash\n> PORT=18792   # browser.profiles.chrome-relay.cdpUrl in ~/.openclaw/openclaw.json\n> curl -s \"http://127.0.0.1:$PORT/extension/status\"   # want {\"connected\":true,\"count\":>=1}\n> curl -s \"http://127.0.0.1:$PORT/json/version\"       # want \"Browser\":\"OpenClaw/extension-relay\"\n> curl -s \"http://127.0.0.1:$PORT/json/list\"          # want a page whose url is teams.cloud.microsoft\n> ```\n> `/json/list` is the endpoint that works — **`/tabs` returns the literal string `not found`** on\n> some builds, which reads like a broken relay and is not. Only if `count` is 0 or no Teams page\n> appears do you ask the user to Share the tab.\n>\n> **A dead stored token is NOT a reason to ask either.** `AADSTS700084` just means the ~24 h SPA\n> refresh token expired; the fix is to re-extract from the already-shared tab (§1).\n\n### The original preflight notes\n\nToken extraction runs **through the OpenClaw browser relay**, which only exposes tabs the user has actively clicked **Share** on. Each browser's extension is a *separate, independent connection* that must be live. An empty tab list or a 404 almost never means \"broken code\" — it means no tab is currently shared, or the extension's background socket dropped (common right after a gateway restart, because an MV3 service worker has to re-dial). **Verify these two things before concluding you can't read mail:**\n\n1. **An extension is connected.** `GET http://127.0.0.1:<relayPort>/extension/status` → expect `{\"connected\":true,\"count\":>=1}`. `<relayPort>` is the port of `browser.profiles.chrome-relay.cdpUrl` in `~/.openclaw/openclaw.json` (typically `18792`). Confirm it's the relay via `GET /json/version` → `\"Browser\":\"OpenClaw/extension-relay\"`.\n2. **The Teams tab is in the shared list.** List shared tabs (`GET /json/list`) and confirm a tab whose url is `teams.cloud.microsoft` appears, with its `targetId`.\n\n**If count is 0, or Teams isn't listed:** ask the user to (a) reload the OpenClaw extension in the browser holding Teams (`chrome://extensions` → reload), then (b) click **Share** on the Teams tab. A full gateway restart also reconnects every extension. Re-run the two checks.\n\n**Two-browser setups:** if Teams lives in one browser and another tab in a second browser, each browser's extension is its own connection — reloading one does **not** connect the other. Check the count, not just one tab.\n\n**Running the extraction JS:** evaluate the localStorage snippet below against the Teams tab **through the relay's CDP channel targeting that tab's `targetId`** (the Teams tab should be foreground/active). Capture the returned `secret` straight into a variable/file — never echo it to stdout/transcript.\n\n### 1. Token Extraction (one-time, ~30 seconds)\n\nExtract from the **Teams** tab, not Outlook. Classic Outlook no longer exists in most orgs, and new Outlook uses PoP tokens that can't be extracted. The Teams tab provides an MSAL refresh token (auto-rotating, capped at a **fixed ~24 h lifetime** — see Token Lifetime) that powers both this skill and the `teams-hack` skill.\n\n> ⚠️ **The refresh-token snippet below may return an empty `secret`** on newer MSAL builds that\n> encrypt the refresh-token cache entry (key `msal.2|<clientId>.<tenantId>`, value has no\n> `secret` field). When that happens, use the plaintext **access token** instead — Teams keeps\n> ~23 access-token entries in localStorage, each with a real `secret` (the bearer JWT), a\n> `target` (scopes) and `expiresOn`. One is scoped to `graph.microsoft.com` with `Mail.Read` +\n> `Mail.ReadWrite`. Pull that `secret` and call Graph directly — it lives only ~1 h, so re-pull\n> per session:\n>\n> ```javascript\n> (() => {\n>   for (const k of Object.keys(localStorage)) {\n>     if (!/accesstoken/i.test(k)) continue;\n>     let v; try { v = JSON.parse(localStorage.getItem(k)); } catch { continue; }\n>     if (/graph\\.microsoft\\.com/i.test(v.target || '') && /Mail\\.ReadWrite/i.test(v.target))\n>       return { secret: v.secret, expiresOn: v.expiresOn, scopes: v.target };\n>   }\n>   return { err: 'no graph mail access token' };\n> })()\n> ```\n>\n> Then `fetch('https://graph.microsoft.com/v1.0/me/...', {headers:{Authorization:'Bearer '+secret}})`\n> straight from the relay's Node context. Message ids contain `+`/`=`/`/`; pass them **raw** in\n> the path (do NOT `encodeURIComponent`, that 400s).\n\nOpen Microsoft Teams (`teams.cloud.microsoft`) in Chrome with the OpenClaw browser relay attached. Then run this in-browser evaluation:\n\n```javascript\n(() => {\n  const keys = Object.keys(localStorage).filter(\n    (k) => k.includes(\"refreshtoken\") || k.includes(\"RefreshToken\"),\n  );\n  const parsed = JSON.parse(localStorage.getItem(keys[0]));\n  const accountKeys = Object.keys(localStorage).filter((k) => {\n    try {\n      return JSON.parse(localStorage.getItem(k)).tenantId;\n    } catch {\n      return false;\n    }\n  });\n  let tenantId = null;\n  for (const k of accountKeys) {\n    try {\n      tenantId = JSON.parse(localStorage.getItem(k)).tenantId;\n      break;\n    } catch {}\n  }\n  return { secret: parsed.secret, tenantId };\n})();\n```\n\nSave the token via the `teams` CLI (not the outlook-mail-fetch script):\n\n```bash\nteams token store --refresh-token \"<secret>\" --tenant-id \"<tenantId>\"\n```\n\n### 2. Verify Access\n\n```bash\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --test\n```\n\n### 3. Bulk Fetch (opt-in — writes your mailbox to disk)\n\n`--fetch-all` exports raw bodies, an attachment index and a digest to\n`~/.openclaw/workspace/data/outlook-emails/` **unencrypted**. It refuses to run without `--yes`\nand first prints exactly what it will write. If you only need to answer a question, prefer\n`--test` / `--list-drafts`, which stay in memory.\n\n```bash\n# Last 6 months (default) — --yes is required\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes\n\n# Custom range\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes --months 12\n```\n\nOutput: `~/.openclaw/workspace/data/outlook-emails/`\n\n- `raw-emails.jsonl` — full email data (subject, from, to, body text, preview)\n- `attachments-index.jsonl` — every attachment per message\n- `email-summary.md` — readable digest with stats and per-email summaries\n\nDelete it all with `rm -rf ~/.openclaw/workspace/data/outlook-emails`.\n\n<token_source>\nAlways extract from the Teams tab — that's the only working source.\n\n| Source                                  | Token Type                      | Extractable?       | Lifetime                |\n| --------------------------------------- | ------------------------------- | ------------------ | ----------------------- |\n| Teams (`teams.cloud.microsoft`)         | MSAL refresh token              | Yes                | **~24 h**, auto-rotates |\n| New Outlook (`outlook.cloud.microsoft`) | PoP token (Proof-of-Possession) | No                 | Crypto-bound to browser |\n| Classic Outlook (`outlook.office.com`)  | Bearer access token             | Deprecated/gone    | Most orgs migrated      |\n\nNew Outlook uses Proof-of-Possession tokens that cannot be extracted or replayed. Classic Outlook is deprecated and no longer available in most orgs.\n</token_source>\n\n### When someone says a draft already exists — LIST it, don't invent a file\n\n1. **`--list-drafts` is the first move**, not a last resort, whenever a draft is said to exist,\n   be unfinished, or need replacing. Don't report it missing until that command returns empty.\n2. **The artifact is an Outlook draft.** Not a Word file, not a chat paste, not an attachment to\n   upload. Create it, or `--patch-draft` it, then `--list-drafts` again and report subject + time.\n3. Relay preflight (§0) still runs first. A listed draft with a dead token is a re-extract, not a\n   missing draft.\n\n## Drafts & Reading a Full Message\n\n`--test`/`--fetch-all` are for bulk digests. To work with one specific message or a draft, use these (all read/edit only — sending stays code-disabled):\n\n```bash\n# List your drafts (subject, to/cc, lastModified, id)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --list-drafts [--limit 15]\n\n# Print the COMPLETE body of any message or draft (not the truncated preview)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --get <id>          # cleaned plain text\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --get <id> --raw    # raw HTML (for editing)\n\n# Download a message's file attachments (plans, photos, PDFs) to a folder — read-only, never sends.\n# Defaults to ~/.openclaw/workspace/data/outlook-emails/attachments/<id> if --out is omitted.\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --get-attachments <id> --out \"<dir>\"\n\n# Replace a draft's body from an HTML fragment file.\n# --keep-signature splices your new HTML BEFORE the draft's EXISTING signature block,\n# preserving that signature (incl. its inline logo) AND the quoted thread underneath.\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --patch-draft <id> --body-file new-body.html --keep-signature\n```\n\n<why_these_exist>\n`--get` solves the \"the preview cuts off the email\" problem — Graph's `bodyPreview` and the bulk digest truncate; `--get` returns the full body. `--patch-draft --keep-signature` reads the signature from the **existing draft** (cut at `<div id=\"Signature\">`, keep head + signature + quoted thread, replace only the top), so you edit a draft without clobbering its signature/logo/quote — and without any bundled personal signature file.\n</why_these_exist>\n\n## How It Works (Technical)\n\n1. Share your Microsoft Teams tab with OpenClaw via the Browser Relay\n2. The agent reads `localStorage` to extract the MSAL refresh token\n3. Token is stored (0600) and exchanged for a Graph API access token via `teams token store`\n4. Both this skill and `teams-hack` share `~/.openclaw/credentials/outlook-msal.json`\n5. The scripts use the Graph access token for read/draft operations\n6. Refresh token auto-rotates on each use, but has a **fixed ~24 h ceiling** (SPA-issued)\n\nThe skill is not scraping the page. It speaks Outlook's own REST API, authenticated through your existing browser session.\n\n## Token Lifetime & Refresh\n\n- Refresh token: **hard ~24 h lifetime** — Entra issues SPA tokens with a fixed, non-extendable window. Rotation on use does NOT reset the clock; once ~24 h from first issue elapse, refresh fails with `AADSTS700084`.\n- Access token: ~1 hour, automatically refreshed by the scripts\n- Practical consequence: **any sync gap > 24 h needs a fresh extraction.** Plan on re-extracting from the Teams tab roughly daily.\n- When expired: re-extract from Teams tab (relay preflight §0 → localStorage snippet → `teams token store`). Requires the Teams tab to be shared.\n- Meanwhile the local SQLite DB still answers historical questions offline — a dead token blocks *new* mail, not the archive.\n\n## Local Database (SQLite + FTS5)\n\n`outlook-sync.py` keeps a local searchable mirror of your mail at\n`~/.openclaw/workspace/data/outlook-emails/outlook.db`. This is persistent, **unencrypted** mail\nstorage — the same privacy note as the bulk export applies. Incremental sync (no flag) only\nfetches what's new; a `--full` re-sync bulk-copies months of bodies and requires `--yes`. Delete\nthe store with `rm -rf ~/.openclaw/workspace/data/outlook-emails`.\n\n<sync_first>\nBefore answering an Outlook question against the local mirror, run incremental sync:\n\n```bash\npython3 {baseDir}/scripts/outlook-sync.py\n```\n\nIt checks the DB for the latest email date and fetches only what's new (with 1-day overlap). Takes seconds for a caught-up DB.\n</sync_first>\n\n### Commands\n\n```bash\n# Incremental sync (default — fetches only new emails)\npython3 {baseDir}/scripts/outlook-sync.py\n\n# Full re-sync (e.g., 36 months) — --yes required (bulk copy of mail bodies to disk)\npython3 {baseDir}/scripts/outlook-sync.py --full 36 --yes\n\n# Check DB stats\npython3 {baseDir}/scripts/outlook-sync.py --status\n\n# Full-text search\npython3 {baseDir}/scripts/outlook-sync.py --query \"invoice\"\npython3 {baseDir}/scripts/outlook-sync.py --query \"project kickoff\" --limit 10\n```\n\n### Query the DB directly (for complex queries)\n\n```python\nimport sqlite3\nfrom pathlib import Path\ndb = sqlite3.connect(str(Path.home() / \".openclaw/workspace/data/outlook-emails/outlook.db\"))\n\n# FTS search\ndb.execute('SELECT date, \"from\", subject FROM emails WHERE rowid IN (SELECT rowid FROM emails_fts WHERE emails_fts MATCH ?) ORDER BY date DESC LIMIT 10', (\"search terms\",))\n\n# By sender\ndb.execute('SELECT date, subject FROM emails WHERE \"from\" = ? ORDER BY date DESC LIMIT 10', (\"someone@example.com\",))\n\n# Attachments for a message\ndb.execute('SELECT name, content_type, size FROM attachments WHERE message_id = ?', (msg_id,))\n```\n\n<architecture_notes>\n- Zero external dependencies — pure Node.js/Python (v18+/3.10+), no npm/pip packages\n- Send-blocked — `/sendmail`, `/send`, `/reply`, `/replyall`, `/forward` are hard-blocked in `outlook.mjs`, and absent from `outlook-mail-fetch.mjs`\n- Delete-blocked — `mail delete` is rewritten to a reversible archive\n- Rate-limited — fetches 50 emails per page with automatic pagination + 429 retry\n- Body text cleaned — HTML stripped, whitespace normalized, truncated per email\n- Incremental sync — only fetches emails newer than the latest in the local DB\n</architecture_notes>\n\n## The Full Stack\n\nPair with [whatsapp-ultimate](https://clawhub.ai/globalcaos/whatsapp-ultimate) for messaging and [jarvis-voice](https://clawhub.ai/globalcaos/jarvis-voice) for voice.\n\nEverything the documentation above describes is in this package. If you find a claim here that the code does not do, that is a bug — open an issue on [the repo](https://github.com/globalcaos/tinkerclaw/issues).\n\n[Clone it. Fork it. Break it. Make it yours.](https://github.com/globalcaos/tinkerclaw)\n\nFile v3.2.0:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"3.2.0\",\n  \"publishedAt\": 1788778171278\n}\n\nFile v3.2.0:skill-card.md\n\n## Description:\n\nLets an agent read and search Outlook mail, calendar, contacts, create and edit drafts, and optionally export mailbox data locally using a Teams session token while hard-blocking send operations.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[globalcaos](https://clawhub.ai/user/globalcaos)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nExternal users and developers use this skill to let an agent inspect Outlook mailbox, calendar, contact, and profile data, generate mailbox digests, and prepare drafts for manual review. It is intended for accounts where the user accepts session-token reuse and local storage of sensitive mail data.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill borrows and stores a Microsoft Teams session token that can provide delegated access to mail, calendar, contacts, and profile data.\n\nMitigation: Install only with intentional consent, avoid work or regulated accounts unless approved, protect ~/.openclaw/credentials/outlook-msal.json like a password, and revoke by deleting local credentials and signing out or revoking Microsoft sessions.\n\nRisk: Bulk sync and export can persist plaintext mailbox bodies, attachment metadata, digests, and a SQLite database on local disk.\n\nMitigation: Avoid --yes bulk export or full sync unless persistent unencrypted mail storage is acceptable, and delete ~/.openclaw/workspace/data/outlook-emails when the local archive is no longer needed.\n\nRisk: The skill can create and overwrite drafts and can move, flag, or archive messages, changing mailbox state.\n\nMitigation: Review drafts manually before sending outside the skill, use the documented reversible archive behavior for delete requests, and restrict use to accounts where these mailbox changes are acceptable.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/globalcaos/skills/outlook-hack)\n- [Publisher Profile](https://clawhub.ai/user/globalcaos)\n- [Microsoft Account Security](https://myaccount.microsoft.com)\n- [Microsoft Graph API Endpoint](https://graph.microsoft.com/v1.0)\n- [Microsoft Teams Web App](https://teams.cloud.microsoft)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, JSON, Code, Shell commands, Configuration, Files, Guidance]\n\n**Output Format:** [Markdown guidance with shell commands, JSON CLI output, Outlook draft content, and local mailbox export files.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May create or update Outlook drafts and local files under ~/.openclaw/credentials and ~/.openclaw/workspace/data/outlook-emails; send operations are documented as absent or blocked.]\n\n## Skill Version(s):\n\n3.2.0 (source: frontmatter and server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v3.1.0: 6 files, 30534 bytes\n\nFiles: scripts/outlook-mail-fetch.mjs (17887b), scripts/outlook-sync.py (15146b), scripts/outlook.mjs (26327b), skill-card.md (2525b), SKILL.md (23454b), _meta.json (131b)\n\nFile v3.1.0:SKILL.md\n\n---\nname: outlook-hack\nversion: 3.1.0\ndescription: \"Your agent reads Outlook email all day. Drafts replies for you. Won't send a single one. Not even if you ask nicely. Reads mail, calendar and contacts and (opt-in, --yes) exports your mailbox to disk; it borrows your signed-in Teams session token and stores it locally. Sending is hard-blocked. See Permissions, Data Flow & Consent.\"\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📧\",\n        \"os\": [\"linux\", \"darwin\"],\n        \"requires\": { \"capabilities\": [\"browser\"] },\n        \"notes\":\n          {\n            \"security\": \"This skill borrows the Microsoft Graph access token from a Teams Web tab you have actively Shared with the OpenClaw browser relay, then calls Graph directly — no API key, no admin consent. That token is broad: it can reach your MAIL (all folders), CALENDAR, CONTACTS and PROFILE, and this package uses all four. It can READ, SEARCH and BULK-EXPORT mail; LIST/READ/CREATE/EDIT drafts; DOWNLOAD attachments; MOVE and FLAG messages; and 'delete' — which is CODE-REWRITTEN to a reversible ARCHIVE (move to the Archive folder), never a hard delete. SENDING IS ABSENT/BLOCKED: /sendmail, /send, /reply, /replyall and /forward are hard-blocked in outlook.mjs and simply do not exist in outlook-mail-fetch.mjs; reply/forward only ever produce DRAFTS. DISK WRITES: the borrowed refresh + access token live at ~/.openclaw/credentials/outlook-msal.json (mode 0600). Bulk mailbox exports (raw bodies, attachment index, digest, SQLite DB) are written to ~/.openclaw/workspace/data/outlook-emails/ UNENCRYPTED and ONLY when you pass --yes. Network egress is limited to login.microsoftonline.com and graph.microsoft.com — no telemetry, no third parties. Full disclosure, capability table and off-switches: the 'Permissions, Data Flow & Consent' section.\",\n          },\n      },\n  }\n---\n\n> One of dozens of skills and plugins in **[TinkerClaw](https://github.com/globalcaos/tinkerclaw)** — a self-improving OpenClaw fork that's been running 24/7 for months.\n\nWon't send a single one. Not even if you ask nicely.\n\nYour agent reads the whole inbox all day, digests it, and drafts your replies — then stops cold at the one thing that could embarrass you. The brake isn't a setting you trusted someone to leave on. The send button simply was never built into the code.\n\nIt reads, searches, and summarizes your entire Outlook mailbox with no API key and no admin sign-off — it just borrows the session your browser already has. It can write a reply and drop it in your Drafts for you to look over. What it can't do is mail anyone: the send, reply, and forward functions don't exist in the code, so there's nothing to disable and nothing to slip through.\n\n**Part of [TinkerClaw](https://github.com/globalcaos/tinkerclaw)** — real-time token tracking, self-improving crons, persistent cognitive memory. This is one piece of that stack; the repo has dozens more.\n\n👉 **https://github.com/globalcaos/tinkerclaw**\n\n_Clone it. Fork it. Break it. Make it yours._\n\n# Outlook Hack\n\n<why_this_matters>\nYour AI agent won't email the CEO at 3am. Not because of a setting, not because of a policy — because the code physically cannot send email. The send/reply/forward Graph endpoints are hard-blocked in one script and were never written into the other; drafts land in the Drafts folder for manual review. And `delete` is rewritten to a reversible **archive**, so an agent can't destroy a message even if asked.\n</why_this_matters>\n\n<capabilities>\nThis is honest and complete — the scanner reads the code, and so should you. The package ships two Node CLIs and one Python sync tool:\n\n**`outlook-mail-fetch.mjs`** (mail + drafts)\n- Read, search, and **bulk-fetch** emails across all folders (`--fetch-all`, opt-in, writes to disk)\n- Index all attachments (name, type, size) per message\n- Generate digest summaries with top senders, unread counts, body text\n- Create/list drafts, read any single message's **complete body** (text or raw HTML), download a message's file attachments, and edit a draft's body in place preserving the live signature + quoted thread (`--patch-draft --keep-signature`)\n- **Never sends** — there is no send/reply/forward function in this file\n\n**`outlook.mjs`** (fuller Graph CLI)\n- `mail list | read | search | draft | reply-draft | forward-draft | move | flag | attachments | attachment | fetch-all`\n- `folders list`, `calendar`, `contacts`, `me` (profile)\n- `mail delete` is **rewritten to archive** (reversible) — a real hard delete is never issued\n- Send/reply/forward-to-network are **hard-blocked** at the HTTP layer (`BLOCKED_PATHS`); reply-draft/forward-draft only create DRAFTS\n\n**`outlook-sync.py`** (local index)\n- Incremental sync into a local SQLite + FTS5 database, offline full-text search\n</capabilities>\n\n## Permissions, Data Flow & Consent\n\nShort version: this skill borrows the Graph token from a Teams tab you Shared, reads your\nmailbox/calendar/contacts through Microsoft's own API, and can — only when you say `--yes` —\nwrite an unencrypted copy of your mail to disk. It never sends mail. Longer version, because\nyou should not take that on trust:\n\n**What data it touches.** Your Outlook **mail** (all folders), **calendar**, **contacts**, and\n**profile**, via Microsoft Graph. It reads what those APIs return; it does not scrape the page.\n\n**Where it goes / what it writes to disk.**\n\n| Path | What | When | Sensitivity |\n| --- | --- | --- | --- |\n| `~/.openclaw/credentials/outlook-msal.json` (0600) | The borrowed refresh token + rotating access token | On token store, refreshed on use | **High** — this is a live account credential |\n| `~/.openclaw/workspace/data/outlook-emails/raw-emails.jsonl` | Subjects, senders, recipients, body text | Only on `--fetch-all --yes` / `--full --yes` | High — plaintext mail |\n| `…/attachments-index.jsonl`, `…/email-summary.md`, `…/outlook.db` | Attachment metadata, digest, SQLite mirror | Same | High — plaintext mail |\n| A folder you name | Downloaded attachment bytes | Only on `--get-attachments` / `mail attachment` | As sensitive as the files |\n\n**Network.** Exactly two hosts: `login.microsoftonline.com` (token exchange) and\n`graph.microsoft.com` (all mail/calendar/contact calls). No telemetry, no third parties.\n\n**Credentials it reads.** The MSAL **refresh token** from the Teams tab's `localStorage` — read\nthrough the OpenClaw browser relay, and only from a tab you actively **Shared**. Thereafter it\nreads the token file above. It reads no other keys or auth files.\n\n| Capability | Why | Scope |\n| --- | --- | --- |\n| Browser relay read (localStorage) | One-time token extraction from your Shared Teams tab | Only tabs you clicked **Share** on |\n| Token exchange (`login.microsoftonline.com`) | Turn the refresh token into ~1h access tokens | The scopes the Teams token already carries — see below |\n| Graph read (`graph.microsoft.com`) | Read/search mail, calendar, contacts, profile, attachments | Read-only reads |\n| Graph write (Graph) | Create/edit **drafts**, move, flag, archive | Draft + non-destructive mailbox ops only; **no send** |\n| File write | Token store (0600); opt-in mail export | Paths in the table above; export gated by `--yes` |\n\n**About the refresh token — read this, it's the sharp edge.** The skill's whole trick is\nreusing the token your Teams session already holds, so no API key or admin approval is needed.\nThat token is powerful: it is a Microsoft account credential, it inherits Teams' broad scopes\n(Mail, Calendars, Files, ChannelMessage… — this skill uses only Mail/Calendar/Contacts, but the\ntoken can do more), and it auto-rotates within a **fixed ~24 h lifetime**. It is stored in a\nflat file (`outlook-msal.json`, mode 0600), **not** in an OS keychain — treat that file like a\npassword. We disclose this rather than pretend it's a standard consent-screen OAuth flow, because\nit isn't: it is a deliberate session-token reuse, and that is the feature. If you are not\ncomfortable with an agent holding a 24 h Graph credential in a local file, do not install this.\n\n**Off switches — all real, all in the shipped code:**\n\n```bash\n# Revoke this skill's access (delete the stored token). For full revocation, also sign out of\n# Teams / revoke sessions in your Microsoft account security page.\nrm ~/.openclaw/credentials/outlook-msal.json\n\n# Never write mail to disk: just don't pass --yes. --test, --list-drafts, `mail list`,\n# `mail read` all keep data in memory. Bulk export refuses to run without --yes.\n\n# Delete everything already exported to disk (bodies, index, digest, SQLite DB):\nrm -rf ~/.openclaw/workspace/data/outlook-emails\n```\n\n**Sending has no off switch because it was never built.** `outlook.mjs` blocks `/sendmail`,\n`/send`, `/reply`, `/replyall`, `/forward` before any request leaves; `outlook-mail-fetch.mjs`\nhas no send code at all. `delete` is rewritten to a reversible archive. These are enforced in\ncode, not just described here — read the two `scripts/*.mjs`, they are short.\n\n**Consent, explicitly.** (1) Token extraction only works on a Teams tab you actively **Shared**.\n(2) Bulk mailbox export (`--fetch-all`, `--full`) refuses to run without `--yes` and first prints\nexactly what it will write and where. (3) `delete` is downgraded to archive; a real delete stays\nin your hands, in Outlook.\n\n## Quick Start\n\n### 0. Relay Preflight — RUN IT, don't ask about it\n\n> **Don't tell the user you're blocked before you've checked.** The Teams tab is often already\n> shared; a working setup can look broken if you ask first and check later. Run the three\n> commands below before reporting \"no access\" or asking anyone to share a tab.\n>\n> ```bash\n> PORT=18792   # browser.profiles.chrome-relay.cdpUrl in ~/.openclaw/openclaw.json\n> curl -s \"http://127.0.0.1:$PORT/extension/status\"   # want {\"connected\":true,\"count\":>=1}\n> curl -s \"http://127.0.0.1:$PORT/json/version\"       # want \"Browser\":\"OpenClaw/extension-relay\"\n> curl -s \"http://127.0.0.1:$PORT/json/list\"          # want a page whose url is teams.cloud.microsoft\n> ```\n> `/json/list` is the endpoint that works — **`/tabs` returns the literal string `not found`** on\n> some builds, which reads like a broken relay and is not. Only if `count` is 0 or no Teams page\n> appears do you ask the user to Share the tab.\n>\n> **A dead stored token is NOT a reason to ask either.** `AADSTS700084` just means the ~24 h SPA\n> refresh token expired; the fix is to re-extract from the already-shared tab (§1).\n\n### The original preflight notes\n\nToken extraction runs **through the OpenClaw browser relay**, which only exposes tabs the user has actively clicked **Share** on. Each browser's extension is a *separate, independent connection* that must be live. An empty tab list or a 404 almost never means \"broken code\" — it means no tab is currently shared, or the extension's background socket dropped (common right after a gateway restart, because an MV3 service worker has to re-dial). **Verify these two things before concluding you can't read mail:**\n\n1. **An extension is connected.** `GET http://127.0.0.1:<relayPort>/extension/status` → expect `{\"connected\":true,\"count\":>=1}`. `<relayPort>` is the port of `browser.profiles.chrome-relay.cdpUrl` in `~/.openclaw/openclaw.json` (typically `18792`). Confirm it's the relay via `GET /json/version` → `\"Browser\":\"OpenClaw/extension-relay\"`.\n2. **The Teams tab is in the shared list.** List shared tabs (`GET /json/list`) and confirm a tab whose url is `teams.cloud.microsoft` appears, with its `targetId`.\n\n**If count is 0, or Teams isn't listed:** ask the user to (a) reload the OpenClaw extension in the browser holding Teams (`chrome://extensions` → reload), then (b) click **Share** on the Teams tab. A full gateway restart also reconnects every extension. Re-run the two checks.\n\n**Two-browser setups:** if Teams lives in one browser and another tab in a second browser, each browser's extension is its own connection — reloading one does **not** connect the other. Check the count, not just one tab.\n\n**Running the extraction JS:** evaluate the localStorage snippet below against the Teams tab **through the relay's CDP channel targeting that tab's `targetId`** (the Teams tab should be foreground/active). Capture the returned `secret` straight into a variable/file — never echo it to stdout/transcript.\n\n### 1. Token Extraction (one-time, ~30 seconds)\n\nExtract from the **Teams** tab, not Outlook. Classic Outlook no longer exists in most orgs, and new Outlook uses PoP tokens that can't be extracted. The Teams tab provides an MSAL refresh token (auto-rotating, capped at a **fixed ~24 h lifetime** — see Token Lifetime) that powers both this skill and the `teams-hack` skill.\n\n> ⚠️ **The refresh-token snippet below may return an empty `secret`** on newer MSAL builds that\n> encrypt the refresh-token cache entry (key `msal.2|<clientId>.<tenantId>`, value has no\n> `secret` field). When that happens, use the plaintext **access token** instead — Teams keeps\n> ~23 access-token entries in localStorage, each with a real `secret` (the bearer JWT), a\n> `target` (scopes) and `expiresOn`. One is scoped to `graph.microsoft.com` with `Mail.Read` +\n> `Mail.ReadWrite`. Pull that `secret` and call Graph directly — it lives only ~1 h, so re-pull\n> per session:\n>\n> ```javascript\n> (() => {\n>   for (const k of Object.keys(localStorage)) {\n>     if (!/accesstoken/i.test(k)) continue;\n>     let v; try { v = JSON.parse(localStorage.getItem(k)); } catch { continue; }\n>     if (/graph\\.microsoft\\.com/i.test(v.target || '') && /Mail\\.ReadWrite/i.test(v.target))\n>       return { secret: v.secret, expiresOn: v.expiresOn, scopes: v.target };\n>   }\n>   return { err: 'no graph mail access token' };\n> })()\n> ```\n>\n> Then `fetch('https://graph.microsoft.com/v1.0/me/...', {headers:{Authorization:'Bearer '+secret}})`\n> straight from the relay's Node context. Message ids contain `+`/`=`/`/`; pass them **raw** in\n> the path (do NOT `encodeURIComponent`, that 400s).\n\nOpen Microsoft Teams (`teams.cloud.microsoft`) in Chrome with the OpenClaw browser relay attached. Then run this in-browser evaluation:\n\n```javascript\n(() => {\n  const keys = Object.keys(localStorage).filter(\n    (k) => k.includes(\"refreshtoken\") || k.includes(\"RefreshToken\"),\n  );\n  const parsed = JSON.parse(localStorage.getItem(keys[0]));\n  const accountKeys = Object.keys(localStorage).filter((k) => {\n    try {\n      return JSON.parse(localStorage.getItem(k)).tenantId;\n    } catch {\n      return false;\n    }\n  });\n  let tenantId = null;\n  for (const k of accountKeys) {\n    try {\n      tenantId = JSON.parse(localStorage.getItem(k)).tenantId;\n      break;\n    } catch {}\n  }\n  return { secret: parsed.secret, tenantId };\n})();\n```\n\nSave the token via the `teams` CLI (not the outlook-mail-fetch script):\n\n```bash\nteams token store --refresh-token \"<secret>\" --tenant-id \"<tenantId>\"\n```\n\n### 2. Verify Access\n\n```bash\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --test\n```\n\n### 3. Bulk Fetch (opt-in — writes your mailbox to disk)\n\n`--fetch-all` exports raw bodies, an attachment index and a digest to\n`~/.openclaw/workspace/data/outlook-emails/` **unencrypted**. It refuses to run without `--yes`\nand first prints exactly what it will write. If you only need to answer a question, prefer\n`--test` / `--list-drafts`, which stay in memory.\n\n```bash\n# Last 6 months (default) — --yes is required\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes\n\n# Custom range\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes --months 12\n```\n\nOutput: `~/.openclaw/workspace/data/outlook-emails/`\n\n- `raw-emails.jsonl` — full email data (subject, from, to, body text, preview)\n- `attachments-index.jsonl` — every attachment per message\n- `email-summary.md` — readable digest with stats and per-email summaries\n\nDelete it all with `rm -rf ~/.openclaw/workspace/data/outlook-emails`.\n\n<token_source>\nAlways extract from the Teams tab — that's the only working source.\n\n| Source                                  | Token Type                      | Extractable?       | Lifetime                |\n| --------------------------------------- | ------------------------------- | ------------------ | ----------------------- |\n| Teams (`teams.cloud.microsoft`)         | MSAL refresh token              | Yes                | **~24 h**, auto-rotates |\n| New Outlook (`outlook.cloud.microsoft`) | PoP token (Proof-of-Possession) | No                 | Crypto-bound to browser |\n| Classic Outlook (`outlook.office.com`)  | Bearer access token             | Deprecated/gone    | Most orgs migrated      |\n\nNew Outlook uses Proof-of-Possession tokens that cannot be extracted or replayed. Classic Outlook is deprecated and no longer available in most orgs.\n</token_source>\n\n### When someone says a draft already exists — LIST it, don't invent a file\n\n1. **`--list-drafts` is the first move**, not a last resort, whenever a draft is said to exist,\n   be unfinished, or need replacing. Don't report it missing until that command returns empty.\n2. **The artifact is an Outlook draft.** Not a Word file, not a chat paste, not an attachment to\n   upload. Create it, or `--patch-draft` it, then `--list-drafts` again and report subject + time.\n3. Relay preflight (§0) still runs first. A listed draft with a dead token is a re-extract, not a\n   missing draft.\n\n## Drafts & Reading a Full Message\n\n`--test`/`--fetch-all` are for bulk digests. To work with one specific message or a draft, use these (all read/edit only — sending stays code-disabled):\n\n```bash\n# List your drafts (subject, to/cc, lastModified, id)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --list-drafts [--limit 15]\n\n# Print the COMPLETE body of any message or draft (not the truncated preview)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --get <id>          # cleaned plain text\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --get <id> --raw    # raw HTML (for editing)\n\n# Download a message's file attachments (plans, photos, PDFs) to a folder — read-only, never sends.\n# Defaults to ~/.openclaw/workspace/data/outlook-emails/attachments/<id> if --out is omitted.\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --get-attachments <id> --out \"<dir>\"\n\n# Replace a draft's body from an HTML fragment file.\n# --keep-signature splices your new HTML BEFORE the draft's EXISTING signature block,\n# preserving that signature (incl. its inline logo) AND the quoted thread underneath.\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --patch-draft <id> --body-file new-body.html --keep-signature\n```\n\n<why_these_exist>\n`--get` solves the \"the preview cuts off the email\" problem — Graph's `bodyPreview` and the bulk digest truncate; `--get` returns the full body. `--patch-draft --keep-signature` reads the signature from the **existing draft** (cut at `<div id=\"Signature\">`, keep head + signature + quoted thread, replace only the top), so you edit a draft without clobbering its signature/logo/quote — and without any bundled personal signature file.\n</why_these_exist>\n\n## How It Works (Technical)\n\n1. Share your Microsoft Teams tab with OpenClaw via the Browser Relay\n2. The agent reads `localStorage` to extract the MSAL refresh token\n3. Token is stored (0600) and exchanged for a Graph API access token via `teams token store`\n4. Both this skill and `teams-hack` share `~/.openclaw/credentials/outlook-msal.json`\n5. The scripts use the Graph access token for read/draft operations\n6. Refresh token auto-rotates on each use, but has a **fixed ~24 h ceiling** (SPA-issued)\n\nThe skill is not scraping the page. It speaks Outlook's own REST API, authenticated through your existing browser session.\n\n## Token Lifetime & Refresh\n\n- Refresh token: **hard ~24 h lifetime** — Entra issues SPA tokens with a fixed, non-extendable window. Rotation on use does NOT reset the clock; once ~24 h from first issue elapse, refresh fails with `AADSTS700084`.\n- Access token: ~1 hour, automatically refreshed by the scripts\n- Practical consequence: **any sync gap > 24 h needs a fresh extraction.** Plan on re-extracting from the Teams tab roughly daily.\n- When expired: re-extract from Teams tab (relay preflight §0 → localStorage snippet → `teams token store`). Requires the Teams tab to be shared.\n- Meanwhile the local SQLite DB still answers historical questions offline — a dead token blocks *new* mail, not the archive.\n\n## Local Database (SQLite + FTS5)\n\n`outlook-sync.py` keeps a local searchable mirror of your mail at\n`~/.openclaw/workspace/data/outlook-emails/outlook.db`. This is persistent, **unencrypted** mail\nstorage — the same privacy note as the bulk export applies. Incremental sync (no flag) only\nfetches what's new; a `--full` re-sync bulk-copies months of bodies and requires `--yes`. Delete\nthe store with `rm -rf ~/.openclaw/workspace/data/outlook-emails`.\n\n<sync_first>\nBefore answering an Outlook question against the local mirror, run incremental sync:\n\n```bash\npython3 {baseDir}/scripts/outlook-sync.py\n```\n\nIt checks the DB for the latest email date and fetches only what's new (with 1-day overlap). Takes seconds for a caught-up DB.\n</sync_first>\n\n### Commands\n\n```bash\n# Incremental sync (default — fetches only new emails)\npython3 {baseDir}/scripts/outlook-sync.py\n\n# Full re-sync (e.g., 36 months) — --yes required (bulk copy of mail bodies to disk)\npython3 {baseDir}/scripts/outlook-sync.py --full 36 --yes\n\n# Check DB stats\npython3 {baseDir}/scripts/outlook-sync.py --status\n\n# Full-text search\npython3 {baseDir}/scripts/outlook-sync.py --query \"invoice\"\npython3 {baseDir}/scripts/outlook-sync.py --query \"project kickoff\" --limit 10\n```\n\n### Query the DB directly (for complex queries)\n\n```python\nimport sqlite3\nfrom pathlib import Path\ndb = sqlite3.connect(str(Path.home() / \".openclaw/workspace/data/outlook-emails/outlook.db\"))\n\n# FTS search\ndb.execute('SELECT date, \"from\", subject FROM emails WHERE rowid IN (SELECT rowid FROM emails_fts WHERE emails_fts MATCH ?) ORDER BY date DESC LIMIT 10', (\"search terms\",))\n\n# By sender\ndb.execute('SELECT date, subject FROM emails WHERE \"from\" = ? ORDER BY date DESC LIMIT 10', (\"someone@example.com\",))\n\n# Attachments for a message\ndb.execute('SELECT name, content_type, size FROM attachments WHERE message_id = ?', (msg_id,))\n```\n\n<architecture_notes>\n- Zero external dependencies — pure Node.js/Python (v18+/3.10+), no npm/pip packages\n- Send-blocked — `/sendmail`, `/send`, `/reply`, `/replyall`, `/forward` are hard-blocked in `outlook.mjs`, and absent from `outlook-mail-fetch.mjs`\n- Delete-blocked — `mail delete` is rewritten to a reversible archive\n- Rate-limited — fetches 50 emails per page with automatic pagination + 429 retry\n- Body text cleaned — HTML stripped, whitespace normalized, truncated per email\n- Incremental sync — only fetches emails newer than the latest in the local DB\n</architecture_notes>\n\n## The Full Stack\n\nPair with [whatsapp-ultimate](https://clawhub.ai/globalcaos/whatsapp-ultimate) for messaging and [jarvis-voice](https://clawhub.ai/globalcaos/jarvis-voice) for voice.\n\nEverything the documentation above describes is in this package. If you find a claim here that the code does not do, that is a bug — open an issue on [the repo](https://github.com/globalcaos/tinkerclaw/issues).\n\n[Clone it. Fork it. Break it. Make it yours.](https://github.com/globalcaos/tinkerclaw)\n\nFile v3.1.0:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"3.1.0\",\n  \"publishedAt\": 1788774033105\n}\n\nFile v3.1.0:skill-card.md\n\n## Description:\n\nTinkerClaw Outlook Hack lets an agent read, search, summarize, export, and draft Outlook mail using a shared Teams session token while blocking send operations.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[globalcaos](https://clawhub.ai/user/globalcaos)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and OpenClaw users use this skill to let an agent triage Outlook mail, inspect calendar and contact context, draft replies for human review, and optionally build a local searchable mailbox index.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill extracts and stores a live Microsoft Teams/Graph session credential on disk.\n\nMitigation: Install only when this access is intentional, treat the token file like a password, avoid managed or shared machines, and delete ~/.openclaw/credentials/outlook-msal.json when access is no longer needed.\n\nRisk: The skill can persist sensitive Outlook mailbox data, attachment metadata, and a local search database in unencrypted files.\n\nMitigation: Prefer in-memory read and draft commands when possible, run bulk export only with explicit --yes, protect the local data directory, and remove ~/.openclaw/workspace/data/outlook-emails after use.\n\nRisk: The reused Teams token may carry broader Microsoft Graph scopes than the skill's main mail workflow requires.\n\nMitigation: Review before installation, use only on accounts where this access is acceptable, and prefer a standard least-privilege OAuth-based Outlook integration when possible.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/globalcaos/skills/outlook-hack)\n- [ClawHub publisher profile](https://clawhub.ai/user/globalcaos)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Files]\n\n**Output Format:** [Terminal text, JSON responses, Markdown email digests, draft email bodies, downloaded attachments, and SQLite/JSONL local files]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Send operations are blocked; bulk mailbox export requires explicit --yes and writes unencrypted local files.]\n\n## Skill Version(s):\n\n3.1.0 (source: SKILL.md frontmatter and server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.0.2: 4 files, 14198 bytes\n\nFiles: _meta.json (131b), scripts/outlook-mail-fetch.mjs (10360b), scripts/outlook.mjs (25026b), SKILL.md (5772b)\n\nFile v1.0.2:SKILL.md\n\n---\nname: outlook-hack\nversion: 3.0.0\ndescription: \"Your agent reads Outlook email all day. Drafts replies for you. Won't send a single one. Not even if you ask nicely.\"\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📧\",\n        \"os\": [\"linux\", \"darwin\"],\n        \"requires\": { \"capabilities\": [\"browser\"] },\n        \"notes\":\n          {\n            \"security\": \"This skill captures Outlook Web session tokens via browser tab sharing to make direct REST calls to Microsoft's Outlook REST API v2.0. No API keys or admin approval needed. SENDING IS CODE-DISABLED: the fetch script physically blocks /sendmail, /reply, /replyall, /forward. It reads, searches, and creates drafts only. Drafts land in the user's Drafts folder for manual review and sending. Tokens are stored at ~/.openclaw/credentials/outlook-msal.json with 0600 permissions.\",\n          },\n      },\n  }\n---\n\n# Outlook Hack\n\n**Your AI agent won't email the CEO at 3am.**\n\nNot because there's a setting. Not because there's a policy. Because the code physically cannot send emails. We removed that capability the way you'd remove a chainsaw from a toddler — completely and without negotiation.\n\n## What It Does\n\n- 📧 Read, search, and bulk-fetch emails across all folders\n- 📎 Index all attachments (name, type, size) per message\n- 📊 Generate digest summaries with top senders, unread counts, full body text\n- ✏️ Create email drafts (lands in Drafts folder — never sends)\n- 📅 Access calendar events, 👥 Browse contacts\n\n## Quick Start\n\n### 1. Token Extraction (one-time, ~30 seconds)\n\n**Extract from the Teams tab, NOT Outlook.** Classic Outlook no longer exists in most orgs, and new Outlook uses PoP tokens that can't be extracted. The Teams tab provides an MSAL refresh token (90-day, auto-rotating) that powers both this skill and the `teams-hack` skill.\n\nOpen **Microsoft Teams** (`teams.cloud.microsoft`) in Chrome with the OpenClaw browser relay attached. Then run this in-browser evaluation:\n\n```javascript\n(() => {\n  const keys = Object.keys(localStorage).filter(\n    (k) => k.includes(\"refreshtoken\") || k.includes(\"RefreshToken\"),\n  );\n  const parsed = JSON.parse(localStorage.getItem(keys[0]));\n  const accountKeys = Object.keys(localStorage).filter((k) => {\n    try {\n      return JSON.parse(localStorage.getItem(k)).tenantId;\n    } catch {\n      return false;\n    }\n  });\n  let tenantId = null;\n  for (const k of accountKeys) {\n    try {\n      tenantId = JSON.parse(localStorage.getItem(k)).tenantId;\n      break;\n    } catch {}\n  }\n  return { secret: parsed.secret, tenantId };\n})();\n```\n\nSave the token via the `teams` CLI (NOT the outlook-mail-fetch script):\n\n```bash\nteams token store --refresh-token \"<secret>\" --tenant-id \"<tenantId>\"\n```\n\n### 2. Verify Access\n\n```bash\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --test\n```\n\n### 3. Bulk Fetch\n\n```bash\n# Last 6 months (default)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all\n\n# Custom range\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --months 12\n```\n\n**Output:** `~/.openclaw/workspace/data/outlook-emails/`\n\n- `raw-emails.jsonl` — full email data (subject, from, to, body text, preview)\n- `attachments-index.jsonl` — every attachment per message\n- `email-summary.md` — readable digest with stats and per-email summaries\n\n## Critical: Teams is the Token Source\n\n| Source                                  | Token Type                      | Extractable?       | Lifetime                |\n| --------------------------------------- | ------------------------------- | ------------------ | ----------------------- |\n| Teams (`teams.cloud.microsoft`)         | MSAL refresh token              | ✅ Yes             | 90 days, auto-rotates   |\n| New Outlook (`outlook.cloud.microsoft`) | PoP token (Proof-of-Possession) | ❌ No              | Crypto-bound to browser |\n| Classic Outlook (`outlook.office.com`)  | Bearer access token             | ⚠️ Deprecated/gone | Most orgs migrated      |\n\n**Always extract from the Teams tab.** New Outlook uses Proof-of-Possession tokens that cannot be extracted or replayed. Classic Outlook is deprecated and no longer available in most orgs.\n\n## How It Works (Technical)\n\n1. Share your **Microsoft Teams** tab with OpenClaw via the Browser Relay\n2. The agent reads `localStorage` to extract the MSAL refresh token\n3. Token is stored and exchanged for a Graph API access token via `teams token store`\n4. Both this skill and `teams-hack` share `~/.openclaw/credentials/outlook-msal.json` (0600)\n5. The `outlook-mail-fetch.mjs` script uses the Graph API access token for mail operations\n6. Refresh token lasts 90 days and auto-rotates on each use\n\nThe skill is NOT scraping the page. It speaks Outlook's own REST API, authenticated through your existing browser session.\n\n## Token Lifetime & Refresh\n\n- Refresh token: 90 days, auto-rotates on each use (shared with `teams-hack`)\n- Access token: ~1 hour, automatically refreshed by the scripts\n- Any cron job using either skill keeps the refresh token alive\n- When expired: re-extract from Teams tab (one browser relay session)\n\n## Architecture Notes\n\n- **Zero external dependencies** — pure Node.js (v18+), no npm packages\n- **Send-blocked** — the script has no send/reply/forward functions. They don't exist.\n- **Rate-limited** — fetches 50 emails per page with automatic pagination\n- **Body text cleaned** — HTML stripped, whitespace normalized, truncated to 3000 chars per email\n\n## The Full Stack\n\nPair with [**whatsapp-ultimate**](https://clawhub.com/globalcaos/whatsapp-ultimate) for messaging and [**jarvis-voice**](https://clawhub.com/globalcaos/jarvis-voice) for voice.\n\n👉 **[Clone it. Fork it. Break it. Make it yours.](https://github.com/globalcaos/tinkerclaw)**\n\nFile v1.0.2:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"1.0.2\",\n  \"publishedAt\": 1780764670299\n}\n\nArchive v1.0.1: 4 files, 14198 bytes\n\nFiles: scripts/outlook-mail-fetch.mjs (10360b), scripts/outlook.mjs (25026b), SKILL.md (5772b), _meta.json (131b)\n\nFile v1.0.1:SKILL.md\n\n---\nname: outlook-hack\nversion: 3.0.0\ndescription: \"Your agent reads Outlook email all day. Drafts replies for you. Won't send a single one. Not even if you ask nicely.\"\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📧\",\n        \"os\": [\"linux\", \"darwin\"],\n        \"requires\": { \"capabilities\": [\"browser\"] },\n        \"notes\":\n          {\n            \"security\": \"This skill captures Outlook Web session tokens via browser tab sharing to make direct REST calls to Microsoft's Outlook REST API v2.0. No API keys or admin approval needed. SENDING IS CODE-DISABLED: the fetch script physically blocks /sendmail, /reply, /replyall, /forward. It reads, searches, and creates drafts only. Drafts land in the user's Drafts folder for manual review and sending. Tokens are stored at ~/.openclaw/credentials/outlook-msal.json with 0600 permissions.\",\n          },\n      },\n  }\n---\n\n# Outlook Hack\n\n**Your AI agent won't email the CEO at 3am.**\n\nNot because there's a setting. Not because there's a policy. Because the code physically cannot send emails. We removed that capability the way you'd remove a chainsaw from a toddler — completely and without negotiation.\n\n## What It Does\n\n- 📧 Read, search, and bulk-fetch emails across all folders\n- 📎 Index all attachments (name, type, size) per message\n- 📊 Generate digest summaries with top senders, unread counts, full body text\n- ✏️ Create email drafts (lands in Drafts folder — never sends)\n- 📅 Access calendar events, 👥 Browse contacts\n\n## Quick Start\n\n### 1. Token Extraction (one-time, ~30 seconds)\n\n**Extract from the Teams tab, NOT Outlook.** Classic Outlook no longer exists in most orgs, and new Outlook uses PoP tokens that can't be extracted. The Teams tab provides an MSAL refresh token (90-day, auto-rotating) that powers both this skill and the `teams-hack` skill.\n\nOpen **Microsoft Teams** (`teams.cloud.microsoft`) in Chrome with the OpenClaw browser relay attached. Then run this in-browser evaluation:\n\n```javascript\n(() => {\n  const keys = Object.keys(localStorage).filter(\n    (k) => k.includes(\"refreshtoken\") || k.includes(\"RefreshToken\"),\n  );\n  const parsed = JSON.parse(localStorage.getItem(keys[0]));\n  const accountKeys = Object.keys(localStorage).filter((k) => {\n    try {\n      return JSON.parse(localStorage.getItem(k)).tenantId;\n    } catch {\n      return false;\n    }\n  });\n  let tenantId = null;\n  for (const k of accountKeys) {\n    try {\n      tenantId = JSON.parse(localStorage.getItem(k)).tenantId;\n      break;\n    } catch {}\n  }\n  return { secret: parsed.secret, tenantId };\n})();\n```\n\nSave the token via the `teams` CLI (NOT the outlook-mail-fetch script):\n\n```bash\nteams token store --refresh-token \"<secret>\" --tenant-id \"<tenantId>\"\n```\n\n### 2. Verify Access\n\n```bash\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --test\n```\n\n### 3. Bulk Fetch\n\n```bash\n# Last 6 months (default)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all\n\n# Custom range\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --months 12\n```\n\n**Output:** `~/.openclaw/workspace/data/outlook-emails/`\n\n- `raw-emails.jsonl` — full email data (subject, from, to, body text, preview)\n- `attachments-index.jsonl` — every attachment per message\n- `email-summary.md` — readable digest with stats and per-email summaries\n\n## Critical: Teams is the Token Source\n\n| Source                                  | Token Type                      | Extractable?       | Lifetime                |\n| --------------------------------------- | ------------------------------- | ------------------ | ----------------------- |\n| Teams (`teams.cloud.microsoft`)         | MSAL refresh token              | ✅ Yes             | 90 days, auto-rotates   |\n| New Outlook (`outlook.cloud.microsoft`) | PoP token (Proof-of-Possession) | ❌ No              | Crypto-bound to browser |\n| Classic Outlook (`outlook.office.com`)  | Bearer access token             | ⚠️ Deprecated/gone | Most orgs migrated      |\n\n**Always extract from the Teams tab.** New Outlook uses Proof-of-Possession tokens that cannot be extracted or replayed. Classic Outlook is deprecated and no longer available in most orgs.\n\n## How It Works (Technical)\n\n1. Share your **Microsoft Teams** tab with OpenClaw via the Browser Relay\n2. The agent reads `localStorage` to extract the MSAL refresh token\n3. Token is stored and exchanged for a Graph API access token via `teams token store`\n4. Both this skill and `teams-hack` share `~/.openclaw/credentials/outlook-msal.json` (0600)\n5. The `outlook-mail-fetch.mjs` script uses the Graph API access token for mail operations\n6. Refresh token lasts 90 days and auto-rotates on each use\n\nThe skill is NOT scraping the page. It speaks Outlook's own REST API, authenticated through your existing browser session.\n\n## Token Lifetime & Refresh\n\n- Refresh token: 90 days, auto-rotates on each use (shared with `teams-hack`)\n- Access token: ~1 hour, automatically refreshed by the scripts\n- Any cron job using either skill keeps the refresh token alive\n- When expired: re-extract from Teams tab (one browser relay session)\n\n## Architecture Notes\n\n- **Zero external dependencies** — pure Node.js (v18+), no npm packages\n- **Send-blocked** — the script has no send/reply/forward functions. They don't exist.\n- **Rate-limited** — fetches 50 emails per page with automatic pagination\n- **Body text cleaned** — HTML stripped, whitespace normalized, truncated to 3000 chars per email\n\n## The Full Stack\n\nPair with [**whatsapp-ultimate**](https://clawhub.com/globalcaos/whatsapp-ultimate) for messaging and [**jarvis-voice**](https://clawhub.com/globalcaos/jarvis-voice) for voice.\n\n👉 **[Clone it. Fork it. Break it. Make it yours.](https://github.com/globalcaos/tinkerclaw)**\n\nFile v1.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1772974529849\n}\n\nArchive v5.0.0: 4 files, 14679 bytes\n\nFiles: scripts/outlook-mail-fetch.mjs (10339b), scripts/outlook.mjs (25693b), SKILL.md (6685b), _meta.json (131b)\n\nFile v5.0.0:SKILL.md\n\n---\nname: outlook-hack\nversion: 5.0.0\ndescription: \"Your agent reads Outlook email all day. Drafts replies for you. Won't send a single one. 90 days per browser tap.\"\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📧\",\n        \"os\": [\"linux\", \"darwin\"],\n        \"requires\": { \"capabilities\": [\"browser\"] },\n        \"notes\":\n          {\n            \"security\": \"This skill captures a refresh token from Microsoft Teams' localStorage via browser tab sharing, then uses it to call Microsoft Graph API. No API keys or admin approval needed. SENDING IS CODE-DISABLED: the fetch script physically blocks /sendmail, /reply, /replyall, /forward. It reads, searches, and creates drafts only. Drafts land in the user's Drafts folder for manual review and sending. Tokens are stored at ~/.openclaw/credentials/outlook-msal.json with 0600 permissions. Refresh tokens auto-rotate and last 90+ days.\",\n          },\n      },\n  }\n---\n\n# Outlook Hack\n\n**Your AI agent won't email the CEO at 3am.**\n\nNot because there's a setting. Not because there's a policy. Because the code physically cannot send emails. We removed that capability the way you'd remove a chainsaw from a toddler — completely and without negotiation.\n\n## What It Does\n\n- 📧 Read, search, and bulk-fetch emails across all folders\n- 📎 Index all attachments (name, type, size) per message\n- 📊 Generate digest summaries with top senders, unread counts, full body text\n- ✏️ Create email drafts (lands in Drafts folder — never sends)\n- 📅 Access calendar events, 👥 Browse contacts\n\n## Quick Start\n\n### 1. Token Extraction (one-time, ~30 seconds)\n\nOpen **Microsoft Teams** (`teams.cloud.microsoft`) in Chrome with the OpenClaw browser relay attached. Then extract the refresh token from localStorage:\n\n```javascript\n// Extract the MSAL refresh token from Teams localStorage\nconst keys = Object.keys(localStorage).filter(k => k.includes('refreshtoken'));\nconst parsed = JSON.parse(localStorage.getItem(keys[0]));\n// parsed.secret is the refresh token\n```\n\nSave credentials to `~/.openclaw/credentials/outlook-msal.json`:\n\n```json\n{\n  \"client_id\": \"5e3ce6c0-2b1f-4285-8d4b-75ee78787346\",\n  \"tenant_id\": \"<your-tenant-id>\",\n  \"refresh_token\": \"<the-secret-value>\",\n  \"origin\": \"https://teams.cloud.microsoft\",\n  \"scope\": \"https://graph.microsoft.com/.default offline_access\",\n  \"api\": \"graph\",\n  \"updated_at\": \"<iso-timestamp>\"\n}\n```\n\n### 2. Verify Access\n\n```bash\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --test\n```\n\n### 3. Bulk Fetch\n\n```bash\n# Last 6 months (default)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all\n\n# Custom range\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --months 12\n```\n\n**Output:** `~/.openclaw/workspace/data/outlook-emails/`\n- `raw-emails.jsonl` — full email data (subject, from, to, body text, preview)\n- `attachments-index.jsonl` — every attachment per message\n- `email-summary.md` — readable digest with stats and per-email summaries\n\n## The Teams Refresh Token Hack\n\nMicrosoft killed the classic Outlook web app in 2026. The new Outlook (`outlook.cloud.microsoft`) uses PoP (Proof-of-Possession) tokens that are cryptographically bound to the browser — they cannot be extracted or replayed.\n\n**The workaround:** Microsoft Teams stores a standard MSAL refresh token in `localStorage`. This refresh token can be exchanged for a Graph API access token that includes full mail scopes — no admin consent required, because Teams' first-party client ID already has those permissions pre-authorized.\n\nThe trick: the token endpoint requires a `Origin: https://teams.cloud.microsoft` header (SPA client enforcement), which curl/fetch happily provides.\n\n### Why This Works Without Admin Consent\n\n- Teams' client ID (`5e3ce6c0-2b1f-4285-8d4b-75ee78787346`) is a Microsoft first-party app\n- First-party apps have pre-authorized access to Graph API scopes\n- The refresh token inherits the user's existing session — no new consent prompt\n- Your tenant admin never sees a consent request because there isn't one\n\n## Token Lifetime & Refresh\n\n- **Refresh tokens last 90+ days** and auto-rotate on each use\n- The script stores the new refresh token after every token exchange\n- As long as the script runs at least once every 90 days, **you never touch the browser again**\n- Only breaks if: password change, Teams session revoked, or 90+ days of inactivity\n- One browser tap per quarter at most. Realistically: once, ever.\n\n## How It Works (Technical)\n\n1. Share your **Teams** tab with OpenClaw via the Browser Relay (one time)\n2. The agent reads `localStorage` to extract the MSAL refresh token\n3. Token is saved to `~/.openclaw/credentials/outlook-msal.json` (0600 permissions)\n4. The `outlook-mail-fetch.mjs` script exchanges the refresh token for a Graph API access token\n5. Script makes REST calls to `https://graph.microsoft.com/v1.0/`\n6. New refresh token is saved after each exchange — perpetual access\n\n## Architecture Notes\n\n- **Zero external dependencies** — pure Node.js (v18+), no npm packages\n- **Send-blocked** — the script has no send/reply/forward functions. They don't exist.\n- **Rate-limited** — fetches 50 emails per page with automatic pagination\n- **Body text cleaned** — HTML stripped, whitespace normalized, truncated to 3000 chars per email\n- **Graph API v1.0** — uses Microsoft's current, supported API (not the deprecated Outlook REST v2.0)\n\n## Sibling Skill: Teams Hack\n\nThis skill shares the same MSAL refresh token with [**teams-hack**](https://clawhub.com/globalcaos/teams-hack). **One extraction covers both.** Extract the token once from Teams localStorage → get full email access (this skill) AND chat/channels/search access (Teams Hack).\n\nBoth skills read and write to the same credentials file:\n```\n~/.openclaw/credentials/outlook-msal.json\n```\n\nIf either skill refreshes the token, the other benefits automatically. The token auto-rotates on every use and lasts 90+ days.\n\n| Skill | What it does | Send-blocked? |\n|-------|-------------|---------------|\n| **outlook-hack** (this) | Email: read, search, draft, folders, attachments, calendar, contacts | ✅ Cannot send |\n| **[teams-hack](https://clawhub.com/globalcaos/teams-hack)** | Chat: read, send, channels, search, presence, org directory | No (chat sending enabled) |\n\n## The Full Stack\n\nPair with [**teams-hack**](https://clawhub.com/globalcaos/teams-hack) for chat, [**whatsapp-ultimate**](https://clawhub.com/globalcaos/whatsapp-ultimate) for messaging, and [**jarvis-voice**](https://clawhub.com/globalcaos/jarvis-voice) for voice.\n\n👉 **[Clone it. Fork it. Break it. Make it yours.](https://github.com/globalcaos/clawdbot-moltbot-openclaw)**\n\nFile v5.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"5.0.0\",\n  \"publishedAt\": 1771959955521\n}\n\nArchive v4.0.0: 3 files, 6972 bytes\n\nFiles: scripts/outlook-mail-fetch.mjs (10339b), SKILL.md (5782b), _meta.json (131b)\n\nFile v4.0.0:SKILL.md\n\n---\nname: outlook-hack\nversion: 4.0.0\ndescription: \"Your agent reads Outlook email all day. Drafts replies for you. Won't send a single one. Token refreshes itself for 90+ days — one browser tap and you're done for the quarter.\"\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"📧\",\n        \"os\": [\"linux\", \"darwin\"],\n        \"requires\": { \"capabilities\": [\"browser\"] },\n        \"notes\":\n          {\n            \"security\": \"This skill captures a refresh token from Microsoft Teams' localStorage via browser tab sharing, then uses it to call Microsoft Graph API. No API keys or admin approval needed. SENDING IS CODE-DISABLED: the fetch script physically blocks /sendmail, /reply, /replyall, /forward. It reads, searches, and creates drafts only. Drafts land in the user's Drafts folder for manual review and sending. Tokens are stored at ~/.openclaw/credentials/outlook-msal.json with 0600 permissions. Refresh tokens auto-rotate and last 90+ days.\",\n          },\n      },\n  }\n---\n\n# Outlook Hack\n\n**Your AI agent won't email the CEO at 3am.**\n\nNot because there's a setting. Not because there's a policy. Because the code physically cannot send emails. We removed that capability the way you'd remove a chainsaw from a toddler — completely and without negotiation.\n\n## What It Does\n\n- 📧 Read, search, and bulk-fetch emails across all folders\n- 📎 Index all attachments (name, type, size) per message\n- 📊 Generate digest summaries with top senders, unread counts, full body text\n- ✏️ Create email drafts (lands in Drafts folder — never sends)\n- 📅 Access calendar events, 👥 Browse contacts\n\n## Quick Start\n\n### 1. Token Extraction (one-time, ~30 seconds)\n\nOpen **Microsoft Teams** (`teams.cloud.microsoft`) in Chrome with the OpenClaw browser relay attached. Then extract the refresh token from localStorage:\n\n```javascript\n// Extract the MSAL refresh token from Teams localStorage\nconst keys = Object.keys(localStorage).filter(k => k.includes('refreshtoken'));\nconst parsed = JSON.parse(localStorage.getItem(keys[0]));\n// parsed.secret is the refresh token\n```\n\nSave credentials to `~/.openclaw/credentials/outlook-msal.json`:\n\n```json\n{\n  \"client_id\": \"5e3ce6c0-2b1f-4285-8d4b-75ee78787346\",\n  \"tenant_id\": \"<your-tenant-id>\",\n  \"refresh_token\": \"<the-secret-value>\",\n  \"origin\": \"https://teams.cloud.microsoft\",\n  \"scope\": \"https://graph.microsoft.com/.default offline_access\",\n  \"api\": \"graph\",\n  \"updated_at\": \"<iso-timestamp>\"\n}\n```\n\n### 2. Verify Access\n\n```bash\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --test\n```\n\n### 3. Bulk Fetch\n\n```bash\n# Last 6 months (default)\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all\n\n# Custom range\nnode {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --months 12\n```\n\n**Output:** `~/.openclaw/workspace/data/outlook-emails/`\n- `raw-emails.jsonl` — full email data (subject, from, to, body text, preview)\n- `attachments-index.jsonl` — every attachment per message\n- `email-summary.md` — readable digest with stats and per-email summaries\n\n## The Teams Refresh Token Hack\n\nMicrosoft killed the classic Outlook web app in 2026. The new Outlook (`outlook.cloud.microsoft`) uses PoP (Proof-of-Possession) tokens that are cryptographically bound to the browser — they cannot be extracted or replayed.\n\n**The workaround:** Microsoft Teams stores a standard MSAL refresh token in `localStorage`. This refresh token can be exchanged for a Graph API access token that includes full mail scopes — no admin consent required, because Teams' first-party client ID already has those permissions pre-authorized.\n\nThe trick: the token endpoint requires a `Origin: https://teams.cloud.microsoft` header (SPA client enforcement), which curl/fetch happily provides.\n\n### Why This Works Without Admin Consent\n\n- Teams' client ID (`5e3ce6c0-2b1f-4285-8d4b-75ee78787346`) is a Microsoft first-party app\n- First-party apps have pre-authorized access to Graph API scopes\n- The refresh token inherits the user's existing session — no new consent prompt\n- Your tenant admin never sees a consent request because there isn't one\n\n## Token Lifetime & Refresh\n\n- **Refresh tokens last 90+ days** and auto-rotate on each use\n- The script stores the new refresh token after every token exchange\n- As long as the script runs at least once every 90 days, **you never touch the browser again**\n- Only breaks if: password change, Teams session revoked, or 90+ days of inactivity\n- One browser tap per quarter at most. Realistically: once, ever.\n\n## How It Works (Technical)\n\n1. Share your **Teams** tab with OpenClaw via the Browser Relay (one time)\n2. The agent reads `localStorage` to extract the MSAL refresh token\n3. Token is saved to `~/.openclaw/credentials/outlook-msal.json` (0600 permissions)\n4. The `outlook-mail-fetch.mjs` script exchanges the refresh token for a Graph API access token\n5. Script makes REST calls to `https://graph.microsoft.com/v1.0/`\n6. New refresh token is saved after each exchange — perpetual access\n\n## Architecture Notes\n\n- **Zero external dependencies** — pure Node.js (v18+), no npm packages\n- **Send-blocked** — the script has no send/reply/forward functions. They don't exist.\n- **Rate-limited** — fetches 50 emails per page with automatic pagination\n- **Body text cleaned** — HTML stripped, whitespace normalized, truncated to 3000 chars per email\n- **Graph API v1.0** — uses Microsoft's current, supported API (not the deprecated Outlook REST v2.0)\n\n## The Full Stack\n\nPair with [**whatsapp-ultimate**](https://clawhub.com/globalcaos/whatsapp-ultimate) for messaging and [**jarvis-voice**](https://clawhub.com/globalcaos/jarvis-voice) for voice.\n\n👉 **[Clone it. Fork it. Break it. Make it yours.](https://github.com/globalcaos/clawdbot-moltbot-openclaw)**\n\nFile v4.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"4.0.0\",\n  \"publishedAt\": 1771956490805\n}","readmeExcerpt":"Skill: TinkerClaw Outlook Owner: globalcaos Summary: Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in. Tags: latest:3.4.2 Version history: v3.4.2 | 2026-09-07T13:06:46.696Z | user Closes remaining scanner findings on 3.4.1: Graph ","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"printf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --test --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --list-drafts --access-token-stdin --limit 15\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get '<message-id>' --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get-attachments '<message-id>' --access-token-stdin --out '<private-dir>'\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --patch-draft '<draft-id>' --body-file body.html --keep-signature --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes --months 6 --access-token-stdin"},{"language":"bash","snippet":"printf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --test --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --list-drafts --access-token-stdin --limit 15\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get '<message-id>' --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get-attachments '<message-id>' --access-token-stdin --out '<private-dir>'\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --patch-draft '<draft-id>' --body-file body.html --keep-signature --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes --months 6 --access-token-stdin"},{"language":"bash","snippet":"printf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --test --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --list-drafts --access-token-stdin --limit 15\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get '<message-id>' --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --get-attachments '<message-id>' --access-token-stdin --out '<private-dir>'\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --patch-draft '<draft-id>' --body-file body.html --keep-signature --access-token-stdin\nprintf '%s' \"$OUTLOOK_ACCESS_TOKEN\" | node {baseDir}/scripts/outlook-mail-fetch.mjs --fetch-all --yes --months 6 --access-token-stdin"},{"language":"bash","snippet":"# Revoke this skill's access (delete the stored token). For full revocation, also sign out of\n# Teams / revoke sessions in your Microsoft account security page.\nrm ~/.openclaw/credentials/outlook-msal.json\n\n# Never write mail to disk: just don't pass --yes. --test, --list-drafts, `mail list`,\n# `mail read` all keep data in memory. Bulk export refuses to run without --yes.\n\n# Delete everything already exported to disk (bodies, index, digest, SQLite DB):\nrm -rf ~/.openclaw/workspace/data/outlook-emails"},{"language":"bash","snippet":"> PORT=18792   # browser.profiles.chrome-relay.cdpUrl in ~/.openclaw/openclaw.json\n> curl -s \"http://127.0.0.1:$PORT/extension/status\"   # want {\"connected\":true,\"count\":>=1}\n> curl -s \"http://127.0.0.1:$PORT/json/version\"       # want \"Browser\":\"OpenClaw/extension-relay\"\n> curl -s \"http://127.0.0.1:$PORT/json/list\"          # want a page whose url is teams.cloud.microsoft\n>"},{"language":"javascript","snippet":"> (() => {\n>   for (const k of Object.keys(localStorage)) {\n>     if (!/accesstoken/i.test(k)) continue;\n>     let v; try { v = JSON.parse(localStorage.getItem(k)); } catch { continue; }\n>     if (/graph\\.microsoft\\.com/i.test(v.target || '') && /Mail\\.ReadWrite/i.test(v.target))\n>       return { secret: v.secret, expiresOn: v.expiresOn, scopes: v.target };\n>   }\n>   return { err: 'no graph mail access token' };\n> })()\n>"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: outlook-hack\nversion: 3.4.2\ndescription: \"Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in.\"\nmetadata:\n  openclaw:\n    emoji: \"📧\"\n    os: [\"linux\", \"darwin\"]\n    requires:\n      capabilities: [\"network\", \"file_write\"]\n    permissions:\n      network: \"Credentialed HTTPS requests only to graph.microsoft.com; enforced by a runtime allowlist.\"\n      file_write: \"No credential storage. Optional mailbox exports require --yes and use mode 0600 files inside a mode 0700 directory.\"\n      mail_write: \"Can create and patch drafts. No send, reply-to-network, forward-to-network, move, flag, or delete operation is shipped.\"\n---\n\n> Part of **[TinkerClaw](https://github.com/globalcaos/tinkerclaw)**.\n\n# Outlook — read, search, and draft; never send\n\nThis skill reads Outlook mail and creates or edits drafts through Microsoft Graph. The shipped client has no send endpoint. It does not install packages, run subprocesses, store credentials, contact telemetry services, or transmit mailbox data anywhere except Microsoft Graph.\n\n## What ships\n\nOne zero-dependency Node.js client: `scripts/outlook-mail-fetch.mjs`.\n\nIt can:\n- list recent messages and drafts;\n- read a complete message body;\n- download attachments to a directory you choose;\n- create or patch drafts while preserving the existing signature;\n- export mailbox bodies and attachment metadata locally, only with `--fetch-all --yes`.\n\nIt cannot send, reply, forward, permanently delete, move, flag, read contacts, or read calendars. Those capabilities are intentionally outside this public package.\n\n## Permissions, data flow, and consent\n\n**Authentication.** Supply a short-lived Microsoft Graph access token on standard input for each invocation. This package does not extract, refresh, print, or store tokens. Obtain the token through a Microsoft-supported login tool or identity flow approved by your organisation, requesting only the mail scopes needed for the command.\n\n**Network boundary.** Every credentialed request is runtime-checked before the token is read. The destination must be exactly `https://graph.microsoft.com` with no user information, custom port, HTTP downgrade, or lookalike subdomain. Pagination URLs are checked by the same function.\n\n**Local mailbox data.** Normal list/read commands print to the current process and do not create a mailbox mirror. Bulk export refuses to run without `--yes`. Export directories are rejected if they are symlinks and forced to mode `0700`; exported bodies, summaries, indexes, and downloaded attachments reject symlink targets and are forced to mode `0600`, including pre-existing paths. Attachment downloads never overwrite an existing file: they allocate a unique name and create it with exclusive `wx`. Message IDs are restricted to Graph-safe characters and encoded before they ente"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7623hrcwt6rg73a67xw3wyx580asdw\",\n  \"slug\": \"outlook-hack\",\n  \"version\": \"3.4.2\",\n  \"publishedAt\": 1788786406696\n}"},{"path":"skill-card.md","content":"## Description:\n\nRead and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[globalcaos](https://clawhub.ai/user/globalcaos)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to inspect Outlook mail, download selected attachments, and prepare or update drafts through Microsoft Graph while leaving final sending to a human in Outlook.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: A Microsoft Graph token grants access to sensitive mailbox data and draft operations.\n\nMitigation: Use a short-lived token scoped only to the mail actions needed for the command.\n\nRisk: Email content and generated summaries may contain deceptive or untrusted instructions.\n\nMitigation: Treat message content and summaries as untrusted context and do not follow instructions found in mail without independent review.\n\nRisk: Optional mailbox exports and attachment downloads write plaintext local files.\n\nMitigation: Use exports only when needed, keep them in private directories, and remove local plaintext data when it is no longer required.\n\nRisk: Draft creation or patching can alter mailbox state even though the skill does not send mail.\n\nMitigation: Review every draft manually in Outlook before sending or relying on it.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/globalcaos/skills/outlook-hack)\n- [Microsoft Graph endpoint](https://graph.microsoft.com)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, code, configuration, guidance]\n\n**Output Format:** [Markdown guidance with shell command examples and command output from Microsoft Graph mail operations]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May produce local plaintext mailbox exports and attachment files only when explicitly requested with the consent flag.]\n\n## Skill Version(s):\n\n3.4.2 (source: frontmatter and server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in. Skill: TinkerClaw Outlook Owner: globalcaos Summary: Read and search Outlook, inspect attachments, and create or edit drafts without any send endpoint. Uses one short-lived Microsoft Graph access token supplied on stdin for one run; it never stores credentials. Bulk mailbox export is opt-in. Tags: latest:3.4.2 Version history: v3.4.2 | 2026-09-07T13:06:46.696Z | user Closes remaining scanner findings on 3.4.1: Graph","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1562,"uniquenessScore":46,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T02:22:58.506Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T02:22:58.506Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T04:33:06.364Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}