Skip to main content
Glama
jopmiddelkamp

outlook-mcp-limited

outlook-mcp-limited

A local Model Context Protocol (MCP) server that gives an AI assistant (Claude Desktop, Claude Code, Cursor, …) read + draft-only access to one personal Microsoft mailbox (@live.nl, @outlook.com, @hotmail.com). It can search, list and read mail, list folders, save attachments to a jailed folder, and create drafts. It cannot send, delete, move or mark mail, and it never touches calendar, contacts or files.

This is a cut-down fork of xdarkoy/outlook-mcp (MIT). The fork removes six tools and one OAuth scope, and adds two hard-coded allowlists that make the server refuse to start if anyone widens them. See docs/decision-note.md for why this base was chosen.

It runs in two modes: stdio on your own machine (default, 6 tools), or hosted over Streamable HTTP behind a bearer token for cloud agents such as Cursor's Grok Bot (5 tools, no attachment saving). Hosted mode puts your refresh token and your mail content on other people's servers; read docs/hosting.md before you use it.

What it can and cannot do

Allowed (tool)

Forbidden (no tool, and no OAuth scope for it)

search_emails — full-text search across the mailbox

Send mail (Mail.Send is never requested)

list_emails — list a folder with filters

Send an existing draft

read_email — one message, body as plain text, attachment metadata

Delete mail

list_folders — folder tree, one level per call

Move or mark mail

save_attachment — write one attachment into ~/Downloads/outlook-mcp/ (stdio mode only)

Calendar, contacts, OneDrive, shared mailboxes

create_draft — new draft or reply draft in Drafts; never sends

Anything as an application (no client secret, no app-only permissions)

Related MCP server: mcp-outlook-desktop

Security model

1. The OAuth token cannot send. The server requests exactly four delegated scopes, hard-coded in src/auth/scopes.ts:

Scope

What Microsoft says it allows

Why we need it

offline_access

Refresh tokens

Sign in once, not every hour. Refresh tokens last 90 days by default [9].

User.Read

Sign in and read the user's profile

Identify the signed-in account (MSA vs work)

Mail.Read

"Read user mail" [5]

search, list, read, list folders, save attachments

Mail.ReadWrite

"Create, read, update and delete email in user mailboxes" [5]

create_draft — Graph requires Mail.ReadWrite to create a draft [8]

Mail.Send is the only permission that lets a token call POST /me/sendMail [6] or POST /me/messages/{id}/send [7]. It is not requested, so both calls fail with HTTP 403. The consent screen you see at first login therefore never says "send mail as you". npm run smoke:live proves this against your real mailbox (see Acceptance checklist).

2. The tool list cannot grow by accident. src/tools/registry.ts holds the six allowed tool names. At startup main() runs both guards; on any extra, missing or duplicate tool, or any scope outside the allowlist, the process prints refusing to start and exits 1 before it serves a single request. The tests in scripts/test-policy.mjs and scripts/test-mail-trust-boundary.mjs pin both lists a third time.

3. Residual risk you should know about. Mail.ReadWrite also covers update and delete [5]. This server exposes no tool for that, but the token could do it. Anyone who steals ~/.outlook-mcp/cache.json can read your mail and edit or delete messages until you revoke consent. Treat that file like a password.

4. Token cache. MSAL writes ~/.outlook-mcp/cache.json (override: OUTLOOK_MCP_CACHE_DIR) with mode 0600, via atomic write-to-temp-and-rename. No cloud, no keychain, no telemetry. To wipe it:

rm -rf ~/.outlook-mcp

5. Revoke. Go to https://microsoft.com/consent, sign in with the mailbox account, open the app and choose Remove these permissions [10]. Then delete the cache as above. Deleting the app registration in Entra also kills every token issued for it.

6. No client secret. The app registration is a public client using the device code flow [2]. Nothing secret is stored in this repo or on disk except the token cache itself.

7. Attachments are jailed. save_attachment only writes inside OUTLOOK_MCP_ALLOWED_DIR (default ~/Downloads/outlook-mcp/), never overwrites, and rejects path traversal.

8. Hosted mode is locked and stateless. In http mode, initialize, ping and tools/list are public metadata (tool names and descriptions, nothing else) so a hosting platform can register the server. Every other request to /mcp must carry Authorization: Bearer <MCP_AUTH_TOKEN> (32+ characters, compared in constant time) and is rate limited (default 60/min). Each request gets a fresh MCP server; nothing is kept between requests. The signed-in session comes from the OUTLOOK_MCP_TOKEN_CACHE variable, which you create locally with npm run export-token. Without a usable MCP_AUTH_TOKEN the server runs locked: mail calls answer 503 and GET /healthz reports "status":"locked" plus which settings are present (never their values). Full runbook and the risks you accept: docs/hosting.md.

Quick start (about 30 minutes, once)

  1. Register the app in Microsoft Entra — follow docs/entra-setup.md. You end with an Application (client) ID. Personal accounts only, public client flows on, four delegated permissions, no secret.

  2. Install and build (Node 20 or newer; this repo was built with Node 26):

    cd /path/to/outlook-mcp
    npm install
    npm run build
  3. Sign in once (device code flow; prints a URL and a code):

    OUTLOOK_MCP_CLIENT_ID=<your-client-id> npm run login

    Open the URL, enter the code, sign in with you@live.nl, and read the consent screen: it must list only read/write mail, profile and offline access — not "send mail as you". Accept.

  4. Smoke test against the real mailbox:

    OUTLOOK_MCP_CLIENT_ID=<your-client-id> npm run smoke:live

    Expected: every line PASS, including the two AC5 … refused lines with HTTP 403. The script leaves one draft addressed to yourself in Drafts; check it in Outlook web, then delete it by hand.

  5. Connect a client — see docs/wire-up-notes.md. Short version for Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS [13]):

    {
      "mcpServers": {
        "outlook": {
          "command": "node",
          "args": ["/path/to/outlook-mcp/dist/index.js"],
          "env": { "OUTLOOK_MCP_CLIENT_ID": "<your-client-id>" }
        }
      }
    }

Configuration

Variable

Required

Default

Purpose

OUTLOOK_MCP_CLIENT_ID

yes

Application (client) ID of your Entra app registration. Not a secret, but keep it out of git.

OUTLOOK_MCP_TENANT

no

consumers

Authority tenant. consumers = personal Microsoft accounts only, which is what this fork is for and which blocks an accidental work-account login. common also accepts work accounts.

OUTLOOK_MCP_ALLOWED_DIR

no

~/Downloads/outlook-mcp/

Where save_attachment may write files.

OUTLOOK_MCP_CACHE_DIR

no

~/.outlook-mcp/

Token cache location.

OUTLOOK_MCP_MAX_ATTACHMENT_MB

no

50

Hard cap before save_attachment aborts.

MCP_AUTH_TOKEN

http mode: yes

Shared secret clients must send as Authorization: Bearer …. At least 32 characters.

OUTLOOK_MCP_TOKEN_CACHE

http mode: yes

Base64 of the local token cache, from npm run export-token. Contains the refresh token: secret.

PORT / HOST

no

3000 / 0.0.0.0

Where http mode listens. Setting PORT (as hosts do) selects http mode automatically.

MCP_RATE_LIMIT_PER_MIN

no

60

Accepted requests per minute in http mode.

OUTLOOK_MCP_MODE

no

Set to http to force hosted mode without a PORT.

Copy .env.example to .env if you prefer a file; nothing in this repo reads .env automatically, so pass the values through your MCP client's env block or your shell.

Commands

node dist/index.js               # MCP stdio server (what your local client launches)
node dist/index.js http          # hosted Streamable HTTP server on $PORT   (alias: npm run start:http)
node dist/index.js login         # one-time device-code sign-in              (alias: npm run login)
node dist/index.js export-token  # token cache as base64 for hosting         (alias: npm run export-token)
node dist/index.js help          # help
npm test                         # build + all offline tests (no account needed)
npm run smoke:live               # acceptance checks against the real mailbox (local)
npm run smoke:remote             # acceptance checks against a deployed URL (MCP_URL + MCP_AUTH_TOKEN)

dist/ is committed on purpose, so a host that only runs npm install && npm start works without a build step. After changing src/, run npm run build and commit dist/ too (npm run check:dist fails if you forget).

Acceptance checklist

#

Criterion

How to verify

AC1

No Mail.Send in requested scopes

npm test (policy + trust-boundary tests); consent screen at login shows no "send mail" line

AC2

No send tool in the MCP tool list

npm test (tools/list is asserted to be exactly the six tools)

AC3

Search + read work on real mail

npm run smoke:liveAC3 … lines

AC4

create_draft lands in Drafts, not sent

npm run smoke:liveAC4 … lines, then look in Outlook web → Drafts

AC5

Graph send endpoints refuse this token

npm run smoke:live → both AC5 … refused lines show HTTP 403

AC6

Non-expert finishes Entra + local run in ≤30 min

Follow Quick start; the runbook has one click per line

AC7

Calendar/contact/send tools absent

npm test; node dist/index.js help lists six tools

AC8

.env.example + security model, no client secret

This file, .env.example, docs/entra-setup.md

H1

Hosted /mcp refuses requests without the exact bearer token

npm test (scripts/test-http-server.mjs); npm run smoke:remote → "wrong token is refused"

H2

Hosted tool list is five tools, no save_attachment

npm test; npm run smoke:remote → "exactly the five hosted tools"

H3

Hosted server stays locked (503 on /mcp) without MCP_AUTH_TOKEN

npm test → "starts LOCKED without MCP_AUTH_TOKEN"; curl /healthz shows "status":"locked"

Troubleshooting

Symptom

Cause / fix

AADSTS7000218 at login

"Allow public client flows" is off. Entra → your app → Authentication → Advanced settings → Yes → Save [3].

AADSTS50020 / "user account does not exist in tenant"

Wrong authority. Keep OUTLOOK_MCP_TENANT at consumers for a personal account.

AADSTS90133 or AADSTS50059

Device code rejected for the tenant alias. Try OUTLOOK_MCP_TENANT=common (the app registration must then allow personal accounts) [4].

Not signed in. Run this in a terminal ONCE … in the client

The cache is empty or expired (90 days [9]). Run npm run login again; the MCP server never prompts by itself.

HTTP 403 on search_emails / read_email

A delegated permission is missing in the app registration, or consent was declined. Re-check API permissions, then npm run login again.

refusing to start: Refusing to request OAuth scope …

Someone edited the scope list or tool registry. That is the guard doing its job.

Search returns hits from any year despite received: filter

Known MSA backend limitation; use list_emails with since/until for strict dates.

Claude Desktop shows no tools

Quit and restart the app fully; check ~/Library/Logs/Claude/mcp-server-outlook.log [13].

Development

npm run build      # tsc → dist/
npm test           # build + scripts/test-*.mjs + offline MCP smoke test

Trust boundaries and the rules for changing them are in AGENTS.md. Upstream history and the fork changes are in CHANGELOG.md.

Sources

  1. How to register an app in Microsoft Entra ID

  2. OAuth 2.0 device authorization grant

  3. Configure desktop apps that call web APIs — enable public client flow

  4. Using device code flow in MSAL.NET — Microsoft personal accounts

  5. Microsoft Graph permissions reference

  6. user: sendMail

  7. message: send

  8. Create message (draft)

  9. Refresh tokens in the Microsoft identity platform

  10. Managing apps and services connected to our Microsoft Accounts

  11. Cursor docs — Model Context Protocol

  12. Cursor forum — Grok Bot custom remote MCP

  13. MCP — Connect to local MCP servers (Claude Desktop)

  14. Claude Code — MCP

License

MIT, same as upstream. See LICENSE.

Available Tools

6 tools
create_draftA

Create a draft email in the user's Drafts folder — NEVER sends. If replyToMessageId is set, the draft is created as a threaded reply; if body is provided it replaces Graph's quoted original so the caller fully controls the outgoing text. Otherwise a new standalone draft is created. The user must review and send manually in Outlook; this server has no ability to send.

ParametersJSON Schema
NameRequiredDescriptionDefault
ccNoCC recipients.
toYesRecipient email addresses (To:).
bccNoBCC recipients.
bodyYesEmail body. Plain text by default.
subjectYesEmail subject.
bodyFormatNoBody format. Default 'text'.
replyToMessageIdNoIf set, create the draft as a reply to this message (preserves thread, quotes original).

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses that the tool never sends, that sending is manual in Outlook, that body replaces Graph's quoted original, and that the draft lands in Drafts. It omits failure/permission behavior and what happens to existing settings, but the safety profile is unusually well disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and the critical 'NEVER sends' constraint before the conditional detail. Three sentences, each earning its place, though 'Otherwise a new standalone draft is created' is mildly redundant given the opening.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a create tool with no output schema and no annotations, the description covers the important behavioral and branching facts. It stops short of indicating what the call returns (e.g., a draft identifier), which no structured field supplies, but otherwise an agent has enough to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description goes beyond the schema by explaining the semantics of replyToMessageId (threaded reply, quotes original) and the interaction where a provided body overrides that quoted original — meaning that is genuinely additive, not a restatement.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Create a draft email in the user's Drafts folder') and immediately constrains scope with 'NEVER sends', making it unmistakably distinct from the read-only siblings (read_email, list_emails, search_emails). An agent can differentiate it from every sibling without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description sets a clear context (composing a draft the user will send manually) and spells out two conditional branches: reply-to threading when replyToMessageId is set, standalone otherwise. It does not name an alternative tool, but no sibling performs a comparable write, so there is little to exclude against.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_emailsA

List emails from a mail folder with optional filters (sender substring, date range, unread-only). Returns a compact JSON array of message summaries. Use read_email with a returned id to fetch full content.

ParametersJSON Schema
NameRequiredDescriptionDefault
fromNoFilter: only emails whose sender address STARTS WITH this value (case-sensitive, Graph OData limitation). For a full domain match use 'user@acme.com' or 'acme.com'. For fuzzy / case-insensitive matching use the search_emails tool instead.
limitNoMaximum number of emails to return. Default 25, max 100.
sinceNoFilter: only emails received at or after this ISO-8601 timestamp (e.g. 2026-04-15T00:00:00Z).
untilNoFilter: only emails received strictly before this ISO-8601 timestamp.
folderNoMail folder to list. Defaults to 'inbox'. Either a well-known name (inbox, drafts, sentitems, deleteditems, junkemail, archive, outbox, conversationhistory) or a folder ID returned by list_folders. Unknown names produce a 404 from Graph — use list_folders to discover custom folders.
unreadOnlyNoIf true, return only unread messages.

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It usefully discloses the return shape ('compact JSON array of message summaries'), but omits pagination behavior, permission/auth requirements, and whether an empty folder returns an empty array or an error.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the primary action, then filters, then the return format and follow-up call. Every sentence earns its place with no padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a six-parameter, filter-based list tool with no output schema and no annotations, the description covers purpose, filtering scope, return shape, and the next step in the workflow. Remaining gaps (pagination, auth, empty-result behavior) are modest for a read-only lister.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all six parameters thoroughly (defaults, max, ISO-8601 format, folder names, 404 behavior). The description's paraphrase ('sender substring') is actually looser than the schema's precise 'STARTS WITH' semantics, adding no meaning beyond it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List emails from a mail folder') plus the scope of optional filters, and names the sibling tool (read_email) that handles full content. An agent can distinguish it from read_email and search_emails without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes in the retrieval workflow: use read_email with a returned id to fetch full content, which clarifies that this tool yields summaries, not bodies. It stops short of an explicit when-to-use-this vs search_emails rule (that guidance only lives in the schema for the 'from' parameter).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_foldersA

List mail folders. With no parentFolderId, returns top-level folders. With parentFolderId set, returns immediate child folders (one level deep). Use the returned 'id' as a folder reference for list_emails. For Outlook's standard folders you can also pass their well-known names directly (inbox, drafts, sentitems, deleteditems, junkemail, archive, outbox, conversationhistory) without calling this tool first.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of folders to return. Default 100, max 200.
parentFolderIdNoIf set, list the immediate children of this folder (subfolders). If omitted, list top-level folders in the user's mailbox.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the burden, and it does disclose real behavioral traits: recursion is limited to one level deep, the scoping depends on whether parentFolderId is present, and the 'id' field in the response is the folder reference. It stops short of covering permissions, pagination behavior against the limit parameter, or response shape.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four compact sentences, front-loaded with the core behavior, then the id-for-list_emails tip, then the well-known-names shortcut. Every sentence carries information; slight redundancy between the descriptions of parentFolderId here and in the schema keeps it from being maximally tight.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description does the necessary work of noting that the returned 'id' is the folder reference to reuse, and with no annotations it covers scoping and depth. It does not address the limit parameter's interaction (e.g., truncation of results in large mailboxes), which is the main remaining gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3 and the schema already documents both parameters. The description still adds meaning: it clarifies the depth semantics ('immediate child folders, one level deep') rather than just 'children', and it enumerates the valid well-known folder name values, which the schema does not.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List mail folders') and immediately splits the behavior by input: no parentFolderId returns top-level folders, parentFolderId returns immediate children. It also distinguishes itself from siblings by naming list_emails as the consumer of the returned id.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells the agent when NOT to call it: Outlook's well-known folder names (inbox, drafts, sentitems, etc.) can be passed directly to other tools without calling list_folders first. This is a genuine routing rule against an alternative path, not just a use-case hint.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_emailA

Fetch the full content of one email message by id, plus a list of its attachments (metadata only — use save_attachment to write an attachment to disk). Returns JSON with body as plain text.

ParametersJSON Schema
NameRequiredDescriptionDefault
messageIdYesThe Graph message ID (returned by list_emails or search_emails).
includeBodyNoIf true, include the full body (text preferred over HTML). Default true.

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It usefully discloses output behavior (JSON, body as plain text, attachments returned as metadata only, implying save_attachment is needed to get file bytes), but says nothing about permissions, size/truncation limits, or error behavior for invalid ids.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single tightly written sentence with a parenthetical that carries real information (attachment metadata vs. disk write, plain-text body). No filler, and the core action is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no annotations, the description covers the essentials: what is returned, that attachments are metadata-only, and where to go for file content. It is a bit thin on failure modes and whether the body can be omitted via includeBody, but nothing critical is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, including the messageId provenance ('returned by list_emails or search_emails') and includeBody's default and text-over-HTML preference. The description adds the 'plain text' return hint but no new parameter detail, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Fetch) and resource (one email message) with the identifier (id) and an additional scope note about attachments. It is clearly distinguishable from siblings list_emails and search_emails, which enumerate rather than fetch a single message.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear context (a single message, found via an id from list_emails/search_emails per the schema) and routes attachment retrieval to save_attachment, an explicit alternative. It stops short of stating when to prefer this over searching or when not to use it, so it is strong but not exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

save_attachmentA

Download one attachment from a message and save it to the local filesystem. Writes are confined to the server's allowed directory (default ~/Downloads/outlook-mcp/). Existing files are never overwritten — the tool appends ' (2)', ' (3)', … to the filename. Returns the absolute path of the saved file.

ParametersJSON Schema
NameRequiredDescriptionDefault
messageIdYesThe Graph message ID that contains the attachment.
attachmentIdYesThe Graph attachment ID (from read_email's 'attachments' list).
targetFilenameNoOptional filename override. If omitted, the original attachment filename is used. Must be a bare filename — no path separators, no parent traversal. The destination directory is fixed by the server's OUTLOOK_MCP_ALLOWED_DIR setting and is not LLM-controllable.

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With zero annotations, the description carries the full behavior burden and handles it well: it discloses the filesystem write side effect, the confinement to the server's allowed directory, the non-overwrite policy with the ' (2)', ' (3)' renaming scheme, and the return value. What is missing is auth/permission requirements and failure modes (oversized attachments, missing IDs).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the action and resource, then side effects, then return value. No filler, and the overwrite-avoidance detail is stated before the return contract where an agent would want it.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter mutation tool with no output schema and no annotations, the description covers what an agent needs: the write target directory, the collision-handling rule, and the return value (absolute path). The only omission — error behavior — is minor for this operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% — all three parameters, including the targetFilename constraints and the non-LLM-controllable destination directory, are fully documented in the schema. The description adds only marginal framing ("one attachment", "default ~/Downloads/outlook-mcp/"), so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb+resource ("Download one attachment from a message and save it to the local filesystem") with clear scope — exactly one attachment per call, written to disk. This is cleanly distinguishable from the read-only siblings (read_email, list_emails, search_emails), which retrieve content but do not persist it.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the phrasing (extract an attachment to disk), and the schema notes that attachmentId comes from read_email's attachments list, which hints at the workflow. However, the description itself names no alternatives and states no when-not conditions — e.g. whether it works for inline images or whether read_email is sufficient when you don't need the bytes on disk.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_emailsA

Full-text search across the user's ENTIRE mailbox (all folders, all years). Supports KQL-like operators: 'from:acme.com', 'subject:contract', 'hasAttachment:true', 'body:Pantheon', plus quoted phrases and AND/OR. Works on both personal (hotmail.com / outlook.com) and work/school accounts — the tool picks the correct backend automatically. IMPORTANT: results are ranked by relevance, NOT sorted by date. Date filters like 'received:this-week' are honored on AAD (work/school) but are IGNORED by the personal-account search backend, which returns matches from any time period. If you need strict date filtering, use list_emails (folder-scoped) with since/until instead. Returns a compact JSON array of hits with message IDs usable by read_email.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of results. Default 25, max 250.
queryYesFull-text search query using Microsoft Graph search syntax. Examples: 'invoice from:acme.com', 'subject:contract received:this week', 'hasAttachment:true Q4 report'.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so well: it discloses relevance-based ranking (not date-sorted), that received: filters are honored on AAD but IGNORED on the personal backend, automatic backend selection, and the return shape. These are non-obvious behavioral quirks an agent must know to call correctly.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in the first clause and each sentence carries useful information (operators, account behavior, ranking, alternative). The single dense paragraph of intertwined caveats is slightly heavy but nearly all of it earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having no output schema, the description explains the return format (compact JSON array of hits with message IDs usable by read_email), and it covers the query syntax and cross-account pitfalls. Nothing essential for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds query semantics beyond the schema: additional operators (hasAttachment:true, body:, quoted phrases, AND/OR) and the critical caveat about date-operator behavior per backend. It does not, however, add anything for the limit parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Full-text search across the user's ENTIRE mailbox') and scopes it explicitly (all folders, all years), which separates it from the folder-scoped list_emails sibling. An agent can identify the tool's job without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names the alternative ('use list_emails (folder-scoped) with since/until') and the exact condition that selects it (need for strict date filtering). It also clarifies cross-account behavior so the agent knows when results may be date-unreliable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 6 tool updatesv0.4.2
    • First observedcreate_draft
    • First observedlist_emails
    • First observedlist_folders
    • First observedread_email
    • First observedsave_attachment
    • First observedsearch_emails

TDQS

A4.4/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: reading, listing, searching, folder browsing, downloading attachments, and drafting. Descriptions explicitly clarify boundaries, such as search_emails vs list_emails for date filtering, and save_attachment being separated from read_email's metadata-only attachment list.

Naming Consistency5/5

All tools follow a consistent verb_noun snake_case pattern: read_email, list_emails, search_emails, list_folders, save_attachment, create_draft. No deviations or mixed conventions.

Tool Count5/5

Six tools provide a focused, well-scoped set for a read-oriented email MCP server. Each tool earns its place, and the count is appropriate given the deliberate limitation of not sending emails.

Completeness4/5

The surface covers reading, listing, searching, folder navigation, attachment saving, and draft creation, but lacks delete, move, mark-as-read, or reply-draft updates. These are reasonable omissions for a limited server, but agents may hit dead ends for basic mailbox management.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides programmatic access to Microsoft Outlook mailboxes, enabling AI assistants to search, analyze, and extract insights from emails in personal and shared mailboxes.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI assistants to securely read, search, draft, and send Outlook emails, manage calendar events, and access mailbox folders through a local MAPI connection to Windows Outlook, with human-in-the-loop safeguards.
    9
    1
    MIT
  • A
    license
    B
    quality
    B
    maintenance
    Enables controlled Microsoft 365 mail workflows including search, read, thread, attachment, and managed draft operations through Microsoft Graph, without sending or modifying messages.
    9
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables MCP-compatible AI assistants to securely search multiple mailboxes, reconstruct email threads, and inspect attachments through read-only tools without altering mailbox state.
    Apache 2.0