Skip to main content
Glama
README.md
# Etchv MCP server

A local stdio [Model Context Protocol](https://modelcontextprotocol.io) server
that lets AI assistants watermark and detect images, PDFs and videos with
[Etchv](https://etchv.com/?utm_source=mcp-local&utm_medium=readme&utm_campaign=integrations).

## Requirements

- Node.js 24 or later
- An Etchv API key with only the [scopes](#tools) you need
- An existing directory for input and output files

## Configuration

| Variable | Required | Description |
| --- | --- | --- |
| `ETCHV_API_KEY` | Yes | Etchv API key. Keep it in your client's secret or env config, never in chat. |
| `ETCHV_FILES_ROOT` | Yes | Absolute path of an existing directory. All input and output files must be inside it. |
| `ETCHV_API_BASE_URL` | No | API origin. Defaults to `https://api.etchv.com`. |
| `ETCHV_LARGE_FILE_THRESHOLD` | No | Files above this many bytes are uploaded once to a signed URL and sent by ID. Defaults to 40 MiB. |

### Claude Code

```sh
claude mcp add etchv --scope user \
  -e ETCHV_API_KEY="$ETCHV_API_KEY" \
  -e ETCHV_FILES_ROOT="$HOME/etchv-media" \
  -- npx -y @etchv-labs/mcp-server@1
```

### Editor plugins

This repository is also a Cursor plugin (`.cursor-plugin/plugin.json` with
`mcp.json`) and a Gemini CLI extension (`gemini-extension.json`). Both run the
same package and need `ETCHV_API_KEY` and `ETCHV_FILES_ROOT`. Gemini CLI asks
for them at install; in Cursor, set them under Plugins, then Configure:

```sh
gemini extensions install https://github.com/etchv-labs/mcp-server
```

### Claude Desktop, Cursor and other `mcpServers` clients

Add this to the client's MCP config (for example `claude_desktop_config.json`
or `~/.cursor/mcp.json`) and restart it:

```json
{
  "mcpServers": {
    "etchv": {
      "command": "npx",
      "args": ["-y", "@etchv-labs/mcp-server@1"],
      "env": {
        "ETCHV_API_KEY": "YOUR_ETCHV_API_KEY",
        "ETCHV_FILES_ROOT": "/absolute/path/to/etchv-media"
      }
    }
  }
}
```

Then ask the assistant to run `check_api_key` to confirm the connection.

## Tools

| Tool | What it does | Key scope |
| --- | --- | --- |
| `check_api_key` | Show organization ID, key ID and scopes; no credits | Any valid key |
| `watermark_media` | Watermark a file, sync or async; spends credits | `watermarks:embed` (`assets:read` for links) |
| `detect_media` | Detect a watermark, sync or async; uses credits | `watermarks:detect` |
| `create_upload` | Create an upload session and return its signed upload URL | `watermarks:embed`, or `watermarks:detect` for `detect` |
| `get_job`, `get_job_result` | Read job status; collect a finished result | Matching embed/detect scope |
| `watermark_batch` | Watermark up to 100 local files in one batch, each with its own data; spends credits | `watermarks:embed` |
| `get_batch`, `download_batch` | Follow a batch; save each result or the batch's zip | `watermarks:embed` |
| `list_assets`, `get_asset`, `download_asset` | List, inspect, download or link to assets | `assets:read` |
| `update_asset` | Rename or replace metadata (version-checked) | `assets:write` |
| `delete_asset` | Delete an asset | `assets:delete`, owner/admin |
| `create_link` | Share a source asset so each recipient opens their own watermarked copy; copies spend credits | `links:write` |
| `list_links`, `get_link` | List links; read one with each recipient's opens and copy | `links:read` |
| `revoke_link` | Stop new opens and downloads of a link | `links:write` |
| `list_storage_destinations`, `list_storage_deliveries`, `get_storage_delivery` | Inspect customer storage | `storage:read` |
| `verify_storage_destination`, `store_asset`, `retry_storage_delivery` | Test a bucket; deliver or retry a result | `storage:write`, owner/admin |
| `list_webhooks`, `list_webhook_deliveries` | Inspect webhook endpoints and deliveries | `webhooks:read` |
| `redeliver_webhook` | Resend an existing event | `webhooks:write`, owner/admin |

Every tool has a title and `readOnlyHint`, `destructiveHint`, `idempotentHint`
and `openWorldHint` annotations.

### Inputs and outputs

`watermark_media` and `detect_media` take the file as exactly one of:

- `input_path`: a local file inside `ETCHV_FILES_ROOT`.
- `input_url`: a public HTTPS URL. The server downloads it and submits it like
  a local file. See [Security](#security) for the checks it applies. The file
  name comes from the `Content-Disposition` header, else the URL path; pass
  `filename` to set it. Its extension must match `media`.
- `upload_id`: an `upl_` ID from `create_upload`. A client that can send files
  itself calls `create_upload` with `kind` (`image`, `document`, `video`, or
  `detect` to check a delivered file), `filename` and the exact `size` in bytes,
  PUTs the bytes to the returned `upload.url` without an API key, then passes
  `upload_id`. An upload is used once and expires after 24 hours.
- `asset_id`: an existing asset, such as a delivered file to check. The server
  downloads it and submits it again. `filename` can override its name here too.

A watermarked file is saved to `output_path` when one is given (sync
`watermark_media`, `get_job_result` and `download_asset`). Without
`output_path`, these tools return the result's `asset_id` and metadata with
`download_url` and `download_expires_at`: a link that works without an API key
for 15 minutes. Anyone with the link can download the file, so share it only
with the person who needs it. Creating a link needs the `assets:read` scope; if
it fails, the error names the asset so you can call `download_asset` once the
key allows it.

Submissions require a stable `idempotency_key`; reuse it when retrying so you
are not charged twice. Image and PDF uploads are limited to 50 MiB and DOCX,
PPTX and video uploads to 20 MiB. A PDF has 1 to 2,000 pages, and its
watermarked copy must fit in 95 MiB with 48 KiB per page, so a 2,000-page PDF
can be up to about 1 MiB. Detection takes the files Etchv delivered: up to 192 MiB
for images, 95 MiB for PDFs and 100 MiB for DOCX, PPTX and video. Files over
40 MiB go through an upload session automatically, and image or document
detection above 95 MiB returns a job receipt to collect with `get_job_result`. Storage destinations and webhook endpoints are created in the
dashboard, not through this server.

`watermark_media` and `detect_media` accept an optional `accelerator` (`cpu`,
the default, or `gpu`). GPU processing requires a Business or Enterprise plan
and costs 3× credits; if no GPU is ready, the job runs on CPU at normal credits.
Results report the hardware actually used as `accelerator`, and `get_job` also
returns `accelerator_requested`. `watermark_media` and `watermark_batch` also
accept `active_content` for PDFs: `keep` (the default) returns scripts and
actions as uploaded, and `remove_auto_run` removes what a PDF viewer runs by
itself, such as open actions, document-level JavaScript and XFA forms. In a
batch, `remove_auto_run` needs every file to be a PDF. When a rate or concurrency limit is reached,
errors include `retry_after_seconds` and point to the plan upgrade page. Trial and
Launch plans run one operation at a time.

### Batches

`watermark_batch` takes up to 100 files, named one of three ways: `input_paths`,
`input_dir` with an optional `glob` (for example `"contracts/*.pdf"`; braces and `..` are refused), or
`items` with their own `data`. A `data_template` fills in each file's data;
string values may use `{filename}`, `{stem}` and `{index}` (the file's 0-based
position). The server creates the batch, uploads each file once to its signed
URL without the API key, and starts it. With `wait: true` it polls at the pace
the API asks for, for up to `wait_seconds` (default 600), sending progress
notifications when the client asks for them, and with `output_dir` saves the
results. Otherwise call `get_batch`, then `download_batch`, which saves each
successful file under its name in the batch zip (`NNN-<name>-watermarked.<ext>`), or the single zip when the
batch was created with `archive: true`, and lists failed items with their error
codes. Each file costs the same credits as a single request, and files that
fail are refunded. Retrying with the same arguments resumes or returns the
same batch; if that batch expired unstarted or its results have expired, the
tool asks for a new `idempotency_key`.

### Hosted mode

The `etchv-mcp` command always runs in local mode. `createServer` in
`src/server.ts` also takes `mode: "hosted"` for a server that runs remotely and
has no access to the user's files. In hosted mode no `filesRoot` is needed;
`input_path`, `output_path` and the other local file arguments are left out of
every tool schema; `watermark_batch` and `download_batch` are not offered;
results are returned as download links; and the server instructions and
`etchv://capabilities` describe that mode.

## Security

- The API key is read only from `ETCHV_API_KEY`, sent only to the configured
  origin, and never appears in tool arguments, results, errors or logs.
- File access is confined to `ETCHV_FILES_ROOT`: traversal and symlinks are
  rejected, and existing files are never overwritten.
- Only HTTPS API origins are accepted (HTTP for localhost only), and API
  redirects are never followed.
- `input_url` must be HTTPS without a user name or password. The host must
  resolve only to public addresses: loopback, private, link-local, carrier-grade
  NAT, multicast, reserved and documentation ranges are refused, including IPv6
  forms that embed such an IPv4 address. The connection uses the checked
  address, at most 3 redirects are followed and each is checked again, the
  download is capped at the operation's upload limit and times out, and the
  API key is never sent to the URL.
- API responses, asset names and metadata are treated as untrusted data, never
  instructions.

## Links

- Guide: https://etchv.com/docs/mcp?utm_source=mcp-local&utm_medium=readme&utm_campaign=integrations
- API reference: https://etchv.com/docs?utm_source=mcp-local&utm_medium=readme&utm_campaign=integrations
- Issues: https://github.com/etchv-labs/mcp-server/issues
- Support: hello@etchv.com

Questions or bug reports: open an issue here or email hello@etchv.com.

## License

[MIT](LICENSE)

TDQS

A3.6/5.0

Scored across 18 tools

Disambiguation4/5

Most tools target a distinct resource+action pair, and descriptions clarify the job-based flow (watermark/detect → get_job → get_job_result). The only mild overlaps are get_job vs get_job_result and the storage delivery list/get pair, but the descriptions make the boundaries readable.

Naming Consistency5/5

Every tool follows a predictable snake_case verb_noun pattern (list_assets, get_asset, download_asset, update_asset, delete_asset, store_asset, redeliver_webhook, etc.). No camelCase or vague verbs break the pattern.

Tool Count4/5

18 tools is slightly heavy but justified: four distinct areas (watermarking jobs, assets, storage destinations/deliveries, webhooks) each need CRUD-ish coverage. Nothing feels gratuitous, though a couple of read tools could conceivably be folded together.

Completeness3/5

Assets cover read/download/update/delete but there is no create/upload operation, and webhooks expose list/redeliver/deliveries but no create or delete (explicitly deferred to the dashboard). These are deliberate gaps but leave agents unable to complete full lifecycle workflows without leaving the tool surface.

Maintenance

ActivityActive
ResponsivenessNo issues