Criando seu primeiro servidor MCP
Use o SDK MCP para Python para criar um servidor que disponibilize recursos, ferramentas e instruções; depois, conecte-o ao Claude Desktop para vê-lo funcionando de ponta a ponta.
Criando seu primeiro servidor MCP é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Engineering Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Engineering Academy inclui 4 aulas no total.
Configuração do projeto para um servidor MCP
Criar um servidor MCP em Python requer o SDK mcp e um ambiente Python. Você criará um servidor ao qual o Claude Desktop ou qualquer cliente MCP poderá se conectar por meio de stdio. Comece instalando o pacote e criando o arquivo do servidor.
# Create a project directory
# mkdir my_mcp_server && cd my_mcp_server
# Create a virtual environment
# python -m venv venv && source venv/bin/activate
# Install the MCP SDK
# pip install mcp httpx
# Project structure:
# my_mcp_server/
# server.py <- Your MCP server
# requirements.txt
# README.mdCriando o objeto do servidor
Importe do pacote mcp e crie uma instância de Server com o nome do seu servidor. O nome é mostrado aos clientes na lista de servidores MCP; escolha algo descritivo. O objeto do servidor é o ponto de entrada para registrar todas as suas ferramentas, recursos e prompts.
# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import asyncio
import httpx
# Create the server — name is shown in Claude Desktop
app = Server('weather-server')
# --- Tool registrations go here ---
# Entry point
if __name__ == '__main__':
asyncio.run(stdio_server(app))Registrando ferramentas com @app.list_tools()
O decorador @app.list_tools() registra um manipulador que retorna a lista de ferramentas disponíveis quando o cliente a solicita. Cada ferramenta é definida com um nome, uma descrição e um inputSchema — o esquema JSON que descreve seus parâmetros. O cliente envia essa lista ao LLM para que ele saiba quais ferramentas pode chamar.
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name='get_weather',
description='Get current weather for a city. Use when the user asks about weather in a specific location.',
inputSchema={
'type': 'object',
'properties': {
'city': {
'type': 'string',
'description': 'City name, e.g. London or New York'
},
'units': {
'type': 'string',
'enum': ['metric', 'imperial'],
'description': 'Temperature units. Default is metric.'
}
},
'required': ['city']
}
),
types.Tool(
name='list_cities',
description='Return a list of major cities the user can query weather for.',
inputSchema={'type': 'object', 'properties': {}, 'required': []}
)
]Implementando a execução de ferramentas
O decorador @app.call_tool() gerencia a execução de ferramentas. Quando o cliente chama uma ferramenta, este manipulador recebe o nome e os argumentos da ferramenta. Execute a lógica apropriada e retorne uma lista de objetos TextContent contendo a string do resultado.
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
if name == 'list_cities':
cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney']
return [types.TextContent(type='text', text=', '.join(cities))]
if name == 'get_weather':
city = arguments['city']
units = arguments.get('units', 'metric')
unit_symbol = 'C' if units == 'metric' else 'F'
# Call real weather API (stub here)
async with httpx.AsyncClient() as client:
# Replace with actual API call
result = f'{city}: 18{chr(176)}{unit_symbol}, partly cloudy, humidity 65%'
return [types.TextContent(type='text', text=result)]
raise ValueError(f'Unknown tool: {name}')Expondo recursos
Recursos são dados estáticos ou dinâmicos que o modelo pode ler — pense neles como arquivos. Defina URIs de recursos e implemente um leitor de recursos. Os recursos aparecem no cliente como itens aos quais a IA pode fazer referência, sendo úteis para configurações, documentação ou instantâneos de dados acessados com frequência.
@app.list_resources()
async def list_resources() -> list[types.Resource]:
return [
types.Resource(
uri='weather://supported-cities',
name='Supported Cities List',
description='Complete list of cities available in this weather server.',
mimeType='text/plain'
)
]
@app.read_resource()
async def read_resource(uri: str) -> str:
if uri == 'weather://supported-cities':
cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney', 'Dubai', 'Singapore']
return '\n'.join(cities)
raise ValueError(f'Unknown resource URI: {uri}')Registrando modelos de prompts
Prompts são modelos de mensagens reutilizáveis que os clientes apresentam aos usuários como comandos de barra ou ações rápidas. Eles aceitam parâmetros e retornam uma lista de mensagens que se tornam o contexto inicial de uma conversa. Os prompts são excelentes para codificar instruções complexas que os usuários acionam com um único comando.
@app.list_prompts()
async def list_prompts() -> list[types.Prompt]:
return [
types.Prompt(
name='weather-report',
description='Generate a formatted weather report for a city.',
arguments=[
types.PromptArgument(name='city', description='City name', required=True)
]
)
]
@app.get_prompt()
async def get_prompt(name: str, arguments: dict) -> types.GetPromptResult:
if name == 'weather-report':
city = arguments.get('city', 'London')
return types.GetPromptResult(
description=f'Weather report for {city}',
messages=[
types.PromptMessage(
role='user',
content=types.TextContent(
type='text',
text=f'Use the get_weather tool to look up {city} and give me a detailed weather report including what clothing I should wear.'
)
)
]
)
raise ValueError(f'Unknown prompt: {name}')Conectando ao Claude Desktop
Para usar seu servidor MCP com o Claude Desktop, adicione-o ao arquivo de configuração do Claude Desktop. No macOS, ele fica em ~/Library/Application Support/Claude/claude_desktop_config.json. Especifique o comando para iniciar seu servidor e todas as variáveis de ambiente necessárias.
# ~/Library/Application Support/Claude/claude_desktop_config.json
# Add this JSON configuration:
# {
# "mcpServers": {
# "weather-server": {
# "command": "/path/to/venv/bin/python",
# "args": ["/path/to/my_mcp_server/server.py"],
# "env": {
# "WEATHER_API_KEY": "your_api_key_here"
# }
# }
# }
# }
# After saving, restart Claude Desktop.
# Your server's tools will appear in Claude's tool list.Testando seu servidor com o MCP CLI
Antes de conectar ao Claude Desktop, teste seu servidor usando o inspetor do MCP ou as ferramentas de CLI. O comando mcp dev inicia seu servidor e abre um inspetor no navegador, no qual você pode chamar ferramentas manualmente e ver as mensagens brutas do protocolo, facilitando a depuração de problemas.
# Install the MCP development tools
# pip install 'mcp[cli]'
# Start the inspector with your server
# mcp dev server.py
# The inspector opens at http://localhost:5173
# You can:
# - See all registered tools and their schemas
# - Call tools with custom arguments
# - Browse available resources
# - Test prompt templates
# - View the full JSON-RPC message exchangeTratamento de erros em servidores MCP
Os servidores MCP nunca devem travar ao receber entradas inválidas. Envolva toda a execução de ferramentas em try/except e retorne mensagens de erro como TextContent, em vez de lançar exceções. Para erros fatais, como problemas de configuração ou chaves de API ausentes, registre-os na inicialização e lance uma exceção antes de o servidor entrar no loop principal.
@app.call_tool()
async def call_tool_safe(name: str, arguments: dict) -> list[types.TextContent]:
try:
if name == 'get_weather':
city = arguments.get('city')
if not city:
return [types.TextContent(type='text', text='Error: city argument is required.')]
result = await fetch_weather(city, arguments.get('units', 'metric'))
return [types.TextContent(type='text', text=result)]
raise ValueError(f'Unknown tool: {name}')
except httpx.TimeoutException:
return [types.TextContent(type='text', text='Error: Weather API timed out. Try again.')]
except Exception as e:
return [types.TextContent(type='text', text=f'Error: {str(e)}')]Registro de eventos em servidores MCP
Como os servidores MCP se comunicam com o cliente por meio de stdio, instruções de impressão interromperão o protocolo. Use sempre stderr para fazer o registro — ele vai para um fluxo separado que não interfere na troca de mensagens do MCP. Configure o módulo de registro do Python para escrever em stderr.
import logging
import sys
# Configure logging to stderr (NOT stdout — that's the MCP channel)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
stream=sys.stderr
)
logger = logging.getLogger('weather-server')
# In your tool handler:
# logger.info(f'Getting weather for {city}')
# logger.error(f'API call failed: {e}')
# Claude Desktop captures stderr to a log file:
# ~/Library/Logs/Claude/mcp-server-weather-server.logEmpacotando seu servidor MCP
Compartilhe seu servidor MCP empacotando-o com um pyproject.toml e publicando-o no PyPI, ou distribua-o como um contêiner Docker para equipes. Use variáveis de ambiente para todos os segredos — chaves de API e URLs de bancos de dados — para manter a configuração do servidor separada do código. Documente as variáveis de ambiente necessárias e um exemplo de configuração em um README claro.
Verificação rápida
Teste sua compreensão sobre a criação de um servidor MCP em Python.
Recapitulação da lição
Nesta lição, você aprendeu que: os servidores MCP expõem ferramentas por meio dos decoradores @app.list_tools() e @app.call_tool(); os recursos e prompts ampliam o servidor com dados legíveis e modelos reutilizáveis; e o registro deve usar stderr para evitar corromper o canal do protocolo MCP via stdio. A seguir, conectaremos um servidor MCP a um banco de dados e exporemos recursos dinâmicos com paginação.
Aprenda Python com um tutor de IA — grátis
Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.
- Cursos
- 30
- Aulas
- 120
Perguntas Frequentes
A aula “Criando seu primeiro servidor MCP” é grátis?
Sim — o texto completo de “Criando seu primeiro servidor MCP” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Engineering Academy, atualize para CoddyKit PRO. O curso de AI Engineering Academy inclui 4 aulas no total.
O que vou aprender em “Criando seu primeiro servidor MCP”?
Use o SDK MCP para Python para criar um servidor que disponibilize recursos, ferramentas e instruções; depois, conecte-o ao Claude Desktop para vê-lo funcionando de ponta a ponta. Você pratica AI Engineering Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Engineering Academy?
Nenhuma experiência prévia é necessária. AI Engineering Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Criando seu primeiro servidor MCP”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Engineering Academy?
Sim. Cada aula de AI Engineering Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- O que é MCP e por que ele importa
- Criando seu primeiro servidor MCP
- Disponibilizando recursos de banco de dados via MCP
- Segurança e autenticação no MCP