# Veilus (npm · veilus-mcp)

Drive Veilus browser profiles from an AI assistant: profiles, pages, scripts, schedules.

- Trust score: 68/100 (medium)
- Change this week: +3
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-23

## Components

- npm · `veilus-mcp`: 68/100 (this document), [markdown](https://verifymcp.io/servers/veilus-veilus/veilus-mcp.md), [page](https://verifymcp.io/servers/veilus-veilus/veilus-mcp)

## Channel facts

- Registry: `npm`
- Package: `veilus-mcp`
- Version: `0.1.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-10-23.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - No production dependencies, so there is no dependency health to assess.
- **Provenance & Transparency**: 45/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (MIT OR Apache-2.0).
  - Actively maintained (last published 7 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 69/100
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 5054 tokens (~129/item across 39 items; 39 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 23/100
  - Stability observed for 7 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 84/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 53% of tool parameters carry a description.
- **Tool Safety**: 50/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 1 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "unassign_dataset" implies "remove" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - Manipulation not yet verified: none of the 39 captured unit(s) of tool text has been judged yet, so we will not certify text no model has read as clean.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the Veilus MCP server?

Veilus runs locally as an npm package, launched with npx -y veilus-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add veilus-veilus -- npx -y veilus-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "veilus-veilus": {
      "command": "npx",
      "args": [
        "-y",
        "veilus-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "veilus-veilus": {
      "command": "npx",
      "args": [
        "-y",
        "veilus-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add veilus-veilus -- npx -y veilus-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "veilus-veilus": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "veilus-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add veilus-veilus --command npx --arg -y --arg veilus-mcp
```

### Hermes

```yaml
mcp_servers:
  veilus-veilus:
    command: "npx"
    args: ["-y", "veilus-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "veilus-veilus": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "veilus-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add veilus-veilus -t stdio -c npx -a -y veilus-mcp
```

### Other

```json
{
  "mcpServers": {
    "veilus-veilus": {
      "command": "npx",
      "args": [
        "-y",
        "veilus-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-10-23 (score 68, +3)

- [functional improvement] Stability: unverified → 0.23

### 2026-10-16 (score 65)

First indexed and scored.

## MCP tools (39)

### `list_profiles` (~34 tokens)

List Veilus browser profiles, with their running state. A running profile also carries `cdpUrl` (see launch_profile).

### `launch_profile` (~284 tokens)

Launch a profile: waits for its browser to finish starting, then returns `{pid, cdpUrl}`. `cdpUrl` (http://127.0.0.1:<port>) is the profile's own Chromium DevTools endpoint: any CDP client — Playwright `connectOverCDP`, Puppeteer `connect`, browser-use, playwright-mcp — can drive the profile through it with its fingerprint and proxy. It has no authentication: any local process can use it while the profile runs. If the profile pool is already full this fails immediately with a 409 (`REST returned 409 ...`) rather than queuing. With `diagnostic: true` it opens the profile without its fingerprint, to tell whether a site blocks the fingerprint or the proxy.

Input parameters:

- `diagnostic` (boolean): Diagnostic launch: the profile's proxy, but none of its fingerprint, in a fresh temporary browser directory deleted on stop — no cookies or logins, and nothing is written to the profile. The page see…
- `profile_id` (string, required): Profile UUID

### `run_script` (~97 tokens)

Run a script on the given profiles. Returns the run IMMEDIATELY, without waiting for it to finish; its `id` is the run_id for get_run_result.

Input parameters:

- `profile_ids` (array, required): UUIDs of the profiles to run on
- `script_id` (string, required): Script UUID
- `variables` (object): Variables for the script, read in the script as process.env.VEILUS_VAR_<NAME>

### `get_run_result` (~38 tokens)

Result of a run by `run_id` — use after calling run_script.

Input parameters:

- `run_id` (string, required): UUID returned by run_script

### `save_script` (~370 tokens)

Save a Playwright (TypeScript) script into Veilus Flow. Without script_id: create a new script; with script_id: replace its source. Contract - violations are rejected with line numbers: (1) connect with chromium.connectOverCDP(`http://127.0.0.1:${process.env.VEILUS_DEBUG_PORT}`); (2) no chromium.launch/launchPersistentContext; (3) use browser.contexts()[0], not newContext(); (4) no setUserAgent/setViewportSize - identity belongs to the profile; (5) errors must exit non-zero: main().catch((e) => { console.error(e); process.exit(1); }); (6) import only playwright and Node built-ins; (7) end main() with `await browser.close()` - it only disconnects from the profile's browser and lets the script exit; without it the run hangs. Read variables via process.env.VEILUS_VAR_<NAME> (names are UPPER_SNAKE_CASE); print what you need to see with console.log to stdout. A newly saved script is not approved: run_script can run it (on at most 3 profiles), schedules wait until the user approves it in the app. `name` is used only when creating; updating (with script_id) does not rename, and only scripts saved by an agent can be updated. Returns `scriptId` (this script's id — pass as script_id to get_script or to save_script again), `version` and `approved`.

Input parameters:

- `description` (string)
- `name` (string, required)
- `script_id` (string): UUID of the script whose source to replace; omit to create a new script
- `source` (string, required): The full TypeScript source

### `list_scripts` (~89 tokens)

List every script, newest first, without its source: `scriptId`, `name`, `description`, `version`, `mode` (`raw` or `dsl`), `origin` (`mcp` if an agent wrote it, `app` if the user did), `approved` and `updatedAt`. Check here before save_script so you update an existing script instead of saving a duplicate.

### `get_script` (~60 tokens)

Read the source and approval state of a Raw script, to fix it and save_script it again. Returns `scriptId`, `name`, `description`, `version`, `source`, `approved` and `origin`.

Input parameters:

- `script_id` (string, required)

### `compile_script` (~53 tokens)

Compile a drag-and-drop DSL script into runnable code. Does not run it. Agents writing scripts should use save_script instead.

Input parameters:

- `dsl` (object, required): DSL script body
- `target` (string): Compile target

### `create_profiles` (~292 tokens)

Create 1 to 50 new browser profiles in one call. With proxy_pool_id (a static pool, e.g. from import_proxies), each new profile gets the pool's least-loaded slot (free slots first, lowest index on ties) and its timezone and language are generated to match that slot's exit-IP geo; a profile whose slot has no known geo is listed in `failed` and NOT created. Without proxy_pool_id the fingerprint is random. The whole call is rejected, creating nothing, if it would exceed the plan's profile limit. `name_template` replaces {n} with 1, 2, ...; `tags` are tag names. `failed[].index` is that same n. With identity_dataset_id (a fixed dataset) each new profile takes one unassigned row, and the call is refused if count exceeds the unassigned rows; content_dataset_id (a consume dataset) goes to every new profile's Content slot.

Input parameters:

- `content_dataset_id` (string): UUID of a consume dataset
- `count` (integer, required)
- `identity_dataset_id` (string): UUID of a fixed dataset (create_dataset)
- `name_template` (string): Profile name; {n} becomes 1, 2, ...
- `os` (string)
- `proxy_pool_id` (string): UUID of a static proxy pool
- `tags` (array): Tag names

### `import_proxies` (~152 tokens)

Create a static proxy pool from proxy lines (host:port, host:port:user:pass or user:pass@host:port). Prefix a line with socks5:// for a SOCKS5 proxy; http:// or no prefix means HTTP; https:// is rejected (TLS to the proxy is not supported). Bad lines are reported in `rejected` by line number (counting from 1) and do not block good ones; if no line is valid, nothing is created. Returns pool_id for create_profiles or assign_proxy_pool. Passwords are stored but never returned.

Input parameters:

- `lines` (array, required): One proxy per element
- `name` (string, required)
- `type` (string)

### `list_proxy_pools` (~48 tokens)

List proxy pools: `pool_id`, `name`, `kind` (static or rotating), `entry_count` and `countries` detected for their exit IPs. Never returns credentials.

### `get_profile_proxy` (~59 tokens)

Show which proxy a profile goes out through: its pool and slot, the slot's host:port, whether it needs auth, and the slot's detected country and timezone. Never returns the password.

Input parameters:

- `profile_id` (string, required): Profile UUID

### `assign_proxy_pool` (~121 tokens)

Assign a proxy pool to existing profiles, one slot each. Refuses the whole call (nothing assigned) if any profile already has a proxy or pool, listing them; pass force: true to replace. Does NOT change the profiles' timezone: if the new proxy is in another country, launch_profile will be blocked by the geo check. New profiles should get their pool through create_profiles instead.

Input parameters:

- `force` (boolean): Replace an existing proxy. Default false
- `pool_id` (string, required): Proxy pool UUID
- `profile_ids` (array, required)

### `test_proxy` (~66 tokens)

Test every proxy in a pool now: alive or dead, latency, exit IP and its country and timezone. Refreshes the pool's cached geo, which create_profiles uses. A dead proxy can take up to about 10 seconds.

Input parameters:

- `pool_id` (string, required): Proxy pool UUID

### `stop_profile` (~46 tokens)

Stop a profile's browser and release everything its launch held. Stopping a profile that is not running also succeeds. Returns an empty result.

Input parameters:

- `profile_id` (string, required): Profile UUID

### `set_profile_variables` (~159 tokens)

Replace ALL stored variables of one profile with `variables` - not a merge: names you omit are deleted, {} clears them. Names are UPPER_SNAKE_CASE, at most 64 variables, values up to 32 KiB. They are delivered only to runs of APPROVED scripts (run_batch, schedules, and run_script once the user has approved the script) as process.env.VEILUS_VAR_<NAME>; a run_script trial of an unapproved script gets only the variables passed to that call. A variable passed to the run itself wins on the same name. Returns the stored names, never the values.

Input parameters:

- `profile_id` (string, required): Profile UUID
- `variables` (object, required): The complete new set of variables

### `run_batch` (~230 tokens)

Run an APPROVED script on many profiles for real. Returns the run IMMEDIATELY; its `id` is the run_id for get_run_result. Unlike run_script (a trial on at most 3 profiles, no approval needed), the script must have been approved by the user in Veilus → Flow, otherwise the call is refused. A profile id that does not exist is counted as a failed profile of the run, not rejected (unlike create_schedule, which refuses unknown ids with 404). concurrency (default 3, 1-16, capped by get_capacity's `max`) is how many profiles run at once; stagger_ms (default 1500, 0-600000 i.e. 10 minutes) is the pause between launches; use get_capacity to choose.

Input parameters:

- `concurrency` (integer)
- `profile_ids` (array, required)
- `script_id` (string, required): UUID of an approved script
- `stagger_ms` (integer)
- `variables` (object): Variables for this run, read as process.env.VEILUS_VAR_<NAME>

### `create_schedule` (~331 tokens)

Create a schedule that runs an APPROVED script on the given profiles at set times. The script must already be approved by the user in Veilus → Flow: an unapproved script is refused now, not skipped later. schedule_type: interval (interval_minutes, with interval_unit minutes or seconds, at least 5 seconds), daily (daily_hour and daily_minute, the computer's local time), weekly (daily_hour, daily_minute and weekly_day, 0 = Sunday), cron (cron_expr, 5 fields) or once (run_at, a future RFC 3339 time with offset; the schedule runs once and then turns itself off). concurrency (default 1, 1-16) and stagger_ms (default 0, 0-600000 i.e. 10 minutes) apply to every run. Returns the schedule, including nextRunAt.

Input parameters:

- `concurrency` (integer)
- `cron_expr` (string)
- `daily_hour` (integer)
- `daily_minute` (integer)
- `enabled` (boolean)
- `interval_minutes` (integer)
- `interval_unit` (string)
- `name` (string, required)
- `profile_ids` (array, required)
- `run_at` (string): RFC 3339 with offset, e.g. 2026-10-04T09:00:00+07:00; required for once
- `schedule_type` (string, required)
- `script_id` (string, required): UUID of an approved script
- `stagger_ms` (integer)
- `weekly_day` (integer)

### `list_schedules` (~31 tokens)

List every schedule: its script, target profiles, timing, enabled flag, and next and last run times.

### `set_schedule_enabled` (~64 tokens)

Turn a schedule on or off. Turning it on recomputes the next run from now and requires its script to still be approved; turning it off keeps the schedule and its run history.

Input parameters:

- `enabled` (boolean, required)
- `schedule_id` (string, required): Schedule UUID

### `run_schedule_now` (~74 tokens)

Run a schedule once right now, without waiting for its timer. Returns the run IMMEDIATELY; its `id` is the run_id for get_run_result. Refused while the schedule already has a run in progress, or if its script is not approved.

Input parameters:

- `schedule_id` (string, required): Schedule UUID

### `list_runs` (~81 tokens)

List the most recent runs, newest first: status and profile counts, without per-profile detail (get_run_result has that). Without schedule_id it lists runs of every schedule and of run_script and run_batch. limit defaults to 20, at most 100.

Input parameters:

- `limit` (integer)
- `schedule_id` (string): Only this schedule's runs

### `get_capacity` (~42 tokens)

How many browsers are open (active), waiting for a slot (queued) and allowed at once (max) on this machine. Use it to choose run_batch concurrency.

### `navigate` (~104 tokens)

Load a URL in the tab of a RUNNING profile and wait for its load event (up to 15 s); on timeout it PROCEEDS ANYWAY and the result says the page is still loading - then call wait_for_element for what you need before reading the page. The profile must already be open - call launch_profile first; this never opens it.

Input parameters:

- `profile_id` (string, required): UUID of a running profile (launch_profile first)
- `url` (string, required)

### `snapshot` (~170 tokens)

Accessibility snapshot of the current page of a running profile: one line per element with role, name and [id=N]; pass N as node_id to click or type. Ids change when the page changes - take a new snapshot after navigating or clicking. Lists at most 150 elements and 64 KiB; truncated: true means the page has more - use wait_for_element with a CSS selector to reach an element that is not listed. Elements inside same-origin iframes follow a `--- iframe URL ---` line and work with click and type like any other; a cross-origin iframe gets a `(cross-origin …)` line with its URL but its elements are not listed - navigate to that URL to work inside it.

Input parameters:

- `profile_id` (string, required): UUID of a running profile (launch_profile first)

### `click` (~208 tokens)

Click an element of a running profile's page by node_id with a real mouse: the pointer moves to a point inside the element, then presses and releases. Fails if the page changed since the snapshot (take a new one), if the element has no visible box (hidden or zero-size — click its visible label or parent), or if another element covers it (the error names it — close or scroll past it first). A JavaScript alert/confirm/prompt that the action opens is answered at once: `dialog` "dismiss" (default, i.e. Cancel) or "accept" (OK), with `prompt_text` as the typed answer to a prompt; the result says which dialog opened and how it was answered.

Input parameters:

- `dialog` (string)
- `node_id` (integer, required): node_id from snapshot ([id=N]) or wait_for_element
- `profile_id` (string, required): UUID of a running profile (launch_profile first)
- `prompt_text` (string)

### `type` (~123 tokens)

Focus an element by node_id and type text into it, one key event per character (at most 5000 characters). Replaces the field's current content: existing text is removed before typing. If the result notes that the value could not be confirmed (masked, length-limited or auto-formatted fields), verify the field with snapshot or evaluate_js.

Input parameters:

- `node_id` (integer, required): node_id from snapshot ([id=N]) or wait_for_element
- `profile_id` (string, required): UUID of a running profile (launch_profile first)
- `text` (string, required)

### `press_key` (~168 tokens)

Press one named key in a running profile's page: Enter, Tab, Escape, ArrowUp, ArrowDown, ArrowLeft, ArrowRight, Backspace, Delete or Space. If no input is focused, the LAST input on the page is focused first. For text use `type`. A JavaScript alert/confirm/prompt that the action opens is answered at once: `dialog` "dismiss" (default, i.e. Cancel) or "accept" (OK), with `prompt_text` as the typed answer to a prompt; the result says which dialog opened and how it was answered.

Input parameters:

- `dialog` (string)
- `key` (string, required)
- `profile_id` (string, required): UUID of a running profile (launch_profile first)
- `prompt_text` (string)

### `scroll` (~141 tokens)

Scroll a running profile's page by `amount` pixels (1 to 20000) in direction up, down, left or right, with mouse-wheel steps at the pointer's position. Like a real wheel, a scrollable area under the pointer scrolls instead of the page — after a click inside such an area, scroll there, or click outside it first. On a macOS profile the distance varies a few percent around `amount`, like a real wheel; the result reports the pixels actually scrolled.

Input parameters:

- `amount` (integer, required)
- `direction` (string, required)
- `profile_id` (string, required): UUID of a running profile (launch_profile first)

### `evaluate_js` (~275 tokens)

Run a JavaScript expression in a running profile's page and return its value as `text`, JSON-encoded (strings come back quoted); promises are awaited. `undefined` comes back as the text `(undefined)`, not JSON `null`. A thrown error comes back as text starting with [JS Error] — this is a normal call result, not a tool failure. Results over 64 KiB are cut and flagged truncated: true. A synchronous loop is stopped by the PAGE ITSELF after about 9 s; its result also comes back as ordinary [JS Error] text. A promise that never settles instead makes the call itself fail after 10 s with a timeout error. This is NOT a sandbox: it runs with the full rights of the logged-in page (reads cookies, sends requests as the user). A JavaScript alert/confirm/prompt that the action opens is answered at once: `dialog` "dismiss" (default, i.e. Cancel) or "accept" (OK), with `prompt_text` as the typed answer to a prompt; the result says which dialog opened and how it was answered.

Input parameters:

- `dialog` (string)
- `expression` (string, required)
- `profile_id` (string, required): UUID of a running profile (launch_profile first)
- `prompt_text` (string)

### `wait_for_element` (~101 tokens)

Wait until a CSS selector matches an element in a running profile's page (polls every 200 ms; timeout_ms 1 to 30000, default 5000) and return its node_id for click or type. Use after navigate or click to wait for the next page.

Input parameters:

- `profile_id` (string, required): UUID of a running profile (launch_profile first)
- `selector` (string, required): CSS selector
- `timeout_ms` (integer)

### `set_file` (~156 tokens)

Attach a file to a file input (<input type=file>, often shown as a "Choose File" button) of a running profile's page by node_id, instead of clicking it - a click opens the system file picker, which cannot be controlled. Only files inside the app's uploads folder (~/.veilus/data/uploads) are allowed: pass the file name, or an absolute path inside that folder; anything else is refused. Ask the user to copy the file there first.

Input parameters:

- `node_id` (integer, required): node_id from snapshot ([id=N]) or wait_for_element
- `path` (string, required): file name inside ~/.veilus/data/uploads
- `profile_id` (string, required): UUID of a running profile (launch_profile first)

### `create_dataset` (~273 tokens)

Create a dataset: a table stored in Veilus that approved scripts read per profile. mode `fixed`: each profile gets ONE row for good (accounts); its columns arrive as process.env.VEILUS_VAR_<COLUMN>. mode `consume`: each run takes `rows_per_run` (1-50, default 1) unused rows; a failed run gives them back, a successful run marks them used; they arrive as VEILUS_VAR_ROWS (JSON array of {COLUMN: value}) and, when rows_per_run is 1, also one variable per column. VEILUS_VAR_ROW_INDEX is the row's number. Columns are UPPER_SNAKE_CASE; ROWS, ROW_INDEX, PROFILE_ID, RUN_ID and DEBUG_PORT are reserved. Mark a column `secret: true` for passwords: it is stored and given to approved scripts, but no tool ever returns it. Bad rows are reported in `rejected` by row number (from 1). Returns `datasetId`.

Input parameters:

- `columns` (array, required)
- `mode` (string, required)
- `name` (string, required)
- `rows` (array): One object per row, keyed by column name; every value must be a string — convert numbers and dates to text before sending
- `rows_per_run` (integer)

### `append_dataset_rows` (~87 tokens)

Add rows to a dataset, in the same shape as create_dataset's `rows`. Bad rows are reported in `rejected` by row number and do not block good ones.

Input parameters:

- `dataset_id` (string, required): Dataset UUID
- `rows` (array, required): One object per row, keyed by column name; every value must be a string — convert numbers and dates to text before sending

### `list_datasets` (~57 tokens)

List datasets: id, name, mode, columns with their secret flag, rowsPerRun, row counts (total and assigned for fixed; available, reserved and used for consume) and how many profiles use each. Never returns row values.

### `get_dataset_rows` (~97 tokens)

Read a dataset's rows in order: `limit` rows (default 100, at most 500) from `offset`. Each row has its index, its values, and for a fixed dataset the profile it is assigned to, for a consume dataset its state. Secret columns are always left out of `values`.

Input parameters:

- `dataset_id` (string, required): Dataset UUID
- `limit` (integer)
- `offset` (integer)

### `assign_dataset` (~166 tokens)

Assign a dataset to existing profiles. A fixed dataset goes to each profile's Identity slot and gives it one unassigned row (lowest index first); profiles left without a row are named in `withoutRow` and get nothing — never share one account between profiles. A consume dataset goes to the Content slot. Refuses the whole call if a profile already has a different dataset in that slot, unless force: true; refuses column names that also exist in the dataset in the profile's other slot. Every later run of an APPROVED script on those profiles carries the data; schedules and run_batch need no new field.

Input parameters:

- `dataset_id` (string, required): Dataset UUID
- `force` (boolean): Replace a dataset already in the slot. Default false
- `profile_ids` (array, required)

### `unassign_dataset` (~63 tokens)

Remove a dataset from profiles. For a fixed dataset their rows become unassigned again. Profile ids that no longer exist are accepted, so rows held by deleted profiles can be freed.

Input parameters:

- `dataset_id` (string, required): Dataset UUID
- `profile_ids` (array, required)

### `reset_dataset_rows` (~44 tokens)

For a consume dataset: put every used row back to available, so runs take them again. Returns how many rows changed.

Input parameters:

- `dataset_id` (string, required): Dataset UUID

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/veilus-veilus/veilus-mcp#diagnostics

## Score history

- 2026-10-23: 68
- 2026-10-22: 65
- 2026-10-21: 65
- 2026-10-20: 65
- 2026-10-19: 65
- 2026-10-18: 65
- 2026-10-17: 65
- 2026-10-16: 65

## Common questions

### What is the Veilus MCP server?

Veilus is an MCP server listed in the public MCP registry as io.github.veilus/veilus. Drive Veilus browser profiles from an AI assistant: profiles, pages, scripts, schedules. This page covers its npm package (veilus-mcp).

### Is the Veilus MCP server safe to use?

Veilus scores 68 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 23 October 2026. It declares no install or post-install scripts. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Veilus MCP server expose?

Veilus exposes 39 tools: list_profiles, launch_profile, run_script, get_run_result, save_script, and 34 more. Their descriptions and schemas cost roughly 5,054 tokens of context every time the server is loaded.

### Is the Veilus MCP server still maintained?

Veilus is still listed as active in the MCP registry. We last reached this channel on 23 October 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the Veilus MCP server under?

Veilus declares the MIT OR Apache-2.0 licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/veilus-mcp
- Socket report: https://socket.dev/npm/package/veilus-mcp
- Repository: https://github.com/veilus/sdk
- Website: https://docs.veilus.io/reference/mcp/
- Changelog RSS feed: https://verifymcp.io/servers/veilus-veilus/veilus-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/veilus-veilus/veilus-mcp.json
- HTML version of this page: https://verifymcp.io/servers/veilus-veilus/veilus-mcp
