Expondo Dados Legíveis como Recursos MCP
Um recurso é como um servidor MCP entrega dados para um cliente ler, endereçado por um URI em vez de invocado como uma função.
Arquivos, linhas de banco de dados, valores de configuração e respostas de API são todos adequados para recursos, uma vez que você os pensa como coisas que um cliente pode buscar, não ações que um modelo executa.
Resumo
Recursos no SDK Python do MCP são definidos com o decorador @mcp.resource(), recebendo um URI ou um template de URI como argumento.
Um recurso estático tem um URI fixo, como config://settings, enquanto um recurso com template usa placeholders entre chaves, como notes://{name}, que se tornam parâmetros de função.
Como um recurso representa uma leitura, ele nunca deve mutar estado ou disparar um efeito colateral; essa distinção é o que separa um recurso de uma ferramenta.
Retornar um tipo MIME sensato junto com o conteúdo ajuda o cliente a decidir como renderizar ou analisar o que recebe de volta, seja texto puro, JSON ou uma imagem.
Recursos combinam naturalmente com ferramentas: uma ferramenta pode criar ou atualizar algo, e um recurso pode expor o resultado para leitura posterior.
Receita
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("resource-recipes")
@mcp.resource("config://settings")
def get_settings() -> str:
"""Retorna a configuração atual do servidor como JSON."""
return '{"theme": "dark", "max_results": 20}'
@mcp.resource("notes://{name}")
def get_note(name: str) -> str:
"""Retorna o conteúdo de uma nota nomeada."""
return f"Contents of note '{name}'"Quando usar isso:
- Os dados destinam-se a ser lidos, não a serem agidos, como um arquivo, um valor de configuração ou uma linha de banco de dados.
- Você quer que o cliente possa listar e buscar os dados sem que o modelo precise construir uma chamada de ferramenta.
- O mesmo dado pode ser anexado como contexto diretamente pela aplicação, não apenas buscado a pedido do modelo.
- Você quer um identificador estável e endereçável (um URI) para um pedaço específico de dados que possa ser referenciado consistentemente.
Exemplo de Trabalho
# server.py
import json
import sqlite3
from pathlib import Path
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("docs-resources")
DB_PATH = Path(__file__).parent / "articles.db"
DOCS_DIR = Path(__file__).parent / "docs"
def _get_db() -> sqlite3.Connection:
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
@mcp.resource("articles://{article_id}")
def get_article(article_id: str) -> str:
"""Retorna uma única linha de artigo como JSON, endereçada por seu id."""
conn = _get_db()
try:
row = conn.execute(
"SELECT id, title, body FROM articles WHERE id = ?", (article_id,)
).fetchone()
if row is None:
raise ValueError(f"No article found with id {article_id}")
return json.dumps(dict(row))
finally:
conn.close()
@mcp.resource("docs://{filename}")
def get_doc_file(filename: str) -> str:
"""Retorna o texto bruto de um arquivo markdown do diretório docs."""
path = (DOCS_DIR / filename).resolve()
if DOCS_DIR.resolve() not in path.parents:
raise ValueError("filename must stay within the docs directory")
if not path.exists():
raise FileNotFoundError(f"No such file: {filename}")
return path.read_text(encoding="utf-8")
@mcp.resource("stats://article-count")
def get_article_count() -> str:
"""Retorna o número total de artigos como um recurso de resumo estático."""
conn = _get_db()
try:
count = conn.execute("SELECT COUNT(*) FROM articles").fetchone()[0]
return json.dumps({"article_count": count})
finally:
conn.close()
if __name__ == "__main__":
mcp.run(transport="stdio")O que isso demonstra:
- Um recurso com template (
articles://{article_id}) lendo uma única linha de uma conexão de banco de dados real. - Um recurso com template (
docs://{filename}) servindo arquivos do disco, com uma proteção contra travessia de caminho antes de tocar no sistema de arquivos. - Um recurso estático (
stats://article-count) sem parâmetros, útil para resumos ou dados agregados. - Uso consistente de
json.dumpspara que dados estruturados retornem em um formato previsível e analisável. - Lançamento de exceções para uma linha ausente ou arquivo ausente, exatamente como uma ferramenta faria, já que recursos ainda podem falhar.
Mergulho Profundo
Como Funciona
@mcp.resource("scheme://path/{param}")registra um template de URI; o SDK extrai segmentos{param}e os passa como argumentos de função quando um URI correspondente é lido.- Um cliente descobre recursos com
list_resources()(para URIs concretos) oulist_resource_templates()(para os com template), então lê um URI específico comread_resource(uri). - Diferente de uma chamada de ferramenta, uma leitura de recurso não tem um objeto "argumentos" separado; cada parâmetro vem do próprio URI, o que mantém os recursos endereçáveis e cacheados.
- A parte do esquema do URI (
config://,notes://,docs://) é arbitrária e escolhida por você; não é um protocolo de rede real, apenas um namespace. - O SDK infere um tipo MIME do valor de retorno quando pode, mas você pode ser explícito para qualquer coisa que não seja texto puro.
Escolhendo um Esquema de URI
| Estilo de Esquema | Exemplo | Melhor Para |
|---|---|---|
| Namespace customizado | config://settings | Conceitos específicos do servidor sem equivalente em sistema de arquivos ou rede |
Estilo file:// | file:///reports/q1.csv | Dados que genuinamente mapeiam para um caminho de arquivo real |
| Estilo de Domínio | articles://{id}, users://{id} | Linhas ou registros de um banco de dados, um esquema por tipo de entidade |
| Agregado/Estático | stats://summary | Resumos ou contagens fixas, sem parâmetros |
Notas de Python
# Protege contra travessia de caminho sempre que um recurso lê do disco usando um
# segmento de caminho fornecido pelo usuário.
path = (DOCS_DIR / filename).resolve()
if DOCS_DIR.resolve() not in path.parents:
raise ValueError("filename must stay within the docs directory")Parâmetros e Valores de Retorno
| Elemento | Tipo | Descrição |
|---|---|---|
| Template de URI | str | Passado para @mcp.resource(); segmentos {param} se tornam parâmetros de função |
| Parâmetros de função | str (tipicamente) | Extraídos dos segmentos de URI correspondentes no momento da leitura |
| Valor de retorno | str (ou bytes para conteúdo binário) | O conteúdo do recurso, retornado ao cliente em read_resource |
Armadilhas
- Tratar um recurso como uma ação. Um recurso chamado
send_email://{to}implica um efeito colateral e engana clientes que assumem que recursos são seguros para ler repetidamente. Correção: mova qualquer coisa com efeito colateral para uma ferramenta. - Sem proteção contra travessia de caminho em recursos baseados em sistema de arquivos. Um URI como
docs://../../etc/passwdpode escapar do diretório pretendido se você construir um caminho ingenuamente. Correção: resolva o caminho e confirme que ele permanece dentro da raiz permitida antes de ler. - Retornar texto não estruturado para o que é realmente dado estruturado. Os chamadores então têm que analisar formatos ad hoc em vez de JSON. Correção: retorne
json.dumps(...)para qualquer coisa com mais de um campo. - Abrir uma conexão de banco de dados por recurso sem fechá-la. Sob carga isso vaza conexões e eventualmente esgota o pool. Correção: use um
try/finally(como mostrado acima) ou um gerenciador de contexto de conexão. - Sobrecarga de um template de recurso para também aceitar parâmetros opcionais estilo query. Templates de URI no MCP são baseados em caminho, não em string de query, então isso se torna estranho e inconsistente. Correção: use recursos separados ou uma ferramenta se a lógica de seleção de dados ficar complexa o suficiente para precisar de filtros opcionais.
- Retornar silenciosamente uma string vazia quando uma busca falha. Um chamador não consegue distinguir "sem dados" de "um erro ocorreu". Correção: levante uma exceção com uma mensagem clara quando o recurso solicitado genuinamente não existir.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Ferramenta MCP | A operação executa uma ação ou computação com um resultado | Os dados são uma leitura simples sem efeitos colaterais |
| Prompt MCP | Você está empacotando instruções, não dados | O cliente precisa buscar ou referenciar conteúdo real |
| Uma ferramenta que retorna os mesmos dados | Você quer que o modelo solicite explicitamente os dados como parte de uma etapa de raciocínio | Você quer que o cliente anexe ou cache os dados como contexto ambiente |
| Acesso direto ao banco de dados/API fora do MCP | Apenas uma aplicação interna precisará desses dados | Você quer os mesmos dados endereçáveis do Claude Code, Claude Desktop e outros clientes |
FAQs
Qual é a diferença entre um recurso e uma ferramenta que apenas retorna dados?
- Um recurso é endereçado por um URI estável e destina-se a ser lido, listado e potencialmente cacheado.
- Uma chamada de ferramenta é iniciada pelo modelo como parte do raciocínio sobre uma tarefa e não carrega a mesma garantia de endereçabilidade.
Um URI de recurso pode ter mais de um parâmetro?
Sim, um template como orgs://{org_id}/users/{user_id} extrai ambos os segmentos como parâmetros de função separados, desde que cada um seja delimitado por seu próprio segmento de caminho.
Recursos devem ter efeitos colaterais?
Não. Recursos destinam-se a ser lidos com segurança, potencialmente mais de uma vez, sem alterar nada. Qualquer coisa que crie, atualize ou exclua dados pertence a uma ferramenta.
Como retorno conteúdo binário, como uma imagem, de um recurso?
Retorne bytes do manipulador em vez de str, e o SDK o codificará apropriadamente para o cliente, tipicamente junto com um tipo MIME explícito descrevendo o formato binário.
O que acontece se um cliente solicitar um URI de recurso sem um manipulador correspondente?
O servidor retorna um erro indicando que o recurso não existe. É por isso que o esquema de URI exato e o design do template importam, já que um erro de digitação no esquema significa que nenhum manipulador corresponde.
Posso listar todos os recursos disponíveis sem saber seus URIs exatos?
Sim. list_resources() e list_resource_templates() permitem que um cliente descubra o que um servidor oferece, incluindo recursos com template e seus placeholders {param} mostrados, antes de ler qualquer URI específico.
O esquema de URI (como config:// ou articles://) é um protocolo real?
Não, é apenas um namespace que você escolhe. MCP não exige que estes sejam URIs de rede resolvíveis; eles só precisam ser únicos e significativos dentro do seu servidor.
Como devo lidar com um recurso que falha ao encontrar seus dados?
Levante uma exceção com uma mensagem específica, exatamente como faria em um manipulador de ferramenta, em vez de retornar uma string vazia ou None que esconde a falha.
Um recurso pode depender de estado de banco de dados que muda frequentemente?
Sim, recursos podem ser tão dinâmicos quanto qualquer função manipuladora; cada leitura é executada de forma fresca, então um recurso baseado em banco de dados sempre reflete os dados atuais, a menos que você adicione sua própria camada de cache.
Recursos baseados em arquivo devem confiar diretamente no parâmetro de nome de arquivo?
Não. Sempre resolva o caminho resultante e verifique se ele permanece dentro do diretório raiz pretendido antes de ler, para evitar travessia de caminho para arquivos fora do diretório servido.
Qual tipo MIME devo usar para um recurso JSON?
application/json é a escolha convencional, e retornar conteúdo via json.dumps(...) junto com esse tipo MIME dá aos clientes uma carga útil previsível e analisável.
Um servidor pode misturar recursos estáticos e com template?
Sim, um único servidor FastMCP pode registrar qualquer número de recursos estáticos (config://settings) e com template (articles://{id}) lado a lado.
Relacionado
- Entendendo MCP: Ferramentas, Recursos e Prompts - a distinção conceitual entre recursos, ferramentas e prompts.
- Noções Básicas de Conceitos Principais do MCP - um servidor e cliente stdio mínimo para construir recursos em cima.
- Definindo Ferramentas Chamáveis em um Servidor MCP - o primitivo irmão para ações e computações.
- Comparação de Ferramentas vs. Recursos vs. Prompts MCP - decidindo qual primitivo se encaixa em uma determinada capacidade.
- Melhores Práticas de Conceitos Principais do MCP - uma lista de verificação para design de URI de recurso.
Versões da Stack: Escrito contra a linha de modelos Claude atual em ~junho de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (o padrão), e Claude Haiku 4.5 - e a especificação atual do Model Context Protocol. Nomes de modelos, versões de SDK e a especificação MCP mudam rapidamente - verifique os detalhes atuais em platform.claude.com/docs e modelcontextprotocol.io antes de confiar neles.