dcc-mcp-openscreen 0.1.1__py3-none-any.whl

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.
@@ -0,0 +1,2 @@
1
+ from .server import OpenScreenMcpServer
2
+ __all__ = ["OpenScreenMcpServer"]
@@ -0,0 +1,20 @@
1
+ """Standalone OpenScreen MCP adapter."""
2
+ from __future__ import annotations
3
+ import argparse, os, signal, threading
4
+ from pathlib import Path
5
+ from dcc_mcp_core import DccServerOptions
6
+ from dcc_mcp_core.server_base import DccServerBase
7
+ class OpenScreenMcpServer(DccServerBase):
8
+ def __init__(self, *, port: int | None = None):
9
+ options = DccServerOptions.from_env("openscreen", Path(__file__).parent / "skills", port=port, server_name="dcc-mcp-openscreen", instance_type="standalone", adapter_version="0.1.0", standalone_main_thread=False)
10
+ super().__init__(options=options)
11
+ def main(argv=None):
12
+ p=argparse.ArgumentParser(); p.add_argument("--mcp-port", type=int); a=p.parse_args(argv)
13
+ server=OpenScreenMcpServer(port=a.mcp_port); server.register_builtin_actions(); server.start()
14
+ stopped=threading.Event(); signal.signal(signal.SIGINT, lambda *_: stopped.set())
15
+ if hasattr(signal,"SIGTERM"): signal.signal(signal.SIGTERM, lambda *_: stopped.set())
16
+ try: stopped.wait()
17
+ finally: server.stop()
18
+
19
+ if __name__ == '__main__':
20
+ main()
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+ import json, os, subprocess, time
3
+ from pathlib import Path
4
+
5
+ def executable() -> str:
6
+ return os.getenv("DCC_MCP_OPENSCREEN_PATH", "openscreen")
7
+
8
+ def _events(stdout: str) -> list[dict]:
9
+ out=[]
10
+ for line in stdout.splitlines():
11
+ try: out.append(json.loads(line))
12
+ except json.JSONDecodeError: pass
13
+ return out
14
+
15
+ def run(args: list[str], *, timeout: float = 900.0) -> dict:
16
+ try:
17
+ proc=subprocess.run([executable(), *args, "--json"], capture_output=True, text=True, timeout=timeout, check=False)
18
+ except subprocess.TimeoutExpired as exc:
19
+ raise RuntimeError(f"OpenScreen CLI timed out after {timeout:.0f}s; verify permissions and headless build") from exc
20
+ return _finish(proc.returncode, proc.stdout, proc.stderr)
21
+
22
+ def record(args: list[str], *, duration: int, timeout: float) -> dict:
23
+ proc=subprocess.Popen([executable(), "record", *args, "--json"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
24
+ try:
25
+ time.sleep(duration)
26
+ if proc.poll() is None and proc.stdin:
27
+ proc.stdin.write("stop\n"); proc.stdin.flush()
28
+ stdout, stderr=proc.communicate(timeout=max(10.0, timeout-duration))
29
+ except subprocess.TimeoutExpired as exc:
30
+ if os.name == "nt":\n subprocess.run(["taskkill", "/PID", str(proc.pid), "/T", "/F"], capture_output=True, check=False)\n else:\n proc.kill()\n proc.communicate()
31
+ raise RuntimeError("OpenScreen recording did not stop after a cooperative stop request") from exc
32
+ return _finish(proc.returncode, stdout, stderr)
33
+
34
+ def _finish(code: int, stdout: str, stderr: str) -> dict:
35
+ events=_events(stdout)
36
+ done=next((e for e in reversed(events) if e.get("event")=="done"), None)
37
+ if code != 0 or not done or done.get("success") is False:
38
+ detail=(stderr or "OpenScreen command failed").strip()
39
+ if not events and not detail: detail="OpenScreen produced no JSON events; verify a headless-capable build"
40
+ raise RuntimeError(detail[-2000:])
41
+ return done
42
+
43
+ def path_arg(value: str) -> str:
44
+ path=Path(value).expanduser().resolve()
45
+ if len(str(path))>4096: raise ValueError("path too long")
46
+ return str(path)
47
+
@@ -0,0 +1,4 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+ from ._client import run, path_arg
3
+ @skill_entry
4
+ def main(project, min_words=2, max_words=7, **_): return skill_success("OpenScreen captions generated.", **run(["captions", path_arg(project), "--min-words", str(min_words), "--max-words", str(max_words)], timeout=1800))
@@ -0,0 +1,7 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+ from ._client import run, path_arg
3
+ @skill_entry
4
+ def main(project, output=None, quality="source", **_):
5
+ args=["export",path_arg(project),"--quality",quality]
6
+ if output: args += ["--out",path_arg(output)]
7
+ return skill_success("OpenScreen export completed.", **run(args, timeout=1800))
@@ -0,0 +1,4 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+ from ._client import run, path_arg
3
+ @skill_entry
4
+ def main(project, **_): return skill_success("OpenScreen project inspected.", **run(["info", path_arg(project)]))
@@ -0,0 +1,4 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+ from ._client import run, path_arg
3
+ @skill_entry
4
+ def main(project, output, **_): return skill_success("OpenScreen project packed.", **run(["pack", path_arg(project), "--out", path_arg(output)], timeout=600))
@@ -0,0 +1,11 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+ from ._client import record, path_arg
3
+ @skill_entry
4
+ def main(duration=30, window=None, display=0, project=None, mic=False, system_audio=False, **_):
5
+ if not isinstance(duration,int) or duration<1 or duration>3600: raise ValueError("duration must be 1..3600 seconds")
6
+ args=["--display",str(display)]
7
+ if window: args += ["--window",str(window)]
8
+ if project: args += ["--project",path_arg(project)]
9
+ if mic: args += ["--mic"]
10
+ if system_audio: args += ["--system-audio"]
11
+ return skill_success("OpenScreen recording completed.", **record(args,duration=duration,timeout=duration+120))
@@ -0,0 +1,4 @@
1
+ from dcc_mcp_core.skill import skill_entry, skill_success
2
+ from ._client import run
3
+ @skill_entry
4
+ def main(**_): return skill_success("OpenScreen sources enumerated.", **run(["sources"]))
@@ -0,0 +1,67 @@
1
+ skill: openscreen
2
+ version: "0.1.0"
3
+ description: Record and export OpenScreen projects through its typed headless CLI.
4
+ tools:
5
+ - name: sources
6
+ description: List available displays, windows, and microphones.
7
+ source_file: scripts/sources.py
8
+ input_schema: {type: object, additionalProperties: false}
9
+ execution: sync
10
+ affinity: any
11
+ annotations: {read_only_hint: true, destructive_hint: false, idempotent_hint: true, open_world_hint: false}
12
+ - name: record
13
+ description: Record a bounded display or window capture to an optional OpenScreen project.
14
+ source_file: scripts/record.py
15
+ input_schema:
16
+ type: object
17
+ additionalProperties: false
18
+ properties:
19
+ duration: {type: integer, minimum: 1, maximum: 3600, default: 30}
20
+ window: {type: string, minLength: 1, maxLength: 256}
21
+ display: {type: integer, minimum: 0, maximum: 32, default: 0}
22
+ project: {type: string, maxLength: 4096}
23
+ mic: {type: boolean, default: false}
24
+ system_audio: {type: boolean, default: false}
25
+ execution: sync
26
+ affinity: any
27
+ timeout_hint_secs: 3720
28
+ annotations: {read_only_hint: false, destructive_hint: true, idempotent_hint: false, open_world_hint: false}
29
+ - name: export
30
+ description: Render an OpenScreen project to MP4 or GIF.
31
+ source_file: scripts/export.py
32
+ input_schema:
33
+ type: object
34
+ required: [project]
35
+ additionalProperties: false
36
+ properties:
37
+ project: {type: string, minLength: 1, maxLength: 4096}
38
+ output: {type: string, maxLength: 4096}
39
+ quality: {enum: [medium, good, source], default: source}
40
+ execution: sync
41
+ affinity: any
42
+ timeout_hint_secs: 1800
43
+ annotations: {read_only_hint: false, destructive_hint: true, idempotent_hint: true, open_world_hint: false}
44
+ - name: info
45
+ description: Inspect a project and verify referenced media.
46
+ source_file: scripts/info.py
47
+ input_schema: {type: object, required: [project], additionalProperties: false, properties: {project: {type: string, minLength: 1, maxLength: 4096}}}
48
+ execution: sync
49
+ affinity: any
50
+ timeout_hint_secs: 60
51
+ annotations: {read_only_hint: true, destructive_hint: false, idempotent_hint: true, open_world_hint: false}
52
+ - name: pack
53
+ description: Pack a project and referenced media into a portable folder.
54
+ source_file: scripts/pack.py
55
+ input_schema: {type: object, required: [project, output], additionalProperties: false, properties: {project: {type: string, minLength: 1, maxLength: 4096}, output: {type: string, minLength: 1, maxLength: 4096}}}
56
+ execution: sync
57
+ affinity: any
58
+ timeout_hint_secs: 600
59
+ annotations: {read_only_hint: false, destructive_hint: true, idempotent_hint: true, open_world_hint: false}
60
+ - name: captions
61
+ description: Generate local Whisper captions for a project with bounded word grouping.
62
+ source_file: scripts/captions.py
63
+ input_schema: {type: object, required: [project], additionalProperties: false, properties: {project: {type: string, minLength: 1, maxLength: 4096}, min_words: {type: integer, minimum: 1, maximum: 20, default: 2}, max_words: {type: integer, minimum: 1, maximum: 20, default: 7}}}
64
+ execution: sync
65
+ affinity: any
66
+ timeout_hint_secs: 1800
67
+ annotations: {read_only_hint: false, destructive_hint: true, idempotent_hint: true, open_world_hint: false}
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: dcc-mcp-openscreen
3
+ Version: 0.1.1
4
+ Summary: OpenScreen recording and demo export adapter for DCC-MCP
5
+ Author-email: Long Hao <hal.long@outlook.com>
6
+ License: MIT
7
+ Requires-Python: >=3.7
8
+ Requires-Dist: dcc-mcp-core<1.0.0,>=0.20.14
9
+ Description-Content-Type: text/markdown
10
+
11
+ # dcc-mcp-openscreen
12
+
13
+ Standalone DCC-MCP adapter for [OpenScreen](https://github.com/getopenscreen/openscreen).
14
+
15
+ The adapter exposes only typed, bounded CLI operations. Set `DCC_MCP_OPENSCREEN_PATH` when the executable is not on `PATH`. It does not automate the desktop UI; DCC window selection and unsupported UI actions remain owned by `dcc-cua` / `ui-control`.
16
+
17
+ ## Run
18
+
19
+ ```powershell
20
+ python -m pip install -e .
21
+ dcc-mcp-openscreen
22
+ ```
23
+
24
+ Tools: `openscreen__sources`, `openscreen__record`, and `openscreen__export`.
25
+
26
+ ## License
27
+
28
+ MIT
@@ -0,0 +1,14 @@
1
+ dcc_mcp_openscreen/__init__.py,sha256=GzgC6No-VRRRJbserj4W6nBdeaOYmTLHcxc0_1JnpG4,74
2
+ dcc_mcp_openscreen/server.py,sha256=_qZDFbrnm6JSlYFzdztm-BWt5TDRLx8ChB1mLla5M0M,1069
3
+ dcc_mcp_openscreen/skills/openscreen/tools.yaml,sha256=hd4_xBiU7f9D-kRJJURXaNF6HxXBhD59BChbeRsXROI,3376
4
+ dcc_mcp_openscreen/skills/openscreen/scripts/_client.py,sha256=1h2YjFIZNI0SPnq9T4270ESf8Bw2F_595HqMplNS4EU,2274
5
+ dcc_mcp_openscreen/skills/openscreen/scripts/captions.py,sha256=M8OsrwnadUD17IbMaw7KaFuR4szqhIMRdjiqCkahpBk,327
6
+ dcc_mcp_openscreen/skills/openscreen/scripts/export.py,sha256=SWltsgjgCtmeG0nnqTsKwtxHzb592XYm4ygIoUfqoKM,353
7
+ dcc_mcp_openscreen/skills/openscreen/scripts/info.py,sha256=3dDv4vnLmR6UZGoJAt8l-9Ua3_qaABOu_SflBh9rlH8,220
8
+ dcc_mcp_openscreen/skills/openscreen/scripts/pack.py,sha256=ddKtAs_uqHKYc3R_WqRyQjwt8g9p4gA-Y0jwrhYI6oo,265
9
+ dcc_mcp_openscreen/skills/openscreen/scripts/record.py,sha256=lACbFOb99jyczSr-vqjhBdnnV6ANB_SSCVnqfslyEQI,660
10
+ dcc_mcp_openscreen/skills/openscreen/scripts/sources.py,sha256=OqSKegAN-_6suUWLXK5tCmoBQ1ZARWho9PdrY7qibjY,186
11
+ dcc_mcp_openscreen-0.1.1.dist-info/METADATA,sha256=GdmyiFCWPL7YkeyczHWEnEvWHdnaymIjThxmN-rjzQA,828
12
+ dcc_mcp_openscreen-0.1.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
13
+ dcc_mcp_openscreen-0.1.1.dist-info/entry_points.txt,sha256=nx5HVrUKcDOko0CvJ42TKG2noBMLXemRK7FJJQ4yT6I,142
14
+ dcc_mcp_openscreen-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ dcc-mcp-openscreen = dcc_mcp_openscreen.server:main
3
+
4
+ [dcc_mcp.adapters]
5
+ openscreen = dcc_mcp_openscreen:OpenScreenMcpServer