katto-mcp 0.1.0
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 +40 -0
- package/index.mjs +106 -0
- package/package.json +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# katto-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for [Katto](https://katto.tech) — turn long videos into scored, captioned 9:16 clips from any MCP client (Claude Desktop, Cursor, Claude Code, …).
|
|
4
|
+
|
|
5
|
+
It runs locally over stdio and calls the Katto REST API with your key. Nothing to host.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
1. Create an API key at **[katto.tech/dashboard/api-keys](https://katto.tech/dashboard/api-keys)**.
|
|
10
|
+
2. Add the server to your MCP client config:
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"mcpServers": {
|
|
15
|
+
"katto": {
|
|
16
|
+
"command": "npx",
|
|
17
|
+
"args": ["-y", "katto-mcp"],
|
|
18
|
+
"env": { "KATTO_API_KEY": "sk_live_..." }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Tools
|
|
25
|
+
|
|
26
|
+
- **`katto_create_clip_job(url, config?)`** — submit a long video (YouTube, Twitch, Vimeo, Rumble, Zoom, Dailymotion). Returns a job id.
|
|
27
|
+
- **`katto_get_job(id)`** — poll until `status` is `completed`; `clips` holds the finished MP4 + caption (SRT) urls.
|
|
28
|
+
|
|
29
|
+
Jobs draw from your Katto plan's monthly video quota (25 on Creator, 2 on Free). Videos up to 90 minutes.
|
|
30
|
+
|
|
31
|
+
## Env
|
|
32
|
+
|
|
33
|
+
| var | required | default |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `KATTO_API_KEY` | yes | — |
|
|
36
|
+
| `KATTO_API_URL` | no | `https://katto.tech` |
|
|
37
|
+
|
|
38
|
+
Full docs: **[katto.tech/docs/api](https://katto.tech/docs/api)**
|
|
39
|
+
|
|
40
|
+
MIT
|
package/index.mjs
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Katto MCP server — exposes the Katto clipping API as MCP tools so agents
|
|
4
|
+
* (Claude, Cursor, ...) can turn long videos into scored 9:16 clips. Runs
|
|
5
|
+
* locally over stdio and calls https://katto.tech/api/v1 with your API key.
|
|
6
|
+
*
|
|
7
|
+
* Config:
|
|
8
|
+
* KATTO_API_KEY (required) — create at https://katto.tech/dashboard/api-keys
|
|
9
|
+
* KATTO_API_URL (optional) — defaults to https://katto.tech
|
|
10
|
+
*/
|
|
11
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
12
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
+
|
|
15
|
+
const API_URL = (process.env.KATTO_API_URL || "https://katto.tech").replace(/\/$/, "");
|
|
16
|
+
const API_KEY = process.env.KATTO_API_KEY;
|
|
17
|
+
|
|
18
|
+
if (!API_KEY) {
|
|
19
|
+
console.error("[katto-mcp] KATTO_API_KEY is required. Create one at https://katto.tech/dashboard/api-keys");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function api(path, init = {}) {
|
|
24
|
+
const res = await fetch(`${API_URL}${path}`, {
|
|
25
|
+
...init,
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
...(init.headers || {}),
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const text = await res.text();
|
|
33
|
+
let data;
|
|
34
|
+
try {
|
|
35
|
+
data = JSON.parse(text);
|
|
36
|
+
} catch {
|
|
37
|
+
data = { raw: text };
|
|
38
|
+
}
|
|
39
|
+
if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const TOOLS = [
|
|
44
|
+
{
|
|
45
|
+
name: "katto_create_clip_job",
|
|
46
|
+
description:
|
|
47
|
+
"Submit a long video (YouTube, Twitch, Vimeo, Rumble, Zoom, Dailymotion) to Katto. Returns a job id; " +
|
|
48
|
+
"the clips finish asynchronously in ~5-7 min. Poll katto_get_job with the id until status is 'completed'.",
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
url: { type: "string", description: "Public video URL to clip." },
|
|
53
|
+
config: {
|
|
54
|
+
type: "object",
|
|
55
|
+
description: "Optional pre-clip settings.",
|
|
56
|
+
properties: {
|
|
57
|
+
genre: { type: "string", description: "e.g. podcast, gaming, sports, interview" },
|
|
58
|
+
clipLength: { type: "string", enum: ["lt30", "30_60", "60_90", "90_180"] },
|
|
59
|
+
customPrompt: { type: "string" },
|
|
60
|
+
topics: { type: "string" },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
required: ["url"],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "katto_get_job",
|
|
69
|
+
description:
|
|
70
|
+
"Get the status and clips of a Katto job by id. When status is 'completed', 'clips' holds the finished " +
|
|
71
|
+
"9:16 MP4 urls and caption (SRT) urls.",
|
|
72
|
+
inputSchema: {
|
|
73
|
+
type: "object",
|
|
74
|
+
properties: { id: { type: "string", description: "Job id from katto_create_clip_job." } },
|
|
75
|
+
required: ["id"],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const server = new Server({ name: "katto", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
81
|
+
|
|
82
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
83
|
+
|
|
84
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
85
|
+
const { name, arguments: args = {} } = req.params;
|
|
86
|
+
try {
|
|
87
|
+
let data;
|
|
88
|
+
if (name === "katto_create_clip_job") {
|
|
89
|
+
data = await api("/api/v1/jobs", {
|
|
90
|
+
method: "POST",
|
|
91
|
+
body: JSON.stringify({ url: args.url, config: args.config }),
|
|
92
|
+
});
|
|
93
|
+
} else if (name === "katto_get_job") {
|
|
94
|
+
data = await api(`/api/v1/jobs/${encodeURIComponent(args.id)}`);
|
|
95
|
+
} else {
|
|
96
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
97
|
+
}
|
|
98
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
99
|
+
} catch (e) {
|
|
100
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const transport = new StdioServerTransport();
|
|
105
|
+
await server.connect(transport);
|
|
106
|
+
console.error("[katto-mcp] ready");
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "katto-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Katto — turn long videos into scored, captioned 9:16 clips from any MCP client (Claude, Cursor, ...).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"katto-mcp": "index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.mjs",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"katto",
|
|
20
|
+
"video",
|
|
21
|
+
"clips",
|
|
22
|
+
"shorts",
|
|
23
|
+
"ai"
|
|
24
|
+
],
|
|
25
|
+
"homepage": "https://katto.tech/docs/api",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|