screenslick-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/LICENSE +21 -0
- package/README.md +191 -0
- package/SECURITY.md +31 -0
- package/dist/bridge.d.ts +9 -0
- package/dist/bridge.js +356 -0
- package/dist/bridge.js.map +1 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.js +14 -0
- package/dist/log.js.map +1 -0
- package/dist/toolSchemas.d.ts +255 -0
- package/dist/toolSchemas.js +143 -0
- package/dist/toolSchemas.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vlad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# ScreenSlick MCP Server
|
|
2
|
+
|
|
3
|
+
Local MCP server for controlling the ScreenSlick browser editor from Codex,
|
|
4
|
+
Claude Code, Claude Desktop, Cursor, and other MCP clients.
|
|
5
|
+
|
|
6
|
+
The server runs locally over stdio and opens a localhost bridge at:
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
ws://127.0.0.1:32117/screenslick-agent
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Open ScreenSlick, enter the editor, click **Agent**, then ask your MCP client to
|
|
13
|
+
call `screenslick_bridge_status`.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
No ScreenSlick source checkout is required. Use the npm package from your MCP
|
|
18
|
+
client:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"screenslick": {
|
|
24
|
+
"type": "stdio",
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["-y", "screenslick-mcp"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If your client uses form fields:
|
|
33
|
+
|
|
34
|
+
| Field | Value |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| Name | `screenslick` |
|
|
37
|
+
| Transport | `stdio` |
|
|
38
|
+
| Command | `npx` |
|
|
39
|
+
| Arguments | `-y`, `screenslick-mcp` |
|
|
40
|
+
|
|
41
|
+
## Claude Code
|
|
42
|
+
|
|
43
|
+
Project-scoped `.mcp.json`:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"screenslick": {
|
|
49
|
+
"type": "stdio",
|
|
50
|
+
"command": "npx",
|
|
51
|
+
"args": ["-y", "screenslick-mcp"]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Restart Claude Code, approve the MCP server, then run `/mcp` to confirm the
|
|
58
|
+
`screenslick` server is connected.
|
|
59
|
+
|
|
60
|
+
## Codex CLI
|
|
61
|
+
|
|
62
|
+
Add a stdio server entry to your Codex config:
|
|
63
|
+
|
|
64
|
+
```toml
|
|
65
|
+
[mcp_servers.screenslick]
|
|
66
|
+
command = "npx"
|
|
67
|
+
args = ["-y", "screenslick-mcp"]
|
|
68
|
+
startup_timeout_sec = 10
|
|
69
|
+
tool_timeout_sec = 120
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Cursor
|
|
73
|
+
|
|
74
|
+
Create `.cursor/mcp.json` in a project, or `~/.cursor/mcp.json` globally:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"screenslick": {
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["-y", "screenslick-mcp"]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Available tools
|
|
88
|
+
|
|
89
|
+
- `screenslick_bridge_status`
|
|
90
|
+
- `screenslick_get_project`
|
|
91
|
+
- `screenslick_get_capabilities`
|
|
92
|
+
- `screenslick_list_voices`
|
|
93
|
+
- `screenslick_list_music`
|
|
94
|
+
- `screenslick_list_sound_effects`
|
|
95
|
+
- `screenslick_list_effects`
|
|
96
|
+
- `screenslick_remove_silences`
|
|
97
|
+
- `screenslick_generate_transcript`
|
|
98
|
+
- `screenslick_generate_script`
|
|
99
|
+
- `screenslick_generate_voiceover`
|
|
100
|
+
- `screenslick_add_transcript_voiceover_to_timeline`
|
|
101
|
+
- `screenslick_preview_voiceover`
|
|
102
|
+
- `screenslick_toggle_voiceover`
|
|
103
|
+
- `screenslick_apply_commands`
|
|
104
|
+
- `screenslick_capture_frame`
|
|
105
|
+
- `screenslick_export_video`
|
|
106
|
+
|
|
107
|
+
## Environment variables
|
|
108
|
+
|
|
109
|
+
| Variable | Default | Purpose |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| `SCREEN_SLICK_AGENT_PORT` | `32117` | Local bridge port |
|
|
112
|
+
| `SCREEN_SLICK_AGENT_HOST` | `127.0.0.1` | Must remain localhost |
|
|
113
|
+
| `SCREEN_SLICK_AGENT_LOG` | package `.tmp/screenslick-agent-mcp.log` | Debug log path |
|
|
114
|
+
|
|
115
|
+
The bridge is intentionally localhost-only. Remote hosts are rejected.
|
|
116
|
+
|
|
117
|
+
## Verify
|
|
118
|
+
|
|
119
|
+
1. Start ScreenSlick and open the editor.
|
|
120
|
+
2. Click **Agent** in the editor.
|
|
121
|
+
3. Ask the MCP client to call:
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
screenslick_bridge_status
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Healthy response:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"ok": true,
|
|
132
|
+
"connected": true,
|
|
133
|
+
"port": 32117,
|
|
134
|
+
"path": "/screenslick-agent",
|
|
135
|
+
"session": {
|
|
136
|
+
"hasVideo": true,
|
|
137
|
+
"timelineDuration": 62.63
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Development
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm install
|
|
146
|
+
npm run build
|
|
147
|
+
npm run dev
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Use development mode from this repo:
|
|
151
|
+
|
|
152
|
+
For live source changes:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"mcpServers": {
|
|
157
|
+
"screenslick": {
|
|
158
|
+
"type": "stdio",
|
|
159
|
+
"command": "npx",
|
|
160
|
+
"args": ["tsx", "src/index.ts"],
|
|
161
|
+
"cwd": "/path/to/screenslick-mcp"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
For testing the built package:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"mcpServers": {
|
|
172
|
+
"screenslick": {
|
|
173
|
+
"type": "stdio",
|
|
174
|
+
"command": "node",
|
|
175
|
+
"args": ["dist/index.js"],
|
|
176
|
+
"cwd": "/path/to/screenslick-mcp"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Best-practice notes
|
|
183
|
+
|
|
184
|
+
- Uses the official MCP TypeScript SDK over stdio.
|
|
185
|
+
- Keeps editor bridge traffic on `127.0.0.1`.
|
|
186
|
+
- Does not require the ScreenSlick source repo on the user's machine.
|
|
187
|
+
- Uses structured input schemas for every tool.
|
|
188
|
+
- Routes editor actions through ScreenSlick's native editor APIs instead of
|
|
189
|
+
processing video files directly.
|
|
190
|
+
- Treats premium voice generation as a consent boundary: agents should ask
|
|
191
|
+
before using premium/Gemini voices because they can consume credits.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
ScreenSlick MCP is a local stdio MCP server. It starts a localhost-only bridge so
|
|
4
|
+
the ScreenSlick browser editor can connect to the MCP process.
|
|
5
|
+
|
|
6
|
+
## Trust boundary
|
|
7
|
+
|
|
8
|
+
- The bridge binds to `127.0.0.1`.
|
|
9
|
+
- Non-local HTTP and WebSocket requests are rejected.
|
|
10
|
+
- `SCREEN_SLICK_AGENT_HOST` must remain `127.0.0.1`.
|
|
11
|
+
- The server does not process media files directly.
|
|
12
|
+
- Editor mutations go through ScreenSlick's existing editor command layer.
|
|
13
|
+
|
|
14
|
+
## User consent
|
|
15
|
+
|
|
16
|
+
Agents should ask before using premium/Gemini voice generation because it can
|
|
17
|
+
consume ScreenSlick credits.
|
|
18
|
+
|
|
19
|
+
Agents should also ask before destructive broad edits, such as deleting many
|
|
20
|
+
clips or removing large timeline ranges, unless the user explicitly requested
|
|
21
|
+
that action.
|
|
22
|
+
|
|
23
|
+
## Reporting issues
|
|
24
|
+
|
|
25
|
+
Open a GitHub issue with:
|
|
26
|
+
|
|
27
|
+
- MCP client name and version
|
|
28
|
+
- Operating system
|
|
29
|
+
- Node.js version
|
|
30
|
+
- ScreenSlick URL/build
|
|
31
|
+
- Relevant `.tmp/screenslick-agent-mcp.log` lines, with private data removed
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function getBridgeStatus(): {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
connected: boolean;
|
|
4
|
+
port: number;
|
|
5
|
+
path: string;
|
|
6
|
+
session: unknown;
|
|
7
|
+
};
|
|
8
|
+
export declare function callEditor(method: string, params: unknown): Promise<unknown>;
|
|
9
|
+
export declare function ensureBridge(): Promise<unknown>;
|
package/dist/bridge.js
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { getConfig, WS_PATH } from "./config.js";
|
|
4
|
+
import { debugLog } from "./log.js";
|
|
5
|
+
// Editor calls (export, capture, generation) can run long; keep the WS and HTTP
|
|
6
|
+
// fallback paths on the same ceiling. The status probe is a quick liveness check.
|
|
7
|
+
const EDITOR_CALL_TIMEOUT_MS = 120_000;
|
|
8
|
+
const BRIDGE_PROBE_TIMEOUT_MS = 5_000;
|
|
9
|
+
let serverPromise = null;
|
|
10
|
+
let activeEditor = null;
|
|
11
|
+
let editorSession = null;
|
|
12
|
+
let editorRequestCounter = 0;
|
|
13
|
+
const pendingEditorRequests = new Map();
|
|
14
|
+
function isLocalRequest(request) {
|
|
15
|
+
const address = request.socket.remoteAddress;
|
|
16
|
+
return (address === "127.0.0.1" ||
|
|
17
|
+
address === "::1" ||
|
|
18
|
+
address === "::ffff:127.0.0.1");
|
|
19
|
+
}
|
|
20
|
+
function createAcceptKey(key) {
|
|
21
|
+
return createHash("sha1")
|
|
22
|
+
.update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`)
|
|
23
|
+
.digest("base64");
|
|
24
|
+
}
|
|
25
|
+
function sendJsonResponse(response, status, body) {
|
|
26
|
+
const payload = JSON.stringify(body);
|
|
27
|
+
response.writeHead(status, {
|
|
28
|
+
"content-type": "application/json",
|
|
29
|
+
"content-length": Buffer.byteLength(payload),
|
|
30
|
+
});
|
|
31
|
+
response.end(payload);
|
|
32
|
+
}
|
|
33
|
+
async function readJsonBody(request) {
|
|
34
|
+
const chunks = [];
|
|
35
|
+
for await (const chunk of request) {
|
|
36
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
37
|
+
}
|
|
38
|
+
if (chunks.length === 0)
|
|
39
|
+
return {};
|
|
40
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
41
|
+
}
|
|
42
|
+
class WebSocketConnection {
|
|
43
|
+
socket;
|
|
44
|
+
buffer = Buffer.alloc(0);
|
|
45
|
+
fragments = [];
|
|
46
|
+
fragmentOpcode = 0;
|
|
47
|
+
closed = false;
|
|
48
|
+
constructor(socket) {
|
|
49
|
+
this.socket = socket;
|
|
50
|
+
socket.on("data", (chunk) => this.read(chunk));
|
|
51
|
+
socket.on("close", () => this.close());
|
|
52
|
+
socket.on("error", (error) => {
|
|
53
|
+
debugLog(`editor socket error ${error.message}`);
|
|
54
|
+
this.close();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
sendJson(value) {
|
|
58
|
+
this.sendText(JSON.stringify(value));
|
|
59
|
+
}
|
|
60
|
+
close() {
|
|
61
|
+
if (this.closed)
|
|
62
|
+
return;
|
|
63
|
+
this.closed = true;
|
|
64
|
+
debugLog("editor disconnected");
|
|
65
|
+
if (activeEditor === this) {
|
|
66
|
+
activeEditor = null;
|
|
67
|
+
editorSession = null;
|
|
68
|
+
for (const [id, pending] of pendingEditorRequests) {
|
|
69
|
+
clearTimeout(pending.timer);
|
|
70
|
+
pending.reject(new Error("ScreenSlick editor disconnected."));
|
|
71
|
+
pendingEditorRequests.delete(id);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
this.socket.destroy();
|
|
75
|
+
}
|
|
76
|
+
sendText(text) {
|
|
77
|
+
if (this.closed)
|
|
78
|
+
return;
|
|
79
|
+
const payload = Buffer.from(text);
|
|
80
|
+
let header;
|
|
81
|
+
if (payload.length < 126) {
|
|
82
|
+
header = Buffer.from([0x81, payload.length]);
|
|
83
|
+
}
|
|
84
|
+
else if (payload.length <= 0xffff) {
|
|
85
|
+
header = Buffer.alloc(4);
|
|
86
|
+
header[0] = 0x81;
|
|
87
|
+
header[1] = 126;
|
|
88
|
+
header.writeUInt16BE(payload.length, 2);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
header = Buffer.alloc(10);
|
|
92
|
+
header[0] = 0x81;
|
|
93
|
+
header[1] = 127;
|
|
94
|
+
header.writeBigUInt64BE(BigInt(payload.length), 2);
|
|
95
|
+
}
|
|
96
|
+
this.socket.write(Buffer.concat([header, payload]));
|
|
97
|
+
}
|
|
98
|
+
read(chunk) {
|
|
99
|
+
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
100
|
+
while (this.buffer.length >= 2) {
|
|
101
|
+
const first = this.buffer[0];
|
|
102
|
+
const second = this.buffer[1];
|
|
103
|
+
const fin = (first & 0x80) !== 0;
|
|
104
|
+
const opcode = first & 0x0f;
|
|
105
|
+
const masked = (second & 0x80) !== 0;
|
|
106
|
+
let length = second & 0x7f;
|
|
107
|
+
let offset = 2;
|
|
108
|
+
if (length === 126) {
|
|
109
|
+
if (this.buffer.length < offset + 2)
|
|
110
|
+
return;
|
|
111
|
+
length = this.buffer.readUInt16BE(offset);
|
|
112
|
+
offset += 2;
|
|
113
|
+
}
|
|
114
|
+
else if (length === 127) {
|
|
115
|
+
if (this.buffer.length < offset + 8)
|
|
116
|
+
return;
|
|
117
|
+
length = Number(this.buffer.readBigUInt64BE(offset));
|
|
118
|
+
offset += 8;
|
|
119
|
+
}
|
|
120
|
+
const maskLength = masked ? 4 : 0;
|
|
121
|
+
const frameEnd = offset + maskLength + length;
|
|
122
|
+
if (this.buffer.length < frameEnd)
|
|
123
|
+
return;
|
|
124
|
+
const mask = masked ? this.buffer.subarray(offset, offset + 4) : null;
|
|
125
|
+
offset += maskLength;
|
|
126
|
+
const payload = Buffer.from(this.buffer.subarray(offset, offset + length));
|
|
127
|
+
this.buffer = this.buffer.subarray(frameEnd);
|
|
128
|
+
if (mask) {
|
|
129
|
+
for (let index = 0; index < payload.length; index += 1) {
|
|
130
|
+
payload[index] ^= mask[index % 4];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Control frames (0x8-0xF) are never fragmented and may be interleaved
|
|
134
|
+
// between data fragments.
|
|
135
|
+
if (opcode === 0x8) {
|
|
136
|
+
this.close();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (opcode === 0x9) {
|
|
140
|
+
this.sendPong(payload);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (opcode === 0xa) {
|
|
144
|
+
continue; // pong; nothing to do
|
|
145
|
+
}
|
|
146
|
+
// Data frames: 0x1 text, 0x2 binary, 0x0 continuation. Reassemble across
|
|
147
|
+
// fragments so large editor responses (export MP4, captured PNG) survive a
|
|
148
|
+
// multi-frame send.
|
|
149
|
+
if (opcode === 0x1 || opcode === 0x2) {
|
|
150
|
+
this.fragments = [payload];
|
|
151
|
+
this.fragmentOpcode = opcode;
|
|
152
|
+
}
|
|
153
|
+
else if (opcode === 0x0) {
|
|
154
|
+
if (this.fragments.length === 0) {
|
|
155
|
+
debugLog("unexpected continuation frame with no message in progress");
|
|
156
|
+
this.close();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.fragments.push(payload);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
debugLog(`ignoring unsupported ws opcode 0x${opcode.toString(16)}`);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (!fin)
|
|
166
|
+
continue; // more fragments to come
|
|
167
|
+
const message = Buffer.concat(this.fragments);
|
|
168
|
+
const completedOpcode = this.fragmentOpcode;
|
|
169
|
+
this.fragments = [];
|
|
170
|
+
this.fragmentOpcode = 0;
|
|
171
|
+
if (completedOpcode === 0x1)
|
|
172
|
+
this.handleText(message.toString("utf8"));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
sendPong(payload) {
|
|
176
|
+
if (this.closed)
|
|
177
|
+
return;
|
|
178
|
+
// Control frame payloads are capped at 125 bytes; echo what fits.
|
|
179
|
+
const length = Math.min(payload.length, 125);
|
|
180
|
+
const header = Buffer.from([0x8a, length]);
|
|
181
|
+
this.socket.write(Buffer.concat([header, payload.subarray(0, length)]));
|
|
182
|
+
}
|
|
183
|
+
handleText(text) {
|
|
184
|
+
let message;
|
|
185
|
+
try {
|
|
186
|
+
message = JSON.parse(text);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (message &&
|
|
192
|
+
typeof message === "object" &&
|
|
193
|
+
message.type === "hello") {
|
|
194
|
+
editorSession = message.session;
|
|
195
|
+
debugLog(`editor hello ${JSON.stringify(editorSession)}`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const response = message;
|
|
199
|
+
if (typeof response.id !== "string")
|
|
200
|
+
return;
|
|
201
|
+
const pending = pendingEditorRequests.get(response.id);
|
|
202
|
+
if (!pending)
|
|
203
|
+
return;
|
|
204
|
+
clearTimeout(pending.timer);
|
|
205
|
+
pendingEditorRequests.delete(response.id);
|
|
206
|
+
if (response.error) {
|
|
207
|
+
pending.reject(new Error(String(response.error.message ?? "Editor error.")));
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
pending.resolve(response.result);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
export function getBridgeStatus() {
|
|
215
|
+
const { port } = getConfig();
|
|
216
|
+
return {
|
|
217
|
+
ok: true,
|
|
218
|
+
connected: Boolean(activeEditor && !activeEditor.closed),
|
|
219
|
+
port,
|
|
220
|
+
path: WS_PATH,
|
|
221
|
+
session: editorSession,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async function fetchBridgeStatus() {
|
|
225
|
+
const { bridgeBaseUrl } = getConfig();
|
|
226
|
+
const response = await fetch(`${bridgeBaseUrl}/status`, {
|
|
227
|
+
signal: AbortSignal.timeout(BRIDGE_PROBE_TIMEOUT_MS),
|
|
228
|
+
});
|
|
229
|
+
if (!response.ok)
|
|
230
|
+
throw new Error(`Bridge status failed: ${response.status}`);
|
|
231
|
+
return response.json();
|
|
232
|
+
}
|
|
233
|
+
async function callExternalBridge(method, params) {
|
|
234
|
+
const { bridgeBaseUrl } = getConfig();
|
|
235
|
+
const response = await fetch(`${bridgeBaseUrl}/call`, {
|
|
236
|
+
method: "POST",
|
|
237
|
+
headers: { "content-type": "application/json" },
|
|
238
|
+
body: JSON.stringify({ method, params }),
|
|
239
|
+
signal: AbortSignal.timeout(EDITOR_CALL_TIMEOUT_MS),
|
|
240
|
+
});
|
|
241
|
+
const payload = (await response.json());
|
|
242
|
+
if (!response.ok || payload.error) {
|
|
243
|
+
throw new Error(payload.error ?? `Bridge call failed: ${response.status}`);
|
|
244
|
+
}
|
|
245
|
+
return payload.result;
|
|
246
|
+
}
|
|
247
|
+
export async function callEditor(method, params) {
|
|
248
|
+
await ensureBridge();
|
|
249
|
+
if (!activeEditor || activeEditor.closed) {
|
|
250
|
+
if (!serverPromise) {
|
|
251
|
+
return callExternalBridge(method, params);
|
|
252
|
+
}
|
|
253
|
+
throw new Error("No active ScreenSlick editor session. Open the editor and enable Agent.");
|
|
254
|
+
}
|
|
255
|
+
const id = `agent_${++editorRequestCounter}`;
|
|
256
|
+
activeEditor.sendJson({ id, method, params });
|
|
257
|
+
return new Promise((resolve, reject) => {
|
|
258
|
+
const timer = setTimeout(() => {
|
|
259
|
+
pendingEditorRequests.delete(id);
|
|
260
|
+
reject(new Error(`ScreenSlick editor did not answer "${method}" in time.`));
|
|
261
|
+
}, EDITOR_CALL_TIMEOUT_MS);
|
|
262
|
+
pendingEditorRequests.set(id, { resolve, reject, timer });
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
export async function ensureBridge() {
|
|
266
|
+
if (!serverPromise) {
|
|
267
|
+
try {
|
|
268
|
+
return await fetchBridgeStatus();
|
|
269
|
+
}
|
|
270
|
+
catch { }
|
|
271
|
+
}
|
|
272
|
+
if (serverPromise) {
|
|
273
|
+
await serverPromise;
|
|
274
|
+
return getBridgeStatus();
|
|
275
|
+
}
|
|
276
|
+
serverPromise = startBridgeServer();
|
|
277
|
+
try {
|
|
278
|
+
await serverPromise;
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
// Starting our own bridge failed, most likely because another MCP instance
|
|
282
|
+
// already bound the port (EADDRINUSE). Clear the rejected promise so future
|
|
283
|
+
// calls re-probe, and fall back to whoever owns the bridge right now.
|
|
284
|
+
serverPromise = null;
|
|
285
|
+
debugLog(`bridge startup failed, falling back to existing bridge ${error instanceof Error ? error.message : String(error)}`);
|
|
286
|
+
return await fetchBridgeStatus();
|
|
287
|
+
}
|
|
288
|
+
return getBridgeStatus();
|
|
289
|
+
}
|
|
290
|
+
async function startBridgeServer() {
|
|
291
|
+
const { host, port } = getConfig();
|
|
292
|
+
const server = createServer(async (request, response) => {
|
|
293
|
+
if (!isLocalRequest(request)) {
|
|
294
|
+
sendJsonResponse(response, 403, { error: "Forbidden" });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
if (request.method === "GET" && request.url === "/status") {
|
|
299
|
+
sendJsonResponse(response, 200, getBridgeStatus());
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (request.method === "POST" && request.url === "/call") {
|
|
303
|
+
const body = await readJsonBody(request);
|
|
304
|
+
const method = String(body.method ?? "");
|
|
305
|
+
const result = await callEditor(method, body.params);
|
|
306
|
+
sendJsonResponse(response, 200, { result });
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
sendJsonResponse(response, 404, { error: "Not found" });
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
sendJsonResponse(response, 500, {
|
|
313
|
+
error: error instanceof Error ? error.message : "Unknown bridge error.",
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
server.on("upgrade", (request, socket, head) => {
|
|
318
|
+
debugLog(`upgrade url=${request.url} remote=${request.socket.remoteAddress}`);
|
|
319
|
+
if (request.url !== WS_PATH ||
|
|
320
|
+
!isLocalRequest(request) ||
|
|
321
|
+
request.headers.upgrade?.toLowerCase() !== "websocket") {
|
|
322
|
+
socket.destroy();
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const key = request.headers["sec-websocket-key"];
|
|
326
|
+
if (typeof key !== "string") {
|
|
327
|
+
socket.destroy();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
socket.write([
|
|
331
|
+
"HTTP/1.1 101 Switching Protocols",
|
|
332
|
+
"Upgrade: websocket",
|
|
333
|
+
"Connection: Upgrade",
|
|
334
|
+
`Sec-WebSocket-Accept: ${createAcceptKey(key)}`,
|
|
335
|
+
"\r\n",
|
|
336
|
+
].join("\r\n"));
|
|
337
|
+
if (head.length > 0)
|
|
338
|
+
socket.unshift(head);
|
|
339
|
+
activeEditor?.close();
|
|
340
|
+
activeEditor = new WebSocketConnection(socket);
|
|
341
|
+
debugLog("editor connected");
|
|
342
|
+
});
|
|
343
|
+
server.on("error", (error) => {
|
|
344
|
+
debugLog(`bridge server error ${error.message}`);
|
|
345
|
+
});
|
|
346
|
+
await new Promise((resolve, reject) => {
|
|
347
|
+
server.once("error", reject);
|
|
348
|
+
server.listen(port, host, () => {
|
|
349
|
+
server.off("error", reject);
|
|
350
|
+
debugLog(`bridge listening ${host}:${port}${WS_PATH}`);
|
|
351
|
+
resolve();
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
return server;
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const DEFAULT_HOST = "127.0.0.1";
|
|
2
|
+
export declare const DEFAULT_PORT = 32117;
|
|
3
|
+
export declare const WS_PATH = "/screenslick-agent";
|
|
4
|
+
export declare function getConfig(): {
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
logPath: string;
|
|
8
|
+
bridgeBaseUrl: string;
|
|
9
|
+
websocketUrl: string;
|
|
10
|
+
};
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
4
|
+
export const DEFAULT_HOST = "127.0.0.1";
|
|
5
|
+
export const DEFAULT_PORT = 32117;
|
|
6
|
+
export const WS_PATH = "/screenslick-agent";
|
|
7
|
+
export function getConfig() {
|
|
8
|
+
const port = Number(process.env.SCREEN_SLICK_AGENT_PORT ?? DEFAULT_PORT);
|
|
9
|
+
const host = process.env.SCREEN_SLICK_AGENT_HOST ?? DEFAULT_HOST;
|
|
10
|
+
const logPath = process.env.SCREEN_SLICK_AGENT_LOG ??
|
|
11
|
+
join(packageRoot, ".tmp", "screenslick-agent-mcp.log");
|
|
12
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
13
|
+
throw new Error(`Invalid SCREEN_SLICK_AGENT_PORT "${String(process.env.SCREEN_SLICK_AGENT_PORT)}".`);
|
|
14
|
+
}
|
|
15
|
+
if (host !== DEFAULT_HOST) {
|
|
16
|
+
throw new Error("SCREEN_SLICK_AGENT_HOST must be 127.0.0.1. The ScreenSlick bridge is intentionally localhost-only.");
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
host,
|
|
20
|
+
port,
|
|
21
|
+
logPath,
|
|
22
|
+
bridgeBaseUrl: `http://${host}:${port}`,
|
|
23
|
+
websocketUrl: `ws://${host}:${port}${WS_PATH}`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAErE,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AACxC,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC;AAClC,MAAM,CAAC,MAAM,OAAO,GAAG,oBAAoB,CAAC;AAE5C,MAAM,UAAU,SAAS;IACvB,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,YAAY,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,YAAY,CAAC;IACjE,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,sBAAsB;QAClC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,2BAA2B,CAAC,CAAC;IAEzD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CACb,oCAAoC,MAAM,CACxC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CACpC,IAAI,CACN,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,oGAAoG,CACrG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,OAAO;QACP,aAAa,EAAE,UAAU,IAAI,IAAI,IAAI,EAAE;QACvC,YAAY,EAAE,QAAQ,IAAI,IAAI,IAAI,GAAG,OAAO,EAAE;KAC/C,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { callEditor, ensureBridge, getBridgeStatus } from "./bridge.js";
|
|
5
|
+
import { debugLog } from "./log.js";
|
|
6
|
+
import { editorMethods, passthroughToolSchemas, toolDescriptions, } from "./toolSchemas.js";
|
|
7
|
+
function textContent(value) {
|
|
8
|
+
return {
|
|
9
|
+
content: [
|
|
10
|
+
{
|
|
11
|
+
type: "text",
|
|
12
|
+
text: typeof value === "string" ? value : JSON.stringify(value, null, 2),
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function selectVoiceInfo(capabilities) {
|
|
18
|
+
if (capabilities &&
|
|
19
|
+
typeof capabilities === "object" &&
|
|
20
|
+
"commands" in capabilities) {
|
|
21
|
+
const commands = capabilities.commands;
|
|
22
|
+
if (commands && typeof commands === "object" && "voiceover" in commands) {
|
|
23
|
+
const voiceover = commands.voiceover;
|
|
24
|
+
if (voiceover &&
|
|
25
|
+
typeof voiceover === "object" &&
|
|
26
|
+
"voiceSelection" in voiceover) {
|
|
27
|
+
return voiceover.voiceSelection;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return capabilities;
|
|
32
|
+
}
|
|
33
|
+
async function callScreenSlickTool(name, args) {
|
|
34
|
+
if (name === "screenslick_bridge_status") {
|
|
35
|
+
await ensureBridge();
|
|
36
|
+
return textContent(getBridgeStatus());
|
|
37
|
+
}
|
|
38
|
+
const editorMethod = editorMethods[name];
|
|
39
|
+
if (!editorMethod) {
|
|
40
|
+
throw new Error(`Unknown ScreenSlick tool "${name}".`);
|
|
41
|
+
}
|
|
42
|
+
const result = await callEditor(editorMethod, args);
|
|
43
|
+
if (name === "screenslick_list_voices") {
|
|
44
|
+
return textContent(selectVoiceInfo(result));
|
|
45
|
+
}
|
|
46
|
+
return textContent(result);
|
|
47
|
+
}
|
|
48
|
+
async function main() {
|
|
49
|
+
const server = new McpServer({
|
|
50
|
+
name: "screenslick",
|
|
51
|
+
version: "0.1.0",
|
|
52
|
+
});
|
|
53
|
+
for (const [name, inputSchema] of Object.entries(passthroughToolSchemas)) {
|
|
54
|
+
server.registerTool(name, {
|
|
55
|
+
description: toolDescriptions[name],
|
|
56
|
+
inputSchema,
|
|
57
|
+
}, async (args) => callScreenSlickTool(name, args));
|
|
58
|
+
}
|
|
59
|
+
void ensureBridge().catch((error) => {
|
|
60
|
+
debugLog(`background bridge startup failed ${error instanceof Error ? error.message : String(error)}`);
|
|
61
|
+
});
|
|
62
|
+
const transport = new StdioServerTransport();
|
|
63
|
+
await server.connect(transport);
|
|
64
|
+
}
|
|
65
|
+
process.on("SIGINT", () => {
|
|
66
|
+
debugLog("mcp received SIGINT");
|
|
67
|
+
process.exit(0);
|
|
68
|
+
});
|
|
69
|
+
process.on("SIGTERM", () => {
|
|
70
|
+
debugLog("mcp received SIGTERM");
|
|
71
|
+
process.exit(0);
|
|
72
|
+
});
|
|
73
|
+
main().catch((error) => {
|
|
74
|
+
debugLog(`fatal startup error ${error instanceof Error ? error.message : String(error)}`);
|
|
75
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function debugLog(message: string): void;
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { getConfig } from "./config.js";
|
|
4
|
+
export function debugLog(message) {
|
|
5
|
+
try {
|
|
6
|
+
const { logPath } = getConfig();
|
|
7
|
+
mkdirSync(dirname(logPath), { recursive: true });
|
|
8
|
+
appendFileSync(logPath, `[${new Date().toISOString()}] ${message}\n`);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// Logging should never break MCP tool calls.
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,CAAC;QAChC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,cAAc,CAAC,OAAO,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,OAAO,IAAI,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,6CAA6C;IAC/C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const passthroughToolSchemas: {
|
|
3
|
+
screenslick_bridge_status: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
|
|
4
|
+
screenslick_get_project: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
|
|
5
|
+
screenslick_get_capabilities: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
|
|
6
|
+
screenslick_list_voices: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
|
|
7
|
+
screenslick_list_music: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
|
|
8
|
+
screenslick_list_sound_effects: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
|
|
9
|
+
screenslick_list_effects: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
|
|
10
|
+
screenslick_remove_silences: z.ZodObject<{
|
|
11
|
+
minDuration: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
dryRun: z.ZodOptional<z.ZodBoolean>;
|
|
13
|
+
mode: z.ZodOptional<z.ZodEnum<["aggressive", "default", "conservative", "long-pauses"]>>;
|
|
14
|
+
}, "strict", z.ZodTypeAny, {
|
|
15
|
+
minDuration?: number | undefined;
|
|
16
|
+
dryRun?: boolean | undefined;
|
|
17
|
+
mode?: "default" | "aggressive" | "conservative" | "long-pauses" | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
minDuration?: number | undefined;
|
|
20
|
+
dryRun?: boolean | undefined;
|
|
21
|
+
mode?: "default" | "aggressive" | "conservative" | "long-pauses" | undefined;
|
|
22
|
+
}>;
|
|
23
|
+
screenslick_generate_transcript: z.ZodObject<{
|
|
24
|
+
language: z.ZodOptional<z.ZodString>;
|
|
25
|
+
modelSize: z.ZodOptional<z.ZodEnum<["tiny", "base", "small"]>>;
|
|
26
|
+
provider: z.ZodOptional<z.ZodEnum<["local", "premium"]>>;
|
|
27
|
+
enableSubtitles: z.ZodOptional<z.ZodBoolean>;
|
|
28
|
+
}, "strict", z.ZodTypeAny, {
|
|
29
|
+
provider?: "local" | "premium" | undefined;
|
|
30
|
+
language?: string | undefined;
|
|
31
|
+
modelSize?: "tiny" | "base" | "small" | undefined;
|
|
32
|
+
enableSubtitles?: boolean | undefined;
|
|
33
|
+
}, {
|
|
34
|
+
provider?: "local" | "premium" | undefined;
|
|
35
|
+
language?: string | undefined;
|
|
36
|
+
modelSize?: "tiny" | "base" | "small" | undefined;
|
|
37
|
+
enableSubtitles?: boolean | undefined;
|
|
38
|
+
}>;
|
|
39
|
+
screenslick_generate_script: z.ZodObject<{
|
|
40
|
+
mode: z.ZodOptional<z.ZodEnum<["script", "improve"]>>;
|
|
41
|
+
brief: z.ZodOptional<z.ZodObject<{
|
|
42
|
+
videoType: z.ZodOptional<z.ZodEnum<["demo", "tutorial", "walkthrough", "release", "sales"]>>;
|
|
43
|
+
goal: z.ZodOptional<z.ZodString>;
|
|
44
|
+
audience: z.ZodOptional<z.ZodString>;
|
|
45
|
+
tone: z.ZodOptional<z.ZodString>;
|
|
46
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
47
|
+
}, "strict", z.ZodTypeAny, {
|
|
48
|
+
videoType?: "tutorial" | "demo" | "walkthrough" | "release" | "sales" | undefined;
|
|
49
|
+
goal?: string | undefined;
|
|
50
|
+
audience?: string | undefined;
|
|
51
|
+
tone?: string | undefined;
|
|
52
|
+
notes?: string | undefined;
|
|
53
|
+
}, {
|
|
54
|
+
videoType?: "tutorial" | "demo" | "walkthrough" | "release" | "sales" | undefined;
|
|
55
|
+
goal?: string | undefined;
|
|
56
|
+
audience?: string | undefined;
|
|
57
|
+
tone?: string | undefined;
|
|
58
|
+
notes?: string | undefined;
|
|
59
|
+
}>>;
|
|
60
|
+
}, "strict", z.ZodTypeAny, {
|
|
61
|
+
mode?: "script" | "improve" | undefined;
|
|
62
|
+
brief?: {
|
|
63
|
+
videoType?: "tutorial" | "demo" | "walkthrough" | "release" | "sales" | undefined;
|
|
64
|
+
goal?: string | undefined;
|
|
65
|
+
audience?: string | undefined;
|
|
66
|
+
tone?: string | undefined;
|
|
67
|
+
notes?: string | undefined;
|
|
68
|
+
} | undefined;
|
|
69
|
+
}, {
|
|
70
|
+
mode?: "script" | "improve" | undefined;
|
|
71
|
+
brief?: {
|
|
72
|
+
videoType?: "tutorial" | "demo" | "walkthrough" | "release" | "sales" | undefined;
|
|
73
|
+
goal?: string | undefined;
|
|
74
|
+
audience?: string | undefined;
|
|
75
|
+
tone?: string | undefined;
|
|
76
|
+
notes?: string | undefined;
|
|
77
|
+
} | undefined;
|
|
78
|
+
}>;
|
|
79
|
+
screenslick_generate_voiceover: z.ZodObject<{
|
|
80
|
+
source: z.ZodOptional<z.ZodEnum<["transcript", "script"]>>;
|
|
81
|
+
script: z.ZodOptional<z.ZodString>;
|
|
82
|
+
clips: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
83
|
+
text: z.ZodString;
|
|
84
|
+
startMs: z.ZodOptional<z.ZodNumber>;
|
|
85
|
+
endMs: z.ZodOptional<z.ZodNumber>;
|
|
86
|
+
targetDurationMs: z.ZodOptional<z.ZodNumber>;
|
|
87
|
+
voice: z.ZodOptional<z.ZodString>;
|
|
88
|
+
speed: z.ZodOptional<z.ZodNumber>;
|
|
89
|
+
provider: z.ZodOptional<z.ZodEnum<["local", "gemini"]>>;
|
|
90
|
+
style: z.ZodOptional<z.ZodEnum<["natural", "tutorial", "cheerful", "calm", "energetic", "dramatic"]>>;
|
|
91
|
+
direction: z.ZodOptional<z.ZodString>;
|
|
92
|
+
}, "strict", z.ZodTypeAny, {
|
|
93
|
+
text: string;
|
|
94
|
+
startMs?: number | undefined;
|
|
95
|
+
endMs?: number | undefined;
|
|
96
|
+
targetDurationMs?: number | undefined;
|
|
97
|
+
voice?: string | undefined;
|
|
98
|
+
speed?: number | undefined;
|
|
99
|
+
provider?: "local" | "gemini" | undefined;
|
|
100
|
+
style?: "natural" | "tutorial" | "cheerful" | "calm" | "energetic" | "dramatic" | undefined;
|
|
101
|
+
direction?: string | undefined;
|
|
102
|
+
}, {
|
|
103
|
+
text: string;
|
|
104
|
+
startMs?: number | undefined;
|
|
105
|
+
endMs?: number | undefined;
|
|
106
|
+
targetDurationMs?: number | undefined;
|
|
107
|
+
voice?: string | undefined;
|
|
108
|
+
speed?: number | undefined;
|
|
109
|
+
provider?: "local" | "gemini" | undefined;
|
|
110
|
+
style?: "natural" | "tutorial" | "cheerful" | "calm" | "energetic" | "dramatic" | undefined;
|
|
111
|
+
direction?: string | undefined;
|
|
112
|
+
}>, "many">>;
|
|
113
|
+
enable: z.ZodOptional<z.ZodBoolean>;
|
|
114
|
+
}, "strict", z.ZodTypeAny, {
|
|
115
|
+
script?: string | undefined;
|
|
116
|
+
source?: "script" | "transcript" | undefined;
|
|
117
|
+
clips?: {
|
|
118
|
+
text: string;
|
|
119
|
+
startMs?: number | undefined;
|
|
120
|
+
endMs?: number | undefined;
|
|
121
|
+
targetDurationMs?: number | undefined;
|
|
122
|
+
voice?: string | undefined;
|
|
123
|
+
speed?: number | undefined;
|
|
124
|
+
provider?: "local" | "gemini" | undefined;
|
|
125
|
+
style?: "natural" | "tutorial" | "cheerful" | "calm" | "energetic" | "dramatic" | undefined;
|
|
126
|
+
direction?: string | undefined;
|
|
127
|
+
}[] | undefined;
|
|
128
|
+
enable?: boolean | undefined;
|
|
129
|
+
}, {
|
|
130
|
+
script?: string | undefined;
|
|
131
|
+
source?: "script" | "transcript" | undefined;
|
|
132
|
+
clips?: {
|
|
133
|
+
text: string;
|
|
134
|
+
startMs?: number | undefined;
|
|
135
|
+
endMs?: number | undefined;
|
|
136
|
+
targetDurationMs?: number | undefined;
|
|
137
|
+
voice?: string | undefined;
|
|
138
|
+
speed?: number | undefined;
|
|
139
|
+
provider?: "local" | "gemini" | undefined;
|
|
140
|
+
style?: "natural" | "tutorial" | "cheerful" | "calm" | "energetic" | "dramatic" | undefined;
|
|
141
|
+
direction?: string | undefined;
|
|
142
|
+
}[] | undefined;
|
|
143
|
+
enable?: boolean | undefined;
|
|
144
|
+
}>;
|
|
145
|
+
screenslick_add_transcript_voiceover_to_timeline: z.ZodObject<{
|
|
146
|
+
source: z.ZodOptional<z.ZodEnum<["transcript", "script"]>>;
|
|
147
|
+
script: z.ZodOptional<z.ZodString>;
|
|
148
|
+
clips: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
149
|
+
text: z.ZodString;
|
|
150
|
+
startMs: z.ZodOptional<z.ZodNumber>;
|
|
151
|
+
endMs: z.ZodOptional<z.ZodNumber>;
|
|
152
|
+
targetDurationMs: z.ZodOptional<z.ZodNumber>;
|
|
153
|
+
voice: z.ZodOptional<z.ZodString>;
|
|
154
|
+
speed: z.ZodOptional<z.ZodNumber>;
|
|
155
|
+
provider: z.ZodOptional<z.ZodEnum<["local", "gemini"]>>;
|
|
156
|
+
style: z.ZodOptional<z.ZodEnum<["natural", "tutorial", "cheerful", "calm", "energetic", "dramatic"]>>;
|
|
157
|
+
direction: z.ZodOptional<z.ZodString>;
|
|
158
|
+
}, "strict", z.ZodTypeAny, {
|
|
159
|
+
text: string;
|
|
160
|
+
startMs?: number | undefined;
|
|
161
|
+
endMs?: number | undefined;
|
|
162
|
+
targetDurationMs?: number | undefined;
|
|
163
|
+
voice?: string | undefined;
|
|
164
|
+
speed?: number | undefined;
|
|
165
|
+
provider?: "local" | "gemini" | undefined;
|
|
166
|
+
style?: "natural" | "tutorial" | "cheerful" | "calm" | "energetic" | "dramatic" | undefined;
|
|
167
|
+
direction?: string | undefined;
|
|
168
|
+
}, {
|
|
169
|
+
text: string;
|
|
170
|
+
startMs?: number | undefined;
|
|
171
|
+
endMs?: number | undefined;
|
|
172
|
+
targetDurationMs?: number | undefined;
|
|
173
|
+
voice?: string | undefined;
|
|
174
|
+
speed?: number | undefined;
|
|
175
|
+
provider?: "local" | "gemini" | undefined;
|
|
176
|
+
style?: "natural" | "tutorial" | "cheerful" | "calm" | "energetic" | "dramatic" | undefined;
|
|
177
|
+
direction?: string | undefined;
|
|
178
|
+
}>, "many">>;
|
|
179
|
+
}, "strict", z.ZodTypeAny, {
|
|
180
|
+
script?: string | undefined;
|
|
181
|
+
source?: "script" | "transcript" | undefined;
|
|
182
|
+
clips?: {
|
|
183
|
+
text: string;
|
|
184
|
+
startMs?: number | undefined;
|
|
185
|
+
endMs?: number | undefined;
|
|
186
|
+
targetDurationMs?: number | undefined;
|
|
187
|
+
voice?: string | undefined;
|
|
188
|
+
speed?: number | undefined;
|
|
189
|
+
provider?: "local" | "gemini" | undefined;
|
|
190
|
+
style?: "natural" | "tutorial" | "cheerful" | "calm" | "energetic" | "dramatic" | undefined;
|
|
191
|
+
direction?: string | undefined;
|
|
192
|
+
}[] | undefined;
|
|
193
|
+
}, {
|
|
194
|
+
script?: string | undefined;
|
|
195
|
+
source?: "script" | "transcript" | undefined;
|
|
196
|
+
clips?: {
|
|
197
|
+
text: string;
|
|
198
|
+
startMs?: number | undefined;
|
|
199
|
+
endMs?: number | undefined;
|
|
200
|
+
targetDurationMs?: number | undefined;
|
|
201
|
+
voice?: string | undefined;
|
|
202
|
+
speed?: number | undefined;
|
|
203
|
+
provider?: "local" | "gemini" | undefined;
|
|
204
|
+
style?: "natural" | "tutorial" | "cheerful" | "calm" | "energetic" | "dramatic" | undefined;
|
|
205
|
+
direction?: string | undefined;
|
|
206
|
+
}[] | undefined;
|
|
207
|
+
}>;
|
|
208
|
+
screenslick_preview_voiceover: z.ZodObject<{
|
|
209
|
+
action: z.ZodOptional<z.ZodEnum<["start", "stop"]>>;
|
|
210
|
+
}, "strict", z.ZodTypeAny, {
|
|
211
|
+
action?: "start" | "stop" | undefined;
|
|
212
|
+
}, {
|
|
213
|
+
action?: "start" | "stop" | undefined;
|
|
214
|
+
}>;
|
|
215
|
+
screenslick_toggle_voiceover: z.ZodObject<{
|
|
216
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
217
|
+
}, "strict", z.ZodTypeAny, {
|
|
218
|
+
enabled?: boolean | undefined;
|
|
219
|
+
}, {
|
|
220
|
+
enabled?: boolean | undefined;
|
|
221
|
+
}>;
|
|
222
|
+
screenslick_apply_commands: z.ZodObject<{
|
|
223
|
+
commands: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">;
|
|
224
|
+
dryRun: z.ZodOptional<z.ZodBoolean>;
|
|
225
|
+
description: z.ZodOptional<z.ZodString>;
|
|
226
|
+
}, "strict", z.ZodTypeAny, {
|
|
227
|
+
commands: Record<string, unknown>[];
|
|
228
|
+
dryRun?: boolean | undefined;
|
|
229
|
+
description?: string | undefined;
|
|
230
|
+
}, {
|
|
231
|
+
commands: Record<string, unknown>[];
|
|
232
|
+
dryRun?: boolean | undefined;
|
|
233
|
+
description?: string | undefined;
|
|
234
|
+
}>;
|
|
235
|
+
screenslick_capture_frame: z.ZodObject<{
|
|
236
|
+
time: z.ZodOptional<z.ZodNumber>;
|
|
237
|
+
}, "strict", z.ZodTypeAny, {
|
|
238
|
+
time?: number | undefined;
|
|
239
|
+
}, {
|
|
240
|
+
time?: number | undefined;
|
|
241
|
+
}>;
|
|
242
|
+
screenslick_export_video: z.ZodObject<{
|
|
243
|
+
settings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
244
|
+
watermark: z.ZodOptional<z.ZodBoolean>;
|
|
245
|
+
}, "strict", z.ZodTypeAny, {
|
|
246
|
+
settings?: Record<string, unknown> | undefined;
|
|
247
|
+
watermark?: boolean | undefined;
|
|
248
|
+
}, {
|
|
249
|
+
settings?: Record<string, unknown> | undefined;
|
|
250
|
+
watermark?: boolean | undefined;
|
|
251
|
+
}>;
|
|
252
|
+
};
|
|
253
|
+
export type ToolName = keyof typeof passthroughToolSchemas;
|
|
254
|
+
export declare const toolDescriptions: Record<ToolName, string>;
|
|
255
|
+
export declare const editorMethods: Partial<Record<ToolName, string>>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const emptySchema = z.object({}).strict();
|
|
3
|
+
const jsonObjectSchema = z.record(z.unknown());
|
|
4
|
+
const optionalJsonObjectSchema = jsonObjectSchema.optional();
|
|
5
|
+
const scriptClipSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
text: z.string(),
|
|
8
|
+
startMs: z.number().optional(),
|
|
9
|
+
endMs: z.number().optional(),
|
|
10
|
+
targetDurationMs: z.number().optional(),
|
|
11
|
+
voice: z.string().optional(),
|
|
12
|
+
speed: z.number().optional(),
|
|
13
|
+
provider: z.enum(["local", "gemini"]).optional(),
|
|
14
|
+
style: z
|
|
15
|
+
.enum(["natural", "tutorial", "cheerful", "calm", "energetic", "dramatic"])
|
|
16
|
+
.optional(),
|
|
17
|
+
direction: z.string().optional(),
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
20
|
+
export const passthroughToolSchemas = {
|
|
21
|
+
screenslick_bridge_status: emptySchema,
|
|
22
|
+
screenslick_get_project: emptySchema,
|
|
23
|
+
screenslick_get_capabilities: emptySchema,
|
|
24
|
+
screenslick_list_voices: emptySchema,
|
|
25
|
+
screenslick_list_music: emptySchema,
|
|
26
|
+
screenslick_list_sound_effects: emptySchema,
|
|
27
|
+
screenslick_list_effects: emptySchema,
|
|
28
|
+
screenslick_remove_silences: z
|
|
29
|
+
.object({
|
|
30
|
+
minDuration: z.number().optional(),
|
|
31
|
+
dryRun: z.boolean().optional(),
|
|
32
|
+
mode: z
|
|
33
|
+
.enum(["aggressive", "default", "conservative", "long-pauses"])
|
|
34
|
+
.optional(),
|
|
35
|
+
})
|
|
36
|
+
.strict(),
|
|
37
|
+
screenslick_generate_transcript: z
|
|
38
|
+
.object({
|
|
39
|
+
language: z.string().optional(),
|
|
40
|
+
modelSize: z.enum(["tiny", "base", "small"]).optional(),
|
|
41
|
+
provider: z.enum(["local", "premium"]).optional(),
|
|
42
|
+
enableSubtitles: z.boolean().optional(),
|
|
43
|
+
})
|
|
44
|
+
.strict(),
|
|
45
|
+
screenslick_generate_script: z
|
|
46
|
+
.object({
|
|
47
|
+
mode: z.enum(["script", "improve"]).optional(),
|
|
48
|
+
brief: z
|
|
49
|
+
.object({
|
|
50
|
+
videoType: z
|
|
51
|
+
.enum(["demo", "tutorial", "walkthrough", "release", "sales"])
|
|
52
|
+
.optional(),
|
|
53
|
+
goal: z.string().optional(),
|
|
54
|
+
audience: z.string().optional(),
|
|
55
|
+
tone: z.string().optional(),
|
|
56
|
+
notes: z.string().optional(),
|
|
57
|
+
})
|
|
58
|
+
.strict()
|
|
59
|
+
.optional(),
|
|
60
|
+
})
|
|
61
|
+
.strict(),
|
|
62
|
+
screenslick_generate_voiceover: z
|
|
63
|
+
.object({
|
|
64
|
+
source: z.enum(["transcript", "script"]).optional(),
|
|
65
|
+
script: z.string().optional(),
|
|
66
|
+
clips: z.array(scriptClipSchema).optional(),
|
|
67
|
+
enable: z.boolean().optional(),
|
|
68
|
+
})
|
|
69
|
+
.strict(),
|
|
70
|
+
screenslick_add_transcript_voiceover_to_timeline: z
|
|
71
|
+
.object({
|
|
72
|
+
source: z.enum(["transcript", "script"]).optional(),
|
|
73
|
+
script: z.string().optional(),
|
|
74
|
+
clips: z.array(scriptClipSchema).optional(),
|
|
75
|
+
})
|
|
76
|
+
.strict(),
|
|
77
|
+
screenslick_preview_voiceover: z
|
|
78
|
+
.object({
|
|
79
|
+
action: z.enum(["start", "stop"]).optional(),
|
|
80
|
+
})
|
|
81
|
+
.strict(),
|
|
82
|
+
screenslick_toggle_voiceover: z
|
|
83
|
+
.object({
|
|
84
|
+
enabled: z.boolean().optional(),
|
|
85
|
+
})
|
|
86
|
+
.strict(),
|
|
87
|
+
screenslick_apply_commands: z
|
|
88
|
+
.object({
|
|
89
|
+
commands: z.array(jsonObjectSchema),
|
|
90
|
+
dryRun: z.boolean().optional(),
|
|
91
|
+
description: z.string().optional(),
|
|
92
|
+
})
|
|
93
|
+
.strict(),
|
|
94
|
+
screenslick_capture_frame: z
|
|
95
|
+
.object({
|
|
96
|
+
time: z.number().optional(),
|
|
97
|
+
})
|
|
98
|
+
.strict(),
|
|
99
|
+
screenslick_export_video: z
|
|
100
|
+
.object({
|
|
101
|
+
settings: optionalJsonObjectSchema,
|
|
102
|
+
watermark: z.boolean().optional(),
|
|
103
|
+
})
|
|
104
|
+
.strict(),
|
|
105
|
+
};
|
|
106
|
+
export const toolDescriptions = {
|
|
107
|
+
screenslick_bridge_status: "Check whether a ScreenSlick editor is connected to the local bridge.",
|
|
108
|
+
screenslick_get_project: "Inspect the current ScreenSlick editor project.",
|
|
109
|
+
screenslick_get_capabilities: "List ScreenSlick agent-editable commands, effect fields, animations, sound bindings, backgrounds, layouts, and workflow guidance.",
|
|
110
|
+
screenslick_list_voices: "List ScreenSlick local and premium voiceover voices. Premium Gemini voices consume credits and require user confirmation.",
|
|
111
|
+
screenslick_list_music: "List built-in ScreenSlick music tracks.",
|
|
112
|
+
screenslick_list_sound_effects: "List built-in ScreenSlick sound effects.",
|
|
113
|
+
screenslick_list_effects: "List agent-editable visual effect options.",
|
|
114
|
+
screenslick_remove_silences: "Run the editor's silence removal feature on the current timeline.",
|
|
115
|
+
screenslick_generate_transcript: "Run the editor's transcription/subtitle generation flow on the current video or timeline.",
|
|
116
|
+
screenslick_generate_script: "Ask ScreenSlick's script assistant to write or improve a timed narration plan.",
|
|
117
|
+
screenslick_generate_voiceover: "Generate a full voiceover track using ScreenSlick's native voiceover flow.",
|
|
118
|
+
screenslick_add_transcript_voiceover_to_timeline: "Add transcript or agent-provided script voiceover clips to the timeline.",
|
|
119
|
+
screenslick_preview_voiceover: "Preview or stop the current generated ScreenSlick voiceover.",
|
|
120
|
+
screenslick_toggle_voiceover: "Enable or disable the current generated ScreenSlick voiceover track.",
|
|
121
|
+
screenslick_apply_commands: "Apply a batch of validated creative edit commands to the editor timeline. Call screenslick_get_capabilities first to learn the valid command shapes and effect fields.",
|
|
122
|
+
screenslick_capture_frame: "Capture a PNG preview frame from the current editor.",
|
|
123
|
+
screenslick_export_video: "Export the current editor project as an MP4 data URL.",
|
|
124
|
+
};
|
|
125
|
+
export const editorMethods = {
|
|
126
|
+
screenslick_get_project: "get_project",
|
|
127
|
+
screenslick_get_capabilities: "get_capabilities",
|
|
128
|
+
screenslick_list_voices: "get_capabilities",
|
|
129
|
+
screenslick_list_music: "list_music",
|
|
130
|
+
screenslick_list_sound_effects: "list_sound_effects",
|
|
131
|
+
screenslick_list_effects: "list_effects",
|
|
132
|
+
screenslick_remove_silences: "remove_silences",
|
|
133
|
+
screenslick_generate_transcript: "generate_transcript",
|
|
134
|
+
screenslick_generate_script: "generate_script",
|
|
135
|
+
screenslick_generate_voiceover: "generate_voiceover",
|
|
136
|
+
screenslick_add_transcript_voiceover_to_timeline: "add_transcript_voiceover_to_timeline",
|
|
137
|
+
screenslick_preview_voiceover: "preview_voiceover",
|
|
138
|
+
screenslick_toggle_voiceover: "toggle_voiceover",
|
|
139
|
+
screenslick_apply_commands: "apply_commands",
|
|
140
|
+
screenslick_capture_frame: "capture_frame",
|
|
141
|
+
screenslick_export_video: "export_video",
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=toolSchemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolSchemas.js","sourceRoot":"","sources":["../src/toolSchemas.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "screenslick-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local MCP server for controlling the ScreenSlick browser editor.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"screenslick-mcp": "dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"SECURITY.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"dev": "tsx src/index.ts",
|
|
31
|
+
"start": "node dist/index.js",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"mcp",
|
|
36
|
+
"model-context-protocol",
|
|
37
|
+
"screenslick",
|
|
38
|
+
"video",
|
|
39
|
+
"screen-recording"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20.10"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
46
|
+
"zod": "^3.25.76"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^24.0.3",
|
|
50
|
+
"tsx": "^4.20.3",
|
|
51
|
+
"typescript": "^5.8.3"
|
|
52
|
+
}
|
|
53
|
+
}
|