Pular para conteúdo

O Context

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.

Os argumentos de uma ferramenta (tool) vêm do modelo. Todo o resto (a requisição que você está atendendo, o servidor em que você vive, um jeito de falar de volta com o cliente) vem de um único objeto: o Context.

Você não o constrói nem o configura. Você pede por ele.

Peça por ele

Adicione a qualquer ferramenta um parâmetro anotado com Context:

server.py
from mcp.server import MCPServer
from mcp.server.mcpserver import Context

mcp = MCPServer("Bookshop")


@mcp.tool()
def search_books(query: str, ctx: Context) -> str:
    """Search the catalog by title or author."""
    return f"[request {ctx.request_id}] Found 3 books matching {query!r}."
  • O SDK constrói um Context novo para cada requisição e o passa para a função.
  • O nome do parâmetro não importa. ctx, context, c: o SDK o encontra pela anotação.
  • Recursos e prompts também podem declarar um, do mesmo jeito.
  • ctx.request_id é o id da requisição que sua função está atendendo neste momento.

Info

Se você já usou FastAPI, já conhece essa jogada: declare um parâmetro com o tipo do próprio framework (Request lá, Context aqui) e o framework o fornece. Nada para registrar, nada para configurar: a anotação de tipo é o mecanismo inteiro.

Invisível para o modelo

Esta é a parte para internalizar. Aqui está o schema de entrada que tools/list informa para search_books:

{
  "type": "object",
  "properties": {
    "query": {"title": "Query", "type": "string"}
  },
  "required": ["query"],
  "title": "search_booksArguments"
}

Uma única propriedade. ctx não é um argumento: ele nunca aparece no schema, o modelo nunca fica sabendo dele e nenhum cliente consegue preenchê-lo. É um contrato entre você e o SDK, invisível no protocolo.

Experimente

Execute o servidor com o MCP Inspector:

uv run mcp dev server.py

O formulário de search_books tem um único campo, query. Chame-a com dune:

[request 3] Found 3 books matching 'dune'.

O número é o da requisição da vez. Chame a ferramenta de novo e ele muda: cada requisição recebe seu próprio Context.

O que ele oferece

O objeto injetado é pequeno. Além de request_id:

  • await ctx.read_resource(uri): lê um dos recursos do próprio servidor, de dentro de uma ferramenta. É a próxima seção.
  • await ctx.report_progress(progress, total, message): envia o progresso de volta a quem chamou, durante uma chamada demorada. Progresso tem a história completa.
  • await ctx.elicit(message, schema) e await ctx.elicit_url(...): pausam a ferramenta e fazem uma pergunta ao usuário. Isso é Elicitação (elicitation).
  • ctx.session: o lado do servidor na conversa com este cliente. As notificações que você envia ao cliente ficam aqui; a última seção usa isso.
  • ctx.headers: os cabeçalhos da requisição que o transporte carregou, ou None no stdio. Leia um cabeçalho customizado com (ctx.headers or {}).get("x-..."). Cabeçalhos são entrada fornecida pelo cliente - servem para um locale ou uma feature flag, nunca para uma identidade.
  • ctx.request_context: o registro bruto de cada requisição. O campo que você vai querer é lifespan_context, o objeto que seu código de inicialização entregou no yield (veja Lifespan).

Logging está fora dessa lista de propósito. Um servidor registra logs com o módulo logging do Python, como qualquer outro programa Python. Logging é a página curta que explica o porquê.

Tip

A injeção só acontece na função que você registrou. Uma função auxiliar que sua ferramenta chama não recebe um Context próprio; passe ctx adiante como um argumento comum. Não existe um "contexto atual" implícito para buscar de algum outro lugar.

Leia seus próprios recursos

Os recursos de um servidor não são só para os clientes. Uma ferramenta também pode lê-los:

server.py
from mcp.server import MCPServer
from mcp.server.mcpserver import Context

mcp = MCPServer("Bookshop")


@mcp.resource("catalog://genres")
def genres() -> str:
    """The genres the catalog is organised into."""
    return "fiction, non-fiction, poetry"


@mcp.tool()
async def describe_catalog(ctx: Context) -> str:
    """Describe how the catalog is organised."""
    [contents] = await ctx.read_resource("catalog://genres")
    return f"The catalog is organised into: {contents.content}"

ctx.read_resource resolve a URI pelo mesmo registro que atende resources/read, então uma ferramenta recebe o que um cliente receberia: um iterável de ReadResourceContents, um por bloco de conteúdo. Para esta URI existe um só:

contents.content    # 'fiction, non-fiction, poetry'
contents.mime_type  # 'text/plain'
  • content é exatamente o que genres() retornou. Uma única fonte da verdade: o cliente navega pelo recurso, suas ferramentas o consomem, ninguém copia a string.
  • O único parâmetro de describe_catalog é o Context, então seu schema de entrada não tem nenhuma propriedade. O modelo a chama com {}.

Avise o cliente de que a lista mudou

O que um servidor oferece não é fixo no momento do import. Registre uma ferramenta em tempo de execução e depois avise o cliente:

server.py
from mcp.server import MCPServer
from mcp.server.mcpserver import Context

mcp = MCPServer("Bookshop")


def recommend_book(genre: str) -> str:
    """Recommend a book in the given genre."""
    return f"In {genre}, try 'Dune'."


@mcp.tool()
async def enable_recommendations(ctx: Context) -> str:
    """Switch on the recommendation tool."""
    mcp.add_tool(recommend_book)
    await ctx.session.send_tool_list_changed()
    return "Recommendations are now available."
  • mcp.add_tool(recommend_book) registra uma função comum como ferramenta: nome, descrição e schema derivados exatamente como @mcp.tool() faria.
  • await ctx.session.send_tool_list_changed() envia notifications/tools/list_changed. Um cliente que a recebe chama tools/list de novo e vê recommend_book.

Os irmãos são send_resource_list_changed(), send_prompt_list_changed() e send_resource_updated(uri), este último para uma mudança em um recurso específico.

Em uma conexão 2026-07-28, os clientes só recebem notificações de mudança em um stream subscriptions/listen que eles mesmos abriram, então os métodos send_* acima não alcançam esses streams. Os métodos de publicação do Context entregam a todos os streams assinantes de uma vez só: await ctx.notify_tools_changed(), await ctx.notify_prompts_changed(), await ctx.notify_resources_changed() e await ctx.notify_resource_updated(uri). Assinaturas tem a história completa, incluindo como escalar horizontalmente entre réplicas.

Check

Antes de alguém executar enable_recommendations, a ferramenta que você está prometendo não existe. Chame-a mesmo assim e o resultado é um erro que o modelo consegue ler:

Unknown tool: recommend_book

Execute enable_recommendations e a mesmíssima chamada dá certo. A lista de ferramentas é dinâmica de verdade: tools/list reflete o que quer que esteja registrado neste exato momento.

Recapitulando

  • Anote um parâmetro com Context (em uma ferramenta, um recurso ou um prompt) e o SDK o injeta. O nome fica por sua conta.
  • Ele é invisível para o modelo: o schema de entrada sempre contém apenas seus argumentos de verdade.
  • ctx.request_id identifica a requisição; ctx.request_context.lifespan_context é o que sua inicialização entregou no yield.
  • await ctx.read_resource(uri) permite que uma ferramenta leia os recursos do próprio servidor.
  • ctx.session é o canal de volta para o cliente: send_tool_list_changed() e seus irmãos dizem a ele para buscar de novo uma lista que você mudou.
  • Relatar progresso e a elicitação também começam no Context; cada um tem sua própria página.

Parâmetros que o modelo nunca vê, preenchidos pelas suas próprias funções, são as Dependências.