Exemplos de servidor MCP em Python e TypeScript (código no GitHub) que funcionam com os SDKs atuais
Exemplos de servidor MCP em Python e TypeScript prontos para copiar, baseados nos repositórios oficiais dos SDKs no GitHub. Cada exemplo roda por stdio ou Streamable HTTP, e os dois últimos envolvem uma API de imagem e vídeo para que um assistente gere mídia a partir de uma janela de chat.
Um servidor MCP é um pequeno programa que entrega a um assistente de IA uma lista de ferramentas que ele pode chamar, e a forma mais rápida de entender como um deles funciona é ler alguns que já estão rodando. Este artigo reúne exemplos de servidor MCP em Python e TypeScript, escritos com base nos repositórios oficiais dos SDKs no GitHub, para que você possa colar um arquivo, iniciá-lo e ver um assistente usá-lo em poucos minutos.
Tudo a seguir segue a documentação dos SDKs de outubro de 2026, o que importa porque os dois SDKs chegaram recentemente à segunda versão principal. Em Python, FastMCP virou MCPServer. Em TypeScript, o código do servidor passou para seu próprio pacote @modelcontextprotocol/server. A primeira metade do artigo cria um servidor simples em cada linguagem. A segunda metade adiciona ferramentas que geram imagens e vídeos, que é onde o MCP deixa de ser demonstração e começa a economizar trabalho de verdade.
O que um servidor MCP faz
O Model Context Protocol (MCP) é um padrão aberto que permite a um cliente de IA, como um aplicativo de chat, uma IDE ou um agente, chamar código que você escreveu. O cliente abre uma conexão, pergunta ao seu servidor o que ele oferece e deixa o modelo decidir quando usar cada item. As mensagens trafegam como JSON-RPC, e seu servidor nunca chama o modelo por conta própria. Ele espera ser solicitado, executa a função e devolve um resultado.
Ferramentas, recursos e prompts
Todo servidor é construído a partir de três tipos de blocos:
Ferramentas são funções que o modelo pode chamar, como add ou generate_image. Elas podem ter efeitos colaterais, por isso são a parte que exige mais cuidado no projeto.
Recursos são dados somente leitura endereçados por uma URI, como greeting://alice ou um caminho de arquivo. O cliente os lê para dar contexto ao modelo.
Prompts são modelos de mensagem reutilizáveis que uma pessoa escolhe em um menu, por exemplo um pedido de revisão de código.
💡 Dica: Crie primeiro as ferramentas. A maioria dos servidores no GitHub expõe apenas ferramentas, e um modelo escolhe a ferramenta certa com mais confiabilidade em uma lista curta de ferramentas com nomes claros do que em uma lista longa de nomes vagos.
Qual linha do SDK instalar
Os dois SDKs oficiais agora têm uma linha atual e uma linha de manutenção. Escolha a certa antes de copiar o código, porque os imports são diferentes.
Python
TypeScript
Atual (v2)
pip install "mcp[cli]", classe MCPServer
npm install @modelcontextprotocol/server
Manutenção (v1.x)
pip install "mcp[cli]<2", classe FastMCP
npm install @modelcontextprotocol/sdk zod
Repositório no GitHub
modelcontextprotocol/python-sdk
modelcontextprotocol/typescript-sdk
A linha Python v1 agora recebe apenas correções de segurança, então novos projetos devem começar na v2, e os exemplos Python abaixo fazem isso. Se você mantém um servidor Python antigo, migrar significa trocar from mcp.server.fastmcp import FastMCP por from mcp.server.mcpserver import MCPServer e renomear a chamada do construtor. Os decoradores continuam os mesmos.
Para TypeScript, os exemplos completos usam o pacote v1.x que a maioria dos servidores existentes importa hoje. Em seguida vem uma versão curta do mesmo servidor na v2, para você ver exatamente o que muda.
Exemplo em Python com MCPServer
Python tem o caminho mais curto do zero até um servidor funcionando, porque as type hints e as docstrings viram o schema da ferramenta. Não há JSON Schema para escrever à mão.
O arquivo do servidor
Instale com uv add "mcp[cli]" (ou pip install "mcp[cli]") e salve isto como server.py:
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("Demo")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
@mcp.prompt()
def review_code(code: str) -> str:
"""Ask for a code review that lists bugs first."""
return f"Please review this code and list bugs first:\n\n{code}"
if __name__ == "__main__":
mcp.run(transport="stdio")
A função add vira uma ferramenta cujo schema de entrada é gerado a partir de a: int, b: int. A docstring se torna a descrição que o modelo lê ao decidir se deve chamá-la. greeting é um modelo de recurso: um cliente que lê greeting://Ada recebe de volta Hello, Ada!.
Execute e inspecione
uv run mcp dev server.py # opens the MCP Inspector in your browser
uv run mcp run server.py # plain stdio, for a client to launch
uv run mcp run server.py --transport streamable-http # HTTP instead
Use primeiro o Inspector. Ele lista todas as ferramentas, oferece um formulário para os argumentos e mostra o tráfego JSON-RPC bruto, que é a forma mais rápida de identificar um schema ruim. Quando funcionar, registre o servidor em um cliente. No Claude Code isso é uma linha:
claude mcp add demo -- uv run mcp run server.py
Exemplo em TypeScript com Zod
TypeScript exige um pouco mais de trabalho, porque você descreve as entradas com Zod em vez de type hints. Em troca, você ganha validação em tempo de execução e argumentos tipados no seu handler.
O arquivo do servidor na v1.x
Instale o pacote com npm install @modelcontextprotocol/sdk zod e defina "type": "module" em package.json. Escreva o servidor como uma função para que os dois transportes possam reutilizá-la. Salve isto como src/build-server.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export function buildServer(): McpServer {
const server = new McpServer({ name: "demo", version: "1.0.0" });
server.registerTool(
"add",
{
title: "Add numbers",
description: "Add two numbers",
inputSchema: { a: z.number(), b: z.number() },
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
})
);
return server;
}
Depois, um ponto de entrada de três linhas para stdio, em src/stdio.ts:
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { buildServer } from "./build-server.js";
await buildServer().connect(new StdioServerTransport());
Compile com tsc e depois verifique com npx @modelcontextprotocol/inspector node dist/stdio.js. Recursos e prompts seguem o mesmo formato por meio de registerResource e registerPrompt. O repositório v1.x também traz src/examples/server/simpleStreamableHttp.ts, um exemplo completo com ferramentas, recursos, prompts, logs e OAuth opcional, que vale a pena ler quando seu servidor deixar de ser um brinquedo.
O mesmo servidor na v2
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import * as z from "zod/v4";
const server = new McpServer({ name: "demo", version: "1.0.0" });
server.registerTool(
"add",
{
description: "Add two numbers",
inputSchema: z.object({ a: z.number(), b: z.number() }),
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
})
);
await server.connect(new StdioServerTransport());
Três coisas mudam: o nome do pacote, o import do subcaminho /stdio e o schema, que passa a ser um z.object(...) completo de zod/v4 em vez de um objeto simples com campos. O corpo do handler é idêntico. Servir por HTTP na v2 passa por pequenos pacotes adaptadores, como @modelcontextprotocol/express, então leia o README desse pacote antes de portar um servidor HTTP.
Stdio ou Streamable HTTP
O transporte é a única decisão que muda a forma como você faz o deploy. O código das ferramentas continua idêntico.
stdio
Streamable HTTP
Quem inicia o servidor
O cliente o executa como processo filho
Você o executa, e os clientes se conectam por URL
Ideal para
Ferramentas locais, IDEs, aplicativos de desktop
Servidores compartilhados ou remotos, equipes
Autenticação
Herda seu usuário e ambiente
Você a adiciona (tokens ou OAuth)
Logs
Somente stderr
A saída normal é aceitável
Escala
Um processo por cliente
Escala web convencional
Processo local por stdio
Um servidor stdio é o padrão certo para qualquer coisa que toque na sua própria máquina, como arquivos, um banco de dados local ou um script. O cliente o inicia, conversa pela sua entrada e saída padrão e o encerra quando a sessão termina. Não há porta para proteger.
Servidor remoto por HTTP
O Streamable HTTP permite que um único servidor em execução atenda muitos clientes. Esta versão stateless com Express cria um servidor novo para cada requisição, o que evita estado de sessão compartilhado. Salve-a como src/http.ts:
import express from "express";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { buildServer } from "./build-server.js";
const app = express();
app.use(express.json());
app.post("/mcp", async (req, res) => {
const server = buildServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
res.on("close", () => {
transport.close();
server.close();
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(3000, "127.0.0.1");
Registre-o com claude mcp add --transport http demo http://127.0.0.1:3000/mcp. Em Python, o equivalente é uma linha: mcp.run(transport="streamable-http", host="127.0.0.1", port=9000). Vincule ao localhost, a menos que algo na frente do servidor cuide da autenticação, porque uma porta MCP aberta é uma porta aberta para todas as ferramentas que você registrou.
Exemplo: ferramentas que geram imagens
As ferramentas ficam mais interessantes quando o resultado não é um número. Os casos de geração de imagens e vídeo são bons exemplos didáticos porque são lentos, assíncronos e devolvem uma URL em vez de texto. Os exemplos abaixo chamam a API da PicassoIA, que segue o estilo Replicate: cria uma previsão, consulta o status e lê a saída.
URL base e autenticação:https://api.picassoia.com/v1 com o cabeçalho Authorization: Bearer pia_sk_...
Criar:POST /models/{owner}/{name}/predictions com o corpo {"input": {...}}
Consultar:GET /predictions/{id} até o status ser succeeded, failed ou canceled
Tempo: a resposta de criação inclui eta.next_poll_in_seconds, um intervalo de consulta que vale respeitar
Ferramenta Python com polling
import asyncio
import os
import httpx
from mcp.server.mcpserver import MCPServer
API = "https://api.picassoia.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PICASSOIA_API_TOKEN']}"}
mcp = MCPServer("picassoia-media")
slots = asyncio.Semaphore(5) # the account allows 5 concurrent predictions
async def run_prediction(model: str, payload: dict, timeout_s: int = 600) -> list[str]:
async with slots, httpx.AsyncClient(headers=HEADERS, timeout=30) as http:
created = await http.post(
f"{API}/models/{model}/predictions", json={"input": payload}
)
created.raise_for_status()
prediction = created.json()
waited = 0
while prediction["status"] in ("starting", "processing"):
if waited >= timeout_s:
raise TimeoutError(f"Prediction {prediction['id']} is still running")
delay = (prediction.get("eta") or {}).get("next_poll_in_seconds", 3)
await asyncio.sleep(delay)
waited += delay
polled = await http.get(f"{API}/predictions/{prediction['id']}")
polled.raise_for_status()
prediction = polled.json()
if prediction["status"] != "succeeded":
raise RuntimeError(f"Prediction {prediction['status']}: {prediction.get('error')}")
output = prediction["output"]
return output if isinstance(output, list) else [output]
@mcp.tool()
async def generate_image(prompt: str, aspect_ratio: str = "16:9") -> str:
"""Generate one image from a text prompt and return its URL."""
urls = await run_prediction(
"picassoia/picassoia-image",
{"prompt": prompt, "aspect_ratio": aspect_ratio, "num_outputs": 1},
)
return urls[0]
if __name__ == "__main__":
mcp.run(transport="stdio")
Dois detalhes importam aqui. O loop usa asyncio.sleep, então o servidor continua respondendo a outras requisições enquanto um job roda, e segue o intervalo sugerido pela API em vez de bombardeá-la. O semáforo mantém você abaixo do limite de concorrência da conta. O modelo PicassoIA Image aceita prompt, aspect_ratio, seed, num_outputs (1 ou 2), output_format e output_quality. Para edições, aponte o mesmo auxiliar para o PicassoIA Image Editor Pro.
Ferramenta TypeScript para vídeo
O mesmo padrão funciona em TypeScript. Esta versão acrescenta uma ferramenta de vídeo sobre a função buildServer vista antes:
const API = "https://api.picassoia.com/v1";
const headers = {
Authorization: `Bearer ${process.env.PICASSOIA_API_TOKEN}`,
"Content-Type": "application/json",
};
async function runPrediction(model: string, input: Record<string, unknown>) {
const created = await fetch(`${API}/models/${model}/predictions`, {
method: "POST",
headers,
body: JSON.stringify({ input }),
});
if (!created.ok) throw new Error(`Create failed: ${created.status}`);
let prediction = await created.json();
while (["starting", "processing"].includes(prediction.status)) {
const delay = prediction.eta?.next_poll_in_seconds ?? 5;
await new Promise((resolve) => setTimeout(resolve, delay * 1000));
const polled = await fetch(`${API}/predictions/${prediction.id}`, { headers });
prediction = await polled.json();
}
if (prediction.status !== "succeeded") {
throw new Error(`Prediction ${prediction.status}`);
}
return [prediction.output].flat() as string[];
}
server.registerTool(
"generate_video",
{
title: "Generate video",
description: "Make a short clip with synchronized audio from a text prompt",
inputSchema: {
prompt: z.string().max(4000),
duration: z.union([z.literal(5), z.literal(10)]).default(5),
resolution: z.enum(["480p", "720p"]).default("720p"),
},
},
async ({ prompt, duration, resolution }) => {
const [url] = await runPrediction("picassoia/seedance-2.5-lite", {
prompt,
duration,
resolution,
});
return { content: [{ type: "text", text: url }] };
}
);
O vídeo é mais lento. A página do modelo Seedance 2.5 Lite lista exemplos de execução de cerca de 100 a 190 segundos, tempo suficiente para que alguns clientes desistam de uma única chamada de ferramenta. Um desenho mais seguro divide o trabalho em duas partes: start_video retorna a previsão id imediatamente, e check_video recebe esse id e devolve o status ou a URL final.
É assim que o conector da PicassoIA para o claude.ai funciona. Suas ferramentas de geração retornam um predict_id e uma espera sugerida, e get_generation é chamada até o job ter sucesso ou falhar. O mesmo modelo também aceita um image opcional como primeiro quadro, um seed, um aspect_ratio e uma flag save_audio para clipes sem som.
💡 Dica: Mantenha o resultado da ferramenta pequeno. Devolva a URL e um resumo de uma linha, não o arquivo. O assistente só precisa de um link para mostrar ou repassar.
Como usar a PicassoIA com MCP
Há duas formas de acessar a PicassoIA a partir de um assistente: o conector pronto, ou um servidor próprio envolvendo a API, como nos exemplos acima.
Configuração passo a passo
Escolha o caminho. Para um cliente de chat, adicione o conector da PicassoIA nas configurações do claude.ai. Ele expõe generate_image, edit_image, generate_video_picassoia, generate_video_seedance, get_generation, cancel_generation, list_models, get_account e list_generations. Para o código do seu próprio agente, use a rota da API.
Crie um token de API. Entre na página da API da PicassoIA e crie um. Ele começa com pia_sk_, e uma conta tem no máximo dois. Confira a página de preços para ver qual plano inclui acesso à API antes de construir sobre ela.
Guarde-o nas variáveis de ambiente. Execute export PICASSOIA_API_TOKEN=pia_sk_... no seu shell. Mantenha-o fora dos arquivos de código e de qualquer configuração de cliente que você versione.
Escolha um modelo na tabela abaixo.
Envolva e teste. Cole uma ferramenta dos exemplos, rode-a no Inspector com um prompt simples e depois registre-a no seu cliente.
Texto ou imagem para vídeo com áudio sincronizado, 5 ou 10 segundos
Qual modelo decide quando chamar suas ferramentas? Qualquer modelo de linguagem capaz de chamar ferramentas pode fazer isso. O catálogo de modelos de linguagem da PicassoIA lista Claude Sonnet 5, GPT 5.6 Sol, Kimi K2.6 e Gemini 3.5 Flash, entre muitos outros. Teste mais de um contra o mesmo servidor e compare a confiabilidade com que cada um escolhe a ferramenta certa.
Limites que vale conhecer
5 previsões simultâneas por conta, compartilhadas entre tokens e conexões MCP
10 MB de tamanho máximo do corpo da requisição
4.000 caracteres no máximo por prompt
3 horas até uma previsão expirar
2 tokens de API por conta
Erros que quebram servidores MCP
A maioria das primeiras execuções que falham vem de um de três problemas. Cada um é fácil de evitar quando você sabe onde procurar.
Registrar logs na saída padrão
Com stdio, a saída padrão é o canal do protocolo. Um print() ou console.log() perdido corrompe o fluxo JSON-RPC, e o cliente relata um erro de análise ou um servidor que nunca conectou. Envie os logs para a saída de erro padrão:
Em TypeScript, use console.error("polling prediction").
Chamadas bloqueantes e nomes vagos
Um time.sleep(30) dentro de uma ferramenta assíncrona congela todas as outras requisições no mesmo servidor. Use await asyncio.sleep em Python e um timer com await em TypeScript, como fazem os exemplos. Os nomes importam tanto quanto: uma ferramenta chamada do_task não dá ao modelo nada para escolher, enquanto generate_image com uma docstring clara diz exatamente quando recorrer a ela.
Segredos e payloads grandes
Leia os tokens de variáveis de ambiente, nunca de arquivos de código, e nunca os exiba em um resultado de ferramenta. Para a saída, devolva URLs em vez de base64. Uma única imagem em base64 pode chegar a megabytes e encher a janela de contexto do modelo, e a API da PicassoIA limita o corpo das requisições a 10 MB de qualquer forma.
Experimente na PicassoIA hoje
Copie server.py ou o par TypeScript, rode no Inspector e você terá um servidor MCP funcionando em menos de dez minutos. Depois, dê a ele algo visual para fazer. Os servidores de referência oficiais no GitHub são um bom lugar para ler mais padrões, e os dois repositórios dos SDKs incluem uma pasta de exemplos.
Abra o PicassoIA Image e escreva um prompt próprio, edite uma foto com o Image Editor Pro ou anime um quadro com o Seedance 2.5 Lite. Seja o que for que você construir, o ciclo é o mesmo: descrever, enviar, consultar, revisar.
Experimente criar suas próprias imagens com a Picasso IA e, quando um prompt funcionar, transforme-o em ferramenta para que seu assistente possa repeti-lo sob demanda.