Referência do MCP
Tudo o que um app de IA (ou você, escrevendo um) precisa para falar com a Hystore: os endereços, o login OAuth passo a passo, o formato das mensagens e cada ferramenta com exemplo. Para só conectar um app pronto, veja Conectar a IA.
Endereços
| O quê | Endereço |
|---|---|
| Servidor MCP | POST https://mcp.hystore.com.br/mcp |
| Metadados do recurso (RFC 9728) | GET https://mcp.hystore.com.br/.well-known/oauth-protected-resource/mcp |
| Metadados do login (RFC 8414) | GET https://mcp.hystore.com.br/.well-known/oauth-authorization-server |
| Registro do app (RFC 7591) | POST https://mcp.hystore.com.br/v1/oauth/register |
| Tela de autorização | https://app.hystore.com.br/oauth/authorize |
| Token | POST https://mcp.hystore.com.br/v1/oauth/token |
| Sair (revogar) | POST https://mcp.hystore.com.br/v1/oauth/revoke |
Todas as rotas também estão na OpenAPI.
- Transporte: Streamable HTTP sem sessão. Cada
POST /mcpleva uma mensagem JSON-RPC e recebe uma resposta JSON.GETeDELETErespondem405. - Protocolo:
2025-11-25(também2025-06-18e2025-03-26). - Páginas da web (pedidos com cabeçalho
Origin) não chamam/mcpnem o token: chame de um programa, não do navegador.
Como o login funciona
É OAuth 2.1 com PKCE, do jeito que a especificação do MCP pede. Os apps prontos fazem tudo sozinhos; os passos abaixo mostram o que acontece, com comandos que você pode rodar no terminal.
1. Descobrir onde entrar
Sem token, /mcp responde 401 e aponta para os metadados:
curl -i -X POST https://mcp.hystore.com.br/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize"}'
HTTP/2 401
www-authenticate: Bearer resource_metadata="https://mcp.hystore.com.br/.well-known/oauth-protected-resource/mcp", scope="theme:read theme:write theme:publish-request"
Os metadados dizem quem faz o login (authorization_servers), e os do servidor
de login trazem os endereços de autorização, token e registro:
curl https://mcp.hystore.com.br/.well-known/oauth-authorization-server
{
"issuer": "https://mcp.hystore.com.br",
"authorization_endpoint": "https://app.hystore.com.br/oauth/authorize",
"token_endpoint": "https://mcp.hystore.com.br/v1/oauth/token",
"registration_endpoint": "https://mcp.hystore.com.br/v1/oauth/register",
"revocation_endpoint": "https://mcp.hystore.com.br/v1/oauth/revoke",
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none"],
"scopes_supported": ["theme:read", "theme:write", "theme:publish-request"]
}
2. Registrar o app
O app se registra uma vez e recebe um client_id público (sem segredo):
curl -X POST https://mcp.hystore.com.br/v1/oauth/register \
-H "Content-Type: application/json" \
-d '{"client_name":"Meu app","redirect_uris":["http://127.0.0.1:8765/callback"]}'
{
"client_id": "hys_cl_...",
"client_name": "Meu app",
"redirect_uris": ["http://127.0.0.1:8765/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "none"
}
- O endereço de volta precisa ser
https,httpde loopback (127.0.0.1,localhost,[::1]) ou o esquema próprio de um app (meuapp:/callback). - O nome aparece na tela de autorização marcado como informado pelo app.
- Um app registrado e nunca usado some em 30 dias.
3. Gerar o PKCE
VERIFIER=$(openssl rand -base64 48 | tr -d '=+/' | cut -c1-64)
CHALLENGE=$(printf %s "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '=')
4. Mandar a pessoa autorizar
Abra no navegador (uma linha só):
https://app.hystore.com.br/oauth/authorize?response_type=code&client_id=hys_cl_...&redirect_uri=http%3A%2F%2F127.0.0.1%3A8765%2Fcallback&code_challenge=<CHALLENGE>&code_challenge_method=S256&scope=theme%3Aread%20theme%3Awrite%20theme%3Apublish-request&resource=https%3A%2F%2Fmcp.hystore.com.br%2Fmcp&state=<um valor aleatório>
A pessoa entra no painel, escolhe a loja e autoriza. O navegador volta para o
seu endereço com ?code=hys_ac_...&state=.... Confira o state.
5. Trocar o código pelo token
curl -X POST https://mcp.hystore.com.br/v1/oauth/token \
-d grant_type=authorization_code \
-d code=hys_ac_... \
-d client_id=hys_cl_... \
-d redirect_uri=http://127.0.0.1:8765/callback \
-d code_verifier="$VERIFIER" \
-d resource=https://mcp.hystore.com.br/mcp
{
"access_token": "hys_at_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "hys_rt_...",
"scope": "theme:read theme:write theme:publish-request"
}
O token de acesso vale uma hora e só abre /mcp.
6. Renovar e sair
curl -X POST https://mcp.hystore.com.br/v1/oauth/token \
-d grant_type=refresh_token \
-d refresh_token=hys_rt_...
Cada renovação devolve um par novo e invalida o anterior. Usar de novo um
refresh_token já trocado derruba a sessão inteira (alguém copiou o token).
A sessão dura 30 dias desde o último uso.
curl -X POST https://mcp.hystore.com.br/v1/oauth/revoke -d token=hys_rt_...
Mensagens
Cada chamada é um POST /mcp com o token e uma mensagem JSON-RPC:
curl -X POST https://mcp.hystore.com.br/mcp \
-H "Authorization: Bearer hys_at_..." \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
initialize
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": { "name": "meu-app", "version": "1.0.0" } } }
A resposta traz serverInfo (hystore), a capacidade tools e
instructions: um texto curto que explica à IA como trabalhar com o tema
(ler o guia primeiro, editar o rascunho, mostrar a prévia, pedir publicação).
tools/call
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": { "name": "read_theme_file", "arguments": { "path": "sections/hero.liquid" } }
}
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"content": [{ "type": "text", "text": "path: sections/hero.liquid\nsha256: 3f1c...\n\n<section class=\"hero\">..." }],
"structuredContent": { "path": "sections/hero.liquid", "content": "<section class=\"hero\">...", "sha256": "3f1c..." }
}
}
Uma ferramenta que recusa algo responde com "isError": true e o motivo no
texto (e em structuredContent, quando há detalhes). Erros de protocolo
(método desconhecido, ferramenta que não existe) voltam como error do
JSON-RPC.
Ferramentas
Os nomes dos argumentos estão em snake_case. Escopo é a permissão que a
conexão precisa ter.
read_theme_guide
Lê o guia do tema (em inglês). Sem argumentos traz o guia inteiro; com
section (parte do título de uma seção, como Sections, Data ou Apps), só
aquela parte. Escopo: theme:read.
{ "name": "read_theme_guide", "arguments": { "section": "Sections" } }
get_store
Nome e endereço da loja e as permissões desta conexão. Sem argumentos. Escopo:
theme:read.
list_theme_files
Os arquivos do rascunho com sha256, tamanho e a revision do rascunho. Sem
argumentos. Escopo: theme:read.
read_theme_file
Um arquivo do rascunho com o sha256, que você devolve como base_sha ao
gravar. Escopo: theme:read.
{ "name": "read_theme_file", "arguments": { "path": "layout/theme.liquid" } }
write_theme_files
Cria, muda ou apaga (content: null) até 100 arquivos de uma vez: todos ou
nenhum. Escopo: theme:write.
{
"name": "write_theme_files",
"arguments": {
"changes": [
{ "path": "sections/black-friday.liquid", "content": "<section class=\"bf\">...</section>\n{% schema %}{\"name\":\"Black Friday\",\"settings\":[]}{% endschema %}", "base_sha": null },
{ "path": "templates/home.json", "content": "{\"sections\":{\"bf\":{\"type\":\"black-friday\"}},\"order\":[\"bf\"]}", "base_sha": "9a0b..." },
{ "path": "sections/old-banner.liquid", "content": null }
],
"expected_revision": 12
}
}
base_sha: osha256que você leu. Se o arquivo mudou desde então, nada é gravado e a resposta trazconflict.nullquer dizer "o arquivo ainda não pode existir".expected_revision(opcional): arevisiondo rascunho que você leu; se outra gravação passou na frente, nada é gravado.- Um arquivo com erro volta em
problems, compath,line,codeemessage. Nada é gravado.
Sucesso:
{ "revision": 13, "files": [{ "path": "sections/black-friday.liquid", "sha256": "..." }], "warnings": [] }
Recusa:
{ "refused": true, "problems": [{ "path": "sections/black-friday.liquid", "line": 3, "code": "html_event_handler", "message": "Event attributes (onclick, onload...) are not allowed. Use data-hystore-* components.", "severity": "error" }] }
check_theme
Confere o rascunho inteiro (a mesma conferência de antes de publicar): erros e
avisos com arquivo e linha. Sem argumentos. Escopo: theme:read.
get_preview_link
Um link de 24 horas que abre o rascunho no endereço real da loja e recarrega
sozinho a cada mudança. Entregue para a pessoa olhar. Sem argumentos. Escopo:
theme:read.
request_publish
Pede a publicação do rascunho atual; alguém com permissão aprova em
Personalização > Tema em código. Escopo: theme:publish-request.
{ "name": "request_publish", "arguments": { "note": "Banner da Black Friday no topo da home" } }
list_versions, read_version_file, restore_version
As últimas 30 versões publicadas; um arquivo como estava numa versão
(content: null quando ela não tinha o arquivo); e trazer uma versão de volta
para o rascunho (a loja no ar não muda até uma publicação ser aprovada).
Escopos: theme:read para ler, theme:write para restaurar.
{ "name": "read_version_file", "arguments": { "version": 3, "path": "styles/theme.css" } }
{ "name": "restore_version", "arguments": { "version": 3 } }
list_assets, upload_asset, delete_asset
Imagens e fontes do rascunho, com o endereço público. Use no tema como
{{ 'nome.webp' | asset_url }} ou url(assets/nome.webp). Escopos:
theme:read para listar, theme:write para enviar e apagar.
{ "name": "upload_asset", "arguments": { "path": "assets/banner.webp", "base64": "UklGRl..." } }
PNG, JPEG, WebP, GIF e AVIF até 3 MB; WOFF e WOFF2 até 1 MB. O tipo é lido do conteúdo, não do nome. SVG não entra como arquivo de mídia.
read_catalog
O que um visitante vê da loja: categorias e produtos ativos (id, nome, slug,
preço em centavos, imagem). Use os ids em data-hystore-add-to-cart e os slugs
em {% load products, category: '...' %}. Sem argumentos. Escopo:
theme:read.
Limites
- 120 leituras e 30 gravações por minuto por conexão; acima disso,
429comRetry-After(em segundos). - Arquivos: 256 KB cada, 400 arquivos, 5 MB de texto. Mídia: 100 arquivos, 40 MB.
- Uma gravação leva até 100 mudanças.
Segurança
- O token do app só abre
/mcp. As chaves e as sessões da ferramenta hystore não abrem/mcp, e o token do app não abre a API do tema. - Nenhuma ferramenta mexe em pedidos, pagamentos, jogadores, cupons ou configurações, e nenhuma publica sozinha.
- O texto que vem da loja (nomes de produto, descrições) é tratado como dado, nunca como instrução para a IA.
