Add MCP recording controls
CI / Lint (push) Waiting to run
CI / Type Check (push) Waiting to run
CI / Test (push) Waiting to run
CI / Build (push) Waiting to run
Bump Nix package on release / bump (release) Waiting to run
Update Homebrew Cask / update-cask (release) Waiting to run
CI / Lint (push) Waiting to run
CI / Type Check (push) Waiting to run
CI / Test (push) Waiting to run
CI / Build (push) Waiting to run
Bump Nix package on release / bump (release) Waiting to run
Update Homebrew Cask / update-cask (release) Waiting to run
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { z } from "zod";
|
||||
|
||||
const controlUrl = process.env.OPENSCREEN_MCP_CONTROL_URL || "http://127.0.0.1:52347";
|
||||
const token = process.env.OPENSCREEN_MCP_CONTROL_TOKEN;
|
||||
|
||||
async function callOpenScreen(action, payload = {}) {
|
||||
const response = await fetch(`${controlUrl.replace(/\/$/, "")}/mcp/${action}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
...(token ? { authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
const result = await response.json().catch(() => ({
|
||||
success: false,
|
||||
error: `OpenScreen returned HTTP ${response.status}`,
|
||||
}));
|
||||
if (!response.ok || !result.success) {
|
||||
throw new Error(
|
||||
result.error || result.message || `OpenScreen returned HTTP ${response.status}`,
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function textResult(result) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(result, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const server = new McpServer({
|
||||
name: "openscreen",
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
server.registerTool(
|
||||
"list_sources",
|
||||
{
|
||||
title: "List OpenScreen capture sources",
|
||||
description: "List available screen and window sources that can be passed to record_video.",
|
||||
inputSchema: {},
|
||||
},
|
||||
async () => textResult(await callOpenScreen("list_sources")),
|
||||
);
|
||||
|
||||
server.registerTool(
|
||||
"record_video",
|
||||
{
|
||||
title: "Start OpenScreen recording",
|
||||
description:
|
||||
"Start recording with a selected screen/window source, or the current/default source.",
|
||||
inputSchema: {
|
||||
guideMode: z.boolean().optional().describe("Enable Guide Mode for this recording."),
|
||||
sourceType: z
|
||||
.enum(["screen", "window"])
|
||||
.optional()
|
||||
.describe("Capture a screen/display or a window."),
|
||||
sourceId: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Exact source id returned by list_sources, for example screen:0:0."),
|
||||
sourceName: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Exact or partial source/window name to match when sourceId is not supplied."),
|
||||
displayIndex: z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.optional()
|
||||
.describe("Zero-based display index for screen capture."),
|
||||
},
|
||||
},
|
||||
async (input) => textResult(await callOpenScreen("record_video", input)),
|
||||
);
|
||||
|
||||
server.registerTool(
|
||||
"stop_recording",
|
||||
{
|
||||
title: "Stop OpenScreen recording",
|
||||
description: "Stop the active OpenScreen recording and return the saved video file URL.",
|
||||
inputSchema: {
|
||||
discard: z.boolean().optional().describe("Discard the recording instead of saving it."),
|
||||
},
|
||||
},
|
||||
async (input) => textResult(await callOpenScreen("stop_recording", input)),
|
||||
);
|
||||
|
||||
server.registerTool(
|
||||
"export_video",
|
||||
{
|
||||
title: "Export OpenScreen video",
|
||||
description:
|
||||
"Export the currently loaded OpenScreen editor project and return the exported file URL.",
|
||||
inputSchema: {
|
||||
outputPath: z.string().optional().describe("Absolute output path. Defaults to Downloads."),
|
||||
format: z.enum(["mp4", "gif"]).optional().describe("Export format. Defaults to mp4."),
|
||||
quality: z.enum(["medium", "good", "source"]).optional().describe("MP4 quality preset."),
|
||||
},
|
||||
},
|
||||
async ({ outputPath, format, quality }) =>
|
||||
textResult(
|
||||
await callOpenScreen("export_video", {
|
||||
outputPath,
|
||||
settings: {
|
||||
format: format ?? "mp4",
|
||||
quality: quality ?? "good",
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
server.registerTool(
|
||||
"status",
|
||||
{
|
||||
title: "Get OpenScreen status",
|
||||
description: "Return whether OpenScreen is currently recording.",
|
||||
inputSchema: {},
|
||||
},
|
||||
async () => textResult(await callOpenScreen("status")),
|
||||
);
|
||||
|
||||
await server.connect(new StdioServerTransport());
|
||||
Reference in New Issue
Block a user