MCP é o SMART on FHIR dos agentes (e o que falta copiar)

A autorização do MCP já é OAuth 2.1 com escopo e 403. Falta o que o SMART on FHIR tem: gramática de escopo comum, contexto de lançamento e acesso sem humano.

Rogério Rodrigues

Rogério Rodrigues

Anotação à mão: o MCP já tem OAuth 2.1, RFC 9728, RFC 8707 e 403 com scope; falta copiar gramática de escopo, contexto de lançamento e caminho pra agente sem ninguém na tela.

A especificação de autorização do MCP (versão 2026-07-28) já trata o servidor MCP como resource server OAuth 2.1, exige metadata de recurso protegido (RFC 9728), amarra o token ao servidor com resource (RFC 8707) e recomenda (SHOULD) devolver 403 com scope= quando falta permissão. É a mesma fundação do SMART App Launch 2.2.0 do HL7. O que ainda falta copiar da saúde são três coisas: uma gramática de escopo comum, um contexto de lançamento que diz “este paciente, este atendimento”, e um caminho padrão pra agente que roda sem ninguém na frente da tela.

O que o SMART on FHIR resolveu na saúde

Antes do SMART, cada prontuário eletrônico tinha seu jeito de deixar um app entrar. Quem escrevia um app clínico escrevia uma integração por fornecedor. O projeto começou em Harvard e no Boston Children’s em 2010, adotou o FHIR em 2013 e foi descrito no JAMIA em 2016 (Mandel et al.). A ideia era escrever o app uma vez e rodar sem mudança em qualquer sistema.

A solução não inventou protocolo. Pegou OAuth 2.0 e fixou as partes que ficavam soltas:

  • Escopo com gramática. patient/Observation.rs quer dizer ler e buscar Observation do paciente em contexto. Prefixo patient/, user/ ou system/, recurso FHIR, e as letras c r u d s.
  • Contexto de lançamento. O prontuário abre o app com iss e launch. O app devolve o launch na autorização e recebe, junto com o token, o patient e o encounter que estavam na tela.
  • Token preso ao servidor certo. O parâmetro aud é obrigatório e, segundo a spec, existe pra não vazar um token legítimo pra um servidor falso.
  • Acesso sem usuário. O perfil Backend Services cobre serviço que roda sozinho: client credentials, autenticação com chave assimétrica, escopos system/ e token curto.

A versão publicada hoje é a 2.2.0, de 30/04/2024. A 1.0.0 saiu em 13/11/2018.

Peça por peça: SMART App Launch e autorização do MCP

Problema SMART App Launch 2.2.0 MCP, spec 2026-07-28
Papel do servidor Servidor FHIR protegido por OAuth Servidor MCP é resource server OAuth 2.1; quem emite token é o servidor de autorização
Descoberta .well-known/smart-configuration Protected Resource Metadata (RFC 9728), obrigatório
Token preso ao servidor aud obrigatório resource obrigatório (RFC 8707) e validação de audiência
Gramática de escopo patient/Recurso.cruds, igual em todo servidor Livre. O exemplo da spec é files:read
Contexto de lançamento launch, launch/patient, fhirContext Não existe
Recusa por escopo Servidor concede menos que o pedido 403 insufficient_scope com scope= e step-up
Sem humano Backend Services, escopos system/ Client credentials permitido; identidade de agente está no roadmap

A metade de baixo da tabela é onde a coisa pega. O MCP fez bem a parte de OAuth. A parte clínica do SMART, que é dizer sobre quem e com qual gramática, ainda não tem par.

Os próprios mantenedores dizem isso

No post do roadmap de 22/08/2026, os mantenedores do MCP escreveram: “MCP authorization today is built around a person approving access in a browser”. E completaram que cada vez mais quem chama são agentes rodando como carga de nuvem, com identidade própria, em nome de um usuário que não está presente. A saída que eles apontam é Workload Identity Federation e token exchange. A saúde passou por isso com o Backend Services. O desenho está lá pra ser lido.

A prova curta: escopo SMART por ferramenta MCP

Na demo 05 do repositório do projeto NursIA (PPGINFOS/UFSC, bolsa FAPESC), montei um servidor MCP com duas ferramentas e mapeei cada uma pra um escopo SMART v2. O mapa é a ideia inteira:

TOOL_SCOPES = {
    "read_patient": "patient/Patient.r",
    "create_observation": "patient/Observation.c",
}

Rodei em 15/09/2026 com o SDK oficial do MCP em Python, versão 1.27.0, e dado sintético. Quatro chamadas, duas recusadas:

Chamada Escopos do token Resultado
read_patient nenhum 401 com resource_metadata
read_patient patient/Patient.r 200
create_observation patient/Patient.r 403 com scope="patient/Observation.c"
create_observation patient/Patient.r patient/Observation.c 200

O header da recusa saiu assim:

HTTP 403
WWW-Authenticate: Bearer error="insufficient_scope", scope="patient/Observation.c", resource_metadata="http://127.0.0.1:8000/.well-known/oauth-protected-resource/mcp"

Funciona. Mas deu trabalho que não devia dar. O SDK só conhece required_scopes pro servidor inteiro, sem gancho por ferramenta. A recusa por ferramenta são 36 linhas minhas que leem o corpo JSON-RPC. E o 403 do próprio SDK põe o escopo faltante dentro do error_description, em texto, e não no scope= que a spec diz que o servidor deveria mandar. O passo a passo completo está em escopos MCP com FHIR na prática.

O que a analogia não cobre

Tese boa tem borda. Essa tem cinco.

App SMART é código fixo. Agente decide na hora.

O SMART autoriza um app que faz o que foi programado pra fazer. Um agente com o mesmo escopo escolhe o que chamar a partir de texto que entra no contexto, inclusive texto malicioso. Escopo limita o que o agente pode. Não diz nada sobre o que ele vai fazer dentro desse limite.

Ferramenta não é recurso

Escopo FHIR fala de dado: Patient, Observation. Ferramenta MCP fala de ação. Uma ferramenta “resumir internação” pode ler cinco tipos de recurso. Ou você lista todos os escopos dela, ou cria um escopo de ação que nenhum servidor FHIR entende.

Sem paciente em contexto

Em SMART, patient/ vale pro paciente que veio no lançamento. Na demo 05 o patient_id é argumento da ferramenta. Nada impede o token de ler outro paciente. É a lacuna mais séria, e o MCP não tem onde pendurar esse contexto hoje.

Agente chamando servidor que chama servidor

A spec do MCP proíbe repassar token: o servidor “MUST NOT accept or transit any other tokens”. Então o servidor MCP precisa do próprio token pra falar com o servidor FHIR. O SMART não resolve essa cadeia. Token exchange resolve, e ainda não está fechado no MCP.

Escopo não é consentimento

Escopo concedido não é base legal na LGPD. O SMART nunca prometeu isso, e copiar a gramática não muda o fato.

Limites desta tese

  • A demo usa token estático. Não tem servidor de autorização, login nem consentimento.
  • Hierarquia de escopo não está implementada. A spec do MCP diz que o servidor deve considerar que um escopo amplo implica os estreitos.
  • O gate por ferramenta mexe no objeto da rota depois que o SDK a monta. Não é API pública e pode quebrar na próxima versão.
  • Autorização do MCP só vale em transporte HTTP. Em stdio o token é sempre vazio e nada disso roda.

Quando não usar esse modelo

  • Servidor MCP local em stdio, um usuário só. A spec diz pra pegar credencial do ambiente e não seguir o fluxo OAuth. Não monte OAuth ali.
  • Agente que roda sozinho, sem ninguém pra aprovar no navegador. Não force o fluxo de app com usuário. O modelo certo é o do Backend Services: client credentials e escopo system/, até o MCP fechar identidade de agente.
  • Ferramenta que não toca dado pessoal. Gramática SMART ali é cerimônia.

O que eu faria hoje num servidor MCP clínico

  1. Um escopo por ferramenta, escrito na gramática SMART v2 quando a ferramenta toca recurso FHIR.
  2. 403 com scope= no header, não em texto. Cliente faz step-up lendo esse parâmetro.
  3. Paciente vindo do token ou da sessão, nunca só do argumento que o modelo escreveu.
  4. Servidor MCP com token próprio pro FHIR, nunca o do usuário repassado.

Fonte e como reproduzir

git clone https://github.com/rogeriorrodrigues/nursia-research-lab
cd nursia-research-lab/demos/05-mcp-scopes-smart-on-fhir
pip install -r requirements.txt
python3 -m pytest tests -q -s
python3 demo.py

In English

MCP authorization (spec 2026-07-28) already mirrors the OAuth half of SMART App Launch 2.2.0: resource server role, protected resource metadata, audience binding and 403 insufficient_scope with step-up. What it lacks is the clinical half: a shared scope grammar, launch context that pins the patient, and a standard path for agents running without a user. A small demo maps SMART v2 scopes to MCP tools; the gaps are listed above.

Última revisão em 07/10/2026.

Gostou? O próximo teste sai primeiro no LinkedIn e no Instagram.

Se você põe dado pessoal brasileiro em IA no trabalho e tem uma dúvida, me escreve. O que der pra responder em público vira artigo aqui.