Quick Start

Get from zero to a working Outlook Local MCP server in minutes.

Prerequisites

1. Build

git clone https://github.com/desek/outlook-local-mcp.git
cd outlook-local-mcp
go build ./cmd/outlook-local-mcp/

Or install directly:

go install github.com/desek/outlook-local-mcp/cmd/outlook-local-mcp@latest

2. Configure Claude Desktop

Add the server to your Claude Desktop configuration file:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "outlook-local": {
      "command": "/absolute/path/to/outlook-local-mcp"
    }
  }
}

Replace /absolute/path/to/outlook-local-mcp with the actual path to the built binary.

To set environment variables:

{
  "mcpServers": {
    "outlook-local": {
      "command": "/absolute/path/to/outlook-local-mcp",
      "env": {
        "OUTLOOK_MCP_DEFAULT_TIMEZONE": "America/New_York",
        "OUTLOOK_MCP_LOG_LEVEL": "info"
      }
    }
  }
}

2b. Configure Claude Code

Add an .mcp.json file to your project root:

{
  "mcpServers": {
    "outlook-local": {
      "command": "/absolute/path/to/outlook-local-mcp"
    }
  }
}

Replace /absolute/path/to/outlook-local-mcp with the actual path to the built binary.

3. Authenticate and Verify

Restart Claude Desktop (or reload MCP servers in Claude Code) and verify the server is reachable:

{tool: "system", args: {operation: "about"}}

This returns the build version, Go runtime, OS, and auth backend in use — no authentication required. Keep this output handy when reporting issues (see Before you file an issue).

Then ask:

"List my calendars"

On first use, the server has no cached credentials. The default authentication method (auth_code) opens the system browser for Microsoft login. After signing in, the browser shows a blank page -- copy the full URL from the address bar and paste it when prompted (via MCP Elicitation) or use the complete_auth tool if your client does not support elicitation (e.g., Claude Code). After authentication completes, the tool call is retried automatically and your calendars are returned. Tokens are cached in your OS keychain -- subsequent requests authenticate silently.

4. Tool Examples

Read

List calendars -- no parameters required:

"Show me all my calendars"

List events in a time range:

"What meetings do I have tomorrow?"

Parameters: start_datetime (required), end_datetime (required), calendar_id, max_results, timezone.

Get event details by ID:

"Get the full details of event AAMkAD..."

Parameters: event_id (required), timezone.

Search events by subject, importance, sensitivity, and more:

"Find all high-importance meetings in the next two weeks"

Parameters: query, start_datetime, end_datetime, importance, sensitivity, is_all_day, show_as, is_cancelled, categories, max_results, timezone. All optional; defaults to next 30 days.

Free/busy availability:

"When am I free next Monday?"

Parameters: start_datetime (required), end_datetime (required), timezone.

Write

Create event:

"Schedule a team standup tomorrow at 9 AM Eastern for 30 minutes with alice@example.com"

Required: subject, start_datetime, start_timezone, end_datetime, end_timezone. Optional: body, location, attendees (JSON array), is_online_meeting, is_all_day, importance, sensitivity, show_as, categories, recurrence (JSON object), reminder_minutes, calendar_id.

Update event -- only specified fields change (PATCH semantics):

"Move my 2pm meeting to 3pm"

Required: event_id. All other fields are optional.

Delete

Delete event:

"Delete the event AAMkAD..."

Parameters: event_id (required). Cancellation notices are sent to attendees automatically if you are the organizer.

Cancel event with a message to attendees:

"Cancel tomorrow's team meeting and let everyone know it's rescheduled"

Parameters: event_id (required), comment (optional cancellation message). Only the organizer can cancel.

5. Configuration

All environment variables are prefixed with OUTLOOK_MCP_:

Variable Default Description
CLIENT_ID Microsoft Office client ID OAuth 2.0 client ID
TENANT_ID common Entra ID tenant (common, organizations, consumers, or a GUID)
DEFAULT_TIMEZONE UTC IANA timezone for calendar operations
LOG_LEVEL warn Log level: debug, info, warn, error
READ_ONLY false Disable write tools (create, update, delete, cancel)
LOG_FORMAT json Log format: json or text
LOG_SANITIZE true Mask PII in log output
LOG_FILE (empty = disabled) Log file path for persistent file output
ACCOUNTS_PATH ~/.outlook-local-mcp/accounts.json Path to the persistent accounts file for multi-account support (see CR-0032)

Getting help in-session

The server embeds its own documentation so the LLM can look up answers without leaving the conversation.

List available documents:

{tool: "system", args: {operation: "list_docs"}}

Search across all embedded docs:

{tool: "system", args: {operation: "search_docs", query: "token refresh"}}

Fetch a document or a specific section by heading anchor:

{tool: "system", args: {operation: "get_docs", slug: "troubleshooting"}}
{tool: "system", args: {operation: "get_docs", slug: "troubleshooting", section: "keychain-locked"}}

The embedded bundle contains readme, quickstart, and troubleshooting. Each document is also exposed as an MCP resource at doc://outlook-local-mcp/{slug} for clients that support resources/list and resources/read. Run system.status to discover the base URI and the troubleshooting slug. See CR-0061 for implementation details.

Container deployment

The server is available as an OCI image at ghcr.io/desek/outlook-local-mcp. No Go toolchain is required.

docker run -i --rm \
  -v outlook-mcp-auth:/data/auth \
  -e OUTLOOK_MCP_TENANT_ID=<tenant> \
  -e OUTLOOK_MCP_CLIENT_ID=<client> \
  ghcr.io/desek/outlook-local-mcp:latest

The named volume outlook-mcp-auth persists the token cache across container restarts so the device-code or browser auth flow does not repeat on every session.

Claude Desktop / generic MCP client config

{
  "mcpServers": {
    "outlook-local": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-v", "outlook-mcp-auth:/data/auth",
        "-e", "OUTLOOK_MCP_TENANT_ID",
        "-e", "OUTLOOK_MCP_CLIENT_ID",
        "ghcr.io/desek/outlook-local-mcp:latest"
      ],
      "env": {
        "OUTLOOK_MCP_TENANT_ID": "your-tenant-id",
        "OUTLOOK_MCP_CLIENT_ID": "your-client-id"
      }
    }
  }
}

Non-root variant (distroless)

For deployment targets that enforce non-root containers (Kubernetes PSA restricted, OpenShift, hardened CI), use the :distroless tag:

{
  "mcpServers": {
    "outlook-local": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "--user", "65532:65532",
        "-v", "outlook-mcp-auth:/data/auth",
        "-e", "OUTLOOK_MCP_TENANT_ID",
        "-e", "OUTLOOK_MCP_CLIENT_ID",
        "ghcr.io/desek/outlook-local-mcp:distroless"
      ],
      "env": {
        "OUTLOOK_MCP_TENANT_ID": "your-tenant-id",
        "OUTLOOK_MCP_CLIENT_ID": "your-client-id"
      }
    }
  }
}

For more on image variants and the keychain trade-off, see Container runtime.

Further Reading

See README.md for the full reference documentation.