Skip to main content
Glama

office365-mcp

Microsoft 365용 다중 사용자 원격 MCP 서버 — Microsoft Graph를 통한 Outlook 메일, Teams, SharePoint/OneDrive. 각 사용자는 일반 브라우저 로그인을 통해 자신의 Microsoft 계정으로 연결합니다. 서버는 해당 인증을 암호화하여 보관하고, 이후 모든 도구 호출에서 그 사용자로서 작동하므로, 한 번 연결해 두면 클라이언트가 Microsoft 토큰을 보유하지 않아도 계속 작동합니다. 공유 및 서비스 사서함(support@, billing@, info@)은 일부 도구에 매개변수로 붙이는 것이 아니라 일급(first-class) ID로 취급됩니다.

하나의 TypeScript 코드베이스, 세 가지 배포 대상:

플랫폼

진입점

빌드 / 배포

AWS Lambda (Function URL)

src/entries/lambda.ts

npm run build:lambda && npm run deploy:lambda

AWS를 건드리지 않고 먼저 구성을 검증하려면:

DRY_RUN=1 npm run deploy:lambda

| Azure Functions (v4 Node) | src/entries/azure.ts | npm run build:azure && func azure functionapp publish … | | 일반 Node (개발 / 자체 호스팅) | src/entries/node.ts | npm run dev |

다른 Microsoft 365 MCP 서버와의 차이점

오픈소스 분야는 규모가 크고 여러 프로젝트가 훌륭합니다. 이 서버는 더 많은 Graph 커버리지가 아니라 다른 배포 및 ID 모델을 중심으로 구축되었습니다.

  • 서버가 리프레시 토큰을 중개하며, 클라이언트는 Microsoft 토큰을 절대 볼 수 없습니다. 대부분의 기존 서버는 한 사용자, 한 머신에 대해 하나의 토큰을 평문으로 유지합니다 — ~/.outlook-mcp-tokens.json, ~/.microsoft_mcp_token_cache.json, ~/.office-mcp-tokens.json. 성숙한 원격 서버 하나(Softeria의 ms-365-mcp-server, HTTP 모드)는 토큰 갱신이 클라이언트의 책임이라고 명시하므로, Graph 액세스 토큰이 약 1시간 후 만료되면 세션이 죽습니다. 여기서는 사용자별 Entra 리프레시 토큰이 AES-256-GCM으로 봉인되어 서버 측에 저장되며, 서버가 사용자를 대신해 액세스 토큰을 조용히 갱신합니다.

  • 무상태(stateless)이며 서버리스 형태입니다. SSE 세션 선호도(affinity)가 없고, 장기 실행 프로세스가 없으며, 모든 세션 및 자격 증명 상태는 DynamoDB에 있습니다. HTTP를 지원하는 대안들은 항상 켜져 있는 컨테이너(Express, Azure Container Apps, 로컬 stdio shim 뒤의 App Service 백엔드)를 가정합니다.

  • 공유 사서함이 모델링되며, 앱 전용(app-only) 경로도 포함됩니다. 호출자가 Exchange 권한을 보유한 경우 서버는 /users/{mailbox}에 대해 자체 위임 토큰을 사용합니다. 사서함에 아무도 로그인하지 않는 경우 범위가 지정된 애플리케이션 자격 증명을 사용할 수 있습니다. 다른 오픈소스 서버는 이 두 번째, 관리되는 경로를 제공하지 않으며, Exchange 측 범위 지정은 여기서 스크립트(deploy/entra/scope-app-only.ps1)로 제공되어 독자의 몫으로 남겨두지 않습니다.

  • 사용자가 어시스턴트가 사용할 수 있는 사서함을 선택합니다. Entra는 위임된 Mail.*.Shared에 대한 사서함별 동의가 없습니다. 해당 범위를 부여하면 Exchange가 그 사람에게 열어준 모든 사서함을 열 수 있는 토큰이 생성되며, Microsoft는 이를 좁힐 방법을 제공하지 않습니다. 따라서 로그인 후 이 서버가 제공하는 승인 페이지가 이어지고, 사용자가 거기서 체크한 내용이 모든 요청에서 서버 측에서 강제됩니다. 사서함 승인을 참조하세요.

  • 거버넌스 스토리가 있습니다. 새 도구에 대해 기본 거부(default-deny)인 사용자별 도구 허용 목록, 사용자별 속도 제한, 사용자 자신의 승인 위에 적용되는 관리자 사서함 상한, 되돌릴 수 없는 삭제에 대한 배포 수준 잠금, 그리고 사용자, 도구, 인수, 실제로 접근한 사서함을 명명하는 모든 호출에 대한 구조화된 감사 기록이 있습니다.

  • 도구 표면은 의도적으로 작습니다. 300개의 엔드포인트 형태 도구가 아니라 27개의 작업 형태 도구입니다. 폭이 이 서버가 경쟁하는 지점이 아닙니다. Softeria는 Excel 범위와 OneNote 페이지를 다루고, Microsoft 자체 Work IQ 서버는 의미 검색과 Defender급 추적을 갖추고 있습니다. 둘 다 제공하지 않는 것은 Microsoft 365 Copilot 라이선스 없이 자체 리전에서 직접 호스팅하는 서버입니다.

두 가지 퍼스트파티 옵션이 있으며 알아둘 가치가 있습니다. Microsoft의 MCP Server for Enterprise는 무료이지만 읽기 전용이며 Entra 디렉터리 데이터로 범위가 제한됩니다 — 보완재이지 경쟁자가 아닙니다. Agent 365 / Work IQ는 메일, 일정, Teams, SharePoint를 다루지만 프리뷰 상태이고 Microsoft 호스팅 전용이며 Microsoft 365 Copilot 라이선스가 필요합니다.

빠른 시작

1. Entra 애플리케이션을 등록합니다. 이 단계가 가장 자주 잘못됩니다. deploy/entra/SETUP.md를 따르세요 — 특히 플랫폼을 SPA가 아닌 Web으로 등록하세요(SPA 리디렉션 URI는 리프레시 토큰을 조용히 24시간으로 제한하며, 그 만료는 파생된 모든 토큰에 상속되어 연결-한 번(connect-once) 전제를 파괴합니다).

2. 두 개의 키를 생성합니다. 서로 다른 작업을 수행하는 서로 다른 키이며, 어느 것도 다른 것을 대체할 수 없습니다.

npm install
npm run gen:oauth-key   # RS256 keypair — signs the tokens Claude presents to US
npm run gen:enc-key     # AES-256-GCM key — seals the tokens WE present to Microsoft

gen:enc-key의 출력을 배포와 별도로 시크릿 관리자에 백업하세요. 분실하면 저장된 모든 연결을 해독할 수 없게 되어 모든 사용자가 동시에 다시 로그인해야 합니다.

3. 배포합니다.

npm run build:lambda
npm run deploy:lambda        # wraps `sam deploy` against deploy/aws/template.yaml

스택은 EntraRedirectUri 출력을 인쇄합니다. 앱 등록에 해당 정확한 URI를 등록하세요 — 자동화할 수 없는 유일한 단계입니다.

4. 클라이언트를 연결합니다. https://<your-deployment>/mcp를 사용자 지정 커넥터로 추가하세요. 클라이언트는 /.well-known/oauth-protected-resource에서 OAuth 엔드포인트를 발견하고, 자체 등록한 다음, 사용자를 Microsoft 로그인으로 보냅니다. Microsoft의 동의 화면 다음에는 이 서버 자체의 사서함 승인 페이지가 이어지며, 사용자는 어시스턴트가 사용할 수 있는 공유 사서함을 체크합니다. 클라이언트는 그 후에야 토큰을 받습니다. 그런 다음 o365_whoami를 호출하세요 — 연결 상태, 부여된 권한, 사용자가 승인한 사서함, 호출자가 현재 보유한 도구를 보고합니다.

로컬 개발의 경우 구성의 변수를 .env 파일에 넣으세요 — 최소한 Entra 등록, 두 개의 키, 그리고 파일 기반 저장소용 MCP_USERS_FILE + MCP_GRAPH_FILE + MCP_OAUTH_FILE이 필요합니다. 이것이 npm run dev가 AWS 없이 실제 브라우저 로그인 및 사서함 승인 흐름을 실행할 수 있게 하는 이유입니다. .env.example은 주석이 달린 버전입니다. 그런 다음:

npm run dev                              # http://localhost:3000/mcp

앱 등록에 http://localhost:3000/oauth/callback을 두 번째 리디렉션 URI로 추가하세요.

아키텍처

  Claude / MCP client
        │  1. POST /mcp  (Bearer: our RS256 JWT)
        ▼
┌──────────────────────────────────────────────────────────┐
│  office365-mcp   (Lambda Function URL / Azure Fn / Node)  │
│                                                           │
│  Hono ── /mcp ── JSON-RPC 2.0 ── tool registry            │
│    │                                                       │
│    ├─ OAuth 2.1 authorization server (for the MCP client)  │
│    │    /.well-known/*  /oauth/register  /authorize        │
│    │    /callback  /consent  /token  /jwks.json            │
│    │                                                       │
│    └─ Graph token broker ── actor resolution ── client     │
└───────┬──────────────────────────┬────────────────────────┘
        │                          │
        │ 2. browser sign-in       │ 5. Bearer: Graph access token
        ▼                          ▼
  Microsoft Entra ID        Microsoft Graph
  login.microsoftonline     graph.microsoft.com/v1.0
        │
        │ 3. refresh token ──► AES-256-GCM ──► DynamoDB (MCP_GRAPH_TABLE)
        │                       (row-bound AAD)
        ▼
  4. the browser lands back here, on /oauth/consent — the user ticks
     which mailboxes the assistant may use, and only then is the
     authorization code handed to the MCP client

전송(Transport). Streamable HTTP, 무상태 모드. POST /mcp에 대한 JSON-RPC 2.0, 요청당 메시지 하나 — JSON-RPC 배칭은 -32600으로 거부됩니다. 배칭은 MCP 2025-06-18 개정판에서 제거되었고, 호출 배열이 단일 속도 제한 요금에 실릴 수 있기 때문입니다. 인증되지 않은 요청은 RFC 9728 WWW-Authenticate: Bearer realm="mcp", resource_metadata=… 챌린지와 함께 401을 받으며, 이것이 클라이언트가 커넥터 OAuth 흐름을 시작하게 만드는 것입니다. 보호 리소스 문서는 /.well-known/oauth-protected-resource/.well-known/oauth-protected-resource/mcp 양쪽에서 제공되며, resource{origin}/mcp로 보고합니다 — 사용자가 실제로 입력한 엔드포인트이며, 클라이언트가 비교하는 대상입니다.

ID 모델

두 개의 별도 OAuth 관계가 있으며, 이 둘을 구분하는 것이 전체 설계입니다:

  1. MCP 클라이언트 ↔ 이 서버. 우리가 인증 서버입니다. 클라이언트는 동적으로 등록하고(RFC 7591), /oauth/authorize/oauth/token에 대해 인증 코드 + PKCE 흐름을 실행하며, 우리가 서명한 RS256 JWT를 받습니다. Entra는 동적 클라이언트 등록을 지원하지 않으며 이 교환을 결코 보지 못합니다.

  2. 이 서버 ↔ Entra. 우리는 하나의 정적 Web 리디렉션 URI를 가진 기밀 클라이언트입니다. 사용자 로그인 중에 우리는 Entra를 향해 자체 독립적인 PKCE 체인을 실행하고, 클라이언트 시크릿 또는 인증서로 코드를 교환하며, id_token, Graph 액세스 토큰, 그리고 offline_access를 요청하므로 리프레시 토큰을 받습니다.

그 리프레시 토큰이 핵심 산출물입니다. 행에 바인딩된 추가 인증 데이터(AAD) {tid}:{oid}:refresh 아래 AES-256-GCM으로 봉인되므로, 한 사용자의 레코드에서 꺼낸 블롭을 다른 사용자의 레코드에 재생할 수 없으며, 자격 증명 전용 테이블에 기록됩니다. 이후 모든 도구 호출은: JWT → (tid, oid) → 캐시된 액세스 토큰, 또는 리프레시 교환 → Graph로 진행됩니다. 사용자의 키는 id_token의 불변 쌍 (tid, oid)이며, email, preferred_username, upn은 절대 아닙니다 — 이들은 모두 변경 가능하고 관리자가 제어할 수 있습니다.

그 사이에 /oauth/callback일시 중지됩니다. Microsoft 인증이 저장되면 MCP 클라이언트에 인증 코드를 넘겨주지 않습니다. 대신 단일 사용 티켓과 함께 브라우저를 /oauth/consent로 리디렉션하고, 사용자는 이 어시스턴트가 주소를 지정할 수 있는 사서함을 선택합니다. 사용자가 해당 페이지를 제출해야 코드가 발행되고 클라이언트가 홈으로 리디렉션됩니다. 사서함 승인을 참조하세요.

테넌트 허용 목록(O365_ALLOWED_TENANTS)은 로그인 시에만이 아니라 모든 요청에서 다시 확인되므로, 테넌트를 제거하면 다음 로그인이 아니라 즉시 적용됩니다.

위임 vs 앱 전용

모든 Graph 호출은 먼저 구성에서 결정적으로 **행위자(actor)**를 해석합니다 — 모델이 서버를 속여 승격시킬 수 없습니다:

행위자

주소

시점

볼 수 있는 것

delegated-self

/me

mailbox 인수가 없거나 호출자 자신의 주소인 경우

로그인한 사용자가 보는 것과 정확히 동일

delegated-shared

/users/{upn}

다른 사서함으로, 사용자가 연결 시 승인했고, 관리자 정책이 허용하며, 호출자가 해당 사서함에 대한 Exchange 권한이 있는 경우

Exchange가 해당 사서함에 대해 그 사용자에게 부여한 것

app-only

/users/{upn}

사서함이 O365_APP_ONLY_MAILBOXES에 있고, 앱 전용이 활성화되어 있으며, 호출자의 정책에 allowAppOnly가 있는 경우

Exchange RBAC가 애플리케이션에 범위를 지정한 모든 것

delegated-self 외의 모든 행위자는 Graph에 무엇이든 묻기 전에 먼저 policyAllowsMailbox를 통과합니다 — 사용자 자신의 승인, 그 다음 관리자의 상한. 위임이 기본값이며 정상 경로입니다. 두 관문을 지나면 테넌트의 기존 권한이 실제 관문으로 남으며, 거부된 호출은 서버가 "해당 사서함에 대한 Full Access를 관리자에게 요청하세요"로 번역하는 정직한 403을 생성합니다. 앱 전용은 아무도 로그인하지 않는 사서함에만 존재하며, 기본적으로 꺼져 있고, 아래에 전체 설명이 있습니다. /me는 공유 사서함을 의미하지 않으며 — /me 경로로 공유 사서함에 들어갈 수 없습니다 — 앱 전용 토큰은 로그인한 사용자가 없으므로 /me를 전혀 사용할 수 없습니다.

Teams는 설계상 영구적으로 위임 전용입니다. 제한 사항을 참조하세요.

모델에 노출되는 도구

27개 도구. W는 테넌트 상태를 변경하는 도구를 표시합니다. 이 도구들은 새로 프로비저닝된 사용자에 대해 기본적으로 비활성화되며 추가로 정책에 allowWrites가 필요합니다. 모든 Outlook 도구는 선택적 mailbox 인수(UPN 또는 SMTP 주소)를 사용하여 작동할 사서함을 선택합니다. 자신의 사서함이면 생략하세요. 반환된 모든 ID는 불투명한 Microsoft Graph ID입니다 — 그대로 다시 전달하고, 절대 직접 구성하지 마세요.

Outlook — 읽기

도구

기능

o365_mail_search

Outlook 검색 구문(from:, subject:, attachment:, hasAttachments:true, …)을 사용하여 사서함을 전체 텍스트 검색합니다. 항상 날짜순으로 정렬되며, Microsoft에 의해 최대 1,000개 결과로 제한되고, 필터와 결합할 수 없습니다.

o365_mail_list

구조적 필터와 정렬을 사용하여 폴더의 메시지를 나열합니다 — 읽지 않은 메시지만, 특정 발신자, 날짜 범위, 정렬 순서 등. o365_mail_search의 대응 도구: 정밀한 필터링, 키워드 매칭 없음.

o365_mail_get

메시지 하나를 전체 내용으로, 본문은 일반 텍스트로, 선택적으로 인터넷 메시지 헤더와 첨부 파일 메타데이터를 포함합니다. 긴 본문은 잘리며 원본 길이가 보고됩니다.

o365_mail_folders

메일 폴더를 최상위 또는 전체 트리로 나열하며, 메시지 수와 읽지 않은 수를 포함합니다. 메시지를 이동하기 전에 폴더 ID를 확인하는 데 사용합니다.

o365_mail_attachments_list

한 메시지의 첨부 파일 이름, 유형, 크기 및 인라인 플래그를 표시합니다. 메타데이터만 — 파일 내용은 절대 포함하지 않습니다.

o365_mail_attachment_download

첨부 파일을 다운로드하고 단기 유효한 사전 서명된 URL을 반환합니다. 바이트를 인라인으로 반환하지 않습니다.

Outlook — 쓰기

도구

기능

W o365_mail_send

인라인으로 작성하거나 초안에서 메시지를 즉시 보냅니다. Graph는 전달을 수락하고 ID를 반환하지 않으므로 "배달됨"이 아닌 "수락됨"으로 보고합니다.

W o365_mail_reply

기존 메시지에 답장, 전체 답장 또는 전달을 한 번에 수행합니다. 작성한 텍스트는 인용된 원본 위에 배치됩니다.

W o365_mail_draft_create

처음부터 또는 원본을 이미 인용하는 답장/전달로 보내지 않은 초안을 만듭니다. 초안 ID를 반환합니다.

W o365_mail_draft_update

보내지 않은 초안의 제목, 본문 또는 수신자를 편집합니다. 초안에서만 작동합니다.

W o365_mail_move

메시지를 다른 폴더로 이동하거나 복사합니다. 이동하면 메시지 ID가 변경됩니다 — 새 ID가 반환되고 이전 ID는 더 이상 작동하지 않습니다.

W o365_mail_delete

메시지 삭제: trash(복구 가능, 기본값), soft 또는 permanent — 되돌릴 수 없으며, 추가로 O365_ALLOW_PERMANENT_DELETE로 제한됩니다.

W o365_mail_flags

한 번에 최대 20개의 메시지에 읽음/읽지 않음 표시, 플래그 지정, 분류 및 중요도 설정을 수행합니다.

W o365_mail_folder_manage

메일 폴더를 생성, 이름 변경, 이동 또는 삭제합니다.

W o365_mail_attachment_add

초안에 파일을 첨부합니다. 3MB 미만은 인라인, 최대 150MB는 청크 업로드 세션을 통해 첨부합니다.

Teams

도구

기능

o365_teams_list

내 팀 또는 한 팀의 채널을 나열합니다. 팀 또는 채널 이름을 다른 Teams 도구가 필요로 하는 ID로 변환하는 방법입니다.

o365_teams_chats_list

내 채팅 — 1:1, 그룹 및 모임 — 최근 활동 순으로 나열합니다. 1:1 채팅에는 자체 이름이 없으므로 참가자로부터 이름을 생성합니다.

o365_teams_messages_list

채널 또는 채팅에서 메시지를 읽습니다. 채팅은 날짜 범위를 지원하지만 채널은 지원하지 않습니다. Graph의 채널 API는 날짜 필터를 허용하지 않기 때문입니다.

W o365_teams_message_send

자신으로서 채널, 채널 스레드 또는 채팅에 게시합니다 — 이메일로 사람에게 보내는 것도 포함하며, 이 경우 1:1 채팅을 찾거나 생성합니다. user 인수를 사용하지 않습니다: Teams는 모든 메시지를 로그인한 사용자에게 귀속시키므로, 이를 제공하면 발생할 수 없는 가장을 광고하는 것이 됩니다.

o365_teams_search

볼 수 있는 모든 채팅과 채널을 대상으로 키워드 검색을 수행합니다. Teams를 검색하는 유일한 방법입니다. 목록 API에는 검색 기능이 전혀 없습니다. user 인수도 사용하지 않습니다/search/query는 토큰 소유자로 범위가 지정되며 "다른 사용자로 검색" 매개변수가 없습니다.

SharePoint 및 OneDrive

도구

기능

o365_files_search

SharePoint 및 OneDrive 전체 또는 단일 사이트나 라이브러리 내에서 파일을 검색합니다. KQL 용어(filetype:, author:, path:)를 지원합니다.

o365_files_sites

사이트를 찾거나 사이트의 문서 라이브러리와 해당 드라이브 ID를 나열합니다. SharePoint 작업의 시작점입니다.

o365_files_list

OneDrive 또는 문서 라이브러리의 폴더를 드라이브 + 항목 ID, 경로 또는 내 OneDrive 루트로 나열합니다.

o365_files_get

파일 또는 폴더 하나의 세부 정보 — 붙여넣은 공유 URL에서도 확인할 수 있으며, 이를 해석합니다. 선택적으로 액세스 권한이 있는 사람을 보고합니다.

o365_files_download

파일을 사전 서명된 URL로 다운로드하며, 선택적으로 PDF로 변환할 수 있습니다. 인라인으로 제공하지 않습니다.

W o365_files_share

링크로 공유하거나 사람을 초대하여 공유합니다. 테넌트의 공유 정책이 요청한 내용을 자동으로 낮출 수 있으므로 실제로 부여된 액세스 권한을 보고합니다.

핵심

도구

기능

o365_whoami

로그인한 사용자, Microsoft 연결이 활성 상태인지와 마지막 새로 고침 시점, 부여된 권한, 보유한 도구, 승인했고 지금 실제로 사용할 수 있는 공유 사서함, 그리고 각 사서함에 대해 호출이 사용자로 실행되는지 서비스 계정으로 실행되는지 확인합니다. 문제가 발생하면 먼저 이 도구를 호출하세요.

구성

모든 것은 환경 변수로만 설정됩니다. 권위 있는 목록은 src/config.tsEnv 유형입니다.

Entra 앱 등록

변수

필수 여부

설명

OAUTH_ENTRA_TENANT_ID

테넌트 GUID 또는 확인된 도메인. 모든 호출은 이 특정 테넌트를 대상으로 합니다. /common은 토큰 캐시 미스와 불필요한 재인증을 유발하며 클라이언트 자격 증명에는 유효하지 않습니다. common / organizations는 배포를 다중 테넌트로 전환합니다.

OAUTH_ENTRA_CLIENT_ID

애플리케이션(클라이언트) ID. 등록 시 플랫폼 유형은 Web이어야 합니다.

OAUTH_ENTRA_CLIENT_SECRET

택일

클라이언트 비밀. 가장 간단하지만 Entra는 수명을 24개월로 제한합니다.

OAUTH_ENTRA_CLIENT_CERT_PEM

택일

인증서 클라이언트 인증을 위한 PKCS#8 PEM 개인 키(리터럴 또는 base64). 프로덕션에서 권장됩니다.

OAUTH_ENTRA_CLIENT_CERT_THUMBPRINT

인증서 사용 시

포털에 표시된 16진수 SHA-1 지문. Entra는 지문으로 어서션을 인증서와 대조하므로 두 부분이 모두 필요합니다.

O365_ALLOWED_TENANTS

다중 테넌트 실행 시 허용되는 쉼표로 구분된 테넌트 ID. 로그인 시 그리고 모든 요청에서 다시 확인되므로 여기서 테넌트를 제거하면 기존 연결이 다음 로그인 때가 아니라 즉시 잠깁니다. common과 함께 비어 있으면 동의하는 모든 테넌트가 연결할 수 있으며 서버는 시작 시 경고를 표시합니다.

변수

필수 여부

설명

OAUTH_SIGNING_KEY_PRIVATE

우리 MCP 액세스 토큰에 서명하는 RS256 키의 Base64 PKCS#8 PEM. npm run gen:oauth-key.

OAUTH_SIGNING_KEY_PUBLIC

일치하는 공개 키의 Base64 SPKI PEM. /.well-known/jwks.json에 게시됩니다.

OAUTH_SIGNING_KEY_KID

JWKS 및 토큰 헤더의 키 ID. 키 쌍 회전과 함께 변경하세요. 기본값은 primary입니다.

O365_TOKEN_ENC_KEY

<kid>:<base64 32 bytes> — 저장된 모든 Entra 새로 고침 토큰과 캐시된 액세스 토큰을 봉인합니다. npm run gen:enc-key. 분실하면 저장된 모든 연결이 영구적으로 손상됩니다.

O365_TOKEN_ENC_KEYS_PREVIOUS

동일한 형식의 쉼표로 구분된 폐기된 키로, 복호화 전용으로 허용됩니다. 이것이 회전을 플래그 데이가 아닌 롤링 작업으로 만드는 요소입니다.

저장소

변수

필수 여부

설명

MCP_GRAPH_TABLE

예¹

봉인된 새로 고침 토큰(TTL 없음) 및 캐시된 액세스 토큰(TTL)용 DynamoDB 테이블. 자격 증명이 자체 IAM 경계와 백업 정책을 갖도록 OAuth 테이블과 의도적으로 분리되어 있습니다.

MCP_GRAPH_FILE

자체 호스팅 및 개발용 JSON 파일 폴백. MCP_GRAPH_TABLE이 설정되면 무시됩니다. Lambda에서는 사용할 수 없습니다.

MCP_OAUTH_TABLE

예¹

자체 OAuth 상태(등록된 클라이언트, 로그인 상태, 인증 코드, 새로 고침 토큰)용 DynamoDB 테이블. expiresAt에 TTL이 적용됩니다.

MCP_OAUTH_FILE

동일한 상태에 대한 JSON 파일 폴백으로, npm run dev가 AWS 없이 실제 브라우저 로그인 흐름을 실행할 수 있게 합니다. MCP_OAUTH_TABLE이 설정되면 무시됩니다. 각 컨테이너가 서로 다른 상태를 보게 되는 Lambda에서는 사용할 수 없습니다.

MCP_USERS_TABLE

사용자 및 정책용 DynamoDB 테이블로, keyPrefix-indexoid-index GSI가 있습니다.

MCP_USERS_FILE

자체 호스팅 및 개발용 JSON 사용자 저장소. MCP_USERS_TABLE이 설정되면 무시됩니다.

MCP_SHARED_SECRET

사용자 저장소를 우회하는 레거시 단일 관리자 베어러. 스모크 테스트에 유용합니다. 자체 Graph 연결이 없으므로 로그인한 사용자에 매핑되지 않으면 Graph 도구가 재연결 오류를 반환합니다.

¹ 또는 로컬 개발용 해당 _FILE 변형(MCP_GRAPH_FILE / MCP_OAUTH_FILE).

Graph 동작

변수

기본값

설명

O365_SCOPE_PROFILE

work

work는 공유 사서함, SharePoint 사이트 및 Teams 채널 범위를 포함하며, 그중 여러 개는 테넌트 관리자 동의가 필요합니다. personal은 사용자 동의 가능한 범위만 요청합니다.

O365_SCOPES

파생됨

공백으로 구분된 전체 재정의. 로드 시 검증됩니다. /.default는 명명된 범위와 혼합할 수 없으며(AADSTS70011), offline_access는 필수입니다.

O365_GRAPH_BASE

https://graph.microsoft.com/v1.0

여기서 사용하는 여러 기능이 존재하지 않는 소버린 클라우드에서만 변경하세요.

O365_IMMUTABLE_IDS

true

Outlook 호출에 Prefer: IdType="ImmutableId"를 전송하여 ID가 이동 후에도 유지되도록 합니다. 첫 배포 시 한 번 결정하고 운영 중인 스택에서는 절대 변경하지 마세요.

O365_BODY_FORMAT

text

text는 일반 텍스트 본문을 요청하며, 이는 모델 지향 서버가 원하는 것입니다. HTML 본문은 추적 마크업이 대부분입니다.

O365_GRAPH_TIMEOUT_MS

30000

요청별 타임아웃으로, 클라이언트의 300초 도구 타임아웃보다 훨씬 낮게 유지됩니다.

O365_MAX_CONCURRENCY_PER_MAILBOX

4

(애플리케이션, 사서함)별 진행 중 요청 상한. Exchange는 정확히 4개를 허용합니다. 값을 올리면 처리량이 429로만 전환되므로 이 값으로 제한됩니다.

O365_USER_AGENT

NONISV|SelfHosted|office365-mcp/0.1.0

Microsoft는 장식되지 않은 트래픽의 우선순위를 낮춥니다. 문서화된 형식을 유지하고 중간 필드에 회사 이름을 넣으세요.

O365_SEARCH_REGION

auto

POST /search/query용 SharePoint 지역(NAM, EUR, APC). 앱 전용 검색에 필요하며, 다중 지역 테넌트에서는 명시적으로 설정하세요.

게이트 및 출력

Var

Default

Description

O365_APP_ONLY_ENABLED

false

앱 전용 모드의 마스터 스위치입니다. false인 동안에는 허용 목록과 무관하게 해당 코드 경로에 도달할 수 없습니다.

O365_APP_ONLY_MAILBOXES

empty

애플리케이션 자격 증명으로 접근 가능한 사서함 주소(쉼표로 구분)입니다. 와일드카드는 즉시 거부됩니다.

O365_APP_ONLY_SITES

empty

애플리케이션 자격 증명으로 접근 가능한 사이트 ID 또는 URL(쉼표로 구분)이며, 앱 전용 행위자로 수행되는 모든 사이트 또는 드라이브 주소 호출에 적용됩니다. 빈 목록은 앱 전용이 SharePoint에 전혀 도달하지 못함을 의미하며, 사이트를 지정하지 않은 앱 전용 호출은 허용되지 않고 거부됩니다. 일치는 대소문자를 구분하지 않으며 정확히 일치하거나 / 경계에서 끝나는 접두사 일치이므로, 더 짧은 참조가 접근을 넓히지 않고도 항목 하나가 사이트와 그 하위 전체를 포괄할 수 있습니다. Sites.Selected 등록과 짝을 이룹니다.

O365_ALLOW_PERMANENT_DELETE

false

사용자별 정책에 더해 o365_mail_delete 모드 permanent에 대한 배포 수준 잠금입니다.

MCP_OUTPUT_FORMAT

toon

toon은 목록 출력에서 토큰 사용량을 실질적으로 줄여주는 간결한 표 형식 출력을 생성합니다. json은 프로그래밍 방식 소비자를 위한 예쁜 JSON을 생성합니다.

MCP_ARTIFACT_BUCKET

다운로드용 S3 버킷입니다. 모든 다운로드 도구에 필수입니다. 설계상 base64 폴백은 없습니다.

MCP_ARTIFACT_URL_TTL_SECONDS

3600

사전 서명된 URL의 수명입니다. 이 URL을 보유한 사람은 인증 없이 파일을 가져올 수 있으므로 짧게 유지하세요.

MCP_ARTIFACT_REGION

AWS_REGION

아티팩트 버킷의 리전을 재정의합니다.

MCP_AUDIT_FILE

stderr 외에 감사 및 보안 기록을 위한 JSONL 싱크입니다. 자체 호스팅 배포용입니다. Lambda에서는 stderr가 이미 CloudWatch에 도달합니다.

MCP_AUDIT_READS

unset

1은 쓰기뿐 아니라 읽기 도구도 감사합니다. 읽기가 볼륨을 지배하므로 기본적으로 꺼져 있습니다. 이 설정과 무관하게 모든 변경 호출, 모든 실패, 그리고 다른 사서함이나 사용자를 지칭하는 모든 호출은 항상 기록됩니다. 다른 사람의 사서함에 대해 작업하는 것은 정확히 규정 준수 검토가 묻는 내용이기 때문입니다.

PORT

3000

일반 Node 진입점의 수신 포트입니다. Lambda 및 Azure Functions에서는 사용되지 않습니다.

공유 사서함 액세스

이것이 설계의 중심이 되는 기능이며, 기능 자체는 이 서버가 아니라 테넌트에 의존합니다.

Graph가 이를 수행하려면 두 가지가 모두 참이어야 합니다. 연결에는 위임된 .Shared Graph 범위(Mail.Read.Shared, Mail.ReadWrite.Shared, Mail.Send.Shared — 모두 work 범위 프로필에 포함됨)가 필요하며, 그리고 Exchange Online이 대상 사서함에 대해 로그인한 사용자에게 권한을 부여해야 합니다. 범위는 기능의 잠금을 해제할 뿐입니다. Exchange가 실제 게이트입니다. Exchange 권한이 없으면 동의 여부와 무관하게 Graph는 403을 반환합니다.

관리자는 Exchange 관리 센터(받는 사람 → 사서함 → 공유 사서함 → 위임)에서 다음 중 하나 이상을 부여합니다:

Right

What it enables

Effect

Full Access

해당 사서함에서 읽기, 나열, 이동, 삭제, 초안 작성

해당 사서함에 대한 모든 읽기 및 쓰기 도구에 필요합니다. 보내기가 공유 사서함의 보낸 편지함에 사본을 남겨야 하는 경우에도 필요합니다.

Send As

보낸 사람으로 공유 사서함을 사용하여 보내기

받는 사람은 공유 사서함만 보게 됩니다.

Send on Behalf

사서함을 대신하여 보내기

받는 사람은 "사용자공유 사서함을 대신하여"를 보게 됩니다. 사용자는 Outlook에서 직접 이 권한을 부여할 수 있습니다. Send As는 관리자만 부여할 수 있습니다.

권한이 부여된 후 적용되는 데 최대 1시간이 걸릴 수 있습니다. 부여 직후의 403은 대개 그 때문이며, 서버는 오류에서 이를 명시합니다.

이 두 가지에 더해 이 서버는 자체적인 두 가지를 추가합니다. 사용자가 연결 시 사서함을 승인해야 하며, 관리자의 allowedMailboxes 상한이 이를 허용해야 합니다. 둘 다 Graph에 어떤 요청도 하기 전에 확인됩니다 — 사서함 승인을 참조하세요.

그런 다음 주소를 전달하기만 하면 됩니다: o365_mail_list({ mailbox: "support@contoso.com", unreadOnly: true }). 서버는 행위자를 확인하고 /users/support@contoso.com/…을 호출하며 /me는 절대 호출하지 않습니다. 공유 사서함으로 가는 /me 경로는 없습니다. 주소는 또한 연결 시 사용자가 승인한 것이어야 합니다. 아래 사서함 승인을 참조하세요.

미리 알아두면 좋은 제약 두 가지가 있습니다. 사용자가 권한을 가진 사서함을 열거하는 Graph API는 없습니다 — 이것이 승인 페이지가 후보 주소를 나열하는 대신 검증하는 이유이며, 호출자가 도구 호출에서 여전히 사서함을 지정해야 하는 이유입니다. 그리고 로그인한 사용자는 일반적으로 자신의 라이선스가 있는 사서함이 필요하지만, 공유 사서함 자체에는 라이선스가 필요하지 않습니다.

더 좁히기. policy.allowedMailboxes는 사용자가 직접 승인한 것에 더해지는 관리자의 상한이며, 유효 액세스는 둘의 교집합입니다. 기본값은 "*"이며, 이는 나머지 결정을 Exchange에 위임합니다(그것이 속한 곳). 명시적 목록으로 설정하면 사용자를 Exchange 권한 아래로 제한할 수 있고, null로 설정하면 mailbox 인수를 완전히 거부합니다:

npm run user -- add alice --mailboxes=support@contoso.com,billing@contoso.com

사서함 승인

주소는 수동으로 입력됩니다. Microsoft는 사용자가 열 수 있는 사서함을 나열하는 API를 노출하지 않으며, 통신 상대에서 이를 추론하면 대부분 틀린 목록이 생성되었습니다 — 그래서 페이지는 추측하지 않습니다. 대신 검증을 수행합니다. 입력된 모든 주소는 승인 전에 Exchange에 대해 확인되며, 사용 가능 또는 사용 불가능으로 이유와 함께 표시됩니다.

로그인한 사용자 자신의 사서함은 해당 목록의 일반 항목이며 제거할 수 있습니다. 공유 지원 사서함을 중심으로 구축된 어시스턴트는 운영자의 개인 받은 편지함을 읽을 이유가 없으므로, 그것은 표현 가능해야 합니다. 이를 보류하면 모든 Outlook 도구가 다른 승인된 사서함을 지정하지 않는 호출을 거부합니다. Teams와 SharePoint는 영향을 받지 않습니다. 둘 다 사서함 게이트를 통과하지 않기 때문입니다.

Microsoft는 이 권한을 제한할 수 없으므로 이 서버가 제한합니다. Entra는 위임된 Mail.*.Shared에 대해 사서함별 동의를 제공하지 않습니다. 사용자가 해당 범위를 승인하는 순간, 결과 토큰은 Exchange가 그 사람에게 열어주는 모든 사서함을 열 수 있으며, Microsoft 측에서 이를 좁힐 방법이 없습니다. SharePoint는 2024년에 위임된 Sites.Selected를 얻었지만, Exchange에는 이에 상응하는 것이 없고 로드맵에도 없습니다. 따라서 아래의 승인 페이지는 단순한 편의가 아닙니다. 이는 권한 부여를 제한하는 유일한 수단이며, 모든 요청에서 Graph 호출 전에 서버 측에서 강제됩니다 (resolveActor에서 도달하는 src/users.tspolicyAllowsMailbox).

흐름. /oauth/authorize → Entra 로그인 → /oauth/callback은 봉인된 새로 고침 토큰을 저장한 다음, MCP 클라이언트에 인증 코드를 전달하는 대신 일회용 티켓(15분)과 함께 /oauth/consent로 리디렉션합니다. 페이지에는 사용자 자신의 사서함(항상 포함되며 제거할 수 없음)과 후보 공유 사서함이 표시됩니다. 각 사서함은 이미 프로브되어 열 수 없는 주소는 나중에 수락되어 실패하는 대신 이유와 함께 회색으로 표시됩니다. 사용자는 이 어시스턴트가 사용할 수 있는 항목을 선택하고, 그 후에야 인증 코드가 생성되고 클라이언트가 홈으로 리디렉션됩니다. 다시 연결하면 이전 선택이 미리 선택된 상태로 페이지가 다시 실행되며, 이는 사용자가 나중에 사서함을 제거하는 방법이기도 합니다.

프로브가 볼 수 없는 것. 받은 편지함에 대한 403에는 세 가지 서로 다른 원인이 있으며, 그 중 하나만 '전혀 액세스 권한 없음'입니다. 보내는 사람으로만 보유하거나 전체 사서함이 아닌 단일 폴더에 대한 액세스 권한을 부여받은 사용자는 원하는 작업에 더 좁은 액세스가 작동하더라도 받은 편지함 프로브에 실패합니다. 페이지는 사서함이 표시되는 곳에 그렇게 말합니다. 정직한 요약은 프로브가 과대 보고가 아니라 과소 보고한다는 것입니다. 거짓 긍정을 생성하지 않습니다. 200으로 프로브되는 주소는 서버가 실제로 열 수 있는 주소입니다.

두 개의 게이트, 둘 다 서버 측. grantedMailboxes사용자가 승인한 것이고, allowedMailboxes관리자의 상한선입니다. 사서함은 둘 다에 나타날 때만 도달할 수 있으며, o365_whoami는 그 교집합을 usableSharedMailboxes로 보고하므로 모델은 실제로 사용할 수 있는 사서함만 볼 수 있습니다. 사용자 자신의 사서함은 항상 허용되며 두 목록 모두에 나타나지 않습니다.

API 키 서비스 계정은 페이지를 볼 수 없습니다 — 브라우저도 물어볼 사람도 없으므로 동의 게이트가 적용되지 않습니다. 이는 의도적입니다. 키를 만든 관리자가 동의 당사자이며 allowedMailboxes만이 적용됩니다. 구분은 ID에 Entra oid가 있는지, 즉 브라우저 로그인을 거쳤는지 여부에 따라 결정됩니다.

앱 전용 모드

앱 전용 모드는 한 가지 상황을 위해 존재합니다. 아무도 로그인하지 않고 아무에게도 위임되지 않았지만 에이전트가 여전히 분류해야 하는 사서함입니다. 사용자 자격 증명 대신 애플리케이션 자체 자격 증명을 사용하므로 로그인한 사용자가 없고 /me는 유효하지 않습니다.

기본적으로 꺼져 있으며 필요하지 않으면 꺼진 상태를 유지해야 합니다. 관리자 동의를 받은 애플리케이션 Mail.ReadWrite조직의 모든 사서함에 대한 액세스 권한을 부여하기 때문입니다. 이를 켜려면 세 가지 독립적인 조건이 필요합니다: O365_APP_ONLY_ENABLED=true, O365_APP_ONLY_MAILBOXES에 사서함이 나열되어 있어야 하며, 호출 사용자의 정책에 allowAppOnly가 있어야 합니다. 허용 목록에 있지만 호출자에게 allowAppOnly가 없는 사서함은 단순히 위임으로 대체되어 Exchange가 응답하도록 합니다. 앱 전용으로 승격해도 두 사서함 게이트를 건너뛰지 않습니다. resolveActor는 앱 전용 분기를 고려하기 전에 이를 적용하므로 로그인한 사용자는 여전히 승인 페이지에서 주소를 승인해야 합니다. 일반적인 앱 전용 호출자는 API 키 서비스 계정이며, 이 계정은 요청을 받지 않으며 allowedMailboxes가 전체 제어입니다.

O365_APP_ONLY_MAILBOXES는 제어의 절반에 불과하며, 더 약한 절반입니다. 이 코드가 요청할 내용을 제한합니다. 자격 증명 자체에는 아무런 영향을 미치지 않습니다. 자격 증명을 얻는 사람은 테넌트의 모든 사서함에 도달할 수 있습니다. 실제 제어는 Exchange RBAC for Applications이며 테넌트 측에서 적용됩니다. deploy/entra/scope-app-only.ps1이 이를 스크립트로 작성합니다: Exchange에서 서비스 주체를 등록하고, 메일 사용 보안 그룹에 대한 관리 범위를 만들고, 해당 범위로 제한된 Application Mail.* 역할을 할당하고, Test-ServicePrincipalAuthorization으로 확인합니다.

전체를 무너뜨리는 함정: RBAC 권한은 Entra 권한과 가산적입니다. 범위가 지정되지 않은 애플리케이션 권한이 앱 등록에 동의된 상태로 남아 있으면 두 권한의 합집합이 적용되며 범위 지정은 아무 효과가 없습니다. Entra 애플리케이션 권한을 제거해야 합니다. 또한 권한 캐시에 대한 예산도 고려하세요. 변경 사항이 적용되는 데 30분에서 2시간이 걸립니다 (Test-ServicePrincipalAuthorization은 캐시를 우회하므로 스크립트가 이로 끝나는 이유입니다).

Teams에는 여기 앱 전용 경로가 전혀 없습니다 — 아래를 참조하세요.

사용자별 도구 권한

사용자 저장소가 구성되면 (MCP_USERS_TABLE 또는 개발용 MCP_USERS_FILE), 모든 사용자 레코드에는 policy가 포함됩니다:

필드

의미

allowedTools

"*"은 향후 출시될 도구를 포함한 모든 것을 부여합니다 — 관리자 및 서비스 계정을 위한 전체 액세스 설정입니다. 배열은 고정 허용 목록입니다.

toolPermissions

도구별 {name: boolean} 맵. 기본 거부: 도구는 해당 항목이 true일 때만 호출할 수 있습니다. allowedTools 배열을 대체합니다. "*"은 여전히 우선합니다.

allowWrites

변경으로 표시된 모든 도구에 대해 추가로 필요합니다.

grantedMailboxes

연결 시점에 사용자가 사서함 승인 페이지에서 승인한 내용입니다. 없으면 사용자에게 요청한 적이 없으며 자신의 사서함만 도달할 수 있습니다. 관리자 CLI가 아닌 /oauth/consent에 의해 기록됩니다.

allowedMailboxes

그 위에 관리자의 상한선: "*" (기본값), 명시적 주소 목록, 또는 자신의 사서함만 null. 유효한 액세스는 grantedMailboxes와의 교집합입니다. 승인 페이지를 볼 수 없는 API 키 ID의 경우 이것이 전체 제어입니다.

allowAppOnly

앱 전용 행위자에 대한 사용자별 게이트. 기본값 false이므로 부주의하거나 손상된 사용자가 자신의 Exchange 권한을 넘어 조용히 승격할 수 없습니다.

rateLimitPerMin

사용자별 호출 상한. 기본값 60.

disabled

ID를 삭제하지 않고 비활성화합니다.

사용자가 호출할 수 없는 도구는 tools/list에서도 숨겨지므로 모델이 볼 수 없습니다. 모든 OAuth 로그인 시 맵은 라이브 레지스트리와 조정됩니다. 새로 출시된 도구는 false로 추가되므로 새롭고 잠재적으로 파괴적인 도구가 조용히 부여되지 않습니다. 제거된 도구는 정리됩니다. 저장소는 변경 사항이 있을 때만 기록됩니다.

새로 프로비저닝된 사용자는 모든 읽기 전용 도구가 활성화되고 모든 변경 도구가 비활성화됩니다.

관리자 CLI

npm run user -- list
npm run user -- add alice --writes --mailboxes=support@contoso.com --app-only
npm run user -- tools alice                      # the effective 27-tool map
npm run user -- tools alice --enable=o365_mail_send
npm run user -- rotate alice                     # new API key, old one dead
npm run user -- disable alice
npm run connection -- list                       # who is connected, scopes, last refresh — never token material
npm run connection -- test alice@contoso.com     # one live Graph call, proves the stored credential still redeems
npm run connection -- revoke alice@contoso.com   # server-side kill switch: delete the row, purge cached tokens
npm run connection -- rewrap                     # re-seal every stored secret under the current encryption key

connection revoke이 서버가 자격 증명을 사용하는 것을 중지합니다. 권위 있는 테넌트 측 킬 스위치는 Entra의 사용자 개체에 대한 세션 해지입니다. 비밀번호 변경만으로는 기밀 클라이언트 새로 고침 토큰이 무효화되지 않습니다 (SECURITY.md의 해지 매트릭스 참조).

제한 사항 및 알려진 문제

  • AWS에서 WWW-Authenticate 챌린지 헤더의 이름이 바뀝니다. Lambda 함수 URL은 이를 x-amzn-Remapped-WWW-Authenticate로 다시 작성하며, 함수 내부에서 이를 막을 수 있는 방법은 없습니다. 이는 검색을 깨뜨리지 않습니다. MCP 사양은 클라이언트가 /.well-known/oauth-protected-resource/mcp (그 다음 루트 변형)을 직접 가져오도록 요구하며, 참조 SDK는 401에서 무조건 그렇게 합니다. 이것이 이 서버가 두 문서를 모두 제공하고 MCP 엔드포인트 URL과 바이트 단위로 동일한 resource 값을 보고하는 이유입니다. 헤더가 정말 필요한 클라이언트를 만난다면, CloudFront를 앞에 두고 Lambda@Edge origin-response 함수를 사용하여 다시 매핑된 이름을 복사하십시오. viewer-response 함수는 작동하지 않습니다. CloudFront는 원본이 400 이상을 반환할 때 해당 함수를 호출하지 않기 때문입니다.

명확히 말하면, 대부분의 경우 그렇지 않으면 버그로 경험될 것이기 때문입니다.

새로 고침 토큰은 무작위로 보이는 방식으로 만료됩니다. 90일 창은 슬라이딩 비활성이며 고정 만료가 아닙니다. 매주 연결하는 사용자는 사실상 만료되지 않지만, 91일 동안 조용히 있던 사용자는 죽은 상태로 돌아옵니다 (AADSTS70008 / 700082). 독립적으로, 조건부 액세스 로그인 빈도는 자체 주기로 재인증을 강제하며 서버 측 코드로 이를 막을 수 없습니다. 관리자가 Entra 또는 Microsoft 365 관리 센터를 통해 비밀번호를 재설정하면 토큰이 즉시 해지됩니다. 사용자가 자신의 비밀번호를 변경하는 경우에는 그렇지 않습니다. 이 모든 것은 재연결 URL을 포함한 단일 명확한 도구 오류로 표시됩니다.

클라이언트 비밀 만료는 절벽이지 경사가 아닙니다. Entra는 비밀 수명을 24개월로 제한하며, 만료되면 배포의 모든 사용자가 동시에 AADSTS7000222로 실패합니다 — 점진적으로도, 하나씩도 아닙니다. 인증서 자격 증명은 이 실패 모드를 피합니다. 날짜가 오기 전에 회전시키십시오.

암호화 키를 잃으면 복구할 수 없습니다. 키가 없으면 저장된 연결이 없으며 모든 사용자가 동시에 다시 로그인해야 합니다. 별도로 백업하고 O365_TOKEN_ENC_KEYS_PREVIOUS + connection rewrap을 통해 회전시키십시오. 교체로는 절대 안 됩니다.

Teams 보내기는 영구적으로 위임 전용입니다. 모든 Graph 보내기 엔드포인트는 유일한 애플리케이션 권한으로 Teamwork.Migrate.All을 제공하며 Microsoft는 이를 마이그레이션 시나리오로 제한합니다. 이 아키텍처에서 서비스 계정으로 Teams 메시지를 게시하는 규정 준수 방법은 없습니다. 대안은 Bot Framework 봇 또는 팀별로 설치된 리소스별 동의가 있는 Teams 앱 패키지이며, 둘 다 독립형 원격 MCP 서버에는 적합하지 않습니다. 무인 Teams 게시는 테이블에 없습니다. (Teams 계량은 문제가 아닙니다. 모델 A / 모델 B 청구 체제는 2025년 8월 25일에 종료되었습니다. 대부분의 기존 문서가 여전히 말하는 것과는 달리.)

Teams 채널 범위는 테넌트 관리자 동의가 필요합니다. 특히 ChannelMessage.Read.All은 자체 동의가 불가능합니다. 관리자 권한 없이 O365_SCOPE_PROFILE=personal로 셀프 호스팅하는 경우 메일, 파일, 채팅은 정상 작동하지만 채널 도구는 알 수 없는 403 대신 설명과 함께 실패합니다. personal은 또한 User.ReadBasic.All을 생략하므로 대화 외부에 있는 사람을 @멘션해도 해당 인물을 확인할 수 없습니다. 메시지는 여전히 전송되고 이름은 본문에 일반 텍스트로 남으며, 도구는 해당 인물에게 알림이 전달되지 않았다는 경고를 반환합니다.

사서함 승인 페이지는 과소 보고만 하며 과대 보고는 하지 않습니다. 사서함 사용 가능 여부는 해당 사서함의 받은 편지함을 열어보는 방식으로 결정되며, 403이 발생하는 원인은 세 가지입니다. Send As 권한만 있거나 전체 사서함이 아닌 특정 폴더에만 접근 권한이 있는 경우, 해당 주소는 사용 불가로 표시되어 체크할 수 없습니다. 더 좁은 접근 권한으로도 원하는 작업이 가능했을 텐데도 말입니다. 반대의 실수는 발생할 수 없습니다. 체크할 수 있는 주소는 서버가 실제로 열 수 있는 주소이기 때문입니다.

검색에는 데이터 손실처럼 보이는 상한선이 있습니다. Outlook $search는 최대 1,000개의 결과를 반환하며 필터나 사용자 지정 정렬과 결합할 수 없습니다. Teams 검색은 총계가 아닌 페이지 수를 보고하므로 일치 항목 수로 표시할 수 없습니다. SharePoint 딥 페이징은 결과 1,000개를 넘어서면 중단되며, 앱 전용 검색은 기본적으로 개인 OneDrive 콘텐츠를 제외합니다. 이를 활성화하면 새 인덱스가 프로비저닝되는데 며칠에서 일주일이 걸릴 수 있으며, 그동안 오류 없이 결과가 조용히 불완전하게 유지됩니다.

테넌트 공유 정책은 o365_files_share가 생성하는 결과를 조용히 다시 작성합니다. 조직 수준 및 사이트별 설정은 익명 링크를 조직 전용으로 격하하거나, 만료를 강제하거나, 링크를 보기 전용으로 만들 수 있습니다. 더 나쁜 것은 createLink가 (애플리케이션, 링크 유형)별로 멱등적이라는 점입니다. 따라서 새로운 7일 링크를 요청해도 범위가 다른 수년 전의 만료 없는 링크가 반환될 수 있습니다. 도구는 항상 실제 부여된 권한을 읽어서 보고하며, 이것이 유일한 방어 수단입니다.

메시지 ID는 메시지가 이동하면 변경되며, Teams ID는 전역적으로 고유하지 않습니다. O365_IMMUTABLE_IDS는 정확히 이러한 이유로 기본적으로 켜져 있지만, 사실상 일방통행입니다. 한 형식으로 발급된 ID는 다른 형식에서 작동하지 않으며, 라이브 스택에서 이를 켜면 ErrorInvalidIdMalformed가 발생합니다. 별도로, Teams 메시지 ID는 해당 채팅 또는 채널 내에서만 고유하므로 메시지 ID는 항상 대화 좌표와 함께 반환됩니다.

일상적인 실패의 가장 큰 원인은 스로틀링입니다. Outlook은 (애플리케이션, 사서함)당 동시 요청 4개와 10분당 10,000개를 허용합니다. Teams는 채널, 채팅, 사용자당 초당 약 1회의 요청을 허용합니다. SharePoint는 권한 호출당 리소스 단위 5개를 부과하며 검색은 Graph의 나머지 부분보다 훨씬 더 엄격하게 스로틀링합니다. 일괄 처리는 도움이 되지 않습니다. Graph는 배치에서 Outlook으로 최대 4개의 하위 요청만 동시에 전달합니다. 서버는 자체 팬아웃을 제한하고 Retry-After를 정확히 준수하지만, 적극적인 에이전트는 결국 429를 만나게 됩니다.

3MB를 초과하는 첨부 파일은 공유 사서함에서 작동하지 않습니다. Microsoft는 위임된 호출자가 공유 또는 위임된 사서함의 메시지에 대용량 파일을 첨부하면 403을 받는다고 문서화하고 있습니다. 3MB 미만은 문제없습니다. 도구는 단순한 403을 표시하는 대신 이 사실을 알려줍니다.

소버린 클라우드에는 조용히 누락된 기능이 있습니다. 영구 삭제, 채팅 델타 및 Teams 내보내기 API는 미국 정부 L4/L5와 중국 21Vianet에서 사용할 수 없으며, 교차 지역 사이트 접근은 앱 권한과 무관한 이유로 실패할 수 있습니다. 멀티 지역 테넌트는 지역별로 검색 요청이 하나씩 필요하며, 그렇지 않으면 다른 지역의 콘텐츠가 조용히 누락됩니다.

리프레시 토큰 순환 경쟁은 무해하지만 실제로 발생합니다. Entra는 모든 교환에서 새 리프레시 토큰을 발급하고 이전 토큰을 폐기하지 않으므로, 동일한 사용자에 대한 두 개의 동시 호출은 각각 유효한 후속 토큰을 받습니다. 조건부 쓰기로 인해 하나가 승리하고 패자는 자신의 복사본을 폐기합니다. 경쟁에서 진다고 해서 도구 호출이 실패하지는 않습니다. 액세스 토큰 캐시 덕분에 이런 일이 드물게 발생합니다.

로드맵

다음은 간과된 것이 아니라 의도적인 v1 범위 축소입니다:

  • 캘린더. 메일 다음으로 누구나 기대하는 두 번째 기능입니다. Calendars.ReadWrite는 사용자 동의가 가능하며, 공유 사서함을 위해 이미 구축된 액터 모델이 공유 캘린더에도 그대로 적용됩니다. 다음 순서로 진행됩니다.

  • 파일 업로드 — OneDrive 및 SharePoint로의 업로드. v1은 검색, 목록, 가져오기, 다운로드 및 공유를 다룹니다.

  • 연락처 / 사람 조회 — 이름을 이메일 주소로 확인하는 기능. 현재 모든 전송 도구는 호출자가 이미 이메일 주소를 보유하고 있다고 가정합니다.

  • 받은 편지함 규칙 (messageRules, MailboxSettings.ReadWrite 필요) — 서버 측 분류 자동화용.

추가 검토 중인 사항: stderr를 넘어선 플러그형 감사 싱크(Firehose → S3 → Athena), AWS에서 장기 보안 비밀번호가 전혀 존재하지 않도록 하는 세 번째 클라이언트 자격 증명 유형으로서의 워크로드 ID 페더레이션, 그리고 원시 @odata.nextLink 문자열 대신 서버에서 발급하는 불투명 페이지 핸들.

디렉터리 관리는 의도적으로 범위에서 제외됩니다. Microsoft의 무료 MCP Server for Enterprise가 이미 읽기 전용 Entra 쿼리를 다루고 있기 때문입니다.

기여

CONTRIBUTING.md를 참조하세요. 보안 문제: SECURITY.md — 공개 이슈를 열지 마십시오.

라이선스

MIT. LICENSE를 참조하세요.

-
license - not tested
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/LotzerDigital/aws-office365mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server