A Microsoft Teams bot that gives a natural-language interface to Outlook mail and calendar, powered by a Claude tool-use loop. It reads freely — and never writes without your explicit confirmation.
Read tools run immediately. Write tools never run on the first request: the agent captures the action as pending, shows a draft, and stops. The gate lives in src/nanoclaw.ts.
Claude requests send_mail — the loop intercepts it, stores it as a pending action, and replies with a human-readable draft.
The write executes only after an affirmative ("sí", "ok", "yes"…) typed by the user in a separate, authenticated Teams message.
The confirmation is read from the trusted Teams channel — never from mail or calendar content, so a payload hidden in an email can't approve its own send.
User: @nanoclaw respondele al contador que confirmo la revisión nanoclaw: 📧 *Borrador de mail* • **Para:** estudio.contable@… • **Asunto:** RE: Contrato Pérez — revisión final • **Cuerpo:** Hola, confirmo la revisión final del contrato Pérez. Quedo a la espera. Saludos. Respondé *sí* para confirmar o *no* para cancelar. ↑ send_mail was NOT executed — pending action captured. User: sí nanoclaw: Mail enviado a estudio.contable@… con asunto "RE: Contrato Pérez — revisión final". ↑ only now does the write run.
The transcript is illustrative, not a recording of live traffic — it reproduces the confirm-before-write flow and the raw Spanish draft string the agent emits (Teams-markdown markers included; the bot was built for a Rioplatense law firm).
Six mechanisms, each one implemented in the source — file references link the claim to the code.
ALLOWED_USER_IDS holds the Teams AAD object IDs permitted to use the bot. An empty or unset list denies everyone; the check runs before any message reaches the agent loop. src/index.ts
send_mail and create_event are captured as pending actions and executed only after an explicit affirmative in a separate Teams message. src/nanoclaw.ts
Mail and calendar content returned by tools is wrapped as UNTRUSTED EXTERNAL DATA — information to report on, never instructions to follow. Enforced in the wrapper and the system prompt. src/tools.ts · src/nanoclaw.ts
send_mail validates the to address against a regex before shelling out to the Outlook CLI; malformed addresses are refused. src/tools.ts
The Dockerfile pins the olk (Outlook CLI) binary download to a verified SHA-256 hash. Dockerfile
The deployment scripts store CLIENT_SECRET, ANTHROPIC_API_KEY and the olk keyring password in GCP Secret Manager and injects them as secret-backed environment variables (--update-secrets) — plaintext values never appear in the service configuration. harden.ps1
Four tools. Reads run immediately; writes are gated.
| Tool | Type | What it does |
|---|---|---|
read_mail | read | Lists recent Outlook messages (subject, from, preview) |
read_calendar | read | Lists upcoming calendar events (subject, start, end) |
send_mail | write · gated | Sends email — requires explicit user confirmation |
create_event | write · gated | Creates a calendar event — requires explicit user confirmation |
Published for code review. Every capability on this page is implemented in the source — none of it is a claim of production traffic.