qURL
qURL é o portal para a internet invisível — URLs com expiração e escopo limitado que permitem que agentes de IA acessem serviços que ninguém mais pode ver.
Documentação
@layervai/qurl-mcp
⚠️ Renomeado de
@layerv/qurl-mcpna v0.4.0. O pacote antigo está obsoleto e não receberá mais atualizações. Se você está usando@layerv/qurl-mcp@0.3.x, troque o escopo na configuração do seu cliente MCP — mesmo binário, mesma chave de API, nenhuma outra alteração.
Um servidor MCP qURL que suporta tanto o modo local
stdioquanto o modo remotoHTTPpara criar, gerenciar, resolver e compartilhar links de acesso seguros.
Visão geral
qURL MCP expõe os recursos do qURL para clientes MCP, GPTs, ChatGPT e outras integrações remotas.
Atualmente suporta:
- criar, ler, atualizar e excluir qURLs
- resolver tokens de acesso
- gerenciar tokens e sessões qURL
- enviar conteúdo de texto ou arquivo e gerar qURLs
- opcionalmente servir páginas legais públicas da LayerV
- servir uma página de reprodução de vídeo MP4 configurável
Início rápido: sem necessidade de conta
Compartilhe um aplicativo em execução na sua máquina, ou uma URL, de forma privada pelo Claude Desktop, Claude Code, Cursor, Codex ou qualquer cliente MCP local. Você não precisa de conta nem chave de API: sem QURL_API_KEY definido, o servidor aciona o CLI qURL instalado na sua máquina, e a primeira publicação cria a identidade desta máquina por conta própria.
1. Instale o CLI qURL (macOS e Linux; Node.js 22.12 ou posterior é necessário para o servidor MCP):
(umask 022 && mkdir -p "$HOME/.local/bin" "$HOME/.local/state") && chmod go-w "$HOME/.local" "$HOME/.local/bin" "$HOME/.local/state" && curl -fsSL --proto '=https' https://raw.githubusercontent.com/layervai/qurl-integrations/main/scripts/install.sh | INSTALL_DIR="$HOME/.local/bin" sh
No Windows, baixe o qurl.exe do último lançamento e coloque sua pasta no PATH. O servidor encontra o qurl no PATH, e também em ~/.local/bin, /opt/homebrew/bin e /usr/local/bin, que muitos clientes MCP deixam de fora do PATH que fornecem aos servidores. Se o CLI estiver ausente, todas as ferramentas respondem com este comando de instalação em vez de falhar.
2. Adicione o servidor ao seu cliente MCP, sem bloco env.
Claude Desktop (claude_desktop_config.json) e Cursor (~/.cursor/mcp.json):
{
"mcpServers": {
"qurl": {
"command": "npx",
"args": ["-y", "@layervai/qurl-mcp"]
}
}
}
Codex (~/.codex/config.toml):
[mcp_servers.qurl]
command = "npx"
args = ["-y", "@layervai/qurl-mcp"]
Claude Code:
claude mcp add qurl -- npx -y @layervai/qurl-mcp
3. Peça o que você quer, por exemplo "compartilhe o aplicativo em localhost:3000 com meu cliente". O agente publica o aplicativo (ele permanece na sua máquina; o serviço em segundo plano do CLI mantém o compartilhamento ativo) e fornece um link de curta duração para uma pessoa, ou ativa solicitações de acesso para que as pessoas possam pedir acesso.
Ferramentas sem conta
| Ferramenta | O que faz | Comando CLI |
|---|---|---|
publish_crid | Publicar um aplicativo local ou URL de forma privada e retornar seu CRID | qurl publish <url> [--allow-requests] [--access <mode>] |
share_by_crid | Criar um link de acesso de curta duração para um CRID publicado por esta máquina | qurl share <CRID> [--ttl] [--session-duration] |
list_crids | Listar os recursos publicados desta máquina (somente leitura) | qurl list --status active |
manage_crid_requests | Listar, aprovar por código ou negar solicitações de acesso; listar ou remover pessoas aprovadas; ativar ou desativar solicitações | qurl requests, qurl approve, qurl deny, qurl grants |
manage_crid_access | Política de acesso privado, convites e concessões de consumidores | qurl access <action> <CRID> |
set_crid_sharing | Parar, iniciar ou reiniciar o compartilhamento de um aplicativo local | qurl stop, qurl start, qurl restart |
delete_crid | Excluir um recurso permanentemente | qurl delete <CRID> --yes |
As ferramentas seguem as mesmas regras da habilidade de agente qURL (https://layerv.ai/skills/qurl/SKILL.md):
- Tudo é publicado de forma privada. Estas ferramentas não têm opção de publicação pública.
- Uma solicitação de acesso pendente é aprovada somente com o código de seis dígitos que a pessoa fornece. Nenhuma listagem mostra um código, e nenhuma ferramenta aprova por nome: qualquer pessoa pode digitar qualquer nome, e os nomes retornam como dados não verificados.
- Um link de acesso é um segredo portador para um destinatário. Um CRID não concede nada por si só e é seguro compartilhar.
O servidor executa o CLI com uma matriz de argumentos (nunca um shell), com saída JSON, stdin fechado, timeout e saída limitada, e não registra o que o CLI imprime. As ferramentas que precisam de chave de conta (as ferramentas de link, token, sessão e upload do qURL, e os recursos e prompts qurl://) não são listadas neste modo.
Com chave de conta
Defina QURL_API_KEY para uma chave de API LayerV e o servidor usa a API qURL diretamente, com o conjunto completo de ferramentas descrito no mapa de recursos:
{
"mcpServers": {
"qurl": {
"command": "npx",
"args": ["-y", "@layervai/qurl-mcp"],
"env": { "QURL_API_KEY": "lv_live_xxx" }
}
}
}
Servidor HTTP remoto
O transporte HTTP (qurl-mcp-http) atende runtimes de agentes remotos. Ele é baseado em conta: cada solicitação carrega a chave de API qURL do chamador como token portador, e nunca executa um CLI local. Consulte Autenticação HTTP e o guia MCP da LayerV.
Acesso privado do consumidor
Node.js 22.12 ou posterior é necessário pelo SDK qURL compartilhado.
publish_crid cria um recurso de URL privado com acesso por chaves, somente convite, captura de e-mail, revisão ou contrato. manage_crid_access altera a política com sua versão atual, envia convites, lista concessões, aprova ou nega solicitações e remove acesso. As ferramentas retornam recibos com escopo de recurso e omitem segredos de convite e links portadores. Aplicativos locais usam o CLI qURL na máquina do publicador.
Por exemplo, um assistente pode transformar "compartilhe isso com ana@example.com e não permita solicitações" em um recurso somente convite e um convite autorizado. Para captura de e-mail, o consumidor deve saber que o publicador recebe seu e-mail verificado. Um e-mail verificado prova controle da caixa de entrada, não identidade legal. A aprovação de contrato requer um recibo de direito de backend confiável do publicador; estas ferramentas não verificam pagamento. Não relate a remoção de conexão como concluída enquanto session_cleanup estiver pendente.
Este recurso requer o serviço correspondente e leitores de admissão. Mantenha-o desativado até que os fluxos do publicador e do consumidor passem. O pacote fixa o SDK 3.0.0 publicado e suporta instalação limpa com scripts de dependência bloqueados. Esta versão requer Node.js 22.12 ou posterior; Node.js 20 não é mais suportado.
Modos de execução
| Modo | Propósito | Comando de início | Caso de uso típico |
|---|---|---|---|
stdio | Servidor MCP subprocesso local | npm run start | Claude Desktop, Cursor, Codex e outros clientes MCP locais |
http | Servidor MCP remoto autenticado | npm run start:http | Runtimes de agentes remotos atrás de HTTPS |
Mapa de recursos
Ferramentas de gerenciamento qURL
| Ferramenta | Descrição |
|---|---|
create_qurl | Criar um novo qURL |
resolve_qurl | Resolver um token de acesso em uma URL de destino protegida |
list_qurls | Listar recursos qURL |
get_qurl | Buscar detalhes de um único qURL |
delete_qurl | Excluir um qURL |
extend_qurl | Estender a expiração de um link (precisa de qurl:read também) |
update_qurl | Atualizar metadados ou expiração do qURL |
mint_link | Criar um novo link de acesso para um recurso existente |
share_by_crid | Criar um link de acesso temporário a partir de um CRID de recurso |
batch_create_qurls | Criar vários qURLs em uma única solicitação |
revoke_qurl_token | Revogar um token específico |
update_qurl_token | Atualizar um token específico |
list_qurl_sessions | Listar sessões de acesso ativas |
terminate_qurl_sessions | Encerrar uma ou todas as sessões ativas |
share_by_crid reconhece um valor $<CRID> isolado como uma solicitação explícita para criar um link temporário para esse CRID. O $ é um marcador voltado ao usuário e é removido antes que o CRID seja enviado à API qURL. Um CRID simples permanece suportado. Esta ferramenta requer qurl:resolve. Seu ttl opcional no formato de duração Go deve resolver para um número inteiro positivo de segundos. O serviço limita o tempo de vida solicitado. O compartilhamento faz uma solicitação com timeout de 30 segundos e não tenta novamente automaticamente. Repetir a chamada da ferramenta pode criar outro link.
Os filtros de status de recurso aceitam active, revoked ou active,revoked. Um recurso permanece ativo ou revogado mesmo quando seu expires_at está no passado; tokens de acesso individuais podem expirar. expired não é aceito por list_qurls, embora a análise da resposta tolere valores legados. O instantâneo mantém o texto de expiração desatualizado do upstream; seu enum de status ativo/revogado é autoritativo.
O SDK qURL reutiliza o mesmo Idempotency-Key e corpo de solicitação ao tentar novamente solicitações POST/PATCH após falha de rede ou HTTP 429. Respostas HTTP 5xx mutáveis não são tentadas novamente automaticamente. Cada nova invocação de ferramenta MCP é uma nova operação com uma nova chave, então repetir create_qurl, batch_create_qurls ou mint_link pode criar tokens adicionais. O MCP não expõe uma chave fornecida pelo chamador.
Ferramentas de upload
| Ferramenta | Modo | Descrição |
|---|---|---|
upload_file_qurl | stdio | Enviar um arquivo local e criar um qURL |
upload_file_data_qurl | stdio/HTTP | Enviar conteúdo de arquivo em base64 e criar um qURL |
upload_text_qurl | stdio/HTTP | Enviar conteúdo de texto e criar um qURL |
upload_file_qurl é intencionalmente apenas stdio. Ele pode ler qualquer | ||
| PDF/imagem suportado que o usuário do processo MCP local possa acessar, portanto, | ||
| agentes devem invocá-lo apenas para um caminho que o usuário selecionou | ||
| explicitamente para compartilhamento. Não o exponha a prompts não confiáveis | ||
| ou agentes autônomos: injeção de prompt poderia, de outra forma, selecionar | ||
| outro PDF/imagem legível no host. Execute stdio sob uma conta de sistema | ||
| operacional cujo acesso ao sistema de arquivos seja limitado ao conteúdo | ||
| compartilhável pretendido. O modo HTTP nunca registra esta ferramenta de | ||
| arquivo do host. | ||
| As ferramentas de byte/texto também estão disponíveis em stdio para que | ||
| clientes locais possam compartilhar anexos no chat sem primeiro materializá-los | ||
| em um caminho conhecido do host. | ||
| O upload do conector e a criação de links são operações separadas. O link vem | ||
do POST /api/mint_link/:resource_id do conector (no modo túnel, uma | ||
visualização com marca d'água por destinatário), não do qURL mint_link, portanto, | ||
arquivos enviados não podem ser re-linkados com mint_link; execute a ferramenta | ||
de upload novamente. Links de upload suportam expires_in (1m-30d, convertido | ||
| para uma expiração absoluta no relógio do host MCP, portanto, um relógio do host | ||
| que esteja desregulado encurta ou alonga o link; um link cuja expiração retornada | ||
já passou pelo relógio deste host é retornado com expires_at_already_past), one_time_use e | ||
session_duration (segundos inteiros, até 24h); access_policy e | ||
max_sessions devem ser omitidos ou nulos; valores não nulos são rejeitados | ||
| antes do upload. Se o conector criar mais links do que o solicitado, o resultado | ||
relata quantos extras em unexpected_extra_link_count e quaisquer IDs que ele retornou em | ||
unexpected_extra_qurl_ids (no máximo 10); eles podem estar ativos mesmo quando o relógio | ||
| deste host sugere expiração, então informe o usuário. Se a criação falhar após o | ||
| upload, o conector atualmente não tem endpoint de exclusão; o servidor registra o | ||
resource_id órfão para limpeza pelo operador e retorna a falha de criação. | ||
Links de upload ainda não podem ser revogados deste servidor: delete_qurl no | ||
resource_id do upload não interrompe um link criado pelo conector (a revogação | ||
precisa do /api/revoke_links do conector), portanto, prefira expirações curtas para | ||
| arquivos sensíveis. | ||
Entradas de duração aceitam dias ou semanas inteiros (7d, 1w), | ||
ou sequências de unidades Go sem sinal (1h30m, 1.5h). Sequências | ||
mistas de dias/semanas (1d12h), sinais iniciais, frações com ponto | ||
inicial e mu grego (μs) são rejeitados; o sinal de micro (µs) | ||
| é aceito. | ||
| Tentativas de upload HTTP permanecem limitadas pelos limites de taxa MCP por IP | ||
| e por credencial; operadores de stdio devem restringir separadamente loops de | ||
| repetição autônomos. | ||
| A validação de upload vincula o tipo de mídia declarado ao nome do arquivo mais | ||
| marcadores de início/fim de formato; não é um scanner de malware nem um | ||
| decodificador completo de PDF/imagem. | ||
Para resistência a poliglotas, o marcador final %%EOF de um PDF deve ser | ||
| seguido apenas por espaços em branco ASCII; a saída do produtor com outros bytes | ||
| finais é rejeitada mesmo que um leitor de PDF permissivo a aceitasse. A validação | ||
| JPEG verifica enquadramento e marcadores terminais em vez de decodificar | ||
| segmentos de imagem. O conector autenticado deve decodificar ou validar | ||
| completamente o conteúdo de forma independente antes do armazenamento quando a | ||
| validade semântica da mídia for importante. Ele também deve preservar o tipo de | ||
| mídia seguro declarado e servir downloads com `X-Content-Type-Options: | ||
| nosniff` em vez de inferir um tipo executável. | ||
| Não há intencionalmente uma lista de permissões de caminho em nível de | ||
| aplicação: symlinks e corridas de tempo-de-verificação/tempo-de-uso tornam uma | ||
| verificação de prefixo lexical um limite de segurança enganoso. Use uma conta de | ||
| SO dedicada, contêiner ou montagem somente leitura cujos arquivos legíveis já | ||
| estejam limitados ao diretório de compartilhamento pretendido. | ||
O componente final do caminho é aberto com O_NOFOLLOW; symlinks de diretórios | ||
| intermediários mantêm o comportamento normal do sistema de arquivos sob este | ||
| limite de usuário local confiável. |
Recursos MCP
| URI | Descrição |
|---|---|
qurl://links | Lista qURL atual |
qurl://usage | Informações atuais de cota e uso |
Prompts MCP
| Prompt | Descrição |
|---|---|
secure_a_service | Prompt de integração segura de serviço |
audit_links | Prompt de auditoria de links |
rotate_access | Prompt de rotação de acesso |
Início Rápido
1. Instalar Dependências
npm install
Para uma instalação local apenas stdio a partir da fonte, use npm install --omit=optional; isso
omite o AWS SDK. Implantações HTTP que usam a cota de credenciais DynamoDB devem usar
a instalação comum para que o SDK opcional seja empacotado.
2. Compilar
npm run build
3. Iniciar
Modo stdio local:
npm run start
Modo HTTP remoto:
npm run start:http
Exemplo de Cliente MCP
Se você quiser usar este servidor no modo stdio com um cliente MCP local, use
a configuração de Início rápido. Deixe de fora
env para trabalhar sem uma conta por meio do CLI qURL local, ou adicione
"env": { "QURL_API_KEY": "lv_live_xxx" } para usar a API qURL com uma
chave de conta.
Arquivos de Configuração
Copie os exemplos rastreados para criar arquivos de configuração locais:
cp qurl-mcp.config.example.json qurl-mcp.config.json
cp qurl-mcp.http.example.json qurl-mcp.http.json
Os arquivos locais são ignorados pelo git, portanto, credenciais e caminhos específicos da máquina não são commitados.
Suas responsabilidades são:
| Arquivo | Propósito |
|---|---|
qurl-mcp.config.json | Configuração de runtime compartilhada usada pelos modos stdio e http |
qurl-mcp.http.json | Configuração de listener e acesso público do servidor apenas HTTP |
Referência do qurl-mcp.config.json
Configurações Centrais Compartilhadas
| Campo | Propósito |
|---|---|
maxUploadFileDataBytes | Limita uploads decodificados e de arquivos locais (padrão 10mb) |
defaultQurlApiUrl | URL base da API de backend qURL |
defaultQurlConnectorUrl | URL base do conector de upload |
As configurações compartilhadas têm estas substituições de ambiente. Os valores de ambiente têm precedência sobre o arquivo de configuração compartilhado. O processo armazena em cache as configurações compartilhadas resolvidas, mas invalida automaticamente esse cache quando os metadados do arquivo ou qualquer valor de ambiente relevante muda.
| Variável de ambiente | Campo de configuração |
|---|---|
MCP_MAX_UPLOAD_FILE_DATA_BYTES | maxUploadFileDataBytes |
QURL_API_URL | defaultQurlApiUrl |
QURL_CONNECTOR_URL | defaultQurlConnectorUrl |
QURL_SMTP_HOST | smtp.host |
QURL_SMTP_PORT | smtp.port |
QURL_SMTP_SECURE | smtp.secure |
QURL_SMTP_USERNAME | smtp.username |
QURL_SMTP_PASSWORD | smtp.password |
QURL_SMTP_FROM_EMAIL | smtp.fromEmail |
QURL_SMTP_FROM_NAME | smtp.fromName |
QURL_SMTP_ALLOWED_RECIPIENTS | smtp.allowedRecipients |
QURL_SMTP_ALLOWED_RECIPIENT_DOMAINS | smtp.allowedRecipientDomains |
QURL_SMTP_MAX_RECIPIENTS_PER_MESSAGE | smtp.maxRecipientsPerMessage |
QURL_SMTP_MAX_RECIPIENTS_PER_HOUR | smtp.maxRecipientsPerHour |
QURL_PUBLIC_VIDEO_FILE_PATH | publicVideo.filePath |
QURL_PUBLIC_VIDEO_TITLE | publicVideo.title |
QURL_PUBLIC_VIDEO_PAGE_PATH | publicVideo.pagePath |
QURL_API_KEY é intencionalmente apenas de ambiente e não tem campo de arquivo
de configuração. Prefira QURL_SMTP_PASSWORD para o segredo SMTP também. Se smtp.password
for armazenado no arquivo de configuração em um host POSIX, restrinja esse arquivo
a permissões somente do proprietário (por exemplo, chmod 600); a inicialização
avisa quando bits de leitura de grupo/outros estão presentes. Esta verificação é
intencionalmente consultiva para que implantações existentes não falhem após uma
atualização, e é ignorada no Windows porque os bits de modo POSIX não estão
disponíveis lá.
A entrega de e-mail em si é fechada em caso de falha, a menos que pelo menos uma
entrada exata de smtp.allowedRecipients ou uma entrada de smtp.allowedRecipientDomains seja
configurada; a inicialização avisa quando credenciais SMTP completas não têm essa
política.
Aumentar maxUploadFileDataBytes também aumenta o teto de memória por solicitação do
parser JSON HTTP para aproximadamente 1,5 vezes esse valor (até cerca de 150 MB
no máximo de 100 MB), antes que a decodificação base64 aplique o limite exato de
bytes. Até que uma sessão tenha concluído uma chamada bem-sucedida à API qURL
downstream (a criação de link de um upload concluído conta), seu teto de parser
permanece na configuração padrão menor de upload de 10 MB; clientes configurados
para um primeiro upload maior devem validar a sessão com uma chamada pequena à
API qURL ou um upload menor primeiro.
Dimensione o máximo configurado e o limite de concorrência do proxy reverso
juntos.
Defina QURL_API_KEY no ambiente para o modo stdio usar a API qURL.
Sem ele, o modo stdio executa por meio do CLI qURL local e não precisa de
conta (veja Início rápido). No modo HTTP, cada
solicitação de cliente fornece sua própria chave de API qURL como token bearer.
defaultQurlApiUrl e QURL_API_URL exigem HTTPS para hosts que não são de
loopback porque chaves e dados da API qURL são enviados como bearer para esse
destino. HTTP simples é aceito apenas para endpoints literais de desenvolvimento
em loopback. URLs do conector de upload seguem a mesma regra HTTPS-exceto-loopback.
Loopback significa 127.0.0.0/8 ou ::1; endereços de bind curinga,
como 0.0.0.0 e ::, são intencionalmente não aceitos como
destinos HTTP de saída.
Destinos do conector são configuração confiável do operador, não entrada do
chamador; endereços privados e resolução de DNS são, portanto, permitidos. Fixe o
nome do host do conector no DNS de implantação e não o aponte para serviços de
metadados.
A credencial bearer qURL do chamador é encaminhada para este host, portanto,
trate o controle de URL e DNS do conector como parte do limite de confiança da
credencial.
Configure a URL base do serviço do conector, não uma rota de upload: o qurl-mcp
anexa /api/upload a caminhos base comuns, aceita esse sufixo de endpoint exato
e rejeita caminhos ambíguos semelhantes a upload, como /upload ou
/api/upload/v2.
O servidor MCP executa verificações limitadas de enquadramento de arquivo, não
análise completa de mídia; o conector deve revalidar independentemente o conteúdo
enviado antes do armazenamento ou serviço, e a entrega deve manter o
comportamento de nosniff como o limite de tipo autoritativo.
URLs base de API e conector que contêm credenciais incorporadas, uma string de
consulta ou um fragmento agora são rejeitados durante a inicialização.
Implantações que usavam anteriormente uma dessas formas incomuns de URL devem
mover credenciais para QURL_API_KEY e manter a URL de serviço configurada em
sua origem e prefixo de caminho opcional.
Configurações SMTP
| Campo | Finalidade |
|---|---|
smtp.host | Nome do host do servidor SMTP |
smtp.port | Porta do servidor SMTP |
smtp.secure | true para TLS implícito; false para STARTTLS obrigatório |
smtp.username | Nome de usuário de login SMTP |
smtp.password | Senha de login SMTP ou código específico do aplicativo |
smtp.fromEmail | Endereço de e-mail do remetente |
smtp.fromName | Nome de exibição do remetente |
smtp.allowedRecipients | Lista de permissões opcional de endereços exatos |
smtp.allowedRecipientDomains | Lista de permissões opcional de domínios exatos (subdomínios não são incluídos) |
smtp.maxRecipientsPerMessage | Limite de destinatários por mensagem (padrão 10) |
smtp.maxRecipientsPerHour | Limite de destinatários tentados por chave qURL por janela horária fixa (padrão 100) |
Estas configurações são usadas quando o envio de e-mail é solicitado por ferramentas como:
create_qurlmint_linkupload_text_qurlupload_file_qurlupload_file_data_qurl
Se qualquer lista de permissões de destinatários estiver configurada, apenas um endereço ou domínio
exato é entregue. Se ambas estiverem vazias, os limites de mensagem e horário ainda se aplicam.
Entradas de domínio são exatas: example.com não permite implicitamente
mail.example.com; liste cada subdomínio permitido explicitamente.
Endereços e domínios são normalizados para minúsculas NFC/IDNA ASCII e um
ponto raiz de DNS final é removido antes da comparação e entrega.
Cada lista de permissões de destinatários é limitada a 1.000 entradas configuradas. O
limite de destinatários por mensagem se aplica ao fan-out único solicitado completo
antes da filtragem da lista de permissões, portanto endereços bloqueados não podem ser usados para enviar um
lote superdimensionado.
No modo HTTP, qualquer chamador com uma chave de API qURL válida pode solicitar uma entrega
SMTP no lado do servidor. Configure allowedRecipients ou allowedRecipientDomains
antes de habilitar SMTP em uma implantação HTTP voltada para a Internet; listas de permissões
vazias permitem entrega a qualquer endereço sintaticamente válido sujeito às cotas.
O transporte SMTP usa timeouts limitados de conexão/socket e é fechado após
cada lote de entrega. Tentativas SMTP com falha ainda consomem cota—incluindo quando uma
interrupção transitória resulta em zero mensagens entregues—portanto falhas repetidas não podem
contornar o limite de abuso.
Cada solicitação de entrega também tem um prazo agregado de 60 segundos. Destinatários não
iniciados antes desse prazo são relatados como pulados; filas do lado do provedor são o
caminho suportado para fan-out maior ou mais lento.
A criptografia de transporte é obrigatória: smtp.secure: true usa TLS implícito,
enquanto smtp.secure: false exige uma atualização STARTTLS bem-sucedida. A porta 465 é
reservada para TLS implícito e, portanto, exige smtp.secure: true.
O estado da cota horária é mantido por processo do servidor: ele é redefinido na reinicialização e
não é compartilhado entre réplicas. Operadores que executam múltiplas instâncias devem
aplicar um limite agregado correspondente no provedor SMTP ou gateway.
A cota no processo é, portanto, uma proteção contra abuso, não um limite global durável
de segurança; o comportamento fail-open de reinicialização/escala horizontal deve ser coberto por esse
limite do lado do provedor.
O rastreamento falha fechado para novos principais após 10.000 principais serem retidos
em um processo; principais existentes continuam usando seus buckets atuais até que
entradas expiradas sejam removidas.
Restrinja a emissão de chaves de API qURL e monitore rejeições de cota de novos principais:
alternar muitas chaves válidas pode deliberadamente manter essa tabela compartilhada na capacidade por
até uma janela de cota.
A cota usa uma janela horária fixa que começa com a primeira entrega
tentada após a janela anterior expirar.
Como em qualquer janela fixa, o tráfego imediatamente antes e depois de um limite pode
totalizar quase o dobro do valor horário configurado; use um limite deslizante ou
contínuo do lado do provedor quando esse pico de limite precisar ser evitado entre réplicas.
A entrega SMTP HTTP é restrita à credencial do operador configurada em
QURL_API_KEY. Uma chave qURL válida de um
cliente diferente não autoriza o uso da conta SMTP do operador. Sem
essa chave explícita do operador, o envio de e-mail HTTP é desabilitado. Stdio mantém seu
modelo de confiança do operador local. Listas de permissões de destinatários e cotas se aplicam em ambos os modos.
Links qURL gerados são incluídos no corpo do e-mail em texto simples. Restrinja destinatários com as listas de permissões SMTP e configure a criptografia de transporte no servidor/provedor SMTP quando a confidencialidade do link for importante.
Prefira variáveis de ambiente para credenciais e políticas SMTP:
QURL_SMTP_USERNAME, QURL_SMTP_PASSWORD, QURL_SMTP_FROM_EMAIL,
QURL_SMTP_ALLOWED_RECIPIENTS, QURL_SMTP_ALLOWED_RECIPIENT_DOMAINS,
QURL_SMTP_MAX_RECIPIENTS_PER_MESSAGE, e
QURL_SMTP_MAX_RECIPIENTS_PER_HOUR.
Configurações da Página Pública de Vídeo
| Campo | Finalidade |
|---|---|
publicVideo.title | Título exibido na página pública de vídeo |
publicVideo.pagePath | Caminho público da página de reprodução de vídeo |
publicVideo.filePath | Caminho absoluto do servidor para o arquivo MP4 |
Quando configurado, o servidor HTTP também expõe:
- uma página pública de reprodução de vídeo
- um endpoint de streaming para o arquivo MP4
publicVideo.filePath é configuração confiável do operador. O componente final
deve ser um arquivo .mp4 regular sem symlink; symlinks de diretórios intermediários mantêm
a resolução normal do sistema de arquivos e, portanto, devem permanecer sob controle do operador.
Na inicialização, este ativo opcional é verificado e um aviso é emitido quando está ausente, vazio ou não
regular, mas intencionalmente mantém o serviço MCP e /healthz disponíveis. A
rota do arquivo de vídeo ainda falha fechada com 404 até que o ativo seja corrigido.
Referência do qurl-mcp.http.json
Use qurl-mcp.http.example.json para desenvolvimento
local e com estado. qurl-mcp.http.stateless.example.json
mostra todos os campos de armazenamento e métricas exigidos por um serviço sem estado implantado.
| Campo | Finalidade |
|---|---|
port | Porta do listener HTTP MCP |
host | Endereço de bind HTTP MCP |
baseUrl | URL base pública do serviço |
allowedHosts | Lista de permissões de hosts para validação do cabeçalho Host |
trustProxyHops | Contagem exata de saltos de proxy reverso confiável (padrão 0) |
stateless | Transporte HTTP com escopo de solicitação sem afinidade de sessão (padrão false) |
maxConcurrentRequests | Limite de concorrência de POST/parser somente sem estado por processo (padrão 20) |
credentialRateLimitStore | Backend do contador de credenciais: memory ou dynamodb (padrão memory) |
rateLimitDynamoDbTable | Tabela DynamoDB usada pelo contador de credenciais compartilhado |
metricsNamespace | Namespace CloudWatch EMF para métricas de saturação sem estado |
metricsService | Dimensão estável de Serviço CloudWatch EMF |
metricsEnvironment | Dimensão estável de Ambiente CloudWatch EMF |
maxSessions | Limite rígido de sessões MCP ativas (padrão 1000) |
maxSessionsPerCredential | Limite de sessões ativas e inicializando por portador (padrão 20) |
maxUnvalidatedSessions | Limite de sessões que não concluíram uma chamada de API qURL downstream (padrão 100) |
sessionIdleTtlMs | Janela de evicção por inatividade de sessão conectada (padrão 15 minutos) |
sessionAbsoluteTtlMs | Vida útil absoluta da sessão, incluindo solicitações SSE/ferramentas ativas (padrão 24 horas) |
unvalidatedSessionTtlMs | Prazo absoluto de validação para sessões de portador nunca validadas (padrão 1 minuto) |
mcpRateLimitPerMinute | Limite de solicitações /mcp por cliente (padrão 120) |
publicFileRateLimitPerMinute | Limite de solicitações de rota pública por cliente (padrão 300) |
Os campos HTTP têm substituições de ambiente correspondentes:
| Variável de ambiente | Campo de configuração |
|---|---|
MCP_PORT | port |
MCP_HOST | host |
MCP_BASE_URL | baseUrl |
MCP_ALLOWED_HOSTS | allowedHosts |
MCP_TRUST_PROXY_HOPS | trustProxyHops |
MCP_HTTP_STATELESS | stateless |
MCP_MAX_CONCURRENT_REQUESTS | maxConcurrentRequests |
MCP_CREDENTIAL_RATE_LIMIT_STORE | credentialRateLimitStore |
MCP_SERVE_LAYERV_LEGAL_PAGES | serveLayerVLegalPages (padrão false) |
MCP_RATE_LIMIT_DYNAMODB_TABLE | rateLimitDynamoDbTable |
MCP_METRICS_NAMESPACE | metricsNamespace |
MCP_METRICS_SERVICE | metricsService |
MCP_METRICS_ENVIRONMENT | metricsEnvironment |
MCP_MAX_SESSIONS | maxSessions |
MCP_MAX_SESSIONS_PER_CREDENTIAL | maxSessionsPerCredential |
MCP_MAX_UNVALIDATED_SESSIONS | maxUnvalidatedSessions |
MCP_SESSION_IDLE_TTL_MS | sessionIdleTtlMs |
MCP_SESSION_ABSOLUTE_TTL_MS | sessionAbsoluteTtlMs |
MCP_UNVALIDATED_SESSION_TTL_MS | unvalidatedSessionTtlMs |
MCP_RATE_LIMIT_PER_MINUTE | mcpRateLimitPerMinute |
MCP_PUBLIC_FILE_RATE_LIMIT_PER_MINUTE | publicFileRateLimitPerMinute |
MCP_MAX_UPLOAD_FILE_DATA_BYTES | maxUploadFileDataBytes (compartilhado) |
O listener usa como padrão 127.0.0.1. Um host que não seja de loopback é rejeitado, a menos que | |
allowedHosts esteja explicitamente configurado. Defina trustProxyHops (ou | |
MCP_TRUST_PROXY_HOPS) para o número exato de saltos de proxy confiáveis; deixe-o em | |
0 para conexões diretas, para que cabeçalhos de IP encaminhado não possam falsificar chaves de rate-limit. | |
| A allowlist de Hosts é limitada a 1.000 entradas para que a validação em tempo de requisição permaneça | |
| delimitada mesmo sob configuração operacional patológica. | |
/mcp aplica a cota de requisições configurada de forma independente tanto ao | |
| IP do cliente quanto ao digest SHA-256 do bearer autenticado. O armazenamento em memória | |
| é local ao processo; o armazenamento DynamoDB usa um contador atômico de janela fixa | |
| chaveado pelo digest da credencial e pelo minuto UTC. Ele nunca armazena o bearer. Como em qualquer | |
| janela fixa, requisições em torno de uma fronteira de minuto podem totalizar quase o dobro da | |
| cota configurada. O contrato da tabela é uma chave de partição string chamada | |
rate_key; a atualização atômica grava um contador numérico request_count e um | |
timestamp numérico de TTL expires_at. Habilite o TTL do DynamoDB em expires_at para que | |
| linhas expiradas não se acumulem; o TTL apenas agenda limpeza assíncrona, e | |
| o minuto na chave—não a exclusão física—reinicia a janela ativa. A role da tarefa | |
exige dynamodb:DescribeTable para inicialização e dynamodb:UpdateItem no | |
| caminho da requisição. Use capacidade sob demanda ou provisione capacidade de escrita suficiente para | |
a taxa esperada da frota; throttling falha de forma fechada com 503 e nunca cai | |
| de volta para a memória. O cliente usa o modo de retry padrão com no máximo duas tentativas, | |
| um timeout de conexão de um segundo e um timeout de requisição de dois segundos que lança exceção; | |
| esses limites explícitos restringem por quanto tempo uma requisição segura uma permissão de concorrência | |
| durante uma falha parcial do armazenamento. A dependência opcional do AWS SDK é fixada em versão exata | |
| de nível superior, enquanto o package lock commitado fixa seu grafo transitivo | |
@aws-sdk/* e @smithy/*. Qualquer bump do SDK deve atualizar o lockfile e | |
manter o teste de regressão de materialização de timeout com NodeHttpHandler real verde. | |
| A dependência é carregada somente quando o armazenamento DynamoDB é selecionado, então | |
consumidores somente stdio podem instalar com --omit=optional. Imagens HTTP implantadas | |
| devem incluir dependências opcionais; a inicialização falha antes de escutar se o SDK | |
| estiver ausente ou expuser uma superfície de runtime incompatível. O cliente usa a | |
cadeia padrão de AWS_REGION e de provedor de credenciais; implantações ECS | |
| normalmente obtêm ambos do ambiente da tarefa e da role da tarefa. Implantações com | |
| reverse-proxy devem definir a contagem de saltos correta, ou todos os chamadores atrás do proxy | |
| compartilharão o único bucket de IP do proxy. Somente a cota de credenciais do DynamoDB é | |
| fleet-wide: o limitador de IP é local ao processo, então sua cota efetiva de frota | |
| multiplica pela contagem de tarefas e deve ser respaldada por um limite de borda compartilhado. A | |
| implantação gerenciada no PR #1305 do qurl-integrations-infra aplica tanto um | |
limite WAF por IP de origem quanto um teto agregado de frota /mcp menor, com | |
| prova de headroom ao vivo rastreada na issue #1306. O bucket de credenciais também impede que uma | |
| chave burle a cota de requisições rotacionando IPs de origem, enquanto | |
maxSessionsPerCredential impede que ela ocupe todo o pool de sessões. | |
| Cada valor de bearer distinto retém uma entrada de bucket de credenciais para a janela | |
| atual de um minuto. O limitador de IP executa primeiro, então a rotação de token de uma única fonte | |
não pode criar entradas mais rápido que mcpRateLimitPerMinute; tráfego | |
| distribuído hostil ainda exige o limite de borda compartilhado documentado. O bucket de IP é o | |
| principal controle em processo contra rotação arbitrária de bearer, porque strings de bearer | |
| distintas não validadas ocupam necessariamente buckets de credenciais distintos. | |
| No modo stateful, orce a memória do parser de sessões pendentes como | |
maxUnvalidatedSessions vezes aproximadamente | |
1,5 vezes o menor entre maxUploadFileDataBytes e 10 MB (mais cerca de 64 KiB | |
| por requisição). Nos padrões, o teto teórico de concorrência é de cerca de | |
1,5 GiB. Reduza maxUnvalidatedSessions e o limite de concorrência de borda compartilhado | |
| juntos quando a implantação tiver um orçamento de memória menor. | |
| Credenciais de bearer são validadas de forma conclusiva pela primeira chamada bem-sucedida | |
| à API qURL downstream. Até lá, sessões usam o teto menor de sessões pendentes | |
| e o prazo de validação de um minuto, então strings de bearer arbitrárias não vazias | |
| não podem ocupar todo o pool de sessões pelo TTL normal de 15 minutos. Um cliente que | |
| executa apenas introspecção MCP permanece pendente por design; após a evicção por prazo, | |
| ele deve reinicializar antes da próxima requisição. Os tetos de sessão e o | |
| prazo de validação são configuráveis para clientes com lacunas maiores entre | |
| introspecção e chamada de ferramenta. O prazo é absoluto e se aplica independentemente | |
| da atividade, incluindo um stream SSE aberto ou uma primeira chamada de ferramenta de longa duração. | |
Clientes validados que desconectam sem enviar DELETE /mcp retêm seu | |
| slot de sessão limitado por um período de graça de reconexão de 30 segundos. Uma reconexão limpa | |
esse prazo; caso contrário, a sessão é removida sem esperar pelo TTL ocioso mais longo. Dimensione maxSessions e o TTL ocioso para clientes que permanecem conectados | |
| mas não realizam teardown explícito de sessão. | |
Sessões validadas também expiram em sessionAbsoluteTtlMs (24 horas por padrão), | |
| mesmo durante um stream SSE ativo ou requisição de ferramenta. Isso impede que keepalives | |
| fixem um slot de sessão global ou por credencial indefinidamente. | |
| A primeira operação qURL downstream deve, portanto, ser concluída antes desse | |
| prazo; uma primeira chamada de API excepcionalmente lenta pode ser interrompida e o cliente | |
| deve reinicializar. Esse comportamento fail-closed impede que uma credencial inválida | |
| estenda seu slot pendente com uma requisição deliberadamente de longa duração. |
No teto de sessões não validadas, uma nova inicialização substitui a sessão não validada ociosa mais antiga, então handshakes de lixo não podem reservar todos os slots pelo TTL completo de validação. Sessões validadas e requisições ativas nunca são evictadas; inicializações pendentes e teardown assíncrono permanecem limitados. Tráfego sustentado ainda pode causar churn de sessões não validadas, então implantações públicas devem usar o modo stateless e controles de admissão de borda.
Aceitar um bearer não vazio durante a inicialização MCP é intencional: mantém a
introspecção de protocolo disponível antes da primeira operação qURL, enquanto o
teto global de sessões, o teto de sessões por credencial, o teto de sessões pendentes, o prazo
absoluto e o limite de taxa de requisições limitam o uso de slots por chaves inválidas. O
middleware MCP não valida a chave em si; somente uma resposta bem-sucedida da API qURL
downstream promove a sessão.
Erros downstream, incluindo respostas não-2xx que parecem autenticadas, não
a promovem, porque um intermediário pode tê-las gerado antes de a API qURL
autenticar o bearer.
A promoção, portanto, assume que o endpoint HTTPS da API qURL configurado e cada
intermediário confiável não armazenam em cache nem sintetizam respostas de sucesso autenticadas.
Proxies reversos nesse caminho devem encaminhar a autorização e desabilitar o cache
de respostas para tráfego da API qURL.
Consequentemente, qualquer chamador com um bearer não vazio pode enumerar o catálogo
público de ferramentas/recursos/prompts e segurar brevemente estado de sessão pendente limitado. Em
redes hostis, coloque implantações não-loopback atrás de um proxy ciente de identidade
que preserve a credencial de bearer qURL do chamador para autorização /mcp.
Inicialização e listagem de catálogo retornam apenas metadados estáticos de propriedade do servidor;
elas não invocam handlers de ferramentas/recursos/prompts, não leem arquivos do host, não contatam a
API qURL ou o conector, nem enviam e-mail. Chamadas de handler dependem da API qURL
configurada para autenticar o bearer encaminhado antes de retornar dados ou aplicar uma
operação. O conector configurado é uma segunda autoridade de credencial: ele deve
autenticar o bearer qURL encaminhado antes de aceitar ou armazenar bytes de upload.
Implantar um conector não autenticado não é suportado, pois permitiria que um
chamador MCP não validado criasse estado no lado do conector.
O modo stateful é o padrão de compatibilidade e mantém o registro de sessão MCP existente,
GET SSE, comportamento explícito de DELETE e cobrança de cota de credencial local ao processo
para todos os três métodos MCP. O modo stateless cria e fecha um servidor e
transporte para cada POST, ignora mcp-session-id e
retorna respostas 405 em formato JSON-RPC para GET e DELETE. É o modo
exigido atrás de um load balancer ou serviço de autoscaling, porque nenhuma requisição depende
de afinidade local ao processo. A permissão de concorrência é adquirida antes do parsing
de JSON e liberada em todos os caminhos de resposta/erro/desconexão. O modo stateless
usa o teto de parser maxUploadFileDataBytes configurado diretamente, porque
a permissão de concorrência pré-parsing fornece seu limite de amplificação de memória.
Orce aproximadamente maxConcurrentRequests vezes (1,5 vezes
maxUploadFileDataBytes mais 64 KiB) por processo; a concorrência padrão no
teto de upload de 100 MB é de aproximadamente 3 GiB antes do trabalho downstream.
A inicialização stateless rejeita configurações cujo orçamento de parser conservador
exceda 4 GiB. Reduza ainda mais ambas as configurações quando a tarefa ECS tiver um limite
de memória menor. Em contraste, sessões stateful acima do teto padrão devem
primeiro concluir uma chamada bem-sucedida à API qURL downstream.
Em redes hostis, um limite de tamanho de requisição de borda autenticado não maior que
o teto de parser configurado é um requisito de implantação: a permissão limita a memória
agregada, mas um bearer não vazio não é validado de forma autoritativa até
a operação parseada alcançar a API qURL downstream.
O listener stateless limita o recebimento de cabeçalhos a 15 segundos e tanto o recebimento
completo da requisição quanto o tempo de vida ocioso do socket a 120 segundos. Uma permissão
de concorrência abrange do parsing até a conclusão da resposta, então clientes travados não podem
reter todo o pool de permissões indefinidamente. Uma chamada de ferramenta que não produz tráfego
de socket por 120 segundos é abortada intencionalmente; integrações que precisam de operações
silenciosas mais longas devem mover esse trabalho para trás de uma API assíncrona, em vez de elevar
esse limite de retenção fleet-wide.
O modo stateless (não-loopback) implantado requer o armazenamento de credenciais DynamoDB
e todos os três campos estáveis de identidade de métricas. Ele emite um heartbeat EMF de 30 segundos:
McpConcurrencyUtilization é a utilização máxima de permissões observada durante o
intervalo na admissão de requisições e no heartbeat (incluindo requisições que iniciam e
terminam entre heartbeats), enquanto
McpConcurrencyRejected e McpRateLimitStoreErrors são deltas de intervalo
snapshot-and-zero que incluem zeros explícitos. Limites de sessão e cotas de destinatários de e-mail
permanecem em memória; a cota de credenciais DynamoDB é de toda a frota e conta
cada POST HTTP autenticado, incluindo inicialização, descoberta e chamadas de
ferramentas. Dimensione essa cota para o padrão completo de requisições esperado, em vez de
apenas chamadas de ferramentas. A permissão também abrange o incremento DynamoDB limitado: durante
uma queda de energia do armazenamento, cada requisição admitida pode reter uma permissão por aproximadamente
quatro segundos (duas tentativas de dois segundos) antes de falhar de forma fechada, enquanto requisições
excedentes recebem um 503 de concorrência rápida. O contador de janela fixa incrementa a cada
tentativa, incluindo
tentativas já acima do limite de credenciais; limites de taxa de borda e alarmes de gravação/limite
do DynamoDB devem, portanto, limitar a amplificação de gravação abusiva.
Os proprietários da implantação devem tornar tanto os alarmes quanto uma sonda de amplificação de gravação
acima do limite portões de promoção rígidos, em vez de tratá-los como observabilidade opcional.
A implantação gerenciada em
qurl-integrations-infra#1305
provisiona esses alarmes, com prova ao vivo rastreada em seu registro de rollout e
issue #1306 antes da promoção.
Integradores diretos de createHttpRuntime que injetam uma implementação de
armazenamento de credenciais ainda devem declarar credentialRateLimitStore: "dynamodb" para
o modo stateless não-loopback. A interface de injeção genérica não pode provar que um
backend personalizado é compartilhado entre réplicas, então a injeção deliberadamente não é uma
saída de emergência do contrato implantado.
Campos de identidade de métricas são rejeitados no modo stateful para que o medidor de concorrência
não possa silenciosamente relatar um zero enganoso.
Cada POST stateless possui um novo servidor MCP e transporte, então nenhuma requisição pode
herdar o estado do manipulador de outra credencial. O encerramento de resposta concluída é
rastreado de forma assíncrona. A admissão para quando esse backlog atinge
maxConcurrentRequests; requisições já em andamento podem então terminar, então o
backlog pode transitoriamente se aproximar do dobro dessa contagem, mas permanece limitado. Enquanto
o portão de admissão está fechado, novas requisições falham com 503 e incrementam
McpConcurrencyRejected em vez de crescer a memória de encerramento sem limite.
Esse contador representa intencionalmente falha de admissão por saturação de requisições ativas
ou pressão de backlog de encerramento. O autoscaling deve usar
McpConcurrencyUtilization sozinho; o contador de rejeições permanece digno de página,
e baixa utilização junto com rejeições identifica atraso de encerramento.
Agrupar esses objetos enfraqueceria o isolamento de requisições e deliberadamente não é uma
otimização de desempenho sem pressão de registro medida.
/healthz e o endpoint público de arquivos de vídeo cada um usa seu próprio
bucket publicFileRateLimitPerMinute, isolado do tráfego legal/página de vídeo
e um do outro. Mantenha o balanceador de carga, a sonda de liveness e a frequência esperada de
requisições de intervalo de vídeo abaixo dessa permissão por IP de origem (300
requisições/minuto por padrão), ou aumente-a para clientes excepcionalmente agressivos.
Prioridade de Configuração
Por padrão, a configuração é carregada dos dois arquivos JSON locais acima. Se um
arquivo estiver ausente, padrões embutidos e variáveis de ambiente são usados.
Caminhos de configuração relativos—incluindo os padrões—são resolvidos a partir do diretório
de trabalho do processo. Defina as variáveis de caminho explícitas abaixo quando um supervisor,
npx, ou um host MCP iniciar o servidor a partir de um diretório diferente.
As seguintes variáveis de ambiente substituem independentemente os caminhos dos arquivos de configuração:
QURL_MCP_CONFIGQURL_MCP_HTTP_CONFIG
QURL_MCP_HTTP_CONFIG nunca substitui o caminho de configuração de runtime compartilhado. Isso mantém
as configurações do listener de sombrear silenciosamente as configurações de SMTP, conector ou API.
server.json e smithery.yaml descrevem o transporte stdio publicado, então
eles incluem configurações compartilhadas de upload/SMTP, mas omitem intencionalmente variáveis
de listener somente HTTP, como QURL_MCP_HTTP_CONFIG e MCP_MAX_SESSIONS.
Não commite chaves de API, credenciais SMTP ou caminhos privados de sistema de arquivos.
Rotas HTTP
Após iniciar no modo http, as rotas comuns são:
| Rota | Propósito |
|---|---|
/mcp | Endpoint MCP remoto principal |
/healthz | Endpoint de verificação de saúde |
/legal/privacy | Página pública de política de privacidade |
/legal/terms | Página pública de termos de serviço |
publicVideo.pagePath | Página pública de reprodução de vídeo |
publicVideo.pagePath + /file | Endpoint de streaming MP4 |
/healthz é intencionalmente não autenticado e sem validação de Host para cada
chamador, expõe apenas { "ok": true }, e usa o limite de requisições de rota pública
configurado em um bucket separado para que sondas de saúde não possam consumir a
permissão da rota legal/vídeo. Um 429 desta rota
significa que a fonte da sonda excedeu publicFileRateLimitPerMinute, não que a
aplicação falhou em sua verificação de liveness; mantenha a frequência da sonda abaixo desse limite.
Ela é registrada antes da validação de Host porque as sondas de alvo ALB usam o IP da tarefa
e porta como Host; rotas públicas MCP e de navegador permanecem validadas por Host.
Autenticação HTTP
O endpoint /mcp requer Authorization: Bearer <qURL API key> em cada
requisição. No modo stateful, o token bearer é vinculado à sessão MCP resultante,
então um ID de sessão não pode ser reutilizado com uma credencial diferente. No
modo stateless, o bearer permanece no escopo da requisição e é descartado quando a
resposta fecha.
Limite de autenticação do operador: a inicialização aceita qualquer token bearer
não vazio e permite que o catálogo público de ferramentas/recursos/prompts seja lido
antes da validação autoritativa pela primeira chamada de API qURL downstream.
Esse catálogo é montado a partir de esquemas e descrições estáticas e não
inclui tokens bearer, credenciais SMTP ou outra configuração do operador.
Limites de sessão não validados, um prazo curto de validação e limites de taxa de requisição
limitam esse estado de pré-validação; o token fornecido é encaminhado apenas para a
API qURL configurada e, para ferramentas de upload, o Conector qURL configurado.
Sessões somente de introspecção, portanto, permanecem não validadas e são fechadas em
unvalidatedSessionTtlMs; clientes podem reinicializar se precisarem de uma sessão de vida mais longa.
Uma sessão é promovida somente após uma chamada de API qURL bem-sucedida ou um
link de upload entregável cunhado através do conector configurado (que encaminha o
bearer para a API qURL)—chamadas rejeitadas ou limitadas por taxa não provam a credencial
válida. O conector é, portanto, parte desse limite de confiança: aponte
QURL_CONNECTOR_URL apenas para um conector que autentique o bearer. Seu
texto de erro de upload (limitado a 1 KiB, caracteres de controle achatados) e os links
que ele cunha são retornados ao agente, e links cunhados podem ser enviados por e-mail. Sessões desconectadas
permanecem registradas por um período de graça de reconexão SSE de 30 segundos, enquanto
maxSessions e maxSessionsPerCredential limitam essa permissão sob rotatividade.
Requisições sem um cabeçalho Origin são aceitas para clientes MCP não-navegador.
Quando Origin está presente, ele deve corresponder à origem de baseUrl; valores malformados ou
de origem cruzada são rejeitados em /mcp. Rotas públicas de saúde, legais e de vídeo
configuradas não usam estado de origem de navegador e não são limitadas por esta verificação.
Configure clientes MCP remotos com:
| Configuração | Valor |
|---|---|
| URL do Servidor MCP | Sua URL HTTPS pública mais /mcp |
| Autenticação | Token Bearer |
| Token | A chave de API qURL do chamador |
Se um cliente suportar apenas descoberta OAuth, coloque um gateway compatível com OAuth
na frente deste servidor em vez de expor /mcp sem autenticação.
Como Verificar a Implantação
Verificações de Nível de Serviço
Comece com:
/healthz/mcp
Os documentos legais da LayerV estão desabilitados por padrão. Somente serviços operados pela LayerV
devem definir MCP_SERVE_LAYERV_LEGAL_PAGES=true (ou serveLayerVLegalPages: true
na configuração HTTP). Operadores auto-hospedados devem publicar suas próprias políticas.
Verificações de Página Pública
Quando habilitado, verifique as páginas legais e a página de vídeo configurada:
/legal/privacy/legal/terms- o caminho da página pública de vídeo configurada
Verificação de Domínio
Se você planeja usar a Plataforma OpenAI, certifique-se de que o seguinte caminho de nível raiz exista:
/.well-known/openai-apps-challenge
Este arquivo de verificação deve viver sob o caminho raiz do domínio
.well-known, não sob/mcp.
Docker
O repositório inclui um Dockerfile para implantação em contêiner.
Exemplo:
docker build -t qurl-mcp .
docker run -i -e QURL_API_KEY=lv_live_xxx qurl-mcp
Se você implantar com Docker, certifique-se de que o contêiner ainda possa acessar os arquivos de configuração corretos, ou substitua os caminhos dos arquivos de configuração com variáveis de ambiente.
Execute o listener HTTP localmente no Docker:
A imagem usa como padrão o ponto de entrada stdio e o servidor HTTP usa como padrão
loopback local do contêiner. Implantações HTTP devem substituir o comando e vincular
a 0.0.0.0 com uma lista de permissões de Host explícita e origem pública HTTPS. Este
exemplo publica apenas para loopback do host; coloque um proxy reverso TLS na frente
dele para a origem https://mcp.example.com configurada:
docker run --rm -p 127.0.0.1:3000:3000 \
-e MCP_HOST=0.0.0.0 \
-e MCP_BASE_URL=https://mcp.example.com \
-e MCP_ALLOWED_HOSTS=mcp.example.com,127.0.0.1,localhost \
qurl-mcp node dist/http.js
Para um único proxy reverso de produção confiável, defina
MCP_TRUST_PROXY_HOPS=1, use a origem HTTPS pública em MCP_BASE_URL, e
defina MCP_ALLOWED_HOSTS para o hostname público. Não exponha o listener do contêiner
diretamente quando a confiança do proxy estiver habilitada.
Comandos Comuns
| Comando | Propósito |
|---|---|
npm run build | Compilar TypeScript |
npm test | Executar testes |
npm run test:coverage | Executar cobertura forçada |
npm run lint | Executar ESLint |
npm run dev | Modo de observação TypeScript |
npm run format | Formatar código-fonte |
npm run format:check | Verificar formatação |
npm run start | Iniciar modo stdio |
npm run start:http | Iniciar modo HTTP |
Ordem de Implantação Recomendada
- Copie e atualize os dois arquivos de configuração de exemplo
- Defina credenciais através de variáveis de ambiente
- Execute
npm install - Execute
npm run build - Execute
npm run start:http - Verifique
/healthz - Verifique se requisições não autenticadas
/mcprecebem401 - Configure o proxy reverso HTTPS
- Verifique uma inicialização MCP autenticada e as páginas públicas opcionais
Ativos de Terceiros
A geração de texto para PDF inclui a fonte Noto Sans SC de peso regular como TrueType
(cerca de 10 MB) para cobertura offline de glifos multilíngues. A fonte estática retém
todos os caracteres da fonte variável original; o PDFKit incorpora apenas os glifos
usados por cada documento. Nenhum download ou dependência adicional de runtime é necessário.
Sua Licença de Fonte Aberta SIL e aviso de direitos autorais estão incluídos em
assets/fonts/OFL.txt; detalhes de conversão estão em assets/fonts/README.md.
Licença
MIT -- LayerV AI