mcp-virtual-computer 0.2.3__tar.gz

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.
Files changed (53) hide show
  1. mcp_virtual_computer-0.2.3/LICENSE +7 -0
  2. mcp_virtual_computer-0.2.3/PKG-INFO +179 -0
  3. mcp_virtual_computer-0.2.3/README.md +162 -0
  4. mcp_virtual_computer-0.2.3/pyproject.toml +80 -0
  5. mcp_virtual_computer-0.2.3/setup.cfg +4 -0
  6. mcp_virtual_computer-0.2.3/src/kilntainers/__init__.py +3 -0
  7. mcp_virtual_computer-0.2.3/src/kilntainers/__main__.py +6 -0
  8. mcp_virtual_computer-0.2.3/src/kilntainers/auth.py +34 -0
  9. mcp_virtual_computer-0.2.3/src/kilntainers/backends/__init__.py +17 -0
  10. mcp_virtual_computer-0.2.3/src/kilntainers/backends/base.py +380 -0
  11. mcp_virtual_computer-0.2.3/src/kilntainers/backends/docker.py +1498 -0
  12. mcp_virtual_computer-0.2.3/src/kilntainers/backends/test___init__.py +37 -0
  13. mcp_virtual_computer-0.2.3/src/kilntainers/backends/test_base.py +355 -0
  14. mcp_virtual_computer-0.2.3/src/kilntainers/backends/test_docker.py +841 -0
  15. mcp_virtual_computer-0.2.3/src/kilntainers/backends/test_docker_integration.py +310 -0
  16. mcp_virtual_computer-0.2.3/src/kilntainers/backends/test_docker_management.py +122 -0
  17. mcp_virtual_computer-0.2.3/src/kilntainers/backends/test_utils.py +152 -0
  18. mcp_virtual_computer-0.2.3/src/kilntainers/backends/test_virtual_docker.py +387 -0
  19. mcp_virtual_computer-0.2.3/src/kilntainers/cli.py +375 -0
  20. mcp_virtual_computer-0.2.3/src/kilntainers/computers.py +284 -0
  21. mcp_virtual_computer-0.2.3/src/kilntainers/config.py +71 -0
  22. mcp_virtual_computer-0.2.3/src/kilntainers/dashboard.html +4263 -0
  23. mcp_virtual_computer-0.2.3/src/kilntainers/dashboard.py +23 -0
  24. mcp_virtual_computer-0.2.3/src/kilntainers/desktop.py +47 -0
  25. mcp_virtual_computer-0.2.3/src/kilntainers/desktop_control.py +105 -0
  26. mcp_virtual_computer-0.2.3/src/kilntainers/desktop_image/Dockerfile +67 -0
  27. mcp_virtual_computer-0.2.3/src/kilntainers/desktop_image/desktop-control.py +661 -0
  28. mcp_virtual_computer-0.2.3/src/kilntainers/desktop_image/start-desktop.sh +206 -0
  29. mcp_virtual_computer-0.2.3/src/kilntainers/desktop_image/visual-action.py +185 -0
  30. mcp_virtual_computer-0.2.3/src/kilntainers/desktop_image/wsproxy.py +218 -0
  31. mcp_virtual_computer-0.2.3/src/kilntainers/errors.py +36 -0
  32. mcp_virtual_computer-0.2.3/src/kilntainers/file_tools.py +320 -0
  33. mcp_virtual_computer-0.2.3/src/kilntainers/server.py +1760 -0
  34. mcp_virtual_computer-0.2.3/src/kilntainers/test_cli.py +704 -0
  35. mcp_virtual_computer-0.2.3/src/kilntainers/test_cli_integration.py +213 -0
  36. mcp_virtual_computer-0.2.3/src/kilntainers/test_computers.py +122 -0
  37. mcp_virtual_computer-0.2.3/src/kilntainers/test_config.py +135 -0
  38. mcp_virtual_computer-0.2.3/src/kilntainers/test_dashboard.py +113 -0
  39. mcp_virtual_computer-0.2.3/src/kilntainers/test_desktop_control.py +109 -0
  40. mcp_virtual_computer-0.2.3/src/kilntainers/test_e2e_mcp.py +310 -0
  41. mcp_virtual_computer-0.2.3/src/kilntainers/test_errors.py +62 -0
  42. mcp_virtual_computer-0.2.3/src/kilntainers/test_file_tools.py +175 -0
  43. mcp_virtual_computer-0.2.3/src/kilntainers/test_http_lifecycle.py +353 -0
  44. mcp_virtual_computer-0.2.3/src/kilntainers/test_lifecycle_integration.py +389 -0
  45. mcp_virtual_computer-0.2.3/src/kilntainers/test_server.py +849 -0
  46. mcp_virtual_computer-0.2.3/src/kilntainers/test_windows_docker.py +300 -0
  47. mcp_virtual_computer-0.2.3/src/kilntainers/windows_docker.py +565 -0
  48. mcp_virtual_computer-0.2.3/src/mcp_virtual_computer.egg-info/PKG-INFO +179 -0
  49. mcp_virtual_computer-0.2.3/src/mcp_virtual_computer.egg-info/SOURCES.txt +51 -0
  50. mcp_virtual_computer-0.2.3/src/mcp_virtual_computer.egg-info/dependency_links.txt +1 -0
  51. mcp_virtual_computer-0.2.3/src/mcp_virtual_computer.egg-info/entry_points.txt +5 -0
  52. mcp_virtual_computer-0.2.3/src/mcp_virtual_computer.egg-info/requires.txt +1 -0
  53. mcp_virtual_computer-0.2.3/src/mcp_virtual_computer.egg-info/top_level.txt +2 -0
@@ -0,0 +1,7 @@
1
+ Copyright 2026 - Steve Cosman, Chesterfield Laboratories Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-virtual-computer
3
+ Version: 0.2.3
4
+ Summary: A persistent Docker computer with a cinematic Three.js MCP App
5
+ Author-email: "Steve Cosman, Chesterfield Laboratories Inc" <scosman@users.noreply.github.com>
6
+ Project-URL: Homepage, https://github.com/flujo-app/mcp-virtual-computer
7
+ Project-URL: Repository, https://github.com/flujo-app/mcp-virtual-computer
8
+ Project-URL: Issues, https://github.com/flujo-app/mcp-virtual-computer/issues
9
+ Project-URL: Upstream, https://github.com/Kiln-AI/Kilntainers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Intended Audience :: Developers
12
+ Requires-Python: >=3.13
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: mcp<2,>=1.26.0
16
+ Dynamic: license-file
17
+
18
+ # MCP Virtual Computer
19
+
20
+ <!-- mcp-name: io.github.flujo-app/mcp-virtual-computer -->
21
+
22
+ A local, persistent Docker computer presented as a Three.js laptop on a desk. The MCP server keeps terminal automation from Kilntainers and adds UTF-8 file tools with a computer-screen view.
23
+
24
+ This first slice is Docker-only. It does not provision online or temporary machines.
25
+
26
+ ## What it exposes
27
+
28
+ - `terminal_execute` — run a command in the configured Docker computer.
29
+ - `read_file` — read a UTF-8 text file relative to `/workspace` or by absolute path.
30
+ - `write_file` — atomically write a UTF-8 text file.
31
+ - `edit_file` — replace one exact text match, or all matches when requested.
32
+ - `computer_ui` — open or attach the Three.js computer view.
33
+
34
+ With `DESKTOP_ENVIRONMENT=true`, it additionally exposes:
35
+
36
+ - `look_at_screen` — return the current PNG framebuffer, AT-SPI snapshot, or both.
37
+ - `click`, `type`, and `scroll` — interact by AT-SPI element reference or coordinates.
38
+ - `list_windows` and `switch_window` — enumerate and activate real Xfce windows.
39
+ - `move_window`, `maximize_window`, `restore_window`, `minimize_window`, and `close_window` — control a selected window by ID, title, or class.
40
+ - `computer://screen/current.png` — current live framebuffer resource.
41
+ - `computer://screen/accessibility.json` — Playwright-like AT-SPI tree with element refs, roles, names, actions, focus state, and screen bounds.
42
+
43
+ The computer ID is server configuration, not a tool argument. Lifecycle tools and temporary-computer arguments are not exposed.
44
+
45
+ ## Required configuration
46
+
47
+ ```env
48
+ COMPUTER_ID=agent-workstation
49
+ DESKTOP_ENVIRONMENT=false
50
+ NETWORK_ACCESS=true
51
+ AUTO_INSTALL_DOCKER=true
52
+ ```
53
+
54
+ `COMPUTER_ID` is required and selects the one persistent Docker computer. `DESKTOP_ENVIRONMENT` accepts `true`, `false`, `1`, `0`, `yes`, `no`, `on`, or `off`, and defaults to `false`. `NETWORK_ACCESS` accepts the same values and defaults to `true`. On Windows, `AUTO_INSTALL_DOCKER` accepts the same values and defaults to `true`.
55
+
56
+ ### Lazy Docker setup on Windows
57
+
58
+ The MCP transport and App start without waiting for Docker. The first tool that needs the computer checks the default `docker` engine. When Docker is missing, the server uses WinGet's exact `Docker.DockerDesktop` package, requests Docker's recommended per-user install, accepts the Docker license for unattended startup, adds the discovered CLI directory to both the current process and the current user's `PATH`, starts Docker Desktop, and resumes the original tool call.
59
+
60
+ The Three.js screen shows the observed setup phase. While WinGet downloads Docker it says `Downloading Docker...`; when WinGet supplies byte totals, the bar and percentage use those real totals, otherwise the bar is explicitly indeterminate. Calls that include an MCP progress token also receive standard `notifications/progress`. Installation is shielded from client cancellation, so it continues if a client's roughly five-minute tool timeout expires; retrying the call waits for or uses the same setup task.
61
+
62
+ Set `AUTO_INSTALL_DOCKER=false` to require a preinstalled runtime. `DOCKER_INSTALL_TIMEOUT` defaults to 1200 seconds and `DOCKER_START_TIMEOUT` defaults to 240 seconds. A fresh PC can still require one elevated `wsl --install`/`wsl --update`, a Windows restart, or BIOS/UEFI virtualization; the App reports that condition instead of looping or claiming success.
63
+
64
+ These environment values are startup defaults. New default computers always use the bundled desktop-capable image, even when Xfce starts off. In the Three.js scene, click the LAN cable to really enable or disable the running container's outbound network; the container keeps its loopback-published noVNC transport and applies an outbound firewall inside its network namespace. Click the two-sided mug (`I <3 virtual desktops` / `I <3 real desktops`) to stop or start Xfce inside that same container. The container ID and its entire writable filesystem—not only `/workspace`—stay intact.
65
+
66
+ ## The two screen modes
67
+
68
+ With `DESKTOP_ENVIRONMENT=false`, Xfce is stopped and the laptop shows a virtual desktop. The desktop services remain installed so Xfce can be enabled without recreating the computer. The screen stays idle until a genuine MCP invocation arrives, then renders Terminal, Files, or Text Editor actions from that invocation's actual arguments and result.
69
+
70
+ The Three.js laptop has a labeled QWERTY keyboard whose physical keys follow genuine `write_file` text, plus a wireless desk mouse that mirrors the on-screen pointer during real file navigation, selection, and reading.
71
+
72
+ The display is also directly controllable. Pointer input is raycast onto the angled 3D screen: double-click Files or Workspace, navigate real Docker folders, open text files, edit with the physical keyboard, and press Ctrl+S to save through `write_file`. Right-click inside Files to create a new text file; it opens unsaved in the editor until Ctrl+S is pressed. The red title-bar control closes the active virtual window. The virtual terminal executes typed commands on Enter. With the real desktop enabled, the same pointer and keyboard input is forwarded to the live VNC session. Filesystem and terminal interaction requires the App to be opened through an MCP host; the standalone static preview never invents directory contents.
73
+
74
+ With `DESKTOP_ENVIRONMENT=true`, the server builds the bundled Debian Bookworm/Xfce image on first use. The laptop then displays that container's real X11 framebuffer through noVNC. File tools drive real Thunar and Mousepad input through AT-SPI/Dogtail, `xdotool`, and `wmctrl`; the file operation itself remains deterministic and completes before the MCP result is returned. `terminal_execute` runs exactly once in a visible Xfce terminal, streams the same stdout and stderr to that window, and returns the captured exit status and output through MCP.
75
+
76
+ Sound from Xfce applications is routed through a 48 kHz stereo PulseAudio sink and streamed from the same loopback-only desktop endpoint into the dashboard. Browsers require a user gesture before playing audio, so click anywhere in the Three.js scene once after loading or refreshing it. Audio is sourced only from the real desktop; virtual mode does not synthesize sound.
77
+
78
+ Accessibility element references use paths such as `atspi:8/0/0/2`. They describe the current tree, so clients should call `look_at_screen` again after navigation or major window changes before reusing a reference.
79
+
80
+ The desktop video/input and audio WebSockets are published on `127.0.0.1` only.
81
+
82
+ ## Run locally
83
+
84
+ Requirements:
85
+
86
+ - Python 3.13+
87
+ - `uv`
88
+ - Windows 10/11: WinGet (Docker Desktop is installed lazily when needed)
89
+ - Other platforms: Docker Engine or Docker Desktop with a running daemon
90
+
91
+ For stdio:
92
+
93
+ ```bash
94
+ COMPUTER_ID=agent-workstation uv run mcp-virtual-computer
95
+ ```
96
+
97
+ For streamable HTTP:
98
+
99
+ ```bash
100
+ COMPUTER_ID=agent-workstation uv run mcp-virtual-computer \
101
+ --transport http \
102
+ --host 127.0.0.1 \
103
+ --port 8080 \
104
+ --allow-unauthenticated-http
105
+ ```
106
+
107
+ Enable the real desktop:
108
+
109
+ ```bash
110
+ COMPUTER_ID=agent-workstation DESKTOP_ENVIRONMENT=true \
111
+ uv run mcp-virtual-computer
112
+ ```
113
+
114
+ Or use Compose:
115
+
116
+ ```bash
117
+ COMPUTER_ID=agent-workstation DESKTOP_ENVIRONMENT=true docker compose up --build
118
+ ```
119
+
120
+ The Compose controller mounts `/var/run/docker.sock` so it can create and reattach the persistent workstation container.
121
+
122
+ ## MCP client example
123
+
124
+ ```json
125
+ {
126
+ "mcpServers": {
127
+ "virtual-computer": {
128
+ "command": "uvx",
129
+ "args": ["mcp-virtual-computer"],
130
+ "env": {
131
+ "COMPUTER_ID": "agent-workstation",
132
+ "DESKTOP_ENVIRONMENT": "false",
133
+ "NETWORK_ACCESS": "true",
134
+ "AUTO_INSTALL_DOCKER": "true"
135
+ }
136
+ }
137
+ }
138
+ }
139
+ ```
140
+
141
+ ## Build the MCP App
142
+
143
+ The bundled dashboard is generated from `src/virtual-computer` and packaged as `src/kilntainers/dashboard.html`.
144
+
145
+ ```bash
146
+ npm install
147
+ npm run build:app
148
+ ```
149
+
150
+ Opening `dashboard.html` directly shows an idle virtual desktop. MCP-hosted operation visuals begin only when the host supplies a real tool event.
151
+
152
+ ## File semantics
153
+
154
+ - Paths without a leading slash resolve below `/workspace`.
155
+ - File content must be valid UTF-8 text.
156
+ - Reads and writes default to a 1 MiB text limit.
157
+ - Writes use a temporary file plus rename.
158
+ - Edits require an exact match and reject ambiguous single replacements.
159
+ - Internal SHA-256 checks prevent stale writes during edits.
160
+
161
+ ## Development checks
162
+
163
+ ```bash
164
+ npm run build:app
165
+ uv run pytest src/kilntainers/test_file_tools.py \
166
+ src/kilntainers/test_server.py \
167
+ src/kilntainers/test_cli.py \
168
+ src/kilntainers/test_config.py \
169
+ src/kilntainers/test_dashboard.py \
170
+ src/kilntainers/backends/test_virtual_docker.py
171
+ uv run ruff check src/kilntainers
172
+ uv run ty check src/kilntainers
173
+ ```
174
+
175
+ The real desktop image needs a working Docker daemon for an end-to-end build and framebuffer test.
176
+
177
+ ## License and origin
178
+
179
+ MIT licensed. This project is a Docker-only fork of [Kilntainers](https://github.com/Kiln-AI/Kilntainers).
@@ -0,0 +1,162 @@
1
+ # MCP Virtual Computer
2
+
3
+ <!-- mcp-name: io.github.flujo-app/mcp-virtual-computer -->
4
+
5
+ A local, persistent Docker computer presented as a Three.js laptop on a desk. The MCP server keeps terminal automation from Kilntainers and adds UTF-8 file tools with a computer-screen view.
6
+
7
+ This first slice is Docker-only. It does not provision online or temporary machines.
8
+
9
+ ## What it exposes
10
+
11
+ - `terminal_execute` — run a command in the configured Docker computer.
12
+ - `read_file` — read a UTF-8 text file relative to `/workspace` or by absolute path.
13
+ - `write_file` — atomically write a UTF-8 text file.
14
+ - `edit_file` — replace one exact text match, or all matches when requested.
15
+ - `computer_ui` — open or attach the Three.js computer view.
16
+
17
+ With `DESKTOP_ENVIRONMENT=true`, it additionally exposes:
18
+
19
+ - `look_at_screen` — return the current PNG framebuffer, AT-SPI snapshot, or both.
20
+ - `click`, `type`, and `scroll` — interact by AT-SPI element reference or coordinates.
21
+ - `list_windows` and `switch_window` — enumerate and activate real Xfce windows.
22
+ - `move_window`, `maximize_window`, `restore_window`, `minimize_window`, and `close_window` — control a selected window by ID, title, or class.
23
+ - `computer://screen/current.png` — current live framebuffer resource.
24
+ - `computer://screen/accessibility.json` — Playwright-like AT-SPI tree with element refs, roles, names, actions, focus state, and screen bounds.
25
+
26
+ The computer ID is server configuration, not a tool argument. Lifecycle tools and temporary-computer arguments are not exposed.
27
+
28
+ ## Required configuration
29
+
30
+ ```env
31
+ COMPUTER_ID=agent-workstation
32
+ DESKTOP_ENVIRONMENT=false
33
+ NETWORK_ACCESS=true
34
+ AUTO_INSTALL_DOCKER=true
35
+ ```
36
+
37
+ `COMPUTER_ID` is required and selects the one persistent Docker computer. `DESKTOP_ENVIRONMENT` accepts `true`, `false`, `1`, `0`, `yes`, `no`, `on`, or `off`, and defaults to `false`. `NETWORK_ACCESS` accepts the same values and defaults to `true`. On Windows, `AUTO_INSTALL_DOCKER` accepts the same values and defaults to `true`.
38
+
39
+ ### Lazy Docker setup on Windows
40
+
41
+ The MCP transport and App start without waiting for Docker. The first tool that needs the computer checks the default `docker` engine. When Docker is missing, the server uses WinGet's exact `Docker.DockerDesktop` package, requests Docker's recommended per-user install, accepts the Docker license for unattended startup, adds the discovered CLI directory to both the current process and the current user's `PATH`, starts Docker Desktop, and resumes the original tool call.
42
+
43
+ The Three.js screen shows the observed setup phase. While WinGet downloads Docker it says `Downloading Docker...`; when WinGet supplies byte totals, the bar and percentage use those real totals, otherwise the bar is explicitly indeterminate. Calls that include an MCP progress token also receive standard `notifications/progress`. Installation is shielded from client cancellation, so it continues if a client's roughly five-minute tool timeout expires; retrying the call waits for or uses the same setup task.
44
+
45
+ Set `AUTO_INSTALL_DOCKER=false` to require a preinstalled runtime. `DOCKER_INSTALL_TIMEOUT` defaults to 1200 seconds and `DOCKER_START_TIMEOUT` defaults to 240 seconds. A fresh PC can still require one elevated `wsl --install`/`wsl --update`, a Windows restart, or BIOS/UEFI virtualization; the App reports that condition instead of looping or claiming success.
46
+
47
+ These environment values are startup defaults. New default computers always use the bundled desktop-capable image, even when Xfce starts off. In the Three.js scene, click the LAN cable to really enable or disable the running container's outbound network; the container keeps its loopback-published noVNC transport and applies an outbound firewall inside its network namespace. Click the two-sided mug (`I <3 virtual desktops` / `I <3 real desktops`) to stop or start Xfce inside that same container. The container ID and its entire writable filesystem—not only `/workspace`—stay intact.
48
+
49
+ ## The two screen modes
50
+
51
+ With `DESKTOP_ENVIRONMENT=false`, Xfce is stopped and the laptop shows a virtual desktop. The desktop services remain installed so Xfce can be enabled without recreating the computer. The screen stays idle until a genuine MCP invocation arrives, then renders Terminal, Files, or Text Editor actions from that invocation's actual arguments and result.
52
+
53
+ The Three.js laptop has a labeled QWERTY keyboard whose physical keys follow genuine `write_file` text, plus a wireless desk mouse that mirrors the on-screen pointer during real file navigation, selection, and reading.
54
+
55
+ The display is also directly controllable. Pointer input is raycast onto the angled 3D screen: double-click Files or Workspace, navigate real Docker folders, open text files, edit with the physical keyboard, and press Ctrl+S to save through `write_file`. Right-click inside Files to create a new text file; it opens unsaved in the editor until Ctrl+S is pressed. The red title-bar control closes the active virtual window. The virtual terminal executes typed commands on Enter. With the real desktop enabled, the same pointer and keyboard input is forwarded to the live VNC session. Filesystem and terminal interaction requires the App to be opened through an MCP host; the standalone static preview never invents directory contents.
56
+
57
+ With `DESKTOP_ENVIRONMENT=true`, the server builds the bundled Debian Bookworm/Xfce image on first use. The laptop then displays that container's real X11 framebuffer through noVNC. File tools drive real Thunar and Mousepad input through AT-SPI/Dogtail, `xdotool`, and `wmctrl`; the file operation itself remains deterministic and completes before the MCP result is returned. `terminal_execute` runs exactly once in a visible Xfce terminal, streams the same stdout and stderr to that window, and returns the captured exit status and output through MCP.
58
+
59
+ Sound from Xfce applications is routed through a 48 kHz stereo PulseAudio sink and streamed from the same loopback-only desktop endpoint into the dashboard. Browsers require a user gesture before playing audio, so click anywhere in the Three.js scene once after loading or refreshing it. Audio is sourced only from the real desktop; virtual mode does not synthesize sound.
60
+
61
+ Accessibility element references use paths such as `atspi:8/0/0/2`. They describe the current tree, so clients should call `look_at_screen` again after navigation or major window changes before reusing a reference.
62
+
63
+ The desktop video/input and audio WebSockets are published on `127.0.0.1` only.
64
+
65
+ ## Run locally
66
+
67
+ Requirements:
68
+
69
+ - Python 3.13+
70
+ - `uv`
71
+ - Windows 10/11: WinGet (Docker Desktop is installed lazily when needed)
72
+ - Other platforms: Docker Engine or Docker Desktop with a running daemon
73
+
74
+ For stdio:
75
+
76
+ ```bash
77
+ COMPUTER_ID=agent-workstation uv run mcp-virtual-computer
78
+ ```
79
+
80
+ For streamable HTTP:
81
+
82
+ ```bash
83
+ COMPUTER_ID=agent-workstation uv run mcp-virtual-computer \
84
+ --transport http \
85
+ --host 127.0.0.1 \
86
+ --port 8080 \
87
+ --allow-unauthenticated-http
88
+ ```
89
+
90
+ Enable the real desktop:
91
+
92
+ ```bash
93
+ COMPUTER_ID=agent-workstation DESKTOP_ENVIRONMENT=true \
94
+ uv run mcp-virtual-computer
95
+ ```
96
+
97
+ Or use Compose:
98
+
99
+ ```bash
100
+ COMPUTER_ID=agent-workstation DESKTOP_ENVIRONMENT=true docker compose up --build
101
+ ```
102
+
103
+ The Compose controller mounts `/var/run/docker.sock` so it can create and reattach the persistent workstation container.
104
+
105
+ ## MCP client example
106
+
107
+ ```json
108
+ {
109
+ "mcpServers": {
110
+ "virtual-computer": {
111
+ "command": "uvx",
112
+ "args": ["mcp-virtual-computer"],
113
+ "env": {
114
+ "COMPUTER_ID": "agent-workstation",
115
+ "DESKTOP_ENVIRONMENT": "false",
116
+ "NETWORK_ACCESS": "true",
117
+ "AUTO_INSTALL_DOCKER": "true"
118
+ }
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ ## Build the MCP App
125
+
126
+ The bundled dashboard is generated from `src/virtual-computer` and packaged as `src/kilntainers/dashboard.html`.
127
+
128
+ ```bash
129
+ npm install
130
+ npm run build:app
131
+ ```
132
+
133
+ Opening `dashboard.html` directly shows an idle virtual desktop. MCP-hosted operation visuals begin only when the host supplies a real tool event.
134
+
135
+ ## File semantics
136
+
137
+ - Paths without a leading slash resolve below `/workspace`.
138
+ - File content must be valid UTF-8 text.
139
+ - Reads and writes default to a 1 MiB text limit.
140
+ - Writes use a temporary file plus rename.
141
+ - Edits require an exact match and reject ambiguous single replacements.
142
+ - Internal SHA-256 checks prevent stale writes during edits.
143
+
144
+ ## Development checks
145
+
146
+ ```bash
147
+ npm run build:app
148
+ uv run pytest src/kilntainers/test_file_tools.py \
149
+ src/kilntainers/test_server.py \
150
+ src/kilntainers/test_cli.py \
151
+ src/kilntainers/test_config.py \
152
+ src/kilntainers/test_dashboard.py \
153
+ src/kilntainers/backends/test_virtual_docker.py
154
+ uv run ruff check src/kilntainers
155
+ uv run ty check src/kilntainers
156
+ ```
157
+
158
+ The real desktop image needs a working Docker daemon for an end-to-end build and framebuffer test.
159
+
160
+ ## License and origin
161
+
162
+ MIT licensed. This project is a Docker-only fork of [Kilntainers](https://github.com/Kiln-AI/Kilntainers).
@@ -0,0 +1,80 @@
1
+ [project]
2
+ name = "mcp-virtual-computer"
3
+ version = "0.2.3"
4
+ description = "A persistent Docker computer with a cinematic Three.js MCP App"
5
+ authors = [
6
+ {name = "Steve Cosman, Chesterfield Laboratories Inc", email = "scosman@users.noreply.github.com"}
7
+ ]
8
+ classifiers = [
9
+ "License :: OSI Approved :: MIT License",
10
+ "Intended Audience :: Developers",
11
+ ]
12
+ readme = "README.md"
13
+ requires-python = ">=3.13"
14
+ dependencies = ["mcp>=1.26.0,<2"]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/flujo-app/mcp-virtual-computer"
18
+ Repository = "https://github.com/flujo-app/mcp-virtual-computer"
19
+ Issues = "https://github.com/flujo-app/mcp-virtual-computer/issues"
20
+ Upstream = "https://github.com/Kiln-AI/Kilntainers"
21
+
22
+
23
+
24
+ [project.scripts]
25
+ mcp-virtual-computer = "kilntainers.cli:main"
26
+
27
+ [project.entry-points."kilntainers.backends"]
28
+ docker = "kilntainers.backends.docker:DockerBackend"
29
+
30
+ [tool.hatch.build.targets.wheel]
31
+ packages = ["src/kilntainers"]
32
+
33
+ [tool.hatch.build.targets.wheel.force-include]
34
+ "src/kilntainers/dashboard.html" = "kilntainers/dashboard.html"
35
+ "src/kilntainers/desktop_image" = "kilntainers/desktop_image"
36
+
37
+ [tool.uv]
38
+ package = true
39
+
40
+ [tool.uv.sources]
41
+
42
+ [tool.setuptools.package-data]
43
+ "kilntainers" = ["dashboard.html", "desktop_image/*"]
44
+
45
+ [dependency-groups]
46
+ dev = [
47
+ "coverage>=7.13.4",
48
+ "diff-cover>=10.2.0",
49
+ "dotenv>=0.9.9",
50
+ "pytest>=9.0.2",
51
+ "pytest-asyncio>=0.26.0",
52
+ "pytest-cov>=7.0.0",
53
+ "pytest-timeout>=2.4.0",
54
+ "pytest-xdist>=3.6.0",
55
+ "ruff==0.15.4",
56
+ "ty==0.0.20",
57
+ ]
58
+
59
+ [tool.ty.src]
60
+ exclude = ["**/.venv", "build"]
61
+
62
+ [tool.ruff]
63
+ exclude = []
64
+
65
+ [tool.ruff.lint]
66
+ # I is import sorting
67
+ # F401 is unused imports
68
+ # RUF is ruff rules. In particular 012+008 are mutable defaults (important)
69
+ select = ["E4", "E7", "E9", "F"]
70
+ extend-select = ["I","F401","RUF","TID"]
71
+
72
+ [tool.pytest.ini_options]
73
+ asyncio_mode = "auto"
74
+ timeout = 65
75
+ timeout_method = "thread"
76
+ markers = [
77
+ "integration: marks integration tests that require a backend (deselect with '-m \"not integration\"')",
78
+ "e2e: marks end-to-end tests that run full server lifecycles (deselect with '-m \"not e2e\"')",
79
+ "http_integration: marks HTTP transport integration tests (deselect with '-m \"not http_integration\"')",
80
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Kilntainers: Secure Agent Sandboxes MCP server."""
2
+
3
+ __version__ = "0.2.3"
@@ -0,0 +1,6 @@
1
+ """Entry point for `python -m kilntainers`."""
2
+
3
+ from kilntainers.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,34 @@
1
+ """Minimal static bearer-token protection for remote Streamable HTTP."""
2
+
3
+ import hmac
4
+
5
+ from starlette.datastructures import Headers
6
+ from starlette.responses import JSONResponse
7
+ from starlette.types import ASGIApp, Receive, Scope, Send
8
+
9
+
10
+ class BearerTokenMiddleware:
11
+ """Require a configured bearer token on the MCP protocol route."""
12
+
13
+ def __init__(self, app: ASGIApp, *, token: str) -> None:
14
+ self.app = app
15
+ self.token = token
16
+
17
+ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
18
+ if scope["type"] != "http" or not scope.get("path", "").startswith("/mcp"):
19
+ await self.app(scope, receive, send)
20
+ return
21
+ authorization = Headers(scope=scope).get("authorization", "")
22
+ scheme, _, supplied = authorization.partition(" ")
23
+ if scheme.lower() != "bearer" or not hmac.compare_digest(
24
+ supplied,
25
+ self.token,
26
+ ):
27
+ response = JSONResponse(
28
+ {"error": "unauthorized"},
29
+ status_code=401,
30
+ headers={"WWW-Authenticate": "Bearer"},
31
+ )
32
+ await response(scope, receive, send)
33
+ return
34
+ await self.app(scope, receive, send)
@@ -0,0 +1,17 @@
1
+ """Docker-only backend registry for the first virtual-computer slice."""
2
+
3
+ from kilntainers.backends.base import Backend
4
+
5
+
6
+ def get_backend_class(name: str) -> type[Backend]:
7
+ """Return the only supported backend."""
8
+ if name != "docker":
9
+ raise KeyError(f"Unknown backend {name!r}. Available backends: docker")
10
+ from kilntainers.backends.docker import DockerBackend
11
+
12
+ return DockerBackend
13
+
14
+
15
+ def get_available_backend_names() -> list[str]:
16
+ """Return the deliberately narrow backend surface."""
17
+ return ["docker"]