1. Por que uma aula separada sobre debug local
Nos módulos anteriores já analisamos como funcionam o Apps SDK e o MCP. Agora vamos discutir por que precisamos de uma aula separada sobre debug local.
Muita gente segue este caminho: “Vou apenas abrir o ChatGPT, escrever ‘use meu App’ e ver o que ele diz. Se não funcionar — reescrevo o código no escuro”. Isso é como consertar o backend olhando apenas a página HTML no navegador e sem nunca abrir os logs do servidor.
Com ChatGPT Apps é especialmente fácil escorregar para a “magia”: existe o GPT, ele decide sozinho se chama uma tool ou não, e tem sua própria lógica de erros. Se você não vê o que acontece “debaixo do capô”, o debug vira um ritual xamânico.
Nosso objetivo: transformar isso em um processo de engenharia normal:
- você sabe onde olhar os logs do Next/MCP;
- você sabe acionar o servidor MCP manualmente pelo inspector;
- você entende o que exatamente o Dev Mode verifica e como garantir que o ChatGPT consegue chegar ao seu servidor.
E o mais importante: você para de depurar pela “adivinhação do GPT” e começa verificando primeiro os níveis mais baixos da pilha — servidor e protocolo — e só depois o UI e o comportamento do modelo.
2. Modelo mental: três níveis de debug
Para não se afogar no caos, vamos pensar em debug em termos de três níveis. É nosso pequeno “bolo em camadas”:
| Nível | O que vive lá | Sintomas típicos | Com o que depuramos |
|---|---|---|---|
| UI (widget) | Componentes React, state, window.openai | Widget vazio/cinza, renderização quebrada, botões não funcionam | DevTools do navegador |
| Backend / MCP-servidor | ferramentas, acesso a banco de dados/API | erros 500, “tool caiu”, dados estranhos | logs do servidor, MCP Inspector |
| Protocolo MCP | JSON‑RPC, tools/list, tools/call, esquemas | GPT escreve “não consegui chamar a ferramenta”, invalid params | inspector + logs de requisições |
No segundo nível, nos interessa o que o próprio servidor MCP faz (ferramentas, banco de dados, API), e no terceiro — os “fios” e o formato das mensagens MCP (JSON‑RPC, esquemas etc.).
Esse trio é a base do plano desta aula e do curso de debug.
Para visualizar, veja o fluxo de uma requisição:
sequenceDiagram
participant User as Usuário
participant ChatGPT as ChatGPT (Dev Mode)
participant Tunnel as Túnel (ngrok/CF)
participant Next as Next.js + MCP
User->>ChatGPT: "Escolha um presente de até US$ 50"
ChatGPT->>Next: tools/call search_gifts (via Túnel)
Next->>Next: Chamamos a ferramenta MCP, acessamos BD/API
Next-->>ChatGPT: JSON-RPC result + ToolOutput
ChatGPT-->>User: Resposta + render do widget
Pode quebrar em qualquer ponto: túnel, endpoint, lógica MCP, esquema JSON, widget React. Sua tarefa no debug é entender em qual camada está o erro, e não reescrever tudo de uma vez.
3. Logs do Next.js e do MCP: a base de tudo
Comecemos pelo mais chato e mais útil — os logs.
Onde vivem os logs no desenvolvimento local
No template padrão do Apps SDK em Next.js, o servidor MCP geralmente fica encapsulado em uma rota de API (/api/mcp ou similar). Você roda npm run dev, e em um terminal você tem:
- o dev server do Next.js;
- o handler do endpoint MCP, que recebe requisições JSON‑RPC tools/list, tools/call etc.;
- a impressão de tudo via console.log/console.error.
Se você separou o MCP em outro processo, haverá um segundo terminal, mas a ideia é a mesma: tudo de interessante aparece no console.
Importante distinguir:
- erros de build/inicialização — next dev não sobe, TypeScript falha, import incorreto etc.;
- erros de execução — tudo subiu, mas uma requisição específica para /api/mcp faz a ferramenta cair.
O Next.js, em modo de desenvolvimento, mostra erros de runtime com um overlay bonito e também escreve o stack trace no console.
O que logar no servidor MCP
Embora o MCP use o protocolo JSON‑RPC, para debug você não precisa imprimir absolutamente todo o JSON. Logs curtos, porém estruturados, são bem mais úteis.
Boa prática para logs do MCP — logar pelo menos: timestamp, request_id/traceId, nome da ferramenta, parâmetros (anonimizados), status (ok/error) e tempo de execução.
Um logger.ts bem simples para o GiftGenius pode ser assim:
// src/lib/logger.ts
export function logToolEvent(
phase: "start" | "end" | "error",
data: Record<string, unknown>
) {
const ts = new Date().toISOString();
console.log(JSON.stringify({ ts, phase, ...data }));
}
E no handler da ferramenta:
// src/mcp/tools/searchGifts.ts
import { logToolEvent } from "@/lib/logger";
export async function searchGiftsTool(args: { q: string }) {
const traceId = crypto.randomUUID();
logToolEvent("start", { tool: "search_gifts", traceId, args });
try {
// ... busca real de presentes ...
const results = []; // placeholder
logToolEvent("end", { tool: "search_gifts", traceId, count: results.length });
return results;
} catch (err) {
logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });
throw err;
}
}
Há duas nuances importantes.
Primeiro, não armazene nos logs e‑mails completos, telefones, números de cartão, tokens. Além de inadequado, conflita com as práticas básicas de segurança do MCP.
Segundo, traceId é seu melhor amigo. Ao olhar juntos os logs do Next.js e do MCP, você vincula facilmente eventos: a requisição tools/call específica, o render React correspondente e o log de rede do widget.
Como entender pelos logs onde caiu
Você tem um terminal e nele chegam linhas JSON do logToolEvent. Cenário típico:
- chegou phase: "start" com tool: "search_gifts";
- não há phase: "end", mas há phase: "error" e stack trace;
- disso fica claro que a execução chegou à sua lógica da ferramenta, mas algo quebrou dentro — por exemplo, chamada a API externa, parsing, acesso ao banco de dados.
Se você nem vê logs para esse nome de tool — a requisição nem chegou à ferramenta. Aí você sobe na pilha: túnel, endpoint /mcp, requisição JSON tools/call.
4. MCP Inspector: depuração do MCP antes do ChatGPT
Se os logs são seus olhos, o MCP Inspector (ou MCPJam Inspector) é o microscópio.
Mais sobre o MCP Inspector e por que usá-lo
No módulo sobre MCP já conectamos o Inspector para validar um servidor “Hello, MCP”. Aqui vamos usá-lo como ferramenta principal de depuração: primeiro garantimos que o MCP vive por si só e só depois entramos no Dev Mode e no UI.
O Inspector é um aplicativo separado (geralmente web‑UI mais CLI) que atua como cliente MCP. Ele se conecta ao seu servidor por HTTP/SSE ou stdin/stdout, executa tools/list, tools/call e exibe as mensagens JSON cruas, handshake, lista de ferramentas, recursos etc.
A ideia principal: tirar o ChatGPT da equação. Se sua ferramenta não funciona, primeiro verifique se o servidor está vivo, se o protocolo e o esquema estão corretos, antes de culpar o GPT.
Mini‑fluxo de trabalho com o Inspector
O cenário típico de depuração local é:
- Rodar npm run dev para subir Next.js + endpoint MCP.
- Rodar o MCP Inspector, por exemplo:
npx @modelcontextprotocol/inspector
(o comando específico depende da ferramenta usada).
- No Inspector, indicar a URL do seu endpoint MCP, por exemplo http://localhost:3000/api/mcp (ou o túnel HTTPS, se quiser testá-lo também).
- Verificar se o handshake passou: o servidor deve responder com as capabilities suportadas, lista de tools, recursos etc.
- Chamar manualmente a ferramenta desejada: escolher search_gifts, inserir argumentos {"q": "para uma garota de até 30"}, clicar “Call tool” e observar:
- se veio resposta;
- se não retornou erro JSON‑RPC ou MCP;
- o que o servidor escreve nos logs para essa chamada.
Se no Inspector já está quebrando, nem precisa abrir o ChatGPT: conserte o servidor MCP.
Se no Inspector tudo está ok e o ChatGPT ainda reclama — o problema está acima: URL do Dev Mode, autorização, comportamento do modelo.
Exemplo “quebramos a tool de propósito”
Peguemos nosso search_gifts e quebremos de propósito:
export async function searchGiftsTool(args: { q: string }) {
if (args.q === "quebre-se") {
throw new Error("Erro didático para demonstrar debug");
}
// ... lógica normal ...
return [];
}
Em seguida:
- No Inspector, chame search_gifts com o argumento {"q": "quebre-se"}.
- Nos logs, veja phase: "error" e o stack trace.
- Garanta que o servidor MCP retorna o erro honestamente.
Depois, quando você conectar tudo ao ChatGPT Dev Mode e pedir ao modelo “escolha um presente com a palavra "quebre-se"”, ele tentará chamar a ferramenta e mostrará ao usuário algo como “I encountered an error running the tool”. Fica nítido: o erro não aparece por causa do modelo, mas por causa da sua exceção explícita.
Esse truque treina bem a mentalidade: você separa claramente o erro de negócio (lançamos Error) do erro de protocolo (JSON quebrado, nome de ferramenta errado etc.).
5. Debug do widget: DevTools, state e “debug banner”
Quando o servidor MCP está mais claro, vamos para o frontend — o widget do Apps SDK.
Onde e como ver os erros do widget
Seu widget é renderizado dentro do ChatGPT em um iframe sandbox. A boa notícia: esse iframe tem as mesmas DevTools do navegador.
Mini‑procedimento:
- Abra o ChatGPT no navegador (Chrome/Edge/Firefox).
- Abra o DevTools (geralmente F12 ou Ctrl+Shift+I).
- Na aba Console — selecione o contexto do frame em que vive seu widget (muitas vezes é o domínio web-sandbox.oaiusercontent.com).
- Atualize o chat/envie uma mensagem para o GPT mostrar seu App.
Se o widget:
- nem aparece;
- aparece cinza/vazio;
- mostra um erro em vermelho no console
— quase certamente é problema no código React: propriedade indisponível, import incorreto, hook mal usado etc.
A aba Network também é útil. Você verá:
- o carregamento do bundle JS do seu app (se 404/500 — o problema está no dev server/túnel);
- as requisições que seu widget faz para fora via window.fetch, e respostas 4xx/5xx.
Debug banner simples
Uma prática útil — adicionar ao componente raiz do widget um pequeno “debug banner” que, no Dev Mode, mostra o ambiente e a versão do build.
Exemplo:
// src/components/DebugBanner.tsx
export function DebugBanner() {
if (process.env.NODE_ENV !== "development") return null;
return (
<div style={{ padding: 4, background: "#222", color: "#0f0", fontSize: 10 }}>
ENV: dev | build: local | {new Date().toLocaleTimeString()}
</div>
);
}
E no componente raiz do widget:
// src/app/widget/page.tsx
import { DebugBanner } from "@/components/DebugBanner";
export default function GiftGeniusWidget() {
return (
<div>
<DebugBanner />
{/* restante do UI de busca de presentes */}
</div>
);
}
Se você abriu o ChatGPT, carregou o App e não vê o banner — o seu JS nem chegou ao navegador: ou erro de build, ou problema no endpoint, ou o widget simplesmente não está registrado no servidor MCP.
State local e tratamento de erros
Seu widget já deve exibir diferentes estados: carregando, sucesso, erro. Se não — é uma boa hora para adicionar.
Mini‑padrão:
const [status, setStatus] = useState<"idle"|"loading"|"error"|"success">("idle");
async function handleSearch(query: string) {
try {
setStatus("loading");
// chamamos a ferramenta MCP via window.openai.callTool ou hook do Apps SDK
setStatus("success");
} catch (e) {
console.error("Search failed", e);
setStatus("error");
}
}
No JSX:
{status === "error" && (
<div style={{ color: "red" }}>Algo deu errado, tente novamente.</div>
)}
Para debug é crítico que:
- você não engula exceções (senão o console fica vazio e o UI apenas “trava”);
- você reflita o erro no UI, ou o usuário achará que o App morreu.
6. Dev Mode como parte do debug: o que ele faz e quando não culpá-lo à toa
Agora incluamos o ChatGPT Dev Mode no quadro. Até aqui vimos apenas seu código. Mas às vezes tudo funciona localmente, no Inspector está tudo certo, e o ChatGPT ainda responde “Error talking to [AppName]” ou nem sugere seu App.
O que o Dev Mode faz
Dev Mode é o modo do ChatGPT no qual você pode:
- criar e editar seus Apps;
- especificar o endpoint do servidor MCP (geralmente https://seu-dominio/mcp ou /api/mcp);
- atualizar rapidamente o manifesto e metadados sem publicar na Store.
Do ponto de vista do debug, o Dev Mode é apenas mais uma camada de configuração:
- se a URL lá estiver errada;
- se você esquecer o /mcp no final;
- se o túnel gerou um novo domínio e você não atualizou as configurações
— o ChatGPT simplesmente não conseguirá alcançar seu servidor.
Cenário típico de quebra no Dev Mode
Clássico:
- Você subiu um túnel https://abcd.ngrok.io, configurou no Dev Mode e tudo funcionava.
- No dia seguinte, reiniciou o ngrok e obteve https://efgh.ngrok.io.
- No Dev Mode ainda está https://abcd.ngrok.io/mcp.
- O ChatGPT mostra “Error talking to GiftGenius”.
O MCP Inspector, apontado para http://localhost:3000/api/mcp, mostra que está tudo ok. Isso significa que o MCP está vivo, mas o ChatGPT olha para o lugar errado.
Solução: abrir as configurações do Dev Mode, atualizar a URL e não esquecer o /mcp no final.
Dev Mode vs Store
Nesta aula falamos apenas do Dev Mode — é seu sandbox. Aqui é normal mudar a URL com frequência, reconectar o túnel, ajustar o esquema de ferramentas.
Quando você for para a Store, o endpoint ficará mais rígido e esses truques não serão uma boa ideia. Mas até lá ainda há alguns módulos; por enquanto, quebre e conserte à vontade no Dev Mode.
7. Mini‑algoritmo de depuração: o que fazer quando “nada funciona”
Agora vamos juntar tudo em um algoritmo prático. No fundo, são os mesmos três níveis de debug do início da aula, só que em forma de passos.
Suponha que você abriu o ChatGPT, escolheu o GiftGenius, pediu “Escolha um presente de até 30 US$ para um amigo geek”, e:
- o GPT não fala nada sobre o App;
- ou mostra “Error talking to GiftGenius”;
- ou abre um widget vazio/cinza.
Como não entrar em desespero?
Etapa 1 (nível MCP/servidor). Verificar o MCP via Inspector e logs
Primeiro, ignore GPT e UI. Interessa apenas o servidor.
- Garanta que npm run dev está rodando e que o endpoint (/api/mcp) responde.
- Conecte o MCP Inspector a http://localhost:3000/api/mcp ou ao seu túnel.
- Verifique o handshake — a lista de tools deve aparecer.
- Chame manualmente a mesma ferramenta que, em tese, o GPT chamaria (por exemplo, search_gifts) com argumentos similares.
Se aqui já quebra — conserte o MCP: esquemas, lógica de negócio, chamadas de rede. Use os logs e o traceId para entender o que está falhando.
Etapa 2 (nível protocolo/Dev Mode). Verificar Dev Mode e URL
Se no Inspector tudo está perfeito e o ChatGPT ainda não vê seu App ou reclama de conexão:
- Abra as configurações do Dev Mode do seu App.
- Veja qual URL está indicada para o MCP.
- Compare com o que seu servidor/túnel realmente está servindo (e não esqueça de checar se há /mcp no final, se seu servidor exigir).
Muitas vezes o problema está exatamente aqui.
Etapa 3 (nível UI). Verificar o widget pelo DevTools
Se o ChatGPT chama as ferramentas com sucesso (visível pelos logs do MCP), mas o widget se comporta de forma estranha:
- Abra o DevTools no navegador na página do ChatGPT.
- Na aba Console — selecione o contexto do iframe do seu widget.
- Observe os erros de JS.
- Na aba Network — garanta que:
- o bundle JS do widget carrega sem 404/500;
- as requisições adicionais (via fetch/window.openai.fetch) retornam respostas coerentes.
Em paralelo, veja seu DebugBanner: se ele não apareceu, o React tree nem foi montado.
Etapa 4. Usar o Dev Mode para reproduzir um bug report
Quando receber um bug report de um colega/usuário, tente preservar o prompt exato em que quebrou. No Dev Mode dá para reproduzir o cenário rapidamente:
- Rodar npm run dev, subir o túnel.
- No Dev Mode, selecionar o App.
- Colar o prompt problemático.
- Em paralelo:
- observar quais requisições JSON chegam ao MCP nos logs;
- no Inspector, se necessário, repetir o tools/call com os mesmos argumentos.
Assim você transforma “às vezes algo não funciona” em um cenário reproduzível.
8. Pequenos toques de código para um debug mais confortável
Para fixar o conteúdo, adicionemos mais alguns trechos úteis ao nosso app GiftGenius.
Configuração de ambiente e níveis de log
Em algum ponto da configuração do servidor, é conveniente definir explicitamente o endpoint do MCP e o nível de log:
// src/config.ts
export const config = {
mcpEndpoint:
process.env.NODE_ENV === "development"
? "http://localhost:3000/api/mcp" // o túnel encaminha isso
: "https://api.giftgenius.com/api/mcp",
logLevel: process.env.NODE_ENV === "development" ? "DEBUG" : "ERROR",
};
E no logToolEvent você pode considerar o logLevel para não fazer spam em produção.
Log de erros estruturados do MCP
Ao tratar as ferramentas, tente capturar erros esperados e retornar mensagens claras, em vez de enviar tudo para um throw:
export async function searchGiftsTool(args: { q: string }) {
const traceId = crypto.randomUUID();
logToolEvent("start", { tool: "search_gifts", traceId, args });
try {
// ... código normal ...
return { content: [{ type: "text", text: "3 presentes encontrados" }] };
} catch (err) {
logToolEvent("error", { tool: "search_gifts", traceId, error: String(err) });
return {
content: [{ type: "text", text: "Erro ao buscar presentes. Tente novamente mais tarde." }],
isError: true,
};
}
}
Assim o ChatGPT verá que o resultado foi marcado como isError e poderá comunicar o problema corretamente ao usuário, enquanto você vê o que aconteceu nos logs.
9. Erros típicos no debug local do ChatGPT App
Erro nº 1: depurar “pelo GPT”, e não pelo servidor e inspector.
É tentador apenas ver o que o modelo responde e tentar adivinhar onde está o bug. Mas o modelo é a camada de cima. Se o servidor MCP não funciona por si só (manualmente, via Inspector) — não espere milagres do GPT. Primeiro estabilize o MCP, depois conecte o ChatGPT.
Erro nº 2: não olhar logs ou logar tudo sem critério.
A ausência de logs te deixa cego: você não sabe qual ferramenta foi chamada, com quais argumentos e como terminou. Logar demais, por outro lado, transforma o console em uma “matriz” de linhas desconexas. Melhor ter um log compacto e estruturado com tool, args (anonimizados), traceId, status e tempo de execução.
Erro nº 3: armazenar dados sensíveis nos logs.
Logar tokens, e‑mails completos e números de cartão é uma prática ruim tanto para segurança quanto para a política da OpenAI. Os logs devem conter apenas informações que realmente ajudam no debug; dados pessoais — masque ou não logue.
Erro nº 4: culpar o Dev Mode por tudo.
O Dev Mode vira bode expiatório com frequência: “A OpenAI quebrou algo”. Na prática, geralmente o problema é que você esqueceu de atualizar a URL após reiniciar o túnel ou indicou o caminho errado (/ em vez de /mcp). Antes de acionar o suporte, confira as configurações do Dev Mode e compare o endpoint com o endereço real do servidor.
Erro nº 5: ignorar o DevTools e o erro no widget.
Um widget vazio ou cinza quase sempre indica um erro de JavaScript no cliente. Se você só olha os logs do MCP e não abre o DevTools no ChatGPT, está vendo só metade do quadro. Criar o hábito de pressionar F12 e olhar Console/Network economiza horas de vida.
Erro nº 6: tentar “consertar” bug com atrasos mágicos.
Às vezes dá vontade de fazer um setTimeout ou um atraso estilo Thread.sleep “para tudo carregar”. No mundo MCP/Next/React, isso quase sempre é um tratamento errado: normalmente o problema é no esquema, endpoint errado ou bug de código — não no “tempo do servidor”. Melhor entender exatamente onde está a ruptura (Inspector → Dev Mode → widget) do que enterrá-la com atrasos.
Erro nº 7: fazer deploy na Vercel sem garantir que local está ok.
A vontade de “ir logo para produção” é compreensível, mas colocar um MCP quebrado na Vercel é a forma perfeita de ganhar dois níveis de problema: local e produção. Neste módulo exigimos deliberadamente: primeiro MCP Jam/Inspector → tudo ok, Dev Mode → cenários básicos funcionando; só depois o deploy.
GO TO FULL VERSION