Recursos
Tradução automática
Esta página foi traduzida automaticamente a partir da documentação em inglês, e a página em inglês é a versão de referência. Se algo parecer errado, Traduções explica como avisar.
Um recurso (resource) é um dado que você expõe para a aplicação ler.
A divisão é essa. Uma ferramenta é algo que o modelo decide chamar. Um recurso é algo que a aplicação decide carregar (um arquivo de configuração, um registro, um documento) e colocar diante do modelo como contexto.
Você declara um colocando @mcp.resource(uri) em uma função Python comum.
Seu primeiro recurso
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.resource("config://app")
def get_config() -> str:
"""The active shop configuration."""
return "theme=dark\nlanguage=en"
Tem o mesmo formato de uma ferramenta, com uma coisa a mais: a URI. Recursos têm endereço, não nome. Um cliente pede config://app, nunca get_config.
O SDK ainda lê o restante a partir da função:
- O nome é o nome da função:
get_config. - A descrição que o cliente vê é a docstring.
- O conteúdo é o que você retornar.
Durante resources/list, o cliente recebe isto:
{
"name": "get_config",
"uri": "config://app",
"description": "The active shop configuration.",
"mimeType": "text/plain"
}
E quando ele lê config://app, sua função roda e o valor de retorno volta como texto:
result.contents # [TextResourceContents(uri="config://app", mime_type="text/plain", text="theme=dark\nlanguage=en")]
Tip
Listar é barato. Sua função não é chamada durante resources/list, só durante
resources/read, e apenas para a URI que foi pedida. Exponha mil recursos
e você só paga pelos que alguém abrir.
Experimente
Execute o servidor com o MCP Inspector:
uv run mcp dev server.py
Abra a URL que ele imprime e vá até a aba Resources. config://app está na lista, com sua descrição. Clique nele e o Inspector o lê: ali estão suas duas linhas de configuração.
Templates de recurso
Uma URI por registro não escala. Coloque um placeholder na URI e um parâmetro correspondente na função:
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.resource("config://app")
def get_config() -> str:
"""The active shop configuration."""
return "theme=dark\nlanguage=en"
@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
"""A customer's profile."""
return f"User {user_id}: 12 orders since 2021."
{user_id} na URI, user_id: str na função. O contrato inteiro é esse.
Agora isso é um template de recurso (resource template), e ele se muda: sai de resources/list e passa a aparecer em resources/templates/list, como um padrão em vez de um endereço:
{
"name": "get_user_profile",
"uriTemplate": "users://{user_id}/profile",
"description": "A customer's profile.",
"mimeType": "text/plain"
}
O cliente preenche o placeholder e lê uma URI concreta: users://42/profile, users://ada/profile. Uma única função responde a todas elas, com o valor capturado passado como user_id:
result.contents # [TextResourceContents(uri="users://42/profile", text="User 42: 12 orders since 2021.")]
Repare na uri do resultado. É a URI concreta que o cliente pediu, não o template.
Check
Os placeholders e os parâmetros precisam bater. Renomeie o parâmetro da função para
user enquanto a URI ainda diz {user_id} e o decorador se recusa em tempo de importação,
antes que qualquer cliente chegue perto:
ValueError: Mismatch between URI parameters {'user_id'} and function parameters {'user'}
Uma divergência dessas só pode ser bug, então o SDK torna impossível iniciar o servidor com uma.
A sintaxe dos placeholders é a da RFC 6570: {+path} para valores com vários segmentos, {?q,lang} para parâmetros de query opcionais, e mais. O SDK também aplica, por padrão, verificações de segurança de caminho aos valores extraídos. Veja Templates de URI e segurança de caminhos para a referência completa.
get_user_profile também pode receber um parâmetro anotado com Context. O SDK o injeta sem nunca tratá-lo como parâmetro da URI, e a página O Context cobre o que ele oferece a você.
O que você retorna
Você não está limitado a str. Dê a cada recurso um mime_type e retorne o que fizer sentido:
import base64
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.resource("docs://readme", mime_type="text/markdown")
def readme() -> str:
"""How to use this server."""
return "# Bookshop\n\nSearch the catalog with the `search_books` tool."
@mcp.resource("stats://catalog", mime_type="application/json")
def catalog_stats() -> dict[str, int]:
"""Live counts for the catalog."""
return {"books": 1204, "authors": 391}
@mcp.resource("covers://placeholder", mime_type="image/gif")
def placeholder_cover() -> bytes:
"""A 1x1 transparent GIF, shown when a book has no cover."""
return base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
readmeretorna umastr, então ela é enviada como está. Esse é o caso comum.-
catalog_statsretorna umdict, então o SDK o serializa em texto JSON para você:{ "books": 1204, "authors": 391 } -
placeholder_coverretornabytes, então o cliente recebe umBlobResourceContentsem vez de umTextResourceContents, com seus bytes codificados em base64 no campoblob.
A mesma regra vale para qualquer outra coisa serializável em JSON: uma lista, um modelo Pydantic, uma dataclass. Se não é str nem bytes, vira JSON.
O mime_type é você quem declara, e o padrão é text/plain. O SDK nunca inspeciona o que você retorna para adivinhá-lo, então um recurso dict que você não rotula continua sendo anunciado como texto puro.
Tip
@mcp.resource() também aceita name=, title= e description= quando você não
quer derivá-los da função. E quando não há função nenhuma a escrever,
mcp.server.mcpserver.resources tem classes Resource prontas (TextResource,
BinaryResource, FileResource, HttpResource, DirectoryResource) que você registra
com mcp.add_resource(...).
Um cliente também pode assinar um recurso e ser notificado quando ele muda; essa metade da história é do cliente e está em O cliente.
Recapitulando
@mcp.resource(uri)em uma função a transforma em um recurso. A URI é o endereço, o valor de retorno é o conteúdo, a docstring é a descrição.- Um
{placeholder}na URI a transforma em um template: ele é listado emresources/templates/liste uma única função atende a toda URI que corresponder. - Os nomes dos placeholders devem ser iguais aos nomes dos parâmetros da função. Erre isso e você descobre em tempo de importação, não em produção.
- Sua função roda quando o recurso é lido, não quando é listado.
strvira texto,bytesvira um blob em base64, qualquer outra coisa vira texto JSON.mime_type=é como você rotula isso.- Ferramentas são para o modelo agir. Recursos são para a aplicação ler.
A terceira primitiva, aquela que uma pessoa escolhe em um menu, são os Prompts.