Servidor MCP do Praeko
Um endereço, https://praeko.app/mcp, e uma de duas credenciais: a mesma chave de API da REST ou o login OAuth do seu agente. O agente lê estas docs, consulta a sua conta e, com a sua aprovação, cria, agenda, edita e cancela posts. Toda escrita que publica ou muda conteúdo tem dois passos: nada acontece na primeira chamada.
O que é
O MCP (Model Context Protocol) é o jeito padrão de um agente de IA chamar ferramentas. O servidor do Praeko roda no mesmo endereço da API e não guarda sessão: cada chamada é um POST com JSON-RPC e a resposta é um objeto JSON. Por dentro, cada ferramenta chama a mesma rota REST que você chamaria, com o contrato em inglês (X-Praeko-Contract: en). Por isso a validação, o escopo, o isolamento por workspace e os erros são os da API, e os nomes de campo são os do contrato.
| Uso | Ferramentas |
|---|---|
| Ler as docs | search_docs, read_page: busca e leitura das rotas (corpo, exemplo, erros) e de guias curtos, na versão atual. |
| Consultar a conta | list_channels, list_media, get_media, list_posts, get_calendar, get_insights, get_retention, get_usage. |
| Agir, sempre em dois passos | create_post, schedule_post, update_post, cancel_post, upload_media, create_idea. |
| Enviar um arquivo local | create_media_upload, complete_media_upload: o servidor prepara o envio e o agente roda o curl no shell dele (um passo só: criar o espaço não publica nada). |
create_media_upload (veja "Arquivo local"); sem shell, upload_media com uma URL pública.Dois servidores MCP: o remoto publica, o local faz o vídeo
Um agente pode usar os dois ao mesmo tempo. Este, praeko, é o remoto: roda em praeko.app/mcp e é o único que cria, agenda, edita e cancela posts. O praeko-studio é o servidor local do Praeko Studio (@praeko/studio-mcp): roda na sua máquina por stdio, serve a um agente sem terminal e nunca publica. Precisa de Node, de Chrome/Remotion para renderizar e das suas chaves, tudo no seu computador.
O fluxo completo: criar e renderizar o vídeo no servidor local, subir o resultado com studio_upload (devolve só o mediaId, em duas etapas) e publicar com create_post neste servidor, com a sua aprovação. Detalhes e a lista das 14 ferramentas locais em Studio no Claude Desktop: o servidor MCP local.
Conectar
O endpoint aceita duas credenciais, sempre em Authorization: Bearer … e em toda chamada: uma chave de API (praeko_…), que você cria e cola na configuração do agente, ou um token OAuth (mcpat_…), que o agente obtém sozinho depois que você entra e aprova a conexão. Os dois dão as mesmas ferramentas, com o mesmo escopo e a mesma confirmação em dois passos.
URL https://praeko.app/mcp
Transporte Streamable HTTP (só POST; sem sessão, sem stream)
Autenticação Authorization: Bearer praeko_… (chave de API, em TODA chamada; nunca na URL)
Authorization: Bearer mcpat_… (token OAuth, obtido pelo agente no login)
OAuth https://praeko.app/.well-known/oauth-protected-resource/mcp| Credencial | Quando usar | Como |
|---|---|---|
| Chave de API | Agentes de terminal e editor que aceitam cabeçalho (Claude Code, Codex, Cursor, Gemini CLI, opencode e outros), automações e o envio de arquivo local pelo shell. | Crie a chave e configure o cabeçalho (abaixo). |
| Login OAuth | Conectores que só falam OAuth (Claude.ai, Claude Desktop e mobile) e qualquer agente que prefira entrar sem colar chave, como o Claude Code. | Informe só a URL; o agente abre a tela de consentimento (abaixo). |
Com chave de API
Crie a chave no painel (Desenvolvedores, Chaves de API) ou com praeko key create (que exige uma chave admin). Escopo read basta para ler; as ferramentas que escrevem pedem write ou admin. Guarde a chave em variável de ambiente, nunca em arquivo versionado. Cada agente registra o servidor de um jeito: escolha o seu em Agentes de IA e copie o bloco pronto.
Com login (OAuth)
Adicione https://praeko.app/mcp no agente sem cabeçalho nenhum. A primeira chamada recebe 401 com resource_metadata no WWW-Authenticate; o agente descobre o servidor de autorização do Praeko e abre o navegador. Não há chave para copiar nem para vazar.
https://praeko.app/mcp. No Claude Code, claude mcp add --transport http praeko https://praeko.app/mcp e depois /mcp.# registra o servidor sem cabeçalho; o login acontece no navegador
claude mcp add --transport http praeko https://praeko.app/mcp
# dentro do Claude Code: /mcp, escolha praeko e entre| Ponto | Como funciona |
|---|---|
| Acesso | Leitura = escopo read (as dez ferramentas de leitura e docs). Escrita = escopo write (as dezoito). admin nunca é concedido por OAuth: chaves de API, canais, membros e configurações ficam fora do alcance do agente. O cliente pede um teto e a tela deixa você escolher esse acesso ou menos; sem pedido, vale Leitura. |
| Escopo efetivo | O menor entre o que você concedeu e o que o seu papel no workspace permite hoje. Quem perde poder no workspace perde também no agente. |
| Tokens | Token de acesso mcpat_… vale 1 hora; o de renovação mcprt_…, 30 dias, e é trocado a cada uso. Reapresentar um token de renovação já usado revoga a conexão inteira (o agente precisa entrar de novo). O servidor guarda só o hash. |
| Onde o token vale | Só no /mcp. A REST e o painel recusam o token OAuth; para eles, use uma chave de API. |
| Gerenciar e revogar | No painel, em Desenvolvedores, Chaves de API, seção Conexões de agentes: aplicativo, acesso, pessoa e último uso, com o botão Revogar, que corta o acesso na hora. Cada pessoa vê e revoga as próprias conexões; o admin do workspace vê e revoga todas. Autorizar de novo o mesmo aplicativo substitui a conexão anterior. |
| Padrões | OAuth 2.1 só com cliente público: código de autorização com PKCE S256 obrigatório e resource (RFC 8707). Descoberta por Protected Resource Metadata (/.well-known/oauth-protected-resource/mcp) e pelos metadados do servidor de autorização (/.well-known/oauth-authorization-server). Registro de cliente por Client ID Metadata Document (o client_id é uma URL https) ou por registro dinâmico (RFC 7591, POST /oauth/register). A resposta de autorização traz iss (RFC 9207). |
| Endereço de retorno | https, ou http só em localhost, 127.0.0.1 e [::1] (porta livre, como pede a RFC 8252). Esquemas próprios como cursor:// e vscode:// são recusados: nesses clientes, use a chave de API. |
Authorization configurado tem precedência sobre o OAuth: se a chave for recusada, ele não cai para o login. Para usar o login, registre o servidor sem --header. Com o ChatGPT, o login por OAuth ainda não foi conferido na documentação da OpenAI.Respostas de autenticação
| Situação | Resposta |
|---|---|
Sem Authorization |
401 com WWW-Authenticate: Bearer realm="Praeko MCP", scope="read write", resource_metadata="https://praeko.app/.well-known/oauth-protected-resource/mcp". |
| Chave ou token desconhecido, revogado ou vencido | 401 com error="invalid_token" e o mesmo resource_metadata no WWW-Authenticate. |
| Escopo insuficiente para a ferramenta | 403 com WWW-Authenticate: Bearer error="insufficient_scope", scope="write" (e o resource_metadata). |
| Cookie do painel | Não vale aqui: só chave de API ou token OAuth. |
GET, DELETE ou outro método |
405 com Allow: POST. |
Origin que não é do Praeko nem localhost |
403 (proteção contra DNS rebinding). Cliente de linha de comando não manda Origin. |
| Passou de 300 requisições por minuto | 429. O limite é o mesmo da REST, contado por chave de API ou por conexão OAuth. |
Ferramentas
Dezoito ferramentas. A lista que o agente recebe em tools/list varia com o escopo: read vê as dez de leitura e docs; write e admin veem as dezoito (o servidor não expõe nada a mais para admin: evite admin em agente). A coluna "O agente recebe" é a descrição literal, em inglês, que o servidor entrega; o texto de dois passos que fecha as seis ferramentas que publicam ou mudam conteúdo está logo depois da tabela. create_media_upload e complete_media_upload são as únicas escritas sem dois passos (veja "Arquivo local").
| Ferramenta | Escopo e anotações | O agente recebe | Argumentos |
|---|---|---|---|
search_docs |
read somente leitura | Search the Praeko API documentation (routes, fields, enums, errors, guides). Returns the best pages with a snippet; open one with read_page. Use it before building a create_post or update_post body. | query (obrigatório, até 200 caracteres), limit (1 a 20, padrão 8) |
read_page |
read somente leitura | Read one documentation page in full (a route with its body schema, example and errors, or a guide). Ids come from search_docs: route:posts.create, guide:overview, guide:mcp-two-step... |
id (obrigatório) |
list_channels |
read somente leitura | List the connected channels (one item per account; default marks the one used when a post item has no channel), with state and slots count. Use the channel ids in create_post. |
nenhum |
list_media |
read somente leitura | List the media library (last 100 items): id, type, name, state, size and duration. Use a ready media id in create_post. | type (video, cover ou image) |
get_media |
read somente leitura | One media item of the library: state (uploading or ready), type, size and, when ready, publicUrl, durationMs, width and height. Use it to check a media id (for example after complete_media_upload) before create_post or schedule_post. | mediaId (obrigatório) |
list_posts |
read somente leitura | List posts, newest first (queue and drafts nearest first). Filters: state, channel, tag. | state (queued, draft, sent, error, canceled), channel, tag, limit (1 a 200), offset |
get_calendar |
read somente leitura | Scheduled items in a period plus the free weekly slots of each channel (default: next 14 days; at most 100 days). | from, to (ISO-8601 com fuso), channel, tag |
get_insights |
read somente leitura | Cards, top posts, per-channel results and a summary for a period (views, likes, comments, followers), optionally compared with the previous period. | from, to, channel, tag, type, compare, sort, direction, page, limit |
get_retention |
read somente leitura | Audience retention, impressions and CTR per post of the period (YouTube) and reach/watch time (Instagram). A missing number is null, never 0; channels[].state says whether each account can deliver them. |
from, to, channel, limit |
get_usage |
read somente leitura | API usage of this workspace over the last N days (total, errors, p95, peak per minute vs the limit, by day and by key). MCP calls appear with the route prefix mcp:. |
days (1 a 30, padrão 7) |
create_post |
write escreve, rede externa | Create a post with one item per network. Items are drafts by default or take the next free slot of the channel (queue: true). For a fixed publication time use schedule_post. For a single-photo Instagram post send kind image with media.images (one JPEG image media, optional alt text). Never publishes immediately. TWO-STEP (parágrafo final comum às seis ferramentas de dois passos, logo abaixo da tabela) | media, texts, items (obrigatórios); kind (video ou image), title, baseText, queue, draft, options, tags, confirm. Com kind: image, media.images leva a foto (uma). Recusa scheduledFor |
schedule_post |
write escreve, rede externa | Create a post and publish it at a fixed time per item (scheduledFor, ISO-8601 with timezone: ALWAYS state the BRT time to the user as well; the panel runs on America/Sao_Paulo). The post goes out by itself at that time on the real account. For a single-photo Instagram post send kind image with media.images. TWO-STEP (parágrafo final comum às seis ferramentas de dois passos, logo abaixo da tabela) | os de create_post, mas todo item exige scheduledFor; sem queue nem draft |
update_post |
write escreve, destrutiva, idempotente, rede externa | Edit a post: title, texts, media, tags, items (mode, scheduledFor, queue, draft) or moveTo draft/queue. Send only what changes. WHAT CAN CHANGE, per network and when: (1) Any item still pending (draft or scheduled, not yet sent): time, mode, texts, queue/draft. (2) YouTube item ALREADY UPLOADED (the video sits in the channel as private with a future publish time, state "scheduled", or private without a time): it can still be RESCHEDULED (scheduledFor) and its title, description (texts.youtube), videoTags, categoryId and a not-yet-posted pinnedComment can change, until the video goes live. The server checks live on YouTube first; if the video is already public, the scheduled time already passed, the video was deleted, or Praeko did not upload it, nothing is written and the error names the reason and the manual alternative (YouTube Studio: https://studio.youtube.com/video/<videoId>/edit). The new time must be at least 5 minutes ahead; asking for the same time again changes nothing. A YouTube video never becomes public by this tool. (3) Instagram Reels in Buffer: text, time and draft only. (4) TikTok/Instagram items already sent or in the TikTok inbox cannot change. A request touching several items applies each one independently and the result lists itemResults (applied or not, and why): read it, nothing is skipped silently. TWO-STEP (parágrafo final comum às seis ferramentas de dois passos, logo abaixo da tabela) The preview lists, per item, "will_change" or "cannot_change" with the reason. |
postId (obrigatório); title, texts, media, tags, items (id ou network, mode, scheduledFor, queue, draft, videoTags, categoryId), moveTo (draft ou queue), confirm |
cancel_post |
write escreve, destrutiva, idempotente, rede externa | Cancel the pending items of a post (they will not be published; Instagram items scheduled in Buffer are removed there). Items already sent are kept. IMPORTANT for YouTube: a video Praeko already uploaded and scheduled (private with a future publish time) is NOT cancelled by this tool: it stays and YouTube will still publish it on time (the result says so). To stop it, either reschedule it with update_post, or manage it in YouTube Studio (https://studio.youtube.com/video/<videoId>/edit: remove the schedule to keep it private, or delete it). TWO-STEP (parágrafo final comum às seis ferramentas de dois passos, logo abaixo da tabela) | postId (obrigatório), confirm |
upload_media |
write escreve, rede externa | Add a video, a cover or the photo of an image post (type image: JPEG only) to the media library by giving a PUBLIC https URL that the Praeko server downloads (video/mp4, video/quicktime, image/png, image/jpeg; up to 50 MB; private networks are refused). Files are never sent through MCP: for larger files use the REST/CLI upload and then use the media id. TWO-STEP (parágrafo final comum às seis ferramentas de dois passos, logo abaixo da tabela) | url e type (video, cover ou image) obrigatórios; name, confirm |
create_media_upload |
write escreve, rede externa | Use this to publish a video, cover or photo (type image, JPEG) that is a file on the user's machine (you need a shell; with no shell, use upload_media with a public URL). The file never goes through MCP: this tool only creates the media slot and returns the exact upload command (curl; a loop of parts above 50 MiB). Steps: 1) call this with the real size in bytes (wc -c < file), type (video, cover or image), mime (video/mp4, video/quicktime, image/png, image/jpeg; an image type takes only image/jpeg); 2) run upload.command in the shell after replacing /path/to/file; the API key is read from the shell environment (PRAEKO_KEY): NEVER ask for the key, paste it in the chat, print it or write it to a file; 3) call complete_media_upload with the mediaId; 4) use that media id in create_post or schedule_post (their two-step preview and confirmation still protect the publication). Creating the slot publishes nothing, so there is no confirmation code. Limits: video up to 2 GiB, cover and image up to 20 MiB (Instagram itself takes photos up to 8 MB: that is checked when the post is created), 20 pending uploads per workspace, 60 per hour; an upload not finished in 24 hours is discarded. | type (video, cover ou image), name, mime (video/mp4, video/quicktime, image/png, image/jpeg) e size em bytes, todos obrigatórios. Sem confirm: criar o espaço de envio não publica nada |
complete_media_upload |
write escreve, idempotente | Call this after running the upload command from create_media_upload. It finishes the upload (joins the parts of a large file; a single-PUT file is already complete), verifies it and returns status ready or uploading, with the media record: publicUrl, durationMs, width and height (read from the video header). Safe to call again. Then use the media id in create_post or schedule_post. | mediaId (obrigatório). Sem confirm |
create_idea |
write escreve | Add a card to the idea board (the "Create" screen). Nothing is published. TWO-STEP (parágrafo final comum às seis ferramentas de dois passos, logo abaixo da tabela) | title (obrigatório, até 200 caracteres); note, group (idea, scripting, ready, posted), tags, confirm |
TWO-STEP: the first call (without `confirm`) executes NOTHING: it returns a preview and a single-use `confirmation_code` (10 minutes). Show the preview (action, channels, time in BRT and UTC, media, text, warnings) to the user and WAIT for explicit approval. Only then call this tool again with the SAME arguments plus `confirm: "<code>"`. Changing any argument invalidates the code.As anotações vão em tools/list (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) e a coluna as escreve em palavras: "somente leitura" é readOnlyHint, "destrutiva" é destructiveHint, "idempotente" é idempotentHint e "rede externa" é openWorldHint (o que fala com uma rede social ou baixa da internet). O cliente decide se confia nelas: a especificação manda tratá-las como dicas.
O que cada escrita faz
create_post: um post com um item por rede. Semdraftnemqueue, nasce como rascunho; comqueue: true, pega a próxima vaga livre do canal. Nunca publica na hora e recusascheduledFor.schedule_post: o mesmo post, masscheduledFor(ISO-8601 com fuso) é obrigatório em todo item e vale o horário fixo. Semqueuenemdraft. O post sai sozinho, na conta real.update_post: edita o que ainda não saiu (título, textos, mídia, etiquetas, itens,moveTo). Um vídeo do YouTube que o Praeko já subiu e que ainda está privado ou agendado também muda de horário, título, descrição,videoTags,categoryIde comentário fixo ainda não postado; modo, fila e rascunho não mudam nele. O servidor confere ao vivo no YouTube antes de escrever e, se o vídeo já está público, o horário passou ou o vídeo sumiu, não escreve e o erro traz o motivo e o link do YouTube Studio. O novo horário precisa estar pelo menos 5 minutos à frente, e a ferramenta nunca torna um vídeo público. A prévia lista por itemwill_changeoucannot_change(emitemChanges, com o motivo); a resposta trazitemResults, item a item. Mande só o que muda.cancel_post: cancela os itens pendentes; o que já saiu fica. Não impede a publicação de um vídeo do YouTube já subido e agendado (o YouTube o publica na hora marcada; a prévia e a resposta avisam): mude o horário comupdate_postou resolva no YouTube Studio. A ferramenta não temdismissnem apaga vídeo; pela REST,DELETE /v1/posts/{id}?dismiss=truetira o horário (o vídeo fica privado) edismiss=true&delete_video=trueapaga o vídeo privado.upload_media: baixa uma URL públicahttpspara a biblioteca (veja abaixo).create_idea: um cartão no quadro de ideias. Nada é publicado.
Escrita em dois passos
A confirmação é do servidor, não do agente: mesmo que o agente esteja configurado para aprovar tudo, uma ferramenta de escrita nunca executa na primeira chamada. Vale igual para chave de API e para login OAuth.
confirmO servidor valida e devolve a prévia. Nada é gravado, nada é enviado.confirmation_code.confirmOs mesmos argumentos mais confirm: "<código>". Só agora executa.{
"name": "schedule_post",
"arguments": {
"title": "O teste que só falhava na segunda",
"media": {
"video": "01KA3VQ7M2T9XE4HB8N5RZ6C0D"
},
"texts": {
"youtube": {
"title": "O teste que só falhava na segunda",
"text": "…"
}
},
"items": [
{
"network": "youtube",
"mode": "scheduled",
"scheduledFor": "2026-11-13T20:30:00Z"
}
]
}
}{
"preview": true,
"executed": false,
"tool": "schedule_post",
"action": "Create a post and schedule it at fixed times",
"title": "O teste que só falhava na segunda",
"items": [
{
"network": "youtube",
"channel": {
"id": "…",
"name": "Meu canal",
"connected": true
},
"mode": "network default",
"timing": "fixed_time",
"scheduledFor": {
"utc": "2026-11-13T20:30:00.000Z",
"brt": "2026-11-13 17:30 (BRT, UTC-03:00)"
}
}
],
"media": [
{
"id": "01KA3VQ7M2T9XE4HB8N5RZ6C0D",
"found": true,
"type": "video",
"state": "ready"
}
],
"warnings": [
"Confirming schedules a REAL publication on the connected account(s) at the time above. It can be edited or cancelled until then (update_post, cancel_post)."
],
"confirmation_code": "K7MQ-2VXZ-9RT4",
"expires_at": "2026-11-13T12:10:00.000Z",
"expires_in_seconds": 600,
"next_step": "NOTHING was done yet. Show this preview to the user and wait for approval. …"
}{
"name": "schedule_post",
"arguments": {
"title": "O teste que só falhava na segunda",
"media": {
"video": "01KA3VQ7M2T9XE4HB8N5RZ6C0D"
},
"texts": {
"youtube": {
"title": "O teste que só falhava na segunda",
"text": "…"
}
},
"items": [
{
"network": "youtube",
"mode": "scheduled",
"scheduledFor": "2026-11-13T20:30:00Z"
}
],
"confirm": "K7MQ-2VXZ-9RT4"
}
}{
"preview": false,
"executed": true,
"tool": "schedule_post",
"result": {
"id": "…",
"items": [
{
"network": "youtube",
"state": "scheduled"
}
]
}
}O código de confirmação
| Propriedade | Valor |
|---|---|
| Formato | XXXX-XXXX-XXXX: 12 caracteres aleatórios (cerca de 58 bits), por exemplo K7MQ-2VXZ-9RT4. |
| Validade | 10 minutos (expires_in_seconds: 600). |
| Uso | Único. Duas confirmações ao mesmo tempo: só uma executa. |
| Preso a | O workspace, a credencial que pediu a prévia (a chave de API ou a conexão OAuth: renovar o token não invalida o código), a ferramenta e os argumentos (SHA-256 do JSON canônico: a ordem das chaves não importa). |
| Guardado como | Só o hash, no banco. |
| Recusado pela validação | Se a rota recusar o pedido (4xx, nada foi gravado), o código volta a valer até vencer: o usuário corrige o que faltava (a mídia ainda subindo, por exemplo) e confirma de novo. |
Mudar qualquer argumento, repetir o código, usar um vencido ou de outra credencial, ferramenta ou workspace falha. A resposta é um erro de execução (isError: true) com error.code confirmation_invalid e o motivo em error.reason (a message vem em inglês):
| `reason` | Significa | O que o agente faz |
|---|---|---|
unknown_code |
O código não existe neste workspace. | Chamar sem confirm para uma prévia nova. |
already_used |
O código já foi usado. | Pedir uma prévia nova se for repetir a ação. |
expired |
Passaram os 10 minutos. | Pedir uma prévia nova e mostrar de novo ao usuário. |
wrong_key |
O código foi emitido para outra chave de API ou outra conexão OAuth. | Pedir uma prévia com a credencial atual. |
wrong_tool |
O código é de outra ferramenta. | Usar a ferramenta que gerou o código. |
arguments_changed |
Os argumentos diferem dos que foram vistos na prévia. | Pedir uma prévia nova com os argumentos finais. |
{
"error": {
"code": "confirmation_invalid",
"message": "this confirmation code expired (10 minutes). Request a new preview and show it to the user again.",
"reason": "expired"
}
}upload_media: mídia por URL
A ferramenta recebe uma URL pública https e o servidor baixa o arquivo. O arquivo nunca passa pela conversa com o agente.
| Regra | Valor |
|---|---|
| Tipos | video: video/mp4, video/quicktime. cover: image/png, image/jpeg. image (a foto de um post de imagem): só image/jpeg. O tipo é conferido no download (unsupported_mime). |
| Tamanho | Até 50 MB, conferido no cabeçalho e durante o download. Acima disso, file_too_large: use o envio de arquivo local abaixo, ou o envio em partes da REST, do CLI ou do SDK. |
| Redirecionamentos | Até 3 saltos, e cada salto passa de novo pela mesma verificação. |
| Endereços recusados | Só https, sem usuário e senha, sem localhost, nome interno, IP privado, reservado ou de metadados de nuvem (a mesma barreira dos webhooks). |
O Worker não resolve o DNS antes de baixar: contra um nome público que resolva para IP privado, a proteção é a própria rede da Cloudflare, que não alcança a rede privada de ninguém.
Arquivo local: create_media_upload e complete_media_upload
O caso mais comum de um agente com shell (Claude Code, Codex, Gemini CLI, opencode, Cursor, Kimi, Cline): o vídeo está no disco. O MCP não carrega arquivo grande, então o servidor só prepara o envio e quem envia é o curl do próprio agente, direto para a API, com a chave que já está no ambiente do usuário.
create_media_uploadO agente informa type, name, mime e o size exato em bytes (wc -c < arquivo). O servidor cria a mídia e devolve mediaId, o comando curl pronto e a especificação HTTP.upload.command no shell. A chave vem de $PRAEKO_KEY (exporte a partir de PRAEKO_API_KEY se for o nome que você usa) e nunca aparece na conversa.complete_media_uploadJunta as partes (arquivo grande), confere e devolve status: ready com publicUrl, durationMs, width e height. Pode ser repetida; get_media consulta o estado.create_post ou schedule_postCom o mediaId. Estes continuam em dois passos: prévia, o seu "sim", código.| Tamanho | Como o arquivo sobe |
|---|---|
| Até 50 MiB | Um PUT em /v1/media/{id}/content, com o corpo igual ao arquivo. |
| Acima de 50 MiB (vídeo até 2 GiB) | Partes de 64 MiB em PUT /v1/media/{id}/parts/{n} (todas iguais, a última menor; cada parte pode ser reenviada). O comando devolvido já é o laço com dd e curl; a resposta traz também a lista de partes com a URL de cada uma. |
{
"executed": true,
"tool": "create_media_upload",
"upload": {
"mediaId": "01KB7J3V9Q2W8E5R1T6Y4N0M3P",
"state": "uploading",
"expectedSize": 48213377,
"mime": "video/mp4",
"expiresAt": "2026-11-08T12:00:00.000Z",
"keyHandling": "The API key is already in the user's shell environment as PRAEKO_KEY (…)",
"method": "single_put",
"http": {
"method": "PUT",
"url": "https://praeko.app/v1/media/01KB7J3V9Q2W8E5R1T6Y4N0M3P/content",
"headers": {
"Authorization": "Bearer $PRAEKO_KEY",
"Content-Type": "video/mp4"
},
"body": "the raw bytes of the file (exactly 48213377 bytes)"
},
"command": "curl -sS --fail-with-body -X PUT -H \"Authorization: Bearer $PRAEKO_KEY\" -H \"Content-Type: video/mp4\" -H \"Expect:\" -T \"/path/to/file\" \"https://praeko.app/v1/media/01KB7J3V9Q2W8E5R1T6Y4N0M3P/content\""
},
"next_step": "Run `upload.command` in the shell (replace /path/to/file; the key comes from the environment, do not print it), then call complete_media_upload with this mediaId, then create_post or schedule_post with media.video = mediaId (those keep their two-step preview/confirmation)."
}Por que sem código de confirmação
Criar o espaço de envio não publica nada, não envia nada para uma rede e não altera post. O que protege a conta é o passo seguinte, create_post ou schedule_post, que segue em dois passos. Por isso estas duas ferramentas são de um passo só, com limites no lugar do código:
| Limite | Valor |
|---|---|
| Escopo | write (a lista de quem tem só read não mostra as duas). |
| Tipos | video/mp4, video/quicktime (vídeo), image/png, image/jpeg (capa) e image/jpeg (imagem); o tipo tem de combinar com type. |
| Tamanho | Vídeo até 2 GiB, capa e imagem até 20 MiB. O servidor recusa o envio cujo tamanho difira do declarado (size_mismatch). |
| Envios pendentes | No máximo 20 por workspace (too_many_pending_uploads, com os ids pendentes) e 60 criações por hora (rate_limited). |
| Validade | Um envio que não terminou em 24 horas é descartado (o arquivo parcial é apagado e a mídia some). |
| Isolamento | A mídia é do workspace da credencial; outro workspace recebe media_not_found. |
| A chave | O curl usa uma chave de API com escopo write, lida do ambiente do shell ($PRAEKO_KEY). As ferramentas nunca devolvem a chave, só o marcador $PRAEKO_KEY, e mandam o agente não pedir, não imprimir e não colar a chave na conversa. |
| Com login OAuth | O token OAuth só vale no /mcp, e o curl fala com a REST: mesmo conectado por login, o shell precisa de uma chave write em $PRAEKO_KEY. Sem ela, use upload_media com uma URL pública. |
$PRAEKO_KEY e, sem interação (claude -p), recusa o que não estiver liberado nas permissões. Aprove o curl na sessão ou libere-o nas permissões. O comando devolvido usa só $PRAEKO_KEY, sem a forma ${VAR:-…}, que o Claude Code bloqueia. Isso é regra do cliente, não do Praeko.upload_media com uma URL pública https (até 50 MB). Sem URL, suba pelo CLI (praeko post), pelo SDK (uploadFile) ou pela REST e entregue o id da mídia.Protocolo
O endpoint fala duas eras do MCP no mesmo endereço, como a especificação prevê. Os clientes de hoje, na maioria, ainda usam a 2025-11-25; a 2026-07-28 (a mais nova na data da conferência, 07/11/2026) não tem initialize nem sessão.
| Era moderna `2026-07-28` | Era antiga `2025-11-25`, `2025-06-18`, `2025-03-26` | |
|---|---|---|
| Como é reconhecida | _meta["io.modelcontextprotocol/protocolVersion"] no corpo ou MCP-Protocol-Version: 2026-07-28 |
Qualquer outro pedido. Sem cabeçalho de versão vale 2025-03-26. |
| Abertura | Nenhuma. server/discover é opcional. |
initialize, resposta com a versão pedida (ou 2025-11-25), notifications/initialized (202). |
| Cabeçalhos exigidos | MCP-Protocol-Version igual ao corpo, Mcp-Method igual ao method, Mcp-Name igual a params.name em tools/call. Divergência: 400 e JSON-RPC -32020. |
MCP-Protocol-Version, se vier, tem de ser uma versão conhecida (senão 400). |
| Versão desconhecida | 400 e -32022, com data.supported e data.requested. |
Idem. |
| Método desconhecido | 404 e -32601. |
200 e -32601. |
| Resultado | Traz "resultType": "complete". |
Sem resultType. |
| Lote JSON-RPC | Recusado (400). |
Aceito. |
- A resposta é sempre
application/json(a especificação permite no lugar de SSE). O servidor não emiteMcp-Session-Ide não olha oAccept. Notificação e resposta do cliente:202sem corpo. tools/calldevolvecontent(texto com o JSON) estructuredContent(o mesmo objeto). Não háoutputSchema.- Ferramenta desconhecida é erro de protocolo (
-32602). Argumento inválido, regra de negócio e falha da API são erro de execução (isError: true, comstructuredContent.error = { code, message, status, details? }), para o modelo se corrigir.
curl -s https://praeko.app/mcp \
-H "Authorization: Bearer $PRAEKO_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Segurança
| Cuidado | Como funciona |
|---|---|
| Confirmação no servidor | Nenhuma escrita que publica ou muda conteúdo executa sem o segundo passo. O código é de uso único, vale 10 minutos e está preso à credencial, à ferramenta e aos argumentos. |
| Menor escopo | Uma chave read (ou o acesso Leitura, no login) para o agente que só consulta; write só para quem agenda. O escopo é conferido no /mcp e de novo na rota REST. |
| Consentimento explícito | A tela de consentimento aparece em toda autorização, mesmo para um aplicativo já conhecido, e destaca quando ele pede mais acesso do que tinha. Ela mostra o endereço de retorno e avisa quando o aplicativo é desconhecido ou só tem endereço local. |
| Texto das ferramentas é dado | As instruções do servidor mandam o modelo tratar títulos, comentários e notas devolvidos como dado do usuário, nunca como ordem. |
| Envio de arquivo local | Só um espaço de envio com limites (20 pendentes, 60 por hora, 24 h de validade, 2 GiB); o arquivo vai do shell do agente direto para a API. Publicar continua em dois passos. |
| Credencial fora do código | Chave em variável de ambiente. Chave e token nunca aparecem em resposta, log ou banco (só o hash). Se uma chave vazou, revogue em Chaves de API; uma conexão, em Conexões de agentes. |
| Rastro | Cada chamada MCP vira uma linha em Uso e log, com a rota mcp:tools/call create_post, mcp:tools/list etc. e o status; as chamadas por login aparecem com a chave mcp-oauth. Filtre pela rota mcp: no painel ou com GET /v1/usage/log?route=mcp%3A. |
Clientes conferidos
Testado com o Claude Code real: com chave de API (inicialização, tools/list, list_channels e a prévia de create_idea) e com login OAuth (descoberta, registro pelo documento de metadados do cliente, consentimento, conexão e revogação). Os blocos dos demais agentes em Agentes de IA vêm da documentação de cada fabricante. O login pelo ChatGPT não foi conferido em fonte primária.