Como compartilho links com meus agentes
Quando quero que um agente leia uma página, passo o link pelo xmd: https://xmd.imfrank.dev/?url= mais a URL. O agente recebe só o conteúdo, em Markdown. Não precisa de login, chave de API nem servidor MCP. Se o agente consegue fazer um GET, ele consegue usar. O código está em github.com/franklinjavier/xmd.
Eu uso isso no Grok Bot e no Claude, por exemplo para ler as respostas de um post no X.
O HTML original é quase tudo ruído
Peguei uma resposta do @jack para testar. Um curl direto em x.com/jack/status/2110642522355638381 retorna 160 KB de HTML. O texto do post está lá, no meio dos botões de “Log in” e “Sign up”, mas as respostas não vêm. Pelo xmd, com mode=replies, a mesma URL vira 3,5 KB de Markdown com o post e 13 respostas.
| Link | HTML original | Saída do xmd |
|---|---|---|
Post do X, modo replies |
160 KB, sem as respostas | 3,5 KB, post + 13 respostas |
| MDN, status 418 | 208 KB | 3,8 KB |
| Post do meu blog sobre StyleX | 49 KB | 12,5 KB, o Markdown que o próprio site serve |
| Excalidraw (SPA, depende de JavaScript) | 6,9 KB, quase só script | 885 bytes, o texto da tela inicial |
Os números são de 21 de outubro de 2026.
Como usar
Dá para colar a URL direto no chat do agente, num curl ou num script:
https://xmd.imfrank.dev/?url=https://x.com/jack/status/2110642522355638381&mode=replies
O https:// é opcional: ?url=example.com também funciona. Se o link tiver ? ou &, ele precisa de URL encoding. Senão, os parâmetros dele se misturam com os do xmd. No terminal, o curl resolve isso:
curl -G 'https://xmd.imfrank.dev/' --data-urlencode 'url=https://example.com'
Também tem um CLI, que precisa do Bun 1.4 ou mais novo. Para instalar direto do GitHub:
bun add -g github:franklinjavier/xmd
Ou clonando o repo:
git clone https://github.com/franklinjavier/xmd && cd xmd && bun install && bun link
A instalação ocupa 34 MB em disco, quase tudo do @cloudflare/puppeteer, que o endpoint usa para abrir o browser. O comando é xmd, e x-md também funciona:
xmd https://example.com
xmd --mode replies https://x.com/jack/status/2110642522355638381
O CLI também pede primeiro o Markdown do site, mas, quando precisa converter, faz isso localmente, com um conversor de HTML embutido. Workers AI e Browser Run, que aparecem a seguir, só existem no endpoint.
O xmd só converte quando o site não tem Markdown
Os links do X vão para a API pública do FxTwitter, que retorna o post em JSON sem pedir chave. O xmd monta o Markdown a partir desse JSON.
Para qualquer outro link, o xmd primeiro pede o Markdown para o próprio site. A requisição já vai com o header Accept: text/markdown. Se a resposta vier em HTML, ele procura um <link rel="alternate" type="text/markdown"> na página e, se não achar, tenta a mesma URL com .md no fim, a convenção do llms.txt. Só se nada disso funcionar o HTML vai para o toMarkdown do Workers AI. Segundo a documentação da Cloudflare, essa conversão é grátis para a maioria dos formatos.
Meu blog responde em Markdown quando recebe esse header:
$ curl -sI -H 'Accept: text/markdown' https://franklinjavier.com/pt-br/blog/stylex-nao-deixou-o-site-mais-rapido/
HTTP/2 200
content-type: text/markdown; charset=utf-8
vary: Accept
Passando esse post pelo xmd, o Markdown vem do próprio site, sem conversão:
x-xmd-engine: native
x-xmd-source: native
O MDN não retorna Markdown em nenhuma dessas tentativas, então o header mostra a conversão:
x-xmd-engine: ai
x-xmd-source: ai
Algumas páginas só têm conteúdo depois que o JavaScript carrega. Quando a conversão vem vazia ou parece ser uma página desse tipo, o xmd abre o link no Browser Run, o Chrome headless da Cloudflare (o antigo Browser Rendering), e converte o HTML já renderizado. No Excalidraw, esse caminho levou 5 segundos num teste e 7 em outro, e retornou o texto que aparece na tela, inclusive o “Pick a tool & Start drawing!”.
Os headers x-xmd-source e x-xmd-engine trazem o mesmo valor e dizem de onde veio cada resposta:
| Valor | De onde veio |
|---|---|
native |
Markdown do próprio site (pelo Accept, pelo rel=alternate ou pela URL com .md) |
x |
Post do X, pelo FxTwitter |
ai |
HTML convertido pelo Workers AI |
browser |
Página renderizada no Browser Run e convertida depois |
fallback |
Conversor embutido, que também repassa texto puro sem mudar |
No X, o parâmetro mode define o que vem junto
Às vezes o post é uma resposta a alguém, ou o quinto post de uma thread. O parâmetro mode define o que vem junto com ele:
| Modo | O que entra no Markdown |
|---|---|
post |
Só o post do link. É o padrão. |
ancestors |
A conversa desde o primeiro post até o do link, com no máximo 32 níveis. |
thread |
A thread do autor, em ordem. As respostas de outras pessoas ficam de fora. |
replies |
O post do link e as respostas a ele, de qualquer autor, em ordem cronológica. Os permalinks e as métricas vão para uma lista de referências no fim. |
O começo do replies no post do @jack:
## Replies to @jack
### 1/14 · jack (@jack)
@marcportermagee if you want. or use the screen.
### 2/14 · The Nth ° (@The_future_i)
@jack @marcportermagee Jack. Help a Jack.
Uma extensão para não montar a URL na mão
O repo também tem uma extensão do Chrome, na pasta extension/. Um clique copia o link do xmd da aba aberta e abre esse link numa aba nova. Não está na Chrome Web Store. Para instalar:
curl -fsSL https://xmd.imfrank.dev/extension.sh | sh
O script baixa a extensão para ~/xmd-extension e mostra os passos para carregar a pasta no Chrome.
Onde ele falha
O replies traz só uma página de respostas. O post do @jack mostra 24 respostas, e vieram 13. O FxTwitter retorna uma página só, o xmd não pagina, e a saída termina com um aviso de que pode haver mais. Post apagado ou protegido não vem.
A conversão também não é perfeita. Na página do MDN, sobram os links “Skip to main content” e um pedaço do HTML original, <!doctype html>, logo antes do título.
O browser tem cota. No plano grátis da Cloudflare são 10 minutos por dia, 3 browsers ao mesmo tempo e uma instância nova a cada 20 segundos. Quando o browser está ocupado, o xmd responde 503 com Retry-After: 60, e quando a cota do dia acaba, 503 até a meia-noite UTC. O próprio xmd também limita o browser a 3 páginas por minuto por IP.
O endpoint aceita 30 requisições fora do cache por minuto por IP e depois responde 429. Ele só busca páginas públicas: localhost, IPs privados e portas diferentes de 80 e 443 recebem 400 com “Blocked target”.