Microsoft Planner MCP

Um servidor MCP que conecta assistentes de IA ao Microsoft Planner. Autenticado via Entra ID para autenticação empresarial em nome do usuário.

Documentação

Servidor MCP para Microsoft Planner

Um servidor MCP não oficial que conecta assistentes de IA ao Microsoft Planner. Peça ao seu assistente de IA para criar tarefas, organizar planos, gerenciar buckets e muito mais, tudo por meio de linguagem natural.

Este é um servidor MCP não oficial, comunitário/de código aberto para o Microsoft Planner.
Não é afiliado, endossado ou patrocinado pela Microsoft.

Construído com FastMCP e autenticado via Microsoft Entra ID (Azure AD) usando o fluxo On-Behalf-Of (OBO) para chamar o Microsoft Graph.

Sumário

O Que Ele Pode Fazer?

Depois de conectado, você pode pedir ao seu assistente de IA coisas como:

  • "Mostre-me todas as minhas tarefas do Planner que estão atrasadas"
  • "Crie uma tarefa chamada 'Preparar relatório do 3º trimestre' no plano de Marketing"
  • "Mova todas as tarefas incompletas do bucket Sprint para o bucket Backlog"
  • "Marque a tarefa 'Atualizar documentos' como concluída"

O assistente de IA traduz automaticamente sua solicitação nas chamadas de ferramenta apropriadas.

Observação: Este MCP suporta apenas tarefas e planos básicos do Planner

Pré-requisitos

Para executar o servidor localmente, você também precisa de:

Ou, para executar via Docker:

Configuração do Azure Entra ID

Um registro de aplicativo do Azure é necessário para que o servidor possa autenticar usuários e chamar o Microsoft Graph em nome deles. Você precisa de acesso de administrador a um locatário do Azure Entra ID (ou peça ao seu administrador de TI).

Etapa 1 — Registrar o Aplicativo

  1. Vá para o Portal do Azure → Microsoft Entra ID → Registros de aplicativo → Novo registro
  2. Digite um nome (por exemplo, Microsoft Planner MCP)
  3. Em Tipos de conta com suporte, escolha a opção apropriada para sua organização
  4. Defina o URI de redirecionamento como Web → http://localhost:8000/auth/callback
  5. Clique em Registrar

Etapa 2 — Configurar Permissões de API

  1. No seu novo registro de aplicativo, vá para Permissões de API → Adicionar uma permissão → Microsoft Graph → Permissões delegadas
  2. Adicione estas permissões:
    • Tasks.ReadWrite — ler e gravar tarefas do Planner
    • User.Read — ler o perfil do usuário conectado
    • User.ReadBasic.All — resolver nomes de exibição de usuário a partir dos GUIDs nas atribuições de tarefas (necessário apenas para a ferramenta list_users)
  3. Clique em Conceder consentimento do administrador para sua organização

Etapa 3 — Expor um Escopo de API

  1. Vá para Expor uma API
  2. Defina o URI da ID do aplicativo (aceite o padrão api://<client-id> ou personalize-o)
  3. Clique em Adicionar um escopo:
    • Nome do escopo: mcp-access
    • Quem pode consentir: Administradores e usuários (ou Somente administradores, se preferir)
    • Preencha o nome de exibição e a descrição
    • Defina o estado como Habilitado

Etapa 4 — Definir a Versão do Token

  1. Vá para Manifesto (ou Autenticação → Configurações avançadas nas versões mais recentes do portal)
  2. Defina "requestedAccessTokenVersion" como 2
  3. Salve

Etapa 5 — Criar um Segredo do Cliente

  1. Vá para Certificados e segredos → Novo segredo do cliente
  2. Adicione uma descrição e escolha um período de expiração
  3. Copie o Valor imediatamente (ele é mostrado apenas uma vez)

Etapa 6 — Anote Seus IDs

Você precisará destes três valores para a configuração:

ValorOnde Encontrar
ID do aplicativo (cliente)Registro do aplicativo → Visão geral
ID do diretório (locatário)Registro do aplicativo → Visão geral
Segredo do clienteO valor copiado na Etapa 5

Instalação

Opção A — Local (Python + uv)

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

Opção B — Docker

Nenhuma instalação do Python é necessária. Veja Executando com Docker abaixo.

Configuração

Copie o arquivo de ambiente de exemplo e preencha suas credenciais do Azure:

cp .env.example .env

Edite .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 se o FastMCP solicita que os usuários aprovem explicitamente cada novo cliente MCP. Mantenha true (padrão) em produção para evitar ataques de "deputado confuso"; defina como false apenas durante o desenvolvimento local com clientes descartáveis.

RATE_LIMIT_MAX_REQUESTS e RATE_LIMIT_WINDOW_MINUTES são opcionais e devem permanecer comentados, a menos que você precise substituir os padrões do seu ambiente.

Mantendo logins entre reinicializações (Redis)

Por padrão, o servidor mantém o estado OAuth — registros de clientes e tokens Entra dos usuários — em um armazenamento de arquivos criptografado dentro do contêiner. Esse estado é perdido quando o contêiner é recriado ou um Azure Container App é reduzido a zero, e cada réplica tem sua própria cópia. Os clientes então falham ao atualizar com 401 invalid_client e os usuários precisam se reconectar.

Defina REDIS_URL quando você executar mais de uma réplica, reduzir a escala para zero ou recriar contêineres:

VariávelPadrãoFinalidade
REDIS_URLnão definido (armazenamento de arquivos)redis:// ou rediss:// (TLS — exigido pelo Azure Redis). O caminho é o índice do banco de dados (por exemplo, /1). Codifique em percentual a senha se ela contiver / (as chaves de acesso do Azure podem conter): python -c "import urllib.parse; print(urllib.parse.quote('<key>', safe=''))". Um Redis não clusterizado é necessário (Redis Cluster suporta apenas o banco de dados 0 e não é suportado). Contém uma credencial: mantenha-a em um segredo.
JWT_SIGNING_KEYderivado de CLIENT_SECRETSegredo fixo (pelo menos 32 caracteres) para assinar os tokens do servidor. Defina-o para que a rotação do segredo do cliente Entra não desconecte todos os usuários. Use o mesmo valor em todas as réplicas.
STORAGE_ENCRYPTION_KEYderivado como o padrão do FastMCPChave Fernet que criptografa o armazenamento Redis (gere: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"). Exige REDIS_URL — o servidor se recusa a iniciar sem ela. Defina-a explicitamente em produção: o padrão derivado segue o esquema de chave interno do FastMCP, portanto, é tão estável quanto esse esquema.

Tudo o que é gravado no Redis é criptografado com Fernet. Tokens e transações de login expiram com os tempos de vida do FastMCP (tokens de atualização por 30 dias, transações por minutos), mas os registros de clientes nunca expiram — um é adicionado por registro de cliente MCP, e entradas gravadas sob uma chave antiga permanecem como lixo ilegível. Dê ao armazenamento seu próprio índice de banco de dados (ou seu próprio Redis) em vez de compartilhar um banco de dados com um cache, e use uma política de despejo que não o descarte silenciosamente (noeviction, ou volatile-* com memória suficiente). Um REDIS_URL, JWT_SIGNING_KEY ou STORAGE_ENCRYPTION_KEY inválido falha na inicialização; um valor vazio (JWT_SIGNING_KEY=) conta como não definido. Se o Redis estiver inacessível em tempo de execução, solicitações OAuth e chamadas de ferramenta falham com 500 em cerca de 4 segundos e se recuperam sozinhas quando o Redis volta.

Ativar essas configurações desconecta todos os usuários uma vez. Ativar REDIS_URL, definir JWT_SIGNING_KEY ou alterar qualquer uma das chaves posteriormente invalida logins existentes: clientes conectados recebem um 401 na próxima chamada ou atualização e precisam se reconectar uma vez. O estado do armazenamento de arquivos existente não é migrado. Planeje a mudança.

Executando o Servidor

Local

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

O endpoint MCP está disponível em http://localhost:8000/mcp. Um endpoint de verificação de saúde está em http://localhost:8000/health.

Executando com 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

Isso inicia o servidor MCP na porta 8000. Para substituir os limites de taxa, passe também -e RATE_LIMIT_MAX_REQUESTS=<value> e -e RATE_LIMIT_WINDOW_MINUTES=<minutes> (padrões: 120 e 1). Para manter logins entre recriações de contêiner, passe também -e REDIS_URL=... (veja Mantendo logins entre reinicializações).

Executando com Docker compose

docker compose up

Isso inicia o servidor MCP na porta 8000. A configuração do Docker Compose também inclui uma instância Jaeger para visualização de rastreamento (veja Rastreamento OpenTelemetry).

Conectando um Cliente MCP

Depois que o servidor estiver em execução, configure seu cliente MCP para se conectar a ele.

VS Code

Adicione o seguinte às configurações do seu VS Code (.vscode/settings.json no seu projeto, ou nas configurações do usuário):

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

Em seguida, use o Copilot Chat no modo Agente e peça para interagir com suas tarefas do Planner. O Copilot descobrirá as ferramentas disponíveis automaticamente.

Outros Clientes MCP

Qualquer cliente que suporte o transporte Streamable HTTP pode se conectar apontando para http://localhost:8000/mcp. O servidor anuncia metadados OAuth automaticamente — o cliente lida com o fluxo de autenticação.

Ferramentas Disponíveis

Todas as ferramentas estão disponíveis para seu assistente de IA automaticamente após a conexão. Você não precisa chamá-las diretamente — basta descrever o que deseja em linguagem natural. Os detalhes dos parâmetros abaixo são fornecidos para referência e para desenvolvedores de clientes.

Ferramentas somente leitura são anotadas com readOnlyHint: true para que os clientes possam pular prompts de confirmação. Ferramentas destrutivas (exclusões) são anotadas com destructiveHint: true.

Usuário

get_me

Retorna o perfil do usuário autenticado do Microsoft Graph.

  • Parâmetros: Nenhum
  • Retorna: Objeto de perfil do usuário (id, displayName, mail, etc.) ou null

list_users

Recupera usuários do Microsoft 365 por GUID, endereço de e-mail ou pesquisa de texto livre. Útil para resolver os GUIDs de usuário retornados nos objetos de atribuição de tarefas para nomes de exibição.

ParâmetroTipoObrigatórioDescrição
selectstringNãoCampos separados por vírgula a incluir (padrão: id,displayName,mail,userPrincipalName). Passe *all para todos os campos.
searchstringNãoPesquisa de texto livre no nome de exibição, ou pesquisa por nome e valor do campo, por exemplo, Alice ou surname:Smith
guidslist[string]NãoGUIDs de objetos de usuário para consultar. Traduzido para uma expressão OData $filter.
emailslist[string]NãoNomes principais de usuário (UPNs / endereços de e-mail) para consultar. Traduzido para uma expressão OData $filter.
topintegerNãoNúmero máximo de usuários a retornar (padrão: 10). Ignorado quando guids ou emails são fornecidos.
  • Retorna: Lista de objetos de usuário ou null

Observação: Quando tanto guids/emails quanto search são fornecidos, o filtro de GUID/e-mail tem prioridade. Listas muito grandes de GUIDs ou endereços de e-mail são silenciosamente truncadas para permanecer dentro do limite de URL do Graph de 2.048 caracteres.

Grupos

list_my_groups

Lista todos os grupos do Microsoft 365 dos quais o usuário autenticado é membro.

ParâmetroTipoObrigatórioDescrição
selectstringNãoCampos separados por vírgula a incluir (padrão: id,displayName,mail). Passe *all para todos os campos.
filterstringNãoExpressão de filtro OData, por exemplo, startsWith(displayName,'Project')
searchstringNãoString de pesquisa OData, por exemplo, "displayName:Project"
  • Retorna: Lista de objetos de grupo ou null

Observação: A ferramenta Grupos não retornará detalhes como nome ou e-mail de grupos com as permissões padrão Tasks.ReadWrite documentadas aqui, no entanto pesquisa e filtragem ainda funcionam, então você pode encontrar um grupo com um nome específico usando esta ferramenta.

Planos

list_my_plans

Lista os planos do Planner compartilhados com o usuário autenticado.

ParâmetroTipoObrigatórioDescrição
selectstringNãoCampos separados por vírgula a incluir (padrão: id,title,owner,createdBy,createdDateTime). Passe *all para todos os campos.
  • Retorna: Lista de objetos de plano ou null

list_group_plans

Lista todos os planos do Planner pertencentes a um grupo do Microsoft 365.

ParâmetroTipoObrigatórioDescrição
groupIdstringSimO ID do objeto do grupo (de list_my_groups)
selectstringNãoCampos separados por vírgula para incluir (padrão: id,title,owner,createdBy,createdDateTime). Passe *all para todos os campos.
  • Retorna: Lista de objetos de plano ou null

create_plan

Cria um novo plano do Planner para um grupo do Microsoft 365.

ParâmetroTipoObrigatórioDescrição
groupIdstringSimO ID do objeto do grupo M365 que será o proprietário do plano
titlestringSimTítulo de exibição para o novo plano
  • Retorna: O objeto de plano criado ou null

delete_plan

Exclui um plano do Planner.

ParâmetroTipoObrigatórioDescrição
planIdstringSimO ID do plano a ser excluído
etagstringSimO @odata.etag atual do plano (tenta novamente uma vez se estiver desatualizado)
  • Retorna: Mensagem de confirmação

list_plan_categories

Obtém as definições de rótulos de categoria para um plano do Planner. Retorna todos os 25 slots de categoria com sua chave (ex.: category1) e nome de exibição.

ParâmetroTipoObrigatórioDescrição
planIdstringSimO ID do plano
  • Retorna: Lista de objetos de categoria (key, display_name) ou null

Buckets

list_buckets

Lista todos os buckets em um plano do Planner.

ParâmetroTipoObrigatórioDescrição
planIdstringSimO ID do plano
  • Retorna: Lista de objetos de bucket ou null

create_bucket

Cria um novo bucket em um plano do Planner.

ParâmetroTipoObrigatórioDescrição
planIdstringSimO ID do plano no qual criar o bucket
namestringSimNome de exibição para o novo bucket
  • Retorna: O objeto de bucket criado ou null

delete_bucket

Exclui um bucket do Planner.

ParâmetroTipoObrigatórioDescrição
bucketIdstringSimO ID do bucket a ser excluído
etagstringSimO @odata.etag atual do bucket (tenta novamente uma vez se estiver desatualizado)
  • Retorna: Mensagem de confirmação

Tarefas

list_my_tasks

Lista todas as tarefas do Planner atribuídas ao usuário autenticado em todos os planos.

ParâmetroTipoObrigatórioDescrição
selectstringNãoCampos separados por vírgula para incluir (padrão: *all). Passe *all para todos os campos.
filterstringNãoExpressão de filtro OData, ex.: percentComplete ne 100
searchstringNãoPesquisa de texto livre correspondente ao título da tarefa
  • Retorna: Lista de objetos de tarefa ou null

list_tasks

Lista todas as tarefas em um plano do Planner.

ParâmetroTipoObrigatórioDescrição
planIdstringSimO ID do plano
selectstringNãoCampos separados por vírgula para incluir (padrão: *all). Passe *all para todos os campos.
filterstringNãoExpressão de filtro OData, ex.: percentComplete eq 0
searchstringNãoPesquisa de texto livre correspondente ao título da tarefa
  • Retorna: Lista de objetos de tarefa ou null

get_task_details

Obtém os detalhes completos de uma tarefa: descrição, itens de lista de verificação e referências externas.

ParâmetroTipoObrigatórioDescrição
taskIdstringSimO ID da tarefa
  • Retorna: Objeto de detalhes da tarefa (descrição, lista de verificação, referências) ou null

create_task

Cria uma nova tarefa em um plano do Planner.

ParâmetroTipoObrigatórioDescrição
planIdstringSimO ID do plano
bucketIdstringSimO ID do bucket no qual colocar a tarefa
titlestringSimTítulo da tarefa
startDateTimestringNãoData de início ISO 8601 (ex.: 2026-05-01T00:00:00)
dueDateTimestringNãoData de vencimento ISO 8601 (ex.: 2026-05-31T00:00:00)
percentCompleteinteiroNãoPercentual de conclusão, 0–100
assignUserIdslista[string]NãoIDs de objeto de usuário para atribuir à tarefa
  • Retorna: O objeto de tarefa criado ou null

update_task

Atualiza os campos padrão e/ou campos de detalhes de uma tarefa (descrição, lista de verificação, referências). Somente os campos fornecidos são alterados. Quando campos de detalhes são especificados, uma chamada de API separada atualiza automaticamente o recurso de detalhes da tarefa.

ParâmetroTipoObrigatórioDescrição
taskIdstringSimO ID da tarefa
etagstringSimO @odata.etag atual da tarefa (tenta novamente uma vez se estiver desatualizado)
titlestringNãoNovo título
percentCompleteinteiroNãoPercentual de conclusão, 0–100
dueDateTimestringNãoData de vencimento ISO 8601
bucketIdstringNãoID do bucket para mover a tarefa
priorityinteiroNãoPrioridade 0 (urgente)–10 (baixa). O Planner mapeia: 1=urgente, 3=importante, 5=média, 9=baixa
assigneePrioritystringNãoDica de ordem para classificação na lista de tarefas do responsável
appliedCategoriesobjetoNãoCategorias por slot de categoria (ex.: {"category3": true}). Use list_plan_categories para nomes de rótulos
assignUserIdslista[string]NãoIDs de usuário para atribuir
unassignUserIdslista[string]NãoIDs de usuário para remover
descriptionstringNãoDescrição em texto simples (até 2000 caracteres) — campo de detalhes
previewTypestringNãoEstilo de visualização: automatic, noPreview, checklist, description ou reference — campo de detalhes
checklistItemsobjetoNãoDicionário por GUID de item da lista de verificação. Passe null como chave para excluir esse item — campo de detalhes
referencesobjetoNãoDicionário por URL de referência codificada. Passe null como chave para excluir essa referência — campo de detalhes
etagDetailsstringNãoO @odata.etag do recurso de detalhes da tarefa. Obrigatório somente ao atualizar campos de detalhes; atualizado automaticamente do Graph se omitido.
  • Retorna: O objeto de tarefa atualizado, o objeto de detalhes da tarefa atualizado ou ambos ({ "task": ..., "details": ... }), dependendo de quais campos foram fornecidos. Retorna null se o resultado estiver vazio.

Nota: A ferramenta update_task_details foi removida. Use update_task com parâmetros de campos de detalhes (description, checklistItems, references, previewType) em vez disso. Os recursos de tarefa e detalhes têm ETags separados — forneça etagDetails quando disponível para evitar uma ida e volta extra, ou omita-o para permitir que a ferramenta atualize automaticamente.

list_task_fields

Retorna metadados para cada campo em uma tarefa do Planner, incluindo seu tipo de dados, descrição, se é gravável e se requer uma chamada separada de get_task_details.

  • Parâmetros: Nenhum
  • Retorna: Lista de objetos de metadados de campo (name, type, description, writable, detailed)

Nota: A lista de campos é estática, pois o Planner Standard não suporta campos personalizados. Mas este endpoint aguarda as futuras atualizações da Microsoft na API para suportar totalmente o Planner Premium.

delete_task

Exclui uma tarefa do Planner.

ParâmetroTipoObrigatórioDescrição
taskIdstringSimO ID da tarefa
etagstringSimO @odata.etag atual da tarefa (tenta novamente uma vez se estiver desatualizado)
  • Retorna: Mensagem de confirmação

Desenvolvimento

Esta seção cobre construção, testes e contribuição para o projeto.

Estrutura do Projeto

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

Instalando Dependências de Desenvolvimento

uv sync --dev

Executando Testes

uv run pytest

Com saída detalhada:

uv run pytest -v

tests/test_redis_storage_integration.py é executado contra um Redis real e é ignorado a menos que REDIS_TEST_URL esteja definido (o CI fornece um serviço redis:7-alpine). Aponte-o para um banco de dados de teste — os testes gravam e excluem suas próprias chaves:

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

Arquitetura

O servidor é construído com FastMCP e usa estes padrões principais:

Composição do servidor — As ferramentas são divididas em cinco roteadores de domínio (me, groups, plans, tasks, buckets), cada um uma instância independente de FastMCP montada no aplicativo principal. Isso mantém as ferramentas, importações e testes de cada domínio isolados.

Autenticação — O servidor usa o padrão OAuthProxy do FastMCP por meio de um AzureProvider personalizado. O Azure Entra ID não suporta Registro Dinâmico de Cliente (DCR), então o provedor atua como um proxy compatível com DCR voltado para clientes MCP enquanto usa as credenciais de aplicativo pré-registradas com o Azure. Quando uma chamada de ferramenta chega, o servidor troca o token de sessão MCP por um token do Microsoft Graph por meio do fluxo On-Behalf-Of, com escopo para Tasks.ReadWrite, User.Read e User.ReadBasic.All. O estado do proxy (registros de clientes, tokens Entra dos usuários) reside no armazenamento de arquivos criptografado do FastMCP por padrão, ou em um armazenamento Redis criptografado quando REDIS_URL está definido — consulte Mantendo logins entre reinicializações.

Middleware — Cinco camadas de middleware integrado são empilhadas no servidor (mais externa primeiro):

MiddlewareFinalidade
ErrorHandlingMiddlewareCaptura exceções não tratadas, registra rastreamentos completos no lado do servidor, retorna erros MCP limpos
SlidingWindowRateLimitingMiddleware60 req/min por cliente para proteger a cota do Microsoft Graph
TimingMiddlewareRegistra a duração em tempo real para cada operação MCP
StructuredLoggingMiddlewareEmite uma linha de log JSON por solicitação (método, status, duração, informações do cliente)
ResponseLimitingMiddlewareTrunca respostas de ferramentas acima de 500 KB para evitar estouro dos contextos de janela do LLM

Registro de cliente — Cada ferramenta envia mensagens de progresso em tempo real ao cliente MCP por meio de get_optional_context(), para que os usuários vejam atualizações de status como "Buscando tarefas…" e "Encontradas 12 tarefa(s)" em seu cliente.

Rastreamento OpenTelemetry

O servidor inclui instrumentação OpenTelemetry nativa com zero sobrecarga quando não utilizada. Para habilitar a exportação de rastreamento:

# 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

Os rastreamentos seguem as convenções semânticas MCP e funcionam com qualquer backend compatível com OTLP (Jaeger, Grafana Tempo, Datadog, New Relic, etc.). A configuração do Docker Compose inclui Jaeger com uma interface em http://localhost:16686.

Testando com o MCP Inspector

O MCP Inspector permite testar ferramentas interativamente em um navegador:

  1. Inicie o servidor:

    uv run uvicorn src.server:app --host 0.0.0.0 --port 8000
    
  2. Em um terminal separado:

    npx @modelcontextprotocol/inspector
    
  3. Abra a URL impressa no terminal (ex.: http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=...)

  4. Defina Tipo de Transporte como Streamable HTTP e URL como http://localhost:8000/mcp, depois clique em Conectar.

Contribuindo

Contribuições são bem-vindas! Abra uma issue ou envie um pull request. Consulte os modelos de issue para relatórios de bugs, solicitações de recursos e tarefas.

Licença

Este projeto é licenciado sob a Licença MIT.