Microsoft Teams 플러그인 설정하기
Microsoft Teams (플러그인)
섹션 제목: “Microsoft Teams (플러그인)”“여기에 들어오는 자, 모든 희망을 버려라.”
업데이트: 2026-01-21
상태: 텍스트와 DM 첨부파일이 지원돼요. 채널이나 그룹에서 파일을 보내려면 sharePointSiteId와 Graph 권한이 필요해요 (그룹 채팅에서 파일 보내기 참고). 설문조사는 Adaptive Cards를 통해 전송돼요. 메시지 액션은 파일 우선 전송을 위해 명시적인 upload-file을 노출해요.
플러그인 설치 필수
섹션 제목: “플러그인 설치 필수”Microsoft Teams는 플러그인 형태로 제공되며 코어 설치 패키지에 포함되어 있지 않아요.
변경 사항 (2026.1.15): Microsoft Teams가 코어에서 분리되었어요. Teams를 사용하려면 반드시 플러그인을 설치해야 해요.
이유: 코어 설치 용량을 가볍게 유지하고, Microsoft Teams 의존성만 독립적으로 업데이트하기 위해서예요.
CLI를 통한 설치 (npm registry):
openclaw plugins install @openclaw/msteams로컬 체크아웃 (git 저장소에서 실행 중인 경우):
openclaw plugins install ./path/to/local/msteams-plugin설정 과정에서 Teams를 선택하고 git 체크아웃이 감지되면, OpenClaw가 로컬 설치 경로를 자동으로 제안해 줄 거예요.
상세 내용: Plugins
빠른 설정 (초보자용)
섹션 제목: “빠른 설정 (초보자용)”- Microsoft Teams 플러그인을 설치하세요.
- Azure Bot을 생성하세요 (App ID + client secret + tenant ID).
- 해당 자격 증명을 사용해 OpenClaw를 설정하세요.
- 공용 URL이나 터널을 통해
/api/messages(기본 포트 3978)를 외부로 노출하세요. - Teams 앱 패키지를 설치하고 Gateway를 시작하세요.
최소 설정 예시:
{ channels: { msteams: { enabled: true, appId: "<APP_ID>", appPassword: "<APP_PASSWORD>", tenantId: "<TENANT_ID>", webhook: { port: 3978, path: "/api/messages" }, }, },}참고: 그룹 채팅은 기본적으로 차단되어 있어요 (channels.msteams.groupPolicy: "allowlist"). 그룹 답장을 허용하려면 channels.msteams.groupAllowFrom을 설정하세요 (또는 groupPolicy: "open"으로 설정하여 멘션된 모든 멤버를 허용할 수 있어요).
- Teams DM, 그룹 채팅 또는 채널을 통해 OpenClaw와 대화해요.
- 라우팅을 결정론적으로 유지해요. 답장은 항상 메시지가 처음 도착했던 채널로 다시 전달돼요.
- 안전한 채널 동작을 기본으로 해요. 별도로 설정하지 않는 한 멘션이 필요해요.
설정 쓰기
섹션 제목: “설정 쓰기”기본적으로 Microsoft Teams는 /config set|unset 명령어로 실행되는 설정 업데이트를 작성할 수 있도록 허용되어 있어요 (commands.config: true 설정 필요).
이 기능을 끄려면 다음과 같이 설정하세요:
{ channels: { msteams: { configWrites: false } },}액세스 제어 (DM + 그룹)
섹션 제목: “액세스 제어 (DM + 그룹)”DM 액세스
- 기본값은
channels.msteams.dmPolicy = "pairing"이에요. 승인 전까지는 모르는 발신자가 보낸 메시지는 무시됩니다. channels.msteams.allowFrom에는 고정된 AAD object IDs를 사용하는 것이 좋습니다.- UPN이나 표시 이름은 변경될 수 있기 때문에, 이름 매칭 기능은 기본적으로 비활성화되어 있어요.
channels.msteams.dangerouslyAllowNameMatching: true를 설정해야만 사용할 수 있습니다. - 설정 위저드(wizard)를 사용하면 권한이 있는 경우 Microsoft Graph를 통해 이름을 ID로 변환할 수 있어요.
그룹 액세스
- 기본값은
channels.msteams.groupPolicy = "allowlist"입니다.groupAllowFrom에 추가하지 않으면 차단돼요. 설정값이 없을 때 기본 동작을 바꾸려면channels.defaults.groupPolicy를 사용하세요. channels.msteams.groupAllowFrom은 그룹 채팅이나 채널에서 어떤 발신자가 트리거를 발생시킬 수 있는지 제어합니다 (설정이 없으면channels.msteams.allowFrom설정을 따릅니다).- 모든 멤버를 허용하려면
groupPolicy: "open"으로 설정하세요 (이 경우에도 기본적으로 멘션이 필요합니다). - 모든 채널을 허용하지 않으려면
channels.msteams.groupPolicy: "disabled"로 설정하면 돼요.
Example:
{ channels: { msteams: { groupPolicy: "allowlist", groupAllowFrom: ["user@org.com"], }, },}Teams + 채널 allowlist
channels.msteams.teams아래에 팀과 채널을 나열해서 응답 범위를 제한할 수 있어요.- Key 값은 고정된 team IDs와 channel conversation IDs를 사용해야 합니다.
groupPolicy="allowlist"이고 팀 allowlist가 설정되어 있다면, 목록에 있는 팀과 채널만 허용됩니다 (멘션 필요).- 설정 위저드에서
Team/Channel항목을 입력하면 자동으로 저장해 줍니다. - OpenClaw는 시작할 때 (Graph 권한이 있다면) 팀/채널 및 사용자 allowlist 이름을 ID로 변환하고 매핑 정보를 로그에 기록해요. 변환되지 않은 팀/채널 이름은 입력한 대로 유지되지만,
channels.msteams.dangerouslyAllowNameMatching: true가 활성화되어 있지 않으면 기본적으로 라우팅에서 무시됩니다.
Example:
{ channels: { msteams: { groupPolicy: "allowlist", teams: { "My Team": { channels: { General: { requireMention: true }, }, }, }, }, },}작동 방식
섹션 제목: “작동 방식”- Microsoft Teams 플러그인을 설치하세요.
- Azure Bot을 생성합니다 (App ID + secret + tenant ID).
- 봇을 참조하고 아래의 RSC 권한을 포함하는 Teams app package를 만드세요.
- Teams 앱을 팀에 업로드하거나 설치하세요 (DM의 경우 개인 범위).
~/.openclaw/openclaw.json(또는 환경 변수)에서msteams를 설정하고 Gateway를 시작합니다.- Gateway는 기본적으로
/api/messages경로에서 Bot Framework webhook 트래픽을 대기해요.
Azure Bot 설정 (사전 요구 사항)
섹션 제목: “Azure Bot 설정 (사전 요구 사항)”OpenClaw를 설정하기 전에 Azure Bot 리소스를 먼저 생성해야 합니다.
1단계: Azure Bot 생성
섹션 제목: “1단계: Azure Bot 생성”-
Azure Bot 만들기 페이지로 이동하세요.
-
기본 사항(Basics) 탭을 작성합니다.
Field Value Bot handle 봇 이름, 예: openclaw-msteams(고유해야 함)Subscription Azure 구독 선택 Resource group 새로 만들거나 기존 그룹 사용 Pricing tier 개발/테스트용은 Free 선택 Type of App Single Tenant (권장 - 아래 참고 사항 확인) Creation type Create new Microsoft App ID
지원 중단 공지: 2025년 7월 31일 이후로 새로운 multi-tenant 봇 생성 기능이 지원 중단되었습니다. 새로운 봇에는 Single Tenant를 사용하세요.
- 검토 + 만들기 → 만들기를 클릭하세요 (1~2분 정도 소요됩니다).
2단계: 자격 증명(Credentials) 가져오기
섹션 제목: “2단계: 자격 증명(Credentials) 가져오기”- Azure Bot 리소스의 구성(Configuration) 메뉴로 이동하세요.
- Microsoft App ID를 복사하세요. 이 값이
appId가 됩니다. - **암호 관리(Manage Password)**를 클릭하여 앱 등록 페이지로 이동합니다.
- 인증서 및 암호(Certificates & secrets) → **새 클라이언트 암호(New client secret)**를 클릭하고 **값(Value)**을 복사하세요. 이 값이
appPassword입니다. - **개요(Overview)**로 이동해서 **디렉터리(테넌트) ID(Directory (tenant) ID)**를 복사하세요. 이 값이
tenantId입니다.
3단계: 메시징 엔드포인트 설정
섹션 제목: “3단계: 메시징 엔드포인트 설정”- Azure Bot의 구성(Configuration) 메뉴로 이동하세요.
- **메시징 엔드포인트(Messaging endpoint)**를 여러분의 webhook URL로 설정합니다.
- 프로덕션:
https://your-domain.com/api/messages - 로컬 개발: 터널을 사용하세요 (아래 로컬 개발 섹션 참고).
- 프로덕션:
4단계: Teams 채널 활성화
섹션 제목: “4단계: Teams 채널 활성화”- Azure Bot의 채널(Channels) 메뉴로 이동하세요.
- Microsoft Teams를 클릭하고 구성한 뒤 저장하세요.
- 서비스 약관에 동의합니다.
로컬 개발 (터널링)
섹션 제목: “로컬 개발 (터널링)”Teams는 localhost에 직접 연결할 수 없어요. 로컬 개발 시에는 터널을 사용해야 합니다.
방법 A: ngrok
ngrok http 3978# Copy the https URL, e.g., https://abc123.ngrok.io# Set messaging endpoint to: https://abc123.ngrok.io/api/messages방법 B: Tailscale Funnel
tailscale funnel 3978# Use your Tailscale funnel URL as the messaging endpointTeams Developer Portal (대안)
섹션 제목: “Teams Developer Portal (대안)”매니페스트 ZIP 파일을 직접 만드는 대신 Teams Developer Portal을 사용할 수 있어요.
- + New app을 클릭하세요.
- 기본 정보(이름, 설명, 개발자 정보)를 입력하세요.
- App features → Bot으로 이동하세요.
- Enter a bot ID manually를 선택하고 Azure Bot App ID를 붙여넣으세요.
- Scopes를 확인하세요: Personal, Team, Group Chat.
- Distribute → Download app package를 클릭하세요.
- Teams에서: Apps → Manage your apps → Upload a custom app → ZIP 파일을 선택하세요.
JSON 매니페스트를 직접 수정하는 것보다 이 방법이 훨씬 편할 거예요.
봇 테스트하기
섹션 제목: “봇 테스트하기”옵션 A: Azure Web Chat (웹훅 먼저 확인)
- Azure Portal → Azure Bot 리소스 → Test in Web Chat으로 이동하세요.
- 메시지를 보내보세요. 응답이 와야 합니다.
- Teams 설정을 하기 전에 웹훅 Endpoint가 잘 작동하는지 확인할 수 있어요.
옵션 B: Teams (앱 설치 후)
- Teams 앱을 설치하세요 (사이드로드 또는 조직 카탈로그 이용).
- Teams에서 봇을 찾아 DM을 보내보세요.
- Gateway 로그에 인입되는 활동이 있는지 확인하세요.
설정 (텍스트 전용)
섹션 제목: “설정 (텍스트 전용)”-
Microsoft Teams 플러그인 설치
- npm 이용:
openclaw plugins install @openclaw/msteams - 로컬 체크아웃 이용:
openclaw plugins install ./path/to/local/msteams-plugin
- npm 이용:
-
봇 등록
- Azure Bot을 생성하고(위 내용 참고) 다음 정보를 챙겨두세요:
- App ID
- Client secret (App password)
- Tenant ID (single-tenant)
- Azure Bot을 생성하고(위 내용 참고) 다음 정보를 챙겨두세요:
-
Teams 앱 매니페스트
botId = <App ID>가 포함된bot항목을 넣으세요.- Scopes:
personal,team,groupChat. supportsFiles: true(Personal scope에서 파일 처리를 위해 필요해요).- RSC 권한을 추가하세요 (아래 참고).
- 아이콘을 만드세요:
outline.png(32x32) 및color.png(192x192). manifest.json,outline.png,color.png세 파일을 하나로 압축하세요.
-
OpenClaw 설정
{channels: {msteams: {enabled: true,appId: "<APP_ID>",appPassword: "<APP_PASSWORD>",tenantId: "<TENANT_ID>",webhook: { port: 3978, path: "/api/messages" },},},}설정 키 대신 환경 변수를 사용할 수도 있어요:
MSTEAMS_APP_IDMSTEAMS_APP_PASSWORDMSTEAMS_TENANT_ID
-
봇 Endpoint
- Azure Bot Messaging Endpoint를 다음과 같이 설정하세요:
https://<host>:3978/api/messages(또는 설정한 경로/포트).
- Azure Bot Messaging Endpoint를 다음과 같이 설정하세요:
-
Gateway 실행
- 플러그인이 설치되어 있고 인증 정보가 포함된
msteams설정이 있으면 Teams 채널이 자동으로 시작돼요.
- 플러그인이 설치되어 있고 인증 정보가 포함된
멤버 정보 액션
섹션 제목: “멤버 정보 액션”OpenClaw는 Microsoft Teams를 위한 Graph 기반 member-info 액션을 제공해요. 이 기능을 통해 에이전트나 자동화 도구가 Microsoft Graph에서 직접 채널 멤버의 상세 정보(표시 이름, 이메일, 역할)를 가져올 수 있어요.
요구 사항:
Member.Read.GroupRSC 권한 (추천 매니페스트에 이미 포함되어 있어요)- 교차 팀(cross-team) 조회 시: 관리자 동의가 완료된
User.Read.AllGraph Application 권한
이 액션은 channels.msteams.actions.memberInfo 설정으로 제어할 수 있어요 (Graph 인증 정보가 있으면 기본적으로 활성화돼요).
개발을 하다 보면 이전 대화 맥락을 파악하는 게 정말 중요하죠. Teams에서 히스토리를 어떻게 관리하고 권한을 설정하는지 정리해 드릴게요.
히스토리 컨텍스트 (History context)
섹션 제목: “히스토리 컨텍스트 (History context)”channels.msteams.historyLimit설정은 프롬프트에 포함할 최근 채널 및 그룹 메시지 개수를 조절해요.- 설정값이 없으면
messages.groupChat.historyLimit를 대신 사용해요.0으로 설정하면 비활성화되고, 기본값은 50이에요. - 가져온 스레드 히스토리는 발신자 허용 리스트(
allowFrom/groupAllowFrom)에 따라 필터링돼요. 즉, 허용된 발신자가 보낸 메시지만 스레드 컨텍스트에 포함된다는 점을 기억하세요. - DM 히스토리는
channels.msteams.dmHistoryLimit로 제한할 수 있어요. 사용자별로 다르게 설정하고 싶다면channels.msteams.dms["<user_id>"].historyLimit를 사용하면 돼요.
현재 Teams RSC 권한 (Manifest)
섹션 제목: “현재 Teams RSC 권한 (Manifest)”Teams 앱 manifest에 정의된 기존 resourceSpecific 권한들이에요. 이 권한들은 앱이 설치된 팀이나 채팅 내부에서만 적용돼요.
채널(팀 스코프)용:
ChannelMessage.Read.Group(Application) - @mention 없이도 모든 채널 메시지 수신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) - @mention 없이도 모든 그룹 채팅 메시지 수신
Teams Manifest 예시 (편집됨)
섹션 제목: “Teams Manifest 예시 (편집됨)”필요한 필드가 포함된 유효한 최소 예시예요. 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" }, ], }, },}Manifest 주의사항 (필수 필드)
섹션 제목: “Manifest 주의사항 (필수 필드)”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 권한을 추가하는 등 업데이트가 필요할 때는 이렇게 하세요.
manifest.json파일을 새로운 설정으로 업데이트해요.version필드 값을 올려주세요 (예:1.0.0→1.1.0).- manifest와 아이콘 파일들(
manifest.json,outline.png,color.png)을 **다시 압축(zip)**해요. - 새로운 zip 파일을 업로드해요.
- 방법 A (Teams 관리 센터): Teams 관리 센터 → Teams 앱 → 앱 관리 → 앱 찾기 → 새 버전 업로드
- 방법 B (사이드로드): Teams 내에서 → 앱 → 앱 관리 → 사용자 지정 앱 업로드
- 팀 채널의 경우: 새로운 권한이 적용되도록 각 팀에 앱을 다시 설치하세요.
- 캐시된 앱 메타데이터를 지우기 위해 Teams를 완전히 종료한 후 다시 실행하세요 (창만 닫으면 안 돼요).
기능 비교: RSC 전용 vs Graph
섹션 제목: “기능 비교: RSC 전용 vs Graph”Teams RSC만 사용하는 경우 (앱 설치됨, Graph API 권한 없음)
섹션 제목: “Teams RSC만 사용하는 경우 (앱 설치됨, Graph API 권한 없음)”가능한 작업:
- 채널 메시지의 텍스트 내용 읽기
- 채널 메시지 텍스트 내용 전송
- 개인 DM 파일 첨부 수신
불가능한 작업:
- 채널/그룹의 이미지나 파일 내용 확인 (HTML 스텁만 포함된 페이로드가 전달돼요)
- SharePoint/OneDrive에 저장된 첨부 파일 다운로드
- 실시간 webhook 이벤트 이외의 메시지 히스토리 읽기
Teams RSC와 Microsoft Graph Application 권한을 함께 사용하는 경우
섹션 제목: “Teams RSC와 Microsoft Graph Application 권한을 함께 사용하는 경우”추가되는 기능:
- 호스팅된 콘텐츠 다운로드 (메시지에 붙여넣은 이미지 등)
- SharePoint/OneDrive에 저장된 첨부 파일 다운로드
- Graph를 통해 채널/채팅 메시지 히스토리 읽기
RSC vs Graph API 비교
섹션 제목: “RSC vs Graph API 비교”| 기능 | RSC 권한 | Graph API |
|---|---|---|
| 실시간 메시지 | 가능 (webhook 방식) | 불가능 (폴링만 가능) |
| 과거 메시지 히스토리 | 불가능 | 가능 (히스토리 쿼리 가능) |
| 설정 복잡도 | 앱 manifest만 설정 | 관리자 동의 및 토큰 흐름 필요 |
| 오프라인 작동 | 불가능 (실행 중이어야 함) | 가능 (언제든 쿼리 가능) |
요약하자면: RSC는 실시간 리스닝을 위한 것이고, Graph API는 과거 데이터에 접근하기 위한 것이에요. 오프라인 상태일 때 놓친 메시지까지 파악하려면 ChannelMessage.Read.All 권한이 있는 Graph API가 필요해요.
다음 단계
섹션 제목: “다음 단계”Graph 활성화 미디어 + 히스토리 (채널 필수 사항)
섹션 제목: “Graph 활성화 미디어 + 히스토리 (채널 필수 사항)”채널에서 이미지나 파일을 사용하거나 메시지 히스토리를 가져오려면 Microsoft Graph 권한을 활성화하고 관리자 동의를 받아야 해요.
- Entra ID (Azure AD) App Registration에서 다음 Microsoft Graph Application permissions를 추가하세요.
ChannelMessage.Read.All(채널 첨부 파일 + 히스토리)Chat.Read.All또는ChatMessage.Read.All(그룹 채팅)
- 테넌트에 대해 **관리자 동의(Grant admin consent)**를 완료하세요.
- Teams 앱 manifest version을 올리고, 다시 업로드한 뒤 Teams에 앱을 재설치하세요.
- 캐시된 앱 메타데이터를 삭제하기 위해 Teams를 완전히 종료하고 다시 실행하세요.
사용자 멘션 관련 추가 권한: 대화에 참여 중인 사용자에 대한 @멘션은 기본적으로 작동해요. 하지만 현재 대화에 참여하지 않은 사용자를 동적으로 검색하고 멘션하고 싶다면 User.Read.All (Application) 권한을 추가하고 관리자 동의를 받으세요.
알려진 제한 사항
섹션 제목: “알려진 제한 사항”Webhook 타임아웃
섹션 제목: “Webhook 타임아웃”Teams는 HTTP webhook을 통해 메시지를 전달해요. 처리 시간이 너무 길어지면(예: LLM 응답 지연) 다음과 같은 문제가 발생할 수 있어요.
- Gateway 타임아웃
- Teams의 메시지 재시도 (중복 발생 원인)
- 응답 누락
OpenClaw는 빠르게 응답을 반환하고 나중에 응답을 보내는 방식으로 이를 처리하지만, 응답이 매우 느리면 여전히 문제가 생길 수 있어요.
포맷팅
섹션 제목: “포맷팅”Teams의 markdown은 Slack이나 Discord보다 제한적이에요.
- 기본 포맷팅은 잘 작동해요: 굵게, 기울임,
코드, 링크 - 복잡한 markdown(표, 중첩 리스트)은 제대로 렌더링되지 않을 수 있어요.
- 투표나 임의의 카드 전송을 위한 Adaptive Cards는 지원돼요 (아래 내용 참고).
주요 설정값이에요 (공유 채널 패턴은 /gateway/configuration을 참고하세요).
channels.msteams.enabled: 채널 활성화/비활성화.channels.msteams.appId,channels.msteams.appPassword,channels.msteams.tenantId: 봇 자격 증명.channels.msteams.webhook.port(기본값3978)channels.msteams.webhook.path(기본값/api/messages)channels.msteams.dmPolicy:pairing | allowlist | open | disabled(기본값: pairing)channels.msteams.allowFrom: DM 허용 목록 (AAD object 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: 채널/그룹에서 @멘션 필수 여부 (기본값 true).channels.msteams.replyStyle:thread | top-level(Reply Style 참고).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키는 명시적인 접두사를 사용해야 해요:id:,e164:,username:,name:(접두사가 없는 기존 키는id:로만 매핑돼요).channels.msteams.actions.memberInfo: Graph 기반 멤버 정보 액션 활성화/비활성화 (기본값: Graph 자격 증명이 있을 때 활성화).channels.msteams.sharePointSiteId: 그룹 채팅/채널의 파일 업로드를 위한 SharePoint 사이트 ID (Sending files in group chats 참고).
라우팅 및 세션
섹션 제목: “라우팅 및 세션”- 세션 키는 표준 에이전트 형식을 따라요 (/concepts/session 참고):
- 다이렉트 메시지(DM)는 메인 세션을 공유해요 (
agent:<agentId>:<mainKey>). - 채널/그룹 메시지는 conversation id를 사용해요:
agent:<agentId>:msteams:channel:<conversationId>agent:<agentId>:msteams:group:<conversationId>
- 다이렉트 메시지(DM)는 메인 세션을 공유해요 (
답변 스타일: Threads vs Posts
섹션 제목: “답변 스타일: Threads vs Posts”Teams는 최근 동일한 데이터 모델을 기반으로 두 가지 채널 UI 스타일을 도입했어요.
| 스타일 | 설명 | 권장 replyStyle |
|---|---|---|
| Posts (기존 방식) | 메시지가 카드 형태로 표시되며 아래에 스레드 답변이 달림 | thread (기본값) |
| Threads (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", }, }, }, }, }, },}첨부 파일 및 이미지
섹션 제목: “첨부 파일 및 이미지”현재 몇 가지 제한 사항이 있어요.
- 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 호스트). 이 목록은 엄격하게 관리하세요.
그룹 채팅에서 파일 전송하기
섹션 제목: “그룹 채팅에서 파일 전송하기”봇은 DM에서 기본 제공되는 FileConsentCard 흐름을 사용해 파일을 보낼 수 있어요. 하지만 그룹 채팅이나 채널에서 파일을 보내려면 추가 설정이 필요해요.
| 컨텍스트 | 파일 전송 방식 | 필요한 설정 |
|---|---|---|
| DM | FileConsentCard → 사용자 수락 → 봇 업로드 | 별도 설정 없이 작동 |
| 그룹 채팅/채널 | SharePoint에 업로드 → 공유 링크 전송 | sharePointSiteId + Graph 권한 필요 |
| 이미지 (모든 컨텍스트) | Base64 인코딩 인라인 | 별도 설정 없이 작동 |
그룹 채팅에 SharePoint가 필요한 이유
섹션 제목: “그룹 채팅에 SharePoint가 필요한 이유”봇은 개인 OneDrive 드라이브를 가지고 있지 않아요(애플리케이션 ID의 경우 /me/drive Graph API 엔드포인트가 작동하지 않아요). 그룹 채팅이나 채널에서 파일을 보내기 위해 봇은 SharePoint 사이트에 업로드하고 공유 링크를 생성해요.
설정 방법
섹션 제목: “설정 방법”-
Entra ID (Azure AD) → 앱 등록에서 Graph API 권한을 추가하세요.
Sites.ReadWrite.All(Application) - SharePoint에 파일 업로드Chat.Read.All(Application) - 선택 사항, 사용자별 공유 링크 활성화
-
테넌트에 대해 관리자 동의를 부여하세요.
-
SharePoint 사이트 ID를 가져오세요.
Terminal window # 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를 설정하세요.
{channels: {msteams: {// ... other config ...sharePointSiteId: "contoso.sharepoint.com,guid1,guid2",},},}
공유 동작
섹션 제목: “공유 동작”| 권한 | 공유 동작 |
|---|---|
Sites.ReadWrite.All만 있음 | 조직 전체 공유 링크 (조직 내 누구나 접근 가능) |
Sites.ReadWrite.All + Chat.Read.All | 사용자별 공유 링크 (채팅 멤버만 접근 가능) |
사용자별 공유는 채팅 참여자만 파일에 접근할 수 있어 더 안전해요. Chat.Read.All 권한이 없으면 봇은 조직 전체 공유 방식으로 전환해요.
폴백(Fallback) 동작
섹션 제목: “폴백(Fallback) 동작”| 시나리오 | 결과 |
|---|---|
그룹 채팅 + 파일 + sharePointSiteId 설정됨 | SharePoint에 업로드 후 공유 링크 전송 |
그룹 채팅 + 파일 + sharePointSiteId 설정 안 됨 | OneDrive 업로드 시도(실패 가능), 텍스트만 전송 |
| 개인 채팅 + 파일 | FileConsentCard 흐름 (SharePoint 없이 작동) |
| 모든 컨텍스트 + 이미지 | Base64 인코딩 인라인 (SharePoint 없이 작동) |
파일 저장 위치
섹션 제목: “파일 저장 위치”업로드된 파일은 설정된 SharePoint 사이트의 기본 문서 라이브러리에 있는 /OpenClawShared/ 폴더에 저장돼요.
투표 (Adaptive Cards)
섹션 제목: “투표 (Adaptive Cards)”OpenClaw는 Teams 투표를 Adaptive Cards로 전송해요(Teams 자체 투표 API는 없어요).
- CLI:
openclaw message poll --channel msteams --target conversation:<id> ... - 투표 결과는 Gateway의
~/.openclaw/msteams-polls.json에 기록돼요. - 투표를 기록하려면 Gateway가 온라인 상태를 유지해야 해요.
- 투표 결과 요약은 아직 자동으로 게시되지 않아요(필요한 경우 저장된 파일을 확인하세요).
Adaptive Cards (임의 형식)
섹션 제목: “Adaptive Cards (임의 형식)”message 툴이나 CLI를 사용해서 Teams 사용자나 대화방에 원하는 Adaptive Card JSON을 보낼 수 있어요.
card 파라미터는 Adaptive Card JSON 객체를 받아요. card가 포함된 경우, 메시지 텍스트는 선택 사항이에요.
Agent tool:
{ action: "send", channel: "msteams", target: "user:<id>", card: { type: "AdaptiveCard", version: "1.5", body: [{ type: "TextBlock", text: "Hello!" }], },}CLI:
openclaw message send --channel msteams \ --target "conversation:19:abc...@thread.tacv2" \ --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello!"}]}'카드 스키마와 예시는 Adaptive Cards documentation를 참고해 주세요. 대상 형식에 대한 자세한 내용은 아래 대상 형식 섹션에서 확인할 수 있어요.
대상 형식 (Target formats)
섹션 제목: “대상 형식 (Target formats)”MSTeams 대상은 사용자와 대화를 구분하기 위해 접두사(prefix)를 사용해요.
| 대상 유형 | 형식 | 예시 |
|---|---|---|
| 사용자 (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 |
| 그룹/채널 (Raw) | <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 an Adaptive Card to a conversationopenclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" \ --card '{"type":"AdaptiveCard","version":"1.5","body":[{"type":"TextBlock","text":"Hello"}]}'Agent tool 예시:
{ action: "send", channel: "msteams", target: "user:John Smith", message: "Hello!",}{ action: "send", channel: "msteams", target: "conversation:19:abc...@thread.tacv2", card: { type: "AdaptiveCard", version: "1.5", body: [{ type: "TextBlock", text: "Hello" }], },}참고: user: 접두사가 없으면 이름은 기본적으로 그룹이나 팀으로 인식돼요. 표시 이름으로 사람을 지정할 때는 항상 user:를 사용해 주세요.
선제적 메시지 전송 (Proactive messaging)
섹션 제목: “선제적 메시지 전송 (Proactive messaging)”선제적 메시지는 사용자가 먼저 상호작용을 한 이후에만 보낼 수 있어요. 그 시점에 대화 참조(conversation references)를 저장해 두기 때문이죠. dmPolicy나 allowlist 게이팅에 대한 자세한 내용은 /gateway/configuration 문서를 참고해 보세요.
팀 및 채널 ID (자주 하는 실수)
섹션 제목: “팀 및 채널 ID (자주 하는 실수)”Teams URL에 포함된 groupId 쿼리 파라미터는 설정을 위한 팀 ID가 아니에요. 이 부분에서 실수가 정말 많은데, ID는 URL 경로에서 직접 추출해야 합니다.
Team URL:
https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=... └────────────────────────────┘ Team ID (URL-decode this)Channel URL:
https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=... └─────────────────────────┘ Channel ID (URL-decode this)설정 시 주의사항:
- Team ID =
/team/뒤에 오는 경로 세그먼트 (URL-decoded, 예:19:Bk4j...@thread.tacv2) - Channel ID =
/channel/뒤에 오는 경로 세그먼트 (URL-decoded) groupId쿼리 파라미터는 무시하세요.
비공개 채널
섹션 제목: “비공개 채널”비공개 채널에서 봇은 제한적으로만 작동해요.
| 기능 | 표준 채널 | 비공개 채널 |
|---|---|---|
| 봇 설치 | 예 | 제한적 |
| 실시간 메시지 (webhook) | 예 | 작동하지 않을 수 있음 |
| RSC 권한 | 예 | 다르게 동작할 수 있음 |
| @mentions | 예 | 봇에 접근 가능한 경우 |
| Graph API 기록 | 예 | 예 (권한 필요) |
비공개 채널이 제대로 작동하지 않을 때의 해결 방법:
- 봇과 상호작용할 때는 표준 채널을 사용하세요.
- DM을 활용해 보세요. 사용자는 언제든 봇에게 직접 메시지를 보낼 수 있어요.
- 과거 기록에 접근하려면 Graph API를 사용하세요 (
ChannelMessage.Read.All권한이 필요해요).
문제 해결
섹션 제목: “문제 해결”일반적인 문제
섹션 제목: “일반적인 문제”- 채널에서 이미지가 보이지 않음: Graph 권한이나 관리자 동의가 누락된 경우예요. Teams 앱을 다시 설치하고 Teams를 완전히 종료했다가 다시 열어보세요.
- 채널에서 응답이 없음: 기본적으로 멘션이 필요해요.
channels.msteams.requireMention=false로 설정하거나 팀/채널별로 구성하세요. - 버전 불일치 (Teams에 여전히 이전 manifest가 표시됨): 앱을 제거하고 다시 추가한 뒤, Teams를 완전히 종료하여 새로고침하세요.
- webhook에서 401 Unauthorized 발생: Azure JWT 없이 수동으로 테스트할 때 발생하는 예상된 결과예요. 엔드포인트에는 도달했지만 인증에 실패했다는 뜻이죠. 제대로 테스트하려면 Azure Web Chat을 사용하세요.
Manifest 업로드 오류
섹션 제목: “Manifest 업로드 오류”- “Icon file cannot be empty”: Manifest가 0바이트인 아이콘 파일을 참조하고 있네요. 유효한 PNG 아이콘을 만드세요 (
outline.png는 32x32,color.png는 192x192). - “webApplicationInfo.Id already in use”: 앱이 이미 다른 팀이나 채팅에 설치되어 있어요. 해당 앱을 찾아 삭제하거나, 변경 사항이 반영될 때까지 5~10분 정도 기다려 보세요.
- 업로드 중 “Something went wrong” 발생: 대신 https://admin.teams.microsoft.com을 통해 업로드를 시도해 보세요. 브라우저 개발자 도구(F12)의 Network 탭을 열고 응답 본문(response body)에서 실제 오류 내용을 확인하세요.
- 사이드로딩 실패: “커스텀 앱 업로드” 대신 “조직의 앱 카탈로그에 앱 업로드”를 시도해 보세요. 이 방법으로 사이드로딩 제한을 우회할 수 있는 경우가 많아요.
RSC 권한이 작동하지 않을 때
섹션 제목: “RSC 권한이 작동하지 않을 때”webApplicationInfo.id가 봇의 App ID와 정확히 일치하는지 확인하세요.- 앱을 다시 업로드하고 팀/채팅에 다시 설치하세요.
- 조직 관리자가 RSC 권한을 차단했는지 확인하세요.
- 올바른 scope를 사용 중인지 확인하세요. 팀의 경우
ChannelMessage.Read.Group, 그룹 채팅의 경우ChatMessage.Read.Chat이 필요해요.
참고 자료
섹션 제목: “참고 자료”- Create Azure Bot — Azure Bot 설정 가이드예요.
- Teams Developer Portal — Teams 앱을 생성하고 관리할 수 있어요.
- Teams app manifest schema — Teams 앱 manifest 스키마 문서예요.
- Receive channel messages with RSC — RSC를 사용해 채널 메시지를 받는 방법이에요.
- RSC permissions reference — RSC 권한에 대한 참고 자료예요.
- Teams bot file handling (채널/그룹은 Graph가 필요해요) — Teams 봇의 파일 처리 방식이에요.
- Proactive messaging — Proactive messaging에 대한 내용이에요.
관련 문서
섹션 제목: “관련 문서”- Channels Overview — 지원하는 모든 채널을 확인할 수 있어요.
- Pairing — DM 인증과 페어링 흐름에 대한 내용이에요.
- Groups — 그룹 채팅 동작과 멘션 게이팅을 다뤄요.
- Channel Routing — 메시지 세션 라우팅에 대해 알아보세요.
- Security — 액세스 모델과 보안 강화 방법이에요.
OpenClaw Expert
아직 막혀 있나요?
이 문서에서 답을 못 찾았다면 OpenClaw Expert에게 바로 물어보세요.