katto-mcp 0.5.8 → 0.5.10
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 +5 -3
- package/SKILL.md +14 -10
- package/index.mjs +15 -6
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
**The AI video clipping MCP server.** Turn long videos into scored, captioned, vertical 9:16 short clips from any MCP client (Claude, Cursor, Claude Code, ChatGPT, VS Code, and other conformant clients). Drop in a YouTube link, a podcast, or a Twitch VOD and get publish-ready shorts back through natural conversation.
|
|
9
9
|
|
|
10
|
-
Powered by [Katto](https://katto.tech), an AI video clipper that turns long-form video (podcasts, interviews, streams, webinars) into short-form clips for TikTok, Reels and YouTube Shorts. Katto is a flat-priced, no-credits alternative to tools like OpusClip:
|
|
10
|
+
Powered by [Katto](https://katto.tech), an AI video clipper that turns long-form video (podcasts, interviews, streams, webinars) into short-form clips for TikTok, Reels and YouTube Shorts. Katto is a flat-priced, no-credits alternative to tools like OpusClip: the Creator plan's 25-video monthly quota covers videos up to 90 minutes each. API and MCP access is included on **every plan, including the free one** (2 videos/month), not gated behind a paid or enterprise tier.
|
|
11
11
|
|
|
12
12
|
## What it does
|
|
13
13
|
|
|
@@ -65,6 +65,8 @@ claude mcp add --transport http katto https://mcp.katto.tech/mcp
|
|
|
65
65
|
|
|
66
66
|
## Tools
|
|
67
67
|
|
|
68
|
+
**16 tools**, in three groups: six for clipping, three for editing, seven for account and reference.
|
|
69
|
+
|
|
68
70
|
Clipping:
|
|
69
71
|
- **`katto_create_clip_job(url, config?)`** clip a long video (YouTube, Twitch, Vimeo, Rumble, Zoom, Dailymotion). Returns a job id.
|
|
70
72
|
- **`katto_get_job(id)`** poll until `status` is `completed`; `clips` holds the finished MP4 + caption (SRT) urls.
|
|
@@ -87,7 +89,7 @@ Account and reference:
|
|
|
87
89
|
- **`katto_list_clip_lengths()`** the valid target clip-length buckets.
|
|
88
90
|
- **`katto_list_caption_styles()`** the caption-style presets you can pass as `caption_style` when creating a job or re-rendering a clip.
|
|
89
91
|
|
|
90
|
-
Jobs draw from your Katto plan's monthly video quota (25 on Creator, 2 on Free), videos up to 90 minutes. The underlying REST API also supports `Idempotency-Key` safe retries, read-only scoped keys, and signed HMAC webhooks. See the [docs](https://katto.tech/docs/api).
|
|
92
|
+
Jobs draw from your Katto plan's monthly video quota (25 on Creator, 10 on Solo, 2 on Free), with source videos up to 90, 60 and 30 minutes respectively. The underlying REST API also supports `Idempotency-Key` safe retries, read-only scoped keys, and signed HMAC webhooks. See the [docs](https://katto.tech/docs/api).
|
|
91
93
|
|
|
92
94
|
## Env
|
|
93
95
|
|
|
@@ -108,7 +110,7 @@ Full docs: **[katto.tech/docs/api](https://katto.tech/docs/api)**
|
|
|
108
110
|
|
|
109
111
|
- npm: [katto-mcp](https://www.npmjs.com/package/katto-mcp)
|
|
110
112
|
- GitHub: [miracleweasel/katto-mcp](https://github.com/miracleweasel/katto-mcp)
|
|
111
|
-
- [mcp.so](https://mcp.so/servers/katto) · [Smithery](https://smithery.ai/
|
|
113
|
+
- [mcp.so](https://mcp.so/servers/katto) · [Smithery](https://smithery.ai/servers/dev-4lue/katto-mcp) · [Glama](https://glama.ai/mcp/servers/@miracleweasel/katto-mcp)
|
|
112
114
|
- Official MCP Registry: `io.github.miracleweasel/katto-mcp`
|
|
113
115
|
|
|
114
116
|
## Public Discovery
|
package/SKILL.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: katto-clip
|
|
3
|
-
description: Turn a long video (a YouTube / Twitch / Vimeo / Rumble / Zoom / Dailymotion link
|
|
3
|
+
description: Turn a long video (a YouTube / Twitch / Vimeo / Rumble / Zoom / Dailymotion link) into scored, captioned, 9:16 short clips using Katto's cloud pipeline. Use when the user wants to clip, repurpose, or make shorts from long-form video, or to dub / re-render an existing clip.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Katto clipping skill
|
|
7
7
|
|
|
8
8
|
Katto runs the whole pipeline in its cloud — download → transcript → moment scoring →
|
|
9
|
-
cut → captions → reframe → optional dub
|
|
10
|
-
the
|
|
9
|
+
cut → captions → reframe → optional dub. You trigger it; nothing renders on the local
|
|
10
|
+
machine. Publishing to social platforms is done in the Katto app: no tool here publishes.
|
|
11
|
+
Local files go through the REST upload API (`POST /v1/uploads`), not through these tools.
|
|
11
12
|
|
|
12
13
|
## Connect
|
|
13
14
|
- **Hosted MCP (recommended, zero-install):** point the client at `https://mcp.katto.tech/mcp`
|
|
@@ -17,8 +18,9 @@ the local machine.
|
|
|
17
18
|
|
|
18
19
|
## Core flow
|
|
19
20
|
1. `katto_create_clip_job({ url, config? })` → returns a `job_id`. Spends **1 video** from the
|
|
20
|
-
monthly quota. Pass
|
|
21
|
-
|
|
21
|
+
monthly quota. Pass `idempotency_key` (e.g. a UUID) when you might retry, so a retry never
|
|
22
|
+
double-spends; pass `webhook_url` to be notified instead of polling.
|
|
23
|
+
2. Poll `katto_get_job({ id })` every 30–60 s until `status` is `"completed"`.
|
|
22
24
|
3. `katto_get_clips({ id })` → finished clips (MP4 url + SRT + title + score 0–100).
|
|
23
25
|
|
|
24
26
|
## Editing — never counts against quota
|
|
@@ -31,15 +33,17 @@ the local machine.
|
|
|
31
33
|
- `katto_get_transcript` (timestamped), `katto_get_brand_kit`, `katto_get_webhook_secret` (treat as a credential).
|
|
32
34
|
|
|
33
35
|
## Rules of the road
|
|
34
|
-
- **One quota**, shared with the app
|
|
35
|
-
Re-renders are free; dubbing is
|
|
36
|
-
-
|
|
36
|
+
- **One quota**, shared with the app and the API: Free = 2 videos/month (≤30 min each),
|
|
37
|
+
Solo = 10 (≤60 min), Creator = 25 (≤90 min). Re-renders are free; dubbing is Creator only.
|
|
38
|
+
- If a call fails on quota, tell the user and give them the upgrade link included in the error.
|
|
39
|
+
- Captions: **99 languages** (word-timed in 41, sentence-level in the rest). Dubbing: **19**.
|
|
37
40
|
- **Don't invent timings.** The only measured figure is ~5 minutes for 8 clips on a 20-minute video;
|
|
38
41
|
longer sources take longer. Say that, not a number you'd like.
|
|
39
42
|
|
|
40
43
|
## Example
|
|
41
|
-
User: *"Clip
|
|
42
|
-
→ `katto_create_clip_job(url,
|
|
44
|
+
User: *"Clip this podcast, then dub clip 2 in Spanish."*
|
|
45
|
+
→ `katto_create_clip_job({ url, idempotency_key })` → poll `katto_get_job` → `katto_get_clips`
|
|
46
|
+
(Katto decides how many clips the video holds: there is no clip-count parameter.)
|
|
43
47
|
→ `katto_dub_clip(id, 2, ["es"])` → `katto_get_rerender`.
|
|
44
48
|
|
|
45
49
|
Full reference: https://katto.tech/docs/api · OpenAPI: https://katto.tech/openapi.json
|
package/index.mjs
CHANGED
|
@@ -57,14 +57,16 @@ const TOOLS = [
|
|
|
57
57
|
type: "object",
|
|
58
58
|
properties: {
|
|
59
59
|
url: { type: "string", description: "Public video URL to clip." },
|
|
60
|
+
webhook_url: { type: "string", description: "Optional https URL that receives a signed POST when the job finishes, so you do not have to poll. Verify the signature with the secret from katto_get_webhook_secret. Private and local addresses are refused." },
|
|
61
|
+
idempotency_key: { type: "string", description: "Optional unique string (max 255 chars). Retrying with the same key returns the original job instead of creating a second one and spending another video. Use one per intended job, e.g. a UUID." },
|
|
60
62
|
config: {
|
|
61
63
|
type: "object",
|
|
62
64
|
description: "Optional pre-clip settings.",
|
|
63
65
|
properties: {
|
|
64
66
|
genre: { type: "string", description: "e.g. podcast, gaming, sports, interview" },
|
|
65
|
-
clipLength: { type: "string", enum: ["lt30", "30_60", "60_90", "90_180"] },
|
|
66
|
-
customPrompt: { type: "string" },
|
|
67
|
-
topics: { type: "string" },
|
|
67
|
+
clipLength: { type: "string", enum: ["lt30", "30_60", "60_90", "90_180"], description: "Target length band for the clips, in seconds. Guides candidate selection by setting a minimum and maximum duration; it is a preference, not a hard cut. Omit to let the model decide." },
|
|
68
|
+
customPrompt: { type: "string", description: "Free-text hint about what matters in this video. Its keywords nudge selection toward matching moments. It does not filter: a clip that matches nothing can still be selected if it scores well." },
|
|
69
|
+
topics: { type: "string", description: "Comma-separated topics to favour. Merged with customPrompt into a single keyword hint, so both bias the ranking rather than restrict it." },
|
|
68
70
|
},
|
|
69
71
|
},
|
|
70
72
|
},
|
|
@@ -175,6 +177,7 @@ const TOOLS = [
|
|
|
175
177
|
clip_index: { type: "number", description: "0-based index of the clip to re-render." },
|
|
176
178
|
layout_mode: {
|
|
177
179
|
type: "string",
|
|
180
|
+
description: "Force a reframing layout instead of letting detection choose one. Omit for automatic, which is what produces the best result on most clips.",
|
|
178
181
|
enum: ["face_tracking", "wide", "split_screen", "stacked", "passthrough", "grid_3", "grid_4"],
|
|
179
182
|
},
|
|
180
183
|
caption_style: {
|
|
@@ -199,6 +202,7 @@ const TOOLS = [
|
|
|
199
202
|
clip_index: { type: "number", description: "0-based clip index." },
|
|
200
203
|
languages: {
|
|
201
204
|
type: "array",
|
|
205
|
+
description: "Target languages for the dub, as ISO 639-1 codes. Nineteen are available; the clip keeps its original audio for any language not listed here.",
|
|
202
206
|
items: { type: "string", enum: ["en", "es", "fr", "it", "pt", "hi", "ja", "zh", "de", "ko", "nl", "pl", "ru", "tr", "id", "vi", "th", "uk", "ar"] },
|
|
203
207
|
},
|
|
204
208
|
},
|
|
@@ -263,7 +267,7 @@ const ANNOTATIONS = {
|
|
|
263
267
|
// Agent-facing descriptions (when to call, returns, errors, quota cost). Served
|
|
264
268
|
// on tools/list — identical across all sources, enriching the terser inline text.
|
|
265
269
|
const DESCRIPTIONS = {
|
|
266
|
-
katto_create_clip_job: "Submit a long video (YouTube, Twitch, Vimeo, Rumble, Zoom, Dailymotion) for clipping. Consumes 1 video from your monthly quota — check katto_get_usage first. Clips are produced asynchronously; returns a job id, then poll katto_get_job until status is 'completed'. Fails if the URL is unsupported or the video is over 90 minutes.",
|
|
270
|
+
katto_create_clip_job: "Submit a long video (YouTube, Twitch, Vimeo, Rumble, Zoom, Dailymotion) for clipping. Consumes 1 video from your monthly quota — check katto_get_usage first. Clips are produced asynchronously; returns a job id, then poll katto_get_job every 30-60 s until status is 'completed', or pass webhook_url to be notified. Pass idempotency_key when you might retry, so a retry never spends a second video. Fails if the URL is unsupported or the video is over 90 minutes.",
|
|
267
271
|
katto_get_job: "Read-only. Get the status, progress and clips of a job by id. Clips come ranked by virality score (0-100, highest first; ties broken by clip_index). Each clip is { url, captions_url, title, score, clip_index, hd }: when status is 'completed' every url is a finished, downloadable clip; hd is true once the 1080p high-quality render is ready. hd=false does NOT mean broken — the url already works, the 1080p is just still rendering in the background (and stays the standard render on the free plan). The job-level hd_ready flag turns true once every clip is hd, i.e. the URLs are final and won't be swapped again (status='completed' fires earlier). Returns 404 for an unknown id.",
|
|
268
272
|
katto_list_jobs: "Read-only. List your recent jobs, newest first. Paginate with 'cursor' (pass the previous next_cursor); optional 'status' filter. Returns { jobs: [{id, status, source, created_at, completed_at}], next_cursor }.",
|
|
269
273
|
katto_get_clips: "Read-only convenience: the clips of a job as { url, captions_url, title, score, clip_index, hd }, ranked by score (highest first). hd=true means the 1080p render is ready; hd=false means the url is the standard render (HD still finishing, or the free plan). Empty until the first clip is ready.",
|
|
@@ -331,7 +335,11 @@ const CAPTION_STYLES = [
|
|
|
331
335
|
{ value: 'multicolor', label: 'Multicolor' },
|
|
332
336
|
];
|
|
333
337
|
|
|
334
|
-
|
|
338
|
+
// Lues par le client au demarrage, avant tout appel : le deroule type, la
|
|
339
|
+
// cadence de sondage et quoi faire d'un quota epuise. Meme texte dans les 3 sources.
|
|
340
|
+
const SERVER_INSTRUCTIONS = "Katto turns a long video into short vertical clips. Typical flow: katto_get_usage (each new job spends 1 video from the monthly quota) -> katto_create_clip_job with the public URL -> poll katto_get_job every 30-60 seconds until status is 'completed' (a 20-minute video took about 5 minutes for 8 clips; longer sources take longer) -> katto_get_clips, and show each clip's title and score rather than only ids. Do not download, open or transcribe the video yourself: Katto fetches it. If a call fails with a quota error, tell the user and give them the upgrade link included in the message. Re-renders and dubbing are separate jobs: start one, then poll katto_get_rerender; do not start the same re-render twice in a loop.";
|
|
341
|
+
|
|
342
|
+
const server = new Server({ name: "katto", version: "0.5.10" }, { capabilities: { tools: {} }, instructions: SERVER_INSTRUCTIONS });
|
|
335
343
|
|
|
336
344
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS_LISTED }));
|
|
337
345
|
|
|
@@ -342,7 +350,8 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
342
350
|
if (name === "katto_create_clip_job") {
|
|
343
351
|
data = await api("/api/v1/jobs", {
|
|
344
352
|
method: "POST",
|
|
345
|
-
body: JSON.stringify({ url: args.url, config: args.config }),
|
|
353
|
+
body: JSON.stringify({ url: args.url, config: args.config, ...(args.webhook_url ? { webhook_url: args.webhook_url } : {}) }),
|
|
354
|
+
...(args.idempotency_key ? { headers: { "Idempotency-Key": String(args.idempotency_key) } } : {}),
|
|
346
355
|
});
|
|
347
356
|
} else if (name === "katto_get_job") {
|
|
348
357
|
data = await api(`/api/v1/jobs/${encodeURIComponent(args.id)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "katto-mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.10",
|
|
4
4
|
"mcpName": "io.github.miracleweasel/katto-mcp",
|
|
5
5
|
"description": "AI video clipping MCP server: turn long videos, podcasts and Twitch VODs into scored, captioned 9:16 shorts from Claude, Cursor, ChatGPT and any MCP client.",
|
|
6
6
|
"type": "module",
|
|
@@ -38,8 +38,13 @@
|
|
|
38
38
|
"video-dubbing"
|
|
39
39
|
],
|
|
40
40
|
"homepage": "https://katto.tech/docs/api",
|
|
41
|
-
"repository": {
|
|
42
|
-
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/miracleweasel/katto-mcp.git"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://katto.tech/contact"
|
|
47
|
+
},
|
|
43
48
|
"author": "Katto (https://katto.tech)",
|
|
44
49
|
"license": "MIT",
|
|
45
50
|
"dependencies": {
|