qURL

qURL es el portal hacia el internet invisible: URLs con caducidad y alcance limitado que permiten a los agentes de IA acceder a servicios que nadie más puede ver.

Documentación

@layervai/qurl-mcp

npm version

⚠️ Renombrado desde @layerv/qurl-mcp en v0.4.0. El paquete antiguo está obsoleto y no recibirá más actualizaciones. Si estás usando @layerv/qurl-mcp@0.3.x, cambia el ámbito en la configuración de tu cliente MCP: mismo binario, misma clave API, sin otros cambios.

Un servidor MCP de qURL que admite tanto el modo local stdio como el modo remoto HTTP para crear, gestionar, resolver y compartir enlaces de acceso seguros.

Descripción general

qURL MCP expone las capacidades de qURL a clientes MCP, GPTs, ChatGPT y otras integraciones remotas.

Actualmente admite:

  • crear, leer, actualizar y eliminar qURLs
  • resolver tokens de acceso
  • gestionar tokens y sesiones de qURL
  • subir contenido de texto o archivos y generar qURLs
  • servir opcionalmente las páginas legales públicas de LayerV
  • servir una página de reproducción de video MP4 configurable

Inicio rápido: sin necesidad de cuenta

Comparte una aplicación que se ejecuta en tu máquina, o una URL, de forma privada desde Claude Desktop, Claude Code, Cursor, Codex o cualquier cliente MCP local. No necesitas cuenta ni clave API: sin QURL_API_KEY configurado, el servidor utiliza la CLI de qURL instalada en tu máquina, y la primera publicación crea la identidad de esta máquina por sí sola.

1. Instala la CLI de qURL (macOS y Linux; se necesita Node.js 22.12 o posterior para el 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

En Windows, descarga qurl.exe desde el último lanzamiento y coloca su carpeta en PATH. El servidor encuentra qurl en PATH, y también en ~/.local/bin, /opt/homebrew/bin y /usr/local/bin, que muchos clientes MCP omiten del PATH que proporcionan a los servidores. Si la CLI falta, cada herramienta responde con este comando de instalación en lugar de fallar.

2. Agrega el servidor a tu cliente MCP, sin bloque env.

Claude Desktop (claude_desktop_config.json) y 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. Pide lo que quieras, por ejemplo "comparte la aplicación en localhost:3000 con mi cliente". El agente publica la aplicación (permanece en tu máquina; el servicio en segundo plano de la CLI mantiene el uso compartido activo) y te da un enlace de corta duración para una persona, o activa las solicitudes de acceso para que las personas puedan pedir acceso.

Herramientas sin cuenta

HerramientaQué haceComando CLI
publish_cridPublicar una aplicación local o URL de forma privada y devolver su CRIDqurl publish <url> [--allow-requests] [--access <mode>]
share_by_cridCrear un enlace de acceso de corta duración para un CRID publicado por esta máquinaqurl share <CRID> [--ttl] [--session-duration]
list_cridsListar los recursos publicados de esta máquina (solo lectura)qurl list --status active
manage_crid_requestsListar, aprobar por código o denegar solicitudes de acceso; listar o eliminar personas aprobadas; activar o desactivar solicitudesqurl requests, qurl approve, qurl deny, qurl grants
manage_crid_accessPolítica de acceso privado, invitaciones y concesiones de consumidoresqurl access <action> <CRID>
set_crid_sharingDetener, iniciar o reiniciar el uso compartido de una aplicación localqurl stop, qurl start, qurl restart
delete_cridEliminar un recurso permanentementequrl delete <CRID> --yes

Las herramientas siguen las mismas reglas que la habilidad de agente qURL (https://layerv.ai/skills/qurl/SKILL.md):

  • Todo se publica de forma privada. Estas herramientas no tienen opción de publicación pública.
  • Una solicitud de acceso pendiente se aprueba solo con el código de seis dígitos que la persona te proporciona. Ninguna lista muestra un código, y ninguna herramienta aprueba por nombre: cualquiera puede escribir cualquier nombre, y los nombres vuelven como datos no verificados.
  • Un enlace de acceso es un secreto de portador para un destinatario. Un CRID no otorga nada por sí mismo y es seguro compartirlo.

El servidor ejecuta la CLI con una matriz de argumentos (nunca un shell), con salida JSON, stdin cerrado, un tiempo de espera y salida limitada, y no registra lo que imprime la CLI. Las herramientas que necesitan una clave de cuenta (las herramientas de enlace, token, sesión y carga de qURL, y los recursos y avisos qurl://) no se listan en este modo.

Con una clave de cuenta

Configura QURL_API_KEY con una clave API de LayerV y el servidor usa la API de qURL directamente, con el conjunto completo de herramientas descrito en el mapa de funciones:

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

Servidor HTTP remoto

El transporte HTTP (qurl-mcp-http) sirve a entornos de ejecución de agentes remotos. Está basado en cuentas: cada solicitud lleva la clave API de qURL del llamante como token de portador, y nunca ejecuta una CLI local. Consulta Autenticación HTTP y la guía MCP de LayerV.

Acceso privado de consumidores

Se requiere Node.js 22.12 o posterior para el SDK compartido de qURL.

publish_crid crea un recurso URL privado con acceso por claves, solo invitación, captura de correo electrónico, revisión o contrato. manage_crid_access cambia la política con su versión actual, envía invitaciones, lista concesiones, aprueba o deniega solicitudes y elimina accesos. Las herramientas devuelven recibos con ámbito de recurso y omiten secretos de invitación y enlaces de portador. Las aplicaciones locales usan la CLI de qURL en la máquina del publicador.

Por ejemplo, un asistente puede convertir "comparte esto con ana@example.com y no permitas solicitudes" en un recurso de solo invitación y una invitación autorizada. Para la captura de correo electrónico, el consumidor debe saber que el publicador recibe su correo electrónico verificado. Un correo electrónico verificado demuestra control del buzón, no identidad legal. La aprobación de contrato requiere un recibo de derecho de backend de publicador confiable; estas herramientas no verifican pagos. No informes la eliminación de la conexión como completa mientras session_cleanup esté pendiente.

Esta función requiere el servicio correspondiente y lectores de admisión. Mantenla deshabilitada hasta que los recorridos de publicador y consumidor pasen. El paquete fija el SDK 3.0.0 publicado y admite instalación limpia con scripts de dependencia bloqueados. Esta versión requiere Node.js 22.12 o posterior; Node.js 20 ya no es compatible.

Modos de ejecución

ModoPropósitoComando de inicioCaso de uso típico
stdioServidor MCP de subproceso localnpm run startClaude Desktop, Cursor, Codex y otros clientes MCP locales
httpServidor MCP remoto autenticadonpm run start:httpEntornos de ejecución de agentes remotos detrás de HTTPS

Mapa de funciones

Herramientas de gestión de qURL

HerramientaDescripción
create_qurlCrear un nuevo qURL
resolve_qurlResolver un token de acceso en una URL de destino protegida
list_qurlsListar recursos qURL
get_qurlObtener detalles de un solo qURL
delete_qurlEliminar un qURL
extend_qurlExtender la expiración de un enlace (necesita qurl:read también)
update_qurlActualizar metadatos o expiración de qURL
mint_linkAcuñar un nuevo enlace de acceso para un recurso existente
share_by_cridAcuñar un enlace de acceso temporal desde un CRID de recurso
batch_create_qurlsCrear múltiples qURLs en una sola solicitud
revoke_qurl_tokenRevocar un token específico
update_qurl_tokenActualizar un token específico
list_qurl_sessionsListar sesiones de acceso activas
terminate_qurl_sessionsTerminar una o todas las sesiones activas

share_by_crid reconoce un valor $<CRID> independiente como una solicitud explícita para acuñar un enlace temporal para ese CRID. El $ es un marcador orientado al usuario y se elimina antes de que el CRID se envíe a la API de qURL. Un CRID simple sigue siendo compatible. Esta herramienta requiere qurl:resolve. Su ttl opcional de duración Go debe resolverse a un número entero positivo de segundos. El servicio limita la vida útil solicitada. El uso compartido realiza una solicitud con un tiempo de espera de 30 segundos y no reintenta automáticamente. Repetir la llamada a la herramienta puede acuñar otro enlace.

Los filtros de estado de recursos aceptan active, revoked o active,revoked. Un recurso permanece activo o revocado incluso cuando su expires_at está en el pasado; los tokens de acceso individuales pueden expirar. expired no es aceptado por list_qurls, aunque el análisis de respuestas tolera valores heredados. La instantánea conserva la prosa de expiración obsoleta del proveedor; su enumeración de estado activo/revocado es autoritativa.

El SDK de qURL reutiliza el mismo Idempotency-Key y cuerpo de solicitud al reintentar solicitudes POST/PATCH después de una falla de red o HTTP 429. Las respuestas HTTP 5xx mutantes no se reintentan automáticamente. Cada nueva invocación de herramienta MCP es una nueva operación con una nueva clave, por lo que repetir create_qurl, batch_create_qurls o mint_link puede acuñar tokens adicionales. MCP no expone una clave proporcionada por el llamante.

Herramientas de carga

HerramientaModoDescripción
upload_file_qurlstdioSubir un archivo local y acuñar un qURL
upload_file_data_qurlstdio/HTTPSubir contenido de archivo en base64 y acuñar un qURL
upload_text_qurlstdio/HTTPSubir contenido de texto y acuñar un qURL
upload_file_qurl es intencionalmente solo stdio. Puede leer cualquier PDF/imagen compatible al que el usuario del proceso MCP local pueda acceder, por lo que los agentes deben invocarlo únicamente para una ruta que el usuario haya seleccionado explícitamente para compartir. No lo exponga a prompts no confiables ni a agentes autónomos: la inyección de prompts podría seleccionar otro PDF/imagen legible en el host. Ejecute stdio bajo una cuenta del sistema operativo cuyo acceso al sistema de archivos esté limitado al contenido compartible previsto. El modo HTTP nunca registra esta herramienta de archivos del host.
Las herramientas de bytes/texto también están disponibles en stdio para que los clientes locales puedan compartir archivos adjuntos en el chat sin materializarlos primero en una ruta conocida del host.
La carga del conector y la acuñación de enlaces son operaciones separadas. El enlace proviene del POST /api/mint_link/:resource_id del conector (en modo túnel, una vista con marca de agua por destinatario), no de qURL mint_link, por lo que los archivos cargados no se pueden volver a enlazar con mint_link; ejecute la herramienta de carga nuevamente. Los enlaces de carga admiten expires_in (1m-30d, convertido a una caducidad absoluta en el reloj del host MCP, por lo que un reloj del host desviado acorta o alarga el enlace; un enlace cuya caducidad devuelta ya ha pasado según el reloj de este host se devuelve con expires_at_already_past), one_time_use y session_duration (segundos completos, hasta 24h); access_policy y max_sessions deben omitirse o ser nulos; los valores no nulos se rechazan antes de la carga. Si el conector alguna vez acuña más enlaces de los solicitados, el resultado informa cuántos adicionales en unexpected_extra_link_count y cualquier ID que devolvió en unexpected_extra_qurl_ids (como máximo 10); pueden estar activos incluso cuando el reloj de este host sugiere caducidad, así que informe al usuario. Si la acuñación falla después de la carga, el conector actualmente no tiene un endpoint de eliminación; el servidor registra el resource_id huérfano para la limpieza del operador y devuelve el fallo de acuñación. Los enlaces de carga aún no se pueden revocar desde este servidor: delete_qurl en el resource_id de la carga no detiene un enlace acuñado por el conector (la revocación necesita el /api/revoke_links del conector), así que prefiera caducidades cortas para archivos sensibles.
Las entradas de duración aceptan días o semanas completos (7d, 1w), o secuencias de unidades Go sin signo (1h30m, 1.5h). Las secuencias mixtas de días/semanas (1d12h), signos iniciales, fracciones con punto inicial y mu griega (μs) se rechazan; el signo micro (µs) se acepta.
Los intentos de carga HTTP permanecen limitados por los límites de tasa MCP por IP y por credencial; los operadores de stdio deben restringir por separado los bucles de reintento autónomos.
La validación de carga vincula el tipo de medio declarado con el nombre de archivo más los marcadores de inicio/fin de formato; no es un escáner de malware ni un decodificador completo de PDF/imagen.
Para resistencia a poliglotas, el marcador final %%EOF de un PDF debe ir seguido solo de espacios en blanco ASCII; la salida del productor con otros bytes finales se rechaza incluso si un lector de PDF permisivo lo aceptaría. La validación JPEG verifica el encuadre y los marcadores terminales en lugar de decodificar segmentos de imagen. El conector autenticado debe decodificar de forma independiente o validar completamente el contenido antes del almacenamiento cuando la validez semántica del medio sea importante. También debe preservar el tipo de medio seguro declarado y servir descargas con X-Content-Type-Options: nosniff en lugar de inferir un tipo ejecutable.
Intencionalmente no hay una lista blanca de rutas a nivel de aplicación: los enlaces simbólicos y las carreras de tiempo de verificación/tiempo de uso hacen que una verificación de prefijo léxico sea un límite de seguridad engañoso. Use una cuenta de sistema operativo dedicada, un contenedor o un montaje de solo lectura cuyos archivos legibles ya estén limitados al directorio de intercambio previsto. El componente de ruta final se abre con O_NOFOLLOW; los enlaces simbólicos de directorios intermedios conservan el comportamiento normal del sistema de archivos bajo este límite de usuario local confiable.

Recursos MCP

URIDescripción
qurl://linksLista qURL actual
qurl://usageInformación actual de cuota y uso

Prompts MCP

PromptDescripción
secure_a_servicePrompt de integración de servicio seguro
audit_linksPrompt de auditoría de enlaces
rotate_accessPrompt de rotación de acceso

Inicio rápido

1. Instalar dependencias

npm install

Para una instalación local de solo stdio desde el código fuente, use npm install --omit=optional; esto omite el SDK de AWS. Las implementaciones HTTP que usan la cuota de credenciales de DynamoDB deben usar la instalación ordinaria para que el SDK opcional esté empaquetado.

2. Compilar

npm run build

3. Iniciar

Modo local stdio:

npm run start

Modo remoto HTTP:

npm run start:http

Ejemplo de cliente MCP

Si desea usar este servidor en modo stdio con un cliente MCP local, use la configuración de Inicio rápido. Omita env para trabajar sin una cuenta a través de la CLI local de qURL, o agregue "env": { "QURL_API_KEY": "lv_live_xxx" } para usar la API de qURL con una clave de cuenta.

Archivos de configuración

Copie los ejemplos rastreados para crear archivos de configuración locales:

cp qurl-mcp.config.example.json qurl-mcp.config.json
cp qurl-mcp.http.example.json qurl-mcp.http.json

Los archivos locales están en gitignore para que las credenciales y las rutas específicas de la máquina no se confirmen.

Sus responsabilidades son:

ArchivoPropósito
qurl-mcp.config.jsonConfiguración de tiempo de ejecución compartida utilizada por ambos modos stdio y http
qurl-mcp.http.jsonConfiguración de acceso público y listener del servidor solo HTTP

Referencia de qurl-mcp.config.json

Configuración central compartida

CampoPropósito
maxUploadFileDataBytesLimita las cargas de archivos decodificados y locales (predeterminado 10mb)
defaultQurlApiUrlURL base de la API backend de qURL
defaultQurlConnectorUrlURL base del conector de carga

La configuración compartida tiene estas anulaciones de entorno. Los valores de entorno tienen prioridad sobre el archivo de configuración compartida. El proceso almacena en caché la configuración compartida resuelta, pero invalida automáticamente esa caché cuando cambian los metadatos del archivo o cualquier valor de entorno relevante.

Variable de entornoCampo de configuración
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 es intencionalmente solo de entorno y no tiene un campo de archivo de configuración. Prefiera QURL_SMTP_PASSWORD también para el secreto SMTP. Si smtp.password se almacena en el archivo de configuración en un host POSIX, restrinja ese archivo a permisos de solo propietario (por ejemplo, chmod 600); el inicio advierte cuando hay bits de lectura de grupo/otros presentes. Esta verificación es intencionalmente consultiva para que las implementaciones existentes no fallen después de una actualización, y se omite en Windows porque los bits de modo POSIX no están disponibles allí. La entrega de correo electrónico en sí es de cierre ante fallos a menos que al menos una entrada exacta de smtp.allowedRecipients o una entrada de smtp.allowedRecipientDomains esté configurada; el inicio advierte cuando las credenciales SMTP completas carecen de esa política.

Elevar maxUploadFileDataBytes también eleva el límite de memoria por solicitud del analizador JSON HTTP a aproximadamente 1.5 veces ese valor (hasta unos 150 MB en el máximo de 100 MB), antes de que la decodificación base64 aplique el límite de bytes exacto. Hasta que una sesión haya completado una llamada exitosa a la API de qURL posterior (la acuñación de enlaces de una carga completada cuenta), su límite de analizador permanece en la configuración de carga predeterminada más pequeña de 10 MB; los clientes configurados para una primera carga más grande deben validar la sesión con una llamada pequeña a la API de qURL o una carga más pequeña primero. Dimensione el máximo configurado y el límite de concurrencia del proxy inverso juntos.

Establezca QURL_API_KEY en el entorno para el modo stdio para usar la API de qURL. Sin él, el modo stdio se ejecuta a través de la CLI local de qURL y no necesita una cuenta (consulte Inicio rápido). En modo HTTP, cada solicitud de cliente proporciona su propia clave de API de qURL como token de portador.

defaultQurlApiUrl y QURL_API_URL requieren HTTPS para hosts que no sean de bucle local porque las claves de API y los datos de qURL se envían como portador a ese destino. HTTP simple se acepta solo para endpoints de desarrollo de bucle local literales. Las URL del conector de carga siguen la misma regla de HTTPS excepto bucle local. Bucle local significa 127.0.0.0/8 o ::1; las direcciones de enlace comodín como 0.0.0.0 y :: no se aceptan intencionalmente como destinos HTTP salientes. Los destinos del conector son configuración de operador confiable en lugar de entrada del llamador; por lo tanto, se permiten direcciones privadas y resolución DNS. Fije el nombre de host del conector en el DNS de implementación y no lo apunte a servicios de metadatos. La credencial de portador qURL del llamador se reenvía a este host, así que trate el control de URL y DNS del conector como parte del límite de confianza de credenciales. Configure la URL base del servicio del conector, no una ruta de carga: qurl-mcp agrega /api/upload a las rutas base ordinarias, acepta ese sufijo de endpoint exacto y rechaza rutas ambiguas similares a carga como /upload o /api/upload/v2. El servidor MCP realiza verificaciones de encuadre de archivos limitadas, no análisis de medios completos; el conector debe revalidar de forma independiente el contenido cargado antes del almacenamiento o servicio, y la entrega debe conservar el comportamiento de nosniff como límite de tipo autoritativo.

Las URL base de API y conector que contienen credenciales incrustadas, una cadena de consulta o un fragmento ahora se rechazan durante el inicio. Las implementaciones que anteriormente usaban una de esas formas de URL inusuales deben mover las credenciales a QURL_API_KEY y mantener la URL del servicio configurada en su origen y prefijo de ruta opcional.

Configuración SMTP

CampoPropósito
smtp.hostNombre de host del servidor SMTP
smtp.portPuerto del servidor SMTP
smtp.securetrue para TLS implícito; false para STARTTLS obligatorio
smtp.usernameNombre de usuario de inicio de sesión SMTP
smtp.passwordContraseña de inicio de sesión SMTP o código específico de la aplicación
smtp.fromEmailDirección de correo electrónico del remitente
smtp.fromNameNombre para mostrar del remitente
smtp.allowedRecipientsLista de permitidos opcional de direcciones exactas
smtp.allowedRecipientDomainsLista de permitidos opcional de dominios exactos (los subdominios no están incluidos)
smtp.maxRecipientsPerMessageLímite de destinatarios por mensaje (predeterminado 10)
smtp.maxRecipientsPerHourLímite de destinatarios intentados por clave qURL por ventana horaria fija (predeterminado 100)

Estos ajustes se utilizan cuando la entrega de correo electrónico es solicitada por herramientas como:

  • create_qurl
  • mint_link
  • upload_text_qurl
  • upload_file_qurl
  • upload_file_data_qurl

Si se configura cualquiera de las listas de permitidos de destinatarios, solo se entrega una dirección o dominio exacto que coincida. Si ambas están vacías, los límites de mensajes y por hora aún se aplican. Las entradas de dominio son exactas: example.com no permite implícitamente mail.example.com; enumere cada subdominio permitido explícitamente. Las direcciones y dominios se normalizan a minúsculas NFC/IDNA en forma ASCII y se elimina un punto raíz DNS final antes de la comparación y entrega. Cada lista de permitidos de destinatarios está limitada a 1,000 entradas configuradas. El límite de destinatarios por mensaje se aplica al fan-out único solicitado completo antes del filtrado de la lista de permitidos, por lo que las direcciones bloqueadas no pueden usarse para enviar un lote de tamaño excesivo. En modo HTTP, cualquier llamador con una clave de API qURL válida puede solicitar una entrega SMTP del lado del servidor. Configure allowedRecipients o allowedRecipientDomains antes de habilitar SMTP en una implementación HTTP expuesta a Internet; las listas de permitidos vacías permiten la entrega a cualquier dirección sintácticamente válida sujeta a las cuotas. El transporte SMTP utiliza tiempos de espera de conexión/socket limitados y se cierra después de cada lote de entrega. Los intentos SMTP fallidos aún consumen cuota, incluso cuando una interrupción transitoria resulta en cero mensajes entregados, por lo que los fallos repetidos no pueden eludir el límite de abuso. Cada solicitud de entrega también tiene un plazo agregado de 60 segundos. Los destinatarios que no se inician antes de ese plazo se informan como omitidos; las colas del lado del proveedor son la ruta admitida para un fan-out más grande o más lento. El cifrado del transporte es obligatorio: smtp.secure: true utiliza TLS implícito, mientras que smtp.secure: false requiere una actualización STARTTLS exitosa. El puerto 465 está reservado para TLS implícito y, por lo tanto, requiere smtp.secure: true. El estado de la cuota por hora se mantiene por proceso del servidor: se restablece al reiniciar y no se comparte entre réplicas. Los operadores que ejecutan múltiples instancias deben imponer un límite agregado correspondiente en el proveedor o puerta de enlace SMTP. La cuota en proceso es, por lo tanto, un respaldo contra abuso, no un límite de seguridad global duradero; el comportamiento de fallo abierto al reiniciar/escalar debe cubrirse con ese límite del lado del proveedor. El seguimiento falla de forma cerrada para nuevos principales después de que se retienen 10,000 principales en un proceso; los principales existentes continúan usando sus depósitos actuales hasta que se eliminan las entradas caducadas. Restrinja la emisión de claves de API qURL y supervise los rechazos de límite de cuota de nuevos principales: rotar muchas claves válidas puede mantener deliberadamente esa tabla compartida a capacidad durante hasta una ventana de cuota. La cuota utiliza una ventana fija de una hora que comienza con el primer intento de entrega después de que expire la ventana anterior. Como con cualquier ventana fija, el tráfico inmediatamente antes y después de un límite puede totalizar casi el doble del valor horario configurado; use un límite deslizante o continuo del lado del proveedor cuando ese estallido de límite deba prevenirse entre réplicas.

La entrega SMTP HTTP está restringida a la credencial del operador configurada en QURL_API_KEY. Una clave qURL válida de un cliente diferente no autoriza el uso de la cuenta SMTP del operador. Sin esa clave de operador explícita, la entrega de correo electrónico HTTP está deshabilitada. Stdio conserva su modelo de confianza de operador local. Las listas de permitidos de destinatarios y las cuotas se aplican en ambos modos.

Los enlaces qURL generados se incluyen en el cuerpo del correo electrónico en texto plano. Restrinja los destinatarios con las listas de permitidos SMTP y configure el cifrado del transporte en el servidor/proveedor SMTP cuando la confidencialidad del enlace sea importante.

Prefiera variables de entorno para las credenciales y 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 y QURL_SMTP_MAX_RECIPIENTS_PER_HOUR.

Configuración de la Página de Video Pública

CampoPropósito
publicVideo.titleTítulo mostrado en la página de video pública
publicVideo.pagePathRuta pública de la página de reproducción de video
publicVideo.filePathRuta absoluta del servidor del archivo MP4

Cuando se configura, el servidor HTTP además expone:

  • una página pública de reproducción de video
  • un punto final de transmisión para el archivo MP4

publicVideo.filePath es configuración de operador confiable. El componente final debe ser un archivo regular .mp4 que no sea un enlace simbólico; los enlaces simbólicos de directorios intermedios mantienen la resolución normal del sistema de archivos y, por lo tanto, deben permanecer bajo control del operador. Al inicio, se sondea este activo opcional y se advierte cuando falta, está vacío o no es regular, pero intencionalmente mantiene el servicio MCP y /healthz disponibles. La ruta del archivo de video aún falla de forma cerrada con 404 hasta que el activo se corrija.

Referencia de qurl-mcp.http.json

Use qurl-mcp.http.example.json para desarrollo local y con estado. qurl-mcp.http.stateless.example.json muestra cada campo de almacén y métrica requerido por un servicio sin estado implementado.

CampoPropósito
portPuerto de escucha MCP HTTP
hostDirección de enlace MCP HTTP
baseUrlURL base pública del servicio
allowedHostsLista de permitidos de hosts para validación del encabezado Host
trustProxyHopsConteo exacto de saltos de proxy inverso confiable (predeterminado 0)
statelessTransporte HTTP con ámbito de solicitud sin afinidad de sesión (predeterminado false)
maxConcurrentRequestsLímite de concurrencia de analizador POST solo sin estado por proceso (predeterminado 20)
credentialRateLimitStoreBackend de contador de credenciales: memory o dynamodb (predeterminado memory)
rateLimitDynamoDbTableTabla DynamoDB utilizada por el contador de credenciales compartido
metricsNamespaceEspacio de nombres CloudWatch EMF para métricas de saturación sin estado
metricsServiceDimensión de Servicio CloudWatch EMF estable
metricsEnvironmentDimensión de Entorno CloudWatch EMF estable
maxSessionsLímite máximo de sesiones MCP activas (predeterminado 1000)
maxSessionsPerCredentialLímite de sesiones activas e inicializándose por portador (predeterminado 20)
maxUnvalidatedSessionsLímite de sesiones que no han completado una llamada API qURL posterior (predeterminado 100)
sessionIdleTtlMsVentana de expulsión por inactividad de sesión conectada (predeterminado 15 minutos)
sessionAbsoluteTtlMsVida útil absoluta de la sesión, incluidas solicitudes SSE/herramientas activas (predeterminado 24 horas)
unvalidatedSessionTtlMsPlazo de validación absoluto para sesiones de portador nunca validadas (predeterminado 1 minuto)
mcpRateLimitPerMinuteLímite de solicitudes /mcp por cliente (predeterminado 120)
publicFileRateLimitPerMinuteLímite de solicitudes de ruta pública por cliente (predeterminado 300)

Los campos HTTP tienen anulaciones de entorno correspondientes:

Variable de entornoCampo de configuración
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_SERVE_LAYERV_LEGAL_PAGESserveLayerVLegalPages (predeterminado false)
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 (compartido)
El listener por defecto usa 127.0.0.1. Se rechaza una host que no sea de loopback a menos que
allowedHosts esté configurado explícitamente. Establece trustProxyHops (o
MCP_TRUST_PROXY_HOPS) al número exacto de saltos de proxy de confianza; déjalo en
0 para conexiones directas para que las cabeceras de IP reenviada no puedan suplantar las claves de limitación de tasa.
La lista de permitidos de Host está limitada a 1.000 entradas para que la validación en tiempo de solicitud se mantenga
acotada incluso bajo una configuración operativa patológica.
/mcp aplica la asignación de solicitudes configurada de forma independiente tanto a la
IP del cliente como al resumen SHA-256 del portador autenticado. El almacén de memoria
es local al proceso; el almacén DynamoDB usa un contador atómico de ventana fija con clave
por resumen de credencial y minuto UTC. Nunca almacena el portador. Como con cualquier
ventana fija, las solicitudes alrededor de un límite de minuto pueden totalizar casi el doble de la
asignación configurada. El contrato de la tabla es una clave de partición de cadena llamada
rate_key; la actualización atómica escribe un contador numérico request_count y una
marca de tiempo numérica expires_at de TTL. Habilita el TTL de DynamoDB en expires_at para que
las filas expiradas no se acumulen; el TTL solo programa una limpieza asíncrona, y
el minuto en la clave—no la eliminación física—reinicia la ventana activa. El rol de la tarea
requiere dynamodb:DescribeTable para el arranque y dynamodb:UpdateItem en
la ruta de solicitud. Usa capacidad bajo demanda o aprovisiona suficiente capacidad de escritura para
la tasa esperada de la flota; la limitación falla de forma cerrada con 503 y nunca recurre
a la memoria. El cliente usa el modo de reintento estándar con como máximo dos intentos,
un tiempo de espera de conexión de un segundo y un tiempo de espera de solicitud de dos segundos que lanza una excepción;
estos límites explícitos acotan cuánto tiempo una solicitud mantiene un permiso de concurrencia
durante una falla parcial del almacén. La dependencia opcional del SDK de AWS está fijada a nivel superior
con versión exacta, mientras que el lockfile de paquetes confirmado fija su grafo transitivo
@aws-sdk/* y @smithy/*. Cualquier actualización del SDK debe actualizar el lockfile y
mantener verde la prueba de regresión de materialización de tiempo de espera real NodeHttpHandler.
La dependencia se carga solo cuando se selecciona el almacén DynamoDB, por lo que
los consumidores solo-stdio pueden instalar con --omit=optional. Las imágenes HTTP desplegadas
deben incluir dependencias opcionales; el arranque falla antes de escuchar si el SDK
está ausente o expone una superficie de runtime incompatible. El cliente usa la
cadena estándar de AWS_REGION y proveedor de credenciales; los despliegues ECS
normalmente obtienen ambas del entorno de la tarea y del rol de la tarea. Los despliegues
con proxy inverso deben establecer el recuento de saltos correcto o todos los llamadores detrás del proxy
compartirán el único bucket de IP del proxy. Solo la cuota de credenciales DynamoDB es
a nivel de flota: el limitador de IP es local al proceso, por lo que su asignación efectiva de flota
se multiplica con el número de tareas y debe estar respaldado por un límite de borde compartido. El
despliegue gestionado en qurl-integrations-infra PR #1305 aplica tanto un
límite WAF por IP de origen como un tope agregado de flota /mcp más bajo, con
prueba de margen en vivo rastreada en el issue #1306. El bucket de credenciales también evita que una
clave evite la asignación de solicitudes rotando IPs de origen, mientras que
maxSessionsPerCredential evita que ocupe todo el pool de sesiones.
Cada valor de portador distinto retiene una entrada de bucket de credenciales para la
ventana actual de un minuto. El limitador de IP se ejecuta primero, por lo que la rotación de tokens desde una fuente
no puede crear entradas más rápido que mcpRateLimitPerMinute; el tráfico
distribuido hostil aún requiere el límite de borde compartido documentado. El bucket de IP es el
control principal en proceso contra la rotación arbitraria de portadores porque
cadenas de portador no validadas distintas ocupan necesariamente buckets de credenciales distintos.
En modo con estado, presupuesta la memoria del parser de sesiones pendientes como
maxUnvalidatedSessions veces aproximadamente
1,5 veces el menor entre maxUploadFileDataBytes y 10 MB (más alrededor de 64 KiB
por solicitud). Con los valores predeterminados, el techo teórico de concurrencia es de aproximadamente
1,5 GiB. Reduce maxUnvalidatedSessions y el límite de concurrencia de borde compartido
juntos cuando el despliegue tiene un presupuesto de memoria más pequeño.
Las credenciales del portador se validan de forma concluyente con la primera
llamada exitosa a la API qURL descendente. Hasta entonces, las sesiones usan el tope de sesiones pendientes más pequeño
y el plazo de validación de un minuto, por lo que cadenas de portador no vacías arbitrarias
no pueden ocupar todo el pool de sesiones durante el TTL normal de 15 minutos. Un cliente que
solo realiza introspección MCP permanece pendiente por diseño; después del desalojo por plazo
debe reinicializarse antes de su próxima solicitud. Los topes de sesión y el
plazo de validación son configurables para clientes con intervalos más largos entre
introspección y llamada a herramienta. El plazo es absoluto y se aplica independientemente
de la actividad, incluido un flujo SSE abierto o una primera llamada a herramienta de larga duración.
Los clientes validados que se desconectan sin enviar DELETE /mcp retienen su
espacio de sesión acotado durante un período de gracia de reconexión de 30 segundos. Una reconexión
borra ese plazo; de lo contrario, la sesión se elimina sin esperar el
TTL de inactividad más largo. Dimensiona maxSessions y el TTL de inactividad para clientes que permanecen conectados
pero no realizan un cierre de sesión explícito.
Las sesiones validadas también expiran en sessionAbsoluteTtlMs (24 horas por defecto),
incluso durante un flujo SSE activo o una solicitud de herramienta. Esto evita que los keepalives
fijen un espacio de sesión global o por credencial indefinidamente.
La primera operación qURL descendente debe, por lo tanto, completarse antes de ese
plazo; una primera llamada API inusualmente lenta puede interrumpirse y el cliente
debe reinicializarse. Este comportamiento de fallo cerrado evita que una credencial inválida
extienda su espacio pendiente con una solicitud deliberadamente de larga duración.

En el tope de sesiones no validadas, una nueva inicialización reemplaza la sesión no validada inactiva más antigua, por lo que los handshakes basura no pueden reservar cada espacio durante todo el TTL de validación. Las sesiones validadas y las solicitudes activas nunca se desalojan; las inicializaciones pendientes y el cierre asíncrono permanecen acotados. El tráfico sostenido aún puede agitar las sesiones no validadas, por lo que los despliegues públicos deberían usar modo sin estado y controles de admisión en el borde.

Aceptar un portador no vacío durante la inicialización MCP es intencional: mantiene la introspección del protocolo disponible antes de la primera operación qURL, mientras que el tope global de sesiones, el tope de sesiones por credencial, el tope de sesiones pendientes, el plazo absoluto y el límite de tasa de solicitudes acotan el uso de espacios por claves inválidas. El middleware MCP no valida la clave en sí; solo una respuesta exitosa de la API qURL descendente promueve la sesión. Los errores descendentes, incluidas respuestas no-2xx que parecen autenticadas, no la promueven porque un intermediario puede haberlas generado antes de que la API qURL autenticara el portador. La promoción asume, por lo tanto, que el endpoint HTTPS de la API qURL configurado y cada intermediario de confianza no almacenan en caché ni sintetizan respuestas de éxito autenticadas. Los proxies inversos en esa ruta deben reenviar la autorización y deshabilitar el almacenamiento en caché de respuestas para el tráfico de la API qURL. En consecuencia, cualquier llamador con un portador no vacío puede enumerar el catálogo público de herramientas/recursos/prompts y mantener brevemente un estado de sesión pendiente acotado. En redes hostiles, coloca los despliegues no-loopback detrás de un proxy consciente de identidad que preserve la credencial de portador qURL del llamador para la autorización /mcp. La inicialización y el listado del catálogo devuelven solo metadatos estáticos propiedad del servidor; no invocan manejadores de herramientas/recursos/prompts, no leen archivos del host, no contactan la API qURL ni el conector, ni envían correo. Las llamadas a manejadores dependen de la API qURL configurada para autenticar el portador reenviado antes de devolver datos o aplicar una operación. El conector configurado es una segunda autoridad de credenciales: debe autenticar el portador qURL reenviado antes de aceptar o almacenar bytes de carga. Desplegar un conector no autenticado no es compatible porque permitiría que un llamador MCP no validado creara estado en el lado del conector.

El modo con estado es el predeterminado de compatibilidad y conserva el registro de sesiones MCP existente, GET SSE, el comportamiento explícito de DELETE y el cobro de cuota de credenciales local al proceso para los tres métodos MCP. El modo sin estado crea y cierra un servidor y transporte para cada POST, ignora mcp-session-id y devuelve respuestas 405 con forma JSON-RPC para GET y DELETE. Es el modo requerido detrás de un balanceador de carga o servicio de autoescalado porque ninguna solicitud depende de la afinidad local al proceso. El permiso de concurrencia se adquiere antes del análisis JSON y se libera en cada ruta de respuesta/error/desconexión. El modo sin estado usa el techo del parser maxUploadFileDataBytes configurado directamente porque el permiso de concurrencia previo al análisis proporciona su límite de amplificación de memoria. Presupuesta aproximadamente maxConcurrentRequests veces (1,5 veces maxUploadFileDataBytes más 64 KiB) por proceso; la concurrencia predeterminada en el techo de carga de 100 MB es de aproximadamente 3 GiB antes del trabajo descendente. El arranque sin estado rechaza configuraciones cuyo presupuesto de parser conservador supere los 4 GiB. Reduce aún más cualquiera de los dos ajustes cuando la tarea ECS tiene un límite de memoria más pequeño. En contraste, las sesiones con estado por encima del techo predeterminado deben primero completar una llamada exitosa a la API qURL descendente. En redes hostiles, un límite de tamaño de solicitud en el borde autenticado no mayor que el techo del parser configurado es un requisito de despliegue: el permiso acota la memoria agregada, pero un portador no vacío no se valida de forma autoritativa hasta que la operación analizada llega a la API qURL descendente.

El listener sin estado acota la recepción de cabeceras a 15 segundos y tanto la recepción completa de la solicitud como la vida útil del socket inactivo a 120 segundos. Un permiso de concurrencia abarca desde el análisis hasta la finalización de la respuesta, por lo que los clientes atascados no pueden retener todo el pool de permisos indefinidamente. Una llamada a herramienta que no produce tráfico de socket durante 120 segundos se aborta intencionalmente; las integraciones que necesitan operaciones silenciosas más largas deben mover ese trabajo detrás de una API asíncrona en lugar de aumentar este límite de retención a nivel de flota. El modo stateless desplegado (no-loopback) requiere el almacén de credenciales DynamoDB y los tres campos estables de identidad de métricas. Emite un heartbeat EMF de 30 segundos: McpConcurrencyUtilization es la utilización máxima de permisos observada durante el intervalo en la admisión de solicitudes y el heartbeat (incluyendo solicitudes que comienzan y terminan entre heartbeats), mientras que McpConcurrencyRejected y McpRateLimitStoreErrors son deltas de intervalo snapshot-and-zero que incluyen ceros explícitos. Los límites de sesión y las cuotas de destinatarios de correo permanecen en memoria; la cuota de credenciales DynamoDB es a nivel de flota y cuenta cada POST HTTP autenticado, incluyendo inicialización, descubrimiento y llamadas de herramientas. Dimensione esa cuota para el patrón completo de solicitudes esperado en lugar de solo llamadas de herramientas. El permiso también abarca el incremento acotado de DynamoDB: durante un brownout del almacén, cada solicitud admitida puede retener un permiso durante aproximadamente cuatro segundos (dos intentos de dos segundos) antes de fallar de forma cerrada, mientras que las solicitudes excedentes reciben un 503 de concurrencia rápido. El contador de ventana fija se incrementa en cada intento, incluyendo intentos que ya están por encima del límite de credenciales; los límites de tasa perimetrales y las alarmas de escritura/throttle de DynamoDB deben, por lo tanto, acotar la amplificación de escritura abusiva. Los propietarios del despliegue deben convertir tanto las alarmas como una sonda de amplificación de escritura por encima del límite en puertas de promoción estrictas, en lugar de tratarlas como observabilidad opcional. El despliegue gestionado en qurl-integrations-infra#1305 aprovisiona esas alarmas, con prueba en vivo rastreada en su ledger de despliegue y el issue #1306 antes de la promoción. Los integradores directos de createHttpRuntime que inyectan una implementación de almacén de credenciales deben declarar credentialRateLimitStore: "dynamodb" para el modo stateless no-loopback. La interfaz de inyección genérica no puede probar que un backend personalizado se comparte entre réplicas, por lo que la inyección no es deliberadamente una vía de escape del contrato desplegado. Los campos de identidad de métricas se rechazan en modo stateful para que el indicador de concurrencia no pueda informar silenciosamente un cero engañoso. Cada POST stateless posee un servidor MCP y transporte nuevos, por lo que ninguna solicitud puede heredar el estado del handler de otra credencial. El teardown de respuesta completada se rastrea de forma asíncrona. La admisión se detiene cuando ese backlog alcanza maxConcurrentRequests; las solicitudes ya en vuelo pueden entonces terminar, por lo que el backlog puede acercarse transitoriamente al doble de esa cantidad, pero permanece acotado. Mientras la puerta de admisión está cerrada, las nuevas solicitudes fallan con 503 e incrementan McpConcurrencyRejected en lugar de crecer la memoria de teardown sin límite. Ese contador representa intencionalmente fallos de admisión por saturación de solicitudes activas o por backpressure de teardown. El autoscaling debe usar McpConcurrencyUtilization solo; el contador de rechazos sigue siendo digno de página, y la baja utilización junto con rechazos identifica retraso de teardown. Agrupar estos objetos debilitaría el aislamiento de solicitudes y no es deliberadamente una optimización de rendimiento sin presión de registro medida. /healthz y el endpoint público de archivos de video usan cada uno su propio bucket de publicFileRateLimitPerMinute, aislado del tráfico legal/página de video y entre sí. Mantenga la frecuencia del balanceador de carga, las sondas de liveness y las solicitudes de rango de video esperadas por debajo de esa asignación por IP de origen (300 solicitudes/minuto por defecto), o auméntela para clientes inusualmente agresivos.

Prioridad de Configuración

Por defecto, la configuración se carga desde los dos archivos JSON locales anteriores. Si un archivo está ausente, se usan los valores predeterminados integrados y las variables de entorno. Las rutas de configuración relativas—incluyendo los valores predeterminados—se resuelven desde el directorio de trabajo del proceso. Establezca las variables de ruta explícitas a continuación cuando un supervisor, npx, o un host MCP inicie el servidor desde un directorio diferente.

Las siguientes variables de entorno anulan de forma independiente las rutas de los archivos de configuración:

  • QURL_MCP_CONFIG
  • QURL_MCP_HTTP_CONFIG

QURL_MCP_HTTP_CONFIG nunca reemplaza la ruta de configuración de runtime compartida. Esto mantiene los ajustes del listener sin ocultar silenciosamente los ajustes SMTP, de conector o de API.

server.json y smithery.yaml describen el transporte stdio publicado, por lo que incluyen ajustes compartidos de subida/SMTP pero omiten intencionalmente variables de listener solo HTTP como QURL_MCP_HTTP_CONFIG y MCP_MAX_SESSIONS.

No confíe claves de API, credenciales SMTP o rutas privadas del sistema de archivos.

Rutas HTTP

Después de iniciar en modo http, las rutas comunes son:

RutaPropósito
/mcpEndpoint MCP remoto principal
/healthzEndpoint de verificación de salud
/legal/privacyPágina pública de política de privacidad
/legal/termsPágina pública de términos de servicio
publicVideo.pagePathPágina pública de reproducción de video
publicVideo.pagePath + /fileEndpoint de streaming MP4

/healthz está intencionalmente sin autenticación y sin validación de Host para cada llamador, expone solo { "ok": true }, y usa el límite de solicitudes de ruta pública configurado en un bucket separado para que las sondas de salud no puedan consumir la asignación de ruta legal/video. Un 429 de esta ruta significa que la fuente de la sonda excedió publicFileRateLimitPerMinute, no que la aplicación falló su verificación de liveness; mantenga la frecuencia de sondas por debajo de ese límite. Se registra antes de la validación de Host porque las sondas de destino ALB usan la IP de la tarea y el puerto como Host; las rutas públicas MCP y de navegador permanecen validadas por Host.

Autenticación HTTP

El endpoint /mcp requiere Authorization: Bearer <qURL API key> en cada solicitud. En modo stateful, el token bearer está vinculado a la sesión MCP resultante, por lo que un ID de sesión no puede reutilizarse con una credencial diferente. En modo stateless, el bearer permanece con alcance de solicitud y se descarta cuando la respuesta se cierra.

Límite de autenticación del operador: la inicialización acepta cualquier token bearer no vacío y permite que el catálogo público de herramientas/recursos/prompts se lea antes de la validación autoritativa por la primera llamada API qURL posterior. Ese catálogo se ensambla a partir de esquemas y descripciones estáticas y no incluye tokens bearer, credenciales SMTP u otra configuración del operador. Los límites de sesión no validados, un plazo de validación corto y los límites de tasa de solicitudes acotan ese estado previo a la validación; el token suministrado se reenvía solo a la API qURL configurada y, para herramientas de subida, al Conector qURL configurado. Las sesiones solo de introspección permanecen por lo tanto sin validar y se cierran en unvalidatedSessionTtlMs; los clientes pueden reinicializar si necesitan una sesión de mayor duración. Una sesión se promueve solo después de una llamada API qURL exitosa o un enlace de subida entregable acuñado a través del conector configurado (que reenvía el bearer a la API qURL)—las llamadas rechazadas o limitadas por tasa no prueban que la credencial sea válida. El conector es por lo tanto parte de este límite de confianza: apunte QURL_CONNECTOR_URL solo a un conector que autentique el bearer. Su texto de error de subida (acotado a 1 KiB, caracteres de control aplanados) y los enlaces que acuña se devuelven al agente, y los enlaces acuñados pueden enviarse por correo. Las sesiones desconectadas permanecen registradas durante un período de gracia de reconexión SSE de 30 segundos, mientras que maxSessions y maxSessionsPerCredential acotan esa asignación bajo rotación.

Las solicitudes sin un encabezado Origin se aceptan para clientes MCP que no son de navegador. Cuando Origin está presente, debe coincidir con el origen de baseUrl; los valores malformados o de origen cruzado se rechazan en /mcp. Las rutas públicas de salud, legales y de video configuradas no usan estado de origen de navegador y no están sujetas a esta verificación.

Configure clientes MCP remotos con:

ConfiguraciónValor
URL del servidor MCPSu URL HTTPS pública más /mcp
AutenticaciónToken Bearer
TokenLa clave API qURL del llamador

Si un cliente solo admite descubrimiento OAuth, coloque una puerta de enlace compatible con OAuth delante de este servidor en lugar de exponer /mcp sin autenticación.

Cómo Verificar el Despliegue

Verificaciones a Nivel de Servicio

Comience con:

  • /healthz
  • /mcp

Los documentos legales de LayerV están deshabilitados por defecto. Solo los servicios operados por LayerV deben establecer MCP_SERVE_LAYERV_LEGAL_PAGES=true (o serveLayerVLegalPages: true en la configuración HTTP). Los operadores autoalojados deben publicar sus propias políticas.

Verificaciones de Páginas Públicas

Cuando estén habilitadas, verifique las páginas legales y la página de video configurada:

  • /legal/privacy
  • /legal/terms
  • la ruta de página de video público configurada

Verificación de Dominio

Si planea usar OpenAI Platform, asegúrese de que exista la siguiente ruta a nivel raíz:

/.well-known/openai-apps-challenge

Este archivo de verificación debe vivir bajo la ruta raíz del dominio .well-known, no bajo /mcp.

Docker

El repositorio incluye un Dockerfile para el despliegue en contenedores.

Ejemplo:

docker build -t qurl-mcp .
docker run -i -e QURL_API_KEY=lv_live_xxx qurl-mcp

Si despliega con Docker, asegúrese de que el contenedor aún pueda acceder a los archivos de configuración correctos, o anule las rutas de los archivos de configuración con variables de entorno.

Ejecute el listener HTTP localmente en Docker:

La imagen usa por defecto el punto de entrada stdio y el servidor HTTP usa por defecto loopback local del contenedor. Los despliegues HTTP deben anular el comando y vincularse a 0.0.0.0 con una lista de Hosts permitidos explícita y un origen HTTPS público. Este ejemplo publica solo en loopback del host; coloque un proxy inverso TLS delante de él para el origen https://mcp.example.com configurado:

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 un único proxy inverso de producción confiable, establezca MCP_TRUST_PROXY_HOPS=1, use el origen HTTPS público en MCP_BASE_URL, y establezca MCP_ALLOWED_HOSTS al nombre de host público. No exponga el listener del contenedor directamente cuando la confianza del proxy esté habilitada.

Comandos Comunes

ComandoPropósito
npm run buildCompilar TypeScript
npm testEjecutar pruebas
npm run test:coverageEjecutar cobertura forzada
npm run lintEjecutar ESLint
npm run devModo de observación de TypeScript
npm run formatFormatear código fuente
npm run format:checkVerificar formato
npm run startIniciar modo stdio
npm run start:httpIniciar modo HTTP

Orden de Despliegue Recomendado

  1. Copie y actualice los dos archivos de configuración de ejemplo
  2. Establezca las credenciales a través de variables de entorno
  3. Ejecute npm install
  4. Ejecute npm run build
  5. Ejecute npm run start:http
  6. Verifique /healthz
  7. Verifique que las solicitudes /mcp no autenticadas reciban 401
  8. Configure el proxy inverso HTTPS
  9. Verifique una inicialización MCP autenticada y las páginas públicas opcionales

Activos de Terceros

La generación de texto a PDF incluye la fuente Noto Sans SC de peso regular como TrueType (alrededor de 10 MB) para cobertura de glifos multilingüe sin conexión. La fuente estática conserva todos los caracteres de la fuente variable original; PDFKit incrusta solo los glifos usados por cada documento. No se necesita descarga ni dependencia de runtime adicional. Su Licencia de Fuente Abierta SIL y aviso de copyright están incluidos en assets/fonts/OFL.txt; los detalles de conversión están en assets/fonts/README.md.

Licencia

MIT -- LayerV AI