MeuAccio: favoritos sincronizados, com API e MCP

Olá meus Unicórnios! 🦄✨
Confesso uma coisa: eu tinha favoritos espalhados em quatro navegadores, uma pasta "ler depois" com 300 links que eu nunca li e aquela aba aberta há três semanas "para não esquecer". 😅 E quando eu queria achar o link daquela documentação boa, não estava em nenhum deles.
Foi daí que nasceu o MeuAccio, o meu novo projeto. Quem leu Harry Potter reconhece o nome: é o feitiço convocatório, o Accio que traz o objeto até você. Aqui o objeto é o link. Você cola o endereço, ele vira um card bonito com capa, título e descrição, e aparece nos favoritos de todos os seus navegadores. ✨
E, como tudo que eu faço ultimamente, ele nasceu conversando com IA: tem uma API REST e um servidor MCP para o Claude, o ChatGPT e o Cursor. Neste artigo eu mostro como o app funciona e depois as duas portas de entrada, com chamadas de verdade e as respostas que voltaram.
MeuAccioSeus favoritos em cards, em todo navegador, na sua IA e na sua API. Gratuito, sem anúncios e sem cartão.meuaccio.com.br🪄 Como o MeuAccio funciona
A ideia cabe numa linha: cole um link, ele vira card, vira favorito em todo navegador, e a sua IA acha.
Quando você salva um endereço, o MeuAccio visita o site e lê de lá o título, a descrição, a imagem de capa e o ícone. Você não digita nada disso. E cada link pode, se você quiser:
- ficar numa lista, com nome e cor. Cada lista vira uma pasta de favoritos no navegador;
- ser arquivado: sai do mural e dos navegadores, mas continua guardado no Arquivo;
- ir para a lixeira, onde fica 30 dias antes de sumir de vez.
Para salvar, é só colar o endereço na barra do topo (ou apertar Ctrl+K). Aparece esta janelinha, e o Enter salva na lista que está aberta. O Tab troca de lista antes de salvar:

Um segundo depois, o card já estava no mural, com a cobrinha do Python de capa e a descrição tirada do próprio site:

Os cards desta lista são todos de teste, que eu criei para este artigo (e apaguei depois). No rodapé da barra lateral ficam o Arquivo e a Lixeira; em cima, as listas, que você arrasta para mudar a ordem.
O mesmo link em todo lugar
A parte que resolveu o meu problema dos quatro navegadores é a extensão, para Chrome, Edge, Opera e Firefox. Ela sincroniza nos dois sentidos: o que você salva no MeuAccio aparece nos favoritos do navegador, e o favorito que você cria com a estrelinha do navegador aparece no mural.
Você escolhe o quanto sincronizar: todos os favoritos, ou só uma pasta chamada "Meu Accio", para quem prefere manter o resto do navegador intocado. E arquivar um link tira ele dos favoritos também, sem apagar nada.
Além da extensão, tem app para Windows (com um atalho global de "Salvar link rápido", que funciona sem abrir a janela), o site instala como app no celular e aparece no menu Compartilhar dos outros apps, e dá para importar o HTML de favoritos exportado por qualquer navegador. Tudo de graça: sem anúncio, sem cartão, sem plano pago.
🔑 A chave da API
Tudo que vem daqui para baixo, fora o MCP com login, usa uma chave pessoal. Para gerar, clique no seu nome no rodapé da barra lateral e escolha API. Dê um nome para a chave (tipo "Script de backup") e clique em Gerar chave:

Tirei o começo da minha chave da captura, que é o que a janela mostra ao lado do nome. Três coisas que vale saber:
- A chave começa com
pk_e aparece inteira uma vez só, na hora em que é gerada. Copie na hora. - Ela dá acesso completo aos seus links e listas. Trate como senha.
- Crie uma chave por integração: se precisar revogar uma (a lixeirinha do lado), as outras continuam funcionando.
Para não colar a chave dentro de script nenhum, guarde numa variável de ambiente. No Linux, no Mac ou no Git Bash:
export MEUACCIO_API_KEY=SUA_CHAVE_AQUI
No PowerShell do Windows, o jeito muda:
$env:MEUACCIO_API_KEY = "SUA_CHAVE_AQUI"
Essa variável vale só para aquela janela de terminal. Fechou, abriu outra, tem de definir de novo. E, para colar no terminal, use Ctrl+Shift+V ou o botão direito do mouse: o Ctrl+V comum não funciona na maioria deles.
🌐 A API na prática
A API fica em https://www.meuaccio.com.br/api/v1, fala JSON e recebe a chave no cabeçalho Authorization: Bearer. A primeira chamada confirma que a chave funciona e diz de quem ela é:
curl https://www.meuaccio.com.br/api/v1/me \
-H "Authorization: Bearer $MEUACCIO_API_KEY"
A resposta traz o dono da conta. Troquei o nome e o e-mail por fictícios aqui:
{
"user": {
"id": 2,
"name": "Unicórnio de Teste",
"email": "[email protected]"
}
}
A barra invertida no fim da linha só diz ao terminal que o comando continua na linha de baixo. Dá para escrever tudo numa linha só, se preferir.
E se a chave estiver errada, o 401 já diz qual é o problema. Com uma chave inventada:
{
"error": {
"message": "API Key inválida ou revogada.",
"code": "API_KEY_INVALID"
}
}
Criar uma lista
Lista tem nome e cor em hexadecimal. Criei uma só para os testes deste artigo:
curl -X POST https://www.meuaccio.com.br/api/v1/lists \
-H "Authorization: Bearer $MEUACCIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Exemplo do blog", "color": "#6e4c9a" }'
{
"list": {
"id": 2,
"name": "Exemplo do blog",
"color": "#6E4C9A",
"position": 1,
"linkCount": 0,
"createdAt": "2026-09-28T10:46:10.000Z"
}
}
A API respondeu 201 e devolveu a cor em maiúsculas: ela normaliza. O nome é único na conta, sem diferenciar maiúsculas: mandei de novo como "exemplo do blog" e veio um 409:
{
"error": {
"message": "Você já tem uma lista com esse nome.",
"fields": {
"name": "Nome já usado."
}
}
}
Salvar um link
O único campo obrigatório é a url. O listId é a lista (sem ele, o link fica em "Sem lista"):
curl -X POST https://www.meuaccio.com.br/api/v1/links \
-H "Authorization: Bearer $MEUACCIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://www.php.net/", "listId": 2 }'
{
"link": {
"id": 6,
"url": "https://www.php.net/",
"title": "PHP",
"titleCustom": false,
"listId": 2,
"position": 0,
"domain": "php.net",
"description": "PHP is a popular general-purpose scripting language that powers everything from your blog to the most popular websites in the world.",
"siteName": null,
"imageUrl": "https://www.php.net/images/meta-image.png",
"iconUrl": "https://www.php.net/favicon-196x196.png?v=2",
"broken": false,
"pending": false,
"isArchived": false,
"archivedAt": null,
"createdAt": "2026-09-28T10:46:17.000Z",
"updatedAt": "2026-09-28T10:46:17.000Z",
"deletedAt": null,
"purgeAt": null,
"appUrl": "https://www.meuaccio.com.br/app?link=6"
}
}
Olha quanta coisa veio de graça: o title, a description, a capa em imageUrl e o ícone em iconUrl, tudo lido do site do PHP. Os outros campos que valem a pena conhecer:
| Campo | O que significa |
|---|---|
titleCustom | true quando você deu um nome próprio ao link |
pending | true enquanto o site ainda está sendo lido |
broken | true quando o site falhou nas últimas checagens |
isArchived | se o link está no Arquivo |
deletedAt e purgeAt | quando foi para a lixeira e quando some de vez |
appUrl | abre o card destacado no mural do MeuAccio |
As datas vêm sempre em UTC (o Z no fim). O 10:46 ali são 7h46 em Brasília.
O link repetido não é erro
Esta é a regra de que eu mais gosto: a mesma URL nunca é salva duas vezes. E a comparação é esperta. Tentei salvar o PHP de novo, só que com domínio em maiúsculas, um parâmetro de rastreamento e uma âncora:
curl -X POST https://www.meuaccio.com.br/api/v1/links \
-H "Authorization: Bearer $MEUACCIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://WWW.PHP.NET/?utm_source=blog#topo" }'
E o MeuAccio reconheceu que era o mesmo link. Veio um 409 com o código DUPLICATE_LINK e, dentro do erro, o link que já existia:
{
"error": {
"message": "Esse link já está salvo.",
"fields": {
"url": "Você já salvou esse link."
},
"code": "DUPLICATE_LINK",
"link": {
"id": 6,
"url": "https://www.php.net/",
"title": "PHP",
"titleCustom": false,
"listId": 2,
"position": 0,
"domain": "php.net",
"description": "PHP is a popular general-purpose scripting language that powers everything from your blog to the most popular websites in the world.",
"siteName": null,
"imageUrl": "https://www.php.net/images/meta-image.png",
"iconUrl": "https://www.php.net/favicon-196x196.png?v=2",
"broken": false,
"pending": false,
"isArchived": false,
"archivedAt": null,
"createdAt": "2026-09-28T10:46:17.000Z",
"updatedAt": "2026-09-28T10:46:17.000Z",
"deletedAt": null,
"purgeAt": null,
"appUrl": "https://www.meuaccio.com.br/app?link=6"
}
}
}
Repare no detalhe: o 409 aqui não é falha. Ele responde a pergunta "onde está esse link?". Se o seu código trata todo status diferente de 2xx como erro e tenta de novo, vai ficar batendo no mesmo 409 para sempre. O certo é ler o error.link e dizer à pessoa: "você já salvou, está na lista 2".
Dois outros 422 que valem conhecer. Um texto que não é endereço:
{
"error": {
"message": "Link inválido.",
"fields": {
"url": "Informe um link válido (começando com http:// ou https://)."
},
"code": "INVALID_URL"
}
}
E um endereço interno, como http://127.0.0.1/admin. O MeuAccio visita cada site que você salva, então ele se recusa a visitar endereços da rede interna (senão, qualquer pessoa usaria o servidor dele para espiar máquinas que não estão na internet):
{
"error": {
"message": "Esse endereço não pode ser salvo.",
"fields": {
"url": "Endereço interno ou bloqueado."
},
"code": "BLOCKED_URL"
}
}
Vários links de uma vez
Para salvar uma pilha de links, o /links/batch aceita até 50 numa chamada. Mandei quatro: dois novos, um repetido e um inválido, de propósito:
curl -X POST https://www.meuaccio.com.br/api/v1/links/batch \
-H "Authorization: Bearer $MEUACCIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "links": [
{ "url": "https://nodejs.org/", "listId": 2 },
{ "url": "https://web.dev/", "listId": 2 },
{ "url": "https://www.php.net/" },
{ "url": "isto nao e um site" }
] }'
{
"summary": {
"created": 2,
"restored": 0,
"duplicates": 1,
"invalid": 1
},
"results": [
{
"index": 0,
"status": "created",
"link": {
"id": 7,
"url": "https://nodejs.org/",
"title": "nodejs.org",
"titleCustom": false,
"listId": 2,
"position": 1,
"domain": "nodejs.org",
"description": null,
"siteName": null,
"imageUrl": null,
"iconUrl": "https://nodejs.org/favicon.ico",
"broken": false,
"pending": true,
"isArchived": false,
"archivedAt": null,
"createdAt": "2026-09-28T10:46:38.000Z",
"updatedAt": "2026-09-28T10:46:38.000Z",
"deletedAt": null,
"purgeAt": null,
"appUrl": "https://www.meuaccio.com.br/app?link=7"
}
},
{
"index": 1,
"status": "created",
"link": {
"id": 8,
"url": "https://web.dev/",
"title": "web.dev",
"titleCustom": false,
"listId": 2,
"position": 2,
"domain": "web.dev",
"description": null,
"siteName": null,
"imageUrl": null,
"iconUrl": "https://web.dev/favicon.ico",
"broken": false,
"pending": true,
"isArchived": false,
"archivedAt": null,
"createdAt": "2026-09-28T10:46:38.000Z",
"updatedAt": "2026-09-28T10:46:38.000Z",
"deletedAt": null,
"purgeAt": null,
"appUrl": "https://www.meuaccio.com.br/app?link=8"
}
},
{
"index": 2,
"status": "duplicate",
"link": {
"id": 6,
"url": "https://www.php.net/",
"title": "PHP",
"titleCustom": false,
"listId": 2,
"position": 0,
"domain": "php.net",
"description": "PHP is a popular general-purpose scripting language that powers everything from your blog to the most popular websites in the world.",
"siteName": null,
"imageUrl": "https://www.php.net/images/meta-image.png",
"iconUrl": "https://www.php.net/favicon-196x196.png?v=2",
"broken": false,
"pending": false,
"isArchived": false,
"archivedAt": null,
"createdAt": "2026-09-28T10:46:17.000Z",
"updatedAt": "2026-09-28T10:46:17.000Z",
"deletedAt": null,
"purgeAt": null,
"appUrl": "https://www.meuaccio.com.br/app?link=6"
}
},
{
"index": 3,
"status": "invalid",
"error": {
"code": "INVALID_URL",
"message": "Informe um link válido (começando com http:// ou https://)."
}
}
]
}
Duas coisas nessa resposta. Primeiro, o lote não é tudo ou nada: o inválido não impediu os outros. Cada item volta com o seu status (created, restored, duplicate ou invalid) e o index da posição que você mandou. Então leia o results item a item, não só o HTTP 200.
Segundo, olhe os dois links novos: "pending": true, o título é só o domínio e a capa é null. 😳 À primeira vista, parece que a leitura do site falhou. Não falhou: o lote salva primeiro e lê os sites em segundo plano. Uns segundos depois, pedi o web.dev de novo:
curl https://www.meuaccio.com.br/api/v1/links/8 \
-H "Authorization: Bearer $MEUACCIO_API_KEY"
{
"link": {
"id": 8,
"url": "https://web.dev/",
"title": "web.dev",
"titleCustom": false,
"listId": 2,
"position": 2,
"domain": "web.dev",
"description": "Orientações para criar experiências da Web modernas que funcionam em qualquer navegador.",
"siteName": "web.dev",
"imageUrl": "https://web.dev/images/social-wide.jpg?hl=pt-br",
"iconUrl": "https://www.gstatic.com/devrel-devsite/prod/v5be16d8c9f9402e54c2da2ff24cb75c148f5d2bc84c6f8dfc8b9e16ae456160b/web/images/favicon.png",
"broken": false,
"pending": false,
"isArchived": false,
"archivedAt": null,
"createdAt": "2026-09-28T10:46:38.000Z",
"updatedAt": "2026-09-28T10:46:38.000Z",
"deletedAt": null,
"purgeAt": null,
"appUrl": "https://www.meuaccio.com.br/app?link=8"
}
}
Agora sim: pending virou false, chegaram a descrição, a capa e o ícone. Se o seu programa mostra o título logo depois de salvar, espere uns segundos e leia de novo quando vier pending: true.
Buscar
A busca é o parâmetro q. Cada palavra precisa aparecer no nome, na descrição ou no endereço. Buscando "navegador":
curl "https://www.meuaccio.com.br/api/v1/links?q=navegador&limit=10" \
-H "Authorization: Bearer $MEUACCIO_API_KEY"
Vieram dois links: o web.dev, que tem "navegador" na descrição, e um link da MDN sobre HTTP que já estava na conta (a descrição dele fala em "navegadores web"). A resposta termina com a paginação:
"total": 2, "limit": 10, "offset": 0
O limit vai até 100, e o offset anda de página em página até o total. Dá para trocar a visão com view (all, nolist, archived, trash) e filtrar uma lista com listId. As aspas em volta do endereço importam: sem elas, o terminal entende o & como "rode isto em segundo plano" e corta o comando no meio.
Renomear, mover e arquivar
O PATCH recebe só o que muda. Dei um nome próprio ao link do PHP:
curl -X PATCH https://www.meuaccio.com.br/api/v1/links/6 \
-H "Authorization: Bearer $MEUACCIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "title": "Manual do PHP" }'
O link voltou com "title": "Manual do PHP" e "titleCustom": true. Mandando "title": null, ele voltou a ser "PHP", o título do site, com titleCustom de novo em false. Sem nome próprio, o título acompanha o site quando ele muda.
Arquivar é o mesmo PATCH, com { "isArchived": true }. Arquivei o web.dev e pedi as contagens da conta:
curl https://www.meuaccio.com.br/api/v1/links/counts \
-H "Authorization: Bearer $MEUACCIO_API_KEY"
{
"counts": {
"all": 4,
"noList": 1,
"archived": 1,
"trash": 0
}
}
O arquivado some da lista e do mural, mas não vai para a lixeira: fica no archived. Para trocar a ordem dos cards, existe o /move, com o beforeId dizendo "coloque antes deste":
curl -X POST https://www.meuaccio.com.br/api/v1/links/6/move \
-H "Authorization: Bearer $MEUACCIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "listId": 2, "beforeId": 7 }'
O link 6 foi para a position 0 da lista 2, logo antes do 7. Sem o beforeId, ele vai para o fim da lista.
🗑️ Excluir não é apagar
O DELETE responde 204, sem corpo nenhum, e manda o link para a lixeira:
curl -X DELETE https://www.meuaccio.com.br/api/v1/links/6 \
-H "Authorization: Bearer $MEUACCIO_API_KEY"
Com ?view=trash, ele aparece com as duas datas preenchidas:
"deletedAt": "2026-09-28T10:47:12.000Z",
"purgeAt": "2026-10-28T10:47:12.000Z",
O purgeAt é o dia em que ele some de vez: 30 dias depois. Até lá, um POST /links/6/restore traz o link de volta. E o DELETE /links/6/permanent, que apaga na hora, só funciona com o link já na lixeira. Tentei com um link fora dela e veio 404, "Link não encontrado(a)". É uma trava de propósito: nada some para sempre sem passar pela lixeira.
A armadilha de salvar de novo
Esta me pegou. Com o link do PHP na lixeira, salvei o mesmo endereço outra vez, sem o listId:
curl -X POST https://www.meuaccio.com.br/api/v1/links \
-H "Authorization: Bearer $MEUACCIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://www.php.net/" }'
{
"link": {
"id": 6,
"url": "https://www.php.net/",
"title": "Manual do PHP",
"titleCustom": true,
"listId": null,
"position": 1,
"domain": "php.net",
"description": "PHP is a popular general-purpose scripting language that powers everything from your blog to the most popular websites in the world.",
"siteName": null,
"imageUrl": "https://www.php.net/images/meta-image.png",
"iconUrl": "https://www.php.net/favicon-196x196.png?v=2",
"broken": false,
"pending": false,
"isArchived": false,
"archivedAt": null,
"createdAt": "2026-09-28T10:47:12.000Z",
"updatedAt": "2026-09-28T10:47:12.000Z",
"deletedAt": null,
"purgeAt": null,
"appUrl": "https://www.meuaccio.com.br/app?link=6"
}
}
Não veio 409, nem um link novo: voltou o mesmo id 6, com o nome "Manual do PHP" que eu tinha dado. Só que com "listId": null. 🤯 O link saiu da lixeira direto para "Sem lista", porque a lista vale o que você mandou agora. Se o seu script "desfaz" uma exclusão salvando de novo, mande o listId junto, ou use o /restore, que devolve o link para a lista de antes.
🟩 Um script em Node, sem instalar nada
O curl é ótimo para experimentar. Para o dia a dia, um script é mais confortável. Este aqui acha uma lista pelo nome e salva nela uma pilha de links numa chamada só, mostrando o que aconteceu com cada um. Ele usa o fetch que já vem no Node 18 ou mais novo: não precisa de npm install nenhum.
Salve como favoritos.js (no nano favoritos.js, cole com Ctrl+Shift+V, salve com Ctrl+O e Enter, saia com Ctrl+X; ou use o editor que preferir):
// favoritos.js: salva vários links numa lista do MeuAccio usando só o fetch do Node 18+
// Rode com: MEUACCIO_API_KEY=sua_chave node favoritos.js
const BASE = "https://www.meuaccio.com.br/api/v1";
const CHAVE = process.env.MEUACCIO_API_KEY;
const NOME_DA_LISTA = "Exemplo do blog";
const LINKS = [
"https://www.typescriptlang.org/",
"https://www.php.net/",
"isto nao e um site",
];
if (!CHAVE) {
console.error("Defina a variável de ambiente MEUACCIO_API_KEY.");
process.exit(1);
}
async function chamarApi(metodo, caminho, corpo) {
const resposta = await fetch(BASE + caminho, {
method: metodo,
headers: {
"Authorization": "Bearer " + CHAVE,
"Content-Type": "application/json",
},
body: corpo ? JSON.stringify(corpo) : undefined,
});
// O DELETE responde 204, sem corpo: chamar .json() aqui daria erro
if (resposta.status === 204) {
return null;
}
const dados = await resposta.json();
if (!resposta.ok) {
throw new Error(`HTTP ${resposta.status}: ${dados.error.message}`);
}
return dados;
}
async function acharLista(nome) {
const dados = await chamarApi("GET", "/lists");
// Compara sem diferenciar maiúsculas: "exemplo do blog" acha "Exemplo do blog"
for (const lista of dados.lists) {
if (lista.name.toLowerCase() === nome.toLowerCase()) {
return lista;
}
}
return null;
}
function esperar(segundos) {
return new Promise((pronto) => setTimeout(pronto, segundos * 1000));
}
async function principal() {
try {
const lista = await acharLista(NOME_DA_LISTA);
if (!lista) {
console.error("A lista", NOME_DA_LISTA, "não existe. Crie antes no MeuAccio.");
return;
}
const itens = [];
for (const url of LINKS) {
itens.push({ url: url, listId: lista.id });
}
// Uma chamada só para até 50 links, em vez de um POST por link
const dados = await chamarApi("POST", "/links/batch", { links: itens });
console.log("Resumo:", dados.summary);
const pendentes = [];
for (const item of dados.results) {
if (item.status === "invalid") {
console.log("-", LINKS[item.index], "->", item.error.message);
} else {
console.log("-", item.link.url, "->", item.status, "(id " + item.link.id + ")");
}
// Link novo volta com pending: true e o título provisório é só o domínio
if (item.link && item.link.pending) {
pendentes.push(item.link.id);
}
}
if (pendentes.length > 0) {
await esperar(5);
for (const id of pendentes) {
const lido = await chamarApi("GET", "/links/" + id);
console.log("Depois de ler o site:", lido.link.title);
}
}
} catch (erro) {
console.error("Deu erro:", erro.message);
}
}
principal();
Três detalhes evitam bugs que eu já vi muita gente cometer:
- O
ifdo 204 vem antes do.json(). ODELETEresponde sem corpo, eresposta.json()num corpo vazio estoura umSyntaxError. Este script não exclui nada, mas ochamarApié o pedaço que você vai copiar para outros. - O lote é lido item a item. O HTTP é 200 mesmo com item inválido ou repetido; quem diz o que aconteceu com cada link é o
statusdentro deresults. - O
item.link &&antes dopending. Item inválido não temlink, sóerror. Sem essa checagem, o script morre com "Cannot read properties of undefined" justamente no link ruim.
Para rodar, no Linux, no Mac ou no Git Bash, a variável vai na frente do comando:
MEUACCIO_API_KEY=SUA_CHAVE_AQUI node favoritos.js
No PowerShell, defina a variável antes e rode em seguida:
$env:MEUACCIO_API_KEY = "SUA_CHAVE_AQUI"
node favoritos.js
A saída:
Resumo: { created: 1, restored: 0, duplicates: 1, invalid: 1 }
- https://www.typescriptlang.org/ -> created (id 10)
- https://www.php.net/ -> duplicate (id 6)
- isto nao e um site -> Informe um link válido (começando com http:// ou https://).
Depois de ler o site: JavaScript With Syntax For Types.
O TypeScript entrou com título provisório e, cinco segundos depois, já tinha o título do site. O PHP foi reconhecido como repetido, e o texto inválido voltou com a mensagem pronta para mostrar. Com uma chave errada, a mesma execução para no primeiro passo:
Deu erro: HTTP 401: API Key inválida ou revogada.
📖 A documentação interativa
Não precisa decorar nada disso. A API tem uma referência interativa em https://www.meuaccio.com.br/api/v1/docs/, gerada da especificação OpenAPI 3.1, aberta e sem precisar de chave para ler:

O botão Authorize recebe a sua chave e deixa testar as chamadas pelo navegador. O arquivo /api/v1/openapi.json importa direto no Postman ou no Insomnia. E também tem um /api/v1/skill.md, um manual no formato Agent Skills para agentes de IA que rodam comandos, como o Claude Code, aprenderem a usar a API sozinhos.
Os erros cabem numa tabela, e todos vêm no mesmo formato: uma message em português, o campo com problema em fields e, quando o seu código precisa reagir a um caso, um code:
| Código | Quando acontece |
|---|---|
| 401 | Chave ausente (API_KEY_MISSING) ou inválida e revogada (API_KEY_INVALID) |
| 404 | O link ou a lista não existe, é de outra conta ou está na lixeira |
| 409 | Link já salvo (DUPLICATE_LINK) ou nome de lista repetido |
| 422 | Dado inválido: INVALID_URL, BLOCKED_URL ou o campo em fields |
| 429 | Mais de 120 requisições por minuto com a mesma chave |
🔌 O MCP: a sua IA ganha ferramentas
A API é para o seu código. O MCP (Model Context Protocol) é para a sua IA: o MeuAccio vira um servidor que entrega ferramentas prontas, cada uma com nome, descrição e parâmetros. A IA não monta curl nenhum; ela chama save_link e pronto.
O servidor fica em https://www.meuaccio.com.br/api/mcp. No Claude, no ChatGPT, no Cursor ou no VS Code, você procura por "conector personalizado" (ou "servidor MCP"), cola esse endereço e entra com a sua conta numa janela do MeuAccio. Não precisa copiar chave nenhuma. As IAs conectadas aparecem na mesma janela da API, na aba IAs (MCP), cada uma com o seu botão de desconectar:

No Claude Code, é um comando:
claude mcp add --transport http meuaccio https://www.meuaccio.com.br/api/mcp
Para cliente que não fala OAuth, a chave vai no cabeçalho, num mcp.json:
{
"mcpServers": {
"meuaccio": {
"type": "http",
"url": "https://www.meuaccio.com.br/api/mcp",
"headers": {
"Authorization": "Bearer SUA_CHAVE_AQUI"
}
}
}
}
O que o servidor oferece
MCP é JSON-RPC por HTTP, então dá para conversar com ele pelo próprio curl. Pedi a lista de ferramentas (o método tools/list do protocolo), usando a chave no cabeçalho:
curl -X POST https://www.meuaccio.com.br/api/mcp \
-H "Authorization: Bearer $MEUACCIO_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }'
O cabeçalho Accept com os dois formatos é exigência do transporte do MCP: sem ele, o servidor responde 406, "Not Acceptable". Vieram dezesseis ferramentas:
| Ferramenta | O que faz |
|---|---|
search | Busca links pelo nome, descrição ou endereço |
fetch | Lê os detalhes de um link pelo id |
whoami | Nome e e-mail da conta conectada |
list_links | Lista links com filtros e paginação |
get_link_counts | Quantos há em Todos, Sem lista, Arquivo, Lixeira e em cada lista |
save_link | Salva um link (sem duplicar) |
save_links | Salva de 1 a 50 links numa chamada |
update_link | Renomeia, muda de lista ou arquiva |
move_link | Põe o link numa lista, numa posição |
refresh_link | Lê o site de novo agora |
delete_link | Manda para a lixeira |
restore_link | Tira da lixeira |
list_lists | Todas as listas, na ordem da pessoa |
create_list | Cria uma lista |
update_list | Renomeia ou muda a cor |
delete_list | Exclui a lista; os links ficam "Sem lista" |
A descrição de cada ferramenta já ensina a IA a se comportar. A da delete_link, por exemplo, termina com "Confirme com a pessoa antes", e a da create_list manda perguntar antes de criar. E vêm quatro prompts prontos: organizar_sem_lista (sugere uma lista para cada link solto e só move depois da sua confirmação), resumo_da_semana (o que você salvou nos últimos 7 dias, por assunto), salvar_links_da_conversa (junta os links que apareceram no chat e salva todos de uma vez) e revisar_links_quebrados (acha os sites que pararam de responder).
O MCP em ação
Chamei a save_link com um endereço sem o https://, do jeito que uma pessoa escreveria no chat:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "save_link",
"arguments": {
"url": "developer.mozilla.org/pt-BR/docs/Web/JavaScript",
"listId": 2
}
}
}
O resultado vem em structuredContent:
{
"saved": true,
"link": {
"id": 9,
"url": "https://developer.mozilla.org/pt-BR/docs/Web/JavaScript",
"title": "JavaScript | MDN",
"titleCustom": false,
"listId": 2,
"position": 2,
"domain": "developer.mozilla.org",
"description": "JavaScript® (às vezes abreviado para JS) é uma linguagem leve, interpretada e baseada em objetos com funções de primeira classe, mais conhecida como a linguagem de script para páginas Web, mas usada também em vários outros ambientes sem browser, tais como node.js, Apache CouchDB e Adobe Acrobat. O JavaScript é uma linguagem baseada em protótipos, multi-paradigma e dinâmica, suportando estilos de orientação a objetos, imperativos e declarativos (como por exemplo a programação funcional). Saiba mais sobre o JavaScript.",
"siteName": "MDN Web Docs",
"imageUrl": "https://developer.mozilla.org/mdn-social-image.46ac2375.png",
"iconUrl": "https://developer.mozilla.org/favicon.ico",
"broken": false,
"pending": false,
"isArchived": false,
"archivedAt": null,
"createdAt": "2026-09-28T10:47:37.000Z",
"updatedAt": "2026-09-28T10:47:37.000Z",
"deletedAt": null,
"purgeAt": null,
"appUrl": "https://www.meuaccio.com.br/app?link=9"
}
}
O https:// que faltava foi colocado sozinho, e o "saved": true diz à IA que o link é novo (salvei o PHP pelo MCP também, e veio "saved": false com o link que já existia). O appUrl no fim é o que permite à IA responder "salvei, está aqui" com um link clicável que abre o card no mural. ✨
E a search devolve um formato enxuto, o mesmo que o ChatGPT espera dos conectores. Buscando "php":
{
"results": [
{
"id": "6",
"title": "PHP",
"url": "https://www.php.net/"
}
]
}
Repare que o id vem como texto ("6"), não como número. É o formato do protocolo de busca. Para ler o link inteiro, a IA chama a fetch com esse id.
Todos os links e a lista de teste deste artigo foram apagados no fim, e a conta ficou como estava. Se você quiser experimentar, é só criar a sua conta e colar o primeiro link:
Abrir o MeuAccioLinks favoritos em cards, extensão para Chrome, Edge, Opera e Firefox, API REST e servidor MCP, tudo gratuito.meuaccio.com.brPor hoje é só, meus unicórnios! 🦄✨
Que a magia do arco-íris continue brilhando em suas vidas! Até mais! 🌈🌟
Perguntas frequentes
O MeuAccio é gratuito?
Por que a API responde 409 ao salvar um link?
utm_*, a #âncora e maiúsculas no domínio, então https://WWW.PHP.NET/?utm_source=blog#topo é o mesmo link que https://www.php.net/. O 409 traz em error.link onde ele já está.Por que o link salvo volta com o título igual ao domínio?
pending: true e título provisório (o domínio). Segundos depois, um GET /links/{id} traz o título, a descrição e a capa de verdade.Qual a diferença entre arquivar e excluir um link?
isArchived: true) tira o link do mural e dos favoritos do navegador, mas ele continua salvo no Arquivo. Excluir manda para a lixeira por 30 dias, de onde ele volta com POST /links/{id}/restore.Salvar de novo um link que está na lixeira cria outro?
listId na hora de salvar. Mande a lista junto, ou mova depois.Preciso copiar a chave da API para usar o MCP?
https://www.meuaccio.com.br/api/mcp como conector e entra com a sua conta (OAuth). A chave só é necessária para cliente que não fala OAuth, no cabeçalho Authorization: Bearer.Leia também
ClickHouse: instalando na VPS e usando com Node.js
Como instalar o ClickHouse numa VPS Ubuntu, proteger com senha, criar uma tabela de acessos e consultar 10 milhões de linhas pelo Node.js.
Avaliando o RAG com o Jev como juiz
Como dar nota ao seu RAG em Node.js: um gabarito, a busca do Qdrant medida sem IA e o Jev, da TypeSafe, dizendo se cada resposta é fiel e correta.
Pensieve: notas com API, MCP e Skill para a sua IA
Como funciona o Pensieve, app gratuito de notas com lembretes, e como usar a API REST, o servidor MCP e a Skill para a sua IA anotar por você.


