Microsoft 365

Servidor MCP que se conecta a todo el conjunto de Microsoft 365 (Microsoft Office, Outlook, Excel) mediante Graph API (incluyendo correo, archivos, calendario)

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 (predeterminada)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
  • Presets 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 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) Habilitar el formato TOON globalmente:

Mediante el indicador 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 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 Microsoft Graph. Cada herramienta se asigna 1 a 1 a un punto de conexión de la API de Graph y se define declarativamente 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 el indicador --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

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 contar con consentimiento del administrador antes de implementar una nueva versión.

El JSON de --list-permissions incluye:

  • toolPermissions: permisos implícitos por la superficie de herramientas antes del filtrado de --allowed-scopes
  • effectivePermissions: permisos implícitos 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 alcance configurada, cuando se proporciona
  • disabledTools: herramientas ocultas porque sus alcances de Graph requeridos no están cubiertos por allowedScopes
  • missingAllowedScopesForTools: alcances faltantes únicos en todas las herramientas deshabilitadas
  • extraAllowedScopesNotUsedByTools: alcances permitidos que no son utilizados por la superficie de herramientas actual

Alcances permitidos

De forma predeterminada, MSAL solicita los alcances implícitos 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 interfaz gráfica pueden agregar un límite de alcance con --allowed-scopes o MS365_MCP_ALLOWED_SCOPES. Cuando se configura, el servidor primero calcula la superficie de herramientas normal y luego oculta las herramientas de Graph cuyos alcances 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 CLI tiene prioridad sobre MS365_MCP_ALLOWED_SCOPES; si no se establece ninguno, el comportamiento predeterminado de alcance derivado de herramientas no cambia. Proporcionar un valor vacío falla al inicio para que las implementaciones no caigan accidentalmente en una superficie de herramientas más amplia.

La cobertura de alcance 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:

  • Alcances 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 propios del usuario con sesión iniciada en el momento de la solicitud.

El comportamiento predeterminado del modo de organización continúa solicitando los alcances amplios de SharePoint utilizados por las implementaciones existentes. Las empresas que desean acceso a sitios seleccionados de SharePoint pueden establecer una lista de permitidos que contenga Sites.Selected en lugar de alcances 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, cargar, 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 alcances 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 alcances adicionales

--allowed-scopes solo reduce la solicitud de tokens. Para solicitar un alcance 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 alcances se agregan textualmente a la solicitud de tokens, además de los alcances 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 alcances adicionales contra una aplicación que usted controle (su administrador de inquilino otorga consentimiento allí). El valor CLI tiene prioridad sobre la variable de entorno; un valor vacío falla al inicio.

Modo de organización/trabajo

Para acceder a funciones de trabajo/escuela (Teams, SharePoint, etc.), habilite el modo de organización usando cualquiera de estos indicadores:

{
  "mcpServers": {
    "ms365": {
      "command": "npx",
      "args": ["-y", "@softeria/ms-365-mcp-server", "--org-mode"]
    }
  }
}

El modo de organización debe habilitarse desde el inicio para acceder a las funciones de cuenta de trabajo. Sin este indicador, solo están disponibles las funciones de cuenta personal (correo electrónico, calendario, OneDrive, etc.).

Acceso a buzones compartidos

Para acceder a buzones compartidos, necesita:

  1. Modo de organización: Las herramientas de buzón compartido requieren el indicador --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 tener acceso concedido al buzón compartido
  4. Uso: Use la dirección de correo electrónico del buzón compartido como parámetro user-id en las herramientas de buzón compartido

Cómo encontrar buzones compartidos: Use la herramienta list-users para descubrir usuarios disponibles y buzones compartidos en su organización.

Ejemplo: list-shared-mailbox-messages con user-id establecido en shared-mailbox@company.com

Ejemplo de inicio rápido

Probar 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 con 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 local o pruebas:

# 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 CLI:
    npx @softeria/ms-365-mcp-server --login
    
    Siga la URL y el indicador 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 en 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 Microsoft Graph
  • 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 de Azure AD:
  • Vaya a Portal de Azure
  • Navegue a Azure Active Directory → Registros de aplicaciones → Nuevo registro
  • Establezca el nombre: "MS365 MCP Server"
  1. Configurar URI de redireccionamiento:
  • Configure el URI de devolución de llamada de OAuth: Vaya a su registro de aplicación y en el lado izquierdo, vaya a Autenticación.
  • En Configuraciones de plataforma:
    • Haga clic en Agregar una plataforma (si aún no ve una para "Aplicaciones móviles y de escritorio" / "Cliente público").
    • Elija Aplicaciones móviles y de escritorio 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 a tu registro de 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 secretosNuevo 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 personalizada de Azure en lugar de la integrada.

Nota: .env se lee desde el directorio donde se inicia el servidor, y el cliente MCP decide qué es ese directorio. Solo MS365_MCP_CLIENT_ID, MS365_MCP_CLIENT_SECRET, MS365_MCP_TENANT_ID y MS365_MCP_CLOUD_TYPE se leen desde allí. Cada otra variable listada arriba debe configurarse 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 a Microsoft Graph API.
  • No gestiona 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 gestiona la autenticación. Usa --enable-auth-tools si las necesitas disponibles.

Soporte multi-cuenta

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, lo que te permite 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 a herramientas: Pasa "account": "work@company.com" en cualquier solicitud de herramienta:

{ "tool": "list-mail-messages", "arguments": { "account": "work@company.com" } }

Comportamiento:

  • Con una sola cuenta configurada, se selecciona automáticamente (no se necesita el parámetro account).
  • Con múltiples cuentas y sin el parámetro account, el servidor usa la cuenta predeterminada seleccionada o devuelve un error útil listando las cuentas disponibles.
  • 100% compatible hacia atrás: las configuraciones de cuenta única existentes funcionan sin cambios.
  • El parámetro account acepta una dirección de correo electrónico (p. ej., user@outlook.com) o el homeAccountId de MSAL.

Fijación estricta de cuenta

Las implementaciones headless de stdio pueden fijar la caché local de MSAL a una única 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 fijar el ID exacto.

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; las fijaciones de homeAccountId son exactas.
  • Si ambas fijaciones están configuradas, deben resolverse en la misma cuenta en caché.
  • El inicio de stdio local falla rápidamente cuando la cuenta esperada no está en la caché de tokens. Inicializa configurando la fijación, ejecutando --login y luego iniciando el servidor headless.
  • Los inicios de sesión por 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 reduce el modo MCP efectivo a una sola cuenta: 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 en 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 multi-cuenta 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 predefinidas 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 de nombres de herramientas exacta 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 todos los presets 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í, 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 ni 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
--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: Filtra herramientas usando un patrón regex (alternativa a la bandera --enabled-tools)
  • MS365_MCP_ORG_MODE=true|1: Habilita 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: Habilita 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 listado (entero positivo). Cuando el modelo pasa un valor mayor, el servidor lo limita a n para que las respuestas sean 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: Deshabilita el seguimiento de múltiples páginas por completo. Cuando se configura, 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: Devuelve los cuerpos de correo 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 de CLI: --message-signoff-prefix <text> (ver Firma de mensajes abajo)
  • MS365_MCP_MESSAGE_SIGNOFF_SUFFIX=<text>: Firma añadida al final de los mensajes salientes. Predeterminado: ninguno. Equivalente de CLI: --message-signoff-suffix <text>. --no-message-signoff deshabilita ambas (ver Firma de mensajes abajo)
  • MS365_MCP_RATE_LIMIT_DISABLED=true|1: Deshabilita la limitación de velocidad por IP en modo HTTP (predeterminado: habilitada — 30 req/min en /authorize, /token, /register; 120 req/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 tu implementación — configúralo con el número de proxies frente al servidor, 0 para usar la IP del socket peer sin procesar, o una lista de subredes separadas por comas
  • MS365_MCP_ATTACHMENT_PORT=<port>: Sirve la ruta de 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 predetermina al host al que --http está vinculado — lo que para un --http comodín significa que ambos puertos responden en todas partes y la división de puertos no aísla nada. Ver "Dividir el listener de adjuntos"
  • MS365_MCP_CLOUD_TYPE=global|china: Entorno de nube de Microsoft (alternativa a la bandera --cloud)
  • LOG_LEVEL: Configura el nivel de registro (predeterminado: 'info')
  • SILENT=true|1: Deshabilita la salida de consola
  • MS365_MCP_REDACT_PII=false|0: Deshabilita la depuración de JWTs, encabezados Bearer, campos de tokens OAuth y direcciones de correo electrónico de los mensajes de registro (predeterminado: habilitada). El servidor maneja tokens Bearer de Graph en vivo, por lo que la redacción está activa a menos que optes por no hacerlo para depuración local completamente detallada.
  • MS365_MCP_CLIENT_ID: ID de cliente de aplicación personalizada de Azure (se predetermina a la aplicación integrada)
  • MS365_MCP_TENANT_ID: ID de inquilino personalizado (se predetermina a 'common' para multi-inquilino). Las cuentas personales de Microsoft deben configurar 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 Microsoft Graph API (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 de MSAL (ver Almacenamiento de tokens abajo)
  • MS365_MCP_SELECTED_ACCOUNT_PATH: Ruta de archivo personalizada para los metadatos de la cuenta seleccionada (ver Almacenamiento de tokens abajo)
  • MS365_MCP_AUTH_CACHE_COMMAND: Ejecutable externo para el almacenamiento de caché de autenticación neutral al proveedor (ver Almacenamiento de tokens abajo)
  • 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: Requiere que la autenticación MSAL local use este nombre de usuario de cuenta de Microsoft (sin distinguir mayúsculas; la bandera de CLI tiene prioridad)
  • MS365_MCP_EXPECTED_HOME_ACCOUNT_ID: Requiere que la autenticación MSAL local use este homeAccountId exacto de MSAL (la bandera de CLI tiene prioridad)

URLs de adjuntos emitidas por el servidor

get-download-url devuelve las @microsoft.graph.downloadUrl preautenticadas propias de Microsoft para elementos de OneDrive y SharePoint. Graph no publica tal URL para adjuntos de correo y calendario, grabaciones de reuniones o cualquier otro endpoint 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 una URL propia, get-download-url genera 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 ningún encabezado Authorization y no posee ninguna credencial de Microsoft.

Esto no otorga ninguna autoridad que el agente llamante no tuviera ya. Cada destino que puede ser acuñado es uno que download-bytes obtendrí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 obtiene de servidor a servidor y comúnmente es 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 acuñaría URLs que nada puede verificar, silenciosamente.

Dividiendo el listener de 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 portador, y es un problema cuando no lo están. Bajo --trust-proxy-auth el endpoint MCP no lee ningún encabezado Authorization en absoluto — la alcanzabilidad es la autenticación — así que un puerto compartido significa que el sidecar que permitiste para obtener 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) indica qué interfaz enlaza 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 adjuntos y nada más. Sin router OAuth, sin analizadores de cuerpo, sin CORS, sin verificación de salud.
  • El limitador de 60 req/min que protege la ruta la sigue al nuevo listener.
  • trust proxy está desactivado en el listener de 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 límite de tasa.

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 enlacen dos interfaces

Esta es la parte que decide si algo de lo anterior vale algo. Léelo 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 adjuntos hereda el host que --http enlazó — y --http 3000, la forma común, no nombra ningún host en absoluto, así que Node enlaza 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. Pon un sidecar de conversión de documentos en un puente compartido para que pueda obtener /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 un error, y la configuración se ve exactamente como la aislada.

Para hacerlo real, da a los dos listeners direcciones diferentes, y pon solo la dirección de 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 enlace — 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 ejecutas --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 en el comodín). Ambas direcciones enlazadas se registran, leídas del socket en lugar 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 forzarse — --attachment-host 10.0.0.5:3001 es un error que nombra --attachment-port, no un enlace a otra cosa. Ten 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 enlazas el listener a una dirección IPv6, nómbrala en la base por nombre de host.

Apunta MS365_MCP_ATTACHMENT_URL_BASE al puerto de adjuntos. El servidor no puede verificar esto por ti: la base suele ser un nombre de contenedor en una red que este proceso no puede resolver, así que un puerto incorrecto aquí aparece como un fallo de obtención en el sidecar, no un error aquí. Tanto la base como el puerto enlazado 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 al canjear, 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 acuñamos la URL — que el ticket ya prueba — mientras acopla el canje al reloj del sidecar y a la clave que sobrevive 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 re-codificado, 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 difiere de Python (!*'() escapado, + decodificado como un 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 obtenida en sus mensajes de error y elimina la consulta.

No disponible en modo OAuth/OBO

La identidad allí llega por solicitud en el encabezado Authorization del llamante, y un ticket se canjea más tarde por un buscador que no envía ninguno. La acuñación se niega 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 a través de keytar.

La caché en sí es demasiado grande para que algunos almacenes de credenciales la contengan — un blob del Administrador de credenciales de Windows tiene un límite de 2560 bytes y una caché de tokens real es varias veces eso, así que en Windows la escritura nunca podría tener éxito. Una clave es de 32 bytes independientemente de cuántas cuentas estén conectadas, así que esto funciona igual en cada plataforma.

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/)

Versiones anteriores usaban por defecto una ruta dentro del paquete instalado, que bajo npx 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, así 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 probar que escribió, lo que no vale un inicio de sesión guardado.

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 padre se crean automáticamente. Los archivos se escriben con permisos 0600.

Sin un almacén de credenciales (Linux sin cabeza, 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.

Omitiendo 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 cada plataforma, 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 llamante cambia, 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 silenciosamente.

Apagarlo deja varada una caché que fue cifrada bajo 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, que cierra la sesión de cada cuenta que contenía, no solo la que inicias de nuevo. Desactiva la variable primero si esa caché vale la pena conservarla.

Solo una caché que nada en la máquina puede abrir se reemplaza. Una que falla al descifrar mientras una clave utilizable está justo ahí — un archivo truncado, una degradación a una compilación más antigua, una caché de otro lugar — es daño en lugar de una caché varada, y se deja sola 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 se vayan. Y un .cache-key que existe pero no puede leerse (propietario incorrecto en un directorio de configuración montado por 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 funcionaría de nuevo una vez que los permisos se arreglen. 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 puede descifrarse — clave perdida, llavero bloqueado, archivo modificado — se te pide iniciar sesión de nuevo en lugar de que el servidor falle al iniciar. El archivo de caché se deja exactamente como estaba: no eliminado, y no sobrescrito por ese nuevo inicio de sesión tampoco. Un llavero que solo está bloqueado generalmente se lee bien en el próximo inicio, y la caché sigue ahí cuando lo hace.

El costo es que la nueva sesión no se guarda mientras esto dure, así que cada inicio te pide iniciar sesión de nuevo. Si la clave realmente se ha ido 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 sandbox (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 headless de MSAL local pueden reemplazar el almacenamiento integrado de keytar/archivos con un comando externo neutral respecto 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 utiliza únicamente ese comando para la caché de tokens de MSAL y los metadatos de la cuenta seleccionada. No recurre a keytar ni a archivos locales. Si la ruta del comando no existe, no es ejecutable en POSIX, sale con código distinto de cero, se agota el tiempo de espera 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 a un ejecutable contenedor. No es una cadena de comando de shell, y no existe una variable de entorno de argumentos complementaria. Coloque 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 o script contenedor 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 y {"found":true,"value":"<stored envelope string>"} cuando está presente. Un fallo es salir con 0 y {"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 haya confirmado de forma duradera. No hay guardados de tipo "dispara y olvida" ni combinados en v1.
  • delete <key> no lee stdin y sale con 0 tanto si la clave existía como si no.
  • <key> es token-cache o selected-account.
  • Cualquier salida distinta de cero es un error de almacenamiento. No use el código de salida 2 para fallos de caché.
  • Stderr se captura y se trunca en errores saneados. El servidor nunca registra las cargas útiles de stdin y stdout.
  • Las cargas útiles de la 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 utilizan el 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, puede 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. Cree un Key Vault (si no tiene uno):

    az keyvault create --name your-keyvault-name --resource-group your-rg --location eastus
    
  2. Agregue 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. Conceda 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. Configure 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 del secreto en Key VaultVariable de entorno¿Requerido?
ms365-mcp-client-idMS365_MCP_CLIENT_ID
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 utiliza 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 usa Key Vault, estos paquetes no son necesarios.

Firma de mensajes

Los mensajes salientes pueden envolverse en una firma configurable (por ejemplo, un prefijo 🤖) para que los destinatarios puedan distinguir los mensajes enviados por agentes de los que usted mismo escribió. Desactivada por defecto — actívela 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 entorno vacío la desactiva nuevamente.

Una vez configurada, se aplica a todos los mensajes de Teams (envíos, respuestas y ediciones, incluso mediante graph-batch), a los envíos directos de correo (send-mail, responder/reenviar, sus variantes de buzón compartido y respuestas en 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 usted mismo escribió sale sin modificar. Un mensaje que ya lleva el marcador no se firma dos veces, y un envío cuyo cuerpo no puede llevar la firma se rechaza en lugar de enviarse sin firmar.

Los marcadores pueden contener marcado (por ejemplo, un <span> de color) siempre que muestren texto visible. Tenga en cuenta que la firma es una salvaguarda contra el mal uso de las herramientas por parte de un agente, 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

Consulte docs/deployment.md para una guía completa sobre cómo alojar el servidor para acceso a nivel organizacional, incluidos Docker, Azure Container Apps, Azure App Service, registro de aplicaciones de Azure AD, configuración de proxy inverso, configuración de clientes y endpoints expuestos.

Contribuciones

¡Agradecemos las contribuciones! Antes de enviar una solicitud de extracción, asegúrese de que sus cambios cumplan con nuestros estándares de calidad.

Ejecute 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 necesite generar el código del 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 que usan 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 tiene problemas o necesita ayuda:

Licencia

MIT © 2026 Softeria