Microsoft 365
Interactúa con servicios de Microsoft 365 como Outlook, OneDrive y Teams usando la API de Graph.
Documentación
ms-365-mcp-server
Servidor MCP de Microsoft 365
Un servidor de Protocolo de Contexto de Modelo (MCP) para interactuar con los servicios de Microsoft 365 y Microsoft Office a través de la API de Graph.
Nubes compatibles
Este servidor admite múltiples entornos de nube de Microsoft:
| Nube | Descripción | Punto de conexión de autenticación | Punto de conexión de API de Graph |
|---|---|---|---|
| Global (predeterminado) | Microsoft 365 internacional | login.microsoftonline.com | graph.microsoft.com |
| China (21Vianet) | Microsoft 365 operado por 21Vianet | login.chinacloudapi.cn | microsoftgraph.chinacloudapi.cn |
Requisitos previos
- Node.js >= 20 (recomendado)
- Node.js 14+ puede funcionar con advertencias de dependencias
Características
- Autenticación mediante la Biblioteca de Autenticación de Microsoft (MSAL)
- Integración integral de servicios de Microsoft 365
- Soporte de modo de solo lectura para operaciones seguras
- Filtrado de herramientas para control de acceso granular
- Preajustes de herramientas y descubrimiento dinámico para reducir la superficie de herramientas y el uso de tokens
Formato de salida: JSON vs TOON
El servidor admite dos formatos de salida que se pueden configurar globalmente:
Formato JSON (predeterminado)
Salida JSON estándar con formato legible:
{
"value": [
{
"id": "1",
"displayName": "Alice Johnson",
"mail": "alice@example.com",
"jobTitle": "Software Engineer"
}
]
}
Formato TOON (experimental)
Notación de Objetos Orientada a Tokens para un uso eficiente de tokens de LLM:
value[1]{id,displayName,mail,jobTitle}:
"1",Alice Johnson,alice@example.com,Software Engineer
Beneficios:
- 30-60% menos tokens en comparación con JSON
- Mejor para datos de arreglos uniformes (listas de correos electrónicos, eventos de calendario, archivos, etc.)
- Ideal para aplicaciones sensibles a costos a escala
Uso: (experimental) Habilite el formato TOON globalmente:
Mediante la bandera de CLI:
npx @softeria/ms-365-mcp-server --toon
Mediante la configuración de Claude Desktop:
{
"mcpServers": {
"ms365": {
"command": "npx",
"args": ["-y", "@softeria/ms-365-mcp-server", "--toon"]
}
}
}
Mediante la variable de entorno:
MS365_MCP_OUTPUT_FORMAT=toon npx @softeria/ms-365-mcp-server
Servicios y herramientas compatibles
El servidor proporciona más de 300 herramientas que cubren la mayor parte de la superficie de la API de Graph de Microsoft. Cada herramienta se asigna 1 a 1 a un punto de conexión de la API de Graph y se define de forma declarativa en src/endpoints.json.
Herramientas de cuenta personal (disponibles de forma predeterminada)
Correo electrónico (Outlook), Calendario, Archivos de OneDrive, Excel, OneNote, Tareas de To Do, Planner, Contactos, Perfil de usuario, Búsqueda
Herramientas de cuenta organizacional (requiere la bandera --org-mode)
Teams y Chats, Reuniones en línea, Transcripciones y grabaciones, Informes de asistencia, Sitios y listas de SharePoint, Buzones y calendarios compartidos, Gestión de usuarios, Presencia, Eventos virtuales
Los emojis personalizados de Teams están disponibles en modo organizacional a través de list-custom-emojis
y create-custom-emoji (preajustes teams y work). Estos utilizan la API beta de Microsoft Graph
y solicitan los permisos delegados TeamworkCustomEmoji.Read y
TeamworkCustomEmoji.Create, respectivamente. El modo de solo lectura expone solo la herramienta de listado.
Las implementaciones existentes pueden necesitar consentimiento para estos nuevos ámbitos y reautenticación;
agregar soporte de herramientas no actualiza un token ya emitido.
El listado puede devolver contentBytes: null de forma predeterminada. Para solicitar imágenes PNG/GIF en base64,
pase select: "displayName,contentBytes" o select: ["displayName", "contentBytes"].
Use un top pequeño para mantener las respuestas de imágenes manejables.
Para crear un emoji, pase body: { displayName, contentBytes }
con el nombre aprobado exacto y los bytes de archivo PNG/GIF en base64. Consulte los contratos de
listado
y creación de Microsoft.
Estas herramientas no publican mensajes ni reacciones.
Permisos requeridos de la API de Graph
Los permisos se solicitan dinámicamente según las herramientas habilitadas. Use --list-permissions para ver los permisos exactos para su configuración:
# Personal mode (default)
npx @softeria/ms-365-mcp-server --list-permissions
# Organization mode (includes Teams, SharePoint, etc.)
npx @softeria/ms-365-mcp-server --org-mode --list-permissions
# Filtered by preset
npx @softeria/ms-365-mcp-server --preset mail --list-permissions
Esto es útil para entornos empresariales donde los permisos de la API de Graph deben preaprobarse y recibir consentimiento del administrador antes de implementar una nueva versión.
El JSON de --list-permissions incluye:
toolPermissions: permisos implicados por la superficie de herramientas antes del filtrado de--allowed-scopeseffectivePermissions: permisos implicados por las herramientas que permanecen habilitadas después de--allowed-scopespermissions: alias heredado paraeffectivePermissions, mantenido para compatibilidad con scripts existentesallowedScopes: la lista de permitidos de ámbitos configurada, cuando se proporcionadisabledTools: herramientas ocultas porque sus ámbitos de Graph requeridos no están cubiertos porallowedScopesmissingAllowedScopesForTools: ámbitos faltantes únicos entre las herramientas deshabilitadasextraAllowedScopesNotUsedByTools: ámbitos permitidos que no son utilizados por la superficie de herramientas actual
Ámbitos permitidos
De forma predeterminada, MSAL solicita los ámbitos implicados por las herramientas habilitadas, y la superficie de herramientas se controla mediante --enabled-tools, --preset, --org-mode y --read-only.
Las implementaciones empresariales y sin cabeza pueden agregar un límite de ámbito con --allowed-scopes o MS365_MCP_ALLOWED_SCOPES. Cuando se configura, el servidor primero calcula la superficie de herramientas normal, luego oculta las herramientas de Graph cuyos ámbitos requeridos no están cubiertos por la lista de permitidos. Los metadatos de OAuth y los flujos de inicio de sesión solicitan solo los permisos efectivos para las herramientas que permanecen habilitadas.
npx @softeria/ms-365-mcp-server \
--org-mode \
--enabled-tools '^(list-mail-messages|get-mail-message|list-drives|get-drive-item|download-bytes)$' \
--allowed-scopes 'User.Read Mail.Read Files.Read'
El valor de CLI tiene prioridad sobre MS365_MCP_ALLOWED_SCOPES; si no se establece ninguno, el comportamiento predeterminado de ámbito derivado de herramientas no cambia. Proporcionar un valor vacío falla al inicio para que las implementaciones no vuelvan accidentalmente a una superficie de herramientas más amplia.
La cobertura de ámbitos es consciente de la jerarquía: por ejemplo, Mail.ReadWrite cubre herramientas que requieren Mail.Read, y Files.ReadWrite.All cubre herramientas que requieren Files.Read.
SharePoint admite dos modelos de permisos empresariales:
- Ámbitos amplios de inquilino como
Sites.Read.All,Sites.ReadWrite.AllySites.Manage.All. Sites.Selectedde Microsoft Graph, donde el acceso al sitio de SharePoint se otorga a la aplicación en colecciones de sitios específicas y Graph evalúa los permisos del usuario con sesión iniciada en el momento de la solicitud.
El comportamiento predeterminado del modo organizacional continúa solicitando los ámbitos amplios de SharePoint utilizados por las implementaciones existentes. Las empresas que desean acceso a SharePoint de sitios seleccionados pueden establecer una lista de permitidos que contenga Sites.Selected en lugar de ámbitos amplios de Sites.*.All. Las herramientas directas de sitio/lista/ítem que apuntan a un sitio de SharePoint explícito, y las herramientas de ítems de /drives/{drive-id}/... (listar, obtener, subir, carpeta, mover/renombrar, copiar, versiones) para unidades de un sitio concedido, pueden ejecutarse con Sites.Selected; las herramientas de descubrimiento y búsqueda de SharePoint en todo el inquilino aún requieren ámbitos amplios de SharePoint.
npx @softeria/ms-365-mcp-server \
--org-mode \
--read-only \
--enabled-tools 'sharepoint|site|drive|planner' \
--allowed-scopes 'User.Read Files.Read Notes.Read Tasks.Read Sites.Selected'
En modo HTTP, el descubrimiento de OAuth anuncia los permisos filtrados efectivos para que los clientes soliciten la misma superficie de consentimiento. El modo En-Nombre-De (--obo) aún anuncia api://<clientId>/access_as_user para metadatos de recursos protegidos; --allowed-scopes no anula OBO.
Solicitud de ámbitos adicionales
--allowed-scopes solo reduce la solicitud de tokens. Para solicitar un ámbito de Graph que ninguna herramienta incluida necesita — por ejemplo, para impulsar un punto de conexión mediante graph-batch — use --extra-scopes (o MS365_MCP_EXTRA_SCOPES). Estos ámbitos se agregan textualmente a la solicitud de tokens, además de los ámbitos derivados de herramientas.
npx @softeria/ms-365-mcp-server \
--org-mode \
--extra-scopes 'CopilotPackages.ReadWrite.All'
Esto es para usar con su propio registro de aplicación de Azure (MS365_MCP_CLIENT_ID / MS365_MCP_CLIENT_SECRET): la aplicación Softeria predeterminada solo declara un conjunto de permisos fijo y reducido, por lo que solicite ámbitos adicionales contra una aplicación que usted controle (su administrador de inquilino da su consentimiento allí). El valor de CLI tiene prioridad sobre la variable de entorno; un valor vacío falla al inicio.
Modo organizacional/de trabajo
Para acceder a funciones de trabajo/escuela (Teams, SharePoint, etc.), habilite el modo organizacional usando cualquiera de estas banderas:
{
"mcpServers": {
"ms365": {
"command": "npx",
"args": ["-y", "@softeria/ms-365-mcp-server", "--org-mode"]
}
}
}
El modo organizacional debe habilitarse desde el inicio para acceder a las funciones de cuenta de trabajo. Sin esta bandera, solo las funciones de cuenta personal (correo electrónico, calendario, OneDrive, etc.) están disponibles.
Acceso a buzones compartidos
Para acceder a buzones compartidos, necesita:
- Modo organizacional: Las herramientas de buzón compartido requieren la bandera
--org-mode(solo cuentas de trabajo/escuela) - Permisos delegados:
Mail.Read.Sharedpara leer,Mail.ReadWrite.Sharedpara crear, actualizar o mover mensajes,Mail.Send.Sharedpara enviar, responder o reenviar, yCalendars.Read.Sharedpara las herramientas de calendario compartido - Permisos de Exchange: El usuario con sesión iniciada debe haber recibido acceso al buzón compartido
- Uso: Use la dirección de correo electrónico del buzón compartido como el parámetro
user-iden las herramientas de buzón compartido
Cómo encontrar buzones compartidos: Use la herramienta list-users para descubrir usuarios y buzones compartidos disponibles en su organización.
Ejemplo: list-shared-mailbox-messages con user-id establecido en shared-mailbox@company.com
Ejemplo de inicio rápido
Probar el inicio de sesión en Claude Desktop:
Ejemplos
Integración
Claude Desktop
Para agregar este servidor MCP a Claude Desktop, edite el archivo de configuración en Configuración > Desarrollador.
Cuenta personal (MSA)
{
"mcpServers": {
"ms365": {
"command": "npx",
"args": ["-y", "@softeria/ms-365-mcp-server"]
}
}
}
Cuenta de trabajo/escuela (Global)
{
"mcpServers": {
"ms365": {
"command": "npx",
"args": ["-y", "@softeria/ms-365-mcp-server", "--org-mode"]
}
}
}
Cuenta de trabajo/escuela (China 21Vianet)
{
"mcpServers": {
"ms365-china": {
"command": "npx",
"args": ["-y", "@softeria/ms-365-mcp-server", "--org-mode", "--cloud", "china"]
}
}
}
CLI de Claude Code
Cuenta personal (MSA)
claude mcp add ms365 -- npx -y @softeria/ms-365-mcp-server
Cuenta de trabajo/escuela (Global)
# macOS/Linux
claude mcp add ms365 -- npx -y @softeria/ms-365-mcp-server --org-mode
# Windows (use cmd /c wrapper)
claude mcp add ms365 -s user -- cmd /c "npx -y @softeria/ms-365-mcp-server --org-mode"
Cuenta de trabajo/escuela (China 21Vianet)
# macOS/Linux
claude mcp add ms365-china -- npx -y @softeria/ms-365-mcp-server --org-mode --cloud china
# Windows (use cmd /c wrapper)
claude mcp add ms365-china -s user -- cmd /c "npx -y @softeria/ms-365-mcp-server --org-mode --cloud china"
Para otras interfaces que admiten MCP, consulte su documentación respectiva para el método de integración correcto.
Open WebUI
Open WebUI admite servidores MCP mediante transporte HTTP con OAuth 2.1.
-
Inicie el servidor en modo HTTP:
npx @softeria/ms-365-mcp-server --http -
En Open WebUI, vaya a Configuración de administrador → Herramientas (
/admin/settings/tools) → Agregar conexión:- Tipo: MCP Streamable HTTP
- URL: La URL de su servidor MCP con la ruta
/mcp - Autenticación: OAuth 2.1
-
Haga clic en Registrar cliente.
Nota: El registro dinámico de clientes está habilitado de forma predeterminada en modo HTTP. Use
--no-dynamic-registration(o establezcaMS365_MCP_DISABLE_DCR=true) para deshabilitarlo. Si usa una aplicación personalizada de Azure Entra, el tipo de plataforma para su URI de redireccionamiento depende de si la aplicación tiene un secreto de cliente: con un secreto use "Web", sin uno use "Aplicaciones móviles y de escritorio" (nunca "Aplicación de página única").
Configuración de prueba rápida usando la aplicación de Azure predeterminada (ID ms-365 y localhost:8080 están preconfigurados):
docker run -d -p 8080:8080 \
-e WEBUI_AUTH=false \
-e OPENAI_API_KEY \
ghcr.io/open-webui/open-webui:main
npx @softeria/ms-365-mcp-server --http
Luego agregue la conexión con URL http://localhost:3000/mcp e ID ms-365.
¿Ejecutando en Docker detrás de un proxy inverso? Establezca
--public-url https://your-domain.compara que la URL de autorización de OAuth entregada al navegador del usuario sea accesible desde fuera de la red del contenedor. Consulte docs/deployment.md para la guía completa.
Desarrollo local
Para desarrollo o pruebas locales:
# From the project directory
claude mcp add ms -- npx tsx src/index.ts --org-mode
O configure Claude Desktop manualmente:
{
"mcpServers": {
"ms365": {
"command": "node",
"args": ["/absolute/path/to/ms-365-mcp-server/dist/index.js", "--org-mode"]
}
}
}
Nota: Ejecute
npm run builddespués de cambios de código para actualizar la carpetadist/.
Autenticación
⚠️ Debe autenticarse antes de usar las herramientas.
El servidor admite tres métodos de autenticación:
1. Flujo de código de dispositivo (predeterminado)
Para autenticación interactiva mediante código de dispositivo:
- Inicio de sesión del cliente MCP:
- Llame a la herramienta
login(verifica automáticamente el token existente) - Si es necesario, obtenga URL+código, visite en el navegador
- Use la herramienta
verify-loginpara confirmar
- Llame a la herramienta
- Inicio de sesión de CLI:
Siga la URL y el mensaje de código en la terminal.npx @softeria/ms-365-mcp-server --login
Los tokens se almacenan en caché de forma segura en su almacén de credenciales del sistema operativo (respaldo a archivo).
2. Flujo de código de autorización OAuth (solo modo HTTP)
Cuando se ejecuta con --http, el servidor requiere autenticación OAuth:
npx @softeria/ms-365-mcp-server --http 3000
Este modo:
- Anuncia capacidades de OAuth a los clientes MCP
- Proporciona puntos de conexión de OAuth en
/auth/*(autorizar, token, metadatos) - Requiere
Authorization: Bearer <token>para todas las solicitudes MCP - Valida tokens con la API de Graph de Microsoft
- Deshabilita las herramientas de inicio/cierre de sesión de forma predeterminada (use
--enable-auth-toolspara habilitarlas)
Los clientes MCP manejarán automáticamente el flujo de OAuth cuando vean las capacidades anunciadas.
Configuración de Azure AD para pruebas de OAuth
Para usar el modo OAuth con credenciales personalizadas de Azure (recomendado para producción), deberá configurar un registro de aplicación de Azure AD:
- Crear registro de aplicación en Azure AD:
- Ve a Portal de Azure
- Navega a Azure Active Directory → Registros de aplicaciones → Nuevo registro
- Establece el nombre: "MS365 MCP Server"
- Configurar URI de redirección:
- Configura la URI de devolución de llamada de OAuth: Ve al registro de tu aplicación y en el lado izquierdo, ve a Autenticación.
- En Configuraciones de plataforma:
- Haz clic en Agregar una plataforma (si aún no ves una para "Aplicaciones de escritorio y móviles" / "Cliente público").
- Elige Aplicaciones de escritorio y móviles o Cliente público/nativo (móvil y escritorio) (la etiqueta depende de la versión del portal).
- Pruebas con MCP Inspector (
npm run inspector):
- Ve al registro de tu aplicación y en el lado izquierdo, ve a Autenticación.
- En Configuraciones de plataforma:
- Haz clic en Agregar una plataforma (si aún no ves una para "Web").
- Elige Web.
- Configura las siguientes URI de redirección
http://localhost:6274/oauth/callbackhttp://localhost:6274/oauth/callback/debughttp://localhost:3000/callback(opcional, para la devolución de llamada del servidor)
- Obtener credenciales:
- Copia el ID de aplicación (cliente) desde la página de Información general
- Ve a Certificados y secretos → Nuevo secreto de cliente → Copia el valor del secreto (opcional para aplicaciones públicas)
- Configurar variables de entorno:
Crea un archivo
.enven la raíz de tu proyecto:MS365_MCP_CLIENT_ID=your-azure-ad-app-client-id-here MS365_MCP_CLIENT_SECRET=your-secret-here # Optional for public apps MS365_MCP_TENANT_ID=common
Con esto configurado, el servidor usará tu aplicación de Azure personalizada en lugar de la integrada.
Nota:
.envse lee desde el directorio donde se inicia el servidor, y el cliente MCP decide qué es eso. SoloMS365_MCP_CLIENT_ID,MS365_MCP_CLIENT_SECRET,MS365_MCP_TENANT_IDyMS365_MCP_CLOUD_TYPEse leen de él. Cada otra variable listada arriba debe establecerse en tu shell o en la configuración del cliente MCP; cualquier otra cosa encontrada en un.envse ignora con una advertencia en stderr.
3. Trae tu propio token (BYOT)
Si estás ejecutando ms-365-mcp-server como parte de un sistema más grande que gestiona tokens OAuth de Microsoft externamente, puedes proporcionar un token de acceso directamente a este servidor MCP:
MS365_MCP_OAUTH_TOKEN=your_oauth_token npx @softeria/ms-365-mcp-server
Este método:
- Omite los flujos de autenticación interactivos
- Usa tu token OAuth preexistente para las solicitudes de Microsoft Graph API
- No maneja la renovación de tokens (la gestión del ciclo de vida del token es tu responsabilidad)
Nota: El modo HTTP requiere autenticación. Para pruebas sin autenticación, usa el modo stdio con el flujo de código de dispositivo.
Herramientas de autenticación: En modo HTTP, las herramientas de inicio/cierre de sesión están deshabilitadas por defecto ya que OAuth maneja la autenticación. Usa
--enable-auth-toolssi las necesitas disponibles.
Soporte para múltiples cuentas
Usa una única instancia del servidor para atender múltiples cuentas de Microsoft. Cuando hay más de una cuenta iniciada, se inyecta automáticamente un parámetro account en cada herramienta, permitiéndote especificar qué cuenta usar en cada llamada.
Iniciar sesión con múltiples cuentas (una vez por cuenta):
# Login first account (device code flow)
npx @softeria/ms-365-mcp-server --login
# Follow the device code prompt, sign in as personal@outlook.com
# Login second account
npx @softeria/ms-365-mcp-server --login
# Follow the device code prompt, sign in as work@company.com
Listar cuentas configuradas:
npx @softeria/ms-365-mcp-server --list-accounts
Usar en llamadas de herramientas: Pasa "account": "work@company.com" en cualquier solicitud de herramienta:
{ "tool": "list-mail-messages", "arguments": { "account": "work@company.com" } }
Comportamiento:
- Con una cuenta única configurada, se selecciona automáticamente (no se necesita el parámetro
account). - Con múltiples cuentas y sin parámetro
account, el servidor usa la cuenta predeterminada seleccionada o devuelve un error útil listando las cuentas disponibles. - 100% compatible con versiones anteriores: las configuraciones existentes de cuenta única funcionan sin cambios.
- El parámetro
accountacepta dirección de correo electrónico (p. ej.,user@outlook.com) ohomeAccountIdde MSAL.
Fijación estricta de cuentas
Las implementaciones headless de stdio pueden fijar la caché local de MSAL a una cuenta de Microsoft esperada:
# Username matching is case-insensitive
MS365_MCP_EXPECTED_USERNAME=work@company.com npx @softeria/ms-365-mcp-server --login
# Or pin the exact MSAL homeAccountId shown by --list-accounts
npx @softeria/ms-365-mcp-server --expected-home-account-id <homeAccountId> --login
Usa --list-accounts para descubrir los valores de homeAccountId. La herramienta list-accounts de MCP oculta intencionalmente los IDs de cuenta, así que usa la CLI para la fijación exacta de IDs.
La fijación es opcional y solo para MSAL local:
- Los valores de CLI (
--expected-username,--expected-home-account-id) tienen prioridad sobreMS365_MCP_EXPECTED_USERNAMEyMS365_MCP_EXPECTED_HOME_ACCOUNT_ID. - Proporcionar un valor de fijación vacío falla al inicio en lugar de ignorarse.
- Las fijaciones de nombre de usuario se comparan sin distinguir mayúsculas/minúsculas; las fijaciones de
homeAccountIdson exactas. - Si ambas fijaciones están establecidas, deben resolverse a la misma cuenta en caché.
- El inicio de stdio local falla rápidamente cuando la cuenta esperada no está en la caché de tokens. Arranca estableciendo la fijación, ejecutando
--login, y luego iniciando el servidor headless. - Los inicios de sesión con código de dispositivo y navegador rechazan una cuenta faltante o no coincidente antes de persistir la cuenta seleccionada o la caché de tokens.
- La fijación colapsa el modo MCP efectivo a cuenta única: el servidor no anuncia un parámetro
accounty las instrucciones de MCP no sugieren cambiar de cuenta. --http,--oboyMS365_MCP_OAUTH_TOKENusan tokens proporcionados por la solicitud para las llamadas a Graph, por lo que las fijaciones de cuenta son solo de advertencia en esos modos. Si las herramientas de autenticación HTTP están habilitadas, la fijación aún se aplica a esos flujos auxiliares locales de MSAL.--logoutborra todas las cuentas en caché, incluida la cuenta fijada. Para una limpieza quirúrgica, prefiere--remove-account <id>.
Para multiplexores MCP (Legate, Governor): El modo de múltiples cuentas reemplaza el patrón de N procesos. En lugar de generar un servidor por cuenta, una única instancia maneja todas las cuentas mediante el parámetro
account, reduciendo la duplicación de herramientas de N×110 a 110.
Presets de herramientas
Para reducir la sobrecarga inicial de conexión y el uso de tokens, usa categorías de herramientas preestablecidas en lugar de cargar el conjunto completo de herramientas:
npx @softeria/ms-365-mcp-server --preset mail
npx @softeria/ms-365-mcp-server --list-presets # See all available presets
Presets disponibles: mail, calendar, files, personal, work, excel, contacts, tasks, onenote, search, users, outlook, onedrive, teams, teams-write, all
Cada endpoint en endpoints.json declara a qué presets pertenece mediante un array presets, por lo que cada preset es una lista de permitidos exacta de nombres de herramientas que nunca coincide en exceso entre aplicaciones (p. ej., mail no incluye herramientas de buzones compartidos; esas están en work). El lector binario universal download-bytes se incluye en cada preset excepto teams-write, por lo que cualquier cosa que devuelva una aplicación (un archivo, un adjunto, una foto, una grabación) siempre se puede obtener; get-download-url (una URL preautenticada para archivos de drive/SharePoint) viaja con los presets respaldados por drive. Así que un preset que puede encontrar un archivo siempre puede leer sus bytes.
Los presets outlook, onedrive y teams están limitados por aplicación: exponen exactamente una aplicación de Microsoft. Úsalos para implementaciones de "exponer exactamente una aplicación":
# Outlook only (mail + calendar + contacts; no shared mailboxes, no files)
npx @softeria/ms-365-mcp-server --preset outlook
# Teams only (requires --org-mode)
npx @softeria/ms-365-mcp-server --org-mode --preset teams
El preset teams-write es la contraparte solo de envío de --read-only: enviar en chats, enviar/responder en canales, listar chats/equipos/canales por nombre y notificaciones de actividad - sin lectura de mensajes y sin descargadores de bytes. El token solicitado es mínimo por construcción (Chat.ReadBasic, los ámbitos *.Send y listado básico de equipos/canales - nada que pueda leer contenido de mensajes):
npx @softeria/ms-365-mcp-server --org-mode --preset teams-write
Descubrimiento dinámico de herramientas
En lugar de cargar cada herramienta por adelantado, usa el descubrimiento dinámico para que el LLM encuentre y cargue herramientas solo cuando las necesite:
npx @softeria/ms-365-mcp-server --discovery
Mantiene el contexto inicial pequeño y reduce el uso de tokens, especialmente útil para sesiones largas o configuraciones sensibles al costo (p. ej., Open WebUI ejecutándose contra una API de pago).
Opciones de CLI
Las siguientes opciones se pueden usar al ejecutar ms-365-mcp-server directamente desde la línea de comandos:
--login Login using device code flow
--logout Log out and clear saved credentials
--verify-login Verify login without starting the server
--list-permissions List required Graph API permissions and exit (respects --org-mode, --preset, --enabled-tools, --allowed-scopes)
--org-mode Enable organization/work mode from start (includes Teams, SharePoint, etc.)
--work-mode Alias for --org-mode
--force-work-scopes Backwards compatibility alias for --org-mode (deprecated)
--cloud <type> Microsoft cloud environment: global (default) or china (21Vianet)
--allowed-scopes <scopes> Limit exposed tools to Graph scopes covered by this allowlist
--extra-scopes <scopes> Append additional Graph scopes to the token request (for use with your own app registration + graph-batch)
--expected-username <username> Require local MSAL auth to use this account username
--expected-home-account-id <id> Require local MSAL auth to use this exact homeAccountId
Opciones del servidor
Cuando se ejecuta como servidor MCP, se pueden usar las siguientes opciones:
-v Enable verbose logging
--read-only Start server in read-only mode, disabling write operations
--http [port] Use Streamable HTTP transport instead of stdio (optionally specify port, default: 3000)
Starts Express.js server with MCP endpoint at /mcp. Bound to a loopback host
(e.g. --http 127.0.0.1:3000 or --http [::1]:3000) with no --public-url, it
rejects requests whose Host or Origin is not localhost (not applied to the
--attachment-port listener)
--http-local-file-tools Register download-bytes-to-file over HTTP. Anyone who can reach the port
can write files as the server's user (without a valid token, only an empty
file that is removed again), so enable it only on a single-user machine.
Refused unless --http binds a loopback host with no --public-url and no
--trust-proxy-auth
--enable-auth-tools Enable login/logout tools when using HTTP mode (disabled by default in HTTP mode)
--enable-attachment-urls Let get-download-url mint a server-served URL for byte resources Graph
exposes no pre-authenticated URL for (see "Server-Minted Attachment URLs")
--attachment-port <port> Serve /attachment on its own listener on this port instead of on the
MCP app, so a fetcher that can read attachments cannot also reach /mcp
(requires --enable-attachment-urls; see "Splitting the attachment listener")
--attachment-host <host> Interface the --attachment-port listener binds. Defaults to whatever
--http bound, which with a wildcard --http leaves BOTH ports on every
interface and so isolates nothing — set this to make the split real
(requires --attachment-port; see "Splitting the attachment listener")
--no-dynamic-registration Disable OAuth Dynamic Client Registration (enabled by default in HTTP mode)
--enabled-tools <pattern> Filter tools using regex pattern (e.g., "excel|contact" to enable Excel and Contact tools)
--preset <names> Use preset tool categories (comma-separated). See "Tool Presets" section above
--list-presets List all available presets and exit
--toon (experimental) Enable TOON output format for 30-60% token reduction
--discovery Dynamic tool discovery: loads tools on demand to reduce initial token usage (see "Dynamic Tool Discovery" above)
--public-url <url> Public base URL for OAuth when behind a reverse proxy (see Open WebUI section and docs/deployment.md)
Variables de entorno:
READ_ONLY=true|1: Alternativa a la bandera --read-onlyENABLED_TOOLS: Filtrar herramientas usando un patrón regex (alternativa a la bandera --enabled-tools)MS365_MCP_ORG_MODE=true|1: Habilitar el modo organización/trabajo (alternativa a la bandera --org-mode)MS365_MCP_FORCE_WORK_SCOPES=true|1: Compatibilidad hacia atrás para MS365_MCP_ORG_MODEMS365_MCP_OUTPUT_FORMAT=toon: Habilitar el formato de salida TOON (alternativa a la bandera --toon)MS365_MCP_MAX_TOP=<n>: Límite máximo para Graph$top/topen solicitudes de lista (entero positivo). Cuando el modelo pasa un valor mayor, el servidor lo ajusta anpara que las respuestas sigan siendo más pequeñas. Ejemplo:MS365_MCP_MAX_TOP=15MS365_MCP_MAX_PAGES=<n>: Número máximo de páginas seguidas cuando se llama a una herramienta confetchAllPages: true(entero positivo, predeterminado100). Limita la memoria y la latencia para conjuntos de resultados grandes.MS365_MCP_MAX_ITEMS=<n>: Número máximo de elementos acumulados cuandofetchAllPages: true(entero positivo, predeterminado10000). La paginación se detiene y la respuesta se trunca una vez que se recopilan esta cantidad de elementos.MS365_MCP_ALLOW_PAGINATION=0|false|no: Deshabilitar el seguimiento de múltiples páginas por completo. Cuando se establece, el parámetrofetchAllPagesno se anuncia en las herramientas, y cualquier solicitud que aún lo pase devuelve solo la primera página (predeterminado: paginación habilitada).MS365_MCP_BODY_FORMAT=html: Devolver los cuerpos de los correos electrónicos como HTML en lugar de texto plano (predeterminado: texto)MS365_MCP_MESSAGE_SIGNOFF_PREFIX=<text>: Firma antepuesta a los mensajes salientes para que los destinatarios puedan saber que fueron enviados por un agente, p. ej.,🤖. Predeterminado: ninguno. Equivalente CLI:--message-signoff-prefix <text>(ver Firma de mensajes a continuación)MS365_MCP_MESSAGE_SIGNOFF_SUFFIX=<text>: Firma añadida al final de los mensajes salientes. Predeterminado: ninguno. Equivalente CLI:--message-signoff-suffix <text>.--no-message-signoffdeshabilita ambas (ver Firma de mensajes a continuación)MS365_MCP_RATE_LIMIT_DISABLED=true|1: Deshabilitar la limitación de velocidad por IP en modo HTTP (predeterminado: habilitado — 30 solicitudes/min en/authorize,/token,/register; 120 solicitudes/min en/mcp)MS365_MCP_TRUST_PROXY_HOPS=<n>: Número de saltos de proxy inverso de confianza en modo HTTP (predeterminado1). La limitación de velocidad precisa por IP depende de que esto coincida con su implementación — configúrelo al número de proxies frente al servidor,0para usar la IP del par del socket sin procesar, o una lista de subredes separadas por comasMS365_MCP_ATTACHMENT_PORT=<port>: Servir la ruta de archivos adjuntos en su propio listener en este puerto (alternativa a --attachment-port; requiere--enable-attachment-urls)MS365_MCP_ATTACHMENT_HOST=<host>: Interfaz a la que se vincula el listenerMS365_MCP_ATTACHMENT_PORT(alternativa a --attachment-host; requiere--attachment-port). Se establece por defecto al host al que--httpestá vinculado — que para un comodín--httpsignifica que ambos puertos responden en todas partes y la división de puertos no aísla nada. Ver "División del listener de archivos adjuntos"MS365_MCP_HTTP_LOCAL_FILE_TOOLS=true|1: Registrar download-bytes-to-file sobre HTTP (alternativa a --http-local-file-tools; mismas restricciones)MS365_MCP_CLOUD_TYPE=global|china: Entorno de nube de Microsoft (alternativa a la bandera --cloud)LOG_LEVEL: Establecer el nivel de registro (predeterminado: 'info')SILENT=true|1: Deshabilitar la salida de consolaMS365_MCP_REDACT_PII=false|0: Deshabilitar la depuración de JWTs, encabezados Bearer, campos de tokens OAuth y direcciones de correo electrónico de los mensajes de registro (predeterminado: habilitado). El servidor maneja tokens Bearer de Graph en vivo, por lo que la redacción está activa a menos que opte por no participar para una depuración local completamente detallada.MS365_MCP_CLIENT_ID: ID de cliente de aplicación de Azure personalizado (se establece por defecto a la aplicación integrada)MS365_MCP_TENANT_ID: ID de inquilino personalizado (se establece por defecto a 'common' para multiinquilino). Las cuentas personales de Microsoft deben establecer esto enconsumers- a partir de junio de 2026, los tokens de actualización emitidos a través de la autoridad 'common' predeterminada se rechazan en la primera actualización, por lo que las sesiones mueren aproximadamente una hora después del inicio de sesiónMS365_MCP_OAUTH_TOKEN: Token OAuth preexistente para la API de Microsoft Graph (método BYOT)MS365_MCP_KEYVAULT_URL: URL de Azure Key Vault para la gestión de secretos (ver sección Azure Key Vault)MS365_MCP_TOKEN_CACHE_PATH: Ruta de archivo personalizada para la caché de tokens MSAL (ver Almacenamiento de tokens a continuación)MS365_MCP_SELECTED_ACCOUNT_PATH: Ruta de archivo personalizada para los metadatos de la cuenta seleccionada (ver Almacenamiento de tokens a continuación)MS365_MCP_AUTH_CACHE_COMMAND: Envoltorio ejecutable externo para el almacenamiento de caché de autenticación independiente del proveedor (ver Almacenamiento de tokens a continuación)MS365_MCP_AUTH_CACHE_COMMAND_TIMEOUT_MS: Tiempo de espera por invocación paraMS365_MCP_AUTH_CACHE_COMMAND(predeterminado:10000)MS365_MCP_EXPECTED_USERNAME: Requerir que la autenticación MSAL local use este nombre de usuario de cuenta de Microsoft (sin distinción de mayúsculas y minúsculas; la bandera CLI tiene prioridad)MS365_MCP_EXPECTED_HOME_ACCOUNT_ID: Requerir que la autenticación MSAL local use este homeAccountId exacto de MSAL (la bandera CLI tiene prioridad)
URLs de archivos adjuntos emitidas por el servidor
get-download-url devuelve las URLs @microsoft.graph.downloadUrl preautenticadas propias de Microsoft
para elementos de OneDrive y SharePoint. Graph no publica tal URL para archivos adjuntos de correo y calendario, grabaciones de reuniones o cualquier otro punto final de bytes /$value — para esos, la
única forma de leer los bytes ha sido download-bytes, que devuelve base64 al
contexto del agente. Un PDF de 73 KB y 3 páginas cuesta alrededor de 24,500 tokens de esa manera, y el modelo
no puede analizarlos de todos modos.
--enable-attachment-urls (modo HTTP, desactivado por defecto) cierra esa brecha. Cuando Graph no tiene
URL propia, get-download-url emite una que este servidor sirve:
GET /attachment?t=<ticket>&dgk=<key-id>&dgx=<expiry>&dgs=<signature>
El ticket son 32 bytes de salida CSPRNG, de un solo uso, solo en memoria, y expira después de
MS365_MCP_ATTACHMENT_URL_TTL_S segundos. Canjearlo transmite los bytes de Graph con el
token propio de este servidor; el buscador no envía encabezado Authorization y no tiene ninguna credencial de Microsoft.
Esto no otorga ninguna autoridad que el agente llamante no tuviera ya. Cada objetivo que puede
ser emitido es uno que download-bytes buscaría para el mismo llamante en la misma cuenta. El
ticket solo mueve esos bytes fuera de la ventana de contexto y hacia una transferencia directa.
Configuración
MS365_MCP_ATTACHMENT_URL_BASE=http://m365-mcp:3000 # required
MS365_MCP_ATTACHMENT_URL_KEY=... # required (or _KEY_FILE=/path)
MS365_MCP_ATTACHMENT_URL_KEY_ID=1 # optional, default 1
MS365_MCP_ATTACHMENT_URL_TTL_S=120 # optional, default 120, max 300
MS365_MCP_ATTACHMENT_URL_BASE deliberadamente no es MS365_MCP_PUBLIC_URL: ese es
orientado al navegador, para redirecciones OAuth, mientras que este se busca de servidor a servidor y es
comúnmente una dirección de contenedor. Una configuración faltante o malformada falla al inicio en lugar de
por solicitud — una característica de firma que aparece sin una clave emitiría URLs que nada puede
verificar, silenciosamente.
División del listener de archivos adjuntos
Por defecto, /attachment es servido por la misma aplicación Express, en el mismo puerto, que /mcp.
Eso está bien cuando los llamantes están autenticados por un token Bearer, y es un problema cuando
no lo están. Bajo --trust-proxy-auth, el punto final MCP no lee ningún encabezado Authorization
— la accesibilidad es la autenticación — por lo que un puerto compartido significa que el sidecar que usted
permitió para buscar un PDF también puede llamar a cada herramienta en el servidor.
--attachment-port <port> (o MS365_MCP_ATTACHMENT_PORT) mueve la ruta a un listener
propio, y --attachment-host <host> (o MS365_MCP_ATTACHMENT_HOST) dice qué
interfaz vincula ese listener:
ms-365-mcp-server --http 10.89.0.2:3000 --trust-proxy-auth \
--enable-attachment-urls \
--attachment-port 3001 --attachment-host 10.89.1.2
MS365_MCP_ATTACHMENT_URL_BASE=http://m365-mcp:3001 # note: the attachment port
GET /attachmenten 3001 funciona; en 3000 es 404 — la aplicación MCP nunca lo monta./mcpen 3001 es 404, como todo lo demás: la segunda aplicación tiene la ruta de archivos adjuntos y nada más. Sin router OAuth, sin analizadores de cuerpo, sin CORS, sin verificación de salud.- El limitador de 60 solicitudes/min que protege la ruta lo sigue al nuevo listener.
trust proxyestá desactivado en el listener de archivos adjuntos (yMS365_MCP_TRUST_PROXY_HOPSno se lee para él), a diferencia del listener MCP, que confía en un salto. Este puerto está destinado a ser marcado directamente en una red de contenedores; honrarX-Forwarded-Foren la única superficie sin credenciales del servidor permitiría a un llamante elegir su propio cubo de limitación de velocidad.
La bandera requiere --enable-attachment-urls y se niega a iniciar sin ella — por sí sola
abriría un puerto sin nada en él mientras el operador creía que las superficies estaban
separadas. En modo stdio advierte y se ignora, como la bandera de la que depende.
--attachment-host igualmente requiere --attachment-port: sola nombraría una interfaz
para un listener que no existe.
Dos puertos no son dos superficies a menos que vinculen dos interfaces
Esta es la parte que decide si algo de lo anterior vale algo. Léalo antes de implementar la división.
--attachment-port por sí solo separa las dos superficies dentro del proceso. No las
separa en la red. Sin --attachment-host, el listener de archivos adjuntos hereda
el host al que --http se vinculó — y --http 3000, la forma común, no nombra ningún host en absoluto, por lo que
Node vincula el comodín y ambos puertos responden en cada interfaz:
ms-365-mcp-server --http 3000 --trust-proxy-auth \
--enable-attachment-urls --attachment-port 3001 # NOT isolated
Las redes de contenedores otorgan a un par cada puerto en un contenedor, no un puerto. Ponga un
sidecar de conversión de documentos en un puente compartido para que pueda buscar /attachment en 3001, y
ese mismo sidecar puede marcar :3000/mcp — que bajo --trust-proxy-auth no lee ningún
encabezado Authorization en absoluto y devuelve el catálogo completo de herramientas. Nada falla, nada
se registra como error, y la configuración se ve exactamente como la aislada.
Para hacerlo real, dé a los dos listeners direcciones diferentes, y ponga solo la dirección de archivos adjuntos en la red en la que está el buscador:
# docker compose — the MCP port on the agent's own bridge, the attachment port on the
# bridge shared with the converter. The converter can reach 3001 and cannot route to 3000.
services:
m365-mcp:
networks: { agent-net: { ipv4_address: 10.89.0.2 }, convert-net: { ipv4_address: 10.89.1.2 } }
command: >
--http 10.89.0.2:3000 --trust-proxy-auth
--enable-attachment-urls
--attachment-port 3001 --attachment-host 10.89.1.2
docglean:
networks: [convert-net]
El puerto MCP es entonces inalcanzable desde convert-net por vinculación — no hay socket
escuchando en esa interfaz — en lugar de por una regla de firewall que tiene que seguir coincidiendo.
El servidor advierte al inicio si ejecuta --trust-proxy-auth con --attachment-port mientras
ambos listeners aún responden en una interfaz común (ya sea compartiendo una dirección, o cualquiera de ellos
en el comodín). Ambas direcciones vinculadas se registran, leídas de vuelta del socket en lugar de
de las banderas, para que Server listening on … y Attachment listener on … puedan compararse
directamente.
--attachment-host toma una dirección IPv4 simple, dirección IPv6 (entre corchetes [::1] o simple
::1) o nombre de host. Se rechaza en lugar de coaccionarse — --attachment-host 10.0.0.5:3001
es un error que nombra --attachment-port, no una vinculación a otra cosa. Tenga en cuenta que
MS365_MCP_ATTACHMENT_URL_BASE aún no debe ser un literal IPv6 (la firma de URL cubre
el host y las dos implementaciones normalizan IPv6 de manera diferente); si vincula el listener
a una dirección IPv6, nómbrelo en la base por nombre de host.
Apunte MS365_MCP_ATTACHMENT_URL_BASE al puerto de archivos adjuntos. El servidor no puede verificar esto
por usted: la base es usualmente un nombre de contenedor en una red que este proceso no puede resolver, por lo que
un puerto incorrecto aquí aparece como un fallo de búsqueda en el sidecar, no un error aquí. Tanto la
base como el puerto vinculado se registran al inicio, a una línea de distancia, exactamente para esa comparación.
La firma, y quién verifica qué
dgk/dgx/dgs no son verificados por este servidor en el canje, y eso es deliberado.
Existen para el buscador: un sidecar de conversión de documentos que se niega a marcar una dirección
privada a menos que la URL lleve un HMAC válido de un origen que ha sido configurado para confiar.
Lo que autoriza el canje aquí es el ticket. Verificar la firma en el camino de regreso
probaría solo que emitimos la URL — que el ticket ya prueba — mientras acopla el canje
al reloj del sidecar y a que la clave sobreviva un reinicio.
El formato de cable es docglean-mcp's
signing.py (canonical_string), y src/lib/url-signing.ts es un puerto de él. La
cadena canónica está unida por \n: v1, esquema en minúsculas, host en minúsculas, el puerto siempre
explícito, la ruta, la consulta restante con dgk/dgx/dgs eliminados y el resto ordenado
y recodificado, y la expiración. Los vectores de prueba en
test/attachment-url-signing.test.ts se verificaron contra la implementación de Python byte
por byte — tres lugares donde el JavaScript obvio no está de acuerdo con Python (!*'()
escapado, + decodificado como espacio, y orden de clasificación de puntos de código vs UTF-16) son por qué esa verificación
existe en lugar de asumirse.
El ticket viaja en la consulta, no en la ruta, porque el sidecar verificador mantiene la ruta de una URL buscada en sus mensajes de error y elimina la consulta.
No disponible en modo OAuth/OBO
La identidad llega según la solicitud en el encabezado Authorization del llamador, y un ticket se canjea más tarde por un buscador que no envía ninguno. La acuñación se rechaza con una explicación en lugar de producir una URL que siempre falla.
Almacenamiento de tokens
Los tokens de autenticación se almacenan en un archivo cifrado (AES-256-GCM). Solo la clave de cifrado de 32 bytes va al almacén de credenciales del sistema operativo mediante keytar.
La caché en sí es demasiado grande para que algunos almacenes de credenciales puedan contenerla: un blob del Administrador de credenciales de Windows tiene un límite de 2560 bytes y una caché de tokens real es varias veces más grande, por lo que en Windows la escritura nunca podría tener éxito. Una clave tiene 32 bytes independientemente de cuántas cuentas hayan iniciado sesión, por lo que esto funciona igual en todas las plataformas.
Rutas predeterminadas están en el directorio de configuración por usuario:
| Plataforma | Ubicación |
|---|---|
| Windows | %APPDATA%\ms-365-mcp-server\ |
| macOS | ~/Library/Application Support/ms-365-mcp-server/ |
| Linux | $XDG_CONFIG_HOME/ms-365-mcp-server/ (o ~/.config/ms-365-mcp-server/) |
Las versiones anteriores usaban por defecto una ruta dentro del paquete instalado, que bajo npx se resuelve a un directorio de caché con hash de contenido que npm cache clean o un aumento de versión descarta. Una caché que aún esté en el directorio del paquete se mueve a la nueva ubicación en el primer inicio.
Eso cubre instalaciones globales y locales, y npx cuando el hash no ha cambiado. No puede alcanzar una caché dejada en un directorio de hash de npx anterior, por lo que actualizar una instalación de npx una última vez significa iniciar sesión de nuevo. Adoptar una caché de otro directorio significaría confiar en un directorio que este paquete no puede demostrar que escribió, lo que no vale un inicio de sesión ahorrado.
Anula las rutas si lo necesitas:
export MS365_MCP_TOKEN_CACHE_PATH="$HOME/.config/ms365-mcp/.token-cache.json"
export MS365_MCP_SELECTED_ACCOUNT_PATH="$HOME/.config/ms365-mcp/.selected-account.json"
Los directorios principales se crean automáticamente. Los archivos se escriben con permisos 0600.
Sin un almacén de credenciales (Linux sin interfaz gráfica, la mayoría de los contenedores), la clave se escribe en .cache-key junto al archivo de caché, con permisos 0600. Eso evita que los tokens aparezcan en un cat perdido, una copia de seguridad o una confirmación accidental. No protege contra cualquiera que ya pueda leer el directorio: la clave está justo ahí. Usa MS365_MCP_AUTH_CACHE_COMMAND a continuación si necesitas la caché en un almacén de secretos real.
Omitir el almacén de credenciales a propósito:
export MS365_MCP_USE_KEYTAR=0 # also accepts false, no or off
La clave entonces va a .cache-key en todas las plataformas, exactamente como donde no existe un almacén de credenciales, y nada en el servidor llama a keytar. Útil cuando el almacén de credenciales solicita en cada inicio: macOS vuelve a preguntar cada vez que el binario llamador cambia, lo que bajo npx es cada aumento de versión, o cuando el módulo nativo se comporta mal en tu plataforma en lugar de simplemente fallar al cargar. Cualquier otro valor deja el almacén de credenciales en uso, y uno no reconocido se advierte en lugar de pasarse por alto en silencio.
Apagarlo deja varada una caché que se cifró con una clave ya en el almacén de credenciales, ya que nada puede alcanzar esa clave más. El servidor lo dice y reemplaza esa caché en el próximo inicio de sesión, lo que cierra la sesión de todas las cuentas que contenía, no solo la que vuelves a iniciar. Desactiva la variable primero si esa caché vale la pena conservarla.
Solo se reemplaza una caché que nada en la máquina puede abrir. Una que falla al descifrar mientras hay una clave utilizable justo allí (un archivo truncado, una degradación a una compilación anterior, una caché de otro lugar) es daño en lugar de una caché varada, y se deja exactamente como está por defecto.
Dos cosas que deliberadamente no hace. Nunca elimina lo que este servidor ya puso en el almacén de credenciales, al cerrar sesión o de otro modo, porque alcanzar el almacén es lo que acabas de pedirle que deje de hacer: limpia las entradas de ms-365-mcp-server a mano si quieres que desaparezcan. Y un .cache-key que existe pero no se puede leer (propietario incorrecto en un directorio de configuración montado con bind, por ejemplo) se trata como recuperable en lugar de faltante: el servidor se niega tanto a sobrescribir una caché como a acuñar una clave de reemplazo, y lo dice, en lugar de eliminar una clave que volvería a funcionar una vez que se arreglen los permisos. Arregla los permisos, o elimina .cache-key tú mismo para empezar de nuevo, lo que sí significa iniciar sesión de nuevo.
Si la caché no se puede descifrar (clave perdida, llavero bloqueado, archivo modificado), se te pide que inicies sesión de nuevo en lugar de que el servidor falle al iniciar. El archivo de caché se deja exactamente como estaba: no se elimina ni se sobrescribe por ese nuevo inicio de sesión. Un llavero que simplemente está bloqueado generalmente se lee bien en el próximo inicio, y la caché sigue allí cuando lo hace.
El costo es que la nueva sesión no se guarda mientras esto dure, por lo que cada inicio te pide que inicies sesión de nuevo. Si la clave realmente se perdió y la caché nunca se abrirá, elimina .token-cache.json para empezar de nuevo: el registro lo dice y nombra la ruta.
Entornos alojados/en espacio aislado (p. ej., Anthropic Cowork): Establece
MS365_MCP_TOKEN_CACHE_PATHyMS365_MCP_SELECTED_ACCOUNT_PATHa un montaje persistente para que los tokens sobrevivan entre sesiones.
Comando externo de caché de autenticación
Las implementaciones locales de MSAL sin interfaz gráfica pueden reemplazar el almacenamiento integrado de keytar/archivos con un comando externo neutral al proveedor:
export MS365_MCP_AUTH_CACHE_COMMAND="/path/to/ms365-auth-cache-store"
export MS365_MCP_AUTH_CACHE_COMMAND_TIMEOUT_MS=10000
Cuando MS365_MCP_AUTH_CACHE_COMMAND está configurado para un flujo de autenticación local, el servidor usa solo ese comando para la caché de tokens de MSAL y los metadatos de cuenta seleccionada. No recurre a keytar ni a archivos locales. Si la ruta del comando falta, no es ejecutable en POSIX, sale con código distinto de cero, se agota el tiempo o devuelve datos malformados, las operaciones de caché de autenticación fallan de forma segura con un mensaje de error saneado.
El valor debe ser una ruta real de un ejecutable contenedor. No es una cadena de comando de shell, y no hay una variable de entorno de argumentos complementaria. Pon cualquier intérprete, región, perfil o configuración específica del proveedor dentro del contenedor. Los usuarios de Windows deben apuntar la variable a un ejecutable contenedor o script que Node pueda lanzar directamente sin análisis de shell.
El servidor invoca el contenedor con:
$MS365_MCP_AUTH_CACHE_COMMAND load token-cache
$MS365_MCP_AUTH_CACHE_COMMAND save token-cache
$MS365_MCP_AUTH_CACHE_COMMAND delete token-cache
$MS365_MCP_AUTH_CACHE_COMMAND load selected-account
$MS365_MCP_AUTH_CACHE_COMMAND save selected-account
$MS365_MCP_AUTH_CACHE_COMMAND delete selected-account
Protocolo v1:
load <key>no lee stdin. Sale con0con{"found":true,"value":"<stored envelope string>"}cuando está presente. Un fallo es salir con0con{"found":false}o stdout vacío.save <key>recibe{"value":"<stamped envelope string>"}en stdin y debe salir con0solo después de que el valor se confirme de forma duradera. No hay guardados de disparar y olvidar o combinados en v1.delete <key>no lee stdin y sale con0ya sea que la clave existiera o no.<key>estoken-cacheoselected-account.- Cualquier salida distinta de cero es un error de almacenamiento. No uses el código de salida
2para fallos de caché. - Stderr se captura y se trunca en errores saneados. Los payloads de stdin y stdout nunca se registran por el servidor.
- Los payloads de caché de tokens pueden ser grandes; los contenedores deben manejar valores de al menos 256 KB.
Las solicitudes normales de Graph HTTP sin estado no usan almacenamiento de caché de autenticación local. En modo HTTP, el almacenamiento de comandos se omite al inicio y por solicitud a menos que las herramientas de autenticación local estén explícitamente habilitadas o se use un comando de cuenta local como --login, --verify-login, --list-accounts, --select-account o --logout.
Integración con Azure Key Vault
Para implementaciones de producción, puedes almacenar secretos en Azure Key Vault en lugar de variables de entorno. Esto es particularmente útil para Azure Container Apps con identidad administrada.
Configuración
-
Crea un Key Vault (si no tienes uno):
az keyvault create --name your-keyvault-name --resource-group your-rg --location eastus -
Agrega secretos al Key Vault:
az keyvault secret set --vault-name your-keyvault-name --name ms365-mcp-client-id --value "your-client-id" az keyvault secret set --vault-name your-keyvault-name --name ms365-mcp-tenant-id --value "your-tenant-id" # Optional: if using confidential client flow az keyvault secret set --vault-name your-keyvault-name --name ms365-mcp-client-secret --value "your-secret" -
Otorga acceso al Key Vault:
Para Azure Container Apps con identidad administrada:
# Get the managed identity principal ID PRINCIPAL_ID=$(az containerapp show --name your-app --resource-group your-rg --query identity.principalId -o tsv) # Grant access to Key Vault secrets az keyvault set-policy --name your-keyvault-name --object-id $PRINCIPAL_ID --secret-permissions get listPara desarrollo local con Azure CLI:
# Your Azure CLI identity already has access if you have appropriate RBAC roles az login -
Configura el servidor:
MS365_MCP_KEYVAULT_URL=https://your-keyvault-name.vault.azure.net npx @softeria/ms-365-mcp-server
Mapeo de nombres de secretos
| Nombre de secreto en Key Vault | Variable de entorno | Requerido |
|---|---|---|
| ms365-mcp-client-id | MS365_MCP_CLIENT_ID | Sí |
| ms365-mcp-tenant-id | MS365_MCP_TENANT_ID | No (por defecto 'common') |
| ms365-mcp-client-secret | MS365_MCP_CLIENT_SECRET | No |
Autenticación
La integración con Key Vault usa DefaultAzureCredential del SDK de Azure Identity, que automáticamente prueba múltiples métodos de autenticación en orden:
- Variables de entorno (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID)
- Identidad administrada (recomendado para Azure Container Apps)
- Credenciales de Azure CLI (para desarrollo local)
- Credenciales de Visual Studio Code
- Credenciales de Azure PowerShell
Dependencias opcionales
Los paquetes de Azure Key Vault (@azure/identity y @azure/keyvault-secrets) son dependencias opcionales. Solo se cargan cuando MS365_MCP_KEYVAULT_URL está configurado. Si no usas Key Vault, estos paquetes no son necesarios.
Firma de mensajes
Los mensajes salientes se pueden envolver en una firma configurable (p. ej., un prefijo 🤖) para que los destinatarios puedan distinguir los mensajes enviados por agentes de los que escribiste tú. Desactivado por defecto: actívalo con --message-signoff-prefix / --message-signoff-suffix (env: MS365_MCP_MESSAGE_SIGNOFF_PREFIX / MS365_MCP_MESSAGE_SIGNOFF_SUFFIX); --no-message-signoff o un valor de env vacío lo vuelve a desactivar.
Una vez configurado, se aplica a todos los mensajes de Teams (envíos, respuestas y ediciones, incluso a través de graph-batch), a los envíos de correo directos (send-mail, responder/reenviar, sus variantes de buzón compartido y respuestas de hilos de grupo), y a los borradores de correo a medida que se escribe su contenido: send-draft-message envía un borrador tal cual, por lo que un borrador que escribiste tú sale sin tocar. Un mensaje que ya lleva el marcador no se firma dos veces, y un envío cuyo cuerpo no puede aceptar la firma se rechaza en lugar de enviarse sin firmar.
Los marcadores pueden contener marcado (p. ej., un <span> de color) siempre que muestren texto visible. Ten en cuenta que la firma es una salvaguarda contra que un agente use mal las herramientas que se le dieron, no un límite de seguridad estricto: un agente con acceso de shell en la misma máquina podría simplemente reiniciar el servidor sin ella.
Implementación en producción
Consulta docs/deployment.md para una guía completa sobre cómo alojar el servidor para acceso a nivel de organización, incluidos Docker, Azure Container Apps, Azure App Service, registro de aplicaciones de Azure AD, configuración de proxy inverso, configuración de cliente y endpoints expuestos.
Contribuciones
¡Damos la bienvenida a contribuciones! Antes de enviar una solicitud de extracción, asegúrate de que tus cambios cumplan con nuestros estándares de calidad.
Ejecuta el script de verificación para comprobar todos los requisitos de calidad del código:
npm run verify
Para desarrolladores
Después de clonar el repositorio, es posible que necesites generar el código de cliente a partir de la especificación OpenAPI de Microsoft Graph:
npm run generate
Proyectos relacionados
- ms-365-admin-mcp-server por @okapi-ca: servidor complementario para escenarios de administrador/daemon con permisos de aplicación (flujo de credenciales de cliente), que cubre alertas de seguridad, registros de auditoría, estado del servicio e informes de uso.
Soporte
Si tienes problemas o necesitas ayuda:
- Crea un issue
- Inicia una discusión
- Correo electrónico: eirikb@eirikb.no
- Discord: https://discord.gg/WvGVNScrAZ o @eirikb
Licencia
MIT © 2026 Softeria