@etchv-labs/mcp-server 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -231
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,43 +1,36 @@
|
|
|
1
1
|
# Etchv MCP server
|
|
2
2
|
|
|
3
|
-
[
|
|
4
|
-
|
|
3
|
+
A local stdio [Model Context Protocol](https://modelcontextprotocol.io) server
|
|
4
|
+
that lets AI assistants watermark and detect images, PDFs and videos with
|
|
5
|
+
[Etchv](https://etchv.com).
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
[Etchv API](https://etchv.com/docs). Supports synchronous results and asynchronous
|
|
8
|
-
jobs, asset management, customer storage destinations and webhook deliveries.
|
|
7
|
+
## Requirements
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
- Node.js 24 or later
|
|
10
|
+
- An Etchv API key with only the [scopes](#tools) you need
|
|
11
|
+
- An existing directory for input and output files
|
|
13
12
|
|
|
14
13
|
## Configuration
|
|
15
14
|
|
|
16
15
|
| Variable | Required | Description |
|
|
17
16
|
| --- | --- | --- |
|
|
18
|
-
| `ETCHV_API_KEY` | Yes | Etchv API key.
|
|
17
|
+
| `ETCHV_API_KEY` | Yes | Etchv API key. Keep it in your client's secret or env config, never in chat. |
|
|
19
18
|
| `ETCHV_FILES_ROOT` | Yes | Absolute path of an existing directory. All input and output files must be inside it. |
|
|
20
19
|
| `ETCHV_API_BASE_URL` | No | API origin. Defaults to `https://api.etchv.com`. |
|
|
21
20
|
|
|
22
|
-
Create the media directory first. Store the API key in your client's secret or
|
|
23
|
-
environment configuration; do not paste it into a conversation or commit it.
|
|
24
|
-
The examples pin the major version with `@etchv-labs/mcp-server@1`.
|
|
25
|
-
|
|
26
21
|
### Claude Code
|
|
27
22
|
|
|
28
23
|
```sh
|
|
29
|
-
export ETCHV_API_KEY=... # or load it from your secret manager
|
|
30
24
|
claude mcp add etchv --scope user \
|
|
31
25
|
-e ETCHV_API_KEY="$ETCHV_API_KEY" \
|
|
32
26
|
-e ETCHV_FILES_ROOT="$HOME/etchv-media" \
|
|
33
27
|
-- npx -y @etchv-labs/mcp-server@1
|
|
34
28
|
```
|
|
35
29
|
|
|
36
|
-
### Claude Desktop, Cursor
|
|
30
|
+
### Claude Desktop, Cursor and other `mcpServers` clients
|
|
37
31
|
|
|
38
|
-
Add this
|
|
39
|
-
|
|
40
|
-
the client:
|
|
32
|
+
Add this to the client's MCP config (for example `claude_desktop_config.json`
|
|
33
|
+
or `~/.cursor/mcp.json`) and restart it:
|
|
41
34
|
|
|
42
35
|
```json
|
|
43
36
|
{
|
|
@@ -54,229 +47,47 @@ the client:
|
|
|
54
47
|
}
|
|
55
48
|
```
|
|
56
49
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
### VS Code
|
|
60
|
-
|
|
61
|
-
Add to `.vscode/mcp.json`. VS Code prompts for the key and stores it securely:
|
|
62
|
-
|
|
63
|
-
```json
|
|
64
|
-
{
|
|
65
|
-
"inputs": [
|
|
66
|
-
{
|
|
67
|
-
"type": "promptString",
|
|
68
|
-
"id": "etchv-api-key",
|
|
69
|
-
"description": "Etchv API key",
|
|
70
|
-
"password": true
|
|
71
|
-
}
|
|
72
|
-
],
|
|
73
|
-
"servers": {
|
|
74
|
-
"etchv": {
|
|
75
|
-
"type": "stdio",
|
|
76
|
-
"command": "npx",
|
|
77
|
-
"args": ["-y", "@etchv-labs/mcp-server@1"],
|
|
78
|
-
"env": {
|
|
79
|
-
"ETCHV_API_KEY": "${input:etchv-api-key}",
|
|
80
|
-
"ETCHV_FILES_ROOT": "/absolute/path/to/etchv-media"
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Global install
|
|
88
|
-
|
|
89
|
-
`npm install -g @etchv-labs/mcp-server@1` installs the `etchv-mcp` command. Use
|
|
90
|
-
`etchv-mcp` as the configured command with the same environment variables.
|
|
91
|
-
|
|
92
|
-
Ask the assistant to run `check_api_key` to confirm the connection. It returns
|
|
93
|
-
your organization ID, key ID and granted scopes without spending credits.
|
|
94
|
-
|
|
95
|
-
## Try it
|
|
96
|
-
|
|
97
|
-
Put `photo.jpg` inside your configured media directory, then ask your assistant:
|
|
98
|
-
|
|
99
|
-
> Watermark photo.jpg for recipient delivery-42 using Etchv. Use an async job,
|
|
100
|
-
> collect the result as photo-watermarked.jpg, then detect its watermark.
|
|
101
|
-
|
|
102
|
-
The assistant can read `etchv://capabilities` for format and workflow guidance.
|
|
103
|
-
To watermark directly, call `watermark_media` with:
|
|
104
|
-
|
|
105
|
-
```json
|
|
106
|
-
{
|
|
107
|
-
"media": "images",
|
|
108
|
-
"input_path": "photo.jpg",
|
|
109
|
-
"data": { "recipient": "delivery-42" },
|
|
110
|
-
"mode": "async",
|
|
111
|
-
"idempotency_key": "delivery-42-photo-v1"
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
The result includes `request_id`. Check `get_job` with `operation: "embed"`, then
|
|
116
|
-
call `get_job_result` with that request ID, operation and
|
|
117
|
-
`output_path: "photo-watermarked.jpg"`. Pending results return a 202 receipt;
|
|
118
|
-
call again later. Use the same pattern with `operation: "detect"` for an async
|
|
119
|
-
detection result. Detection returns JSON and does not accept an output path.
|
|
120
|
-
|
|
121
|
-
For **sync**, set `mode: "sync"` and provide `output_path` when watermarking.
|
|
122
|
-
The API waits for a bounded period. It can still return a 202 receipt if processing
|
|
123
|
-
continues; collect it through the job tools. Async is the MCP default for every
|
|
124
|
-
media type. There is no automatic polling or background retry loop in the server.
|
|
125
|
-
|
|
126
|
-
Every submission requires a stable `idempotency_key`. Reuse it for the same input,
|
|
127
|
-
data and options after an uncertain failure. Do not generate another key on a
|
|
128
|
-
retry: that can create another billable operation. Results include the key and,
|
|
129
|
-
when supplied by the API, a request ID. A client cancellation or timeout does not
|
|
130
|
-
cancel an accepted API job. API failures set MCP `isError` with an HTTP status and
|
|
131
|
-
safe message; retry delays are returned when available.
|
|
132
|
-
|
|
133
|
-
## Formats and credits
|
|
134
|
-
|
|
135
|
-
| Media value | Formats | Preservation |
|
|
136
|
-
| --- | --- | --- |
|
|
137
|
-
| `images` | JPEG, PNG, APNG, TIFF, PSD, PSB, GIF, BMP, PPM, WebP | Original format, supported animation, pages and layers |
|
|
138
|
-
| `documents` | PDF | Selectable text and vector content |
|
|
139
|
-
| `videos` | MP4, MOV with supported H.264 encoding | Container and supported audio; audio is not watermarked |
|
|
140
|
-
|
|
141
|
-
Format availability follows your plan. The server does not bypass API validation
|
|
142
|
-
or media limits. Uploads are limited to 20 MiB and MCP file downloads to 512 MiB.
|
|
143
|
-
Watermarking uses one credit per image or PDF file, or per started video minute.
|
|
144
|
-
Detection also uses API credits. See [limits](https://etchv.com/docs/api/errors).
|
|
145
|
-
|
|
146
|
-
Watermarks encode the digest of the provided JSON. Detection recovers that digest,
|
|
147
|
-
not the original data. Test your compression and transformation workflow before
|
|
148
|
-
relying on detection; a recovered identifier is not proof of who shared a file.
|
|
149
|
-
|
|
150
|
-
## Tools and key scopes
|
|
151
|
-
|
|
152
|
-
| Tools | Required scope |
|
|
153
|
-
| --- | --- |
|
|
154
|
-
| `check_api_key` | Any valid key |
|
|
155
|
-
| `watermark_media` | `watermarks:embed` |
|
|
156
|
-
| `detect_media` | `watermarks:detect` |
|
|
157
|
-
| `get_job`, `get_job_result` | Corresponding embed or detect scope |
|
|
158
|
-
| `list_assets`, `get_asset`, `download_asset` | `assets:read` |
|
|
159
|
-
| `update_asset` | `assets:write` |
|
|
160
|
-
| `delete_asset` | `assets:delete`, owner/admin |
|
|
161
|
-
| `list_storage_destinations`, `list_storage_deliveries`, `get_storage_delivery` | `storage:read` |
|
|
162
|
-
| `verify_storage_destination`, `store_asset`, `retry_storage_delivery` | `storage:write`, owner/admin |
|
|
163
|
-
| `list_webhooks`, `list_webhook_deliveries` | `webhooks:read` |
|
|
164
|
-
| `redeliver_webhook` | `webhooks:write`, owner/admin |
|
|
165
|
-
|
|
166
|
-
Give the key only the scopes you need. Your MCP client's approval settings govern
|
|
167
|
-
billable and destructive tools; MCP annotations describe their effects. The API
|
|
168
|
-
always enforces organization isolation, scopes, roles and plan access.
|
|
169
|
-
|
|
170
|
-
Asset listing supports cursor pagination and media/kind/watermark filters.
|
|
171
|
-
Updates require the current asset `version` to prevent lost edits; `metadata`
|
|
172
|
-
replaces the whole object, and null clears it. Downloads create new local files
|
|
173
|
-
and return paths, byte counts and SHA-256 hashes, never media bytes in chat.
|
|
174
|
-
|
|
175
|
-
## Storage and webhooks
|
|
176
|
-
|
|
177
|
-
Etchv stores uploads and watermarked results automatically. Job results remain
|
|
178
|
-
available for 24 hours; Etchv asset downloads for 30 days, with asset records
|
|
179
|
-
retained until deleted. No customer bucket is required.
|
|
180
|
-
|
|
181
|
-
Alternatively, configure and verify your own S3, Google Cloud Storage or Azure
|
|
182
|
-
Blob Storage destination in the dashboard. Select its `storage_destination_id`
|
|
183
|
-
on `watermark_media`, optionally with a relative `storage_key`. The watermarked
|
|
184
|
-
result is stored in that bucket instead, and its retention is yours to control.
|
|
185
|
-
Use `store_asset` to choose a destination for an existing watermarked asset.
|
|
186
|
-
After successful delivery, Etchv removes its staged output; asset downloads read
|
|
187
|
-
from your bucket. Deleting an asset does not delete objects in your bucket.
|
|
188
|
-
|
|
189
|
-
Configure webhook endpoints and retain their signing secrets through the
|
|
190
|
-
dashboard. Pass an existing `webhook_id` on an **async** submission, then inspect
|
|
191
|
-
or redeliver events with the webhook tools. Redelivery may trigger your receiver's
|
|
192
|
-
automation again. Credential and endpoint creation is deliberately handled in the
|
|
193
|
-
dashboard; this server does not accept cloud credentials or return signing secrets.
|
|
194
|
-
|
|
195
|
-
## File access and runtime
|
|
196
|
-
|
|
197
|
-
`ETCHV_FILES_ROOT` is required and must name an existing absolute directory. Paths
|
|
198
|
-
may be relative to it or absolute within it. Symlink paths and traversal outside
|
|
199
|
-
the root are rejected. Output parents must already exist; existing files are never
|
|
200
|
-
overwritten. Use a dedicated directory that is not modified by untrusted local
|
|
201
|
-
processes while the server runs. This is an application-level boundary, not an OS
|
|
202
|
-
sandbox. Upload only files you intend to send to Etchv.
|
|
203
|
-
|
|
204
|
-
API responses must start within 45 seconds; file downloads have a 10-minute
|
|
205
|
-
deadline. At most four tool calls run concurrently.
|
|
206
|
-
Redirects are never followed. Optional `ETCHV_API_BASE_URL` must be an HTTPS origin;
|
|
207
|
-
HTTP is accepted only for localhost development. Never point it at an untrusted
|
|
208
|
-
host: it receives your key and uploads. stdout is reserved for MCP protocol traffic.
|
|
209
|
-
Asset metadata and other API content are data, never instructions to an assistant.
|
|
50
|
+
Then ask the assistant to run `check_api_key` to confirm the connection.
|
|
210
51
|
|
|
211
52
|
## Tools
|
|
212
53
|
|
|
213
|
-
| Tool |
|
|
214
|
-
| --- | --- |
|
|
215
|
-
| `check_api_key` |
|
|
216
|
-
| `watermark_media` |
|
|
217
|
-
| `detect_media` |
|
|
218
|
-
| `get_job` | Read
|
|
219
|
-
| `
|
|
220
|
-
| `
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
225
|
-
| `
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
`idempotentHint`, `openWorldHint`) so clients can require approval for billable,
|
|
231
|
-
destructive or other write operations. The `etchv://capabilities` resource
|
|
232
|
-
describes formats, modes, billing and file handling for the assistant.
|
|
54
|
+
| Tool | What it does | Key scope |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| `check_api_key` | Show organization ID, key ID and scopes; no credits | Any valid key |
|
|
57
|
+
| `watermark_media` | Watermark a local file, sync or async; spends credits | `watermarks:embed` |
|
|
58
|
+
| `detect_media` | Detect a watermark, sync or async; uses credits | `watermarks:detect` |
|
|
59
|
+
| `get_job`, `get_job_result` | Read job status; collect a finished result | Matching embed/detect scope |
|
|
60
|
+
| `list_assets`, `get_asset`, `download_asset` | List, inspect and download assets | `assets:read` |
|
|
61
|
+
| `update_asset` | Rename or replace metadata (version-checked) | `assets:write` |
|
|
62
|
+
| `delete_asset` | Delete an asset | `assets:delete`, owner/admin |
|
|
63
|
+
| `list_storage_destinations`, `list_storage_deliveries`, `get_storage_delivery` | Inspect customer storage | `storage:read` |
|
|
64
|
+
| `verify_storage_destination`, `store_asset`, `retry_storage_delivery` | Test a bucket; deliver or retry a result | `storage:write`, owner/admin |
|
|
65
|
+
| `list_webhooks`, `list_webhook_deliveries` | Inspect webhook endpoints and deliveries | `webhooks:read` |
|
|
66
|
+
| `redeliver_webhook` | Resend an existing event | `webhooks:write`, owner/admin |
|
|
67
|
+
|
|
68
|
+
Submissions require a stable `idempotency_key`; reuse it when retrying so you
|
|
69
|
+
are not charged twice. Uploads are limited to 20 MiB. Storage destinations and
|
|
70
|
+
webhook endpoints are created in the dashboard, not through this server.
|
|
233
71
|
|
|
234
72
|
## Security
|
|
235
73
|
|
|
236
|
-
- The API key is read only from `ETCHV_API_KEY
|
|
237
|
-
|
|
238
|
-
results, errors or logs. Tools never accept or return cloud credentials or
|
|
239
|
-
webhook signing secrets; manage those in the dashboard.
|
|
240
|
-
- stdout carries only MCP protocol messages. Startup errors go to stderr as fixed
|
|
241
|
-
messages without secrets.
|
|
74
|
+
- The API key is read only from `ETCHV_API_KEY`, sent only to the configured
|
|
75
|
+
origin, and never appears in tool arguments, results, errors or logs.
|
|
242
76
|
- File access is confined to `ETCHV_FILES_ROOT`: traversal and symlinks are
|
|
243
|
-
rejected,
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
77
|
+
rejected, and existing files are never overwritten.
|
|
78
|
+
- Only HTTPS origins are accepted (HTTP for localhost only), and redirects are
|
|
79
|
+
never followed.
|
|
80
|
+
- API responses, asset names and metadata are treated as untrusted data, never
|
|
81
|
+
instructions.
|
|
248
82
|
|
|
249
|
-
##
|
|
250
|
-
|
|
251
|
-
```sh
|
|
252
|
-
npm ci
|
|
253
|
-
npm run lint
|
|
254
|
-
npm run typecheck
|
|
255
|
-
npm test
|
|
256
|
-
npm run build
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
Native TypeScript tests exercise the compiled server over stdio with the official
|
|
260
|
-
MCP client, covering all media routes, file fidelity, job continuation, storage,
|
|
261
|
-
webhooks, pagination, error handling, timeouts and file access. CI runs on Node 24
|
|
262
|
-
on Linux, macOS and Windows. The optional live suite uses small synthetic media
|
|
263
|
-
fixtures:
|
|
264
|
-
|
|
265
|
-
```sh
|
|
266
|
-
# Set ETCHV_INTEGRATION_API_KEY securely; requires embed, detect, assets:read,
|
|
267
|
-
# assets:delete and owner/admin for cleanup. Uses real API credits.
|
|
268
|
-
npm run test:live
|
|
269
|
-
```
|
|
83
|
+
## Links
|
|
270
84
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
85
|
+
- Guide: https://etchv.com/docs/mcp
|
|
86
|
+
- API reference: https://etchv.com/docs
|
|
87
|
+
- Issues: https://github.com/etchv-labs/mcp-server/issues
|
|
88
|
+
- Support: hello@etchv.com
|
|
274
89
|
|
|
275
|
-
|
|
276
|
-
monorepo, with independent history. Contributions are reviewed and incorporated
|
|
277
|
-
there before publication. Private service code, credentials and customer fixtures
|
|
278
|
-
are not included. Report issues at
|
|
279
|
-
[github.com/etchv-labs/mcp-server/issues](https://github.com/etchv-labs/mcp-server/issues).
|
|
90
|
+
Questions or bug reports: open an issue here or email hello@etchv.com.
|
|
280
91
|
|
|
281
92
|
## License
|
|
282
93
|
|
package/dist/version.js
CHANGED