Microsoft Planner MCP

Un servidor MCP que conecta asistentes de IA con Microsoft Planner. Autenticado con Entra ID para autenticación empresarial en nombre del usuario.

Documentación

Servidor MCP para Microsoft Planner

Un servidor MCP no oficial que conecta asistentes de IA a Microsoft Planner. Pídele a tu asistente de IA que cree tareas, organice planes, gestione buckets y más, todo mediante lenguaje natural.

Este es un servidor MCP no oficial, comunitario/de código abierto para Microsoft Planner.
No está afiliado, respaldado ni patrocinado por Microsoft.

Construido con FastMCP y autenticado mediante Microsoft Entra ID (Azure AD) usando el flujo On-Behalf-Of (OBO) para llamar a Microsoft Graph.

Tabla de Contenidos

¿Qué Puede Hacer?

Una vez conectado, puedes pedirle a tu asistente de IA cosas como:

  • "Muéstrame todas mis tareas de Planner que están vencidas"
  • "Crea una tarea llamada 'Preparar informe Q3' en el plan de Marketing"
  • "Mueve todas las tareas incompletas del bucket Sprint al bucket Backlog"
  • "Marca la tarea 'Actualizar documentos' como completada"

El asistente de IA traduce tu solicitud en las llamadas de herramienta apropiadas automáticamente.

Nota: Este MCP solo admite tareas y planes básicos de Planner

Requisitos Previos

  • Una cuenta de Microsoft 365 con acceso a Microsoft Planner
  • Un registro de aplicación de Azure Entra ID (Azure AD) (ver Configuración de Azure abajo)
  • Un cliente compatible con MCP — por ejemplo:

Para ejecutar el servidor localmente también necesitas:

O, para ejecutar con Docker:

Configuración de Azure Entra ID

Se requiere un registro de aplicación de Azure para que el servidor pueda autenticar usuarios y llamar a Microsoft Graph en su nombre. Necesitas acceso de administrador a un tenant de Azure Entra ID (o pregúntale a tu administrador de TI).

Paso 1 — Registrar la Aplicación

  1. Ve al Portal de Azure → Microsoft Entra ID → Registros de aplicaciones → Nuevo registro
  2. Ingresa un nombre (p. ej. Microsoft Planner MCP)
  3. En Tipos de cuenta admitidos, elige la opción apropiada para tu organización
  4. Establece la URI de redirección a Web → http://localhost:8000/auth/callback
  5. Haz clic en Registrar

Paso 2 — Configurar Permisos de API

  1. En tu nuevo registro de aplicación, ve a Permisos de API → Agregar un permiso → Microsoft Graph → Permisos delegados
  2. Agrega estos permisos:
    • Tasks.ReadWrite — leer y escribir tareas de Planner
    • User.Read — leer el perfil del usuario que ha iniciado sesión
    • User.ReadBasic.All — resolver nombres para mostrar de usuarios desde los GUID en las asignaciones de tareas (requerido solo para la herramienta list_users)
  3. Haz clic en Conceder consentimiento de administrador para tu organización

Paso 3 — Exponer un Alcance de API

  1. Ve a Exponer una API
  2. Establece la URI de ID de aplicación (acepta el valor predeterminado api://<client-id> o personalízalo)
  3. Haz clic en Agregar un alcance:
    • Nombre del alcance: mcp-access
    • Quién puede consentir: Administradores y usuarios (o Solo administradores si lo prefieres)
    • Completa el nombre para mostrar y la descripción
    • Establece el estado en Habilitado

Paso 4 — Establecer la Versión del Token

  1. Ve a Manifiesto (o Autenticación → Configuración avanzada en versiones más nuevas del portal)
  2. Establece "requestedAccessTokenVersion" a 2
  3. Guarda

Paso 5 — Crear un Secreto de Cliente

  1. Ve a Certificados y secretos → Nuevo secreto de cliente
  2. Agrega una descripción y elige un período de expiración
  3. Copia el Valor inmediatamente (solo se muestra una vez)

Paso 6 — Anota Tus IDs

Necesitarás estos tres valores para la configuración:

ValorDónde Encontrarlo
ID de aplicación (cliente)Registro de aplicación → Información general
ID de directorio (tenant)Registro de aplicación → Información general
Secreto de clienteEl valor copiado en el Paso 5

Instalación

Opción A — Local (Python + uv)

git clone https://github.com/aixolotl/microsoft-planner-mcp
cd microsoft-planner-mcp
uv sync

Opción B — Docker

No se necesita instalación de Python. Ver Ejecutar con Docker abajo.

Configuración

Copia el archivo de entorno de ejemplo y completa tus credenciales de Azure:

cp .env.example .env

Edita .env:

# Azure App Registration (required)
CLIENT_ID=your-app-client-id
CLIENT_SECRET=your-app-client-secret
TENANT_ID=your-azure-tenant-id

# Public URL of this server (used for OAuth redirect URI)
BASE_URL=http://localhost:8000

# JSON-encoded list of allowed CORS origins
# Include http://localhost:6274 if using MCP Inspector for testing
ALLOWED_ORIGINS=["http://localhost:8000","http://localhost:6274"]

# Require FastMCP's extra client consent prompt per MCP client. (default: true)
# Prevents confused-deputy attacks by requiring users to explicitly approve each new
# client. Keep true in production; set false only for local dev with throwaway
# clients.
# REQUIRE_AUTHORIZATION_CONSENT=true

# Optional rate-limit overrides (per client sliding window)
# Defaults are 120 requests per 1 minute
# RATE_LIMIT_MAX_REQUESTS=120
# RATE_LIMIT_WINDOW_MINUTES=1

# Optional: keep sign-ins in Redis so they survive restarts (see below)
# REDIS_URL=rediss://:<access-key>@<name>.redis.cache.windows.net:6380/0
# JWT_SIGNING_KEY=<long random string>
# STORAGE_ENCRYPTION_KEY=<Fernet key>

REQUIRE_AUTHORIZATION_CONSENT controla si FastMCP solicita a los usuarios aprobar explícitamente cada nuevo cliente MCP. Mantén esto en true (predeterminado) en producción para prevenir ataques de diputado confundido; establécelo en false solo durante el desarrollo local con clientes desechables.

RATE_LIMIT_MAX_REQUESTS y RATE_LIMIT_WINDOW_MINUTES son opcionales y deben permanecer comentados a menos que necesites anular los valores predeterminados para tu entorno.

Mantener sesiones iniciadas entre reinicios (Redis)

De forma predeterminada, el servidor mantiene el estado de OAuth — registros de clientes y tokens de Entra de los usuarios — en un almacén de archivos cifrado dentro del contenedor. Ese estado se pierde cuando el contenedor se recrea o una Azure Container App se escala a cero, y cada réplica tiene su propia copia. Los clientes entonces fallan al actualizar con 401 invalid_client y los usuarios deben reconectarse.

Establece REDIS_URL cuando ejecutes más de una réplica, escales a cero o recrees contenedores:

VariablePredeterminadoPropósito
REDIS_URLno establecido (almacén de archivos)redis:// o rediss:// (TLS — requerido por Azure Redis). La ruta es el índice de la base de datos (p. ej. /1). Codifica en porcentaje la contraseña si contiene / (las claves de acceso de Azure pueden): python -c "import urllib.parse; print(urllib.parse.quote('<key>', safe=''))". Se requiere un Redis no agrupado (Redis Cluster solo admite DB 0 y no es compatible). Contiene una credencial: mantenla en un secreto.
JWT_SIGNING_KEYderivado de CLIENT_SECRETSecreto fijo (al menos 32 caracteres) para firmar los tokens del servidor. Establécelo para que rotar el secreto de cliente de Entra ya no cierre la sesión de todos los usuarios. Usa el mismo valor en cada réplica.
STORAGE_ENCRYPTION_KEYderivado como el predeterminado de FastMCPClave Fernet que cifra el almacén de Redis (generar: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"). Requiere REDIS_URL — el servidor se niega a iniciar sin él. Establécelo explícitamente en producción: el valor predeterminado derivado sigue el esquema de clave interno de FastMCP, por lo que solo es tan estable como ese esquema.

Todo lo escrito en Redis está cifrado con Fernet. Los tokens y las transacciones de inicio de sesión expiran con las vidas útiles de FastMCP (tokens de actualización 30 días, transacciones minutos), pero los registros de clientes nunca expiran — se agrega uno por registro de cliente MCP, y las entradas escritas bajo una clave antigua quedan como basura ilegible. Dale al almacén su propio índice de base de datos (o su propio Redis) en lugar de compartir una base de datos con una caché, y usa una política de desalojo que no lo elimine silenciosamente (noeviction, o volatile-* con suficiente memoria). Un REDIS_URL, JWT_SIGNING_KEY o STORAGE_ENCRYPTION_KEY inválido falla al inicio; un valor vacío (JWT_SIGNING_KEY=) cuenta como no establecido. Si Redis no es accesible en tiempo de ejecución, las solicitudes de OAuth y las llamadas de herramientas fallan con 500 en aproximadamente 4 segundos y se recuperan por sí solas una vez que Redis vuelve.

Habilitar estas configuraciones cierra la sesión de todos una vez. Activar REDIS_URL, establecer JWT_SIGNING_KEY o cambiar cualquiera de las claves más tarde invalida los inicios de sesión existentes: los clientes conectados reciben un 401 en su próxima llamada o actualización y deben reconectarse una vez. El estado del almacén de archivos existente no se migra. Planifica el cambio.

Ejecutar el Servidor

Local

uv run uvicorn src.server:app --host 0.0.0.0 --port 8000

El endpoint de MCP está disponible en http://localhost:8000/mcp. Un endpoint de verificación de salud está en http://localhost:8000/health.

Ejecutar con Docker

# Pull the latest image
docker pull ghcr.io/aixolotl/microsoft-planner-mcp:latest

# Run with environment variables
docker run --rm -i \
  -e BASE_URL=https://localhost:8000 \
  -e CLIENT_ID=your_client_id \
  -e CLIENT_SECRET=your_api_token \
  -e TENANT_ID=your_tenant_id \
  -e ALLOWED_ORIGINS=["http://localhost:8000","http://localhost:6274", "http://localhost:3000"] \
  -e REQUIRE_AUTHORIZATION_CONSENT=true \
  ghcr.io/aixolotl/microsoft-planner-mcp:latest

Esto inicia el servidor MCP en el puerto 8000. Para anular los límites de tasa, también pasa -e RATE_LIMIT_MAX_REQUESTS=<value> y -e RATE_LIMIT_WINDOW_MINUTES=<minutes> (predeterminados: 120 y 1). Para mantener las sesiones iniciadas entre recreaciones de contenedores, también pasa -e REDIS_URL=... (ver Mantener sesiones iniciadas entre reinicios).

Ejecutar con Docker compose

docker compose up

Esto inicia el servidor MCP en el puerto 8000. La configuración de Docker Compose también incluye una instancia de Jaeger para la visualización de trazas (ver Trazado con OpenTelemetry).

Conectar un Cliente MCP

Una vez que el servidor esté ejecutándose, configura tu cliente MCP para conectarse a él.

VS Code

Agrega lo siguiente a tu configuración de VS Code (.vscode/settings.json en tu proyecto, o tu configuración de usuario):

{
  "mcp": {
    "servers": {
      "planner": {
        "type": "http",
        "url": "http://localhost:8000/mcp"
      }
    }
  }
}

Luego usa Copilot Chat en modo Agente y pídele que interactúe con tus tareas de Planner. Copilot descubrirá las herramientas disponibles automáticamente.

Otros Clientes MCP

Cualquier cliente que admita el transporte Streamable HTTP puede conectarse apuntando a http://localhost:8000/mcp. El servidor anuncia los metadatos de OAuth automáticamente — el cliente maneja el flujo de autenticación.

Herramientas Disponibles

Todas las herramientas están disponibles para tu asistente de IA automáticamente una vez conectado. No necesitas llamarlas directamente — solo describe lo que quieres en lenguaje natural. Los detalles de parámetros a continuación se proporcionan como referencia y para desarrolladores de clientes.

Las herramientas de solo lectura están anotadas con readOnlyHint: true para que los clientes puedan omitir las indicaciones de confirmación. Las herramientas destructivas (eliminaciones) están anotadas con destructiveHint: true.

Usuario

get_me

Devuelve el perfil del usuario autenticado desde Microsoft Graph.

  • Parámetros: Ninguno
  • Devuelve: Objeto de perfil de usuario (id, displayName, mail, etc.) o null

list_users

Recupera usuarios de Microsoft 365 por GUID, dirección de correo electrónico o búsqueda de texto libre. Útil para resolver los GUID de usuario devueltos en los objetos de asignación de tareas a nombres para mostrar.

ParámetroTipoRequeridoDescripción
selectstringNoCampos separados por comas a incluir (predeterminado: id,displayName,mail,userPrincipalName). Pasa *all para todos los campos.
searchstringNoBúsqueda de texto libre en el nombre para mostrar, o búsqueda por nombre de campo y valor, p. ej. Alice o surname:Smith
guidslist[string]NoGUID de objetos de usuario a buscar. Traducidos a una expresión OData $filter.
emailslist[string]NoNombres principales de usuario (UPNs / direcciones de correo electrónico) a buscar. Traducidos a una expresión OData $filter.
topintegerNoNúmero máximo de usuarios a devolver (predeterminado: 10). Se ignora cuando se proporcionan guids o emails.
  • Devuelve: Lista de objetos de usuario o null

Nota: Cuando se proporcionan tanto guids/emails como search, el filtro de GUID/correo electrónico tiene prioridad. Las listas muy grandes de GUID o direcciones de correo electrónico se truncan silenciosamente para mantenerse dentro del límite de URL de Graph de 2 048 caracteres.

Grupos

list_my_groups

Lista todos los grupos de Microsoft 365 de los que el usuario autenticado es miembro.

ParámetroTipoRequeridoDescripción
selectstringNoCampos separados por comas a incluir (predeterminado: id,displayName,mail). Pasa *all para todos los campos.
filterstringNoExpresión de filtro OData, p. ej. startsWith(displayName,'Project')
searchstringNoCadena de búsqueda OData, p. ej. "displayName:Project"
  • Devuelve: Lista de objetos de grupo o null

Nota: La herramienta Grupos no devolverá detalles como nombre o correo de los grupos con los permisos estándar Tasks.ReadWrite documentados aquí, sin embargo la búsqueda y el filtrado aún funcionan, por lo que puedes encontrar un grupo con un nombre específico usando esta herramienta.

Planes

list_my_plans

Lista los planes de Planner compartidos con el usuario autenticado.

ParámetroTipoRequeridoDescripción
selectstringNoCampos separados por comas a incluir (predeterminado: id,title,owner,createdBy,createdDateTime). Pasa *all para todos los campos.
  • Devuelve: Lista de objetos de plan o null

list_group_plans

Lista todos los planes de Planner que pertenecen a un grupo de Microsoft 365.

ParámetroTipoObligatorioDescripción
groupIdstringSíEl ID de objeto del grupo (de list_my_groups)
selectstringNoCampos separados por comas a incluir (predeterminado: id,title,owner,createdBy,createdDateTime). Pase *all para todos los campos.
  • Devuelve: Lista de objetos de plan o null

create_plan

Crea un nuevo plan de Planner para un grupo de Microsoft 365.

ParámetroTipoObligatorioDescripción
groupIdstringSíEl ID de objeto del grupo de M365 que será propietario del plan
titlestringSíTítulo para mostrar del nuevo plan
  • Devuelve: El objeto de plan creado o null

delete_plan

Elimina un plan de Planner.

ParámetroTipoObligatorioDescripción
planIdstringSíEl ID del plan a eliminar
etagstringSíEl @odata.etag actual del plan (reintenta una vez si está obsoleto)
  • Devuelve: Mensaje de confirmación

list_plan_categories

Obtiene las definiciones de etiquetas de categoría para un plan de Planner. Devuelve las 25 ranuras de categoría con su clave (p. ej., category1) y nombre para mostrar.

ParámetroTipoObligatorioDescripción
planIdstringSíEl ID del plan
  • Devuelve: Lista de objetos de categoría (key, display_name) o null

Cubos

list_buckets

Lista todos los cubos en un plan de Planner.

ParámetroTipoObligatorioDescripción
planIdstringSíEl ID del plan
  • Devuelve: Lista de objetos de cubo o null

create_bucket

Crea un nuevo cubo en un plan de Planner.

ParámetroTipoObligatorioDescripción
planIdstringSíEl ID del plan en el que se creará el cubo
namestringSíNombre para mostrar del nuevo cubo
  • Devuelve: El objeto de cubo creado o null

delete_bucket

Elimina un cubo de Planner.

ParámetroTipoObligatorioDescripción
bucketIdstringSíEl ID del cubo a eliminar
etagstringSíEl @odata.etag actual del cubo (reintenta una vez si está obsoleto)
  • Devuelve: Mensaje de confirmación

Tareas

list_my_tasks

Lista todas las tareas de Planner asignadas al usuario autenticado en todos los planes.

ParámetroTipoObligatorioDescripción
selectstringNoCampos separados por comas a incluir (predeterminado: *all). Pase *all para todos los campos.
filterstringNoExpresión de filtro OData, p. ej., percentComplete ne 100
searchstringNoBúsqueda de texto libre que coincide con el título de la tarea
  • Devuelve: Lista de objetos de tarea o null

list_tasks

Lista todas las tareas en un plan de Planner.

ParámetroTipoObligatorioDescripción
planIdstringSíEl ID del plan
selectstringNoCampos separados por comas a incluir (predeterminado: *all). Pase *all para todos los campos.
filterstringNoExpresión de filtro OData, p. ej., percentComplete eq 0
searchstringNoBúsqueda de texto libre que coincide con el título de la tarea
  • Devuelve: Lista de objetos de tarea o null

get_task_details

Obtiene los detalles completos de una tarea: descripción, elementos de lista de verificación y referencias externas.

ParámetroTipoObligatorioDescripción
taskIdstringSíEl ID de la tarea
  • Devuelve: Objeto de detalles de la tarea (descripción, lista de verificación, referencias) o null

create_task

Crea una nueva tarea en un plan de Planner.

ParámetroTipoObligatorioDescripción
planIdstringSíEl ID del plan
bucketIdstringSíEl ID del cubo donde se colocará la tarea
titlestringSíTítulo de la tarea
startDateTimestringNoFecha de inicio ISO 8601 (p. ej., 2026-05-01T00:00:00)
dueDateTimestringNoFecha de vencimiento ISO 8601 (p. ej., 2026-05-31T00:00:00)
percentCompleteintegerNoPorcentaje de finalización, 0–100
assignUserIdslist[string]NoIDs de objeto de usuario para asignar a la tarea
  • Devuelve: El objeto de tarea creado o null

update_task

Actualiza los campos estándar de una tarea y/o los campos de detalle (descripción, lista de verificación, referencias). Solo se cambian los campos proporcionados. Cuando se especifican campos de detalle, una llamada API separada actualiza automáticamente el recurso de detalles de la tarea.

ParámetroTipoObligatorioDescripción
taskIdstringSíEl ID de la tarea
etagstringSíEl @odata.etag actual de la tarea (reintenta una vez si está obsoleto)
titlestringNoNuevo título
percentCompleteintegerNoPorcentaje de finalización, 0–100
dueDateTimestringNoFecha de vencimiento ISO 8601
bucketIdstringNoID del cubo al que se moverá la tarea
priorityintegerNoPrioridad 0 (urgente)–10 (baja). Planner asigna: 1=urgente, 3=importante, 5=media, 9=baja
assigneePrioritystringNoSugerencia de orden para ordenar dentro de la lista de tareas del asignado
appliedCategoriesobjectNoCategorías clave por ranura de categoría (p. ej., {"category3": true}). Use list_plan_categories para nombres de etiquetas
assignUserIdslist[string]NoIDs de usuario para asignar
unassignUserIdslist[string]NoIDs de usuario para eliminar
descriptionstringNoDescripción de texto plano (hasta 2000 caracteres) — campo de detalle
previewTypestringNoEstilo de vista previa: automatic, noPreview, checklist, description o reference — campo de detalle
checklistItemsobjectNoDict clave por GUID de elemento de lista de verificación. Pase null como clave para eliminar ese elemento — campo de detalle
referencesobjectNoDict clave por URL de referencia codificada. Pase null como clave para eliminar esa referencia — campo de detalle
etagDetailsstringNoEl @odata.etag del recurso de detalles de la tarea. Obligatorio solo al actualizar campos de detalle; se actualiza automáticamente desde Graph si se omite.
  • Devuelve: El objeto de tarea actualizado, el objeto de detalles de tarea actualizado o ambos ({ "task": ..., "details": ... }) según los campos proporcionados. Devuelve null si el resultado está vacío.

Nota: La herramienta update_task_details se ha eliminado. Use update_task con parámetros de campo de detalle (description, checklistItems, references, previewType) en su lugar. Los recursos de tarea y detalles tienen ETags separados: proporcione etagDetails cuando esté disponible para evitar un viaje de ida y vuelta adicional, u omítalo para que la herramienta se actualice automáticamente.

list_task_fields

Devuelve metadatos para cada campo en una tarea de Planner, incluido su tipo de datos, descripción, si es editable y si requiere una llamada get_task_details separada.

  • Parámetros: Ninguno
  • Devuelve: Lista de objetos de metadatos de campo (name, type, description, writable, detailed)

Nota: La lista de campos es estática ya que Planner Standard no admite campos personalizados. Pero este endpoint espera las futuras actualizaciones de Microsoft a la API para admitir completamente Planner Premium.

delete_task

Elimina una tarea de Planner.

ParámetroTipoObligatorioDescripción
taskIdstringSíEl ID de la tarea
etagstringSíEl @odata.etag actual de la tarea (reintenta una vez si está obsoleto)
  • Devuelve: Mensaje de confirmación

Desarrollo

Esta sección cubre la compilación, las pruebas y la contribución al proyecto.

Estructura del Proyecto

src/
├── server.py                  # FastMCP app, middleware, route mounting
├── config.py                  # Settings via pydantic-settings
├── auth_provider.py           # Azure OAuth provider (OBO flow) + optional Redis state store
├── deps.py                    # Shared dependency helpers
├── graph_client_manager.py    # Singleton GraphClientManager with per-user OBO clients
├── telemetry.py               # OpenTelemetry setup
├── types.py                   # Shared structural types
├── services/
│   └── planner_service.py     # Business logic wrapping Graph SDK calls
└── tools/
    ├── me.py                  # get_me
    ├── groups.py              # list_my_groups
    ├── plans.py               # plan tools + list_plan_categories
    ├── tasks.py               # task tools + list_task_fields
    ├── buckets.py             # bucket tools
    └── users.py               # list_users
tests/
├── conftest.py
├── test_auth_provider.py
├── test_buckets_tool.py
├── test_config.py
├── test_groups_tool.py
├── test_planner_service.py
├── test_plans_tool.py
├── test_redis_storage_integration.py  # needs a real Redis (REDIS_TEST_URL)
├── test_tasks_tool.py
└── test_users_tool.py

Instalación de Dependencias de Desarrollo

uv sync --dev

Ejecución de Pruebas

uv run pytest

Con salida detallada:

uv run pytest -v

tests/test_redis_storage_integration.py se ejecuta contra un Redis real y se omite a menos que REDIS_TEST_URL esté configurado (CI proporciona un servicio redis:7-alpine). Apúntelo a una base de datos de prueba: las pruebas escriben y eliminan sus propias claves:

docker run -d --rm --name redis-test -p 6379:6379 redis:7-alpine
REDIS_TEST_URL=redis://localhost:6379/15 uv run pytest tests/test_redis_storage_integration.py -v

Arquitectura

El servidor está construido con FastMCP y utiliza estos patrones clave:

Composición del servidor — Las herramientas se dividen en cinco enrutadores de dominio (me, groups, plans, tasks, buckets), cada uno una instancia FastMCP independiente montada en la aplicación principal. Esto mantiene las herramientas, importaciones y pruebas de cada dominio aisladas.

Autenticación — El servidor utiliza el patrón OAuthProxy de FastMCP a través de un AzureProvider personalizado. Azure Entra ID no admite el Registro Dinámico de Clientes (DCR), por lo que el proveedor actúa como un proxy compatible con DCR frente a los clientes MCP mientras usa las credenciales de la aplicación pre-registrada con Azure. Cuando llega una llamada de herramienta, el servidor intercambia el token de sesión MCP por un token de Microsoft Graph mediante el flujo On-Behalf-Of, con ámbito Tasks.ReadWrite, User.Read y User.ReadBasic.All. El estado del proxy (registros de clientes, tokens de Entra de los usuarios) vive en el almacén de archivos cifrado de FastMCP de forma predeterminada, o en un almacén Redis cifrado cuando REDIS_URL está configurado — consulte Mantener los inicios de sesión entre reinicios.

Middleware — Cinco capas de middleware integrado se apilan en el servidor (primero el más externo):

MiddlewarePropósito
ErrorHandlingMiddlewareCaptura excepciones no controladas, registra trazas completas en el servidor, devuelve errores MCP limpios
SlidingWindowRateLimitingMiddleware60 req/min por cliente para proteger la cuota de Microsoft Graph
TimingMiddlewareRegistra la duración de tiempo real para cada operación MCP
StructuredLoggingMiddlewareEmite una línea de registro JSON por solicitud (método, estado, duración, información del cliente)
ResponseLimitingMiddlewareTrunca las respuestas de herramientas por encima de 500 KB para evitar desbordar las ventanas de contexto de LLM

Registro del cliente — Cada herramienta envía mensajes de progreso en tiempo real al cliente MCP mediante get_optional_context(), para que los usuarios vean actualizaciones de estado como "Obteniendo tareas…" y "Encontradas 12 tarea(s)" en su cliente.

Trazado OpenTelemetry

El servidor incluye instrumentación OpenTelemetry nativa con cero sobrecarga cuando no se usa. Para habilitar la exportación de trazas:

# Install the optional OTEL dependency group
uv sync --group otel

# Set the OTLP endpoint and start the server
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_SERVICE_NAME=microsoft-planner-mcp
uv run uvicorn src.server:app --host 0.0.0.0 --port 8000

Las trazas siguen las convenciones semánticas de MCP y funcionan con cualquier backend compatible con OTLP (Jaeger, Grafana Tempo, Datadog, New Relic, etc.). La configuración de Docker Compose incluye Jaeger con una interfaz de usuario en http://localhost:16686.

Pruebas con MCP Inspector

MCP Inspector le permite probar herramientas de forma interactiva en un navegador:

  1. Inicie el servidor:

    uv run uvicorn src.server:app --host 0.0.0.0 --port 8000
    
  2. En una terminal separada:

    npx @modelcontextprotocol/inspector
    
  3. Abra la URL impresa en la terminal (p. ej., http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=...)

  4. Establezca Tipo de Transporte en Streamable HTTP y URL en http://localhost:8000/mcp, luego haga clic en Conectar.

Contribuciones

¡Las contribuciones son bienvenidas! Abra un problema o envíe una solicitud de extracción. Consulte las plantillas de problemas para informes de errores, solicitudes de funciones y tareas.

Licencia

Este proyecto está licenciado bajo la Licencia MIT.