# MDShare > Publica Markdown em link permanente, recebe revisão humana em comentários > ancorados a trechos do texto, e devolve esses comentários pela API para o > agente agir em cima deles. O MDShare fecha o ciclo entre quem escreve e quem revisa: **um LLM publica, um humano comenta o trecho exato, o LLM lê o comentário pela API e corrige.** - Skill pronta para instalar em um agente: `/skill` - Base externa: `https://mdshare-a2m.pages.dev` - Base interna (com login Google Fibersals): `https://mdshare.fibersals.com.br` - Documentação completa para humanos: `/docs` --- ## Ciclo de revisão 1. **Publicar** — `POST /api/upload` com o markdown. Guarde o `id` retornado. 2. **Entregar** — mande a URL `/view/{id}` para quem vai revisar. Se for alguém de fora, publique com `password` e mande a senha junto. 3. **Revisar** — a pessoa seleciona um trecho no navegador e comenta. Cada comentário guarda o texto citado, não uma posição de página, e **pertence à revisão em que foi feito**. 4. **Ler** — `GET /api/{id}?format=commented` devolve o markdown com cada comentário delimitado no lugar exato. É a forma mais direta; se preferir JSON, `GET /api/{id}/comments?status=open`. 5. **Agir** — aplique a correção no markdown e publique com `PUT /api/update` (vira revisão nova, mesmo link). 6. **Fechar** — `PATCH /api/{id}/comments/{commentId}` com `{"status":"resolved"}` para cada comentário tratado. Volte ao passo 4 até não sobrar comentário aberto. ### Comentário pertence à revisão Publicar uma revisão nova **zera o painel**: a revisão 2 começa sem comentários. Os da revisão 1 continuam no sistema, ancorados no texto que eles comentaram — aparecem em `GET /api/{id}/comments?revision=1` e ao trocar a revisão no visualizador. `?revision=all` traz o histórico inteiro. A resposta da listagem traz `otherRevisions` dizendo quanto existe fora da revisão em foco, para nada sumir calado. Consequência prática para o agente: **leia os comentários antes de publicar a correção**, porque depois eles não estarão na revisão nova. ### Duas regras que evitam retrabalho - **Resolva o que você aplicou.** Se a correção altera justamente o trecho comentado, a âncora deixa de encontrar o texto e o comentário aparece como órfão na revisão nova. Isso é esperado — marque como `resolved` ao aplicar. - **Releia antes de agir.** O comentário pode ter sido escrito sobre uma revisão anterior; o campo `revision` diz qual. --- ## Markdown comentado — o formato para agir `GET /api/{id}?format=commented` devolve o documento inteiro com os trechos comentados delimitados e uma legenda no fim. Evita ter que casar âncoras por conta própria: ```markdown # Relatório de Vistoria O telhado apresenta **{{{c1}}}infiltração na face sul{{{/c1}}}**, com manchas. | Item | Situação | |---|---| | {{{c2}}}Manta asfáltica{{{/c2}}} | Comprometida | {{{comments c1 id=3f9a1c2b7d4e5061 status=open author="Eng. Marina" email=marina@fibersals.com.br revision=2 Isso já foi tratado na vistoria de 2025? resposta de "Douglas": Foi tratado sim. c2 id=7b2e91aa4c3d5f80 status=open author="Diretoria" revision=2 Confirmar a área comprometida. }}} ``` Como usar: 1. `{{{cN}}}` … `{{{/cN}}}` cercam **exatamente** o trecho comentado. O `N` segue a ordem do documento. 2. A legenda dá o `id` de cada comentário — é ele que vai no `PATCH` para resolver. 3. Ao corrigir, **remova os delimitadores** antes de publicar a revisão nova. Eles são anotação, não conteúdo. 4. Aceita `?status=open` para trazer só o que está pendente. Três marcações que podem aparecer na legenda: | Marcação | Significa | |---|---| | `scope=documento` | comentário geral, sem trecho — não tem delimitador no texto | | `scope=nao-localizado` | o trecho não existe mais no markdown; a legenda traz o `quote` original | | `scope=aproximado` | o trecho aparece repetido e sem contexto que o distinga; confira antes de agir | --- ## Autenticação e acesso | Quem | Como | Pode | |---|---|---| | Automação com token de agente | `Authorization: Bearer mds_...` | conforme o escopo do token | | Automação da Fibersals (IP na whitelist) | nada a fazer | tudo, inclusive apagar | | Dono do documento | `/auth/google` no domínio interno | tudo no documento dele | | Pessoa compartilhada | `/auth/google` | conforme o papel recebido | | Revisor externo | senha do documento via `POST /api/auth` | ler e comentar | | Qualquer um | — | ler documento aberto por link e seus comentários | ### Visibilidade Três estados, e a diferença entre os dois primeiros importa: | Estado | Como se cria | Quem entra | |---|---|---| | **Protegido** | `password` no upload | quem tem a senha (inclusive de fora da empresa) e qualquer conta `@fibersals.com.br` | | **Privado** | `owner` no upload | o dono e quem ele compartilhou — conta da casa sozinha **não** basta | | **Aberto por link** | sem `owner` nem `password` | qualquer um com o endereço | Documento com senha é feito para sair da empresa: lá fora a senha abre, e aqui dentro ninguém precisa dela. Documento privado é o oposto — é do dono e de quem ele convidar. Pelo caminho da identidade só entra conta do domínio da empresa ou pessoa compartilhada explicitamente. Login não abre documento de terceiros. ```bash # publicar já com dono e com todo mundo convidado, numa chamada só curl -X POST https://mdshare-a2m.pages.dev/api/upload \ -H "Content-Type: application/json" \ -d '{ "markdown": "# Proposta\n\nTexto.", "owner": "ddutra@fibersals.com.br", "share": [ {"subject": "marina@fibersals.com.br", "role": "commenter"}, {"subject": "@fibersals.com.br", "role": "viewer"} ] }' ``` `share` aceita também a forma curta `["a@b.com","c@d.com"]`, que vira `commenter`. Um item inválido recusa a publicação inteira — não existe publicar e compartilhar pela metade. Use `"visibility":"link"` para publicar com dono mas aberto a quem tiver o endereço. ### Compartilhar com pessoas (sem senha) ```bash # quem tem acesso curl https://mdshare-a2m.pages.dev/api/8f14e45f/share # dar acesso a uma pessoa, ou ao domínio inteiro curl -X PUT https://mdshare-a2m.pages.dev/api/8f14e45f/share \ -H "Content-Type: application/json" \ -d '{"subject":"marina@fibersals.com.br","role":"commenter"}' # abrir por link ou fechar de novo curl -X PUT .../share -d '{"visibility":"link"}' # transferir o documento para outra pessoa curl -X PUT .../share -d '{"owner":"marina@fibersals.com.br"}' # tirar o acesso curl -X DELETE ".../share?subject=marina@fibersals.com.br" # meus documentos e os compartilhados comigo (exige sessão) curl https://mdshare.fibersals.com.br/api/docs -b "mdshare_session=..." ``` Papéis: `viewer` lê, `commenter` também comenta, `manager` também compartilha e apaga. O dono é sempre manager. Só quem gerencia pode compartilhar. ### Token de agente `POST /api/upload`, `PUT /api/update` e os `DELETE` exigem **token de agente** ou IP na whitelist — não existe mais depender de IP fixo. ```bash curl -X POST https://mdshare-a2m.pages.dev/api/upload \ -H "Authorization: Bearer mds_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"markdown":"# Título\n\nTexto."}' ``` Escopos: `publish` (upload e update), `comment` e `admin` (apagar documento, revisão e comentário em definitivo). Token sem o escopo recebe 403 dizendo qual falta; token inválido ou revogado recebe 401. O documento publicado por um agente fica no nome do `owner` do token — ou seja, nasce **privado** e o dono compartilha com quem precisar. Peça o token a quem administra o MDShare. Quem administra cria em `POST /api/tokens {"name":"Bot X","scopes":["publish"],"owner":"bot@fibersals.com.br"}` e revoga em `DELETE /api/tokens/{id}` — criar e revogar exige conta da casa ou IP na whitelist, nunca outro token. **Não confunda** com o token de senha de documento: aquele vem de `POST /api/auth` com `{"id":"...","password":"..."}`, vale 24 h e serve só para ler e comentar aquele documento. O token de agente começa com `mds_`. --- ## Endpoints ### Publicar ```bash curl -X POST https://mdshare-a2m.pages.dev/api/upload \ -H "Content-Type: application/json" \ -d '{"markdown":"# Título\n\nTexto.","password":"opcional"}' # → {"success":true,"id":"8f14e45f","url":".../view/8f14e45f","revision":1} ``` Aceita também `text/markdown` no corpo, ou `multipart/form-data` com imagens. O **frontmatter YAML do topo não é renderizado** no visualizador — pode manter `mdshare_id` e afins no arquivo sem poluir a leitura. Ele continua no armazenamento e em `?format=raw` e `?format=commented`, onde você precisa dele. ### Atualizar (cria revisão, mantém o link e os comentários) ```bash curl -X PUT https://mdshare-a2m.pages.dev/api/update \ -H "Content-Type: application/json" \ -d '{"id":"8f14e45f","markdown":"# Título\n\nTexto corrigido."}' ``` ### Ler ```bash curl https://mdshare-a2m.pages.dev/api/8f14e45f # JSON com metadados curl "https://mdshare-a2m.pages.dev/api/8f14e45f?format=raw" # markdown puro curl "https://mdshare-a2m.pages.dev/api/8f14e45f?revision=2" # revisão específica curl "https://mdshare-a2m.pages.dev/api/8f14e45f?format=commented" # com os comentários embutidos ``` ### Comentários ```bash # pendentes curl "https://mdshare-a2m.pages.dev/api/8f14e45f/comments?status=open" \ -H "Authorization: Bearer $TOKEN" # de uma revisão específica, ou o histórico inteiro curl ".../comments?revision=1" -H "Authorization: Bearer $TOKEN" curl ".../comments?revision=all" -H "Authorization: Bearer $TOKEN" # criar, ancorado num trecho curl -X POST https://mdshare-a2m.pages.dev/api/8f14e45f/comments \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"author":"Agente","body":"Revisar este número.", "anchor":{"quote":"40 m²","prefix":"comprometida em cerca de ", "suffix":".","start":312,"end":317}}' # resolver curl -X PATCH https://mdshare-a2m.pages.dev/api/8f14e45f/comments/3f9a1c2b7d4e5061 \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"status":"resolved"}' # excluir (lógico; ?hard=true só para whitelist) curl -X DELETE https://mdshare-a2m.pages.dev/api/8f14e45f/comments/3f9a1c2b7d4e5061 \ -H "Authorization: Bearer $TOKEN" ``` Resposta da listagem: ```json { "success": true, "docId": "8f14e45f", "revision": 3, "total": 1, "comments": [{ "id": "3f9a1c2b7d4e5061", "parentId": null, "revision": 2, "author": "Eng. Marina", "authorEmail": "marina@fibersals.com.br", "body": "Isso já foi tratado na vistoria de 2025?", "anchor": { "quote": "infiltração na face sul", "prefix": "O telhado apresenta ", "suffix": ", com manchas visíveis", "start": 61, "end": 84 }, "status": "open", "created": "2026-08-12T12:00:00.000Z" }] } ``` `anchor` em `null` significa comentário geral do documento. `parentId` preenchido é resposta a outro comentário. `authorEmail` só vem quando a pessoa entrou com a conta Google — comentário feito com a senha do documento tem apenas o nome digitado. ### Apagar ```bash curl -X DELETE "https://mdshare-a2m.pages.dev/api/8f14e45f?confirm=8f14e45f" # documento curl -X DELETE "https://mdshare-a2m.pages.dev/api/8f14e45f?revision=2" # uma revisão ``` Irreversível e restrito à whitelist de IP. O `confirm` precisa repetir o id. --- ## Formato da âncora Uma âncora aponta para um trecho, não para uma coordenada: | Campo | Para que serve | |---|---| | `quote` | o trecho exato comentado | | `prefix` / `suffix` | até 32 caracteres em volta, para desempatar trechos repetidos | | `start` / `end` | posição no texto plano do documento renderizado | | `ordinal` | qual ocorrência do trecho é (0 = a primeira) | | `block` | o parágrafo, célula ou item de lista que contém o trecho | Ao abrir o documento, o trecho é procurado assim: se a posição guardada ainda aponta para ele, usa direto. Senão, procura todas as ocorrências e escolhe pelo conteúdo — primeiro o `block` idêntico (é o que identifica a linha certa de uma tabela com valores repetidos), depois o contexto. Só então recorre a `ordinal` e proximidade, e nesse caso marca a posição como aproximada. Se o trecho sumiu, o comentário fica órfão em vez de grudar no lugar errado. Ao criar comentário pela API sem ter renderizado a página, o mais seguro é mandar `quote`, `prefix` e `suffix` calculados sobre o markdown e deixar `start`/`end` aproximados: o contexto resolve a posição. --- ## Limites - Id do documento: 8 caracteres hexadecimais. Id de comentário: 16. - Corpo do comentário: 5000 caracteres. Nome do autor: 80. - 20 comentários por hora por IP em cada documento (não vale para whitelist nem para quem entrou com conta Google). - Respostas têm um nível só: não se responde a uma resposta. - Documentos e revisões não expiram.