Teams MCP - FAQ
17 min read
General
What type of MCP server is this?
Answer: The Teams MCP Server is a connector-style MCP server, not a traditional MCP server. Unlike traditional MCP servers that provide tools, prompts, resources, or other interactive capabilities, this server operates automatically in the background once connected.
What it does:
Once a user connects their Microsoft account, the connector automatically ingests meeting transcripts into the Unique knowledge base
It does not expose any MCP tools, prompts, or resources
It does not require any tool calls or additional interaction after the initial connection
It operates continuously in the background, processing transcripts as they become available
What it does not do:
Provide MCP tools for querying or manipulating data
Offer prompts or prompt templates
Expose resources for browsing or accessing
Require any interaction beyond the initial connection
Architecture:
Uses MCP OAuth for user authentication and connection
Does not implement MCP tools, prompts, or resources
Operates as a background service that automatically ingests transcripts
Processes webhook notifications from Microsoft Graph API
Ingests content into Unique knowledge base without requiring tool calls
Design rationale:
The connector model allows for automatic, continuous data ingestion
Users connect once and transcripts are automatically captured
No need for interactive tool calls or resource browsing
Simplifies the user experience by removing the need for ongoing interaction
This is a data ingestion connector that uses the MCP protocol for authentication and connection management, but functions as a background service rather than an interactive MCP server.
Authentication & Permissions
Why do I need admin consent?
Answer:OnlineMeetingRecording.Read.All and OnlineMeetingTranscript.Read.All require admin consent because they access sensitive meeting content (audio/video recordings and transcripts). This is a Microsoft requirement, not a Teams MCP requirement.
What to do:
Go to Azure Portal → App Registration → API permissions
Click "Grant admin consent for [Your Organization]"
Users can then connect and grant their own consent
See also:Understanding Admin Consent
Why do users still need to consent after admin consent?
Answer: This is standard Microsoft behavior for delegated permissions. Even after admin consent, each user must individually consent because delegated permissions act on behalf of the signed-in user. This ensures users are aware of what data the app can access.
This is not a bug - it's how Microsoft OAuth works for all Microsoft 365 apps.
See also:Understanding Consent Requirements
What is the "login flicker" when users reconnect?
Answer: After a user has connected once, Microsoft Entra ID uses silent authentication on subsequent connections. The browser quickly redirects through the OAuth flow to validate the existing session, creating a brief "flicker" effect. This is normal Microsoft OAuth behavior, not a bug.
See also:User Reconnection Experience
Why can't I use certificate authentication?
Answer: While it's technically possible to use certificate authentication with the Authorization Code flow, it would require significant additional implementation effort in our OAuth packages. The standard approach for delegated permissions is to use a client secret, which is simpler to implement and maintain.
See also:
Why do I need a client ID and client secret?
Answer: Microsoft Graph API uses OAuth 2.0 for authentication, which requires a CLIENT_ID to identify and authorize applications. The CLIENT_SECRET proves to Microsoft that your server is the legitimate application (not an imposter). It's used during the OAuth token exchange to securely obtain Microsoft access and refresh tokens.
The CLIENT_ID enables Microsoft to verify application identity, enforce permissions, enable consent flows, track and audit API usage, and ensure delegated authorization is scoped to data the signed-in user can access.
Security note: The client secret is never sent to clients - it's only used server-side during the OAuth flow.
See also:
Why can't I use application permissions instead of delegated?
Answer: Application permissions would require tenant administrators to create Application Access Policies via PowerShell for each user. This defeats the self-service MCP model where users connect their own accounts without IT involvement.
See also:Why Delegated (Not Application) Permissions
What's the difference between delegated and application permissions?
Answer:
Delegated: Acts on behalf of the signed-in user, only accesses data that user can access
Application: Acts as the application itself, requires admin-configured policies per user
Teams MCP uses delegated permissions for self-service user connections.
See also:Why Delegated (Not Application) Permissions
Why can't I use Client Credentials flow?
Answer: Client Credentials flow only supports application permissions, which would require tenant admins to create Application Access Policies per user via PowerShell. This is impractical for self-service MCP connections. Delegated permissions require the Authorization Code flow.
See also:
Why can't I use multiple app registrations?
Answer: Each Teams MCP deployment uses one Microsoft Entra ID app registration. The app can be configured as multi-tenant to serve users from multiple organizations, but you don't need separate app registrations per tenant.
Single App Registration Architecture:
Single App Registration: One
CLIENT_ID/CLIENT_SECRETpair per deploymentMulti-Tenant Capable: The app registration can be configured to accept users from multiple Microsoft tenants
Cross-Tenant Authentication: Users from different organizations authenticate via Enterprise Applications in their tenant that reference the original app registration
Enterprise Application Creation: When tenant admin grants consent, Microsoft creates an Enterprise Application in their tenant as a proxy to the original app registration
This design uses a single OAuth application that can serve users across multiple tenants, rather than requiring separate app registrations per organization.
See also:
Authentication Architecture - Single App Registration Architecture
Microsoft Entra ID Documentation - Authentication and authorization
Configuration
Why do I need a Zitadel service account?
Answer: The Teams MCP Server requires a Zitadel service account to authenticate with the Unique Public API and perform operations on behalf of the server.
What the service account is used for:
Retrieve matching user information - Look up users in Unique by email or username to resolve meeting participants from Microsoft Teams
Create scopes (folders) - Create organizational folders in Unique for storing meeting transcripts and recordings
Set access permissions - Grant appropriate read/write permissions to meeting organizers and participants based on their role in the meeting
Upload transcript and recording data - Ingest transcript content (VTT files) and recordings (MP4 files) into the Unique knowledge base
How it works:
The service account credentials are passed via the
x-company-idandx-user-idheaders in all API requests to the Unique Public APIThis ensures proper access control and authorization for all operations
The service account must be created in the Zitadel organization where you want to ingest transcripts
How to create:
Log in to Zitadel and select the target organization
Navigate to Service Accounts in the organization settings
Create a new service account with appropriate permissions
Note the company ID (organization ID) and user ID (service account ID)
Configure these values in Helm values under
mcpConfig.unique.serviceExtraHeaders
See also:Zitadel Service Account
What's the redirect URI format?
Answer: The redirect URI must match exactly:
https://<your-domain>/auth/callbackCommon mistakes:
Missing trailing slash (if configured with one)
Using
http://instead ofhttps://in productionWrong path (must be
/auth/callback)
See also:Redirect URI Configuration
Why do I need a webhook secret?
Answer: The MICROSOFT_WEBHOOK_SECRET validates that incoming webhook notifications are actually from Microsoft Graph, not from an attacker. It's sent to Microsoft when creating subscriptions and returned in every webhook payload for validation.
Generate:openssl rand -hex 64 (128 characters)
See also:Webhook Secret
What happens if I change the encryption key?
Answer: All stored Microsoft tokens become unreadable. All users must reconnect to the MCP server to re-authenticate. There is no zero-downtime rotation for the encryption key.
Best practice: Plan for a maintenance window and notify users before rotating the encryption key.
See also:ENCRYPTION_KEY Rotation
What happens if I change the client secret?
Answer: Update the Kubernetes secret and restart the pods. Users don't need to reconnect - the server will use the new secret for token refresh operations.
Rotation process:
Create new secret in Entra ID
Update Kubernetes secret
Restart pods
Verify authentication works
Delete old secret from Entra ID
See also:Client Secret Management
What happens if I change the webhook secret?
Answer:Rotation is currently not possible - There is no easy way to invalidate all existing subscriptions that were created with the old secret. The MICROSOFT_WEBHOOK_SECRET is sent to Microsoft as clientState when creating subscriptions. When the secret changes, all existing subscriptions will fail webhook validation because they contain the old secret, but there's no automated mechanism to recreate all subscriptions.
Note: Automated rotation might be part of a future release.
If rotation becomes necessary, it would require manually deleting all subscriptions and having all users reconnect, which may miss transcripts created during the gap.
See also:MICROSOFT_WEBHOOK_SECRET Rotation
Architecture & Design
Why are Microsoft tokens never sent to clients?
Answer: This is a critical security design. Microsoft OAuth tokens (access and refresh) are exchanged entirely on the server and stored encrypted. The server then issues separate opaque JWT tokens to clients for MCP API authentication. This ensures: - Microsoft tokens never leave the server - Clients cannot access Microsoft Graph API directly - All Microsoft API calls are made by the server on behalf of authenticated users
Token Isolation Design:
Microsoft OAuth Flow: User authenticates with Microsoft Entra ID
Token Exchange: Server exchanges authorization code for Microsoft tokens (using
CLIENT_SECRET)Token Storage: Microsoft tokens are encrypted and stored on the server only
Client Authentication: Server issues separate opaque JWT tokens to the client for MCP API access
See also:
Why are MCP tokens hashed but Microsoft tokens encrypted?
Answer:
MCP tokens: Opaque JWTs that the server doesn't need to read - hash comparison is sufficient for validation
Microsoft tokens: Must be decrypted to use for Graph API calls - encryption allows retrieval
Hashing reduces attack surface (no decryption key needed for MCP tokens), while encryption enables token retrieval for Microsoft API calls.
See also:Token Security
Why use AES-GCM for token encryption?
Answer: AES-GCM provides authenticated encryption - both confidentiality and integrity. It prevents tampering with ciphertext and is an industry standard for token encryption.
See also:Microsoft Tokens (Encrypted at Rest)
Why refresh tokens rotate?
Answer: Refresh token rotation with family-based revocation detects token theft. If a refresh token is reused (indicating possible theft), the entire token family is revoked. This prevents attackers from using stolen tokens while the legitimate client continues working.
See also:Refresh Token Rotation
Why are subscriptions renewed instead of recreated?
Answer: The biggest reason is that recreation may miss transcripts. Microsoft Graph only sends notifications for transcripts created while a subscription is active. When recreating a subscription (DELETE + POST), there's a gap where no subscription exists. Any transcripts created during that gap will never generate notifications—those transcripts are lost forever.
Renewal (PATCH) keeps the subscription continuously active, eliminating this gap. Additionally, renewal is more efficient than recreation—it preserves the subscription ID and reduces API calls. Renewal happens automatically before expiration (default: 3 AM UTC daily) to ensure token validity is checked consistently.
See also:Subscription Lifecycle
Token Management
What happens if token refresh fails?
Possible causes:
Microsoft refresh token expired (~90 days of inactivity)
User revoked consent in Microsoft account settings
Network issues reaching Microsoft token endpoint
Client secret was rotated without updating the configuration
Resolution: User must reconnect to MCP server to re-authenticate.
See also:
What happens if a token family is revoked?
Answer: All refresh operations fail for that user. The user must re-authenticate completely. This happens automatically when refresh token reuse is detected (possible token theft).
See also:
What happens if the encryption key changes?
Answer: All stored Microsoft tokens become unreadable. All users must reconnect to obtain fresh tokens. There is no zero-downtime rotation for the encryption key.
See also:
Why are MCP access tokens so short-lived (60 seconds)?
Answer: Short-lived access tokens reduce the impact of token theft. If an access token is compromised, it expires quickly. Refresh tokens are used to obtain new access tokens without user re-authentication.
See also:
MCP Authorization - MCP protocol authorization spec
Data Sync
Why can't historical transcripts be synced?
Answer: Microsoft Graph does not provide a way to list transcripts across all past meetings using delegated permissions. The only cross-meeting transcript listing API is getAllTranscripts:
GET /users/{userId}/onlineMeetings/getAllTranscripts(meetingOrganizerUserId='{userId}',startDateTime=...)This API requires application permissions (OnlineMeetingTranscript.Read.All). Microsoft explicitly marks delegated (user) permissions as Not supported for this endpoint.
Teams MCP uses delegated permissions so users can connect their own Microsoft account without IT administrator involvement. With delegated permissions the only supported path is GET /users/{userId}/onlineMeetings/{meetingId}/transcripts, which requires knowing the meeting ID in advance — making bulk historical enumeration impossible.
As a result, Teams MCP can only capture transcripts for meetings that occur after the user connects. Any meetings that took place before the subscription was created are inaccessible.
Additional historical limits (even with application permissions):
One-time meetings expire 60 days after their scheduled time; the transcript API returns an error for expired meetings.
Recording/transcript files are deleted after the tenant's expiration policy window (Microsoft default: 120 days).
See also:
onlineMeeting: getAllTranscripts — Microsoft Graph API reference
Limits and specifications for Microsoft Teams — Meeting expiration
Why is there no delta sync?
Answer: Microsoft Graph does expose delta APIs for transcripts and recordings — callTranscript: delta and callRecording: delta — which support both full initial sync and incremental sync (returning only items added since a $deltaToken). However, these APIs require application permissions. Microsoft explicitly marks delegated permissions as Not supported:
Permission type | Support |
|---|---|
Delegated (work or school account) | Not supported |
Delegated (personal Microsoft account) | Not supported |
Application |
|
Because Teams MCP uses delegated permissions, delta sync is unavailable. The service instead relies on real-time webhook notifications (change notifications), which deliver new transcript events as they occur. This covers all meetings going forward but cannot recover transcripts missed due to a subscription gap.
Switching to application permissions would unlock delta sync, but would require tenant administrators to configure Application Access Policies via PowerShell for each individual user — defeating the self-service connection model.
See also:
What happens if I miss transcripts during a subscription gap?
Answer: They are permanently lost. Microsoft Graph only delivers webhook notifications for transcripts created while a subscription is active. If a subscription expires (or is deleted and recreated), any transcripts produced during the gap will never generate a notification.
This is a fundamental limitation of the change notification model combined with the unavailability of delta sync under delegated permissions. There is no catch-up or replay mechanism.
To minimise the risk:
Ensure the renewal cron runs reliably (default: 3 AM UTC daily).
Monitor for
subscription_renewal_failedlog events — a failed renewal is the most common cause of gaps.Use subscription renewal (PATCH) rather than recreation (DELETE + POST). See Why are subscriptions renewed instead of recreated?
See also:Subscription Lifecycle
Subscriptions & Processing
Why do subscriptions expire?
Answer: Microsoft Graph subscriptions expire after a maximum of 3 days. Teams MCP automatically renews subscriptions before they expire (default: 3 AM UTC daily). This ensures token validity is checked consistently.
See also:Subscription Lifecycle
What happens if a subscription renewal fails?
Answer: The subscription is deleted and the user must reconnect to the MCP server to re-authenticate. This can happen if: - Microsoft refresh token expired (~90 days of inactivity) - User revoked app consent - Network issues reaching Microsoft
Any transcripts produced between the failed renewal and the user reconnecting are permanently lost — there is no backfill or catch-up mechanism once a subscription lapses.
See also:
Why aren't transcripts appearing in Unique?
Answer: Check the following:
User has active subscription - Verify the user successfully connected and subscription was created
Webhook notifications received - Check if Microsoft is sending notifications
RabbitMQ queue processing - Verify messages are being processed
No processing errors - Check logs for any failures during transcript processing
See also:Transcript Processing Flow
Webhooks & Processing
Why use RabbitMQ for webhook processing?
Answer: Microsoft requires webhook endpoints to respond within 10 seconds, or it considers the delivery failed and retries. However, processing transcript notifications involves database lookups, multiple Microsoft Graph API calls, user resolution, and content ingestion, which can take 30+ seconds.
RabbitMQ decouples webhook reception from processing:
Webhook Controller receives the notification, validates it, publishes to RabbitMQ, and returns
202 AcceptedimmediatelyRabbitMQ durably stores the message until a consumer processes it
Transcript Service consumes messages and performs the slow processing asynchronously
This ensures we meet Microsoft's strict timeout requirements while processing transcripts reliably.
Benefits:
Meets Microsoft's 10-second webhook response requirement
Avoids Microsoft retry storms from failed deliveries
Provides reliability via Dead Letter Exchange for failed message inspection and retry
Enables horizontal scaling with multiple service replicas
Handles burst traffic gracefully with message buffering
See also:
Microsoft Graph Webhooks - Microsoft webhook documentation
Microsoft Graph Change Notifications - Change notification requirements
Can I deploy without RabbitMQ?
Answer: No. RabbitMQ is required to meet Microsoft's webhook response time requirements. Without it, webhook processing would timeout and Microsoft would stop sending notifications.
How does webhook validation work?
Answer: When creating a subscription, the server sends MICROSOFT_WEBHOOK_SECRET as clientState to Microsoft. Microsoft returns this value in every webhook payload. The server validates that the received clientState matches the configured secret, rejecting invalid requests.
See also:Webhook Validation
What happens if webhook validation fails?
Answer: The request is rejected with 401 Unauthorized. Microsoft will retry the notification. If validation consistently fails, Microsoft may stop sending notifications for that subscription.
See also:Webhook Validation
What happens to messages that fail processing?
Answer: Failed transcript processing messages are nacked and routed to a Dead Letter Exchange (DLX). Messages accumulate there indefinitely — there is no automatic TTL or retry from the DLQ.
An operator must inspect the DLQ manually (e.g., via the RabbitMQ management UI) to decide whether to republish a message for retry or discard it. Because there is no delta sync available, a message in the DLQ represents the only copy of that webhook notification — if discarded without successful processing, the transcript will not be ingested.
See also:Why use RabbitMQ for webhook processing?
Deployment
What happens if the database is full?
Answer: Write operations will fail. Solutions:
Run token cleanup job manually
Increase database storage
Archive old data
Data Model
Why track token families?
Answer: Token family tracking enables theft detection. Each OAuth session has a token_family identifier. If a refresh token is reused (indicating possible theft), the entire family is revoked. This prevents attackers from using stolen tokens while the legitimate client continues working.
See also:Token Family Tracking
Why store MCP tokens as hashes?
Answer: MCP tokens are opaque JWTs - the server doesn't need to read them, only validate them. Hash comparison is sufficient for validation and reduces attack surface (no decryption key needed).
See also:MCP Tokens (Hashed for Validation)
Why encrypt Microsoft tokens instead of hashing?
Answer: Microsoft tokens must be decrypted to use for Graph API calls. Encryption allows retrieval, while hashing is one-way and would prevent token usage.
See also:Microsoft Tokens (Encrypted at Rest)
Security
How are Microsoft tokens stored?
Answer: Microsoft access and refresh tokens are encrypted at rest using AES-256-GCM and stored in the user_profiles table. They are never sent to clients - only opaque JWT tokens are issued to clients for MCP authentication.
See also:Token Security
What happens if a refresh token is stolen?
Answer: If a refresh token is reused (indicating possible theft), the entire token family is revoked. The user must re-authenticate completely. This is detected automatically by the refresh token rotation mechanism.
See also:Refresh Token Rotation
Why use PKCE?
Answer: PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks. It's required for all OAuth flows in OAuth 2.1 and uses S256 challenge method (SHA-256).
See also:OAuth 2.1 with PKCE
Why separate MCP tokens from Microsoft tokens?
Answer: This design ensures:
Microsoft tokens never leave the server
Clients cannot access Microsoft Graph API directly
All Microsoft API calls are made by the server on behalf of authenticated users
Client tokens are opaque JWTs that only authenticate with the MCP server
See also:
What's the threat model?
Answer: The security architecture protects against:
Token theft (refresh token rotation, family-based revocation)
Authorization code interception (PKCE)
Webhook spoofing (clientState validation)
Token tampering (AES-GCM authenticated encryption)
See also:Security
Microsoft Graph Integration
Why single app registration architecture?
Answer: Each MCP deployment uses one Microsoft Entra ID app registration that can serve users from multiple Microsoft tenants. When tenant admins grant consent, Microsoft creates Enterprise Applications in their tenants. This is simpler than managing multiple app registrations.
See also:
How does multi-tenant authentication work?
Answer:
App registration configured as multi-tenant ("Accounts in any organizational directory")
Tenant admin grants consent → Microsoft creates Enterprise Application in their tenant
Users authenticate via Enterprise Application in their tenant
One MCP deployment serves all tenants
See also:
Multi-Tenant
Can a user connect multiple Microsoft tenants?
Answer: Not in a single session. One OAuth login covers exactly one Microsoft tenant. If a user belongs to multiple tenants (e.g., their home tenant plus a guest tenant), they must authenticate separately for each tenant they want to capture meetings from.
Each tenant authentication creates an independent user profile in Teams MCP. Transcripts from each tenant are ingested separately under the identity used to connect that tenant.
See also:Single App Registration Architecture
Can one deployment serve multiple Microsoft tenants?
Answer: Yes. Configure the app registration with "Accounts in any organizational directory" (multi-tenant). When each organization's admin grants consent, Microsoft creates an Enterprise Application in their tenant. One MCP deployment serves all tenants.
Considerations:
Data isolation: All tenant data stored in same database (with tenant-scoped access controls)
Enterprise Application management: Each tenant admin controls user assignment
Compliance: Some organizations may require dedicated infrastructure
See also:Multi-Tenant App Registration
Related Documentation
Architecture - System components and infrastructure
Security - Encryption, authentication, and threat model
Flows - User connection, subscription lifecycle, transcript processing
Permissions - Required scopes and least-privilege justification
Operator Guide - Deployment and operations
Standard References
Microsoft Graph API - Graph API overview
Microsoft Entra ID Troubleshooting - Authentication troubleshooting
Kubernetes Documentation - Kubernetes official docs