Mainstream messaging
Microsoft Teams
상태: 텍스트 + DM 첨부 파일이 지원됩니다. 채널/그룹 파일 전송에는 sharePointSiteId + Graph 권한이 필요합니다(그룹 채팅에서 파일 보내기 참조). 설문은 Adaptive Cards로 전송됩니다. 메시지 작업은 파일 우선 전송을 위해 명시적인 upload-file을 제공합니다.
번들 Plugin
Microsoft Teams는 현재 OpenClaw 릴리스에서 번들 Plugin으로 제공되므로, 일반 패키지 빌드에서는 별도 설치가 필요하지 않습니다.
번들 Teams가 제외된 이전 빌드나 사용자 지정 설치를 사용 중이라면 npm 패키지를 직접 설치하세요.
openclaw plugins install @openclaw/msteams현재 공식 릴리스 태그를 따르려면 기본 패키지를 사용하세요. 재현 가능한 설치가 필요할 때만 정확한 버전을 고정하세요.
로컬 체크아웃(git 저장소에서 실행하는 경우):
openclaw plugins install ./path/to/local/msteams-plugin자세한 내용: Plugin
빠른 설정
@microsoft/teams.cli는 단일 명령으로 봇 등록, 매니페스트 생성, 자격 증명 생성을 처리합니다.
1. 설치 및 로그인
npm install -g @microsoft/teams.cli@previewteams loginteams status # verify you're logged in and see your tenant info2. 터널 시작(Teams는 localhost에 접근할 수 없음)
아직 설치하지 않았다면 devtunnel CLI를 설치하고 인증하세요(시작 가이드).
# One-time setup (persistent URL across sessions):devtunnel create my-openclaw-bot --allow-anonymousdevtunnel port create my-openclaw-bot -p 3978 --protocol auto # Each dev session:devtunnel host my-openclaw-bot# Your endpoint: https://<tunnel-id>.devtunnels.ms/api/messages대안: ngrok http 3978 또는 tailscale funnel 3978(단, 세션마다 URL이 변경될 수 있음).
3. 앱 생성
teams app create \ --name "OpenClaw" \ --endpoint "https://<your-tunnel-url>/api/messages"이 단일 명령은 다음을 수행합니다.
- Entra ID(Azure AD) 애플리케이션 생성
- 클라이언트 시크릿 생성
- Teams 앱 매니페스트(아이콘 포함) 빌드 및 업로드
- 봇 등록(기본적으로 Teams 관리, Azure 구독 불필요)
출력에는 CLIENT_ID, CLIENT_SECRET, TENANT_ID, 그리고 Teams App ID가 표시됩니다. 다음 단계에서 사용할 수 있도록 기록해 두세요. 앱을 Teams에 직접 설치하는 옵션도 제공됩니다.
4. OpenClaw 구성 출력의 자격 증명을 사용하세요.
{ channels: { msteams: { enabled: true, appId: "<CLIENT_ID>", appPassword: "<CLIENT_SECRET>", tenantId: "<TENANT_ID>", webhook: { port: 3978, path: "/api/messages" }, }, },}또는 환경 변수를 직접 사용하세요: MSTEAMS_APP_ID, MSTEAMS_APP_PASSWORD, MSTEAMS_TENANT_ID.
5. Teams에 앱 설치
teams app create는 앱 설치를 묻습니다. "Install in Teams"를 선택하세요. 건너뛰었다면 나중에 링크를 가져올 수 있습니다.
teams app get <teamsAppId> --install-link6. 모든 것이 작동하는지 확인
teams app doctor <teamsAppId>이 명령은 봇 등록, AAD 앱 구성, 매니페스트 유효성, SSO 설정 전반에 대한 진단을 실행합니다.
프로덕션 배포의 경우 클라이언트 시크릿 대신 페더레이션 인증(인증서 또는 관리 ID)을 사용하는 것을 고려하세요.
목표
- Teams DM, 그룹 채팅 또는 채널을 통해 OpenClaw와 대화합니다.
- 라우팅을 결정적으로 유지합니다. 응답은 항상 도착한 채널로 돌아갑니다.
- 안전한 채널 동작을 기본값으로 사용합니다(달리 구성하지 않는 한 멘션 필요).
구성 쓰기
기본적으로 Microsoft Teams는 /config set|unset으로 트리거된 구성 업데이트를 쓸 수 있습니다(commands.config: true 필요).
비활성화하려면 다음을 사용하세요.
{ channels: { msteams: { configWrites: false } },}액세스 제어(DM + 그룹)
DM 액세스
- 기본값:
channels.msteams.dmPolicy = "pairing". 알 수 없는 발신자는 승인될 때까지 무시됩니다. channels.msteams.allowFrom에는 안정적인 AAD 객체 ID 또는accessGroup:core-team같은 정적 발신자 액세스 그룹을 사용해야 합니다.- 허용 목록에 UPN/표시 이름 매칭을 의존하지 마세요. 변경될 수 있습니다. OpenClaw는 기본적으로 직접 이름 매칭을 비활성화합니다.
channels.msteams.dangerouslyAllowNameMatching: true로 명시적으로 옵트인하세요. - 자격 증명이 허용하는 경우 마법사는 Microsoft Graph를 통해 이름을 ID로 해석할 수 있습니다.
그룹 액세스
- 기본값:
channels.msteams.groupPolicy = "allowlist"(groupAllowFrom을 추가하지 않으면 차단됨). 설정되지 않았을 때 기본값을 재정의하려면channels.defaults.groupPolicy를 사용하세요. channels.msteams.groupAllowFrom은 그룹 채팅/채널에서 트리거할 수 있는 발신자 또는 정적 발신자 액세스 그룹을 제어합니다(channels.msteams.allowFrom으로 폴백).- 모든 구성원(기본적으로 여전히 멘션으로 제한)을 허용하려면
groupPolicy: "open"을 설정하세요. - 채널을 전혀 허용하지 않으려면
channels.msteams.groupPolicy: "disabled"를 설정하세요.
예:
{ channels: { msteams: { groupPolicy: "allowlist", groupAllowFrom: ["00000000-0000-0000-0000-000000000000", "accessGroup:core-team"], }, },}Teams + 채널 허용 목록
channels.msteams.teams아래에 팀과 채널을 나열하여 그룹/채널 응답 범위를 제한하세요.- 키는 변경 가능한 표시 이름이 아니라 Teams 링크의 안정적인 Teams 대화 ID를 사용해야 합니다.
groupPolicy="allowlist"이고 팀 허용 목록이 있으면, 나열된 팀/채널만 수락됩니다(멘션으로 제한).- 구성 마법사는
Team/Channel항목을 받아 저장해 줍니다. - 시작 시 OpenClaw는 팀/채널 및 사용자 허용 목록 이름을 ID로 해석하고(Graph 권한이 허용하는 경우) 매핑을 로그에 기록합니다. 해석되지 않은 팀/채널 이름은 입력한 그대로 유지되지만,
channels.msteams.dangerouslyAllowNameMatching: true가 활성화되지 않는 한 기본적으로 라우팅에서 무시됩니다.
예:
{ channels: { msteams: { groupPolicy: "allowlist", teams: { "My Team": { channels: { General: { requireMention: true }, }, }, }, }, },}수동 설정(Teams CLI 없이)
Teams CLI를 사용할 수 없다면 Azure Portal을 통해 봇을 수동으로 설정할 수 있습니다.
작동 방식
- Microsoft Teams Plugin을 사용할 수 있는지 확인합니다(현재 릴리스에 번들됨).
- Azure Bot(App ID + 시크릿 + 테넌트 ID)을 생성합니다.
- 봇을 참조하고 아래 RSC 권한을 포함하는 Teams 앱 패키지를 빌드합니다.
- Teams 앱을 팀(또는 DM용 개인 범위)에 업로드/설치합니다.
~/.openclaw/openclaw.json(또는 환경 변수)에서msteams를 구성하고 Gateway를 시작합니다.- Gateway는 기본적으로
/api/messages에서 Bot Framework Webhook 트래픽을 수신합니다.
1단계: Azure Bot 생성
-
Azure Bot 생성으로 이동합니다.
-
Basics 탭을 작성합니다.
필드 값 Bot handle 봇 이름, 예: openclaw-msteams(고유해야 함)Subscription Azure 구독 선택 Resource group 새로 만들거나 기존 항목 사용 Pricing tier 개발/테스트용 Free Type of App Single Tenant(권장, 아래 참고 참조) Creation type Create new Microsoft App ID
- Review + create → Create를 클릭합니다(~1-2분 대기).
2단계: 자격 증명 가져오기
- Azure Bot 리소스 → Configuration으로 이동합니다.
- Microsoft App ID를 복사합니다. 이것이
appId입니다. - Manage Password를 클릭하고 App Registration으로 이동합니다.
- Certificates & secrets → New client secret 아래에서 Value를 복사합니다. 이것이
appPassword입니다. - Overview로 이동하여 Directory (tenant) ID를 복사합니다. 이것이
tenantId입니다.
3단계: 메시징 엔드포인트 구성
- Azure Bot → Configuration에서
- Messaging endpoint를 Webhook URL로 설정합니다.
- 프로덕션:
https://your-domain.com/api/messages - 로컬 개발: 터널 사용(아래 로컬 개발 참조)
- 프로덕션:
4단계: Teams 채널 활성화
- Azure Bot → Channels에서
- Microsoft Teams → Configure → Save를 클릭합니다.
- 서비스 약관에 동의합니다.
5단계: Teams 앱 매니페스트 빌드
botId = <App ID>인bot항목을 포함합니다.- 범위:
personal,team,groupChat. supportsFiles: true(개인 범위 파일 처리에 필요).- RSC 권한을 추가합니다(RSC 권한 참조).
- 아이콘 생성:
outline.png(32x32) 및color.png(192x192). - 세 파일을 함께 압축합니다:
manifest.json,outline.png,color.png.
6단계: OpenClaw 구성
{ channels: { msteams: { enabled: true, appId: "<APP_ID>", appPassword: "<APP_PASSWORD>", tenantId: "<TENANT_ID>", webhook: { port: 3978, path: "/api/messages" }, }, },}환경 변수: MSTEAMS_APP_ID, MSTEAMS_APP_PASSWORD, MSTEAMS_TENANT_ID.
7단계: Gateway 실행
Plugin을 사용할 수 있고 msteams 구성에 자격 증명이 있으면 Teams 채널은 자동으로 시작됩니다.
페더레이션 인증(인증서와 관리 ID)
2026.4.11에 추가됨
프로덕션 배포에서 OpenClaw는 클라이언트 시크릿보다 더 안전한 대안으로 페더레이션 인증을 지원합니다. 두 가지 방법을 사용할 수 있습니다.
옵션 A: 인증서 기반 인증
Entra ID 앱 등록에 등록된 PEM 인증서를 사용합니다.
설정:
- 인증서를 생성하거나 확보합니다(개인 키가 포함된 PEM 형식).
- Entra ID → App Registration → Certificates & secrets → Certificates → 공개 인증서를 업로드합니다.
구성:
{ channels: { msteams: { enabled: true, appId: "<APP_ID>", tenantId: "<TENANT_ID>", authType: "federated", certificatePath: "/path/to/cert.pem", webhook: { port: 3978, path: "/api/messages" }, }, },}환경 변수:
MSTEAMS_AUTH_TYPE=federatedMSTEAMS_CERTIFICATE_PATH=/path/to/cert.pem
옵션 B: Azure 관리 ID
비밀번호 없는 인증을 위해 Azure 관리 ID를 사용합니다. 관리 ID를 사용할 수 있는 Azure 인프라(AKS, App Service, Azure VM) 배포에 적합합니다.
작동 방식:
- 봇 pod/VM에 관리 ID(시스템 할당 또는 사용자 할당)가 있습니다.
- 페더레이션 ID 자격 증명이 관리 ID를 Entra ID 앱 등록에 연결합니다.
- 런타임에 OpenClaw는
@azure/identity를 사용하여 Azure IMDS 엔드포인트(169.254.169.254)에서 토큰을 획득합니다. - 토큰은 봇 인증을 위해 Teams SDK에 전달됩니다.
사전 요구 사항:
- 관리 ID가 활성화된 Azure 인프라(AKS 워크로드 ID, App Service, VM)
- Entra ID 앱 등록에 생성된 페더레이션 ID 자격 증명
- pod/VM에서 IMDS(
169.254.169.254:80)로의 네트워크 액세스
구성(시스템 할당 관리 ID):
{ channels: { msteams: { enabled: true, appId: "<APP_ID>", tenantId: "<TENANT_ID>", authType: "federated", useManagedIdentity: true, webhook: { port: 3978, path: "/api/messages" }, }, },}구성(사용자 할당 관리 ID):
{ channels: { msteams: { enabled: true, appId: "<APP_ID>", tenantId: "<TENANT_ID>", authType: "federated", useManagedIdentity: true, managedIdentityClientId: "<MI_CLIENT_ID>", webhook: { port: 3978, path: "/api/messages" }, }, },}환경 변수:
MSTEAMS_AUTH_TYPE=federatedMSTEAMS_USE_MANAGED_IDENTITY=trueMSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id>(사용자 할당에만 해당)
AKS 워크로드 ID 설정
워크로드 ID를 사용하는 AKS 배포의 경우:
-
AKS 클러스터에서 워크로드 ID를 활성화합니다.
-
Entra ID 앱 등록에서 페더레이션 ID 자격 증명을 만듭니다.
bash az ad app federated-credential create --id <APP_OBJECT_ID> --parameters '{ "name": "my-bot-workload-identity", "issuer": "<AKS_OIDC_ISSUER_URL>", "subject": "system:serviceaccount:<NAMESPACE>:<SERVICE_ACCOUNT>", "audiences": ["api://AzureADTokenExchange"]}' -
앱 클라이언트 ID로 Kubernetes 서비스 계정에 주석을 추가합니다.
yaml apiVersion: v1kind: ServiceAccountmetadata: name: my-bot-sa annotations: azure.workload.identity/client-id: "<APP_CLIENT_ID>" -
워크로드 ID 주입을 위해 Pod에 레이블을 지정합니다.
yaml metadata: labels: azure.workload.identity/use: "true" -
IMDS(
169.254.169.254)에 대한 네트워크 액세스를 보장합니다. NetworkPolicy를 사용하는 경우 포트 80에서169.254.169.254/32로의 트래픽을 허용하는 송신 규칙을 추가합니다.
인증 유형 비교
| 방법 | 구성 | 장점 | 단점 |
|---|---|---|---|
| 클라이언트 시크릿 | appPassword |
설정이 간단함 | 시크릿 순환이 필요하고 보안성이 낮음 |
| 인증서 | authType: "federated" + certificatePath |
네트워크를 통한 공유 시크릿이 없음 | 인증서 관리 오버헤드 |
| 관리 ID | authType: "federated" + useManagedIdentity |
비밀번호가 필요 없고 관리할 시크릿 없음 | Azure 인프라가 필요함 |
기본 동작: authType이 설정되지 않은 경우 OpenClaw는 기본적으로 클라이언트 시크릿 인증을 사용합니다. 기존 구성은 변경 없이 계속 작동합니다.
로컬 개발(터널링)
Teams는 localhost에 접근할 수 없습니다. 세션이 바뀌어도 URL이 동일하게 유지되도록 영구 개발 터널을 사용하세요.
# One-time setup:devtunnel create my-openclaw-bot --allow-anonymousdevtunnel port create my-openclaw-bot -p 3978 --protocol auto # Each dev session:devtunnel host my-openclaw-bot대안: ngrok http 3978 또는 tailscale funnel 3978(세션마다 URL이 바뀔 수 있음).
터널 URL이 변경되면 엔드포인트를 업데이트합니다.
teams app update <teamsAppId> --endpoint "https://<new-url>/api/messages"Bot 테스트
진단 실행:
teams app doctor <teamsAppId>Bot 등록, AAD 앱, 매니페스트, SSO 구성을 한 번에 확인합니다.
테스트 메시지 보내기:
- Teams 앱을 설치합니다(
teams app get <id> --install-link의 설치 링크 사용). - Teams에서 Bot을 찾아 DM을 보냅니다.
- 들어오는 활동이 있는지 Gateway 로그를 확인합니다.
환경 변수
대신 모든 구성 키를 환경 변수로 설정할 수 있습니다.
MSTEAMS_APP_IDMSTEAMS_APP_PASSWORDMSTEAMS_TENANT_IDMSTEAMS_AUTH_TYPE(선택 사항:"secret"또는"federated")MSTEAMS_CERTIFICATE_PATH(페더레이션 + 인증서)MSTEAMS_CERTIFICATE_THUMBPRINT(선택 사항, 인증에는 필요하지 않음)MSTEAMS_USE_MANAGED_IDENTITY(페더레이션 + 관리 ID)MSTEAMS_MANAGED_IDENTITY_CLIENT_ID(사용자 할당 MI에만 해당)
구성원 정보 액션
OpenClaw는 Microsoft Teams용 Graph 기반 member-info 액션을 제공하므로 에이전트와 자동화가 Microsoft Graph에서 직접 채널 구성원 세부 정보(표시 이름, 이메일, 역할)를 확인할 수 있습니다.
요구 사항:
Member.Read.GroupRSC 권한(권장 매니페스트에 이미 포함됨)- 팀 간 조회의 경우: 관리자 동의가 있는
User.Read.AllGraph Application 권한
이 액션은 channels.msteams.actions.memberInfo로 제어됩니다(기본값: Graph 자격 증명을 사용할 수 있을 때 활성화).
기록 컨텍스트
channels.msteams.historyLimit는 최근 채널/그룹 메시지를 몇 개까지 프롬프트에 감쌀지 제어합니다.messages.groupChat.historyLimit로 폴백합니다. 비활성화하려면0으로 설정합니다(기본값 50).- 가져온 스레드 기록은 보낸 사람 허용 목록(
allowFrom/groupAllowFrom)으로 필터링되므로, 스레드 컨텍스트 시딩에는 허용된 보낸 사람의 메시지만 포함됩니다. - 인용된 첨부 파일 컨텍스트(Teams 답장 HTML에서 파생된
ReplyTo*)는 현재 수신된 그대로 전달됩니다. - 즉, 허용 목록은 에이전트를 트리거할 수 있는 사람을 제어하며, 현재는 특정 보조 컨텍스트 경로만 필터링됩니다.
- DM 기록은
channels.msteams.dmHistoryLimit(사용자 턴)로 제한할 수 있습니다. 사용자별 재정의:channels.msteams.dms["<user_id>"].historyLimit.
현재 Teams RSC 권한(매니페스트)
다음은 Teams 앱 매니페스트의 기존 resourceSpecific 권한입니다. 앱이 설치된 팀/채팅 내에서만 적용됩니다.
채널의 경우(팀 범위):
ChannelMessage.Read.Group(Application) - @멘션 없이 모든 채널 메시지 수신ChannelMessage.Send.Group(Application)Member.Read.Group(Application)Owner.Read.Group(Application)ChannelSettings.Read.Group(Application)TeamMember.Read.Group(Application)TeamSettings.Read.Group(Application)
그룹 채팅의 경우:
ChatMessage.Read.Chat(Application) - @멘션 없이 모든 그룹 채팅 메시지 수신
Teams CLI를 통해 RSC 권한을 추가하려면:
teams app rsc add <teamsAppId> ChannelMessage.Read.Group --type ApplicationTeams 매니페스트 예시(수정됨)
필수 필드가 포함된 최소한의 유효한 예시입니다. ID와 URL을 바꾸세요.
{ $schema: "https://developer.microsoft.com/en-us/json-schemas/teams/v1.23/MicrosoftTeams.schema.json", manifestVersion: "1.23", version: "1.0.0", id: "00000000-0000-0000-0000-000000000000", name: { short: "OpenClaw" }, developer: { name: "Your Org", websiteUrl: "https://example.com", privacyUrl: "https://example.com/privacy", termsOfUseUrl: "https://example.com/terms", }, description: { short: "OpenClaw in Teams", full: "OpenClaw in Teams" }, icons: { outline: "outline.png", color: "color.png" }, accentColor: "#5B6DEF", bots: [ { botId: "11111111-1111-1111-1111-111111111111", scopes: ["personal", "team", "groupChat"], isNotificationOnly: false, supportsCalling: false, supportsVideo: false, supportsFiles: true, }, ], webApplicationInfo: { id: "11111111-1111-1111-1111-111111111111", }, authorization: { permissions: { resourceSpecific: [ { name: "ChannelMessage.Read.Group", type: "Application" }, { name: "ChannelMessage.Send.Group", type: "Application" }, { name: "Member.Read.Group", type: "Application" }, { name: "Owner.Read.Group", type: "Application" }, { name: "ChannelSettings.Read.Group", type: "Application" }, { name: "TeamMember.Read.Group", type: "Application" }, { name: "TeamSettings.Read.Group", type: "Application" }, { name: "ChatMessage.Read.Chat", type: "Application" }, ], }, },}매니페스트 주의 사항(필수 필드)
bots[].botId는 Azure Bot App ID와 일치해야 합니다.webApplicationInfo.id는 Azure Bot App ID와 일치해야 합니다.bots[].scopes에는 사용할 표면(personal,team,groupChat)이 포함되어야 합니다.- 개인 범위에서 파일 처리를 하려면
bots[].supportsFiles: true가 필요합니다. - 채널 트래픽을 원한다면
authorization.permissions.resourceSpecific에 채널 읽기/보내기가 포함되어야 합니다.
기존 앱 업데이트
이미 설치된 Teams 앱을 업데이트하려면(예: RSC 권한 추가):
# Download, edit, and re-upload the manifestteams app manifest download <teamsAppId> manifest.json# Edit manifest.json locally...teams app manifest upload manifest.json <teamsAppId># Version is auto-bumped if content changed업데이트 후 새 권한이 적용되도록 각 팀에서 앱을 다시 설치하고, 캐시된 앱 메타데이터를 지우기 위해 Teams를 완전히 종료했다가 다시 실행하세요(창만 닫는 것이 아님).
수동 매니페스트 업데이트(CLI 없이)
- 새 설정으로
manifest.json을 업데이트합니다. version필드를 증가시킵니다(예:1.0.0→1.1.0).- 아이콘(
manifest.json,outline.png,color.png)과 함께 매니페스트를 다시 zip으로 압축합니다. - 새 zip을 업로드합니다.
- Teams Admin Center: Teams 앱 → 앱 관리 → 앱 찾기 → 새 버전 업로드
- 사이드로드: Teams에서 → 앱 → 앱 관리 → 사용자 지정 앱 업로드
기능: RSC 전용과 Graph 비교
Teams RSC 전용 사용(앱 설치됨, Graph API 권한 없음)
작동:
- 채널 메시지 텍스트 콘텐츠 읽기.
- 채널 메시지 텍스트 콘텐츠 보내기.
- 개인(DM) 파일 첨부 수신.
작동하지 않음:
- 채널/그룹 이미지 또는 파일 콘텐츠(페이로드에는 HTML 스텁만 포함됨).
- SharePoint/OneDrive에 저장된 첨부 파일 다운로드.
- 메시지 기록 읽기(실시간 Webhook 이벤트 외).
Teams RSC + Microsoft Graph Application 권한 사용
추가:
- 호스팅된 콘텐츠 다운로드(메시지에 붙여넣은 이미지).
- SharePoint/OneDrive에 저장된 파일 첨부 다운로드.
- Graph를 통해 채널/채팅 메시지 기록 읽기.
RSC와 Graph API 비교
| 기능 | RSC 권한 | Graph API |
|---|---|---|
| 실시간 메시지 | 예(Webhook 경유) | 아니요(폴링만 가능) |
| 과거 메시지 | 아니요 | 예(기록 조회 가능) |
| 설정 복잡도 | 앱 매니페스트만 | 관리자 동의 + 토큰 흐름 필요 |
| 오프라인 작동 | 아니요(실행 중이어야 함) | 예(언제든지 조회 가능) |
핵심: RSC는 실시간 수신용이고, Graph API는 과거 접근용입니다. 오프라인 동안 놓친 메시지를 따라잡으려면 관리자 동의가 필요한 ChannelMessage.Read.All이 포함된 Graph API가 필요합니다.
Graph 지원 미디어 + 기록(채널에 필요)
채널에서 이미지/파일이 필요하거나 메시지 기록을 가져오려면 Microsoft Graph 권한을 활성화하고 관리자 동의를 부여해야 합니다.
- Entra ID(Azure AD) 앱 등록에서 Microsoft Graph Application 권한을 추가합니다.
ChannelMessage.Read.All(채널 첨부 파일 + 기록)Chat.Read.All또는ChatMessage.Read.All(그룹 채팅)
- 테넌트에 대해 관리자 동의를 부여합니다.
- Teams 앱 매니페스트 버전을 올리고 다시 업로드한 뒤, Teams에서 앱을 다시 설치합니다.
- 캐시된 앱 메타데이터를 지우기 위해 Teams를 완전히 종료했다가 다시 실행합니다.
사용자 멘션을 위한 추가 권한: 대화에 있는 사용자에 대한 사용자 @멘션은 기본적으로 작동합니다. 하지만 현재 대화에 없는 사용자를 동적으로 검색하고 멘션하려면 User.Read.All(Application) 권한을 추가하고 관리자 동의를 부여하세요.
알려진 제한 사항
Webhook 시간 초과
Teams는 HTTP Webhook을 통해 메시지를 전달합니다. 처리가 너무 오래 걸리면(예: 느린 LLM 응답) 다음이 발생할 수 있습니다.
- Gateway 시간 초과
- Teams가 메시지를 다시 시도함(중복 발생)
- 답장 누락
OpenClaw는 빠르게 반환하고 선제적으로 답장을 보내는 방식으로 이를 처리하지만, 매우 느린 응답은 여전히 문제를 일으킬 수 있습니다.
Teams 클라우드 및 서비스 URL 지원
이 SDK 기반 Teams 경로는 Microsoft Teams 퍼블릭 클라우드에서 실시간 검증되었습니다.
인바운드 답장은 들어오는 Teams SDK 턴 컨텍스트를 사용합니다. 컨텍스트 밖의 선제적 작업(전송, 편집, 삭제, 카드, 투표, 파일 동의 메시지, 대기열에 들어간 장시간 실행 답장)은 저장된 대화 참조 serviceUrl을 사용합니다. 퍼블릭 클라우드는 기본적으로 Teams SDK 퍼블릭 클라우드 환경을 사용하며, 퍼블릭 Teams Connector 호스트의 저장된 참조를 허용합니다: https://smba.trafficmanager.net/.
퍼블릭 클라우드가 기본값입니다. 일반적인 퍼블릭 클라우드 봇에는 channels.msteams.cloud 또는 channels.msteams.serviceUrl을 설정할 필요가 없습니다.
비공개 Teams 클라우드의 경우 Microsoft가 해당 경계를 게시하면 cloud와 일치하는 선제적 경계를 설정하세요.
channels.msteams.cloud는 인증, JWT 검증, 토큰 서비스, Graph 범위에 사용할 Teams SDK 클라우드 프리셋을 선택합니다.channels.msteams.serviceUrl은 선제적 전송, 편집, 삭제, 카드, 투표, 파일 동의 메시지, 대기열에 들어간 장시간 실행 답장 전에 저장된 대화 참조를 검증하는 데 사용되는 Bot Connector 엔드포인트 경계를 선택합니다. USGov 및 DoD SDK 클라우드에는 필수입니다. China/21Vianet의 경우 OpenClaw는 SDKChina프리셋을 사용하며, Azure China Bot Framework 채널 호스트의 저장/구성된 서비스 URL만 허용합니다.
Microsoft는 Teams 선제적 메시징 문서의 대화 만들기 섹션에 전역 선제적 Bot Connector 엔드포인트를 게시합니다. 가능한 경우 들어오는 활동의 serviceUrl을 사용하세요. 전역 선제적 엔드포인트가 필요하면 Microsoft의 표를 사용하세요.
| Teams 환경 | OpenClaw 구성 | 선제적 serviceUrl |
|---|---|---|
| Public | cloud/serviceUrl 구성 필요 없음 | https://smba.trafficmanager.net/teams |
| GCC | serviceUrl 설정, 별도의 Teams SDK 클라우드 프리셋 없음 |
https://smba.infra.gcc.teams.microsoft.com/teams |
| GCC High | cloud: "USGov" + serviceUrl |
https://smba.infra.gov.teams.microsoft.us/teams |
| DoD | cloud: "USGovDoD" + serviceUrl |
https://smba.infra.dod.teams.microsoft.us/teams |
| China/21Vianet | cloud: "China" |
들어오는 활동의 serviceUrl 사용 |
Microsoft는 별도의 선제적 서비스 URL을 문서화하지만 Teams SDK는 별도의 GCC 클라우드 프리셋을 노출하지 않는 GCC 예시:
{ "channels": { "msteams": { "serviceUrl": "https://smba.infra.gcc.teams.microsoft.com/teams" } }}GCC High 예시:
{ "channels": { "msteams": { "cloud": "USGov", "serviceUrl": "https://smba.infra.gov.teams.microsoft.us/teams" } }}channels.msteams.serviceUrl은 지원되는 Microsoft Teams Bot Connector 호스트로 제한됩니다. 서비스 URL이 구성된 경우 OpenClaw는 선제적 전송, 편집, 삭제, 카드, 투표, 대기열에 들어간 장시간 실행 답장을 실행하기 전에 저장된 대화 serviceUrl이 같은 호스트를 사용하는지 확인합니다. 기본 퍼블릭 클라우드 구성에서는 저장된 대화가 퍼블릭 Teams Connector 호스트 밖을 가리키면 OpenClaw가 닫힌 상태로 실패합니다. 클라우드/서비스 URL 설정을 변경한 후에는 저장된 대화 참조가 최신 상태가 되도록 해당 대화에서 새 메시지를 받으세요.
China/21Vianet에는 Microsoft의 Teams 선제적 엔드포인트 표에 별도의 전역 선제적 smba URL이 없습니다. Teams SDK가 Azure China 인증, 토큰, JWT 엔드포인트를 사용하도록 cloud: "China"를 구성하세요. 그러면 선제적 전송에는 들어오는 China Teams 활동에서 얻은 저장된 대화 참조 또는 명시적으로 구성된 서비스 URL이 Azure China Bot Framework 채널 경계(*.botframework.azure.cn)에 있어야 합니다. Graph 기반 Teams 헬퍼는 OpenClaw가 Graph 요청을 Azure China Graph 엔드포인트로 라우팅할 때까지 현재 cloud: "China"에서 비활성화됩니다.
서식 지정
Teams markdown은 Slack 또는 Discord보다 더 제한적입니다.
- 기본 서식은 동작합니다: 굵게, 기울임,
code, 링크 - 복잡한 markdown(표, 중첩 목록)은 올바르게 렌더링되지 않을 수 있습니다
- 투표와 의미적 프레젠테이션 전송에는 Adaptive Cards가 지원됩니다(아래 참고)
구성
주요 설정(공유 채널 패턴은 /gateway/configuration 참고):
channels.msteams.enabled: 채널을 활성화/비활성화합니다.channels.msteams.appId,channels.msteams.appPassword,channels.msteams.tenantId: 봇 자격 증명입니다.channels.msteams.cloud: Teams SDK 클라우드 환경(Public,USGov,USGovDoD또는China; 기본값Public). USGov/DoD SDK 클라우드에는 이를serviceUrl과 함께 설정하세요. China는 SDK 프리셋과 저장된 Azure China Bot Framework 대화 참조를 사용하며, Azure China Graph 라우팅이 구현될 때까지 Graph 기반 헬퍼가 비활성화됩니다.channels.msteams.serviceUrl: SDK 선제적 작업을 위한 Bot Connector 서비스 URL 경계입니다. 퍼블릭 클라우드는 SDK 기본값을 사용합니다. GCC(https://smba.infra.gcc.teams.microsoft.com/teams), GCC High 또는 DoD에는 이를 설정하세요. China는 저장된 대화 참조가 21Vianet에서 운영하는 Teams에서 온 경우 Azure China Bot Framework 채널 호스트를 허용합니다.channels.msteams.webhook.port(기본값3978)channels.msteams.webhook.path(기본값/api/messages)channels.msteams.dmPolicy:pairing | allowlist | open | disabled(기본값: pairing)channels.msteams.allowFrom: DM 허용 목록(AAD 객체 ID 권장). Graph 액세스가 가능하면 마법사가 설정 중 이름을 ID로 해석합니다.channels.msteams.dangerouslyAllowNameMatching: 변경 가능한 UPN/표시 이름 매칭과 직접 팀/채널 이름 라우팅을 다시 활성화하는 비상 토글입니다.channels.msteams.textChunkLimit: 아웃바운드 텍스트 청크 크기입니다.channels.msteams.chunkMode: 길이 기반 청킹 전에 빈 줄(문단 경계)에서 분할하려면length(기본값) 또는newline입니다.channels.msteams.mediaAllowHosts: 인바운드 첨부 파일 호스트 허용 목록입니다(기본값은 Microsoft/Teams 도메인).channels.msteams.mediaAuthAllowHosts: 미디어 재시도 시 Authorization 헤더를 첨부할 호스트 허용 목록입니다(기본값은 Graph + Bot Framework 호스트).channels.msteams.requireMention: 채널/그룹에서 @mention을 요구합니다(기본값 true).channels.msteams.replyStyle:thread | top-level(응답 스타일 참고).channels.msteams.teams.<teamId>.replyStyle: 팀별 재정의입니다.channels.msteams.teams.<teamId>.requireMention: 팀별 재정의입니다.channels.msteams.teams.<teamId>.tools: 채널 재정의가 없을 때 사용하는 팀별 기본 도구 정책 재정의(allow/deny/alsoAllow)입니다.channels.msteams.teams.<teamId>.toolsBySender: 팀별 발신자별 기본 도구 정책 재정의("*"와일드카드 지원)입니다.channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle: 채널별 재정의입니다.channels.msteams.teams.<teamId>.channels.<conversationId>.requireMention: 채널별 재정의입니다.channels.msteams.teams.<teamId>.channels.<conversationId>.tools: 채널별 도구 정책 재정의(allow/deny/alsoAllow)입니다.channels.msteams.teams.<teamId>.channels.<conversationId>.toolsBySender: 채널별 발신자별 도구 정책 재정의("*"와일드카드 지원)입니다.toolsBySender키는 명시적 접두사를 사용해야 합니다:channel:,id:,e164:,username:,name:(레거시 접두사 없는 키는 여전히id:에만 매핑됨).channels.msteams.actions.memberInfo: Graph 기반 멤버 정보 액션을 활성화하거나 비활성화합니다(기본값: Graph 자격 증명이 있으면 활성화).channels.msteams.authType: 인증 유형 -"secret"(기본값) 또는"federated".channels.msteams.certificatePath: PEM 인증서 파일 경로입니다(페더레이션 + 인증서 인증).channels.msteams.certificateThumbprint: 인증서 지문입니다(선택 사항, 인증에 필수 아님).channels.msteams.useManagedIdentity: 관리 ID 인증을 활성화합니다(페더레이션 모드).channels.msteams.managedIdentityClientId: 사용자 할당 관리 ID의 클라이언트 ID입니다.channels.msteams.sharePointSiteId: 그룹 채팅/채널의 파일 업로드에 사용할 SharePoint 사이트 ID입니다(그룹 채팅에서 파일 보내기 참고).
라우팅 및 세션
- 세션 키는 표준 에이전트 형식을 따릅니다(/concepts/session 참고):
- 직접 메시지는 메인 세션을 공유합니다(
agent:<agentId>:<mainKey>). - 채널/그룹 메시지는 대화 ID를 사용합니다:
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
- 직접 메시지는 메인 세션을 공유합니다(
응답 스타일: 스레드와 게시물
Teams는 최근 동일한 기본 데이터 모델 위에 두 가지 채널 UI 스타일을 도입했습니다.
| 스타일 | 설명 | 권장 replyStyle |
|---|---|---|
| 게시물(클래식) | 메시지가 카드로 표시되고 그 아래에 스레드 답장이 표시됩니다 | thread(기본값) |
| 스레드(Slack 유사) | 메시지가 Slack에 더 가깝게 선형으로 흐릅니다 | top-level |
문제: Teams API는 채널이 어떤 UI 스타일을 사용하는지 노출하지 않습니다. 잘못된 replyStyle을 사용하면:
- Threads 스타일 채널의
thread→ 답장이 어색하게 중첩되어 표시됩니다 - Posts 스타일 채널의
top-level→ 답장이 스레드 안이 아니라 별도의 최상위 게시물로 표시됩니다
해결 방법: 채널 설정 방식에 따라 채널별로 replyStyle을 구성하세요.
{ channels: { msteams: { replyStyle: "thread", teams: { "19:abc...@thread.tacv2": { channels: { "19:xyz...@thread.tacv2": { replyStyle: "top-level", }, }, }, }, }, },}해석 우선순위
봇이 채널에 답장을 보낼 때 replyStyle은 가장 구체적인 재정의부터 기본값까지 순서대로 해석됩니다. 첫 번째 non-undefined 값이 적용됩니다.
- 채널별 —
channels.msteams.teams.<teamId>.channels.<conversationId>.replyStyle - 팀별 —
channels.msteams.teams.<teamId>.replyStyle - 전역 —
channels.msteams.replyStyle - 암시적 기본값 —
requireMention에서 파생됨:requireMention: true→threadrequireMention: false→top-level
명시적 replyStyle 없이 전역으로 requireMention: false를 설정하면, 인바운드가 스레드 답장이었더라도 Posts 스타일 채널의 멘션이 최상위 게시물로 표시됩니다. 예기치 않은 동작을 피하려면 전역, 팀 또는 채널 수준에 replyStyle: "thread"를 고정하세요.
스레드 컨텍스트 보존
replyStyle: "thread"가 적용되고 봇이 채널 스레드 안에서 @mentioned된 경우, OpenClaw는 원래 스레드 루트를 아웃바운드 대화 참조(19:…@thread.tacv2;messageid=<root>)에 다시 연결하여 답장이 같은 스레드 안에 도착하도록 합니다. 이는 실시간(턴 내) 전송과 Bot Framework 턴 컨텍스트가 만료된 후 수행되는 선제적 전송(예: 장시간 실행 에이전트, mcp__openclaw__message를 통한 대기열의 도구 호출 답장) 모두에 적용됩니다.
스레드 루트는 대화 참조에 저장된 threadId에서 가져옵니다. threadId보다 이전의 오래된 저장 참조는 activityId(대화를 마지막으로 시드한 인바운드 활동)로 폴백하므로, 기존 배포는 다시 시드하지 않아도 계속 동작합니다.
replyStyle: "top-level"이 적용 중이면 채널 스레드 인바운드는 의도적으로 새 최상위 게시물로 응답됩니다. 스레드 접미사는 붙지 않습니다. 이는 Threads 스타일 채널의 올바른 동작입니다. 스레드 답글을 예상했는데 최상위 게시물이 보인다면 해당 채널의 replyStyle 설정이 잘못된 것입니다.
첨부 파일 및 이미지
현재 제한 사항:
- DM: 이미지와 파일 첨부는 Teams 봇 파일 API를 통해 작동합니다.
- 채널/그룹: 첨부 파일은 M365 스토리지(SharePoint/OneDrive)에 저장됩니다. Webhook 페이로드에는 실제 파일 바이트가 아니라 HTML 스텁만 포함됩니다. 채널 첨부 파일을 다운로드하려면 Graph API 권한이 필요합니다.
- 명시적인 파일 우선 전송에는
media/filePath/path와 함께action=upload-file을 사용하세요. 선택적message는 함께 표시되는 텍스트/댓글이 되고,filename은 업로드된 이름을 재정의합니다.
Graph 권한이 없으면 이미지가 포함된 채널 메시지는 텍스트로만 수신됩니다(이미지 콘텐츠는 봇에서 접근할 수 없습니다).
기본적으로 OpenClaw는 Microsoft/Teams 호스트 이름에서만 미디어를 다운로드합니다. channels.msteams.mediaAllowHosts로 재정의하세요(모든 호스트를 허용하려면 ["*"] 사용).
Authorization 헤더는 channels.msteams.mediaAuthAllowHosts에 포함된 호스트에만 첨부됩니다(기본값은 Graph + Bot Framework 호스트). 이 목록은 엄격하게 유지하세요(멀티 테넌트 접미사는 피하세요).
그룹 채팅에서 파일 보내기
봇은 FileConsentCard 흐름(내장)을 사용하여 DM에서 파일을 보낼 수 있습니다. 그러나 그룹 채팅/채널에서 파일 보내기에는 추가 설정이 필요합니다.
| 컨텍스트 | 파일 전송 방식 | 필요한 설정 |
|---|---|---|
| DM | FileConsentCard → 사용자가 수락 → 봇이 업로드 | 기본으로 작동 |
| 그룹 채팅/채널 | SharePoint에 업로드 → 공유 링크 | sharePointSiteId + Graph 권한 필요 |
| 이미지(모든 컨텍스트) | Base64 인코딩 인라인 | 기본으로 작동 |
그룹 채팅에 SharePoint가 필요한 이유
봇에는 개인 OneDrive 드라이브가 없습니다(/me/drive Graph API 엔드포인트는 애플리케이션 ID에서 작동하지 않음). 그룹 채팅/채널에서 파일을 보내려면 봇이 SharePoint 사이트에 업로드하고 공유 링크를 생성합니다.
설정
-
Entra ID(Azure AD) → 앱 등록에서 Graph API 권한을 추가합니다.
Sites.ReadWrite.All(애플리케이션) - SharePoint에 파일 업로드Chat.Read.All(애플리케이션) - 선택 사항, 사용자별 공유 링크 활성화
-
테넌트에 대해 관리자 동의를 부여합니다.
-
SharePoint 사이트 ID를 가져옵니다.
bash # Via Graph Explorer or curl with a valid token:curl -H "Authorization: Bearer $TOKEN" \ "https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}" # Example: for a site at "contoso.sharepoint.com/sites/BotFiles"curl -H "Authorization: Bearer $TOKEN" \ "https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles" # Response includes: "id": "contoso.sharepoint.com,guid1,guid2" -
OpenClaw를 구성합니다.
json5 { channels: { msteams: { // ... other config ... sharePointSiteId: "contoso.sharepoint.com,guid1,guid2", }, },}
공유 동작
| 권한 | 공유 동작 |
|---|---|
Sites.ReadWrite.All만 |
조직 전체 공유 링크(조직 내 누구나 접근 가능) |
Sites.ReadWrite.All + Chat.Read.All |
사용자별 공유 링크(채팅 멤버만 접근 가능) |
사용자별 공유는 채팅 참가자만 파일에 접근할 수 있으므로 더 안전합니다. Chat.Read.All 권한이 없으면 봇은 조직 전체 공유로 폴백합니다.
폴백 동작
| 시나리오 | 결과 |
|---|---|
그룹 채팅 + 파일 + sharePointSiteId 구성됨 |
SharePoint에 업로드하고 공유 링크 전송 |
그룹 채팅 + 파일 + sharePointSiteId 없음 |
OneDrive 업로드 시도(실패할 수 있음), 텍스트만 전송 |
| 개인 채팅 + 파일 | FileConsentCard 흐름(SharePoint 없이 작동) |
| 모든 컨텍스트 + 이미지 | Base64 인코딩 인라인(SharePoint 없이 작동) |
파일 저장 위치
업로드된 파일은 구성된 SharePoint 사이트의 기본 문서 라이브러리 안에 있는 /OpenClawShared/ 폴더에 저장됩니다.
투표(Adaptive Cards)
OpenClaw는 Teams 투표를 Adaptive Cards로 보냅니다(네이티브 Teams 투표 API는 없습니다).
- CLI:
openclaw message poll --channel msteams --target conversation:<id> ... - 투표는 Gateway가
state/openclaw.sqlite아래의 OpenClaw Plugin 상태 SQLite에 기록합니다. - 기존
msteams-polls.json파일은 실행 중인 Plugin이 아니라openclaw doctor --fix가 가져옵니다. - 투표를 기록하려면 Gateway가 온라인 상태를 유지해야 합니다.
- 투표는 아직 결과 요약을 자동 게시하지 않으며, 지원되는 투표 결과 CLI도 아직 없습니다.
프레젠테이션 카드
message 도구, CLI 또는 일반 답장 전달을 사용하여 의미론적 프레젠테이션 페이로드를 Teams 사용자 또는 대화로 보냅니다. OpenClaw는 일반 프레젠테이션 계약에서 이를 Teams Adaptive Cards로 렌더링합니다.
presentation 매개변수는 의미론적 블록을 받습니다. presentation이 제공되면 메시지 텍스트는 선택 사항입니다. 버튼은 Adaptive Card 제출 또는 URL 작업으로 렌더링됩니다. 선택 메뉴는 아직 Teams 렌더러에서 네이티브가 아니므로 OpenClaw는 전달 전에 읽을 수 있는 텍스트로 다운그레이드합니다.
Agent 도구:
{ action: "send", channel: "msteams", target: "user:<id>", presentation: { title: "Hello", blocks: [{ type: "text", text: "Hello!" }], },}CLI:
openclaw message send --channel msteams \ --target "conversation:19:abc...@thread.tacv2" \ --presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello!"}]}'대상 형식 세부 정보는 아래 대상 형식을 참조하세요.
대상 형식
MSTeams 대상은 사용자와 대화를 구분하기 위해 접두사를 사용합니다.
| 대상 유형 | 형식 | 예시 |
|---|---|---|
| 사용자(ID 기준) | user:<aad-object-id> |
user:40a1a0ed-4ff2-4164-a219-55518990c197 |
| 사용자(이름 기준) | user:<display-name> |
user:John Smith(Graph API 필요) |
| 그룹/채널 | conversation:<conversation-id> |
conversation:19:abc123...@thread.tacv2 |
| 그룹/채널(원시) | <conversation-id> |
19:abc123...@thread.tacv2(@thread가 포함된 경우) |
CLI 예시:
# Send to a user by IDopenclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello" # Send to a user by display name (triggers Graph API lookup)openclaw message send --channel msteams --target "user:John Smith" --message "Hello" # Send to a group chat or channelopenclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello" # Send a presentation card to a conversationopenclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \ --presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello"}]}'Agent 도구 예시:
{ action: "send", channel: "msteams", target: "user:John Smith", message: "Hello!",}{ action: "send", channel: "msteams", target: "conversation:19:abc...@thread.tacv2", presentation: { title: "Hello", blocks: [{ type: "text", text: "Hello" }], },}사전 메시징
- 사전 메시지는 사용자가 상호작용한 후에만 가능합니다. 그 시점에 대화 참조를 저장하기 때문입니다.
dmPolicy와 허용 목록 게이팅은/gateway/configuration을 참조하세요.
팀 및 채널 ID(흔한 실수)
Teams URL의 groupId 쿼리 매개변수는 구성에 사용되는 팀 ID가 아닙니다. 대신 URL 경로에서 ID를 추출하세요.
팀 URL:
https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=... └────────────────────────────┘ Team conversation ID (URL-decode this)채널 URL:
https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=... └─────────────────────────┘ Channel ID (URL-decode this)구성의 경우:
- 팀 키 =
/team/뒤의 경로 세그먼트(URL 디코딩됨, 예:19:Bk4j...@thread.tacv2; 이전 테넌트는@thread.skype를 표시할 수 있으며 이 또한 유효함) - 채널 키 =
/channel/뒤의 경로 세그먼트(URL 디코딩됨) - OpenClaw 라우팅에서는
groupId쿼리 매개변수를 무시하세요. 이는 들어오는 Teams 활동에서 사용되는 Bot Framework 대화 ID가 아니라 Microsoft Entra 그룹 ID입니다.
비공개 채널
봇은 비공개 채널에서 제한적으로 지원됩니다.
| 기능 | 표준 채널 | 비공개 채널 |
|---|---|---|
| 봇 설치 | 예 | 제한적 |
| 실시간 메시지(Webhook) | 예 | 작동하지 않을 수 있음 |
| RSC 권한 | 예 | 다르게 동작할 수 있음 |
| @멘션 | 예 | 봇에 접근할 수 있는 경우 |
| Graph API 기록 | 예 | 예(권한 필요) |
비공개 채널이 작동하지 않을 때의 우회 방법:
- 봇 상호작용에는 표준 채널 사용
- DM 사용 - 사용자는 언제든지 봇에 직접 메시지를 보낼 수 있음
- 기록 접근에는 Graph API 사용(
ChannelMessage.Read.All필요)
문제 해결
일반적인 문제
- 채널에 이미지가 표시되지 않음: Graph 권한 또는 관리자 동의가 누락되었습니다. Teams 앱을 다시 설치하고 Teams를 완전히 종료한 뒤 다시 여세요.
- 채널에서 응답 없음: 기본적으로 멘션이 필요합니다.
channels.msteams.requireMention=false를 설정하거나 팀/채널별로 구성하세요. - 버전 불일치(Teams가 여전히 이전 매니페스트를 표시): 앱을 제거한 뒤 다시 추가하고 Teams를 완전히 종료해 새로고침하세요.
- Webhook에서 401 Unauthorized: Azure JWT 없이 수동 테스트할 때 예상되는 동작입니다. 엔드포인트는 도달 가능하지만 인증에 실패했다는 뜻입니다. 올바르게 테스트하려면 Azure Web Chat을 사용하세요.
매니페스트 업로드 오류
- "Icon file cannot be empty": 매니페스트가 0바이트 아이콘 파일을 참조합니다. 유효한 PNG 아이콘을 만드세요(
outline.png는 32x32,color.png는 192x192). - "webApplicationInfo.Id already in use": 앱이 아직 다른 팀/채팅에 설치되어 있습니다. 먼저 찾아서 제거하거나 전파를 위해 5~10분 기다리세요.
- 업로드 시 "Something went wrong": 대신 https://admin.teams.microsoft.com을 통해 업로드하고, 브라우저 DevTools(F12) → Network 탭을 열어 실제 오류에 대한 응답 본문을 확인하세요.
- 사이드로드 실패: "Upload a custom app" 대신 "Upload an app to your org's app catalog"를 시도하세요. 이 방법은 사이드로드 제한을 우회하는 경우가 많습니다.
RSC 권한이 작동하지 않음
webApplicationInfo.id가 봇의 App ID와 정확히 일치하는지 확인합니다- 앱을 다시 업로드하고 팀/채팅에 다시 설치합니다
- 조직 관리자가 RSC 권한을 차단했는지 확인합니다
- 올바른 범위를 사용 중인지 확인합니다: 팀에는
ChannelMessage.Read.Group, 그룹 채팅에는ChatMessage.Read.Chat
참고 자료
- Azure Bot 만들기 - Azure Bot 설정 가이드
- Teams 개발자 포털 - Teams 앱 생성/관리
- Teams 앱 매니페스트 스키마
- RSC로 채널 메시지 받기
- RSC 권한 참조
- Teams 봇 파일 처리 (채널/그룹에는 Graph 필요)
- 사전 대응 메시징
- @microsoft/teams.cli - 봇 관리를 위한 Teams CLI