office365-mcp
office365-mcp-server
Un servidor MCP remoto multiusuario para Microsoft 365 — Outlook, Teams y SharePoint/OneDrive a través de Microsoft Graph. Cada usuario se conecta con su propia cuenta de Microsoft mediante un inicio de sesión normal en el navegador; el servidor conserva esa autorización, cifrada, y actúa como ese usuario en cada llamada posterior a una herramienta, de modo que una conexión realizada una sola vez sigue funcionando sin que el cliente tenga nunca un token de Microsoft. Los buzones compartidos y de servicio (support@, billing@, info@) son una identidad de primera clase, no un parámetro añadido a un puñado de herramientas.
Una única base de código TypeScript, tres destinos de despliegue:
Plataforma | Entrada | Compilación / despliegue |
AWS Lambda (Function URL) |
|
|
Valida la configuración sin tocar AWS primero:
DRY_RUN=1 npm run deploy:lambda| Azure Functions (v4 Node) | src/entries/azure.ts | npm run build:azure && func azure functionapp publish … |
| Node básico (desarrollo / autoalojado) | src/entries/node.ts | npm run dev |
En qué se diferencia de los otros servidores MCP de Microsoft 365
El panorama del código abierto es amplio y varios proyectos son buenos. Este está construido en torno a un modelo de despliegue e identidad diferente, no a una mayor cobertura de Graph.
El servidor actúa como intermediario de tokens de actualización; el cliente nunca ve un token Microsoft. La mayoría de los servidores existentes guardan un único token para un único usuario en una única máquina (
~/.outlook-mcp-tokens.json,~/.microsoft_mcp_token_cache.json,~/.office-mcp-tokens.json) en texto plano. El único servidor remoto maduro (elms-365-mcp-serverde Softeria, en modo HTTP) declara explícitamente que la renovación del token es responsabilidad del cliente, por lo que una sesión muere cuando el token de acceso de Graph expira aproximadamente a la hora. Aquí, el token de actualización de Entra de cada usuario está sellado con AES-256-GCM y almacenado en el servidor, y el servidor renueva silenciosamente los tokens de acceso en nombre del usuario.Es stateless y con forma serverless. No hay fijación de sesión SSE, no hay proceso de larga duración; todo el estado de sesión y credenciales está en DynamoDB. Las alternativas que soportan HTTP asumen un contenedor siempre encendido (Express, Azure Container Apps, un backend de App Service detrás de un shim local de stdio).
Los buzones compartidos están modelados, incluido el camino de solo aplicación. Cuando la persona código de llamadas tiene derechos de Exchange, el servidor usa su propio token delegado contra
/users/{mailbox}; cuando nadie se ha autenticado en el buzón, puede usar credenciales de aplicación con ámbito delimitado. Ningún otro servidor de código fuente ofrece esa segunda vía controlada, y el ámbito del lado de Exchange se incluye aquí como script (.github/entra/scope-app-only.ps1) en lugar de quedar como tarea para el usuario.El usuario elige los buzones que el asistente puede usar. Entra no dispone de un consentimiento por buzón para
Mail.*.Shared: conceder esos alcances produce un token que puede abril todos los buzones que Exchange permitan a esa específica persona, y Microsoft no ofrece forma de acotarlo. Por eso, tras el inicio de sesión viene una página de aprobación servida por este servidor, y lo que el usuario marca allí se aplica en el servidor de cada solicitude. Consulta Aprobación de buzones.Existe una línea de gobernanza. Con listas de herramientas permitidas para cada usuario, con denegación predeterminada para herramientas nuevas, límites de frecuencia por usuario, un máx acumulado administrador de buzones por encima de la propia aprobación del usuario, un bloqueo a nivel de despliegue para las eliminaciones irreversibles, y un registro de auditoría estructurado para cada llamada que incluye el usuario, la herramienta, los argumentos y el buzón realmente variado.
La superficie de herramientas es deliberadamente pequeña. 27 herramientas con forma de tarea, no 300 con forma de endpoint. La amplitud no es el terreno en el que este servidor compite: Softeria cubre rangos de Excel y páginas de SophiaLetters, y los propios servidores Microsoft Work IQ tienen búsqueda semántica y rastres de grado Defender. Lo que ninguno de estos ofrece es un servidor que hables mismo, en tu propia región, sin licencia de Microsoft 365 Copilot.
Existen dos opciones integradas que merece la pena conocer. El MCP Server for Enterprise de Microsoft es gratuito, pero solo lectura y limitado a los datos de directorio de Entra, un complemento, no un competidor. Lo que Agent 365 / Work IQ sí cubre el correo, calendario, Teams y SharePoint, pero es una vista previa, solo puede adoptarse en el cloud de Microsoft y requer. licencia de Microsoft 365 Copilot.
Inicio rápido
1. Registra la aplicación en Entra. Este es el paso que suele salir más veces mal. Sigue `(deploy/entra/SETUP.md); en particular, registra la plataforma como aplicación Web, no como SPA (una URI de redirección de SPA limita a 24 horas los tokens de actualización, y una caducidad se tira de la un token heredado, lo que destruiría la promesa de conectar una sola vez).
2. Genera las dos claves. Son claves distintas y con funciones distintas, y ninguna puede sustituir a la otra.
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 MicrosoftTPM de text: Respaltes la salida de gen:enc-key en un administrador de secretos, e a del despliegue. Si estás pierde, cada conexión almacenada queda sin remedio y se obliga de vivolved a todos los usuarios a iniciar sesión de nuevo a misma.
3. Despliega.
npm run build:lambda
npm run deploy:lambda # wraps `sam deploy` against deploy/aws/template.yamlEl saber pila imprime un resultado EntraRedirectUri. Registra esa URI exacta en el registro de la aplicación: es el único paso que no se puede automatizar.
4. Conecta tu cliente. Añade <https://your-deployment>/mcp como un conector personalizado. El cliente descubre los endpoints OAuth en /.well-known/oauth-protected-resource, se se guarda y envía al usuario al inicio de sesión de Microsoft. La pantalla de consentimiento de Microsoft es seguida por la página de aprobación de buzones de este servidor, donde el usuario elige qué buzones compartidos puede utilizar el él; el cliente no recoge su token hasta después. Entonces llama a o365_whoami: la llamada the estado de la conexión, los permisos concedidos, los buzones que han has hibite, y qué herramientas medida. call actually.
Para el desarrollo local, pon las variables de En que se la mejor en en un archivo .env, como mínimo el registro de Entra, las dos claves y MCP_USERS_FILE + MCP_GRAPH_FILE + MCP_OAUTH_FILE** para los almacenes con archivos, which es el upthat allows running:npm run dev runs actual del navegador y la aprobación del buzón without AWS. [.env.example`](.env.example) es la versión anotada. A continuación:
npm run dev # http://localhost:3000/mcpAñade http://localhost:3000/oauth/callback como una seconduna URI de redirección en el registro de la aplicación.
Arquitectura
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 clientTransporte. HTTP Streetable, modo sin estado. JSON-RPC 2.0 en POST /mcp, un mensaje por solicitud. El agrupamiento JSON-RPC rechaza con -32600 porque se ha outerrado en MCP en la revisión del día 2025-06, config y porque un lote de llamadas correría en un solo cupo del límite de frecuencia. Una solicitud no autenticada recibe lo un HTTP con el prestaciones WWW-Authenticate: Bearer realm="mcp", resource_metadata=… la RFC 9728, lo que la causa que el cliente inicia el flujo de OAuth del conector. El documento de recurso protegido se sirve en /.well-known/oauth-protected-resource y /.well-known/oauth-protected-resource/mcp, y notifica resource como {origin}/mcp — el endpoint que el usuario really escribe, que es contra e-lo que un cliente compare.
El modelo de identidad
Son **dos relaciones OAuth **, no seens separate, distinct, and keep them separate:
Cliente MCP ↔ este servidor. Nosotros somos el servidor de autorización. El cliente se registra dinámicamente (RFC 7591), ejecuta el flujo de autoridad
authorCode+ PKCE contra/oauth/autorizadoand/oauth/bodega, y recibe un JWT RS256 que hemos firmado nosotros. Entra no autoriza dinámicamente el registro de clientes y no ve este intercambio.Este servidor ↔ Entra. Somos clientes con una única URI de redirección Web estática. Durante el inicio de sesión del usuario ejecutamos nuestra propia cadena PKCE individual contra Entra, canjedel código con el secreto oede tu cliente, y recibimos un
id\_token, un token de acceso a Graph y —como solicitamosoffline_access— un token de actualización.
Ese token de actualización es el producto. Se sella con AES-256-GCM bajo datos adjuntos autenticados vinculados a la fila ({tid}:{oid}:refresh), de manera que un blob extraado de el registro de un usuario no puede retroalimentar otro, y se guarda en una tabla que solo manipulacredenciales. Cada llamada posterior a una herramienta sigue el camino: JWT → (tid, oid) → token de acceso en caché, o (un disposableción y canje* de actualización → Graph. La clave del usuario es el par inmutable (tid, oid) del id\_token, nunca correo ni preferred\_username ni `upn', since son mutables and manageable.
En medio, el /oauth/callback se detiene. Una vez guardada la autorización de Microsoft, no entrega su código de autorización al cliente MCP; redirige la navegador a /oauth/consent con un ticket de uso único, y el usuario elige los buzones a los que le adelante este asistente. Solo tras enviar esa página se maestro en el código redefine y se redirige al cliente a su origen. Consulta Aprobación de buzones.
La lista de direcciones permitidas de tenant (O365_ALLOWED_TENANTS) se vuelve a verificar en cada solicitud, no solo en el inicio de sesión; por lo que al eliminar un inquilino tiene efecto de inmediato, y la próxima conexión no gesta en espera.
Delegado vs solo Aplicación
Cada llamada a Graph del estado firmaun actor *prueba, determinístico's, en la configuración: no hay in a ser**a será la escalation del servidor.
Actor | Dirección | Cuándo (Cuándo) | Ve |
|
| Sin argumento | Exactamente lo que ve el usuario que inició la sesión |
|
| Un buzón diferente, aprobado por el usuario en el momento de conectar, permitido por la política del ;cuando la persona tiene derechos de Exchange sobre él | Lo que Exchange ha concedido a ese usuario en ese buzón |
|
| El buzón está en | Lo que Exchange RBAC otorga como scall opcional a la aplicación |
Todo lo que sea no delegated-self debe cumplir policyAllowsMailbox precedentemiente, la aprobación del usuario primero, luego el techo del administrador — antes de pedir nada a Graph. La vía delegada es la opción por defecto y la vía normal: superesas esas dos puertas, los permits existentes del okupan son la verdadera puerta, y una llamada no aceptable responderá con un 403 honesto que el servidor traducir como decirle a quien llama “pide a un administrador el acceso de Full Access a ese buzón”. La forma solo aplicación existe para los buzones a los que u-yunga inicia sesión; viene off by default and it lays into full. /me never significa un buzón para compartir — no existe /me que conduzca aunado — y la. token de solo aplicación no puede usarlo jamás porque no hay ninguna usuario de sesión.
Teams es una sola institución por diseño, de forma permanente. Consulta Limitaciones.
Herramientas expuestas al modelo
27 tools. W marks a tool that changes tenant estado. Esas herramientas are disabled by default for a new usuario and se configure additionally require allowWrites en su política. Cada herramienta de Outlook contiene un argumento opcional mailbox (un UPN o dirección SMTP) de elección el buzón sobre el que desea actuartes; omite para el suyo. Todos los ids que los kale are opaque Microsoft Graph ids — pásalos de vuelta tal cual, y nunca construyas uno.
Outlook — lectura
Herramienta | Qué hace |
| Realiza una búsqueda de texto completo en un buzón mediante la sintaxis de búsqueda de Outlook ( |
| Lista los mensajes de una carpeta con filtros estructurales y ordenación — solo no leídos, un remitente, un rango de fechas, un orden. La contrapartida de |
| Un mensaje completo, con el cuerpo como texto sin formato y, opcionalmente, con los encabezados de mensaje de Internet y los metadatos de los adjuntos. Los cuerpos largos se truncan y se informa de la longitud original. |
| Lista las carpetas de correo, de nivel superior o todo el árbol, con recuentos de mensajes y de no leídos. Úsalo para resolver un id de carpeta antes de mover mensajes. |
| Nombres, tipos, tamaños e indicadores de en línea de los adjuntos de un mensaje. Solo metadatos: nunca el contenido de los archivos. |
| Descarga un adjunto y devuelve una URL prefirmada de corta duración. Nunca devuelve bytes en línea. |
Outlook — escritura
Herramienta | Qué hace |
W | Envía un mensaje inmediatamente, redactado directamente o desde un borrador. Graph lo acepta para su entrega y no devuelve ningún id, por lo que esto informa de "accepted", no de "delivered". |
W | Responde, responde a todos o reenvía un mensaje existente en un solo paso. Tu texto se coloca encima del original citado. |
W | Crea un borrador sin enviar, desde cero o como respuesta o reenvío que ya cita el original. Devuelve el id del borrador. |
W | Edita el asunto, el cuerpo o los destinatarios de un borrador sin enviar. Solo funciona con borradores. |
W | Mueve o copia un mensaje a otra carpeta. Mover cambia el id del mensaje — se devuelve el nuevo y el antiguo deja de funcionar. |
W | Elimina mensajes: |
W | Marca como leído/no leído, marca con bandera, categoriza y establece la importancia en hasta 20 mensajes a la vez. |
W | Crea, renombra, mueve o elimina una carpeta de correo. |
W | Adjunta un archivo a un borrador. Por debajo de 3 MB, en línea; hasta 150 MB mediante una sesión de carga por fragmentos. |
Teams
Herramienta | Qué hace |
| Tus equipos, o los canales de un equipo. La forma de resolver un nombre de equipo o canal en los ids que necesitan las demás herramientas de Teams. |
| Tus chats — individuales, de grupo y de reunión —, primero los más recientes. Los chats individuales no tienen nombre propio, por lo que se genera uno a partir de los participantes. |
| Lee mensajes de un canal o de un chat. Los chats admiten un rango de fechas; los canales no, porque la API de canales de Graph no acepta ningún filtro de fecha. |
W | Publica como tú mismo en un canal, en un hilo de canal o en un chat, incluso a personas por correo electrónico, lo que busca o crea el chat individual. No acepta ningún argumento |
| Búsqueda por palabras clave en todos los chats y canales que puedes ver. La única forma de buscar en Teams; las API de listado no tienen búsqueda alguna. Tampoco acepta ningún argumento |
SharePoint y OneDrive
Herramienta | Qué hace |
| Busca archivos en SharePoint y OneDrive, o dentro de un sitio o biblioteca. Admite términos KQL ( |
| Encuentra sitios, o lista las bibliotecas de documentos de un sitio y sus ids de drive. El punto de partida para trabajar con SharePoint. |
| Lista una carpeta en OneDrive o en una biblioteca de documentos, por id de drive e id de elemento, por ruta, o la raíz de tu propio OneDrive. |
| Detalles de un archivo o carpeta — incluidos a partir de una URL de uso compartido pegada, que la herramienta resuelve. Opcionalmente informa de quién tiene acceso. |
| Descarga un archivo como URL prefirmada, opcionalmente convertido a PDF antes de devolverlo. Nunca en línea. |
W | Comparte mediante un enlace o invitando a personas. Informa del acceso realmente concedido, porque la política de uso compartido del inquilino puede degradar silenciosamente lo que has solicitado. |
Núcleo
Herramienta | Qué hace |
| Con qué identidad has iniciado sesión, si la conexión con Microsoft está activa y cuándo se actualizó por última vez, qué permisos se concedieron, qué herramientas tienes, qué buzones compartidos has aprobado y puedes usar realmente ahora mismo y, para cada uno de ellos, si una llamada se ejecutaría como tú o como la cuenta de servicio. Llámalo primero cuando algo falle. |
Configuración
Todo se configura exclusivamente mediante variables de entorno. La lista de referencia es el tipo Env en src/config.ts.
Registro de aplicación de Entra
Variable | Requerido | Descripción |
| sí | GUID del inquilino o dominio verificado. Cada llamada se dirige a este inquilino concreto y esperado: |
| sí | Identificador de la aplicación (cliente). El registro debe usar el tipo de plataforma Web. |
| uno de | Secreto de cliente. Es el método más simple, pero Entra limita su vigencia a 24 meses. |
| uno de | Clave privada PEM PKCS#8 (literal o en base64) para la autenticación de cliente con certificado. Preferido en producción. |
| con certificado | Huella digital SHA-1 en hexadecimal que se muestra en el portal. Entra asocia la aserción al certificado mediante la huella, por lo que se requieren ambas partes. |
| Identificadores de inquilinos permitidos, separados por comas, cuando se ejecuta en modo multi-tenant. Se verifica al iniciar sesión y de nuevo en cada solicitud; así, eliminar un inquilino aquí bloquea sus conexiones existentes de inmediato, en lugar de esperar a su próximo inicio de sesión. Vacío con |
Claves
Variable | Requerido | Descripción |
| sí | PEM PKCS#8 en base64 de la clave que firma nuestros tokens de acceso MCP. |
| sí | PEM SPKI en base64 de la misma clave pública correspondiente. Se publica en |
| Identificador de clave en el JWKS y en las cabeceras del token. Se cambia a la vez que se rota el par de claves. El valor predeterminado es | |
| sí |
|
| Claves retiradas separadas por comas, con el mismo formato, aceptadas únicamente para descifrado. Así la rotación es una operación continua, en lugar de un cambio brusco. |
Almacenamiento
Variable | Requerido | Descripción |
| sí¹ | Tabla de DynamoDB para tokens de actualización sellados (sin TTL) y tokens de acceso en caché (con TTL). Deliberadamente separada de la tabla OAuth para que las credenciales tengan su propio límite de IAM y su propia política de copias de seguridad. |
| Alternativa de archivo JSON para autoalojamiento y desarrollo. Se ignora si | |
| sí¹ | Tabla de DynamoDB para el estado OAuth propio: clientes registrados, estados de inicio de sesión, códigos de autorización y refrescos de token. TTL en |
| Alternativa de archivo JSON para el mismo estado, de modo que | |
| Tabla de DynamoDB de usuarios y políticas, con los GSI | |
| Almacén de usuarios en JSON para autoalojamiento y desarrollo. No se usa si | |
| Bearer heredado de un solo administrador que evita el almacén de usuarios. Útil para pruebas de humo. No tiene una conexión Graph propia, así que las herramientas de Graph devolverán un error de reconexión a menos que el secreto se asigne a un usuario que haya iniciado sesión. |
¹ o la variante _FILE correspondiente (MCP_GRAPH_FILE / MCP_OAUTH_FILE) para desarrollo local.
Comportamiento de Graph
Variable | Valor predeterminado | Descripción |
|
|
|
| derivado | Anulación global separada por espacios. Se valida al cargar: |
|
| Cambie únicamente a nubes soberanas, donde varias de las funciones usadas aquí no existen. |
|
| Envía |
|
| Envía |
|
| Tiempo de espera por petición, bien por debajo de los 300 segundos de timeout de la herramienta del cliente. |
|
| Límite de solicitudes en vuelo por (aplicación, buzón). Exchange permite exactamente cuatro; el valor se fija porque baja el máximo, reducir solo convierte el rendimiento en errores 429. |
|
| Microsoft da menos prioridad al tráfico no decorado. Mantenga la estructura documentada y ponga el nombre de su empresa en el campo central. |
| auto | Región de SharePoint ( |
Controles y salida
Var | Default | Description |
|
| Interruptor maestro del modo solo aplicación. Mientras sea |
| vacía | Direcciones de buzón separadas por comas accesibles con credenciales de aplicación. Un comodín se rechaza sin más. |
| vacía | IDs de sitio o URLs separadas por comas accesibles con credenciales de aplicación; se aplica en cada llamada dirigida a un sitio o a una unidad hecha con un actor de solo aplicación. Una lista vacía significa que el modo solo aplicación nunca llega a SharePoint, y una llamada de solo aplicación que no nombre ningún sitio se rechaza en lugar de permitirse. La coincidencia no distingue mayúsculas de minúsculas y es exacta o un prefijo que termina en un límite de ruta |
|
| Bloqueo a nivel de implementación para el modo |
|
|
|
| Bucket S3 para descargas. Todas las herramientas de descarga lo requieren; no hay alternativa base64 por diseño. | |
|
| Tiempo de vida de las URL prefirmadas. Quien tenga una puede obtener el archivo sin autentiarse, así que debes mantenerlo corto. |
|
| Sobrescribe la región del bucket de artefactos. |
| Destino JSONL para los registros de auditoría y seguridad, además de stderr. Para despliegues autoalojados; en Lambda, stderr ya llega a CloudWatch. | |
| sin definir |
|
|
| Puerto de escucha para el punto de entrada simple de Node. No se usa en Lambda ni Azure Functions. |
Acceso a buzones compartidos
Esta es la característica en torno a la que está diseñado el servidor, y la capacidad en sí depende del inquilino, no de este servidor.
Dos condiciones deben cumplirse a la vez para que Graph haga nada en absoluto. La conexión necesita los ámbitos de Graph delegados .Shared (Mail.Read.Shared, Mail.ReadWrite.Shared, Mail.Send.Shared — todos presentes en el perfil del ámbito work) y Exchange Online debe haber concedido al usuario autenticado derechos sobre el buzón de destino. El ámbito solo desbloquea la capacidad; Exchange es el acceso real. Sin la concesión de Exchange, Graph devuelve 403 sin importar lo que se haya consentido.
Un administrador concede uno o más de estos permisos en el Centro de administración de Exchange (Destinatarios → Buzones → el buzón compartido → Delegación):
Permiso | Qué permite | Efecto |
Acceso total | Leer, enumerar, mover, eliminar y redactar en el buzón | Necesario para toda herramienta de lectura y escritura contra ese buzón. También es necesario para que un envío deje una copia en Elementos enviados del buzón compartido. |
Enviar como | Enviar con el buzón compartido como remitente | El destinatario solo ve el buzón compartido. |
Enviar en nombre de | Enviar en nombre del buzón | El destinatario ve "usuario en nombre de buzón compartido". Los usuarios pueden concederse este permiso ellos mismos en Outlook; solo un administrador puede conceder Enviar como. |
Las concesiones pueden requerir hasta una hora en hacer efecto. Un 403 inmediatamente después de una concesión suele ser precisamente eso, y el servidor lo indica en el mensaje de error.
Además de esos dos, este servidor añade otros dos: el usuario debe haber aprobado el buzón al conectarse, y el valor límite de allowedMailboxes del administrador deber permitirlo. Ambos se comprueban antes de que se le pida nada a Graph — ver Aprobación de buzones.
A continuación basta con pasar la dirección: o365_mail_list({ mailbox: "support@contoso.com", unreadOnly: true }). El servidor resuelve el actor de la llamada, llama /users/support@contoso.com/… y nunca a /me — no hay ninguna ruta /me hacia un buzón compartido. La dirección también ha de haber sido aprobada por el usuario en el momento de conectar; ver Aprobación de buzones abajo.
Dos restricciones conviene conocer de antemano. No hay ninguna API de Graph que enumere los buzones sobre los que un usuario tiene derecho de acceso — por eso la página de aprobación verifica una dirección candidata en lugar de mostrar una lista, y por eso el que llama sigue diciendo el buzón en la llamada a la herramienta. Y el usuario autenticado normalmente necesita su propio buzón con licencia, aunque el buzón compartido en sí no necesita licencia.
Para acotarlo aún más. policy.allowedMailboxes es el techo del administrador, por encima de lo que el propio usuario haya aprobado; el acceso efectivo es la intersección de los dos. Su valor predeterminado es "*", que deja la decisión restante a Exchange, donde corresponde. Establézcalo como una lista explícita para una el usuario por debajo de sus derechos de Exchange, o null para rechazar directamente el argumento mailbox:
npm run user -- add alice --mailboxes=support@contoso.com,billing@contoso.comAprobación de buzones
Las direcciones se escriben manualmente. Microsoft no expone ninguna API que liste los buzones que una persona puede abrir, y deducirla de las personas con las que se corresponde el usuario produjo una lista que era mayormente incorrecta — así que la página no adivina. Lo que sí hace es verificar: cada dirección escrita se comprueba contra Exchange antes de poder aprobarla. y se muestra como disponible o no disponible con el motivo.
El buzón propio del usuario autenticado es una entrada normal de esa lista y se puede eliminar. Un asistente creado en torno a un buzón de soporte compartido no tiene por qué leerla el buzón personal del operador, de modo que eso tiene que ser expresable. Para retirarla, toda herramienta de Outlook rechaza una llamada que no nombre otro buzón aprobado; Teams y SharePoint no están afectados, porque ninguno pasa por el control del buzón.
Microsoft no puede restringir esta concesión, así que este servidor lo hace. Entra no ofrece consentimiento por buzón para Mail.*.Shared delegado. En el momento en que un usuario concede esos ámbitos, el token resultante puede abrir todos los buzones que Exchange permita abrir a esa persona, y no hay forma del lado de Microsoft de limitarlo: SharePoint obtuvo Sites.Selected delegado en 2024, Exchange no tiene equivalente ni entrada de hoja de ruta para uno. La página de aprobación que aparece a continuación no es, por tanto, un lujo: es lo único que restringe la concesión, y se aplica en el lado del servidor, en cada solicitud, antes de cualquier llamada a Graph (policyAllowsMailbox en src/users.ts, alcanzado desde resolveActor).
El flujo. /oauth/authorize → inicio de sesión de Entra → /oauth/callback almacena el token de actualización sellado y, en lugar de entregar al cliente MCP su código de autorización, redirige a /oauth/consent con un ticket de un solo uso (15 minutos). La página muestra el buzón propio del usuario — siempre incluido, nunca eliminable — además de los buzones compartidos candidatos, cada uno ya sondeado, de modo que una dirección que no se puede abrir aparece atenuada con el motivo en lugar de ser aceptada y fallar más tarde. El usuario marca lo que este asistente puede usar, y solo entonces se emite el código de autorización y el cliente es redirigido de vuelta. Volver a conectarse ejecuta de nuevo la página con la selección anterior ya marcada, que es también como un usuario elimina un buzón más adelante.
Lo que la prueba no puede ver. Un 403 en la Bandeja de entrada tiene tres causas distintas, y solo una de ellas es «sin acceso en absoluto». Un usuario que solo tiene Enviar como, o al que se le ha dado acceso a una carpeta individual en lugar de a todo el buzón, falla la prueba de la Bandeja de entrada aunque ese acceso más limitado funcionaría para la operación que quiere. La página lo indica donde aparece el buzón; el resumen honesto es que la prueba informa de menos en lugar de informar de más. Nunca produce un falso positivo: una dirección que responde 200 es una que el servidor puede abrir de verdad.
Dos barreras, ambas en el lado del servidor. grantedMailboxes es lo que el usuario aprobó; allowedMailboxes es el techo del administrador. Un buzón solo es accesible cuando aparece en ambas listas, y o365_whoami devuelve esa intersección como usableSharedMailboxes para que el modelo solo vea buzones que de verdad pueda usar. El buzón propio del usuario siempre está permitido y no aparece en ninguna de las dos listas.
Una cuenta de servicio con clave de API nunca ve la página — no hay navegador ni humano al que preguntar —, por lo que la barrera de consentimiento no se le aplica. Esto es deliberado: el administrador que creó la clave es la parte que consiente, y allowedMailboxes gobierna en solitario. La distinción se basa en si la identidad tiene un oid de Entra, es decir, si alguna vez pasó por un inicio de sesión de navegador.
Modo solo aplicación
El modo solo aplicación existe para una situación: un buzón al que nadie inicia sesión, al que nadie tiene acceso delegado, y que un agente debería seguir gestionando. Usa la credencial propia de la aplicación en lugar de la de un usuario, por lo que no hay ningún usuario con sesión iniciada y /me no es válido.
Está desactivado por defecto y debería permanecer desactivado a menos que lo necesites, porque una aplicación con consentimiento de administrador Mail.ReadWrite concede acceso a todos los buzones de la organización. Activarlo requiere tres cosas independientes: O365_APP_ONLY_ENABLED=true, que el buzón esté en O365_APP_ONLY_MAILBOXES, y que allowAppOnly esté en la política del usuario que llama. Un buzón que esté en la lista de permitidos pero cuyo llamador no tenga allowAppOnly simplemente recurre al modo delegado y deja que Exchange responda. Escalar a solo aplicación no se salta las dos barreras de buzones: resolveActor las aplica antes siquiera de considerar la rama de solo aplicación, por lo que un usuario con sesión iniciada todavía tiene que haber aprobado la dirección en la página de aprobación. El llamador típico de solo aplicación es una cuenta de servicio con clave de API, a la que nunca se le pregunta y para la que allowedMailboxes es todo el control.
O365_APP_ONLY_MAILBOXES es solo la mitad del control, y es la mitad más débil. Limita lo que este código va a solicitar. No hace nada sobre la credencial en sí: cualquiera que la obtenga llega a todos los buzones del inquilino. El control real es RBAC de Exchange para Aplicaciones, aplicado en el lado del inquilino. deploy/entra/scope-app-only.ps1 lo automatiza: registrar la entidad de servicio en Exchange, crear un ámbito de administración sobre un grupo de seguridad habilitado para correo, asignar el rol Application Mail.* restringido a ese ámbito y verificar con Test-ServicePrincipalAuthorization.
La trampa que lo echa todo por tierra: las concesiones de RBAC son aditivas con las concesiones de Entra. Si el permiso de aplicación sin ámbito permanece consentido en el registro de la aplicación, se aplica la unión de ambos y la delimitación no logra nada. El permiso de aplicación de Entra debe eliminarse. También hay que prever la caché de permisos: los cambios tardan de 30 minutos a 2 horas en surtir efecto (Test-ServicePrincipalAuthorization evita la caché, por eso el script termina con él).
Teams no tiene ninguna vía de solo aplicación aquí — ver más abajo.
Permisos de herramientas por usuario
Cuando se configura un almacén de usuarios (MCP_USERS_TABLE, o MCP_USERS_FILE para desarrollo), cada registro de usuario incluye una policy:
Campo | Significado |
|
|
| Mapa por herramienta |
| Además, se requiere para toda herramienta marcada como mutadora. |
| Lo que el usuario aprobó en la página de aprobación de buzones al conectarse. Si está ausente, significa que nunca se le preguntó, y solo su propio buzón es accesible. Lo escribe |
| El techo del administrador además de eso: |
| Barrera por usuario para el actor de solo aplicación. Por defecto |
| Límite de llamadas por usuario. Por defecto 60. |
| Inhabilita la identidad sin eliminarla. |
Las herramientas que un usuario no puede llamar también están ocultas de tools/list, por lo que el modelo nunca las ve. En cada inicio de sesión de OAuth, el mapa se reconcilia con el registro en vivo: las herramientas recién publicadas se añaden como false, de modo que una herramienta nueva —posiblemente destructiva— nunca se concede en silencio; las herramientas eliminadas se podan. El almacén solo se escribe cuando algo ha cambiado.
Un usuario recién aprovisionado recibe todas las herramientas de solo lectura habilitadas y todas las herramientas mutadoras deshabilitadas.
CLIs de administración
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 alicenpm 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 keyconnection revoke hace que este servidor deje de usar la credencial. El interruptor de apagado definitivo en el lado del inquilino es Revocar sesiones en el objeto de usuario de Entra; ten en cuenta que un cambio de contraseña por sí solo no invalida un token de actualización de cliente confidencial (consulta la matriz de revocación en SECURITY.md).
Limitaciones y problemas conocidos
En AWS, la cabecera de desafío
WWW-Authenticatese renombra. Una URL de función Lambda la reescribe comox-amzn-Remapped-WWW-Authenticate, y nada dentro de la función puede evitarlo. No rompe el descubrimiento: la especificación de MCP exige que los clientes recurran a obtener/.well-known/oauth-protected-resource/mcp(después la variante raíz) directamente, y el SDK de referencia lo hace incondicionalmente ante un 401 — por eso este servidor sirve ambos documentos y devuelve un valor deresourcebyte-idéntico a la URL de su endpoint MCP. Si te encuentras con un cliente que de verdad necesita la cabecera, pon CloudFront delante con una función origin-response de Lambda@Edge que copie de vuelta el nombre reasignado; una función viewer-response no funcionará, porque CloudFront no las invoca cuando el origen devuelve 400 o superior.
Dicho claramente, porque la mayoría de estas cosas se experimentarán como errores de otro modo.
Los tokens de actualización mueren de maneras que parecen aleatorias. La ventana de 90 días es inactividad móvil, no una caducidad fija: un usuario que se conecta semanalmente nunca caduca de hecho, mientras que uno que permanece inactivo durante 91 días vuelve muerto (AADSTS70008 / 700082). De forma independiente, la frecuencia de inicio de sesión de Acceso condicional obliga a la reautenticación a su propio ritmo y ningún código del lado del servidor puede evitarlo. Un administrador que restablece una contraseña a través del Centro de administración de Entra o de Microsoft 365 revoca los tokens de inmediato; un usuario que cambia su propia contraseña no. Cada uno de estos casos se manifiesta como un único error claro de herramienta que incluye la URL para reconectarse.
La caducidad de un secreto de cliente es un acantilado, no una pendiente. Entra limita la vida del secreto a 24 meses, y cuando caduca, todos los usuarios del despliegue fallan a la vez con AADSTS7000222 — no gradualmente, no uno a uno. Las credenciales de certificado evitan ese modo de fallo; rota cualquiera de ellas con mucha antelación a la fecha.
Perder la clave de cifrado es irrecuperable. Sin clave, no hay conexiones almacenadas, y todos los usuarios deben volver a iniciar sesión a la vez. Haz una copia de seguridad por separado y rótala mediante O365_TOKEN_ENC_KEYS_PREVIOUS + connection rewrap, nunca reemplazándola.
El envío en Teams es solo delegado, permanentemente. Cada endpoint de envío de Graph ofrece Teamwork.Migrate.All como su único permiso de aplicación y Microsoft lo restringe a escenarios de migración. No hay forma conforme de publicar mensajes de Teams desde una cuenta de servicio en esta arquitectura; las alternativas son un bot de Bot Framework o un paquete de aplicación de Teams con consentimiento específico de recurso instalado por equipo, ninguna de las cuales encaja en un servidor MCP remoto independiente. La publicación desatendida en Teams no está sobre la mesa. (La medición de Teams no es una preocupación: el régimen de facturación modelo A / modelo B terminó el 25 de agosto de 2025, a pesar de lo que todavía dice la mayor parte de la documentación existente.)
Los ámbitos de los canales de Teams requieren el consentimiento del administrador del inquilino. ChannelMessage.Read.All, en particular, no puede obtenerse mediante autoconsentimiento. Una persona que se autoaloja sin derechos de administrador y que ejecuta O365_SCOPE_PROFILE=personal obtiene correo, archivos y chat funcionales, y herramientas de canal que fallan con una explicación en lugar de un misterioso 403. personal también omite User.ReadBasic.All, por lo que una mención con @ a alguien fuera de la conversación no se puede resolver: el mensaje se envía de todos modos, el nombre permanece en el cuerpo como texto sin formato y la herramienta devuelve una advertencia de que no se ha notificado a la persona.
La página de aprobación de buzones informa de menos, nunca de más. Decide si puedes usar un buzón intentando abrir su bandeja de entrada, y un 403 allí tiene tres causas. Si solo tienes Enviar como, o acceso a una carpeta en lugar de a todo el buzón, la dirección aparece como no disponible y no se puede marcar, aunque ese acceso más restringido habría servido para lo que querías. El error contrario no puede ocurrir: una dirección que puedes marcar es una que el servidor puede abrir de verdad.
La búsqueda tiene límites máximos que parecen pérdida de datos. La $search de Outlook devuelve como máximo 1.000 resultados y no puede combinarse con filtros ni con ordenación personalizada. La búsqueda de Teams informa de un número de páginas en lugar de un total, por lo que nunca puede presentarse como un recuento de coincidencias. La paginación profunda de SharePoint se detiene más allá del resultado 1.000, y una búsqueda solo de aplicación excluye el contenido privado de OneDrive de forma predeterminada: habilitarla aprovisiona un índice nuevo que puede tardar de días a una semana, periodo durante el cual los resultados están incompletos silenciosamente, sin ningún error.
La directiva de uso compartido del inquilino reescribe silenciosamente lo que produce o365_files_share. La configuración a nivel de organización y por sitio puede degradar un enlace anónimo a solo organización, forzar una caducidad o hacer que los enlaces sean de solo lectura. Peor aún, createLink es idempotente por (aplicación, tipo de enlace), así que una solicitud de un enlace nuevo de siete días puede devolver uno de hace años que nunca caduca y con un ámbito distinto. La herramienta siempre relee e informa de la concesión real, que es la única defensa.
Los id de mensaje cambian cuando los mensajes se mueven, y los id de Teams no son únicos a nivel global. O365_IMMUTABLE_IDS está activado por defecto exactamente por esta razón, pero en la práctica es una puerta de una sola dirección: los id emitidos en un formato no funcionan en el otro, y cambiarlo en una pila activa produce ErrorInvalidIdMalformed. Por otra parte, el id de un mensaje de Teams es único solo dentro de su chat o canal, por lo que los id de mensaje se devuelven siempre con sus coordenadas de conversación.
La limitación de peticiones es el fallo más probable en el día a día. Outlook permite cuatro solicitudes concurrentes por (aplicación, buzón) y 10.000 cada diez minutos; Teams permite aproximadamente una solicitud por segundo por canal, por chat y por usuario; SharePoint cobra cinco unidades de recurso por llamada de permisos y limita la búsqueda mucho más estrictamente que el resto de Graph. El procesamiento por lotes no ayuda: Graph reenvía como máximo cuatro sub-solicitudes de un lote a Outlook de forma concurrente. El servidor acota su propia expansión y respeta exactamente Retry-After, pero un agente impaciente acabará encontrándose con un 429 tarde o temprano.
Los adjuntos de más de 3 MB no funcionan en un buzón compartido. Microsoft documenta que un llamador delegado obtiene un 403 al adjuntar archivos grandes a un mensaje en un buzón compartido o delegado. Por debajo de 3 MB no hay problema. La herramienta lo indica en lugar de mostrar un 403 sin más.
Las nubes soberanas carecen silenciosamente de funciones. La eliminación permanente, el delta de chat y las API de exportación de Teams no están disponibles en US Government L4/L5 y China 21Vianet, y el acceso a sitios entre regiones puede fallar por motivos no relacionados con los permisos de la aplicación. Los inquilinos multi-geo necesitan una solicitud de búsqueda por región o el contenido de otras regiones falta silenciosamente.
Las condiciones de carrera en la rotación del token de actualización son benignas pero reales. Entra emite un nuevo token de actualización en cada canje y no revoca el anterior, por lo que dos invocaciones concurrentes para el mismo usuario reciben cada una un sucesor válido. Una escritura condicional hace que una de ellas gane y la perdedora descarte su copia; una carrera perdida nunca hace fallar una llamada de herramienta. La caché de tokens de acceso es lo que hace que sea poco frecuente.
Hoja de ruta
Estos son recortes de alcance deliberados de v1, no descuidos:
Calendario. Lo segundo que cualquiera espera después del correo.
Calendars.ReadWriteadmite el consentimiento del usuario y el modelo de actor ya construido para los buzones compartidos se aplica sin cambios a los calendarios compartidos. Está previsto como siguiente paso.Carga de archivos a OneDrive y SharePoint. v1 cubre búsqueda, listado, obtención, descarga y uso compartido.
Búsqueda de contactos / personas para resolver un nombre a una dirección de correo electrónico. Toda herramienta de envío asume actualmente que quien la llama ya tiene una.
Reglas de bandeja de entrada (
messageRules, requiereMailboxSettings.ReadWrite) para automatizar la clasificación en el servidor.
También se está considerando: un destino de auditoría conectable (Firehose → S3 → Athena) más allá de stderr, la federación de identidad de cargas de trabajo como tercer tipo de credencial de cliente en AWS, de modo que no exista ningún secreto de larga duración, e identificadores de página opacos emitidos por el servidor en lugar de cadenas @odata.nextLink sin procesar.
La administración de directorios queda deliberadamente fuera del alcance: el MCP Server gratuito de Microsoft para Enterprise ya cubre las consultas de solo lectura de Entra.
Contribuciones
Consulta CONTRIBUTING.md. Problemas de seguridad: SECURITY.md — por favor, no abras una incidencia pública.
Licencia
MIT. Consulta LICENSE.
This server cannot be installed
Maintenance
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
Copilot connector permission audits with owner signoff receipts.
*Updated June 17th 2025** Manage your Microsoft 365 services effortlessly. Create and manage distr…
Governed email for AI agents (Mailbuttons / mbag.ai): sandbox inboxes, policy gate, audit log.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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