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

npm version

⚠️ Renomeado de @layerv/qurl-mcp na v0.4.0. O pacote antigo está obsoleto e não receberá mais atualizações. Se você estiver 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 stdio local quanto o modo HTTP remoto para 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, ele 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
  • servir páginas legais públicas
  • servir uma página de reprodução de vídeo MP4 configurável

Modos de Execução

ModoFinalidadeComando de InícioCaso de Uso Típico
stdioServidor MCP local em subprocessonpm run startClaude Desktop, Cursor, Codex e outros clientes MCP locais
httpServidor MCP remoto autenticadonpm run start:httpAmbientes de execução de agentes remotos atrás de HTTPS

Mapa de Recursos

Ferramentas de Gerenciamento qURL

FerramentaDescrição
create_qurlCriar um novo qURL
resolve_qurlResolver um token de acesso em uma URL de destino protegida
list_qurlsListar recursos qURL
get_qurlBuscar detalhes de um único qURL
delete_qurlExcluir um qURL
extend_qurlEstender a expiração do qURL
update_qurlAtualizar metadados ou expiração do qURL
mint_linkGerar um novo link de acesso para um recurso existente
batch_create_qurlsCriar vários qURLs em uma única solicitação
revoke_qurl_tokenRevogar um token específico
update_qurl_tokenAtualizar um token específico
list_qurl_sessionsListar sessões de acesso ativas
terminate_qurl_sessionsEncerrar uma ou todas as sessões ativas

Ferramentas de Upload

FerramentaModoDescrição
upload_file_qurlstdioEnviar um arquivo local e gerar um qURL
upload_file_data_qurlstdio/HTTPEnviar conteúdo de arquivo em base64 e gerar um qURL
upload_text_qurlstdio/HTTPEnviar conteúdo de texto e gerar um qURL

upload_file_qurl é intencionalmente somente stdio. Ele pode ler qualquer PDF/imagem suportado que o usuário do processo MCP local possa acessar, portanto, os 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: a injeção de prompt poderia, de outra forma, selecionar outro PDF/imagem legível no host. Execute o stdio sob uma conta de sistema operacional cujo acesso ao sistema de arquivos seja limitado ao conteúdo pretendido para compartilhamento. O modo HTTP nunca registra esta ferramenta de arquivo do host. As ferramentas de byte/texto também estão disponíveis no 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 geração do qURL são operações separadas. Se a geração falhar após o upload, o conector atualmente não possui endpoint de exclusão; o servidor registra o resource_id órfão para limpeza pelo operador e retorna a falha de geração. As tentativas de upload HTTP permanecem limitadas pelos limites de taxa MCP por IP e por credencial; os operadores de stdio devem restringir separadamente os loops de repetição autônomos. A validação de upload vincula o tipo de mídia declarado ao nome do arquivo, além dos 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ço em branco ASCII; a saída do produtor com outros bytes finais é rejeitada, mesmo se um leitor de PDF permissivo a aceitasse. A validação JPEG verifica o enquadramento e os marcadores terminais, em vez de decodificar segmentos de imagem. O conector autenticado deve decodificar independentemente ou validar completamente o conteúdo 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 os 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 aplicativo: symlinks e condições de corrida 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 sistema operacional 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

URIDescrição
qurl://linksLista atual de qURLs
qurl://usageInformações atuais de cota e uso

Prompts MCP

PromptDescrição
secure_a_servicePrompt de integração segura de serviço
audit_linksPrompt de auditoria de links
rotate_accessPrompt de rotação de acesso

Início Rápido

1. Instalar Dependências

npm install

Para uma instalação local de fonte somente stdio, 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:

{
  "mcpServers": {
    "qurl": {
      "command": "npx",
      "args": ["@layervai/qurl-mcp"],
      "env": { "QURL_API_KEY": "lv_live_xxx" }
    }
  }
}

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 para que credenciais e caminhos específicos da máquina não sejam commitados.

Suas responsabilidades são:

ArquivoFinalidade
qurl-mcp.config.jsonConfiguração de tempo de execução compartilhada usada pelos modos stdio e http
qurl-mcp.http.jsonConfiguração de listener do servidor e acesso público somente HTTP

Referência do qurl-mcp.config.json

Configurações Centrais Compartilhadas

CampoFinalidade
maxUploadFileDataBytesLimita uploads decodificados e de arquivos locais (padrão 10mb)
defaultQurlApiUrlURL base da API de backend qURL
defaultQurlConnectorUrlURL 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 mudam.

Variável de ambienteCampo de configuração
MCP_MAX_UPLOAD_FILE_DATA_BYTESmaxUploadFileDataBytes
QURL_API_URLdefaultQurlApiUrl
QURL_CONNECTOR_URLdefaultQurlConnectorUrl
QURL_SMTP_HOSTsmtp.host
QURL_SMTP_PORTsmtp.port
QURL_SMTP_SECUREsmtp.secure
QURL_SMTP_USERNAMEsmtp.username
QURL_SMTP_PASSWORDsmtp.password
QURL_SMTP_FROM_EMAILsmtp.fromEmail
QURL_SMTP_FROM_NAMEsmtp.fromName
QURL_SMTP_ALLOWED_RECIPIENTSsmtp.allowedRecipients
QURL_SMTP_ALLOWED_RECIPIENT_DOMAINSsmtp.allowedRecipientDomains
QURL_SMTP_MAX_RECIPIENTS_PER_MESSAGEsmtp.maxRecipientsPerMessage
QURL_SMTP_MAX_RECIPIENTS_PER_HOURsmtp.maxRecipientsPerHour
QURL_PUBLIC_VIDEO_FILE_PATHpublicVideo.filePath
QURL_PUBLIC_VIDEO_TITLEpublicVideo.title
QURL_PUBLIC_VIDEO_PAGE_PATHpublicVideo.pagePath

QURL_API_KEY é intencionalmente somente ambiente e não possui campo no 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 os 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 é de falha fechada, a menos que pelo menos uma entrada exata smtp.allowedRecipients ou smtp.allowedRecipientDomains esteja configurada; a inicialização avisa quando credenciais SMTP completas não possuem essa política.

Aumentar maxUploadFileDataBytes também aumenta o teto de memória por solicitação do analisador 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 de API qURL downstream bem-sucedida, seu teto de analisador 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 pequena chamada de API qURL 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. No modo HTTP, cada solicitação de cliente fornece sua própria chave de API qURL como um token bearer.

defaultQurlApiUrl e QURL_API_URL exigem HTTPS para hosts que não sejam de loopback porque as chaves e dados da API qURL são enviados como bearer para esse destino. HTTP simples é aceito apenas para endpoints de desenvolvimento literal de loopback. As URLs do conector de upload seguem a mesma regra de 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. Os destinos do conector são configuração confiável do operador, em vez de 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 exato de endpoint 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 nosniff como o limite de tipo autoritativo. URLs base de API e de conectores que contenham credenciais embutidas, uma string de consulta ou um fragmento agora são rejeitadas durante a inicialização. Implantações que anteriormente usavam uma dessas formas incomuns de URL devem mover as credenciais para QURL_API_KEY e manter a URL do serviço configurada em sua origem e prefixo de caminho opcional.

Configurações de SMTP

CampoFinalidade
smtp.hostHostname do servidor SMTP
smtp.portPorta do servidor SMTP
smtp.securetrue para TLS implícito; false para STARTTLS obrigatório
smtp.usernameNome de usuário de login SMTP
smtp.passwordSenha de login SMTP ou código específico do aplicativo
smtp.fromEmailEndereço de e-mail do remetente
smtp.fromNameNome de exibição do remetente
smtp.allowedRecipientsLista de permissões opcional de endereços exatos
smtp.allowedRecipientDomainsLista de permissões opcional de domínios exatos (subdomínios não são incluídos)
smtp.maxRecipientsPerMessageLimite de destinatários por mensagem (padrão 10)
smtp.maxRecipientsPerHourLimite de destinatários tentados por chave qURL por janela horária fixa (padrão 100)

Estas configurações são usadas quando a entrega de e-mail é solicitada por ferramentas como:

  • create_qurl
  • mint_link
  • upload_text_qurl
  • upload_file_qurl
  • upload_file_data_qurl

Se qualquer lista de permissões de destinatários estiver configurada, apenas um endereço ou domínio exato correspondente será entregue. Se ambas estiverem vazias, os limites de mensagem e horário ainda se aplicam. As 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 final de raiz DNS é 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 a 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 ignorados; filas no 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 várias instâncias devem aplicar um limite agregado correspondente no provedor ou gateway SMTP. 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 no lado do provedor. O rastreamento falha fechado para novos principais após 10.000 principais serem retidos em um processo; os principais existentes continuam usando seus buckets atuais até que as entradas expiradas sejam removidas. Restrinja a emissão de chaves de API qURL e monitore rejeições de limite 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 fixa de uma hora que começa com a primeira tentativa de entrega após a expiração da janela anterior. 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 no lado do provedor quando esse pico de limite precisar ser evitado entre réplicas. Os links qURL gerados são incluídos no corpo do e-mail em texto simples. Restrinja os 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

CampoFinalidade
publicVideo.titleTítulo exibido na página pública de vídeo
publicVideo.pagePathCaminho público da página de reprodução de vídeo
publicVideo.filePathCaminho 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, 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.

CampoFinalidade
portPorta do listener HTTP MCP
hostEndereço de bind HTTP MCP
baseUrlURL base pública do serviço
allowedHostsLista de permissões de hosts para validação do cabeçalho Host
trustProxyHopsContagem exata de saltos de proxy reverso confiável (padrão 0)
statelessTransporte HTTP com escopo de solicitação sem afinidade de sessão (padrão false)
maxConcurrentRequestsLimite de concorrência de POST/parser somente sem estado por processo (padrão 20)
credentialRateLimitStoreBackend do contador de credenciais: memory ou dynamodb (padrão memory)
rateLimitDynamoDbTableTabela DynamoDB usada pelo contador de credenciais compartilhado
metricsNamespaceNamespace CloudWatch EMF para métricas de saturação sem estado
metricsServiceDimensão de Serviço CloudWatch EMF estável
metricsEnvironmentDimensão de Ambiente CloudWatch EMF estável
maxSessionsLimite rígido de sessões MCP ativas (padrão 1000)
maxSessionsPerCredentialLimite de sessões ativas e inicializando por portador (padrão 20)
maxUnvalidatedSessionsLimite de sessões que não concluíram uma chamada de API qURL downstream (padrão 100)
sessionIdleTtlMsJanela de evicção por inatividade de sessão conectada (padrão 15 minutos)
sessionAbsoluteTtlMsVida útil absoluta da sessão, incluindo solicitações SSE/ferramentas ativas (padrão 24 horas)
unvalidatedSessionTtlMsPrazo absoluto de validação para sessões de portador nunca validadas (padrão 1 minuto)
mcpRateLimitPerMinuteLimite de solicitações /mcp por cliente (padrão 120)
publicFileRateLimitPerMinuteLimite 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 ambienteCampo de configuração
MCP_PORTport
MCP_HOSThost
MCP_BASE_URLbaseUrl
MCP_ALLOWED_HOSTSallowedHosts
MCP_TRUST_PROXY_HOPStrustProxyHops
MCP_HTTP_STATELESSstateless
MCP_MAX_CONCURRENT_REQUESTSmaxConcurrentRequests
MCP_CREDENTIAL_RATE_LIMIT_STOREcredentialRateLimitStore
MCP_RATE_LIMIT_DYNAMODB_TABLErateLimitDynamoDbTable
MCP_METRICS_NAMESPACEmetricsNamespace
MCP_METRICS_SERVICEmetricsService
MCP_METRICS_ENVIRONMENTmetricsEnvironment
MCP_MAX_SESSIONSmaxSessions
MCP_MAX_SESSIONS_PER_CREDENTIALmaxSessionsPerCredential
MCP_MAX_UNVALIDATED_SESSIONSmaxUnvalidatedSessions
MCP_SESSION_IDLE_TTL_MSsessionIdleTtlMs
MCP_SESSION_ABSOLUTE_TTL_MSsessionAbsoluteTtlMs
MCP_UNVALIDATED_SESSION_TTL_MSunvalidatedSessionTtlMs
MCP_RATE_LIMIT_PER_MINUTEmcpRateLimitPerMinute
MCP_PUBLIC_FILE_RATE_LIMIT_PER_MINUTEpublicFileRateLimitPerMinute
MCP_MAX_UPLOAD_FILE_DATA_BYTESmaxUploadFileDataBytes (compartilhado)
O listener 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 solicitação permaneça
delimitada mesmo sob configuração operacional patológica.
/mcp aplica a permissão de solicitação 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, solicitações em torno de um limite de minuto podem totalizar quase o dobro da
permissão 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 expires_at de TTL. 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 função
de tarefa requer dynamodb:DescribeTable para inicialização e dynamodb:UpdateItem no
caminho de solicitação. Use capacidade sob demanda ou provisione capacidade de escrita suficiente para
a taxa esperada do fleet; throttling falha de forma fechada com 503 e nunca cai
para memória. O cliente usa modo de retry padrão com no máximo duas tentativas,
um timeout de conexão de um segundo e um timeout de solicitação de dois segundos que lança exceção;
esses limites explícitos limitam por quanto tempo uma solicitaçã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 atualização do SDK deve atualizar o lockfile e
manter o teste de regressão de materialização de timeout real-NodeHttpHandler 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 provedor de credenciais AWS_REGION; implantações ECS
normalmente obtêm ambas do ambiente da tarefa e da função de tarefa. Implantações
com proxy reverso 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 DynamoDB é
fleet-wide: o limitador de IP é local ao processo, então sua permissão efetiva de fleet
multiplica com a contagem de tarefas e deve ser respaldada por um limite de borda compartilhado. A
implantação gerenciada no qurl-integrations-infra PR #1305 aplica tanto um
limite WAF por IP de origem quanto um teto agregado de fleet /mcp menor, com
prova de headroom ao vivo rastreada na issue #1306. O bucket de credenciais também impede que uma
chave contorne a permissão de solicitação 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 requer 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 não validadas distintas necessariamente ocupam 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 solicitaçã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 conclusivamente pela primeira chamada bem-sucedida
à API qURL downstream. Até lá, as sessões usam o teto menor de sessões pendentes
e o prazo de validação de um minuto, então strings de bearer não vazias arbitrárias
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 expulsão
por prazo, ele deve reinicializar antes da próxima solicitaçã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 executam encerramento 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 solicitaçã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 incomumente lenta pode ser interrompida e o cliente
deve reinicializar. Esse comportamento de falha fechada impede que uma credencial inválida
estenda seu slot pendente com uma solicitação deliberadamente de longa duração.
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ão, o teto de sessão por credencial, o teto de sessões pendentes, o prazo
absoluto e o limite de taxa de solicitação 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 que a API qURL
autenticasse o bearer.
A promoção, portanto, assume que o endpoint HTTPS configurado da API qURL e cada
intermediário confiável não armazenam em cache nem sintetizam respostas de sucesso autenticadas.
Proxies reversos nesse caminho devem encaminhar 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 manter 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;
eles não invocam handlers de ferramentas/recursos/prompts, leem arquivos do host, contatam a
API qURL ou o conector, nem enviam email. 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 porque permitiria que um
chamador MCP não validado criasse estado no lado do conector.

O modo stateful é o padrão de compatibilidade e retém o registro de sessão MCP existente, GET SSE, comportamento DELETE explícito 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 solicitação depende de afinidade local ao processo. A permissão de concorrência é adquirida antes do parsing JSON e liberada em cada caminho 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 conservador de parser exceda 4 GiB. Reduza ainda mais qualquer uma das 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 solicitaçã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é que a operação parseada alcance a API qURL downstream.

O listener stateless limita o recebimento de cabeçalhos a 15 segundos e tanto o recebimento completo da solicitação quanto o tempo de vida do socket ocioso 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 é intencionalmente abortada; integrações que precisam de operações silenciosas mais longas devem mover esse trabalho para uma API assíncrona em vez de aumentar esse limite de retenção fleet-wide. O modo stateless implantado (não-loopback) exige 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ão 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 do tipo 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 é abrangente para todo o fleet 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ápido. O contador de janela fixa incrementa a cada tentativa, incluindo tentativas que já estão acima do limite de credenciais; limites de taxa de borda e alarmes de gravação/limite do DynamoDB devem, portanto, limitar a amplificação abusiva de gravação. 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 rígidos de promoção, 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. Os campos de identidade de métricas são rejeitados no modo stateful para que o medidor de concorrência não possa relatar silenciosamente um zero enganoso. Cada POST stateless possui um novo servidor MCP e transporte, de modo que nenhuma requisição possa herdar o estado do handler de outra credencial. O encerramento da resposta concluída é rastreado de forma assíncrona. A admissão é interrompida 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 guarda de admissão está fechado, novas requisições falham com 503 e incrementam McpConcurrencyRejected em vez de aumentar 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 usam cada um seu próprio bucket publicFileRateLimitPerMinute, isolados 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, os padrões integrados e as 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_CONFIG
  • QURL_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 faça commit de 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:

RotaPropósito
/mcpEndpoint MCP remoto principal
/healthzEndpoint de verificação de saúde
/legal/privacyPágina pública de política de privacidade
/legal/termsPágina pública de termos de serviço
publicVideo.pagePathPágina pública de reprodução de vídeo
publicVideo.pagePath + /fileEndpoint de streaming MP4

/healthz é intencionalmente não autenticado e sem validação de Host para todos os chamadores, expõe apenas { "ok": true }, e usa o limite de requisições de rota pública configurado em um bucket separado, de modo que as 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 na 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 destino do ALB usam o IP e a porta da tarefa como Host; as rotas públicas de MCP e navegador permanecem com validação de Host.

Autenticação HTTP

O endpoint /mcp exige Authorization: Bearer <qURL API key> em cada requisição. No modo stateful, o token de portador é vinculado à sessão MCP resultante, de modo que um ID de sessão não pode ser reutilizado com uma credencial diferente. No modo stateless, o portador permanece com escopo de requisição e é descartado quando a resposta é fechada.

Limite de autenticação do operador: a inicialização aceita qualquer token de portador 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áticos e não inclui tokens de portador, 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. Sessões somente de introspecção, portanto, permanecem não validadas e são fechadas em unvalidatedSessionTtlMs; os clientes podem reinicializar se precisarem de uma sessão de maior duração. Uma sessão é promovida somente após uma chamada de API qURL bem-sucedida—chamadas rejeitadas ou limitadas por taxa não provam que a credencial é válida. 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 que não são de 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çãoValor
URL do servidor MCPSua URL HTTPS pública mais /mcp
AutenticaçãoToken de portador
TokenA 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

Verificações de Páginas Públicas

Verifique também as páginas legais e, quando configurado, a página de vídeo:

  • /legal/privacy
  • /legal/terms
  • o caminho da página pública de vídeo configurado

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 estar 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 modo HTTP localmente no Docker:

A imagem usa como padrão o ponto de entrada stdio e o servidor HTTP usa como padrão 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:

docker run --rm -p 3000:3000 \
  -e MCP_HOST=0.0.0.0 \
  -e MCP_ALLOWED_HOSTS=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

ComandoPropósito
npm run buildCompilar TypeScript
npm testExecutar testes
npm run test:coverageExecutar cobertura obrigatória
npm run lintExecutar ESLint
npm run devModo de observação TypeScript
npm run formatFormatar código-fonte
npm run format:checkVerificar formatação
npm run startIniciar modo stdio
npm run start:httpIniciar modo HTTP

Ordem de Implantação Recomendada

  1. Copie e atualize os dois arquivos de configuração de exemplo
  2. Defina as credenciais por meio de variáveis de ambiente
  3. Execute npm install
  4. Execute npm run build
  5. Execute npm run start:http
  6. Verifique /healthz
  7. Verifique se requisições não autenticadas /mcp recebem 401
  8. Configure o proxy reverso HTTPS
  9. 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 variável Noto Sans SC de 17,8 MB para cobertura offline de glifos multilíngues. Isso aumenta intencionalmente o tarball npm para aproximadamente 11,4 MB e o pacote descompactado para aproximadamente 18,4 MB para todas as instalações, incluindo implantações que não habilitam fluxos de trabalho de PDF. Enviar a fonte no pacote evita uma dependência de rede em runtime e preserva a renderização previsível de CJK; operadores que priorizam uma instalação menor podem remover o ativo e aceitar o fallback documentado Helvetica com cobertura limitada de CJK. Sua licença SIL Open Font e aviso de direitos autorais estão incluídos em assets/fonts/OFL.txt.

Licença

MIT -- LayerV AI