Server data from the Official MCP Registry
Mint, resolve, audit, and rotate scope-limited expiring access links (qURLs) for AI agents.
About
Mint, resolve, audit, and rotate scope-limited expiring access links (qURLs) for AI agents.
Security Report
The qURL MCP server is a developer tool for creating expiring access links, with bearer-token authentication on the HTTP transport, hashed credential tracking, rate limiting, and origin/host validation. The visible code shows no exfiltration, hardcoded secrets, or shell-injection patterns; the CLI is invoked via argument arrays rather than a shell. Notable items are the broad file-read capability of upload_file_qurl and the truncated remainder of the source, which was not fully reviewed. Supply chain analysis found 6 known vulnerabilities in dependencies (0 critical, 4 high severity). Package verification found 1 issue.
3 files analyzed · 10 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
What You'll Need
Set these up before or after installing:
Environment variable: QURL_API_KEY
Environment variable: QURL_API_URL
Environment variable: QURL_CONNECTOR_URL
Environment variable: QURL_LINK_HOSTS
Environment variable: QURL_MCP_CONFIG
Environment variable: MCP_MAX_UPLOAD_FILE_DATA_BYTES
Environment variable: QURL_SMTP_HOST
Environment variable: QURL_SMTP_PORT
Environment variable: QURL_SMTP_SECURE
Environment variable: QURL_SMTP_USERNAME
Environment variable: QURL_SMTP_PASSWORD
Environment variable: QURL_SMTP_FROM_EMAIL
Environment variable: QURL_SMTP_FROM_NAME
Environment variable: QURL_SMTP_ALLOWED_RECIPIENTS
Environment variable: QURL_SMTP_ALLOWED_RECIPIENT_DOMAINS
Environment variable: QURL_SMTP_MAX_RECIPIENTS_PER_MESSAGE
Environment variable: QURL_SMTP_MAX_RECIPIENTS_PER_HOUR
Environment variable: QURL_MCP_FEEDBACK
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-layervai-qurl-mcp": {
"env": {
"QURL_API_KEY": "your-qurl-api-key-here",
"QURL_API_URL": "your-qurl-api-url-here",
"QURL_SMTP_HOST": "your-qurl-smtp-host-here",
"QURL_SMTP_PORT": "your-qurl-smtp-port-here",
"QURL_LINK_HOSTS": "your-qurl-link-hosts-here",
"QURL_MCP_CONFIG": "your-qurl-mcp-config-here",
"QURL_SMTP_SECURE": "your-qurl-smtp-secure-here",
"QURL_MCP_FEEDBACK": "your-qurl-mcp-feedback-here",
"QURL_CONNECTOR_URL": "your-qurl-connector-url-here",
"QURL_SMTP_PASSWORD": "your-qurl-smtp-password-here",
"QURL_SMTP_USERNAME": "your-qurl-smtp-username-here",
"QURL_SMTP_FROM_NAME": "your-qurl-smtp-from-name-here",
"QURL_SMTP_FROM_EMAIL": "your-qurl-smtp-from-email-here",
"QURL_SMTP_ALLOWED_RECIPIENTS": "your-qurl-smtp-allowed-recipients-here",
"MCP_MAX_UPLOAD_FILE_DATA_BYTES": "your-mcp-max-upload-file-data-bytes-here",
"QURL_SMTP_MAX_RECIPIENTS_PER_HOUR": "your-qurl-smtp-max-recipients-per-hour-here",
"QURL_SMTP_ALLOWED_RECIPIENT_DOMAINS": "your-qurl-smtp-allowed-recipient-domains-here",
"QURL_SMTP_MAX_RECIPIENTS_PER_MESSAGE": "your-qurl-smtp-max-recipients-per-message-here"
},
"args": [
"-y",
"@layervai/qurl-mcp"
],
"command": "npx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
qURL MCP server for temporary access links
Give an AI agent tools to share localhost web apps, private APIs, and selected
files through expiring qURL™ by LayerV access links.
This repository implements the Model Context Protocol server for that workflow.
The @layervai/qurl-mcp package runs as a local stdio server backed by the
qURL CLI or API, or as an authenticated remote HTTP server backed by the API.
Start with the no-account local quickstart
or account-backed client configuration, then choose
the tools for your task. In local mode, publish_crid runs the installed
CLI to publish an app that is already running; share_by_crid issues its
access link. Creating a link alone does not publish an app or restrict an
existing public URL. Uploads require the account-backed tool set and use a separate connector lifecycle;
see upload tools for their supported options and limits.
Local app sharing · File sharing · Sharing questions · Private links
⚠️ Renamed from
@layerv/qurl-mcpin v0.4.0. The old package is deprecated and will not receive further updates. If you're using@layerv/qurl-mcp@0.3.x, swap the scope in your MCP client config — same binary, same API key, no other changes.
Let an AI agent share a localhost preview
Use this workflow when an agent needs to give a reviewer access to a development preview or internal API. A tool that creates a link does not start the app on your laptop.
-
Keep the local app running. Configure a local stdio MCP client using the no-account quickstart, with the qURL CLI installed on the same host and no
QURL_API_KEYset. -
Ask the agent to call
publish_cridwithapp_name(a short public name, without secrets or recipient details) and the app's root loopback URL, such ashttp://127.0.0.1:3000, and save the returned CRID. Alternatively, publish it directly with the CLI. Local tools use that machine's device identity; a CRID does not grant another device authority over the resource. -
Ask the agent to call
share_by_cridwith the published CRID and a requested lifetime. The following is a tool argument object, not a complete MCP message:{ "crid": "REPLACE_WITH_PUBLISHED_CRID", "ttl": "5m" } -
Share the full returned link through your intended private channel and report the expiry returned by the service. Do not assume the requested TTL was granted unchanged. Keep the publishing host and app available.
Check that your installed release exposes these tools. In account-backed mode,
share_by_crid instead uses the API credential and requires qurl:resolve and
resource authorization. For resources already managed through the API, see mint_link and the other
link-management tools. Tool availability and resource
identifiers matter; an uploaded-file identifier cannot be substituted into
the same workflow.
Let an AI agent share a PDF or image
For example, give a reviewer a short-lived link to a selected PDF report.
Use account-backed stdio mode (QURL_API_KEY set) or authenticated HTTP mode,
then configure the upload Connector under configuration files.
Upload tools are not listed in no-account local mode. Choose the tool for the transport:
| File location | Tool | Important requirement |
|---|---|---|
| A PDF or supported raster image on the local MCP server host | upload_file_qurl | Available in stdio mode; the host process must be allowed to read the selected file. |
| PDF or image bytes supplied to an HTTP or stdio MCP client | upload_file_data_qurl | Supply the supported file as base64 data with the correct filename and media type. |
| A file already stored by an upload tool | mint_uploaded_file_link | Reuse its resource ID to mint a fresh link without another upload. |
| Text to share as a generated PDF | upload_text_qurl | Uses the configured upload service, rather than publishing a local app. |
A stdio tool argument example:
{
"file_path": "/absolute/path/to/review.pdf",
"expires_in": "5m",
"one_time_use": true
}
Pass these arguments to upload_file_qurl only for a file the user selected.
The tool uploads to the configured file service and returns a new access link.
Use the confirmed expiry in its response. Opening a single-use link to test it
can consume it before the recipient opens it.
Upload links cannot currently be revoked through this MCP server, and mint_link
cannot create another link for an uploaded file. See upload tools
for the supported formats, size limits, response fields, and cleanup limitations.
Choosing what an agent should share
Should the agent share an app or a deployed preview?
If the reviewer needs the current localhost app, keep it running and use the local CLI-backed tools to publish the resource and issue its link. An existing ngrok endpoint, Cloudflare application, or Vercel preview is another possible handoff, but its access controls and availability come from that separately configured service. This server exposes qURL operations; it does not create those other deployments. See the app-sharing comparison when choosing the workflow. Share the confirmed expiry and preserve the app's login requirements.
Should the agent upload the result or link to a running file service?
A selected PDF or image can use this server's account-backed upload tools. A hosted Google Drive document instead uses Drive's sharing permissions; an S3 object can use a presigned download URL. Those storage workflows are distinct from publishing a local file server, which must remain available. Choose based on where the result should live and which service the agent is authorized to use. This server's upload identifiers and revocation limits still apply; a file path, uploaded-file identifier, and local-service CRID are not interchangeable.
MCP sharing questions
Is this a public tunnel to my localhost app?
In no-account stdio mode, this server invokes the local CLI to publish and manage an already running app. API-backed and remote HTTP modes use their configured resources; a remote server cannot publish an app on your laptop through its own localhost address. Use the localhost-sharing comparison when choosing between resource access links and a public endpoint workflow.
Does every AI client support the same tools?
No. Configure the client's supported transport and check the installed server's
tool list. A remote HTTP server cannot use upload_file_qurl to read a file
from the client's laptop; use the file-data tool for a supported attachment.
Can I use an app link and an uploaded-file link interchangeably?
No. App resource management and uploaded-file link minting use different lifecycles. Follow the tool that issued the link, including its revocation limits. Your local app's own login continues to apply when the link opens it.
Overview
The server exposes qURL operations as MCP tools for clients that support the configured transport. See the LayerV MCP guide for product setup and the tables below for the capabilities in this source version.
It currently supports:
- creating, reading, updating, and deleting qURLs
- resolving access tokens
- managing qURL tokens and sessions
- uploading text or file content and generating qURLs
- optionally serving LayerV public legal pages
- serving a configurable MP4 video playback page
Quick start: no account needed
Share an app running on your machine, or a URL, privately from Claude
Desktop, Claude Code, Cursor, Codex, or any local MCP client. You need no
account and no API key: with no QURL_API_KEY set, the server drives the
qURL CLI
installed on your machine, and the first publish creates this machine's
identity by itself.
1. Install the qURL CLI (macOS and Linux; Node.js 22.12 or later is needed for the MCP server):
(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
On Windows, download qurl.exe from the
latest release
and put its folder on PATH. The server finds qurl on PATH, and also in
~/.local/bin, /opt/homebrew/bin, and /usr/local/bin, which many MCP
clients leave off the PATH they give servers. If the CLI is missing, every
tool answers with this install command instead of failing.
2. Add the server to your MCP client, with no env block.
Claude Desktop (claude_desktop_config.json) and 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. Ask for what you want, for example "share the app on localhost:3000 with my client". The agent publishes the app (it stays on your machine; the CLI's background service keeps the share running), and gives you a short-lived link for one person, or turns on access requests so people can ask for access.
Tools without an account
| Tool | What it does | CLI command |
|---|---|---|
publish_crid | Publish a local app or URL privately and return its CRID | qurl publish <url> [--allow-requests] [--access <mode>] |
share_by_crid | Make a short-lived access link for a CRID this machine published | qurl share <CRID> [--ttl] [--session-duration] |
list_crids | List this machine's published resources (read-only) | qurl list --status active |
manage_crid_requests | List, approve by code, or deny access requests; list or remove approved people; turn requests on or off | qurl requests, qurl approve, qurl deny, qurl grants |
manage_crid_access | Private access policy, invitations, and consumer grants | qurl access <action> <CRID> |
set_crid_sharing | Stop, start, or restart sharing a local app | qurl stop, qurl start, qurl restart |
delete_crid | Delete a resource permanently | qurl delete <CRID> --yes |
The tools follow the same rules as the qURL agent skill (https://layerv.ai/skills/qurl/SKILL.md):
- Everything is published private. These tools have no option to publish publicly.
- A pending access request is approved only with the six-digit code the person gives you. No listing shows a code, and no tool approves by name: anyone can type any name, and names come back as unverified data.
- An access link is a bearer secret for one recipient. A CRID grants nothing by itself and is safe to share.
The server runs the CLI with an argument array (never a shell), with
JSON output, closed stdin, a timeout, and bounded output, and it does not
log what the CLI prints. The tools that need an account key (the qURL link,
token, session, and upload tools, and the qurl:// resources and prompts)
are not listed in this mode.
With an account key
Set QURL_API_KEY to a LayerV API key and the server uses the qURL API
directly, with the full tool set described in the feature map:
{
"mcpServers": {
"qurl": {
"command": "npx",
"args": ["-y", "@layervai/qurl-mcp"],
"env": { "QURL_API_KEY": "lv_live_xxx" }
}
}
}
Remote HTTP server
The HTTP transport (qurl-mcp-http) serves remote agent runtimes. It is
account-based: every request carries the caller's qURL API key as a bearer
token, and it never runs a local CLI. See HTTP Authentication
and the LayerV MCP guide.
Private consumer access
Node.js 22.12 or later is required by the shared qURL SDK.
publish_crid creates a private URL resource with keys, invite_only,
email_capture, review, or contract access. manage_crid_access changes the
policy with its current version, sends invitations, lists grants, approves
or denies requests, and removes access. The tools return resource-scoped
receipts and omit invitation secrets and bearer links. Local apps use the
qURL CLI on the publisher machine. In local mode, app_name is required and sets and confirms
the public preview name through qurl preview set (requires a CLI with that command).
If naming fails, the tool returns an error with the published CRID and recovery
instructions. It does not delete or republish the resource. Callers must now
provide app_name; use the user's app name or a short neutral name without
secrets or recipient details. Include the returned name and browser address
in the answer even when invitation emails are sent.
For example, an assistant can turn “share this with ana@example.com, and allow no requests” into an invite_only resource and an authorized invitation. For email capture, the consumer must know that the publisher receives their verified email. A verified email proves mailbox control, not legal identity. Contract approval requires a trusted publisher backend entitlement receipt; these tools do not verify payment. Do not report connection removal as complete while session_cleanup is pending.
This feature requires the matching service and admission readers. Keep it disabled until the publisher and consumer journeys pass. The package pins the published SDK 3.0.0 and supports clean installation with dependency scripts blocked. This release requires Node.js 22.12 or later; Node.js 20 is no longer supported.
Runtime Modes
| Mode | Purpose | Start Command | Typical Use Case |
|---|---|---|---|
stdio | Local subprocess MCP server | npm run start | Claude Desktop, Cursor, Codex, and other local MCP clients |
http | Authenticated remote MCP server | npm run start:http | Remote agent runtimes behind HTTPS |
Feature Map
qURL Management Tools
| Tool | Description |
|---|---|
create_qurl | Create a new qURL |
resolve_qurl | Resolve an access token into a protected target URL |
list_qurls | List qURL resources |
get_qurl | Fetch details for a single qURL |
delete_qurl | Delete a qURL |
extend_qurl | Extend a link's expiration (needs qurl:read too) |
update_qurl | Update qURL metadata or expiration |
mint_link | Mint a new access link for an existing resource |
share_by_crid | Mint a temporary access link from a resource CRID |
batch_create_qurls | Create multiple qURLs in one request |
revoke_qurl_token | Revoke a specific token |
update_qurl_token | Update a specific token |
list_qurl_sessions | List active access sessions |
terminate_qurl_sessions | Terminate one or all active sessions |
share_by_crid recognizes a standalone $<CRID> value as an explicit request
to mint a temporary link for that CRID. The $ is a user-facing marker and is
removed before the CRID is sent to the qURL API. A bare CRID remains supported.
This tool requires qurl:resolve. Its optional Go-duration ttl must resolve to
a positive whole number of seconds. The service clamps the requested lifetime.
Sharing makes one request with a 30-second timeout and does not automatically
retry. Repeating the tool call can mint another link.
Resource status filters accept active, revoked, or active,revoked.
A resource remains active or revoked even when its expires_at is in the past;
individual access tokens can expire. expired is not accepted by list_qurls,
even though response parsing tolerates legacy values. The snapshot retains
upstream's stale expiration prose; its active/revoked status enum is authoritative.
The qURL SDK reuses the same Idempotency-Key and request body when retrying
POST/PATCH requests after a network failure or HTTP 429. Mutating HTTP 5xx
responses are not automatically retried. Each new MCP tool invocation is a new
operation with a new key, so repeating create_qurl, batch_create_qurls, or
mint_link can mint additional tokens. MCP does not expose a caller-supplied key.
Upload Tools
| Tool | Mode | Description |
|---|---|---|
upload_file_qurl | stdio | Upload a local file and mint a qURL |
upload_file_data_qurl | stdio/HTTP | Upload base64 file content and mint a qURL |
upload_text_qurl | stdio/HTTP | Upload text content and mint a qURL |
mint_uploaded_file_link | stdio/HTTP | Mint a link for a stored upload without uploading bytes again |
upload_file_qurl is intentionally stdio-only. It can read any supported
PDF/image that the local MCP process user can access, so agents should invoke
it only for a path the user explicitly selected for sharing. Do not expose it
to untrusted prompts or autonomous agents: prompt injection could otherwise
select another readable PDF/image on the host. Run stdio under an OS account
whose filesystem access is limited to intended shareable content. HTTP mode
never registers this host-file tool.
The byte/text tools are also available in stdio so local clients can share
in-chat attachments without first materializing them at a known host path.
To recover after a successful upload but a failed mint, keep the returned
resource_id and call mint_uploaded_file_link. This makes one mint attempt
without uploading the file again. Each call can create a new live link. A timed-out
or failed prior mint may already have created one, so reconcile that result before
retrying automatically. Existing links are not cancelled. The response contains
the new link and its expiry status.
Connector upload and link minting are separate operations. The link comes from
the connector's POST /api/mint_link/:resource_id (in tunnel mode, a
per-recipient watermarked view), not from qURL mint_link, so uploaded files
cannot be re-linked with mint_link. Use mint_uploaded_file_link with the
saved resource_id instead. Upload links
support expires_in (1m-30d, converted to an absolute expiry on the MCP host's
clock, so a host clock that is off shortens or lengthens the link; a link whose returned expiry
is already past by this host's clock is returned with expires_at_already_past), one_time_use, and session_duration (whole seconds, up to 24h); access_policy and
max_sessions must be omitted or null; non-null values are rejected before the upload. If the connector ever mints more
links than requested, the result reports how many extra in
unexpected_extra_link_count and any IDs it returned in
unexpected_extra_qurl_ids (at most 10); they may be live even when this host's clock suggests expiry, so tell the user. If minting
fails after upload, the connector currently has no delete endpoint; the server logs the orphaned
resource_id for operator cleanup and returns the mint failure. Upload links
cannot yet be revoked from this server: delete_qurl on the upload's
resource_id does not stop a connector-minted link (revocation needs the
connector's /api/revoke_links), so prefer short expiries for sensitive files.
Duration inputs accept whole days or weeks (7d, 1w), or unsigned Go-unit
sequences (1h30m, 1.5h). Mixed day/week sequences (1d12h), leading signs,
leading-dot fractions, and Greek mu (μs) are rejected; micro sign (µs) is accepted.
HTTP upload attempts remain bounded by the per-IP and per-credential MCP rate
limits; stdio operators should separately constrain autonomous retry loops.
Upload validation binds the declared media type to the filename plus format
start/end markers; it is not a malware scanner or full PDF/image decoder.
For polyglot resistance, a PDF's final %%EOF marker must be followed only by
ASCII whitespace; producer output with other trailing bytes is rejected even
if a permissive PDF reader would accept it. JPEG validation checks framing and
terminal markers rather than decoding image segments. The authenticated
connector must independently decode or otherwise fully validate content before
storage when semantic media validity matters. It must also preserve the
declared safe media type and serve downloads with X-Content-Type-Options: nosniff rather than inferring an executable type.
There is intentionally no application-level path allowlist: symlinks and
time-of-check/time-of-use races make a lexical prefix check a misleading
security boundary. Use a dedicated OS account, container, or read-only mount
whose readable files are already limited to the intended sharing directory.
The final path component is opened with O_NOFOLLOW; intermediate directory
symlinks retain normal filesystem behavior under this trusted-local-user
boundary.
MCP Resources
| URI | Description |
|---|---|
qurl://links | Current qURL list |
qurl://usage | Current quota and usage information |
MCP Prompts
| Prompt | Description |
|---|---|
secure_a_service | Secure service integration prompt |
audit_links | Link audit prompt |
rotate_access | Access rotation prompt |
Quick Start
1. Install Dependencies
npm install
For a local stdio-only source install, use npm install --omit=optional; this
omits the AWS SDK. HTTP deployments using the DynamoDB credential quota must use
the ordinary install so the optional SDK is packaged.
2. Build
npm run build
3. Start
Local stdio mode:
npm run start
Remote HTTP mode:
npm run start:http
MCP Client Example
If you want to use this server in stdio mode with a local MCP client, use
the configuration from Quick start. Leave
out env to work without an account through the local qURL CLI, or add
"env": { "QURL_API_KEY": "lv_live_xxx" } to use the qURL API with an
account key.
Configuration Files
Copy the tracked examples to create local configuration files:
cp qurl-mcp.config.example.json qurl-mcp.config.json
cp qurl-mcp.http.example.json qurl-mcp.http.json
The local files are gitignored so credentials and machine-specific paths are not committed.
Their responsibilities are:
| File | Purpose |
|---|---|
qurl-mcp.config.json | Shared runtime config used by both stdio and http modes |
qurl-mcp.http.json | HTTP-only server listener and public access config |
qurl-mcp.config.json Reference
Shared Core Settings
| Field | Purpose |
|---|---|
maxUploadFileDataBytes | Limits decoded and local file uploads (default 10mb) |
defaultQurlApiUrl | Base URL of the qURL backend API |
defaultQurlConnectorUrl | Base URL of the upload connector |
linkHosts | Optional exact host allowlist for connector-minted links |
Shared settings have these environment overrides. Environment values take precedence over the shared config file. The process caches resolved shared settings but automatically invalidates that cache when the file metadata or any relevant environment value changes.
| Environment variable | Config field |
|---|---|
MCP_MAX_UPLOAD_FILE_DATA_BYTES | maxUploadFileDataBytes |
QURL_API_URL | defaultQurlApiUrl |
QURL_CONNECTOR_URL | defaultQurlConnectorUrl |
QURL_LINK_HOSTS | linkHosts |
QURL_SMTP_HOST | smtp.host |
QURL_SMTP_PORT | smtp.port |
QURL_SMTP_SECURE | smtp.secure |
QURL_SMTP_USERNAME | smtp.username |
QURL_SMTP_PASSWORD | smtp.password |
QURL_SMTP_FROM_EMAIL | smtp.fromEmail |
QURL_SMTP_FROM_NAME | smtp.fromName |
QURL_SMTP_ALLOWED_RECIPIENTS | smtp.allowedRecipients |
QURL_SMTP_ALLOWED_RECIPIENT_DOMAINS | smtp.allowedRecipientDomains |
QURL_SMTP_MAX_RECIPIENTS_PER_MESSAGE | smtp.maxRecipientsPerMessage |
QURL_SMTP_MAX_RECIPIENTS_PER_HOUR | smtp.maxRecipientsPerHour |
QURL_PUBLIC_VIDEO_FILE_PATH | publicVideo.filePath |
QURL_PUBLIC_VIDEO_TITLE | publicVideo.title |
QURL_PUBLIC_VIDEO_PAGE_PATH | publicVideo.pagePath |
Set QURL_LINK_HOSTS=qurl.link,files.example.com (or linkHosts as a JSON
array) to restrict connector-minted upload links before they are returned or
emailed. Entries are exact ASCII hostnames or IPs, without schemes, ports,
paths, or wildcards. Subdomains need their own entries. The policy checks the
hostname; existing HTTPS and loopback development rules still apply. When neither source has a non-empty list,
custom-domain behavior stays unchanged. A blank environment list falls back to
any file policy; clear the file list to disable that policy. The tracked example
config uses an empty list. Configure all legitimate link hosts before enabling it. A rejected mint may already have created a live
link and stored file; the error reports that state and does not retry the upload.
QURL_API_KEY is intentionally environment-only and has no config-file field.
Prefer QURL_SMTP_PASSWORD for the SMTP secret as well. If smtp.password is
stored in the config file on a POSIX host, restrict that file to owner-only
permissions (for example, chmod 600); startup warns when group/other read bits
are present. This check is intentionally advisory so existing deployments do
not fail after an upgrade, and it is skipped on Windows because POSIX mode bits
are not available there.
Email delivery itself is fail-closed unless at least one exact
smtp.allowedRecipients entry or smtp.allowedRecipientDomains entry is
configured; startup warns when complete SMTP credentials lack that policy.
Raising maxUploadFileDataBytes also raises the HTTP JSON parser's per-request
memory ceiling to roughly 1.5 times that value (up to about 150 MB at the
100 MB maximum), before base64 decoding applies the exact byte cap. Until a
session has completed a successful downstream qURL API call (a completed upload's
link mint counts), its parser ceiling remains at the smaller 10 MB default upload
setting; clients configured for a larger first upload must validate the session
with a small qURL API call or a smaller upload first.
Size the configured maximum and reverse-proxy concurrency limit together.
Set QURL_API_KEY in the environment for stdio mode to use the qURL API.
Without it, stdio mode runs through the local qURL CLI and needs no account
(see Quick start). In HTTP mode, every
client request supplies its own qURL API key as a bearer token.
defaultQurlApiUrl and QURL_API_URL require HTTPS for non-loopback hosts
because qURL API keys and data are bearer-sent to that destination. Plain HTTP
is accepted only for literal loopback development endpoints. Upload connector
URLs follow the same HTTPS-except-loopback rule.
Loopback means 127.0.0.0/8 or ::1; wildcard bind addresses such as
0.0.0.0 and :: are intentionally not accepted as outbound HTTP targets.
Connector destinations are trusted operator configuration rather than caller
input; private addresses and DNS resolution are therefore permitted. Pin the
connector hostname in deployment DNS and do not point it at metadata services.
The caller's qURL bearer credential is forwarded to this host, so treat connector
URL and DNS control as part of the credential trust boundary.
Configure the connector service base URL, not an upload route: qurl-mcp appends
/api/upload to ordinary base paths, accepts that exact endpoint suffix, and
rejects ambiguous upload-like paths such as /upload or /api/upload/v2.
The MCP server performs bounded file-framing checks, not full media parsing;
the connector must independently revalidate uploaded content before storage or
serving, and delivery must retain nosniff behavior as the authoritative type
boundary.
API and connector base URLs that contain embedded credentials, a query string,
or a fragment are now rejected during startup. Deployments that previously used
one of those unusual URL forms must move credentials to QURL_API_KEY and keep
the configured service URL to its origin and optional path prefix.
SMTP Settings
| Field | Purpose |
|---|---|
smtp.host | SMTP server hostname |
smtp.port | SMTP server port |
smtp.secure | true for implicit TLS; false for required STARTTLS |
smtp.username | SMTP login username |
smtp.password | SMTP login password or app-specific code |
smtp.fromEmail | Sender email address |
smtp.fromName | Sender display name |
smtp.allowedRecipients | Optional exact-address allowlist |
smtp.allowedRecipientDomains | Optional exact-domain allowlist (subdomains are not included) |
smtp.maxRecipientsPerMessage | Per-message recipient cap (default 10) |
smtp.maxRecipientsPerHour | Per-qURL-key attempted-recipient cap per fixed hourly window (default 100) |
These settings are used when email delivery is requested by tools such as:
create_qurlmint_linkupload_text_qurlupload_file_qurlupload_file_data_qurl
If either recipient allowlist is configured, only an exact address or domain
match is delivered. If both are empty, the message and hourly caps still apply.
Domain entries are exact: example.com does not implicitly allow
mail.example.com; list each permitted subdomain explicitly.
Addresses and domains are normalized to lowercase NFC/IDNA ASCII form and a
trailing DNS root dot is removed before comparison and delivery.
Each recipient allowlist is limited to 1,000 configured entries. The
per-message recipient cap applies to the complete unique requested fan-out
before allowlist filtering, so blocked addresses cannot be used to submit an
oversized batch.
In HTTP mode, any caller with a valid qURL API key can request a server-side
SMTP delivery. Configure allowedRecipients or allowedRecipientDomains
before enabling SMTP on an Internet-facing HTTP deployment; empty allowlists
permit delivery to any syntactically valid address subject to the quotas.
The SMTP transport uses bounded connection/socket timeouts and is closed after
each delivery batch. Failed SMTP attempts still consume quota—including when a
transient outage results in zero delivered messages—so repeated failures cannot
bypass the abuse limit.
Each delivery request also has a 60-second aggregate deadline. Recipients not
started before that deadline are reported as skipped; provider-side queues are
the supported path for larger or slower fan-out.
Transport encryption is mandatory: smtp.secure: true uses implicit TLS,
while smtp.secure: false requires a successful STARTTLS upgrade. Port 465 is
reserved for implicit TLS and therefore requires smtp.secure: true.
Hourly quota state is maintained per server process: it resets on restart and
is not shared across replicas. Operators running multiple instances should
enforce a corresponding aggregate limit at the SMTP provider or gateway.
The in-process quota is therefore an abuse backstop, not a durable global
safety boundary; restart/scale-out fail-open behavior must be covered by that
provider-side limit.
Tracking fails closed for new principals after 10,000 principals are retained
in one process; existing principals continue to use their current buckets until
expired entries are pruned.
Restrict qURL API-key issuance and monitor new-principal quota-cap rejections:
cycling many valid keys can deliberately hold that shared table at capacity for
up to one quota window.
The quota uses a fixed one-hour window that starts with the first attempted
delivery after the prior window expires.
As with any fixed window, traffic immediately before and after a boundary can
total nearly twice the configured hourly value; use a provider-side sliding or
rolling limit when that boundary burst must be prevented across replicas.
HTTP SMTP delivery is restricted to the operator credential configured in
QURL_API_KEY. A different customer's
valid qURL key does not authorize use of the operator's SMTP account. Without
that explicit operator key, HTTP email delivery is disabled. Stdio retains its
local operator trust model. Recipient allowlists and quotas apply in both modes.
Generated qURL links are included in the plain-text email body. Restrict recipients with the SMTP allowlists and configure transport encryption at the SMTP server/provider when link confidentiality matters.
Prefer environment variables for SMTP credentials and policy:
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, and
QURL_SMTP_MAX_RECIPIENTS_PER_HOUR.
Public Video Page Settings
| Field | Purpose |
|---|---|
publicVideo.title | Title shown on the public video page |
publicVideo.pagePath | Public path of the video playback page |
publicVideo.filePath | Absolute server path of the MP4 file |
When configured, the HTTP server additionally exposes:
- a public video playback page
- a streaming endpoint for the MP4 file
publicVideo.filePath is trusted operator configuration. The final component
must be a non-symlink regular .mp4 file; intermediate directory symlinks keep
normal filesystem resolution and must therefore remain under operator control.
Startup probes this optional asset and warns when it is missing, empty, or not
regular, but intentionally keeps the MCP service and /healthz available. The
video-file route still fails closed with 404 until the asset is corrected.
qurl-mcp.http.json Reference
Use qurl-mcp.http.example.json for local,
stateful development. qurl-mcp.http.stateless.example.json
shows every store and metric field required by a deployed stateless service.
Documentation truncated — see the full README on GitHub.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Rea
Freeby Morluto · Developer Tools
Reverse engineer anything from your terminal or agent with one CLI and MCP server.
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
