creavit-studio-mcp 1.0.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 ADDED
@@ -0,0 +1,195 @@
1
+ # Creavit Studio MCP Server
2
+
3
+ Lets AI coding agents — Claude Code, Codex, Cursor, or anything else that speaks
4
+ [MCP](https://modelcontextprotocol.io) — drive the Creavit Studio desktop app:
5
+ open projects, change settings, add zoom ranges, start recordings, export video,
6
+ and **see the canvas** to verify the result.
7
+
8
+ macOS only (Creavit Studio is a macOS app). Requires Node 18+.
9
+
10
+ ## Install
11
+
12
+ ### Claude Code
13
+
14
+ ```bash
15
+ claude mcp add creavit-studio -- npx -y creavit-studio-mcp
16
+ ```
17
+
18
+ Or, if you have the app repo checked out, `.mcp.json` in `desktop/` already
19
+ configures it — just open Claude Code there.
20
+
21
+ ### Cursor
22
+
23
+ `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per project):
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "creavit-studio": {
29
+ "command": "npx",
30
+ "args": ["-y", "creavit-studio-mcp"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### Codex CLI
37
+
38
+ `~/.codex/config.toml`:
39
+
40
+ ```toml
41
+ [mcp_servers.creavit-studio]
42
+ command = "npx"
43
+ args = ["-y", "creavit-studio-mcp"]
44
+ ```
45
+
46
+ ### Verify
47
+
48
+ ```bash
49
+ npx creavit-studio-mcp --doctor
50
+ ```
51
+
52
+ ```
53
+ creavit-studio v1.0.0
54
+
55
+ ✓ 34 MCP tools defined
56
+ ✓ Offline project reading available (.crvt files readable without the app)
57
+ ✓ Connected to the app (v3.0.5, pid 61068)
58
+ Open windows: 2
59
+ Agent-connected renderers: main, editor
60
+ ✓ 41 bridge commands available
61
+
62
+ Everything looks good.
63
+ ```
64
+
65
+ ## How it works
66
+
67
+ ```
68
+ Agent (Claude Code / Codex / Cursor)
69
+ │ MCP over stdio (JSON-RPC)
70
+
71
+ creavit-studio-mcp
72
+ │ HTTP + bearer token, bound to 127.0.0.1 only
73
+
74
+ Creavit Studio main process (agent bridge)
75
+ │ IPC
76
+
77
+ Renderer windows (editor / recorder) → the real app state
78
+ ```
79
+
80
+ On startup the app picks a random port, generates a fresh token, and writes an
81
+ endpoint descriptor:
82
+
83
+ ```
84
+ ~/Library/Application Support/creavit-studio/agent-bridge.json (mode 0600)
85
+ ```
86
+
87
+ The MCP server discovers the app through that file. Restarting the app does
88
+ **not** require restarting the MCP server — the endpoint is re-read on every
89
+ request.
90
+
91
+ The app must be running for most tools. Project inspection tools read `.crvt`
92
+ files directly and work with the app closed.
93
+
94
+ ## Tools
95
+
96
+ **App** — `creavit_app_info`, `creavit_app_window`, `creavit_app_mode`,
97
+ `creavit_permissions`, `creavit_events`
98
+
99
+ **Projects** — `creavit_project_list`, `creavit_project_summary`,
100
+ `creavit_project_manifest`, `creavit_project_open`, `creavit_project_save`,
101
+ `creavit_project_reveal`
102
+
103
+ > The first three work with the app closed.
104
+
105
+ **Editor** — `creavit_editor_state`, `creavit_editor_get_settings`,
106
+ `creavit_editor_set_settings`, `creavit_editor_list_zooms`,
107
+ `creavit_editor_add_zoom`, `creavit_editor_update_zoom`,
108
+ `creavit_editor_remove_zoom`, `creavit_editor_segments`,
109
+ `creavit_editor_set_segments`, `creavit_editor_seek`,
110
+ `creavit_editor_playback`, `creavit_editor_screenshot`,
111
+ `creavit_editor_export`, `creavit_editor_history`
112
+
113
+ **Recording** — `creavit_devices_list`, `creavit_recording_status`,
114
+ `creavit_recording_start`, `creavit_recording_stop`
115
+
116
+ **Escape hatches** — `creavit_capabilities`, `creavit_call`, `creavit_ipc`,
117
+ `creavit_eval`, `creavit_logs`
118
+
119
+ The typed tools cannot cover the whole app. `creavit_capabilities` lists
120
+ **every** command and editor action the app exposes; `creavit_call` invokes any
121
+ of them; `creavit_ipc` reaches all ~110 raw IPC channels. New app features
122
+ become reachable through these without changing the MCP server.
123
+
124
+ ## Typical flow
125
+
126
+ ```
127
+ creavit_app_info is the app running, which windows are open
128
+ creavit_project_list see available projects
129
+ creavit_project_summary understand the target project
130
+ creavit_project_open open it in the editor
131
+ creavit_editor_state poll until duration > 0 (loading takes seconds)
132
+ creavit_editor_screenshot see the current look
133
+ creavit_editor_set_settings change something
134
+ creavit_editor_screenshot VERIFY the result
135
+ creavit_project_save persist
136
+ ```
137
+
138
+ Two things worth knowing:
139
+
140
+ - **Wait for the load.** After `creavit_project_open`, poll `creavit_editor_state`
141
+ until `duration > 0`. Settings written before loading finishes get overwritten.
142
+ - **`padding`, `radius`, `shadowSize` are derived values.** Reads and writes are
143
+ transparently redirected to `basePadding` / `baseRadius` / `baseShadowSize`,
144
+ and `creavit_editor_set_settings` returns a `verified` block showing what was
145
+ actually stored. Check it rather than assuming a write landed.
146
+
147
+ ## Security
148
+
149
+ - Binds to `127.0.0.1` only; requests carrying an `Origin` header are rejected,
150
+ so no web page can reach it.
151
+ - Every request requires a bearer token, regenerated on each app launch.
152
+ - The endpoint file is written with `0600` permissions.
153
+ - Arbitrary code execution (`creavit_eval`) is **off by default**.
154
+
155
+ ### Environment variables
156
+
157
+ Set on the **app**, not the MCP server:
158
+
159
+ | Variable | Effect |
160
+ |---|---|
161
+ | `CREAVIT_AGENT_BRIDGE=0` | Disable the bridge entirely |
162
+ | `CREAVIT_AGENT_BRIDGE_PORT` | Pin a fixed port (default: pick a free one) |
163
+ | `CREAVIT_AGENT_BRIDGE_EVAL=1` | Enable the `creavit_eval` tool |
164
+
165
+ Set on the **MCP server**:
166
+
167
+ | Variable | Effect |
168
+ |---|---|
169
+ | `CREAVIT_AGENT_BRIDGE_ENDPOINT` | Explicit path to the endpoint descriptor |
170
+
171
+ ## Troubleshooting
172
+
173
+ **"Creavit Studio is not running"** — start the app. If it is running, check
174
+ that the bridge came up: its log line is `[AgentBridge] Ready → http://127.0.0.1:<port>`.
175
+
176
+ **"The 'editor' window is not open"** — editor tools need a project open. Call
177
+ `creavit_project_open` first.
178
+
179
+ **Agent says a tool does not exist** — MCP clients cache the tool list at
180
+ connect time. Restart the agent session after upgrading the server.
181
+
182
+ **Settings do not seem to apply** — check the `verified` field in the
183
+ `creavit_editor_set_settings` response, and make sure the project finished
184
+ loading (`creavit_editor_state` → `duration > 0`).
185
+
186
+ ## Extending
187
+
188
+ The MCP server is a thin, typed façade. To add a capability:
189
+
190
+ 1. **Main-process work** → add a command in `electron/agentBridge/commands/*.cjs`.
191
+ 2. **Needs renderer state** → add an action to the relevant host composable in
192
+ `composables/agent/`, then publish it via `proxy("<action>")` in
193
+ `electron/agentBridge/commands/editorCommands.cjs`.
194
+ 3. **Want a typed MCP tool** → add it in `mcp/src/tools/*.mjs`. Optional —
195
+ anything registered on the bridge is already reachable via `creavit_call`.
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ // Creavit Studio MCP sunucusu.
3
+ // Claude Code / Codex / Cursor gibi MCP istemcilerinin uygulamayı sürebilmesi
4
+ // için stdio üzerinden çalışır.
5
+ //
6
+ // Teşhis: `npx creavit-studio-mcp --doctor`
7
+
8
+ import { startRpcServer, log } from "../src/rpcServer.mjs";
9
+ import { listTools, callTool } from "../src/tools/index.mjs";
10
+ import { health, listBridgeCommands } from "../src/bridgeClient.mjs";
11
+ import { canReadOffline } from "../src/crvtReader.mjs";
12
+ import { SERVER_NAME, SERVER_VERSION } from "../src/protocol.mjs";
13
+
14
+ async function doctor() {
15
+ console.log(`${SERVER_NAME} v${SERVER_VERSION}\n`);
16
+
17
+ const tools = await listTools();
18
+ console.log(`✓ ${tools.length} MCP tools defined`);
19
+ console.log(
20
+ canReadOffline()
21
+ ? "✓ Offline project reading available (.crvt files readable without the app)"
22
+ : "! Offline project reading unavailable — install the `yauzl` dependency",
23
+ );
24
+
25
+ try {
26
+ const info = await health();
27
+ console.log(`✓ Connected to the app (v${info.appVersion}, pid ${info.pid})`);
28
+ console.log(` Open windows: ${info.windows}`);
29
+ console.log(
30
+ ` Agent-connected renderers: ${
31
+ info.rendererHosts?.map((h) => h.role).join(", ") || "none"
32
+ }`,
33
+ );
34
+ const { commands } = await listBridgeCommands();
35
+ console.log(`✓ ${commands.length} bridge commands available`);
36
+ console.log("\nEverything looks good.");
37
+ } catch (error) {
38
+ console.log(`✗ Could not connect to the app: ${error.message}`);
39
+ console.log("\n Start Creavit Studio, then run this again.");
40
+ console.log(" Project inspection tools work without the app.");
41
+ process.exitCode = 1;
42
+ }
43
+ }
44
+
45
+ if (process.argv.includes("--doctor")) {
46
+ await doctor();
47
+ } else if (process.argv.includes("--list-tools")) {
48
+ console.log(JSON.stringify(await listTools(), null, 2));
49
+ } else if (process.argv.includes("--version")) {
50
+ console.log(SERVER_VERSION);
51
+ } else if (process.argv.includes("--help")) {
52
+ console.log(
53
+ `${SERVER_NAME} v${SERVER_VERSION}\n\n` +
54
+ "MCP server that lets AI agents drive the Creavit Studio desktop app.\n\n" +
55
+ "Usage:\n" +
56
+ " creavit-studio-mcp Run as an MCP server over stdio (what MCP clients do)\n" +
57
+ " creavit-studio-mcp --doctor Check the connection to the app\n" +
58
+ " creavit-studio-mcp --list-tools Print tool schemas as JSON\n" +
59
+ " creavit-studio-mcp --version Print the version\n",
60
+ );
61
+ } else {
62
+ startRpcServer({ listTools, callTool });
63
+ log(`${SERVER_NAME} v${SERVER_VERSION} — ${(await listTools()).length} tools`);
64
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "creavit-studio-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server that lets AI coding agents (Claude Code, Codex, Cursor) drive the Creavit Studio screen recording app",
5
+ "keywords": [
6
+ "mcp",
7
+ "model-context-protocol",
8
+ "creavit-studio",
9
+ "screen-recording",
10
+ "claude",
11
+ "cursor",
12
+ "codex"
13
+ ],
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "bin": {
17
+ "creavit-studio-mcp": "bin/creavit-mcp.mjs"
18
+ },
19
+ "main": "./src/tools/index.mjs",
20
+ "files": [
21
+ "bin",
22
+ "src",
23
+ "README.md"
24
+ ],
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "os": [
29
+ "darwin"
30
+ ],
31
+ "dependencies": {
32
+ "yauzl": "^3.2.0"
33
+ },
34
+ "scripts": {
35
+ "doctor": "node bin/creavit-mcp.mjs --doctor",
36
+ "tools": "node bin/creavit-mcp.mjs --list-tools",
37
+ "prepublishOnly": "node bin/creavit-mcp.mjs --list-tools > /dev/null"
38
+ }
39
+ }
@@ -0,0 +1,133 @@
1
+ // MCP sunucusu — çalışan uygulamaya bağlanan HTTP istemcisi.
2
+ // Endpoint dosyası her uygulama açılışında yenilendiği için her istekten önce
3
+ // tazelik kontrolü yapılır; böylece uygulama yeniden başlatıldığında MCP
4
+ // sunucusunu da yeniden başlatmak gerekmez.
5
+
6
+ import fs from "node:fs";
7
+ import os from "node:os";
8
+ import path from "node:path";
9
+ import { ENDPOINT_FILENAME, APP_NOT_RUNNING_HINT } from "./protocol.mjs";
10
+
11
+ const DEFAULT_ENDPOINT_DIRS = [
12
+ path.join(os.homedir(), "Library", "Application Support", "creavit-studio"),
13
+ path.join(os.homedir(), "Library", "Application Support", "Creavit Studio"),
14
+ ];
15
+
16
+ export class BridgeUnavailableError extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = "BridgeUnavailableError";
20
+ }
21
+ }
22
+
23
+ function endpointCandidates() {
24
+ const fromEnv = process.env.CREAVIT_AGENT_BRIDGE_ENDPOINT;
25
+ const dirs = fromEnv ? [path.dirname(fromEnv)] : DEFAULT_ENDPOINT_DIRS;
26
+ const files = fromEnv ? [fromEnv] : dirs.map((d) => path.join(d, ENDPOINT_FILENAME));
27
+ return files;
28
+ }
29
+
30
+ function readEndpoint() {
31
+ for (const file of endpointCandidates()) {
32
+ try {
33
+ if (!fs.existsSync(file)) continue;
34
+ const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
35
+ if (parsed?.port && parsed?.token) return { ...parsed, file };
36
+ } catch (_) {}
37
+ }
38
+ return null;
39
+ }
40
+
41
+ let cached = null;
42
+
43
+ function currentEndpoint() {
44
+ const fresh = readEndpoint();
45
+ if (!fresh) {
46
+ cached = null;
47
+ throw new BridgeUnavailableError(APP_NOT_RUNNING_HINT);
48
+ }
49
+ // Port ya da token değiştiyse uygulama yeniden başlamış demektir.
50
+ if (!cached || cached.port !== fresh.port || cached.token !== fresh.token) {
51
+ cached = fresh;
52
+ }
53
+ return cached;
54
+ }
55
+
56
+ /**
57
+ * Köprüye istek at.
58
+ * @param {string} route "/rpc" | "/health" | "/commands" | "/events?since=1"
59
+ * @param {object} [options] { method, body, timeoutMs }
60
+ */
61
+ async function request(route, { method = "GET", body, timeoutMs = 120_000 } = {}) {
62
+ const endpoint = currentEndpoint();
63
+ const url = `http://${endpoint.host || "127.0.0.1"}:${endpoint.port}${route}`;
64
+
65
+ const controller = new AbortController();
66
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
67
+
68
+ let response;
69
+ try {
70
+ response = await fetch(url, {
71
+ method,
72
+ signal: controller.signal,
73
+ headers: {
74
+ Authorization: `Bearer ${endpoint.token}`,
75
+ ...(body ? { "Content-Type": "application/json" } : {}),
76
+ },
77
+ body: body ? JSON.stringify(body) : undefined,
78
+ });
79
+ } catch (error) {
80
+ cached = null;
81
+ if (error?.name === "AbortError") {
82
+ throw new Error(`Bridge did not respond within ${timeoutMs}ms: ${route}`);
83
+ }
84
+ throw new BridgeUnavailableError(
85
+ `${APP_NOT_RUNNING_HINT}\n(connection error: ${error?.message || error})`,
86
+ );
87
+ } finally {
88
+ clearTimeout(timer);
89
+ }
90
+
91
+ const text = await response.text();
92
+ let payload;
93
+ try {
94
+ payload = text ? JSON.parse(text) : {};
95
+ } catch (_) {
96
+ throw new Error(`Invalid response from bridge (${response.status}): ${text.slice(0, 200)}`);
97
+ }
98
+
99
+ if (!response.ok || payload?.ok === false) {
100
+ const err = payload?.error || {};
101
+ const error = new Error(err.message || `Bridge error (${response.status})`);
102
+ error.code = err.code || String(response.status);
103
+ error.details = err.details || null;
104
+ throw error;
105
+ }
106
+
107
+ return payload.result;
108
+ }
109
+
110
+ export async function callCommand(command, params, timeoutMs) {
111
+ return request("/rpc", {
112
+ method: "POST",
113
+ body: { command, params: params || {}, timeoutMs },
114
+ timeoutMs: timeoutMs || 120_000,
115
+ });
116
+ }
117
+
118
+ export async function health() {
119
+ return request("/health", { timeoutMs: 5_000 });
120
+ }
121
+
122
+ export async function listBridgeCommands() {
123
+ return request("/commands", { timeoutMs: 10_000 });
124
+ }
125
+
126
+ export async function isAppRunning() {
127
+ try {
128
+ await health();
129
+ return true;
130
+ } catch (_) {
131
+ return false;
132
+ }
133
+ }
@@ -0,0 +1,108 @@
1
+ // MCP sunucusu — .crvt projelerini uygulama olmadan okuma.
2
+ // .crvt bir ZIP'tir; manifest.json'u doğrudan okuruz. Böylece proje listeleme
3
+ // ve inceleme araçları Creavit Studio kapalıyken de çalışır.
4
+
5
+ import fs from "node:fs";
6
+ import os from "node:os";
7
+ import path from "node:path";
8
+ import { createRequire } from "node:module";
9
+
10
+ const require = createRequire(import.meta.url);
11
+
12
+ // yauzl paketin kendi bağımlılığıdır; depo içinden çalışırken üstteki
13
+ // node_modules'tan da çözülür. Bulunamazsa salt-okunur araçlar köprüye düşer.
14
+ let yauzl = null;
15
+ try {
16
+ yauzl = require("yauzl");
17
+ } catch (_) {}
18
+
19
+ export const canReadOffline = () => Boolean(yauzl);
20
+
21
+ export function defaultProjectsDir() {
22
+ return path.join(os.homedir(), "Downloads", "Creavit Studio");
23
+ }
24
+
25
+ export function readManifestOffline(zipPath) {
26
+ if (!yauzl) {
27
+ return Promise.reject(new Error("yauzl is not installed — offline reading is unavailable"));
28
+ }
29
+
30
+ return new Promise((resolve, reject) => {
31
+ yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
32
+ if (err) return reject(err);
33
+
34
+ let found = false;
35
+ zipfile.on("entry", (entry) => {
36
+ if (entry.fileName !== "manifest.json") {
37
+ zipfile.readEntry();
38
+ return;
39
+ }
40
+ found = true;
41
+ zipfile.openReadStream(entry, (streamErr, stream) => {
42
+ if (streamErr) return reject(streamErr);
43
+ const chunks = [];
44
+ stream.on("data", (c) => chunks.push(c));
45
+ stream.on("end", () => {
46
+ try {
47
+ resolve(JSON.parse(Buffer.concat(chunks).toString("utf8")));
48
+ } catch (parseErr) {
49
+ reject(new Error(`Could not parse manifest.json: ${parseErr.message}`));
50
+ }
51
+ zipfile.close();
52
+ });
53
+ stream.on("error", reject);
54
+ });
55
+ });
56
+ zipfile.on("end", () => {
57
+ if (!found) reject(new Error("manifest.json not found — not a valid .crvt file"));
58
+ });
59
+ zipfile.on("error", reject);
60
+ zipfile.readEntry();
61
+ });
62
+ });
63
+ }
64
+
65
+ export async function listProjectsOffline({ dir, limit } = {}) {
66
+ const root = dir || defaultProjectsDir();
67
+ if (!fs.existsSync(root)) return { dir: root, projects: [], source: "offline" };
68
+
69
+ const files = fs
70
+ .readdirSync(root)
71
+ .filter((f) => f.toLowerCase().endsWith(".crvt"))
72
+ .map((f) => {
73
+ const filePath = path.join(root, f);
74
+ const stat = fs.statSync(filePath);
75
+ return {
76
+ fileName: f,
77
+ filePath,
78
+ sizeBytes: stat.size,
79
+ modifiedAt: stat.mtime.toISOString(),
80
+ };
81
+ })
82
+ .sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt))
83
+ .slice(0, Number(limit) > 0 ? Number(limit) : 100);
84
+
85
+ const projects = await Promise.all(
86
+ files.map(async (file) => {
87
+ try {
88
+ const manifest = await readManifestOffline(file.filePath);
89
+ // Manifest v2'de süre klip başına tutulur.
90
+ const durationMs =
91
+ (manifest?.clips || []).reduce((acc, c) => acc + (Number(c?.durationMs) || 0), 0) ||
92
+ null;
93
+ return {
94
+ ...file,
95
+ name: manifest?.name || file.fileName.replace(/\.crvt$/i, ""),
96
+ durationMs,
97
+ durationSeconds: durationMs ? Number((durationMs / 1000).toFixed(2)) : null,
98
+ createdAt: manifest?.createdAt ?? null,
99
+ clipCount: (manifest?.clips || []).length,
100
+ };
101
+ } catch (_) {
102
+ return { ...file, name: file.fileName.replace(/\.crvt$/i, ""), manifestError: true };
103
+ }
104
+ }),
105
+ );
106
+
107
+ return { dir: root, projects, source: "offline" };
108
+ }
@@ -0,0 +1,88 @@
1
+ // .crvt manifest özeti (v2 şeması).
2
+ //
3
+ // Gerçek manifest şekli:
4
+ // { version, createdAt, name, projectSettings:{resolution,fps},
5
+ // playerSettings:{...}, clips:[{id,name,durationMs,media,segments,mouse}] }
6
+ //
7
+ // İki varyant var: taze kayıttan çıkan "hafif" manifest (playerSettings'te
8
+ // sadece birkaç anahtar) ve editörde kaydedilmiş "tam" manifest. Özet ikisini
9
+ // de aynı biçimde raporlar, eksik alanlar null kalır.
10
+ //
11
+ // Bu dosya MCP paketinin tek özet kaynağıdır — Electron köprüsü ham manifest
12
+ // döndürür, özetleme daima burada yapılır. Böylece mcp/ tek başına dağıtılabilir.
13
+
14
+ function sum(list, pick) {
15
+ return (list || []).reduce((acc, item) => acc + (Number(pick(item)) || 0), 0);
16
+ }
17
+
18
+ export function summarizeManifest(manifest, filePath) {
19
+ const ps = manifest?.playerSettings || {};
20
+ const clips = Array.isArray(manifest?.clips) ? manifest.clips : [];
21
+ const totalDurationMs = sum(clips, (c) => c.durationMs);
22
+
23
+ // Editörde kaydedilmiş manifest'te tüm ayar anahtarları bulunur; hafif
24
+ // manifest'te yoktur. Ajanın hangi durumda olduğunu bilmesi gerekiyor.
25
+ const isEdited = "zoomRanges" in ps || "cameraSettings" in ps;
26
+
27
+ return {
28
+ filePath,
29
+ name: manifest?.name || null,
30
+ manifestVersion: manifest?.version ?? null,
31
+ createdAt: manifest?.createdAt || null,
32
+ edited: isEdited,
33
+ durationMs: totalDurationMs || null,
34
+ durationSeconds: totalDurationMs ? Number((totalDurationMs / 1000).toFixed(2)) : null,
35
+ output: {
36
+ resolution: manifest?.projectSettings?.resolution || ps.canvasSize || null,
37
+ fps: manifest?.projectSettings?.fps ?? null,
38
+ cropRatio: ps.cropRatio ?? null,
39
+ },
40
+ look: {
41
+ backgroundType: ps.backgroundType ?? null,
42
+ backgroundColor: ps.backgroundColor ?? null,
43
+ backgroundImage: ps.backgroundImage || null,
44
+ backgroundBlur: ps.backgroundBlur ?? null,
45
+ padding: ps.padding ?? ps.basePadding ?? null,
46
+ radius: ps.radius ?? ps.baseRadius ?? null,
47
+ shadowSize: ps.shadowSize ?? ps.baseShadowSize ?? null,
48
+ },
49
+ clips: clips.map((clip) => ({
50
+ id: clip.id ?? null,
51
+ name: clip.name ?? null,
52
+ durationMs: clip.durationMs ?? null,
53
+ segmentCount: (clip.segments || []).length,
54
+ hasMouseData: Boolean(clip.mouse),
55
+ media: clip.media
56
+ ? Object.fromEntries(
57
+ Object.entries(clip.media).map(([k, v]) => [
58
+ k,
59
+ typeof v === "string" ? v : v?.entry || v?.path || true,
60
+ ]),
61
+ )
62
+ : null,
63
+ })),
64
+ counts: {
65
+ clips: clips.length,
66
+ segments: sum(clips, (c) => (c.segments || []).length),
67
+ zoomRanges: (ps.zoomRanges || []).length,
68
+ animation3DRanges: (ps.animation3DRanges || []).length,
69
+ codeSnippets: (ps.codeSnippets || []).length,
70
+ stepSegments: (ps.stepSegments || []).length,
71
+ layoutRanges: (manifest?.layoutRanges || []).length,
72
+ titleCards: (manifest?.titleCards || []).length,
73
+ soundEffectSegments: (manifest?.soundEffectSegments || []).length,
74
+ shortcutSegments: (manifest?.shortcutSegments || []).length,
75
+ },
76
+ effects: {
77
+ camera: ps.cameraSettings ? Boolean(ps.cameraSettings.visible) : null,
78
+ crtMonitor: ps.crtMonitorEffectEnabled ?? null,
79
+ cursorAnimation: ps.cursorAnimationEnabled ?? null,
80
+ velocityBlur: ps.velocityBlurEnabled ?? null,
81
+ ultrathink: ps.ultrathinkEnabled ?? null,
82
+ cursorTheme: ps.selectedCursorTheme ?? null,
83
+ },
84
+ note: isEdited
85
+ ? "This project has been saved from the editor — all player settings are present in the manifest."
86
+ : "This is a fresh recording: only basic settings exist. Opening it in the editor and saving writes the full settings.",
87
+ };
88
+ }
@@ -0,0 +1,24 @@
1
+ // MCP sunucusu — köprü protokolü sabitleri.
2
+ // electron/agentBridge/protocol.cjs ile eşleşmeli.
3
+
4
+ export const BRIDGE_PROTOCOL_VERSION = 1;
5
+ export const ENDPOINT_FILENAME = "agent-bridge.json";
6
+
7
+ export const MCP_PROTOCOL_VERSION = "2024-11-05";
8
+ export const SERVER_NAME = "creavit-studio";
9
+ export const SERVER_VERSION = "1.0.0";
10
+
11
+ export const JSONRPC_ERRORS = {
12
+ PARSE_ERROR: -32700,
13
+ INVALID_REQUEST: -32600,
14
+ METHOD_NOT_FOUND: -32601,
15
+ INVALID_PARAMS: -32602,
16
+ INTERNAL_ERROR: -32603,
17
+ };
18
+
19
+ // Uygulama kapalıyken ajana ne yapması gerektiğini net söyle.
20
+ export const APP_NOT_RUNNING_HINT =
21
+ "Creavit Studio is not running, or its agent bridge is disabled. Start the app " +
22
+ "(Creavit Studio.app, or `npm run electron:dev` in the repo). The bridge may also " +
23
+ "have been turned off with CREAVIT_AGENT_BRIDGE=0. Tools that work without the app: " +
24
+ "creavit_project_list, creavit_project_summary, creavit_project_manifest.";