# Send O365 Email

> Sends email via Microsoft Graph API using device code OAuth. Use when the user asks to send, draft, or compose an email, or wants to email someone.

- **Type:** Skill
- **Install:** `agentstack add skill-alexjmoore8-claude-code-tools-send-o365-email`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [alexjmoore8](https://agentstack.voostack.com/s/alexjmoore8)
- **Installs:** 0
- **Category:** [Communication](https://agentstack.voostack.com/c/communication)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [alexjmoore8](https://github.com/alexjmoore8)
- **Source:** https://github.com/alexjmoore8/claude-code-tools/tree/main/plugins/o365-email/skills/send-o365-email

## Install

```sh
agentstack add skill-alexjmoore8-claude-code-tools-send-o365-email
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

Send email from your O365 account via Microsoft Graph API. No dependencies -- uses only `curl`, `awk`, `grep`, and standard POSIX tools pre-installed on Mac, Linux, WSL, and Git Bash.

## Configuration

```
TENANT_ID=YOUR_TENANT_ID
CLIENT_ID=YOUR_CLIENT_ID
TOKEN_PATH=~/.config/claude-o365/token.json
SKILL_DIR=~/.claude/skills/send-o365-email
```

Set `O365_TENANT_ID` and `O365_CLIENT_ID` as environment variables, or edit the values directly in `auth.sh`. See the README for Azure app registration instructions.

## Usage

```
/send-o365-email [--to ] [--cc ] [--subject ] [--body ] [--attach  ...]
```

All arguments optional -- ask for any missing required fields (to, subject, body) before proceeding. If the user says "send to myself", use `send.sh --me` to resolve their address. `--to` and `--cc` accept comma-separated addresses.

---

## Workflow

### Step 0: Ensure permissions

Use the `Read` tool to read `~/.claude/settings.json` (it may not exist yet).

Check whether ALL of these are present in `permissions.allow`:

```
"Skill(send-o365-email)"
"Bash(~/.claude/skills/send-o365-email/auth.sh*)"
"Bash(~/.claude/skills/send-o365-email/send.sh*)"
"Bash(chmod +x ~/.claude/skills/send-o365-email/*.sh)"
```

If any are missing, present the following to the user and ask if they want to proceed:

> **One-time permission setup for `send-o365-email`**
>
> To run headlessly (e.g. for automation or scheduled sends), this skill needs to add the following entries to your `~/.claude/settings.json`:
>
> | Permission | What it allows |
> |---|---|
> | `Skill(send-o365-email)` | Trust this skill to run |
> | `Bash(~/.claude/skills/send-o365-email/auth.sh*)` | Run the auth script |
> | `Bash(~/.claude/skills/send-o365-email/send.sh*)` | Run the send script |
> | `Bash(chmod +x ~/.claude/skills/send-o365-email/*.sh)` | Make scripts executable on first run |
>
> **Pros:** Fully headless after this -- no more prompts, works in automation and cron jobs. Permissions are narrowly scoped to this skill's scripts only.
> **Cons:** These are added to your global `~/.claude/settings.json` and apply to all Claude sessions (due to a [known Claude Code bug](https://github.com/anthropics/claude-code/issues/14956) where skill-level permissions aren't enforced).
>
> **Want to add these permissions? (yes/no)**
> - Yes: permissions added, restart Claude Code, all future runs are headless
> - No: skill still works, but you'll see approval prompts each run

If the user says yes, add all missing entries to the allow array (preserving all existing content) and write the file back with the `Write` tool. Tell the user: **"Done -- please restart Claude Code. All future runs of this skill will be fully headless."** Then stop -- do not proceed until they restart.

If the user says no, continue. They will see prompts this run -- that is fine.

If the file doesn't exist and user says yes, create it:

```json
{
  "permissions": {
    "allow": [
      "Skill(send-o365-email)",
      "Bash(~/.claude/skills/send-o365-email/auth.sh*)",
      "Bash(~/.claude/skills/send-o365-email/send.sh*)",
      "Bash(chmod +x ~/.claude/skills/send-o365-email/*.sh)"
    ]
  }
}
```

Continue to Step 1.

---

### Step 1: Ensure scripts are executable

```bash
chmod +x ~/.claude/skills/send-o365-email/*.sh
```

---

### Step 2: Check existing token

```bash
~/.claude/skills/send-o365-email/auth.sh check
```

- `TOKEN_VALID` -- skip to Step 4
- `NEEDS_REFRESH` -- go to Step 3b
- `NEEDS_AUTH` -- go to Step 3a

---

### Step 3a: Authenticate -- Device Code Flow

First, request the device code (returns immediately):

```bash
~/.claude/skills/send-o365-email/auth.sh device-start
```

This prints:
```
VISIT_URL:https://microsoft.com/devicelogin
USER_CODE:XXXXXXXX
```

Immediately tell the user:
**"Go to [VISIT_URL] and enter code [USER_CODE] to sign in. Then say 'done' when complete."**

Wait for the user to confirm they have signed in, then poll for the token (use a `timeout` of `300000`ms):

```bash
~/.claude/skills/send-o365-email/auth.sh device-poll
```

On `TOKEN_SAVED`, continue to Step 4. On error, report to user.

---

### Step 3b: Refresh token

```bash
~/.claude/skills/send-o365-email/auth.sh refresh
```

On `TOKEN_SAVED`, continue to Step 4. On `REFRESH_FAILED`, fall back to Step 3a.

---

### Step 4: Resolve "myself" / "me" recipients

If the user said "send to myself" or similar:

```bash
~/.claude/skills/send-o365-email/send.sh --me
```

Use the printed address as the recipient.

---

### Step 5: Send the email

```bash
~/.claude/skills/send-o365-email/send.sh --to "ADDR" --subject "SUBJECT" --body "BODY"
```

Add `--cc "ADDR"` if CC was provided. Add `--attach "/path/to/file"` for each attachment (repeat the flag).

**For attachments:** pass the path directly to `--attach`. The script handles Unicode spaces automatically (e.g. macOS screenshot filenames use U+202F between the time and AM/PM).

If the user drops an image into the chat, the source path is in the `[Image: source: /path/to/file]` metadata -- use that path directly with `--attach`.

Outputs:
- `SENT` = success, proceed to Step 6
- `TOKEN_EXPIRED` = go to Step 3b, then retry Step 5
- `ERROR:...` = report to user

---

### Step 6: Report result

Tell the user the email was sent, the recipient(s), and the current timestamp.

---

## Notes

- Sends from your own O365 inbox -- always the authenticated account
- Token stored at `~/.config/claude-o365/token.json` (chmod 600), same security model as AWS CLI
- Access token valid ~1hr; refresh token ~90 days; subsequent sends are headless
- No dependencies beyond `curl` and standard POSIX tools (pre-installed on Mac/Linux/WSL/Git Bash)
- Scripts live in the skill directory and are executed in place -- no copying needed

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [alexjmoore8](https://github.com/alexjmoore8)
- **Source:** [alexjmoore8/claude-code-tools](https://github.com/alexjmoore8/claude-code-tools)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-alexjmoore8-claude-code-tools-send-o365-email
- Seller: https://agentstack.voostack.com/s/alexjmoore8
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
