Teams MCP - Flows

3 min read

User Connection Flow

Everything starts when a user connects to the MCP server. This triggers OAuth authentication. After authentication, the user can start the KB integration via the start_kb_integration tool to begin receiving meeting notifications. Alternatively, operators can enable MICROSOFT_AUTO_START_INGESTION, in which case every login automatically enqueues a transcript subscription (no tool call required).

embedded_d0c7bdd7a0a9567c495143b0e20e5a2c.pngembedded_55b4ad08b6f4d42a1ea744f2c68e1cd5.png

OAuth Scopes Required: See Microsoft Graph Permissions for detailed justification.

Important: Microsoft access and refresh tokens are never sent to the client. They are received by the server, encrypted, and stored securely. After the Microsoft OAuth flow completes, the server issues opaque JWT tokens to the client for MCP authentication.

Microsoft OAuth Setup Flow

The following sequence shows the complete Microsoft OAuth authentication flow with detailed token handling:

embedded_62f19769b0abd3d171d20653b6e816cf.png

Microsoft Token Refresh Flow

Microsoft tokens are refreshed on-demand when the Graph API returns a 401 error:

embedded_5548c887b4665744380428391c3bb159.png

Subscription Lifecycle

Subscriptions are renewed (not recreated) before they expire. If renewal fails for any reason, the subscription is deleted and the user must reconnect to the MCP server to re-authenticate.

embedded_9b1a83ae20709d85914195de64a2f5e2.pngembedded_bae2c7e0c41a4bc284aa3e32f0b87b36.png

Subscription Scheduling:

  • Subscriptions are set to expire at a configured UTC hour (default: 3 AM)

  • This batches all renewals to a single time window

  • Daily renewal ensures token validity is checked consistently

  • Minimum 2-hour subscription lifetime required for lifecycle notifications

  • If renewal fails: Subscription is deleted and user must reconnect to MCP server

  • See Microsoft Graph Webhooks - Lifecycle Notifications for details

Transcript Processing Flow

When a meeting transcript becomes available, Microsoft Graph sends a webhook notification. The recording is fetched if available (correlated by contentCorrelationId).

embedded_07191afddf7b392265993543733aafdf.pngembedded_6b354edf7b2a6a8a00e2fcee55f40ca3.png

Webhook Validation:

  • Microsoft Graph sends a clientState value with each notification

  • The server validates this matches the secret configured during subscription creation

  • Invalid clientState results in request rejection

Recording Handling:

  • Recording fetch uses contentCorrelationId to find the matching recording for a transcript

  • If the recording is not available, only the transcript is captured

  • Recording failures are logged but don't fail transcript processing

  • Recordings are stored with SKIP_INGESTION mode (no RAG processing)

  • Both transcript and recording share the same content_correlation_id in metadata

Access Control:

  • Meeting organizer receives write + read access

  • Meeting participants receive read access

  • Users are resolved by email or username in Unique platform

Chat Flows

The Chat Module exposes a synchronous request/response tool surface. Each tool call is handled inline — there is no queue or background worker. This is distinct from the async webhook/transcript ingestion path above.

Each tool targets a chat or channel by id: list_* tools return identifiers that the caller passes to subsequent get_*_messages or send_*_message calls. See also: Tools Reference.

Chat Read Flow

The read flow applies to both personal chats (list_chatsget_chat_messages) and team channels (list_teamslist_channelsget_channel_messages). The diagram below shows the personal chat variant; the channel variant substitutes list_teams/list_channels for list_chats and queries /teams/{teamId}/channels/{channelId}/messages instead of /chats/{chatId}/messages.

embedded_052f7b7a7039aff634c763a532b37aa8.png

Key points:

  • list_chats returns a single bounded page (no auto-pagination); hasMore indicates whether more chats exist.

  • Graph does not support server-side messageType filtering on message endpoints. When excludeSystemMessages=true, the server pages through results client-side until the requested number of user messages is collected.

  • Message bodies are returned as-is from Graph (HTML). The get_channel_messages path is identical but targets /teams/{teamId}/channels/{channelId}/messages.

Chat Search Flow

search_messages queries the Microsoft Search API (POST /search/query on Graph v1.0) and optionally hydrates each hit with its full message body.

embedded_2a3ff535e3351319e97e00c1444a99ac.png

Key points:

  • Pagination uses offset/size on the request body and moreResultsAvailable on the response — not @odata.nextLink. The caller advances the page by incrementing offset by size.

  • The source filter (chat/channel/all) is applied client-side after the Graph response, because the Search API's entityTypes: ['chatMessage'] covers both containers with no sub-filter.

  • detail=full issues one additional Graph call per hit. Concurrency is capped at 5 to avoid Graph throttling. A forbidden or deleted hit falls back to its summary row rather than failing the page.

  • See Tools Reference for the full search_messages parameter list.

Chat Send Flow

The send flow applies to personal chats (send_chat_message) and team channels (send_channel_message). The caller must first obtain the target ID via a list_* call.

embedded_478db557738b01d2140d79dd4fd1dc5a.png

Key points:

  • Only plain-text messages are supported (contentType: 'text'). Rich HTML or adaptive cards are not sent.

  • send_channel_message returns a webUrl linking directly to the posted message in Teams. send_chat_message returns only the message id.

  • The entire flow is synchronous — the tool returns only after Graph confirms the message was created.

Standard References

Last updated