{
  "markdown": "# EWS Meeting MCP\n\n[![npm](https://img.shields.io/npm/v/ews-meeting-mcp?color=blue)](https://www.npmjs.com/package/ews-meeting-mcp)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![Node.js >=18](https://img.shields.io/badge/node-%3E%3D18-339933)](package.json)\n[![Python](https://img.shields.io/badge/python-3.x-3776AB)](pyproject.toml)\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/ap311036/ews-meeting-mcp/master/docs/assets/ews-meeting-mcp-demo.gif\" alt=\"EWS Meeting MCP demo showing a safe Exchange scheduling flow with setup checks, room-aware slot suggestions, preview confirmation, verification, and audit logging\" width=\"960\">\n</p>\n\nThe MCP server for safely reading Outlook inbox messages, sending self-only reports, and scheduling meetings on on-prem Exchange EWS.\n\nEWS Meeting MCP gives coding assistants and desktop agents a safe, structured way to read Outlook inbox messages and availability, send a report only to the configured mailbox, discover rooms, suggest meeting slots, and create, update, or cancel meetings only after an explicit human confirmation step.\n\nIt is built for companies where calendar access is sensitive infrastructure: regulated teams, internal networks, strict security review, legacy Exchange deployments, and organizations that cannot simply hand a cloud agent broad Microsoft 365 permissions. Credentials stay local, meeting writes are previewed before they touch Exchange, self-only report sends require idempotency protection, and every write can be traced through a local audit trail.\n\n```bash\nnpx ews-meeting-mcp\n```\n\n## Why This Exists\n\nMany teams still run calendar infrastructure through on-prem Exchange/EWS. General-purpose agents can reason about scheduling, but they should not receive raw passwords, guess attendee addresses, or send calendar invitations without a reviewable checkpoint.\n\nThis is not a generic Outlook wrapper or a Microsoft Graph-first calendar connector. It is designed for the stricter enterprise case: local EWS access, room resources, explicit human approval, duplicate-send protection, and audit-friendly meeting lifecycle tools.\n\nThis project wraps Exchange calendar operations in a small MCP surface with:\n\n- **Room-aware scheduling**: finds overlapping attendee availability and filters meeting rooms by Exchange discovery or local policy.\n- **Human-in-the-loop writes**: create, update, and cancel operations require a preview plus a matching `confirmation_id`.\n- **Local-first credential handling**: reads `.env`, shell environment, or macOS Keychain without passing passwords through the model.\n- **Structured recovery**: setup failures return machine-readable error codes and user-facing setup commands.\n- **Auditability**: lifecycle previews, confirmed actions, duplicate confirmations, in-progress states, and structured errors are written to a local JSONL audit log.\n- **Agent-ready instructions**: the MCP server exposes tool descriptions and initialization guidance, and the repo includes a companion skill for agents that support skills.\n\n## What Agents Can Do\n\n| Capability | Tooling | Safety posture |\n| --- | --- | --- |\n| Check setup and credentials | `ews_setup_check`, `ews_keychain_status` | Never returns the EWS password |\n| Set up meeting signatures | `ews_signature_setup_guide` | Returns copyable HTML sample and local env guidance |\n| Read Outlook inbox | `ews_search_emails`, `ews_read_email` | Read-only; does not mark messages as read or change mailbox state |\n| Send a weekly report to self | `ews_send_email_to_self` | Only sends to `EWS_EMAIL`; recipient is not an input; requires a stable idempotency key and real EWS CreateItem + SendItem |\n| Read calendar availability | `ews_list_calendar`, `ews_get_free_busy`, `ews_find_calendar_events` | Read-only |\n| Resolve people and rooms | `ews_resolve_attendees`, `ews_list_rooms` | Uses Exchange directory when available |\n| Suggest slots | `ews_suggest_slots` | Applies local workday, avoid windows, and room capacity |\n| Create meetings | `ews_create_meeting_preview`, `ews_create_meeting_confirmed` | Supports optional weekly recurrence; requires preview, explicit approval, and matching confirmation id |\n| Update meetings | `ews_update_meeting_preview`, `ews_update_meeting_confirmed` | Requires exact EWS item metadata and matching confirmation id |\n| Cancel meetings | `ews_cancel_meeting_preview`, `ews_cancel_meeting_confirmed` | Requires exact EWS item metadata and matching confirmation id |\n| Verify and audit | `ews_verify_meeting`, `ews_get_audit_log` | Confirms server-side state without exposing credentials |\n\n## Documentation\n\n- [Usage Guide](docs/usage.md): local setup, Keychain, scheduling policy, smoke checks, CLI examples, MCP config, and npm wrapper details.\n- [Agent Guide](docs/agent-guide.md): tool contracts, setup checks, attendee resolution, room selection, preview-confirm flows, audit log, and verification rules.\n- [Publishing](docs/publishing.md): npm package and GitHub Actions release checklist.\n\n## Quick Start\n\n### 1. Configure EWS\n\nCreate a `.env` file in the working directory or provide equivalent environment variables:\n\n```bash\nEWS_ENDPOINT=https://mail.company.com/EWS/Exchange.asmx\nEWS_EMAIL=your_user@company.com\nEWS_USERNAME='DOMAIN\\your_user'\nEWS_AUTH_TYPE=NTLM\nEWS_TIMEZONE=Asia/Taipei\n```\n\nUse `EWS_AUTH_TYPE=BASIC` only if IT confirms Basic auth is enabled and the endpoint is protected by HTTPS.\n\n### 2. Store the Password\n\nFor local development, the generic `ACCOUNT_PASSWORD` override works, but do not put it in MCP config:\n\n```bash\nACCOUNT_PASSWORD='your-password'\n```\n\nOn macOS, Keychain is safer than storing the password in `.env` or MCP client config:\n\n```bash\nread -rsp \"Account password: \" ACCOUNT_PASSWORD\necho\nsecurity add-generic-password -U -s ews-meeting-mcp-account-password -a 'DOMAIN\\your_user' -w \"$ACCOUNT_PASSWORD\"\nunset ACCOUNT_PASSWORD\n```\n\nUse the same Keychain service/account pair from any local tool that needs this account password:\n\n```bash\nACCOUNT_PASSWORD_KEYCHAIN_SERVICE=ews-meeting-mcp-account-password\nACCOUNT_PASSWORD_KEYCHAIN_ACCOUNT='DOMAIN\\your_user'\n```\n\nIf `ACCOUNT_PASSWORD_KEYCHAIN_ACCOUNT` is omitted, `EWS_USERNAME` is used. `ACCOUNT_PASSWORD` is only a generic local override; Keychain is preferred.\n\n### 3. Add the MCP Server\n\nFor an npm-installed MCP client:\n\n```json\n{\n  \"mcpServers\": {\n    \"ews-meeting-mcp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"ews-meeting-mcp@0.1.25\"],\n      \"env\": {\n        \"EWS_ENDPOINT\": \"https://mail.company.com/EWS/Exchange.asmx\",\n        \"EWS_EMAIL\": \"your_user@company.com\",\n        \"EWS_USERNAME\": \"DOMAIN\\\\your_user\",\n        \"ACCOUNT_PASSWORD_KEYCHAIN_SERVICE\": \"ews-meeting-mcp-account-password\",\n        \"ACCOUNT_PASSWORD_KEYCHAIN_ACCOUNT\": \"DOMAIN\\\\your_user\",\n        \"EWS_AUTH_TYPE\": \"NTLM\",\n        \"EWS_TIMEZONE\": \"Asia/Taipei\"\n      }\n    }\n  }\n}\n```\n\nFor a local checkout:\n\n```json\n{\n  \"mcpServers\": {\n    \"ews-meeting-mcp\": {\n      \"command\": \"/path/to/ews-meeting-mcp/.venv/bin/python\",\n      \"args\": [\"-m\", \"ews_meeting_mcp.mcp_server\"],\n      \"cwd\": \"/path/to/ews-meeting-mcp\",\n      \"env\": {\n        \"PYTHONPATH\": \"src\"\n      }\n    }\n  }\n}\n```\n\n### Optional: Add an Outlook-Style Signature\n\nMeeting invites append a configured HTML signature by default. Ask the MCP tool for a copyable starter template:\n\n```text\news_signature_setup_guide\n```\n\nSave the returned `sample_html` as `ews-meeting-signature.html` in the MCP working directory, then edit the name, email, title, logo URL, and disclaimer. You can also point to a different file:\n\n```bash\nEWS_MEETING_SIGNATURE_HTML_PATH=/path/to/ews-meeting-signature.html\nEWS_MEETING_SIGNATURE_ENABLED=true\n```\n\nUse an HTTPS logo URL recipients can access, or replace the `<img>` source with a base64 data URI. Set `EWS_MEETING_SIGNATURE_ENABLED=false` to temporarily stop appending the signature.\n\n### 4. Verify Setup\n\n```bash\nnpx ews-meeting-mcp --cli env\nnpx ews-meeting-mcp --cli probe\n```\n\nThe first command prints the configured endpoint and account without printing the password. The second command validates that the account can connect to EWS.\n\n## Agent Workflow\n\nFor incoming Outlook mail, search the configured inbox first and then read the selected message:\n\n```text\nuser asks about company email\n-> ews_setup_check\n-> ews_search_emails with the narrowest known filters\n-> user selects or identifies one message\n-> ews_read_email with the exact id and optional changekey\n```\n\nThese mailbox tools are read-only. They do not mark messages as read, send mail, move messages, or change labels.\n\nFor the Thursday Jira weekly-report automation, compose the report from the Jira results and then call the dedicated self-only mail tool through this EWS MCP:\n\n```text\nweekly report is ready\n-> ews_setup_check\n-> ews_send_email_to_self with subject, body, and a stable idempotency_key\n-> stop after sent=true; do not use Gmail, to: me, or another mail provider\n```\n\n`ews_send_email_to_self` takes no recipient, `to`, `cc`, or `bcc` argument. It obtains the only recipient from `EWS_EMAIL`, sends through Exchange EWS, and appends the configured HTML signature by default. It does not write the email body or recipient address to the audit log. Reuse the same idempotency key for an automation retry; a completed or in-progress key will not call Exchange again.\n\nScheduling should follow this shape:\n\n```text\nuser request\n-> ews_setup_check\n-> ews_signature_setup_guide, if the user needs help creating the optional HTML signature\n-> ews_resolve_attendees, if names or aliases are provided\n-> ews_list_rooms, if a room may be needed\n-> ews_suggest_slots\n-> user chooses a slot and room\n-> ews_create_meeting_preview\n-> show exact invite details, recurrence if present, and confirmation_id\n-> user explicitly confirms\n-> ews_create_meeting_confirmed with confirm=true and the same confirmation_id\n-> ews_verify_meeting, when item id and changekey are available\n```\n\nRecurring meetings are created by passing a structured `recurrence` object to both preview and confirmed create calls. For example, \"every Monday and Wednesday\" uses weekly recurrence:\n\n```json\n{\n  \"type\": \"weekly\",\n  \"interval\": 1,\n  \"weekdays\": [\"MO\", \"WE\"],\n  \"range\": {\n    \"type\": \"numbered\",\n    \"count\": 10\n  }\n}\n```\n\n\"Every business day until 7/26\" means Monday through Friday, without holiday or makeup-day handling:\n\n```json\n{\n  \"type\": \"weekly\",\n  \"interval\": 1,\n  \"weekdays\": [\"MO\", \"TU\", \"WE\", \"TH\", \"FR\"],\n  \"range\": {\n    \"type\": \"end_date\",\n    \"end_date\": \"2026-07-26\"\n  }\n}\n```\n\nIf the user asks only for weekdays such as \"every Monday and Wednesday\" without an end date, occurrence count, or explicit no-end choice, ask for one before previewing.\n\nExisting meeting changes should use exact calendar metadata:\n\n```text\nuser asks to update or cancel a meeting\n-> ews_find_calendar_events with the narrowest known time window\n-> user chooses one exact event, if more than one candidate exists\n-> ews_update_meeting_preview or ews_cancel_meeting_preview\n-> show current/proposed details, warnings, and confirmation_id\n-> user explicitly confirms\n-> matching confirmed tool with confirm=true and the same confirmation_id\n```\n\nAgents should never infer an event from subject text when multiple candidates are possible.\n\n## Safety Model\n\nEWS Meeting MCP is designed around a simple rule: reads may be automated, and every write must have a narrow safety gate. Inbox reads are limited to explicit read-only search and read tools; meeting writes require preview plus explicit confirmation, while the self-only report send requires configured-self enforcement plus idempotency protection.\n\n- Authentication failures are fail-fast: the EWS client uses `FailFast`, and after one invalid-credential or locked-account response the MCP blocks all later EWS calls in that process. Fix the shared Keychain item and restart the MCP before trying again.\n- Confirmed create, update, and cancel tools refuse to run unless `confirm=true` is passed.\n- Confirmed tools require the exact `confirmation_id` returned by the matching preview.\n- Confirmed update and cancel tools also require the exact EWS `id` and `changekey` from `ews_find_calendar_events` or a prior verified result.\n- Duplicate confirmed requests return `error_code: \"duplicate_confirmation\"` with prior result metadata instead of calling EWS again.\n- In-progress confirmations return `error_code: \"confirmation_in_progress\"` and should not be blindly retried.\n- Preview tools never save, move, delete, or send Exchange notifications.\n- `ews_send_email_to_self` is the only mail-write capability. It is self-only, requires `EWS_EMAIL` to be configured, and sends a saved draft with EWS `CreateItem` followed by `SendItem`; it does not accept an arbitrary recipient.\n- Self-only sends require a stable, non-secret `idempotency_key`. A duplicate or in-progress key is refused before another EWS send. If delivery is uncertain, inspect Sent Items before retrying.\n- Self-only send audit entries contain only action/status and non-sensitive delivery/idempotency metadata; they do not contain the configured email address, subject, body, password, or token.\n- The local confirmation ledger and audit log store operation metadata, not EWS passwords.\n\nIf `ews_setup_check` returns `ready: false`, an agent should show the returned `user_message` or `setup_command` verbatim and stop. It should not ask for attendee emails or continue scheduling as a workaround.\n\n## Scheduling Policy\n\nBy default, scheduling tools look for `ews-meeting-policy.json` in the current working directory. Set `EWS_MEETING_POLICY_FILE` to point at a different file.\n\nIf no policy file exists, built-in defaults are used:\n\n- workday: `10:00` to `18:00`\n- avoid: `12:00-14:00`\n- fallback rooms: `2-11`, `2-13`, `2-14`, `3-1`, `3-2`, `3-4`\n\nLive room selection uses Exchange room-list discovery when available, then falls back to configured rooms.\n\nExample policy:\n\n```json\n{\n  \"workday_start\": \"10:00\",\n  \"workday_end\": \"18:00\",\n  \"avoid\": [\"12:00-14:00\"],\n  \"rooms\": [\n    {\n      \"alias\": \"3-1\",\n      \"name\": \"3-1 Meeting Room(12P)\",\n      \"email\": \"3-1MeetingRoom@company.com\",\n      \"capacity\": 12\n    }\n  ]\n}\n```\n\nPolicy rooms are merged with the default fallback rooms by `alias`. A matching alias overrides the default room, and new aliases are appended.\n\n## CLI Usage\n\nThe npm wrapper starts the MCP server by default:\n\n```bash\nnpx ews-meeting-mcp\n```\n\nPass `--cli` to run the Python CLI through the same package:\n\n```bash\nnpx ews-meeting-mcp --cli env\nnpx ews-meeting-mcp --cli calendar --days 7\n```\n\nSuggest a 30-minute meeting slot:\n\n```bash\nnpx ews-meeting-mcp --cli suggest \\\n  --attendee alice@company.com \\\n  --attendee bob@company.com \\\n  --start 2026-06-15T09:00:00+08:00 \\\n  --end 2026-06-19T18:00:00+08:00 \\\n  --duration-minutes 30 \\\n  --limit 5\n```\n\nPreview a meeting invitation without sending anything:\n\n```bash\nnpx ews-meeting-mcp --cli create-meeting \\\n  --attendee alice@company.com \\\n  --attendee bob@company.com \\\n  --start 2026-06-15T11:00:00+08:00 \\\n  --end 2026-06-15T11:30:00+08:00 \\\n  --subject \"Project sync\" \\\n  --body \"Discuss next steps\" \\\n  --location \"Webex\"\n```\n\nActually create the meeting and send invitations:\n\n```bash\nnpx ews-meeting-mcp --cli create-meeting \\\n  --attendee alice@company.com \\\n  --attendee bob@company.com \\\n  --start 2026-06-15T11:00:00+08:00 \\\n  --end 2026-06-15T11:30:00+08:00 \\\n  --subject \"Project sync\" \\\n  --body \"Discuss next steps\" \\\n  --location \"Webex\" \\\n  --confirm\n```\n\nThe `--confirm` flag is intentionally required. Without it, the command prints a dry-run preview and does not call EWS to create the event.\n\n## Local Development\n\nFor detailed setup, smoke tests, CLI examples, and MCP client configuration, see the [Usage Guide](docs/usage.md).\n\nThe short development loop is:\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env\nnpm test\n```\n\nRun the MCP server from a checkout:\n\n```bash\nenv PYTHONPATH=src python -m ews_meeting_mcp.mcp_server\n```\n\n## Companion Skill\n\nThe repo includes a companion skill for agents that support skills:\n\n```text\nskills/ews-meeting-mcp/SKILL.md\n```\n\nSee the [Agent Guide](docs/agent-guide.md) for the same workflow in plain Markdown.\n\n## Troubleshooting\n\nOperational troubleshooting lives in the [Usage Guide](docs/usage.md). For agent behavior, setup-check handling, and lifecycle safety, see the [Agent Guide](docs/agent-guide.md).\n\n## Release\n\nSee [Publishing](docs/publishing.md).\n\n## License\n\nMIT\n",
  "bytes": 16579,
  "sha": "0abbf9595cf9e96f57100cfe3567fd8856c44a0160e6ea63d35c93e7d29e1ee0",
  "repo_slug": "ap311036/ews-meeting-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_ap311036_ews_meeting_mcp_405b45d8/readme"
}