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

npm version build status license

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:

NubeDescripciónPunto de conexión de autenticaciónPunto de conexión de API de Graph
Global (predeterminado)Microsoft 365 internacionallogin.microsoftonline.comgraph.microsoft.com
China (21Vianet)Microsoft 365 operado por 21Vianetlogin.chinacloudapi.cnmicrosoftgraph.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-scopes
  • effectivePermissions: permisos implicados por las herramientas que permanecen habilitadas después de --allowed-scopes
  • permissions: alias heredado para effectivePermissions, mantenido para compatibilidad con scripts existentes
  • allowedScopes: la lista de permitidos de ámbitos configurada, cuando se proporciona
  • disabledTools: herramientas ocultas porque sus ámbitos de Graph requeridos no están cubiertos por allowedScopes
  • missingAllowedScopesForTools: ámbitos faltantes únicos entre las herramientas deshabilitadas
  • extraAllowedScopesNotUsedByTools: á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.All y Sites.Manage.All.
  • Sites.Selected de 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:

  1. Modo organizacional: Las herramientas de buzón compartido requieren la bandera --org-mode (solo cuentas de trabajo/escuela)
  2. Permisos delegados: Mail.Read.Shared para leer, Mail.ReadWrite.Shared para crear, actualizar o mover mensajes, Mail.Send.Shared para enviar, responder o reenviar, y Calendars.Read.Shared para las herramientas de calendario compartido
  3. Permisos de Exchange: El usuario con sesión iniciada debe haber recibido acceso al buzón compartido
  4. Uso: Use la dirección de correo electrónico del buzón compartido como el parámetro user-id en 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:

Login example

Ejemplos

Image

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.

  1. Inicie el servidor en modo HTTP:

    npx @softeria/ms-365-mcp-server --http
    
  2. 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
  3. 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 establezca MS365_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.

Open WebUI MCP Connection

¿Ejecutando en Docker detrás de un proxy inverso? Establezca --public-url https://your-domain.com para 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 build después de cambios de código para actualizar la carpeta dist/.

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-login para confirmar
  • Inicio de sesión de CLI:
    npx @softeria/ms-365-mcp-server --login
    
    Siga la URL y el mensaje de código en la terminal.

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-tools para 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:

  1. 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"
  1. 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).
  1. 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/callback
      • http://localhost:6274/oauth/callback/debug
      • http://localhost:3000/callback (opcional, para la devolución de llamada del servidor)
  1. 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)
  1. Configurar variables de entorno: Crea un archivo .env en 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: .env se lee desde el directorio donde se inicia el servidor, y el cliente MCP decide qué es eso. Solo MS365_MCP_CLIENT_ID, MS365_MCP_CLIENT_SECRET, MS365_MCP_TENANT_ID y MS365_MCP_CLOUD_TYPE se 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 .env se 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-tools si 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 account acepta dirección de correo electrónico (p. ej., user@outlook.com) o homeAccountId de 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 sobre MS365_MCP_EXPECTED_USERNAME y MS365_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 homeAccountId son 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 account y las instrucciones de MCP no sugieren cambiar de cuenta.
  • --http, --obo y MS365_MCP_OAUTH_TOKEN usan 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.
  • --logout borra 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-only
  • ENABLED_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_MODE
  • MS365_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 / top en solicitudes de lista (entero positivo). Cuando el modelo pasa un valor mayor, el servidor lo ajusta a n para que las respuestas sigan siendo más pequeñas. Ejemplo: MS365_MCP_MAX_TOP=15
  • MS365_MCP_MAX_PAGES=<n>: Número máximo de páginas seguidas cuando se llama a una herramienta con fetchAllPages: true (entero positivo, predeterminado 100). Limita la memoria y la latencia para conjuntos de resultados grandes.
  • MS365_MCP_MAX_ITEMS=<n>: Número máximo de elementos acumulados cuando fetchAllPages: true (entero positivo, predeterminado 10000). 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ámetro fetchAllPages no 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-signoff deshabilita 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 (predeterminado 1). 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, 0 para usar la IP del par del socket sin procesar, o una lista de subredes separadas por comas
  • MS365_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 listener MS365_MCP_ATTACHMENT_PORT (alternativa a --attachment-host; requiere --attachment-port). Se establece por defecto al host al que --http está vinculado — que para un comodín --http significa 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 consola
  • MS365_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 en consumers - 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ón
  • MS365_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 para MS365_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 /attachment en 3001 funciona; en 3000 es 404 — la aplicación MCP nunca lo monta.
  • /mcp en 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 proxy está desactivado en el listener de archivos adjuntos (y MS365_MCP_TRUST_PROXY_HOPS no 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; honrar X-Forwarded-For en 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:

PlataformaUbicació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_PATH y MS365_MCP_SELECTED_ACCOUNT_PATH a 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 con 0 con {"found":true,"value":"<stored envelope string>"} cuando está presente. Un fallo es salir con 0 con {"found":false} o stdout vacío.
  • save <key> recibe {"value":"<stamped envelope string>"} en stdin y debe salir con 0 solo 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 con 0 ya sea que la clave existiera o no.
  • <key> es token-cache o selected-account.
  • Cualquier salida distinta de cero es un error de almacenamiento. No uses el código de salida 2 para 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

  1. Crea un Key Vault (si no tienes uno):

    az keyvault create --name your-keyvault-name --resource-group your-rg --location eastus
    
  2. 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"
    
  3. 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 list
    

    Para desarrollo local con Azure CLI:

    # Your Azure CLI identity already has access if you have appropriate RBAC roles
    az login
    
  4. 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 VaultVariable de entornoRequerido
ms365-mcp-client-idMS365_MCP_CLIENT_IDSí
ms365-mcp-tenant-idMS365_MCP_TENANT_IDNo (por defecto 'common')
ms365-mcp-client-secretMS365_MCP_CLIENT_SECRETNo

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:

  1. Variables de entorno (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID)
  2. Identidad administrada (recomendado para Azure Container Apps)
  3. Credenciales de Azure CLI (para desarrollo local)
  4. Credenciales de Visual Studio Code
  5. 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:

Licencia

MIT © 2026 Softeria