Pular para a documentação
DocumentaçãoProxy HTTP

Proxy HTTP

Envie qualquer requisição HTTP a um serviço conectado pelo Sallyport. O gateway coloca a chave, então seu script nunca a tem.

Nesta página

O endereço

Envie a requisição para https://mcp.sallyport.cloud/p/{binding_id}/{path} com um token de agente em Authorization: Bearer. O Sallyport coloca a chave da conexão, chama o serviço e devolve a resposta em streaming.

O serviço recebe https://{host}{path_prefix}, depois o caminho que você escreveu após o ID e depois sua query string. Uma conexão do catálogo tem o prefixo /, então escreva o caminho completo da documentação da API do fornecedor.

Uma conexão que cobre todos os hosts de um serviço, como a AWS em *.amazonaws.com, recebe o host como primeiro segmento: /p/{binding_id}/sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15.

O que você precisa

  • Um token de agente. Abra o agente em Agentes e clique em Novo token na seção Tokens. O valor aparece uma única vez.
  • O ID de uma conexão HTTP. Abra Conexões e clique na conexão: o ID é a última parte do endereço da página.
  • Um agente e uma conexão do mesmo espaço de trabalho e ambiente. O Sallyport dá cada chave nova a todos os agentes do espaço de trabalho, então isso já vale, a menos que alguém tenha removido.

Leia sua conta do GitHub

Shell · GET por uma conexão do GitHub
export SP_AGENT_TOKEN='sp_live_…'
export SP_BINDING_ID='…'

curl --fail-with-body \
  --header "Authorization: Bearer $SP_AGENT_TOKEN" \
  "https://mcp.sallyport.cloud/p/$SP_BINDING_ID/user"

Voltam o status, os cabeçalhos e o JSON do próprio GitHub, com X-Sallyport-Call-Id, que nomeia a chamada. A resposta chega em partes sem Content-Length, e chegam todos os cabeçalhos, inclusive o Set-Cookie.

Envie uma escrita

Shell · POST com uma intenção
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer $SP_AGENT_TOKEN" \
  --header "Content-Type: application/json" \
  --header "X-Sallyport-Intent: File the bug the user reported" \
  --data '{"title":"Checkout fails on Safari"}' \
  "https://mcp.sallyport.cloud/p/$SP_BINDING_ID/repos/acme/web/issues"
CabeçalhoO que faz
X-Sallyport-IntentUma frase de até 500 caracteres sobre por que a chamada é feita. Uma pessoa a lê no cartão de aprovação. Uma chamada que espera uma pessoa sem ela recebe 400 com reason: "missing_intent".
X-Sallyport-Idempotency-KeySua própria chave para uma escrita. Uma repetição com a mesma chave e a mesma requisição recebe a primeira resposta por 24 horas em vez de rodar de novo.
X-Sallyport-Call-IdContinua uma chamada cujo ID você já tem, seja qual for o resto da requisição. Nenhuma segunda chamada é criada.

O Sallyport nunca repassa esses três cabeçalhos. Ele também remove Authorization, Cookie, Host, Content-Length, Expect, Forwarded, Via e todo cabeçalho X-Forwarded-*.

Um cabeçalho no lugar da chave, como o PRIVATE-TOKEN do GitLab, é removido. O nome dele vai para o registro, e a chamada em Atividade mostra o valor, marcado como não enviado. Trocas de método como _method e X-HTTP-Method-Override são removidas, então roda o método que você enviou.

Repetições

Uma requisição GET, HEAD ou OPTIONS sempre roda. Uma escrita sem sua própria chave recebe uma calculada a partir da requisição, então duas escritas idênticas são uma única chamada.

Enviada de novo enquanto a primeira espera uma pessoa, ela entra no mesmo cartão. Enquanto a primeira roda, recebe essa resposta ou 409 IN_FLIGHT. Por 2 minutos após o fim, recebe a primeira resposta.

Uma resposta devolvida traz X-Sallyport-Deduplicated: true e o X-Sallyport-Call-Id da primeira chamada. Se o serviço recusou a escrita com um 4xx, a chave é liberada e a repetição corrigida roda. A mesma chave com outra requisição recebe 409 IDEMPOTENCY_CONFLICT.

Quando uma pessoa precisa aprovar

Uma chamada espera quando a regra do agente para aquela conexão diz perguntar, por exemplo depois de ligar Perguntar sempre. A resposta chega na hora, com 202, Retry-After e este corpo:

JSON · 202 enquanto a chamada espera
{
  "code": "APPROVAL_PENDING",
  "instruction": "Show the user approve_url. After poll_after_ms repeat this same call with call_id — you get the result or \"still waiting\" again. This continues the same wait; it is not an idempotent cache. Do not create a new call.",
  "call_id": "sp_c_4pQx…",
  "approve_url": "https://my.sallyport.cloud/approve/sp_c_4pQx…",
  "class": "write",
  "summary": "POST api.github.com/repos/acme/web/issues",
  "expires_at": "2026-09-29T19:05:00Z",
  "poll_after_ms": 5000,
  "expected_wait_ms": 42000,
  "poll_url": "https://mcp.sallyport.cloud/p/calls/sp_c_4pQx…"
}

Mostre approve_url à pessoa. Depois consulte poll_url com o mesmo token após Retry-After segundos. Qualquer método serve, e nada roda duas vezes.

Resposta da consultaSignificado
202 com APPROVAL_PENDINGNinguém respondeu ainda. Pergunte de novo após Retry-After.
A resposta do próprio serviçoA pessoa aprovou. A chamada roda agora e a resposta chega nesta consulta.
202 com stateA chamada ainda está com o serviço. Pergunte de novo em um segundo.
403 APPROVAL_DENIEDA pessoa recusou. reason e decided_by dizem por que e quem. Nada foi enviado.
410 APPROVAL_EXPIREDNinguém respondeu a tempo. Envie a requisição de novo sem X-Sallyport-Call-Id para um cartão novo.
404 CALL_NOT_FOUNDO call_id pertence a outro agente ou não existe. Faça uma chamada nova.
200 com state e bytesA chamada terminou e a resposta já saiu. status é o status do serviço, e body_ref aparece quando a resposta foi guardada.
error_code com state e bytesA chamada terminou com uma falha, como UPSTREAM_TOO_LARGE. A linha de status é a desse código, e instruction diz o que fazer.

Uma resposta com mais de 1 MiB também é guardada, por 30 dias por padrão. Uma consulta da chamada terminada traz o body_ref dela, e a chamada em Atividade oferece Baixar a resposta completa. Baixe com GET https://mcp.sallyport.cloud/v1/bodies/{body_ref} e o mesmo token de agente; Range funciona.

WebSocket

Abra wss://mcp.sallyport.cloud/p/{binding_id}/{path} com o mesmo token de agente em Authorization: Bearer. O Sallyport põe a chave da conexão onde o serviço a lê, no handshake para a maioria dos serviços, abre wss://{host}{path} no serviço e passa cada frame nos dois sentidos como foi enviado: texto, binário, ping, pong e o código de fechamento. O subprotocolo que você pede em Sec-WebSocket-Protocol chega ao serviço, e a escolha dele volta.

JavaScript · transcrição em tempo real da ElevenLabs, com o pacote ws
import WebSocket from 'ws'

const socket = new WebSocket(
  `wss://mcp.sallyport.cloud/p/${process.env.SP_BINDING_ID}/v1/speech-to-text/realtime` +
    '?model_id=scribe_v2_realtime&audio_format=pcm_16000&commit_strategy=vad',
  { headers: { Authorization: `Bearer ${process.env.SP_AGENT_TOKEN}` } },
)

// 16 kHz 16-bit mono PCM, every 20–100 ms
function sendAudio(pcm) {
  socket.send(JSON.stringify({ message_type: 'input_audio_chunk', audio_base_64: pcm.toString('base64') }))
}

socket.on('message', (data) => {
  const event = JSON.parse(data.toString())
  if (event.message_type === 'committed_transcript') console.log(event.text)
})

// 202: the session waits for a person. 4xx: the refusal, as JSON.
socket.on('unexpected-response', (_request, response) => console.error(response.statusCode))

A sessão é uma chamada, decidida como qualquer outra pela regra do agente para a conexão. A transcrição em tempo real da ElevenLabs é uma escrita paga e roda direto, a não ser que Perguntar sempre esteja ligado. A chamada em Atividade mostra quando a sessão abriu e fechou, quem a fechou e com qual código, o que cada lado enviou e cada mensagem nos dois sentidos como foi enviada. As mensagens ficam guardadas 30 dias, como uma resposta longa.

LimiteValorAlém do limite
Duração da sessão60 minutosOs dois lados recebem o fechamento 1001 com o motivo.
Uma mensagem, em qualquer sentido1 MiBOs dois lados recebem o fechamento 1009. A mensagem não é repassada.
Silêncio nos dois sentidos10 minutos sem frameOs dois lados recebem o fechamento 1001 com o motivo.

Quando a sessão precisa esperar uma pessoa, o handshake recebe 202 e o corpo APPROVAL_PENDING acima em vez de 101. Quando a pessoa aprovar, abra o WebSocket de novo com X-Sallyport-Call-Id: {call_id} e a sessão aprovada começa. Uma consulta a poll_url responde então 426 WEBSOCKET_REQUIRED, porque uma sessão só roda sobre um WebSocket. Um serviço que recusa o handshake, por uma chave ou um parâmetro errado, envia o próprio status e corpo.

Serviços que funcionam por WebSocket

Cada um abre sua API WebSocket pelo /p/, e o Sallyport põe a chave onde o fornecedor a lê. Você escreve o endereço e as mensagens como o fornecedor documenta. list_access com service diz o mesmo para cada conexão.

ServiçosOnde o Sallyport põe a chave
OpenAI Realtime, Azure OpenAI Realtime, Gemini Live, xAI, Mistral, Deepgram, AssemblyAI, Speechmatics, Cartesia, MiniMax, Together AI, fal, Murf, Telnyx, ElevenLabs, Retell, GladiaNo handshake, como em toda chamada
Rev.ai, HumeNa query: access_token, api_key
Dados de mercado da Alpaca, CoinGecko, CoinMarketCap, AlchemyNo handshake
Finnhub, Twelve DataNa query: token, apikey
Massive, Financial Modeling Prep, atualizações de ordens da AlpacaNa primeira mensagem que você envia
Binance, Bybit, OKX, Kraken, Coinbase, Coinbase ExchangeEm lugar nenhum: os fluxos públicos não precisam dela, e o fluxo privado da Kraken recebe o token que a chamada GetWebSocketsToken responde
Gateway do DiscordNa primeira mensagem que você envia: o d.token do IDENTIFY
Slack Socket Mode, Twitch EventSub, RingCentral, Vapi, console da HetznerEm lugar nenhum: abra a URL com que o serviço respondeu, com o ticket
Railway, Render, Koyeb, Daytona, Mattermost, ntfy, Grafana CloudNo handshake
Browserless, Steel, BrowserbaseNa query: token, apiKey

Alguns serviços rodam seu WebSocket num host próprio: gateway.discord.gg, agent.deepgram.com, ws.finnhub.io, o wss-….slack.com de um ticket do Socket Mode. Esse host abre pela conexão desse serviço, como primeiro segmento do caminho: wss://mcp.sallyport.cloud/p/{binding_id}/gateway.discord.gg/?v=10&encoding=json. Só um WebSocket chega a ele.

Quando a chave vai na primeira mensagem, envie a mensagem que o fornecedor documenta com "{{credential}}" no lugar da chave, ou sem esse campo. O Sallyport escreve a chave antes que a mensagem chegue ao serviço, e a chamada em Atividade mostra sua mensagem como você a enviou.

JavaScript · OpenAI Realtime, uma resposta em texto
import WebSocket from 'ws'

const socket = new WebSocket(
  `wss://mcp.sallyport.cloud/p/${process.env.SP_BINDING_ID}/v1/realtime?model=gpt-realtime`,
  { headers: { Authorization: `Bearer ${process.env.SP_AGENT_TOKEN}` } },
)

socket.on('open', () => {
  socket.send(JSON.stringify({ type: 'session.update', session: { type: 'realtime', output_modalities: ['text'] } }))
  socket.send(JSON.stringify({
    type: 'conversation.item.create',
    item: { type: 'message', role: 'user', content: [{ type: 'input_text', text: 'Say hello in one sentence.' }] },
  }))
  socket.send(JSON.stringify({ type: 'response.create' }))
})

socket.on('message', (data) => {
  const event = JSON.parse(data.toString())
  if (event.type === 'response.output_text.delta') process.stdout.write(event.delta)
  if (event.type === 'response.done') socket.close()
})
JavaScript · transcrição ao vivo com Deepgram
import WebSocket from 'ws'

const socket = new WebSocket(
  `wss://mcp.sallyport.cloud/p/${process.env.SP_BINDING_ID}/v1/listen` +
    '?model=nova-3&encoding=linear16&sample_rate=16000&interim_results=true',
  { headers: { Authorization: `Bearer ${process.env.SP_AGENT_TOKEN}` } },
)

// 16 kHz 16-bit mono PCM, as it arrives
function sendAudio(pcm) {
  socket.send(pcm)
}

socket.on('message', (data) => {
  const result = JSON.parse(data.toString())
  if (result.type === 'Results' && result.is_final) console.log(result.channel.alternatives[0].transcript)
})

// When the audio ends, Deepgram sends the last results and closes.
function finish() {
  socket.send(JSON.stringify({ type: 'CloseStream' }))
}

Recusas

Uma recusa é JSON: {"error": {"code": …, "instruction": …}}, com os campos que o código precisa ao lado. instruction é uma frase em inglês sobre o que fazer em seguida.

Quatro códigos são resultados, não erros, e chegam como um objeto simples sem o invólucro error: APPROVAL_PENDING, ACCESS_REQUEST_CREATED, ALREADY_EXECUTED e UPSTREAM_ERROR. Em /p/ um corpo UPSTREAM_ERROR é a resposta 424 quando o serviço nunca respondeu. Um serviço que responde com erro envia a você a própria resposta.

Status e códigoO que aconteceu
401 AUTH_FAILEDO token está ausente, errado, revogado ou vale só para /mcp. WWW-Authenticate diz a um cliente OAuth onde obter o certo.
403 SOURCE_IP_NOT_ALLOWEDA requisição veio de um endereço fora da lista permitida do agente.
423 SUSPENDEDO agente, o espaço de trabalho dele ou a organização estão pausados.
404 ACCESS_REQUEST_CREATEDO agente não pode usar esta conexão: sem acesso, conexão pausada ou ID desconhecido. Mostre approve_url a uma pessoa e repita a chamada.
403 POLICY_DENYA regra do agente para esta conexão recusa essa classe de chamada.
404 CALL_NOT_FOUNDO X-Sallyport-Call-Id ou a chamada consultada pertence a outro agente ou não existe. Faça uma chamada nova.
400 BAD_REQUESTreason diz o que corrigir: missing_intent, intent_too_long, body_too_large com max_bytes, missing_host, query_in_path, mcp_binding, database_binding, email_binding, email_operation, sftp_operation, websocket_handshake e outros.
400 PATH_OUT_OF_PREFIXO caminho sai do limite de caminho da conexão, por exemplo com ...
Códigos 409IDEMPOTENCY_CONFLICT, IN_FLIGHT, CALL_CONFLICT ou APPROVAL_SUPERSEDED. A instruction diz o que fazer.
426 UPGRADE_NOT_SUPPORTEDUm upgrade para qualquer protocolo que não seja WebSocket.
426 WEBSOCKET_REQUIREDUma consulta de uma sessão WebSocket aprovada. Abra o WebSocket de novo com X-Sallyport-Call-Id.
429 RATE_LIMITEDUm limite de frequência do agente ou da conexão. Espere Retry-After e repita.
Códigos 424EGRESS_DENIED, SERVICE_NOT_CONNECTED, UPSTREAM_TOO_LARGE, OUTCOME_UNKNOWN ou UPSTREAM_ERROR. Leia a instruction antes de repetir: OUTCOME_UNKNOWN significa que o serviço pode ter agido.
503 UNAVAILABLE, 500 INTERNALUma falha do lado do Sallyport. Repita um 503 após Retry-After.

Descreva a requisição no corpo

Um cliente que não consegue definir o método ou o caminho pode fazer POST de uma descrição com Content-Type: application/vnd.sallyport.request+json. Todos os campos são opcionais e cada um substitui aquela parte da requisição.

Shell · um DELETE descrito em um POST
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer $SP_AGENT_TOKEN" \
  --header "Content-Type: application/vnd.sallyport.request+json" \
  --data '{"method":"DELETE","path":"/repos/acme/web/labels/stale","intent":"Remove the unused label"}' \
  "https://mcp.sallyport.cloud/p/$SP_BINDING_ID/"

Os campos são method, path, query, headers, body, intent e idempotency_key. path não aceita ? nem #: os parâmetros vão em query. Um body JSON é enviado como JSON, e uma string como texto puro.

Uma resposta pode ter até 512 MiB. A página da conexão mostra o limite dela em Limites, como Maior resposta do serviço. A resposta é transmitida conforme chega, então uma que passa do limite é cortada. O status e os cabeçalhos já foram enviados, e o corpo fica incompleto. Uma consulta da chamada indicada em X-Sallyport-Call-Id mostra então UPSTREAM_TOO_LARGE.