@clipwright/mcp-server 0.1.0 → 0.2.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/LICENSE +1 -1
- package/SKILL.md +454 -337
- package/dist/format.js +44 -0
- package/dist/format.js.map +1 -1
- package/dist/server.js +10 -10
- package/dist/server.js.map +1 -1
- package/package.json +4 -4
package/dist/format.js
CHANGED
|
@@ -1,4 +1,48 @@
|
|
|
1
1
|
import { FAILED_STATE, isTerminal } from "@clipwright/core";
|
|
2
|
+
import { ClipwrightApiError } from "@clipwright/sdk";
|
|
3
|
+
/**
|
|
4
|
+
* ОТКАЗ API, РАЗВЁРНУТЫЙ ДЛЯ АГЕНТА (US-622).
|
|
5
|
+
*
|
|
6
|
+
* ЗАЧЕМ ОБЁРТКА, ЕСЛИ ИСКЛЮЧЕНИЕ И ТАК ДОЕЗЖАЕТ. Доезжает — одной строкой:
|
|
7
|
+
* MCP SDK превращает брошенную ошибку в `isError` с текстом сообщения. Для
|
|
8
|
+
* «поле не то» этого хватало, для денег — нет. 402 значит «остановись и скажи
|
|
9
|
+
* человеку числа», 429 значит «подожди столько-то и повтори ТОТ ЖЕ вызов», и
|
|
10
|
+
* различить их в свободном тексте агент может только угадыванием. Здесь он
|
|
11
|
+
* получает `retryable`, `next_action` и сами числа полями.
|
|
12
|
+
*
|
|
13
|
+
* ПОЧЕМУ ТОЛЬКО `ClipwrightApiError`, А НЕ ЛЮБАЯ ОШИБКА. Остальные — местные:
|
|
14
|
+
* нерезолвящийся `CLIPWRIGHT_CLIENT_ID`, сеть, битый baseUrl. У них нет ни
|
|
15
|
+
* статуса, ни кода отказа, и завернуть их в ту же форму значило бы приписать им
|
|
16
|
+
* ответ сервера, которого не было. Они летят дальше как есть, и MCP SDK отдаёт
|
|
17
|
+
* их текст — там он и уместен.
|
|
18
|
+
*
|
|
19
|
+
* `isError: true` остаётся: отказ есть отказ. Меняется не факт, а то, СКОЛЬКО
|
|
20
|
+
* агент о нём узнаёт.
|
|
21
|
+
*/
|
|
22
|
+
export function formatFailure(error) {
|
|
23
|
+
if (!(error instanceof ClipwrightApiError))
|
|
24
|
+
throw error;
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: "text", text: JSON.stringify(error.report()) }],
|
|
27
|
+
isError: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Тело тула, у которого отказ API развёрнут для агента.
|
|
32
|
+
*
|
|
33
|
+
* Обёртка одна на все четыре тула намеренно: забытый `try` в одном из них дал
|
|
34
|
+
* бы агенту плоский текст ровно там, где отказ денежный, — и обнаружилось бы
|
|
35
|
+
* это у клиента. Через общую обёртку забыть нельзя: она либо стоит, либо тул не
|
|
36
|
+
* собирается.
|
|
37
|
+
*/
|
|
38
|
+
export async function withFailureReport(body) {
|
|
39
|
+
try {
|
|
40
|
+
return await body();
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return formatFailure(error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
2
46
|
/**
|
|
3
47
|
* Чистое форматирование ответа `get_run` (вынесено из хендлера, чтобы
|
|
4
48
|
* тестировать терминальную форму без подъёма stdio-транспорта и env).
|
package/dist/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAYrD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,CAAC,CAAC,KAAK,YAAY,kBAAkB,CAAC;QAAE,MAAM,KAAK,CAAC;IACxD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;QACjE,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAoC;IAEpC,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,mEAAmE;QACnE,0EAA0E;QAC1E,iBAAiB;QACjB,IAAI,GAAG,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;YAC/B,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,MAAM,EAAE,QAAQ;4BAChB,MAAM,EAAE,GAAG,CAAC,MAAM;4BAClB,KAAK,EAAE,GAAG,CAAC,KAAK;4BAChB,+DAA+D;4BAC/D,kDAAkD;4BAClD,QAAQ,EAAE,GAAG,CAAC,QAAQ;4BACtB,KAAK,EAAE,GAAG,CAAC,KAAK;yBACjB,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;wBACnB,MAAM,EAAE,WAAW;wBACnB,MAAM,EAAE,GAAG,CAAC,MAAM;wBAClB,KAAK,EAAE,GAAG,CAAC,KAAK;wBAChB,SAAS,EAAE,GAAG,CAAC,YAAY,EAAE,SAAS,IAAI,IAAI;wBAC9C,gBAAgB,EAAE,GAAG,CAAC,YAAY,EAAE,gBAAgB,IAAI,IAAI;wBAC5D,QAAQ,EAAE,GAAG,CAAC,QAAQ;qBACvB,CAAC;iBACH;aACF;SACF,CAAC;IACJ,CAAC;IACD,+EAA+E;IAC/E,gDAAgD;IAChD,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,MAAM,EAAE,aAAa;oBACrB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,SAAS,EAAE,IAAI;oBACf,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,WAAW,EACT,+CAA+C;wBAC/C,GAAG,CAAC,MAAM;wBACV,mFAAmF;wBACnF,wDAAwD;iBAC3D,CAAC;aACH;SACF;KACF,CAAC;AACJ,CAAC"}
|
package/dist/server.js
CHANGED
|
@@ -7,7 +7,7 @@ import { z } from "zod";
|
|
|
7
7
|
// накажем четырёхсотым (блокер B4). Множество выводится из реестра диспозиций,
|
|
8
8
|
// поэтому смена решения по полю переносит его между поверхностями одной строкой.
|
|
9
9
|
import { offeredUgcInputShape, agentRetryShape } from "@clipwright/core";
|
|
10
|
-
import { formatGetRun } from "./format.js";
|
|
10
|
+
import { formatGetRun, withFailureReport } from "./format.js";
|
|
11
11
|
/**
|
|
12
12
|
* Строит MCP-сервер с зарегистрированными тулами, БЕЗ подключения транспорта.
|
|
13
13
|
*
|
|
@@ -18,7 +18,7 @@ import { formatGetRun } from "./format.js";
|
|
|
18
18
|
* тонкий bootstrap).
|
|
19
19
|
*/
|
|
20
20
|
export function createServer(client) {
|
|
21
|
-
const server = new McpServer({ name: "clipwright", version: "0.
|
|
21
|
+
const server = new McpServer({ name: "clipwright", version: "0.2.0" });
|
|
22
22
|
// Полная схема публикуется в tools/list (у конкурента она пустая — антипаттерн).
|
|
23
23
|
server.registerTool("make_ugc", {
|
|
24
24
|
description: "Start generation of a lip-synced UGC video. Give a script (any length); optionally describe " +
|
|
@@ -34,7 +34,7 @@ export function createServer(client) {
|
|
|
34
34
|
// в тексте («1080p 9:16», «character») было бы тем же дефектом
|
|
35
35
|
// «предлагаем и наказываем», просто слоем выше.
|
|
36
36
|
inputSchema: { ...offeredUgcInputShape, ...agentRetryShape },
|
|
37
|
-
}, async (args) => {
|
|
37
|
+
}, async (args) => withFailureReport(async () => {
|
|
38
38
|
// [ARB-1] РАСЩЕПЛЯЕМ плоский вход: `attempt` живёт в opts SDK. Если передать
|
|
39
39
|
// его внутрь `input`, `makeUgcInput.parse` в `idempotencyKeyFor` молча срежет
|
|
40
40
|
// лишний ключ → суффикс всегда `:1` → повтор вернёт тот же run → M1 мёртв.
|
|
@@ -59,18 +59,18 @@ export function createServer(client) {
|
|
|
59
59
|
],
|
|
60
60
|
isError: false,
|
|
61
61
|
};
|
|
62
|
-
});
|
|
62
|
+
}));
|
|
63
63
|
server.registerTool("get_run", {
|
|
64
64
|
description: "Check the status of a video generation started by make_ugc. Pass the run_id. While the run " +
|
|
65
65
|
"is still working it returns status IN_PROGRESS with a next_action telling you to poll again; " +
|
|
66
66
|
"repeat every ~5 seconds until it reaches a terminal state — SUCCEEDED (with video_url) or FAILED.",
|
|
67
67
|
inputSchema: { run_id: z.string() },
|
|
68
|
-
}, async ({ run_id }) => {
|
|
68
|
+
}, async ({ run_id }) => withFailureReport(async () => {
|
|
69
69
|
// Терминальная форма + деградация вынесены в чистый `formatGetRun` (тест
|
|
70
70
|
// без stdio-транспорта). Терминал несёт И `status`, И `state` (US-501).
|
|
71
71
|
const run = await client.getRun(run_id);
|
|
72
72
|
return formatGetRun(run);
|
|
73
|
-
});
|
|
73
|
+
}));
|
|
74
74
|
server.registerTool("quote_ugc", {
|
|
75
75
|
description: "Estimate the credit cost of a make_ugc call WITHOUT spending credits. " +
|
|
76
76
|
"Always call this first and show the user the price before make_ugc.",
|
|
@@ -78,10 +78,10 @@ export function createServer(client) {
|
|
|
78
78
|
// обязательное поле (остальные .optional()/.default()), поэтому переиспользуем
|
|
79
79
|
// shape целиком вместо рукописного {script} — цена учитывает captions/look и не расходится со схемой.
|
|
80
80
|
inputSchema: offeredUgcInputShape,
|
|
81
|
-
}, async (args) => {
|
|
81
|
+
}, async (args) => withFailureReport(async () => {
|
|
82
82
|
const quote = await client.quoteUgc(args);
|
|
83
83
|
return { content: [{ type: "text", text: JSON.stringify(quote) }] };
|
|
84
|
-
});
|
|
84
|
+
}));
|
|
85
85
|
server.registerTool("list_voices", {
|
|
86
86
|
description: "List the curated voice catalog available for make_ugc's `voice` field. Presets are the " +
|
|
87
87
|
"RECOMMENDED path: pick one by name (e.g. voice=\"george\"). `voice_id` is an escape hatch for " +
|
|
@@ -91,12 +91,12 @@ export function createServer(client) {
|
|
|
91
91
|
"full voice list: the paid vendor's full catalog is intentionally not proxied here, so its API " +
|
|
92
92
|
"key does not need to leave the worker process.",
|
|
93
93
|
inputSchema: {},
|
|
94
|
-
}, async () => {
|
|
94
|
+
}, async () => withFailureReport(async () => {
|
|
95
95
|
// Каталог статический (US-513 core), тула лишь ретранслирует /v1/voices —
|
|
96
96
|
// без сети к вендору, без ключа вендора на этой поверхности (§5).
|
|
97
97
|
const voices = await client.listVoices();
|
|
98
98
|
return { content: [{ type: "text", text: JSON.stringify({ voices }) }] };
|
|
99
|
-
});
|
|
99
|
+
}));
|
|
100
100
|
return server;
|
|
101
101
|
}
|
|
102
102
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,oFAAoF;AACpF,4EAA4E;AAC5E,8EAA8E;AAC9E,gFAAgF;AAChF,+EAA+E;AAC/E,iFAAiF;AACjF,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipwright/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP server for Clipwright: generate UGC videos from your coding agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"video"
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"author": "Dimantika
|
|
12
|
+
"author": "Dimantika Sp. z o.o.",
|
|
13
13
|
"homepage": "https://clipwright.io",
|
|
14
14
|
"type": "module",
|
|
15
15
|
"publishConfig": {
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
30
30
|
"zod": "^4.4.3",
|
|
31
|
-
"@clipwright/core": "0.
|
|
32
|
-
"@clipwright/sdk": "0.
|
|
31
|
+
"@clipwright/core": "0.2.0",
|
|
32
|
+
"@clipwright/sdk": "0.2.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^26.1.1"
|