# layervai/qurl-mcp [Health: Active]

**Category:** 🔒 Security  
**Repository:** https://github.com/layervai/qurl-mcp  
**GitHub Stars:** 4  
**npm Downloads (last month):** 2121  
**Views:** 1  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/layervai-qurl-mcp

## Description
Mint, resolve, audit, and rotate expiring scope-limited access links (qURLs) for AI agents — secure URL gateway for the qURL API. 9 tools (create / resolve / list / get / delete / extend / update / mint-link / batch-create), 3 resources, 3 guided prompts. stdio transport, OIDC-attested npm provenance.

## Tools
Capabilities this server exposes over MCP:

- **create_qurl** — Create a qURL — a policy-bound, expiring access link that gates a target URL with optional IP/geo/UA/AI-agent filters and time or session limits. **When to use:** minting a fresh protected access link for share-once or time-limited access (e.g. send a customer a 24-hour download link, gate a doc behind an IP allowlist, distribute a one-time-use credential to a contractor). **When NOT to use:** use `mint_link` when you already have a resource (`r_…`) and just need an additional access token under it — `create_qurl` identifies the resource by target URL and may return an existing same-type resource grouping. Use `batch_create_qurls` to create many in one round-trip. Use `update_qurl` to retag or extend an existing resource without minting a new one. **Behavior:** not idempotent — calling twice produces two distinct qURL tokens, though both may share the same `resource_id` when the target URL groups to an existing same-type resource (this tool doesn't surface the underlying API's `Idempotency-Key` header). The returned `qurl_link` is shown ONCE in this response and is never recoverable through `get_qurl` or `list_qurls`; persist or share it immediately. A returned resource is in `active` status with the policy and per-token limits applied. If `expires_in` is omitted the API defaults to **24h** — do not assume the link is permanent. `max_sessions` is per minted qURL, not resource-wide; set `one_time_use: false` explicitly when you need `max_sessions: 0` to mean unlimited visitors. **Returns:** `{ qurl_id: string (q_…), resource_id: string (r_…), qurl_link: string (shown once), branded_domain?: string, qurl_site: string, expires_at: string (RFC 3339), label?: string, type?: string }`. `qurl_id` is the only `q_…` display ID an agent gets in this response — keep it if you plan a follow-up against `get_qurl`/`update_qurl`/`mint_link` (which accept either prefix). Example: `create_qurl({ target_url: 'https://example.com/private', expires_in: '24h', one_time_use: true, access_policy: { geo_allowlist: ['US'] } })`.
- **resolve_qurl** — Redeem a qURL access token (the `at_` prefix you pulled out of a `qurl_link`) to reveal the underlying URL and obtain a time-bound, IP-bound network access grant. Use this when an agent has been handed an access token and needs to fetch the protected resource — after a successful resolve, requests from `access_grant.src_ip` are permitted to the `target_url` for `access_grant.expires_in` seconds. Use `get_qurl` instead when you have a resource ID (`r_`) or qURL display ID (`q_`) and want admin-side details rather than end-user redemption. **Side-effects:** consumes one use on `one_time_use` tokens, decrements `max_sessions`, and may trip access policies (IP/geo/UA/AI-agent denylists). **`idempotentHint: false`** because one-time-use tokens consume on each call; for non-one-time tokens within an active grant window, repeats are effectively no-ops, but the conservative annotation reflects worst-case behavior.
- **list_qurls** — List qURL resources, paginated and optionally filtered by status, date range, or search text. **When to use:** discovery — finding qURLs by status (e.g. everything still active), auditing date ranges, or full-text search across descriptions and target URLs (via the `q` parameter). Filters AND together (e.g. `status: 'active'` + `expires_before: '2026-05-01T00:00:00Z'` returns active qURLs about to expire). **When NOT to use:** use `get_qurl` instead when you already have a specific resource ID — it returns the same per-resource shape more cheaply and includes the `qurls[]` per-token detail that `list_qurls` omits. Use `resolve_qurl` to actually open access to a target URL. **Behavior:** read-only and idempotent. An empty `data[]` with `meta.has_more: false` means no resource matched the filters (not an error). Pagination is cursor-based: when `meta.has_more` is `true`, pass `meta.next_cursor` as `cursor` on the next call to fetch the following page. Default page size is 20, configurable via `limit` up to 100. By default only `active` qURLs are returned; pass `status: 'revoked'` to see only revoked qURLs or `'active,revoked'` to see both. Sort defaults to `created_at:desc`; override with `sort: 'expires_at:asc'` etc. **Returns:** `{ data: QURL[], meta: { has_more: boolean, next_cursor?: string, page_size?: number, request_id?: string } }` — each `data[]` item is the same stable resource shape returned by `get_qurl` minus per-token detail. Example: `list_qurls({ status: "active", sort: "expires_at:asc", limit: 10 })` returns the 10 active qURLs expiring soonest.
- **get_qurl** — Fetch a single qURL resource by ID and return its current state plus a bounded preview of access tokens. Use this when you have a specific resource ID (r_ prefix) or qURL display ID (q_ prefix) — q_ IDs are auto-resolved to their parent resource. Use `list_qurls` instead when you need to discover qURLs by status, date range, or search query. Use `resolve_qurl` instead when you have an end-user access token (at_ prefix) and need to redeem it for the underlying URL. `qurls[]` is an unordered preview capped by the API at 100 rows and may be omitted on list views, preview lookup failure, or redacted connector-owned resources; use `qurl_count` to detect that more token rows may exist. The one-shot `qurl_link` from creation is never returned here.
- **delete_qurl** — Permanently revoke a qURL — the link and every access token under it stop working immediately. **This action is irreversible.** Use this when you want to cut off access entirely (compromised link, departed user, end-of-engagement). Use `update_qurl` instead when you only need to shorten/extend the expiration, retag, or rename — those preserve the existing access tokens. Use `extend_qurl` when you only need to push the expiration out. **Idempotent:** the API returns 404 for re-deletes, never-existed IDs, and resources owned by another API key (ownership-mismatch is collapsed into 404 server-side to avoid existence disclosure); this tool swallows all three. Branch on `was_already_revoked` to distinguish the no-op case from a successful revoke on this call. When the ID came from user input and ownership matters, call `get_qurl` first — a 200 confirms ownership; a thrown 404 is equally ambiguous on that endpoint too. Returns a confirmation payload. By default the resource is excluded from `list_qurls`; pass `status: "revoked"` to see it.
- **extend_qurl** — Push out the expiration of an active qURL by a relative duration. Convenience wrapper for the most common update — equivalent to `update_qurl({ resource_id, extend_by })`. Use this when the only change you need is more time on the clock. Use `update_qurl` instead when you also need to change tags, description, or set an absolute `expires_at`. Use `delete_qurl` when you want to cut off access entirely. Accepts both `r_` and `q_` IDs (q_ is auto-resolved to its parent resource). **Not idempotent:** calling twice with the same `extend_by` extends the expiration twice. If you need an absolute target, use `update_qurl` with `expires_at` so retries on transient errors don't double-push. Returns the updated resource with the new `expires_at` (same shape as `get_qurl`).
- **update_qurl** — Update a qURL's expiration, tags, description, custom domain, or proxy host-header behavior. The richer alternative to `extend_qurl` — use `update_qurl` whenever you need anything beyond a relative time push. Accepts both `r_` and `q_` IDs for expiration, tags, and description updates (q_ is auto-resolved); custom domain and preserve_host updates require an `r_` resource ID because the qURL API now serves them from `PATCH /v1/resources/{id}`. **Constraints:** `extend_by` and `expires_at` are mutually exclusive; `custom_domain`/`preserve_host` cannot be combined with expiration changes in one call; at least one update field (`extend_by`, `expires_at`, `tags`, `description`, `custom_domain`, `preserve_host`) must be set. **Clearing fields:** pass `description: ""`, `tags: []`, or `custom_domain: ""` to clear those fields explicitly. Use `extend_qurl` when the only change is a relative time push. Use `delete_qurl` when you want to revoke entirely. **Errors:** if the input fails schema refinements (both extend_by + expires_at, or no fields set), the handler returns an `isError: true` content block before any API call. Other API errors throw with the API's `code`/`statusCode`. Returns the updated resource (same shape as `get_qurl`).
- **mint_link** — Mint a fresh access link for an existing qURL resource — same one-shot display semantics as `create_qurl.qurl_link`. Use this to issue additional access links to a resource without creating a brand-new qURL (e.g. a second recipient, a replacement after the original was lost). Use `create_qurl` instead when you want a brand-new resource with its own target_url and policy. Use `update_qurl` when you only want to change expiration/tags/description on the existing resource. Accepts both `r_` and `q_` IDs. **Constraints:** `expires_in` and `expires_at` are mutually exclusive (handler returns an `isError: true` content block before any API call if both are set). If neither expiry field is specified, the API defaults to 24 hours from now. **Output:** the new `qurl_link` is shown ONCE — no subsequent call returns it. Capture `qurl_id` if you need to correlate future access events or update this specific token.
- **batch_create_qurls** — Create up to 100 qURLs in a single request. The single-call alternative to looping `create_qurl` — saves round trips and returns a single envelope of per-item results. **Not transactional:** items succeed or fail independently (see `succeeded`/`failed` counts and per-item `error`). Use this when you need to mint many qURLs at once (e.g. provisioning a vendor list, distributing per-customer share links). Use `create_qurl` for a single resource. **Response shape:** `{ succeeded: number, failed: number, results: BatchItemResult[], request_id?: string }`. Each `results[i]` carries `index` (matching the input position), `success`, plus either `qurl_link` + `resource_id` + `qurl_site` + `expires_at` (success) OR `error: { code, message }` (failure). Successful items may also carry `branded_domain` for custom-domain anchor text. **Partial failure signaling:** the handler sets `isError: true` on the tool response whenever `failed > 0`, so agents can branch without parsing JSON. The HTTP layer also returns 400 when every item fails — that's surfaced through the same shape (read `data.results[*].error`). **One-shot links:** like `create_qurl`, every `qurl_link` in the response is shown ONCE. Don't lose them.
- **revoke_qurl_token** — Revoke one qURL token under a resource without revoking the whole resource. Use this when a single recipient/link should stop working but sibling qURLs on the same `resource_id` must remain active. Use `delete_qurl` instead when you want to revoke the resource and every token under it. **Constraints:** requires the parent `resource_id` (`r_…`) and the token display ID (`q_…`). Re-revoking an inactive token returns an API error rather than being treated as idempotent.
- **update_qurl_token** — Update one qURL token under a resource: expiration, label, access policy, max sessions, or session duration. Use this when you need to change a specific `q_…` token without changing sibling tokens or resource-level metadata. Use `update_qurl` instead for resource-level description/tags/custom-domain changes, and use `revoke_qurl_token` when the token should stop working entirely. **Constraints:** `extend_by` and `expires_at` are mutually exclusive; at least one token update field must be set. Returns the updated token summary.
- **list_qurl_sessions** — List active access sessions for a qURL resource. Use this to inspect who currently has live access before rotating, revoking, or terminating sessions. Use `terminate_qurl_sessions` when active sessions should be ended, and use `get_qurl` when you need token/resource metadata instead of active session state. **Behavior:** read-only and idempotent. Empty `data[]` means no active sessions for the resource.
- **terminate_qurl_sessions** — Terminate active access sessions for a qURL resource. Use this after shortening access, rotating a link, or responding to a suspected leak when already-open sessions should end immediately. Pass `session_id` to terminate one active session; omit it to terminate all active sessions for the resource. Use `list_qurl_sessions` first when you need to inspect current sessions before taking action. **Side-effects:** existing access sessions are closed; qURL tokens themselves are not revoked.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

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

**Requires environment variables:** `QURL_API_KEY` — the values above are empty placeholders; fill in real credentials before running (see the repository for what each one is for).

## Documentation

## What layervai/qurl-mcp does

layervai/qurl-mcp exposes qURL access-link management through the Model Context Protocol. It can create a policy-bound link for a target URL, redeem an access token, inspect resources, issue additional links, and revoke or modify access. Resource-level operations can be addressed with `r_` IDs; token-level operations use `q_` IDs and generally require the parent resource ID.

The server is suited to temporary sharing workflows such as sending a customer a time-limited download URL, granting a contractor one-time access, or placing IP and geographic restrictions around a private endpoint. It also supports session inspection and termination when access needs to be cut off without revoking every token.

## How it works

A qURL resource groups access under a target URL and policy. Creating or minting a link returns a full `qurl_link`, but that link is displayed only in the creation response. Agents should persist or deliver it immediately; later resource and token lookups do not recover the original link.

`resolve_qurl` accepts the `at_` access token extracted from a link. A successful resolution reveals the target URL and supplies a temporary, source-IP-bound network grant. Depending on the token policy, resolution can consume a one-time use, reduce the remaining session allowance, or be rejected by access filters.

Resource changes include expiration, tags, descriptions, custom domains, and host-header behavior. Token-specific changes cover expiration, labels, policies, session limits, and session duration. Batch creation accepts up to 100 items, with each item succeeding or failing independently.

## Setup and configuration

The package runs as a local stdio MCP server. In no-account local mode, it invokes the qURL CLI installed on the same machine; the first publish creates that machine's identity. Node.js 22.12 or later is required for the MCP server. Account-backed operation uses `QURL_API_KEY` and the qURL API, while authenticated remote HTTP operation is also supported.

The repository describes use with Claude Desktop, Claude Code, Cursor, and Codex. Configure the server in the selected MCP client and ensure the chosen transport matches the client and available tool set.

## Tools and capabilities

- Create single links with `create_qurl`, or issue up to 100 links using `batch_create_qurls`.
- Redeem access tokens with `resolve_qurl`.
- Discover resources with cursor-based, filtered `list_qurls` queries and inspect one resource with `get_qurl`.
- Extend, update, or permanently revoke resources with `extend_qurl`, `update_qurl`, and `delete_qurl`.
- Issue another link for an existing resource with `mint_link`.
- Revoke or modify an individual token with `revoke_qurl_token` and `update_qurl_token`.
- Inspect active sessions with `list_qurl_sessions` and terminate one or all sessions with `terminate_qurl_sessions`.

## Limitations and notes

Creation and link minting are not permanent by default: when no expiry is supplied, the API uses 24 hours. Returned links are one-shot display values. `extend_qurl` is not idempotent, so retrying it can extend the same resource more than once; use an absolute `expires_at` through `update_qurl` when retry-safe behavior matters.

Deleting a resource is irreversible and stops every token beneath it. To disable one recipient while preserving sibling links, revoke the individual token instead. Session termination closes active sessions but does not revoke the underlying tokens. Batch operations are non-transactional, so callers must inspect per-item results and handle partial failures.

_Full upstream README: https://allmcps.com/mcp/layervai-qurl-mcp/readme_

