Teams MCP - Configuration — Unique AI Documentation
Teams MCP - Configuration
Environment Variables
All configuration is done via environment variables, either directly or through Helm values.
Required Secrets
These must be provided via Kubernetes secrets:
| Variable | Description | Format |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgresql://user:pass@host:5432/db |
AMQP_URL |
RabbitMQ connection string | amqp://user:pass@host:5672/vhost |
MICROSOFT_CLIENT_SECRET |
Entra app client secret | String from Azure portal |
MICROSOFT_WEBHOOK_SECRET |
Webhook validation secret | 128-character random string |
AUTH_HMAC_SECRET |
JWT signing key | 64-character hex string |
ENCRYPTION_KEY |
Token encryption key | 64-character hex string |
Application Configuration
Set via mcpConfig.app in Helm values:
| Variable | Helm Path | Default | Description |
|---|---|---|---|
SELF_URL |
mcpConfig.app.selfUrl |
(required) | Public URL of the MCP server |
Microsoft Configuration
Set via mcpConfig.microsoft in Helm values:
| Variable | Helm Path | Default | Description |
|---|---|---|---|
MICROSOFT_CLIENT_ID |
mcpConfig.microsoft.clientId |
(required) | Entra app client ID |
MICROSOFT_PUBLIC_WEBHOOK_URL |
mcpConfig.microsoft.publicWebhookUrl |
SELF_URL |
Webhook URL if different from SELF_URL |
MICROSOFT_AUTO_START_INGESTION |
mcpConfig.microsoft.autoStartIngestion |
false |
When enabled, auto-enqueue a transcript subscription for every user at login (skips the start_kb_integration tool) |
Unique API Configuration
Set via mcpConfig.unique in Helm values:
| Variable | Helm Path | Default | Description |
|---|---|---|---|
UNIQUE_SERVICE_AUTH_MODE |
mcpConfig.unique.serviceAuthMode |
cluster_local |
Auth mode: cluster_local or external |
UNIQUE_API_BASE_URL |
mcpConfig.unique.apiBaseUrl |
(required) | Unique API endpoint |
UNIQUE_API_VERSION |
mcpConfig.unique.apiVersion |
2023-12-06 |
API version |
UNIQUE_ROOT_SCOPE_ID |
mcpConfig.unique.rootScopeId |
(required) | Root scope ID for uploads |
UNIQUE_USER_FETCH_CONCURRENCY |
mcpConfig.unique.userFetchConcurrency |
5 |
Parallel user lookups |
UNIQUE_INGESTION_SERVICE_BASE_URL |
mcpConfig.unique.ingestionServiceBaseUrl |
(required) | Ingestion service endpoint |
Authentication Configuration
Set via mcpConfig.auth in Helm values:
| Variable | Helm Path | Default | Description |
|---|---|---|---|
AUTH_ACCESS_TOKEN_EXPIRES_IN_SECONDS |
mcpConfig.auth.accessTokenExpiresInSeconds |
60 |
MCP access token TTL |
AUTH_REFRESH_TOKEN_EXPIRES_IN_SECONDS |
mcpConfig.auth.refreshTokenExpiresInSeconds |
2592000 |
MCP refresh token TTL (30 days) |
Runtime Configuration
Set via server.env in Helm values:
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL |
info |
Log level: debug, info, warn, error |
MAX_HEAP_MB |
1920 |
Node.js max heap size |
NODE_ENV |
production |
Node environment |
Full Example
yaml
server:
# Environment variables from secrets
envVars:
- secretRef:
name: teams-mcp-secrets
# Static environment variables
env:
LOG_LEVEL: info
MAX_HEAP_MB: 1920
NODE_ENV: production
# Resource limits
resources:
limits:
memory: 2048Mi
requests:
cpu: 1
memory: 1984Mi
# Temporary storage for processing
volumes:
- name: tmp
emptyDir:
sizeLimit: 20Gi
volumeMounts:
- name: tmp
mountPath: /tmp
# Application configuration
mcpConfig:
enabled: true
app:
selfUrl: https://teams.mcp.example.com
microsoft:
clientId: "12345678-1234-1234-1234-123456789012"
# publicWebhookUrl: https://teams.mcp.example.com # optional
unique:
serviceAuthMode: cluster_local
apiBaseUrl: http://api-gateway.unique:8080
apiVersion: "2023-12-06"
rootScopeId: folder_abc123 # Your root scope ID from Unique
userFetchConcurrency: 5
ingestionServiceBaseUrl: http://node-ingestion.unique:8091
auth:
accessTokenExpiresInSeconds: 60
refreshTokenExpiresInSeconds: 2592000
# Ingress is disabled by default - traffic routed via Kong Gateway
ingress:
enabled: false
# Monitoring
grafana:
dashboard:
enabled: true
folder: mcp-servers
alerts:
enabled: true
defaultAlerts:
graphql:
enabled: true
uniqueApi:
enabled: true
Service Auth Modes
cluster_local (Default)
For deployments within the same Kubernetes cluster as Unique:
yaml
mcpConfig:
unique:
serviceAuthMode: cluster_local
apiBaseUrl: http://api-gateway.unique:8080
ingestionServiceBaseUrl: http://node-ingestion.unique:8091
serviceExtraHeaders:
x-company-id: "company-id"
x-user-id: "service-user-id"
external
For deployments outside the Unique cluster:
yaml
mcpConfig:
unique:
serviceAuthMode: external
apiBaseUrl: https://api.unique.app
serviceExtraHeaders:
authorization: "Bearer <api-key>"
x-app-id: "app-id"
x-user-id: "user-id"
x-company-id: "company-id"
Why a Zitadel Service Account is Required
The Teams MCP Server requires a Zitadel service account to authenticate with the Unique Public API. This service account is used to:
Retrieve matching user information - Look up users in Unique by email or username to resolve meeting participants
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
Upload transcript and recording data - Ingest transcript content and recordings into the Unique knowledge base
The service account credentials are passed via the x-company-id and x-user-id headers in all API requests to ensure proper access control and authorization.
Creating a Zitadel Service Account
- Navigate to Zitadel
- Log in to your Zitadel instance
- Select the organization where you want to ingest transcripts
- Create Service Account
- Go to Service Accounts in the organization settings
- Click New Service Account
- Provide a descriptive name (e.g., "Teams MCP Server Service Account")
- Set appropriate permissions for the service account
- Note the Service Account Details
- Company ID: The organization ID where the service account was created
- User ID: The service account user ID
- Configure in Helm Values
yaml
mcpConfig:
unique:
serviceAuthMode: cluster_local # or external
serviceExtraHeaders:
x-company-id: "<your-company-id>"
x-user-id: "<your-service-account-user-id>"
Service Account Permissions
The service account must have permissions to:
- Read user information (to resolve meeting participants)
- Create scopes/folders in the organization
- Create and modify access permissions
- Upload content to the knowledge base
Ensure the service account has sufficient permissions in the target organization to perform these operations.
Why a Root Scope is Required
The Teams MCP Server requires a root scope (folder) in the Unique platform. All meeting transcripts and recordings are ingested as child scopes under this root, providing a single organizational entry point and making it easy to manage permissions and visibility for Teams content.
Creating the Root Scope
The root scope must be created manually in the Unique platform before deploying the Teams MCP Server. There is no automated provisioning for this step.
Log in to the Unique platform as an administrator
Create a new scope (folder)
- Navigate to the scope/folder management area
- Create a new top-level scope with a descriptive name (e.g., "Teams MCP")
- Note the scope ID — it starts with
scope_(e.g.,scope_abc123xyz)
- Grant the service account access
- Grant the Zitadel service account (see above)
MANAGE,READ, andWRITEon the root scope through the Unique platform. This remains a required manual step — the server does not provision access on a scope it has no rights to.- On startup the server (re-)issues these three grants for the service user (the
x-user-idfrom the API headers) against the configured root scope via the Public APIfolder/add-accessendpoint. The call is additive, so re-affirming existing access on every boot is safe. If it is rejected — a wrong/missingrootScopeId, or a service account that was never granted management of the scope — the pod hard-fails at boot rather than surfacing the error mid-ingestion. Check the startup logs forroot scope <id> permission bootstrap failed.
- On startup the server (re-)issues these three grants for the service user (the
- Configure in Helm Values
yaml
mcpConfig:
unique:
rootScopeId: "scope_abc123xyz"
One Scope Per Environment
Each environment (QA, prod, etc.) requires its own root scope. Do not reuse the same scope ID across environments.
Connection String Format
postgresql://username:password@hostname:port/database?sslmode=require
Required Extensions
The PostgreSQL database requires no special extensions. Migrations create all necessary tables and indexes.
RabbitMQ Configuration
Connection String Format
amqp://username:password@hostname:5672/vhost
Alternative: Individual Fields
Instead of AMQP_URL, you can set individual fields:
| Variable | Description |
|---|---|
AMQP_USERNAME |
RabbitMQ username |
AMQP_PASSWORD |
RabbitMQ password |
AMQP_HOST |
RabbitMQ hostname |
AMQP_PORT |
RabbitMQ port (default: 5672) |
AMQP_VHOST |
Virtual host |
Security Best Practices
- Rotate secrets regularly (especially
MICROSOFT_CLIENT_SECRET) - Use managed identities where possible (Azure, AWS, GCP)
- Encrypt secrets at rest (Kubernetes secrets encryption or external secret managers)
- Limit network access (enable network policies)
- Monitor for anomalies (use provided Grafana dashboards and alerts)
See Security Documentation for details.