Teams MCP Delegated
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Teams MCP Delegatedschedule a 30 min sync with alx rivra tomorrow at 2pm"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
teams-mcp-delegated
A Model Context Protocol server for Microsoft Teams that can act as the signed-in user — reading their chats and calendar, scheduling meetings, pulling meeting transcripts, and resolving colleagues by partial or misspelled name.
This is a fork of InditexTech/mcp-teams-server (Apache-2.0, © 2025 Industria de Diseño Textil S.A.). Upstream is a Teams bot that posts as itself. This fork keeps that mode byte-for-byte and adds a second one: delegated per-user auth over Microsoft Graph, plus 17 new tools and a Windows installer. Nothing upstream was removed.
Portfolio copy. The Entra app ID and tenant ID this was originally installed against have been removed — the installer now takes them as parameters. Example names in tests and docstrings are fictional.
What this fork adds
1. Delegated (per-user) authentication — the architectural change
Upstream only supports app / service-principal auth: a registered bot posting under its own
identity via ClientSecretCredential + CloudAdapter. That identity cannot read your chats, your
calendar, or a meeting transcript.
src/mcp_teams_server/auth.py (new, 186 lines) adds a credential factory selected by
TEAMS_AUTH_MODE:
app(default) —ClientSecretCredential, exactly as upstream. Untouched.delegated—DeviceCodeCredentialwithTokenCachePersistenceOptions, persisting anAuthenticationRecordto%LOCALAPPDATA%\.IdentityService\mcp-teams-server-auth-record.json. The interactive device-code flow therefore runs once per machine, not once per invocation.
In delegated mode app_lifespan builds no bot adapter at all — it is pure Microsoft Graph with
the user's token, and resolves /me for message attribution.
Two details that were not obvious going in:
The device-code prompt cannot go to stdout. stdout is the JSON-RPC channel; writing a prompt there corrupts the protocol. It goes to stderr — and because Claude Code hides stderr, the prompt additionally auto-opens the browser and raises a native dialog with the code (a detached
MessageBoxon Windows,osascripton macOS,notify-sendon Linux).Explicit cache warm-up.
authenticate()followed byget_token(..., enable_cae=True)runs at startup, so the Graph SDK's first CAE-enabled call does not re-prompt the user.
2. Tools: 6 → 23
Upstream's six (start_thread, update_thread, read_thread, list_threads,
get_member_by_name, list_members) plus seventeen:
Area | Tools |
Discovery |
|
Calendar |
|
People |
|
Transcripts |
|
Chats |
|
Each has dual internals (_start_thread_graph vs the bot path, _send_dm_delegated vs
_send_dm_app, …) so every tool works in both auth modes.
3. Three pieces of engineering worth the read
Transcript access without OnlineMeetings.Read. Fetching a transcript normally requires a
permission the app registration does not hold, and normally only the organizer can do it.
_build_meeting_id_from_join_url() reconstructs the Graph online-meeting ID locally — base64 of the
thread ID plus the organizer OID parsed out of the join URL — and _resolve_meeting_id() tries that
first, falling back to a Graph filter query. The result: any attendee can pull the transcript.
The content fetch sets an explicit Accept: text/vtt header, because the SDK's default is wrong for
this endpoint.
Local fuzzy people-resolution. _build_directory_cache() builds a directory from the caller's
own Teams memberships; _fuzzy_score() ranks candidates locally. So "Alex", "Alex Rivera",
"alx rivra" and "alex.rivera" all resolve to the same person, ranked by relevance to the
signed-in user — no directory-wide search permission needed.
Outbound rendering and attribution. Teams renders a markdown pipe table as literal | --- |
text, so convert_markdown_tables_to_html() turns it into real <table> markup and a text message
containing a table is auto-upgraded to HTML. Separately, _attribution_footer() appends
"Posted via Teams MCP on behalf of <user>" to every outbound message, in both the HTML and
plaintext bodies. An agent posting as a human should say so.
Inbound card extraction. CI and alerting bots post Adaptive Cards and legacy O365 Connector Cards, which carry no plain-text body — so the model sees an empty message. The extraction layer pulls readable text out of both.
4. Windows installer
install-teams-mcp.ps1 (397 lines) is one command: detect or winget-install Python 3.10-3.12, create
an isolated venv under %LOCALAPPDATA%, find or build the wheel, install it, verify the import,
register the server with claude mcp add --scope user, and run the one-time device-code auth.
Two real Windows problems it solves:
A
LongPathsEnabledregistry check with self-elevation guidance, because the msgraph SDK's paths exceedMAX_PATH.Remove-LongPathDir, which purges a >260-character venv viarobocopy /purgeagainst an empty directory —Remove-Item -Recursesimply cannot delete it.
Related MCP server: Teams MCP
Install
.\install-teams-mcp.ps1 -TeamsAppId <app-guid> -TeamsTenantId <tenant-guid>Or set TEAMS_APP_ID / TEAMS_APP_TENANT_ID in the environment and run it with no arguments. You
need an Entra app registration with the delegated Graph scopes — see
doc/MS-Teams-setup.md for the app registration, and
doc/delegated-auth.md for what delegated mode needs specifically.
Manual install:
uv sync --frozen
cp sample.env .env # fill in your own IDs
uv run mcp-teams-serverConfiguration
Variable | Mode | Meaning |
| both |
|
| both | Entra app registration (client) ID |
| both | Entra tenant ID |
| both |
|
| app only | Client secret. Not required in delegated mode. |
| both | Default Teams group ID |
| both | Default channel ID |
| delegated | Token cache filename. Default |
Verifying
python bin/e2e.pyRuns every check that works without a Teams tenant — imports, the 23-tool registry, both credential
factory branches, card extraction against captured payloads, markdown-table conversion — and prints
an explicit SKIP for each path that needs live Graph access, rather than omitting it. Exit code is
non-zero on any failure.
uv run pytest runs the same offline unit tests; the integration suite is excluded by default
(-m "not integration") because it needs a real tenant.
Known limitations
Stated rather than hidden:
teams.pyhardcodesservice_url = "https://smba.trafficmanager.net/emea/", inherited unchanged from upstream. It affects app (bot) mode only; a non-EMEA tenant needs this parameterised.Default timezone for calendar operations is
Central Standard Time, not the caller's. Worth deriving from/me/mailboxSettings.Upstream's
doc/MS-Teams-setup.mdstill describes the bot-mode setup. That is still accurate for app mode; delegated mode is documented indoc/delegated-auth.md.The CI workflows inherited from upstream that depended on upstream's org (the SonarCloud gate, the ghcr release job, OSSF Scorecard on
master) are removed — they could never run here..github/workflows/PR-verify.yml(ruff, pyright, pytest, repolinter, REUSE) is kept and does run.
License and attribution
Apache-2.0, inherited from upstream. LICENSE.txt, LICENSES/, NOTICE and the per-file SPDX
headers are retained as Apache-2.0 §4 requires. Files substantially modified here carry an additional
SPDX-FileCopyrightText: 2026 Jason Hastain (modifications) line; files written here carry only that.
© 2025 INDUSTRIA DE DISEÑO TEXTIL S.A. (INDITEX S.A.) — original work © 2026 Jason Hastain — modifications
This server cannot be deployed
Maintenance
Related MCP Connectors
Permissioned access to Outlook, OneDrive and Teams via the user's own Microsoft account
Mac & Windows: let ChatGPT, Claude & Cursor use your email, calendar, iMessage, Teams, files.
Read email/chat conversations, messages, contacts and teams; draft, send and update threads.
- UproarOAuthchat.uproar
Chat where AI agents are first-class members, with their own identity and permissions.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to interact with Microsoft Teams, users, chats, files, and organizational data via Microsoft Graph APIs, with added support for creating, updating, listing, and inspecting Teams meetings and calendar events.11 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to interact with Microsoft Teams and Microsoft Graph APIs for user and team management, channel and chat messaging, message editing and deletion, file upload/download, and advanced message search.11 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to interact with Microsoft Teams and Microsoft Graph APIs, covering chat and channel messaging, message search, file uploads and downloads, and user and organizational directory lookups. It supports OAuth device-code authentication, rich Markdown formatting, HTML-to-Markdown conversion for LLM-friendly output, and a read-only mode.11 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to interact with Microsoft Teams and Microsoft Graph APIs for user, team, channel, chat, message search, file, and attachment management. Supports authentication, rich Markdown formatting, and LLM-friendly content conversion.11 npmMIT