Phase 13 - Lesson 12
Roots e Elicitação — Escopo e Entrada do Usuário no Meio da Execução
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
Caminhos definidos no código (hard-coded) quebram no momento em que o usuário abre um projeto diferente. Argumentos de ferramentas pré-preenchidos quebram quando o usuário não especifica o suficiente. Roots limitam o escopo do servidor a um conjunto de URIs controlado pelo usuário; a elicitação (elicitation) pausa no meio da chamada da ferramenta para pedir ao usuário uma entrada estruturada por meio de um formulário ou URL. Duas primitivas de cliente, duas correções para modos de falha comuns do MCP. A SEP-1036 (elicitação em modo URL, 2025-11-25) é experimental até o primeiro semestre de 2026 — verifique as versões do SDK antes de depender dela.
Tipo: Build Idiomas: Python (stdlib, demo de roots + elicitação) Pré-requisitos: Fase 13 · 07 (servidor MCP) Tempo: ~45 minutos
Objetivos de Aprendizado
- Declarar
rootse responder anotifications/roots/list_changed. - Restringir operações de arquivo do servidor a URIs dentro do conjunto de roots declarado.
- Usar
elicitation/createpara solicitar ao usuário uma confirmação ou entrada estruturada no meio da chamada da ferramenta. - Escolher entre a elicitação em modo formulário (form-mode) e modo URL (o último é experimental; risco de divergência observado).
O Problema
Duas falhas concretas que um servidor MCP de notas encontra em produção.
Assunção de caminho incorreta. O servidor é escrito considerando ~/notes. Um usuário em uma máquina diferente com notas em ~/Documents/Notes obtém uma chamada de ferramenta que falha silenciosamente (nenhum arquivo encontrado) ou, pior, grava no local errado.
Argumento ausente que o usuário saberia. O usuário solicita "excluir a nota antiga do relatório TPS". O modelo chama notes_delete(title: "TPS report"), mas existem três notas correspondentes de 2023, 2024 e 2025. A ferramenta não tem como adivinhar. Falhar com "ambíguo" é irritante; executar nas três é catastrófico.
As roots corrigem o primeiro problema: o cliente declara no initialize o conjunto de URIs que o servidor pode tocar. A elicitação corrige o segundo: o servidor pausa a chamada da ferramenta e envia elicitation/create para pedir ao usuário que escolha qual delas.
O Conceito
Roots
O cliente declara uma lista de roots no initialize:
{
"capabilities": {"roots": {"listChanged": true}}
}
O servidor pode então chamar roots/list:
{"roots": [{"uri": "file:///Users/alice/Documents/Notes", "name": "Notes"}]}
Os servidores DEVEM tratar as roots como o limite: qualquer leitura ou gravação de arquivo fora do conjunto de roots é rejeitada. Isso não é imposto pelo cliente (o servidor ainda é um código no qual o usuário confiou), mas os servidores compatíveis com a especificação o respeitam.
Quando o usuário adiciona ou remove uma raiz, o cliente envia notifications/roots/list_changed. O servidor chama roots/list novamente e atualiza seu limite.
Por que as roots são uma primitiva de cliente
As roots são declaradas pelo cliente porque representam o modelo de consentimento do usuário. O usuário disse ao Claude Desktop "dê a este servidor de notas acesso a estes dois diretórios". O servidor não pode ampliar esse escopo.
Elicitação: o padrão do modo formulário (form-mode)
O método elicitation/create recebe um esquema de formulário e um prompt em linguagem natural:
{
"method": "elicitation/create",
"params": {
"message": "Delete 'TPS report'? Multiple notes match; pick one.",
"requestedSchema": {
"type": "object",
"properties": {
"note_id": {
"type": "string",
"enum": ["note-3", "note-7", "note-14"]
},
"confirm": {"type": "boolean"}
},
"required": ["note_id", "confirm"]
}
}
}
O cliente renderiza um formulário, coleta a resposta do usuário e retorna:
{
"action": "accept",
"content": {"note_id": "note-14", "confirm": true}
}
Três ações possíveis: accept (usuário preencheu), decline (usuário fechou), cancel (usuário abortou toda a chamada de ferramenta).
Os esquemas de formulário são planos (flat) — objetos aninhados não são suportados na v1. Os SDKs normalmente rejeitam qualquer coisa mais complexa do que uma única camada.
Elicitação: modo URL (SEP-1036, experimental)
Novidade em 2025-11-25. Em vez de um esquema, o servidor envia uma URL:
{
"method": "elicitation/create",
"params": {
"message": "Sign in to GitHub",
"url": "https://github.com/login/oauth/authorize?client_id=..."
}
}
O cliente abre a URL em um navegador, aguarda a conclusão e retorna quando o usuário volta. Útil para fluxos de OAuth, autorização de pagamento e assinatura de documentos onde um formulário é insuficiente.
Nota sobre risco de divergência (drift-risk): o formato da resposta da SEP-1036 ainda está se definindo; alguns SDKs retornam a URL de retorno de chamada (callback URL), outros retornam um token de conclusão. Leia as notas de lançamento do seu SDK antes de usar o modo URL em produção.
Quando a elicitação é a ferramenta certa
- Confirmação do usuário antes de ações destrutivas (dica destrutiva + elicitação).
- Resolução de ambiguidade (escolher uma de N correspondências).
- Configuração inicial na primeira execução (chaves de API, diretórios, preferências).
- Fluxos estilo OAuth (modo URL).
Quando a elicitação está incorreta
- Preencher argumentos obrigatórios de uma ferramenta que o modelo poderia ter solicitado em prosa. Use um novo prompt normal, não uma caixa de diálogo de elicitação.
- Chamadas de alta frequência. A elicitação interrompe a conversa; não a execute dentro de um loop.
- Qualquer coisa que o servidor possa validar após o fato. Valide, retorne um erro e deixe o modelo perguntar ao usuário por texto.
Ponte para o "Human-in-the-loop"
A elicitação combinada com o sampling ativam o modelo "human-in-the-loop" (humano no circuito) do MCP. O loop de agente de um servidor pode pausar para entrada do usuário (elicitação) ou para raciocínio do modelo (sampling). A Fase 13 · 11 cobriu o sampling; esta lição cobre a elicitação. Junte-os para ter controle total no meio do loop.
Use
O arquivo code/main.py estende o servidor de notas com:
- Resposta de
roots/listque o servidor consulta novamente após notificações de alteração na lista de roots. - Uma ferramenta
notes_deleteque usaelicitation/createpara desambiguar quando várias notas correspondem. - Uma ferramenta
notes_setupque usa elicitação em modo URL para abrir uma página de configuração de primeira execução (simulada). - Uma verificação de limite que recusa operações em URIs fora das roots declaradas.
A demonstração executa três cenários: caminho feliz (uma correspondência), desambiguação (três correspondências, a elicitação é disparada), gravação fora da raiz (rejeitada).
Entregue
Esta lição produz o arquivo outputs/skill-elicitation-form-designer.md. Dada uma ferramenta que pode precisar de confirmação ou desambiguação do usuário, a habilidade (skill) projeta o esquema do formulário de elicitação e o modelo de mensagem.
Exercícios
Execute
code/main.py. Dispare o caminho de desambiguação; confirme se a resposta simulada do usuário é enviada de volta para a ferramenta.Adicione uma nova ferramenta
notes_archiveque requer confirmação de elicitação todas as vezes (dica destrutiva). Verifique a experiência do usuário (UX): como isso se compara ao modelo perguntando novamente em texto?Implemente a elicitação em modo URL para um fluxo de OAuth na primeira execução. Observe o risco de divergência e adicione uma proteção de versão do SDK.
Estenda o tratamento de
roots/list: quando uma notificação chegar, o servidor deve reler e verificar novamente de forma atômica os manipuladores de arquivos abertos que agora podem estar fora do escopo.Leia o tópico de discussão da issue SEP-1036 no GitHub. Identifique uma questão aberta que afeta como os servidores devem lidar com retornos de chamada (callbacks) em modo URL.
Termos-Chave
| Termo | O que as pessoas dizem | O que realmente significa |
|---|---|---|
| Root | "Limite de consentimento" | URI que o cliente permitiu que o servidor acessasse |
roots/list |
"O servidor solicita o escopo" | O cliente retorna o conjunto atual de roots |
notifications/roots/list_changed |
"O usuário alterou o escopo" | O cliente sinaliza que o conjunto de roots foi modificado |
| Elicitação | "Perguntar ao usuário no meio da chamada" | Solicitação iniciada pelo servidor para entrada estruturada do usuário |
elicitation/create |
"O método" | Método JSON-RPC para solicitações de elicitação |
| Modo Formulário | "Formulário baseado em esquema" | JSON Schema plano (flat) renderizado como um formulário na interface do usuário do cliente |
| Modo URL | "Redirecionamento do navegador" | SEP-1036 experimental; abre uma URL e aguarda |
accept / decline / cancel |
"Resultados de resposta do usuário" | Três ramificações que o servidor trata |
| Desambiguação | "Escolha um" | Caso de uso comum de elicitação quando uma ferramenta possui N candidatos |
| Formulário plano (flat) | "Apenas propriedades de nível superior" | Esquemas de elicitação não podem ser aninhados |
Leitura Adicional
- MCP — Client roots spec — referência canônica de roots
- MCP — Client elicitation spec — referência canônica de elicitação
- Cisco — What's new in MCP elicitation, structured content, OAuth enhancements — passo a passo das adições de 2025-11-25
- MCP — GitHub SEP-1036 — proposta de elicitação em modo URL (experimental, risco de divergência)
- The New Stack — How elicitation brings human-in-the-loop to AI tools — passo a passo de UX