Secure Outlook CLI for AI Agents
Microsoft Graph's Mail.ReadWrite permission is all-or-nothing. The Secure Outlook CLI replaces that with fine-grained, scriptable rules that control exactly what AI agents can read, draft, and send.
How PortEden Protects You
Six layers of security between AI and your data.
Graph API Scoping from the Terminal
Replace the broad Mail.ReadWrite permission with specific rules per agent, such as read-only for one and draft-only for another.
Per-Agent Permission Sets
Each AI agent gets its own permission profile in your config file, so your summarizer gets read access while your reply agent gets draft-only access.
Content Filtering for Graph Responses
Control what data the Graph API returns to agents by stripping HTML, redacting headers, or returning metadata only.
Sender and Domain Blocking
Prevent agents from accessing emails from specific senders, domains, or distribution lists so sensitive communications stay invisible to AI.
What Mail.ReadWrite actually grants — and how to constrain it
The Microsoft Graph permission reference makes Mail.ReadWrite sound bounded. It isn't. A token with that scope can list every folder, read every message body, modify message properties (including marking as read or unread, changing categories, moving between folders), create drafts, and delete from any folder. The only thing it can't do is send — that requires Mail.Send. For an agent, this is functionally unrestricted.
$ npx @porteden/cli connect outlook --tenant-id 9f...
→ Microsoft login → admin consent for Mail.ReadWrite, Mail.Send
✓ Token written to keyring (outlook:adam@porteden.com, tenant=9f...)
$ porteden outlook policy show
mode: read
action_limits: {move: deny, delete: deny, mark_read: deny}
folder_allowlist: ["Inbox", "Inbox/Customers"]
delta_query: true # incremental sync, not full re-scan
graph_scope_override: Mail.Read # downgrade the token at request timeDelta queries: the right way to sync
A naive agent calls GET /me/messages?$top=50 on a tight loop and pulls gigabytes per day. Graph supports /me/mailFolders/Inbox/messages/delta — incremental change tokens that return only what's new. The Outlook CLI rewrites the agent's requests to use delta queries automatically and caches the delta link in your keyring, which drops Graph throttling errors (HTTP 429) by an order of magnitude on busy mailboxes.
Multi-tenant: the --tenant-id flag
Consulting firms and MSPs end up connecting one agent to a dozen client tenants. Each porteden outlook call accepts --tenant-id and stores per-tenant tokens separately in the keyring. CI pipelines pass the tenant via PORTEDEN_OUTLOOK_TENANT so the same config file works against every client mailbox without per-tenant branching.
Common AAD scope-grant failures
AADSTS65001— admin consent missing. Runporteden outlook auth --admin-consentto open the tenant admin URL.AADSTS50105— user not assigned to the app. Add the user to the Enterprise Application's assigned users list, or set the app to "Assignment required: No".invalid_resource— wrong Graph endpoint. The CLI defaults tograph.microsoft.com; for GCC High set--cloud usgov.
Get Started in 3 Steps
Install and Authenticate
Install the PortEden CLI with npm and authenticate with your Microsoft 365 account via OAuth.
Define Access Rules
Create a config file with your access rules including visibility levels, action limits, and sender blocks per agent.
Route Agents Through PortEden
Point your AI agents to the PortEden proxy so every Outlook request is filtered through your rules with full audit logging.
Without vs. With PortEden
Without PortEden
- Mail.ReadWrite grants full mailbox access with no granularity
- Azure AD permission changes require portal access and manual clicks
- Token management scripts vary across teams and tenants
- No per-agent access controls within the same Graph API app registration
With PortEden
- Fine-grained read-only, draft-only, or scoped write access per agent
- Access rules defined in config files and deployed via CI/CD
- Centralized token management with automatic rotation and audit logs
- Each agent gets its own permission profile within a single config