Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
MSGRAPH_MCP_CLIENT_IDYesYour Entra app's client ID
MSGRAPH_MCP_TENANT_IDYesYour tenant ID, or one of common / organizations / consumers
MSGRAPH_MCP_TOKEN_CACHE_PATHNoOptional path to override the token cache location

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
whoamiA

Return the signed-in user's profile.

Also a smoke test that authentication is working.

Args: include_raw: when True, include the raw Graph payload under "raw".

Returns: Trimmed user object: {id, display_name, user_principal_name, mail, job_title}

list_messagesA

List messages from a mail folder, newest first.

Args: folder_id: Folder id, or a well-known name (inbox, sentitems, drafts, deleteditems, archive, junkemail). Default: inbox. mailbox: Optional mailbox (email or user ID). Default: signed-in user. limit: 1-100. Default 25. page_token: Pass next_page_token from a previous result to continue. include_raw: Include the raw Graph payload under "raw" on each item. unread_only: When True, only return messages where isRead is false. Adds isRead eq false to the server-side $filter. filter: Raw OData $filter expression applied server-side. Combined with unread_only using and. Examples: from/emailAddress/address eq 'a@x.com', hasAttachments eq true, contains(subject,'report'), receivedDateTime ge 2026-05-01T00:00:00Z. Cannot be combined with $search — use search_messages for that.

Returns: {"items": [trimmed_message, ...], "next_page_token": str | None}

search_messagesA

Search messages across all folders using Graph $search.

The query is passed to Graph as-is — supply quoting yourself if you need a literal phrase (e.g. '"weekly report"') or KQL fielded predicates (e.g. 'from:alice subject:"report"').

Graph does not allow combining $search with $filter or $orderby, and KQL has no isread/unread/flag/in predicates. To filter by isRead / sender / date / attachments while keyword-matching, use list_messages with filter="contains(subject,'…')" (or contains(body/content,'…')) instead — that path supports the full $filter grammar.

Args: query: Graph $search expression. Plain tokens match across common mail fields; quoted phrases match literally; KQL field:value forms target specific fields. mailbox: Optional mailbox. limit: 1-100. page_token: Continuation token. include_raw: Include raw payloads.

get_messageA

Fetch a single message by id.

Args: message_id: Graph message id. mailbox: Optional mailbox. include_body: When True, the full body is returned. Default False (snippet only). include_raw: Include the raw Graph payload under "raw".

Returns: Trimmed message object.

list_attachmentsA

List attachments on a message (metadata only — no content).

download_attachmentB

Download a single attachment.

Args: save_path: Write the bytes to this file path (or into this existing directory, using the attachment's name) instead of returning content. Returns {"path", "name", "content_type", "size_bytes"}.

Returns: - Image attachments (PNG/JPEG/GIF/WebP, no save_path): metadata plus the image itself as a native MCP image block, viewable directly. - With save_path: {"path", "name", "content_type", "size_bytes"}. - Otherwise: {name, content_type, size_bytes, content_base64}.

send_messageA

Send a new message immediately.

Args: to: List of recipient email addresses (required, non-empty). subject: Subject line. body: Message body. body_type: "text" or "html". Default "text". cc: Optional CC list. bcc: Optional BCC list. attachments: Optional list of {name, content_b64, content_type}. Total raw size must be <= 3 MB. save_to_sent_items: Default True. mailbox: Optional mailbox (defaults to signed-in user).

Returns: {"status": "sent"}

create_draftB

Create a draft message (not sent). Returns the trimmed draft.

reply_messageB

Reply to the sender of a message.

reply_all_messageB

Reply-all to a message (sender + everyone on the to/cc lines).

forward_messageC

Forward a message to new recipients.

update_messageA

Patch a message's read state / flag / importance / categories.

For moving a message between folders, prefer move_message (clearer intent). This tool patches in-place; setting parent_folder_id here works but is not the primary path.

Returns the trimmed updated message.

delete_messageA

Soft-delete a message (moves it to Deleted Items).

list_foldersB

List the user's mail folders (top level).

Returns: {"items": [trimmed_folder, ...], "next_page_token": None}

move_messageA

Move a message to another folder.

Args: message_id: Graph message id. destination: Target folder id, OR one of the well-known names: 'archive', 'inbox', 'junkemail', 'deleteditems', 'sentitems', 'drafts'. mailbox: Optional mailbox. include_raw: Include the raw Graph payload.

Returns: The trimmed moved message (which lives in the destination folder).

create_folderA

Create a new mail folder (top-level or child).

Args: display_name: Folder name. Required, non-empty. parent_folder_id: If set, create as a child of this folder; else create at the top level. Accepts a folder id or a well-known name ('inbox', 'archive', etc.). mailbox: Optional mailbox (email or user id) for shared mailboxes. include_raw: Include the raw Graph payload.

Returns: The trimmed new folder.

update_folderA

Rename and/or reparent a mail folder.

At least one of display_name or parent_folder_id is required. Well-known folders (inbox, drafts, sent items, etc.) cannot be renamed or moved — Graph will reject the request.

Args: folder_id: Graph folder id. Required. display_name: New folder name. None = unchanged. parent_folder_id: Move under this folder. Accepts a folder id or a well-known name ('inbox', 'archive', etc.). None = unchanged. mailbox: Optional mailbox (email or user id) for shared mailboxes. include_raw: Include the raw Graph payload.

Returns: The trimmed updated folder.

delete_folderA

Delete a mail folder. Refuses to delete folders that aren't empty.

Reads the folder first and aborts if totalItemCount > 0 or childFolderCount > 0. This is a small safety net against accidentally nuking a folder with real messages; there is a small race window between the read and the delete, so don't rely on this for adversarial safety.

Args: folder_id: Graph folder id. Required. mailbox: Optional mailbox (email or user id) for shared mailboxes.

Returns: {"deleted": True, "id": }

archive_messageC

Archive a message (move it to the Archive folder).

mark_readC

Mark a message as read.

mark_unreadC

Mark a message as unread.

flag_messageC

Set the follow-up flag on a message.

unflag_messageC

Clear the follow-up flag on a message.

batch_archive_messagesA

Archive a list of messages (bulk move to the Archive well-known folder).

batch_move_messagesA

Move a list of messages to the given folder.

destination accepts a folder id OR one of the well-known names that move_message already supports ('archive', 'inbox', 'junkemail', 'deleteditems', 'sentitems', 'drafts'). Graph resolves well-known names server-side, so we pass through without local resolution.

Spec deviation: Spec §6.3 calls for pre-resolving well-known names to folder ids. In practice Graph's /move endpoint accepts well-known names directly (this is how the existing single-message move_message already works — see src/msgraph_mcp/tools/mail_folders.py). We pass through unchanged. If a real-world test surfaces a case where Graph rejects a well-known name in $batch but accepts it in single calls, add resolution then.

batch_mark_readB

Mark a list of messages as read.

batch_mark_unreadC

Mark a list of messages as unread.

batch_flag_messagesA

Flag a list of messages (set followup flag to 'flagged').

batch_unflag_messagesA

Unflag a list of messages (set followup flag to 'notFlagged').

list_rulesB

List inbox mail rules.

Returns: {"items": [trimmed_rule, ...], "next_page_token": None}

get_ruleA

Get a single inbox mail rule by id.

Args: rule_id: Graph rule id. Required. mailbox: Optional mailbox (email or user id) for shared mailboxes. include_raw: Include the raw Graph payload.

Returns: Trimmed rule dict.

create_ruleA

Create an inbox mail rule.

At least one condition AND one action are required. Conditions within a rule are AND-ed by Outlook. Pass multiple values in a list (e.g. sender_contains=["example.com", "monitor.io"]) for OR within that condition.

Common condition args: sender_contains: Substrings to match against the sender display name or address (e.g. ["example.com"]). subject_contains: Substrings to match against the subject. body_contains: Substrings to match against the message body. body_or_subject_contains: Match either subject or body. from_addresses: Exact email addresses to match (e.g. ["noreply@example.com"]). has_attachments: True/False to require/exclude attachments.

Common action args: move_to_folder: Destination folder id (use list_folders or create_folder to get one). mark_as_read: Mark matching messages as read. delete: Move matching messages to Deleted Items. stop_processing_rules: If True, no further rules run after this one matches. Recommended for routing-to-folder rules.

Other args: sequence: Optional rule order (lower runs first). is_enabled: Whether the rule is active. Default True. mailbox: Optional mailbox for shared mailboxes. include_raw: Include the raw Graph payload.

Returns: The trimmed new rule.

update_ruleA

Patch fields of an existing inbox mail rule. Pass None to leave a field unchanged.

Replacement semantics for nested blocks: Graph PATCH on messageRules replaces the entire conditions or actions block when you send it. If you pass ANY condition arg (sender_contains, subject_contains, body_contains, body_or_subject_contains, from_addresses, has_attachments) the rule's conditions are rebuilt from ONLY the args you pass — any existing conditions you don't re-specify are dropped. Same for actions (move_to_folder, mark_as_read, delete, stop_processing_rules). If you don't pass any condition or action args, the existing blocks are preserved.

If you want to add to a block without losing the rest, call
``get_rule`` first and re-supply the existing values.

Args: rule_id: Graph rule id. Required. display_name: New display name. None = unchanged. is_enabled: Enable/disable the rule. None = unchanged. sequence: New order (lower runs first). None = unchanged. sender_contains / subject_contains / body_contains / body_or_subject_contains / from_addresses / has_attachments: Passing any of these REPLACES the conditions block. move_to_folder / mark_as_read / delete / stop_processing_rules: Passing any of these REPLACES the actions block. mailbox: Optional mailbox for shared mailboxes. include_raw: Include the raw Graph payload.

Returns: The trimmed updated rule.

delete_ruleA

Delete an inbox mail rule by id.

Args: rule_id: Graph rule id. Required. mailbox: Optional mailbox (email or user id) for shared mailboxes.

Returns: {"deleted": True, "id": }

list_calendarsB

List the user's calendars.

list_eventsA

List events in a calendar within a date range.

Uses Graph's calendarView, which expands recurring events into instances.

Args: calendar_id: Calendar ID. (Use list_calendars to discover.) start_datetime: ISO 8601 datetime, e.g. "2026-05-19T00:00:00Z". Required. end_datetime: ISO 8601 datetime. Required. mailbox: Optional mailbox. limit: 1-100. Default 25. page_token: Continuation token. include_raw: Include raw payloads.

Returns: {"items": [trimmed_event, ...], "next_page_token": str | None}

get_eventB

Fetch a single event by id.

create_eventA

Create a calendar event.

Args: subject: Event subject. start_datetime: ISO 8601 datetime (no offset; pair with time_zone). end_datetime: ISO 8601 datetime. time_zone: IANA tz id (e.g. "America/Los_Angeles") or "UTC". Default "UTC". body, body_type: Optional body and "text"|"html". location: Optional location string. attendees: Optional list of attendee email addresses (treated as required). is_online_meeting: When True, Outlook adds a Teams meeting link. is_all_day: All-day event flag. calendar_id: Optional calendar id; default is the user's primary calendar. mailbox: Optional mailbox.

Returns: Trimmed created event.

update_eventB

Patch fields of an existing event. Pass None to leave a field unchanged.

If you change start_datetime or end_datetime, you must also pass time_zone.

delete_eventA

Delete an event without sending a cancellation to attendees.

cancel_eventC

Cancel an event and send a cancellation notice to attendees.

respond_to_eventA

Respond to a meeting invite.

Args: response: One of "accept", "tentativelyAccept", "decline". comment: Optional comment included with the response. send_response: When False, your response status is recorded without emailing the organizer.

find_meeting_timesA

Suggest meeting times that work for the given attendees.

Args: attendees: List of attendee email addresses. duration_minutes: Meeting duration in whole minutes (e.g. 30). start_window: ISO 8601 start of the candidate window. end_window: ISO 8601 end of the candidate window. max_candidates: Cap on returned suggestions (default 20).

Returns: { "suggestions": [ {"start": {date_time, time_zone}, "end": {...}, "confidence": float, "order_hint": int}, ... ], "empty_reason": str | None, }

list_chatsA

List the signed-in user's Teams chats (1:1, group, meeting).

Members are expanded so 1:1 chats (which have no topic) are identifiable by participant name. Results come back in Graph's default order (most recent message activity first).

Each item carries two timestamps; use the right one:

  • last_message_time: created time of the most recent message (from the expanded lastMessagePreview). This is the true "last activity" time.

  • metadata_updated: Graph's lastUpdatedDateTime, which only changes on rename/membership events (e.g. someone joining a meeting call), NOT on new messages. Do not use it to judge recency -- a busy 1:1 can show a months-old metadata_updated.

Args: limit: 1-100. Default 25. page_token: Pass next_page_token from a previous result to continue. include_raw: Include the raw Graph payload under "raw" on each item.

Returns: {"items": [trimmed_chat, ...], "next_page_token": str | None}

list_chat_messagesA

List messages in a chat, newest first.

Args: chat_id: Graph chat id (from list_chats). limit: 1-100. Default 25. page_token: Continuation token from a previous result. include_body: When True, include each message's full body and any attachment card payloads (e.g. Adaptive Card JSON for bot posts). Default False (snippet only; card text still feeds the snippet). include_raw: Include the raw Graph payload under "raw" on each item.

Returns: {"items": [trimmed_chat_message, ...], "next_page_token": str | None}

list_joined_teamsA

List the teams the signed-in user is a member of.

Args: limit: 1-100. Default 25. Applied client-side (see below); teams beyond the limit are dropped. page_token: Continuation token from a previous result. include_raw: Include the raw Graph payload under "raw" on each item.

Returns: {"items": [trimmed_team, ...], "next_page_token": str | None}

list_channelsA

List channels in a team.

Args: team_id: Graph team id (from list_joined_teams). limit: 1-100. Default 25. Applied client-side (see below); channels beyond the limit are dropped. page_token: Continuation token from a previous result. include_raw: Include the raw Graph payload under "raw" on each item.

Returns: {"items": [trimmed_channel, ...], "next_page_token": str | None}

list_channel_messagesA

List root messages in a channel, newest first.

Replies are not included here; fetch them with list_message_replies.

Args: team_id: Graph team id. channel_id: Graph channel id (from list_channels). limit: 1-50 (Graph caps channel message pages at 50). Default 25. page_token: Continuation token from a previous result. include_body: When True, include each message's full body and any attachment card payloads (e.g. Adaptive Card JSON for bot posts). Default False (snippet only; card text still feeds the snippet). include_raw: Include the raw Graph payload under "raw" on each item.

Returns: {"items": [trimmed_chat_message, ...], "next_page_token": str | None}

list_message_repliesA

List replies to a channel message (the thread under a root post), newest first.

Args: team_id: Graph team id. channel_id: Graph channel id. message_id: Graph id of the root channel message (from list_channel_messages). limit: 1-50 (Graph caps reply pages at 50). Default 25. page_token: Continuation token from a previous result. include_body: When True, include each reply's full body and any attachment card payloads. Default False (snippet only). include_raw: Include the raw Graph payload under "raw" on each item.

Returns: {"items": [trimmed_chat_message, ...], "next_page_token": str | None}

download_hosted_contentA

Download one inline hosted-content item (usually an image).

Specify the message location with exactly one of:

  • chat_id (for a chat message), or

  • team_id AND channel_id (for a channel message).

Get message_id and hosted_content_id from a message's hosted_content_refs (returned by list_chat_messages / list_channel_messages / list_message_replies).

Args: message_id: Graph id of the message the content is attached to. hosted_content_id: Graph hosted-content id (from hosted_content_refs). chat_id: Chat id, if the message is in a chat. team_id: Team id, if the message is in a channel. channel_id: Channel id, if the message is in a channel. save_path: Write the bytes to this file path (or into this existing directory) instead of returning content. Returns {"path", "content_type", "size_bytes"} with no content payload. include_raw: Include the raw payload under "raw" (ignored when the result is an image block or a saved file).

Returns: - Image content (PNG/JPEG/GIF/WebP, no save_path): metadata plus the image itself as a native MCP image block, viewable directly. - With save_path: {"path": str, "content_type": str | None, "size_bytes": int}. - Otherwise: {"content_type": str | None, "size_bytes": int, "content_base64": str | None}. content_type is sniffed from the bytes (Graph does not return it on the $value endpoint); it may be None for unrecognized formats.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

B3.4/5.0

Scored across 50 tools

Disambiguation4/5

Most tools map cleanly to a resource-action pair (messages, folders, rules, events, chats, channels), and shortcuts like archive_message and the batch_* tools are clearly specialized versions of move_message. The main overlap is update_message duplicating the read-state/flag conveniences (mark_read, flag_message, etc.), but the descriptions resolve it.

Naming Consistency5/5

Tool names follow a consistent snake_case verb_noun pattern with list_/get_/create_/update_/delete_ prefixes reused across resources. Exceptions like whoami and reply_all_message are still predictable and fit the overall style.

Tool Count2/5

50 tools is well above the typical well-scoped range, and even for a multi-domain Microsoft Graph server it feels heavy. The broad mail/calendar/Teams coverage gives the count some rationale, so it is 'too many' rather than an extreme mismatch.

Completeness3/5

Mail coverage is very complete with CRUD for messages, folders, rules, attachments, and bulk operations, and calendar events have full lifecycle support. However, Teams is effectively read-only with no send/create/update/delete for chats or channels, and calendar resources themselves cannot be created or deleted, leaving notable dead ends.

Maintenance

ActivitySlowing
ResponsivenessNo issues