dosbox-mcp 0.1.0__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.
- dosbox_mcp-0.1.0/LICENSE +13 -0
- dosbox_mcp-0.1.0/MANIFEST.in +4 -0
- dosbox_mcp-0.1.0/PKG-INFO +121 -0
- dosbox_mcp-0.1.0/PROTOCOL.md +166 -0
- dosbox_mcp-0.1.0/README.md +87 -0
- dosbox_mcp-0.1.0/THIRD_PARTY_LICENSES.txt +30 -0
- dosbox_mcp-0.1.0/dosbox_mcp/__init__.py +3 -0
- dosbox_mcp-0.1.0/dosbox_mcp/capabilities.py +26 -0
- dosbox_mcp-0.1.0/dosbox_mcp/cli.py +177 -0
- dosbox_mcp-0.1.0/dosbox_mcp/client.py +65 -0
- dosbox_mcp-0.1.0/dosbox_mcp/config.py +265 -0
- dosbox_mcp-0.1.0/dosbox_mcp/connection.py +208 -0
- dosbox_mcp-0.1.0/dosbox_mcp/lifecycle.py +261 -0
- dosbox_mcp-0.1.0/dosbox_mcp/protocol.py +85 -0
- dosbox_mcp-0.1.0/dosbox_mcp/server.py +128 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/__init__.py +3 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/bridge.py +284 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/cpu.py +40 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/debug.py +53 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/freeze.py +93 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/input.py +188 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/io.py +81 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/media.py +49 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/memory.py +138 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/screen.py +56 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/script.py +68 -0
- dosbox_mcp-0.1.0/dosbox_mcp/tools/session.py +85 -0
- dosbox_mcp-0.1.0/dosbox_mcp.egg-info/PKG-INFO +121 -0
- dosbox_mcp-0.1.0/dosbox_mcp.egg-info/SOURCES.txt +48 -0
- dosbox_mcp-0.1.0/dosbox_mcp.egg-info/dependency_links.txt +1 -0
- dosbox_mcp-0.1.0/dosbox_mcp.egg-info/entry_points.txt +2 -0
- dosbox_mcp-0.1.0/dosbox_mcp.egg-info/requires.txt +9 -0
- dosbox_mcp-0.1.0/dosbox_mcp.egg-info/top_level.txt +1 -0
- dosbox_mcp-0.1.0/feelies/INSTALL.md +69 -0
- dosbox_mcp-0.1.0/feelies/MANUAL.md +144 -0
- dosbox_mcp-0.1.0/feelies/REFCARD.md +97 -0
- dosbox_mcp-0.1.0/feelies/TECH-SUPPORT.md +81 -0
- dosbox_mcp-0.1.0/licenses/CC-BY-SA-4.0.txt +428 -0
- dosbox_mcp-0.1.0/licenses/GPL-2.0-or-later.txt +339 -0
- dosbox_mcp-0.1.0/pyproject.toml +61 -0
- dosbox_mcp-0.1.0/setup.cfg +4 -0
- dosbox_mcp-0.1.0/tests/test_bridge_tools.py +251 -0
- dosbox_mcp-0.1.0/tests/test_capabilities.py +43 -0
- dosbox_mcp-0.1.0/tests/test_cli.py +100 -0
- dosbox_mcp-0.1.0/tests/test_client.py +68 -0
- dosbox_mcp-0.1.0/tests/test_config.py +192 -0
- dosbox_mcp-0.1.0/tests/test_connection.py +206 -0
- dosbox_mcp-0.1.0/tests/test_lifecycle.py +212 -0
- dosbox_mcp-0.1.0/tests/test_protocol.py +78 -0
- dosbox_mcp-0.1.0/tests/test_server.py +198 -0
dosbox_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
dosbox-mcp License
|
|
2
|
+
------------------
|
|
3
|
+
|
|
4
|
+
dosbox-mcp is licensed under the GNU General Public License
|
|
5
|
+
version 2 or later (GPL-2.0-or-later).
|
|
6
|
+
|
|
7
|
+
The full license text is in licenses/GPL-2.0-or-later.txt.
|
|
8
|
+
|
|
9
|
+
Third-party dependencies are listed in THIRD_PARTY_LICENSES.txt.
|
|
10
|
+
|
|
11
|
+
Exception: PROTOCOL.md (the dosbox-mcp protocol specification) is
|
|
12
|
+
licensed under Creative Commons Attribution-ShareAlike 4.0
|
|
13
|
+
(CC-BY-SA-4.0). The full license text is in licenses/CC-BY-SA-4.0.txt.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dosbox-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for dosbox-automation: drive a DOS machine from an AI agent
|
|
5
|
+
Author-email: dosbox-mcp contributors <dosbox-mcp@trinity2k.net>
|
|
6
|
+
License-Expression: GPL-2.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://dosbox-automation.org
|
|
8
|
+
Project-URL: Repository, https://github.com/dosbox-automation/dosbox-mcp
|
|
9
|
+
Project-URL: Issues, https://github.com/dosbox-automation/dosbox-mcp/issues
|
|
10
|
+
Keywords: dosbox,mcp,model-context-protocol,automation,emulator,dos
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: System :: Emulators
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
License-File: licenses/GPL-2.0-or-later.txt
|
|
23
|
+
License-File: licenses/CC-BY-SA-4.0.txt
|
|
24
|
+
License-File: THIRD_PARTY_LICENSES.txt
|
|
25
|
+
Requires-Dist: mcp>=1.2
|
|
26
|
+
Requires-Dist: httpx>=0.27
|
|
27
|
+
Requires-Dist: platformdirs>=4
|
|
28
|
+
Requires-Dist: tomlkit>=0.13
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff; extra == "dev"
|
|
32
|
+
Requires-Dist: bandit; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# dosbox-mcp
|
|
36
|
+
|
|
37
|
+
MCP server for [dosbox-automation](https://dosbox-automation.org):
|
|
38
|
+
drive a DOS machine from an AI agent.
|
|
39
|
+
|
|
40
|
+
dosbox-automation is a DOSBox fork with a built-in automation API.
|
|
41
|
+
This bridge connects any MCP-capable agent runtime (Claude Code and
|
|
42
|
+
friends) to a running emulator. The agent can type into DOS programs,
|
|
43
|
+
read the screen, capture frames and video, run sandboxed Lua scripts,
|
|
44
|
+
and inspect memory: everything the automation API offers, as MCP tools.
|
|
45
|
+
|
|
46
|
+
## Quick start
|
|
47
|
+
|
|
48
|
+
1. Install dosbox-automation and start it with the API enabled. The
|
|
49
|
+
[manual](https://dosbox-automation.org) covers setup for all
|
|
50
|
+
platforms.
|
|
51
|
+
|
|
52
|
+
2. Add the bridge to your MCP client. For Claude Code:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
claude mcp add dosbox -- uvx dosbox-mcp
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Or in a JSON MCP configuration:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"dosbox": { "command": "uvx", "args": ["dosbox-mcp"] }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`pipx install dosbox-mcp` or `pip install dosbox-mcp` work just as
|
|
69
|
+
well; the command is `dosbox-mcp` either way.
|
|
70
|
+
|
|
71
|
+
That's it. The bridge finds the running emulator, checks what it can
|
|
72
|
+
do, and registers the matching tools. Ask your agent for `bridge_status`
|
|
73
|
+
to confirm the connection.
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
Optional. Write a commented config file to tune the port, spawn an
|
|
78
|
+
emulator on demand, or limit what an agent may do:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
dosbox-mcp setup --init
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The comments in the generated file explain each setting. Environment
|
|
85
|
+
variables `DOSBOX_API_URL` and `DOSBOX_API_TOKEN` override it. The
|
|
86
|
+
bridge talks to loopback addresses only; it will not reach out to a
|
|
87
|
+
remote machine.
|
|
88
|
+
|
|
89
|
+
## What the agent gets
|
|
90
|
+
|
|
91
|
+
Tools for session control, screen text and captures, video recording,
|
|
92
|
+
Lua scripting, keyboard and mouse input, and memory access. You choose
|
|
93
|
+
how much reach an agent has, from read-only observation to full
|
|
94
|
+
control.
|
|
95
|
+
|
|
96
|
+
## An open protocol
|
|
97
|
+
|
|
98
|
+
The contract between this bridge and the emulator is written down in
|
|
99
|
+
[PROTOCOL.md](PROTOCOL.md) and versioned independently. dosbox-automation
|
|
100
|
+
is the reference implementation; any DOSBox variant that speaks the
|
|
101
|
+
protocol works with this bridge.
|
|
102
|
+
|
|
103
|
+
## In the box
|
|
104
|
+
|
|
105
|
+
Like the games it drives, this one ships with its paper. The `feelies/`
|
|
106
|
+
folder holds the manual, a reference card for every tool, an install
|
|
107
|
+
sheet, and a tech-support page.
|
|
108
|
+
|
|
109
|
+
## Requirements
|
|
110
|
+
|
|
111
|
+
- Python 3.11 or newer
|
|
112
|
+
- A running dosbox-automation (0.84-da2 or newer) on the same machine
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
GPL-2.0-or-later. See LICENSE. The protocol specification
|
|
117
|
+
(PROTOCOL.md) is licensed CC-BY-SA-4.0.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
This project is developed with tooled assistance, but tested, reviewed and
|
|
121
|
+
signed off by a human developer.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# The dosbox-mcp Protocol
|
|
2
|
+
|
|
3
|
+
Version 1.0.0 (2026-07-17)
|
|
4
|
+
|
|
5
|
+
This document specifies the contract between an MCP bridge and a
|
|
6
|
+
DOSBox-family emulator that exposes the automation REST API. The bridge
|
|
7
|
+
declares what the protocol can do; the engine delivers it. The reference
|
|
8
|
+
implementation of the engine side is dosbox-automation
|
|
9
|
+
(https://dosbox-automation.org); any other DOSBox variant that
|
|
10
|
+
implements this contract is a valid peer.
|
|
11
|
+
|
|
12
|
+
License: this specification is licensed under Creative Commons
|
|
13
|
+
Attribution-ShareAlike 4.0 International (CC-BY-SA-4.0). You may
|
|
14
|
+
implement it under any license you like - the license covers this
|
|
15
|
+
document, not your code. If you publish a modified version of the
|
|
16
|
+
specification itself, the modification must carry the same license and
|
|
17
|
+
credit the dosbox-mcp project. Full text: licenses/CC-BY-SA-4.0.txt in
|
|
18
|
+
this repository, or https://creativecommons.org/licenses/by-sa/4.0/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Versioning
|
|
22
|
+
|
|
23
|
+
The specification is versioned MAJOR.MINOR.PATCH:
|
|
24
|
+
|
|
25
|
+
- MAJOR changes break existing shapes. Peers with different majors are
|
|
26
|
+
incompatible.
|
|
27
|
+
- MINOR changes are additive: a new route group, route, or field.
|
|
28
|
+
Nothing existing changes meaning.
|
|
29
|
+
- PATCH changes fix or clarify the text without changing behavior.
|
|
30
|
+
They never appear on the wire.
|
|
31
|
+
|
|
32
|
+
On the wire, only "major.minor" travels. An engine advertises its
|
|
33
|
+
version in the info payload (below) as the string field `mcp_protocol`,
|
|
34
|
+
for example `"1.0"`. Two peers are compatible when their majors match;
|
|
35
|
+
the effective contract between them is the smaller of the two minors.
|
|
36
|
+
|
|
37
|
+
Backward compatibility rule: an engine whose info payload has no
|
|
38
|
+
`mcp_protocol` field but does have a `features` object is treated as
|
|
39
|
+
implicit version 1.0, because 1.0 is defined as exactly the surface
|
|
40
|
+
those engines ship. An info payload with neither field is not a
|
|
41
|
+
protocol peer.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Transport and security
|
|
45
|
+
|
|
46
|
+
- HTTP (optionally HTTPS) on a loopback address only. Engines must bind
|
|
47
|
+
to 127.0.0.1 (or ::1); bridges must refuse any non-loopback URL.
|
|
48
|
+
Operation across a network is out of scope for protocol 1.x.
|
|
49
|
+
- Every request except the hello route (below) carries a bearer token:
|
|
50
|
+
`Authorization: Bearer <token>`. The engine generates the token at
|
|
51
|
+
startup; with the engine setting `webserver_token_file = true` it is
|
|
52
|
+
written to a token file that local clients read. The token must never
|
|
53
|
+
be passed on a command line or echoed in error responses.
|
|
54
|
+
- Clients should identify themselves with an `X-Client` header (the
|
|
55
|
+
bridge sends `X-Client: mcp`). Engines may use it to drive on-screen
|
|
56
|
+
activity indicators; they must not grant anything based on it.
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## Discovery
|
|
60
|
+
|
|
61
|
+
### GET /api/v1/hello (unauthenticated)
|
|
62
|
+
|
|
63
|
+
Added in 1.0 as an optional route; engines that predate it simply
|
|
64
|
+
answer 404 and are treated as implicit 1.0. When implemented, it
|
|
65
|
+
returns exactly three fields and nothing else:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{"name": "dosbox-automation", "version": "0.84-da3", "mcp_protocol": "1.0"}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Its purpose is diagnostics before authentication: a client can
|
|
72
|
+
distinguish "no instance on this port" from "instance present, token
|
|
73
|
+
missing" and report precisely. Because it is unauthenticated, it must
|
|
74
|
+
never expose configuration, state, or anything beyond these three
|
|
75
|
+
fields.
|
|
76
|
+
|
|
77
|
+
### GET /api/v1/dosbox/info (authenticated)
|
|
78
|
+
|
|
79
|
+
The identification payload. Required fields for a protocol peer:
|
|
80
|
+
|
|
81
|
+
- `version` (string): the engine's own version.
|
|
82
|
+
- `features` (object): capability flags, see below.
|
|
83
|
+
- `mcp_protocol` (string, "major.minor"): the protocol version.
|
|
84
|
+
Absent on pre-1.0-advertisement engines (implicit 1.0 rule).
|
|
85
|
+
|
|
86
|
+
### Feature flags
|
|
87
|
+
|
|
88
|
+
`features` reports which optional capability groups the running build
|
|
89
|
+
supports. Version 1.0 defines these flags, all boolean:
|
|
90
|
+
|
|
91
|
+
| Flag | Grants |
|
|
92
|
+
|---|---|
|
|
93
|
+
| memory | memory read/write/search, freeze, DOS memory map |
|
|
94
|
+
| input | keyboard and input injection |
|
|
95
|
+
| cpu_registers | CPU register reads |
|
|
96
|
+
| cpu_control | CPU register writes |
|
|
97
|
+
| port_io | x86 I/O port read/write |
|
|
98
|
+
| freeze | per-frame value locks |
|
|
99
|
+
| debugger | execution control (reserved; no 1.0 engine ships it) |
|
|
100
|
+
|
|
101
|
+
A flag that is absent counts as false. Flags gate at call time: a
|
|
102
|
+
client may know a route exists in the contract while the running build
|
|
103
|
+
has it disabled.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
## The 1.0 contract surface
|
|
107
|
+
|
|
108
|
+
The route groups below, under the `/api/v1/` prefix, are the 1.0
|
|
109
|
+
contract. The machine-readable OpenAPI document served at
|
|
110
|
+
`/openapi.json` is part of the contract surface: it is normative for
|
|
111
|
+
the exact request and response schemas of these routes, and clients
|
|
112
|
+
verify the advertised contract against it.
|
|
113
|
+
|
|
114
|
+
| Group | Routes | Notes |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| session | `status`, `program/state`, `dosbox/info`, `dosbox/shutdown` | machine and program state; shutdown is irreversible |
|
|
117
|
+
| screen | `video/frame`, `video/frame/info`, `video/text` | frame capture (clean emulator output) and text-mode screen reading |
|
|
118
|
+
| capture | `capture/video/start`, `capture/video/stop`, `capture/video/status` | video recording control |
|
|
119
|
+
| input | `input/sequence`, `input/type` | named-key sequences; paced string typing (feature: input) |
|
|
120
|
+
| memory | `memory/{offset}/{length}` GET, `memory/{offset}` PUT, `memory/search` | guest physical memory (feature: memory) |
|
|
121
|
+
| freeze | `memory/freeze` POST/GET/DELETE | per-frame value locks (feature: freeze) |
|
|
122
|
+
| dos | `dos/internals` | DOS internals incl. the MCB memory map (feature: memory) |
|
|
123
|
+
| cpu | `cpu/register` PUT | register writes (feature: cpu_control) |
|
|
124
|
+
| io | `io/port` GET/PUT | port I/O (feature: port_io) |
|
|
125
|
+
| script | `script/load`, `script/start`, `script/status`, `script/stop` | sandboxed Lua; `script/load` takes the raw source as a text/plain body |
|
|
126
|
+
| media | `drive/swap`, mount routes | disk image swapping; mount policy applies underneath |
|
|
127
|
+
|
|
128
|
+
Semantics that are part of the contract, not just the schemas:
|
|
129
|
+
|
|
130
|
+
- Memory routes address guest physical memory with plain integer
|
|
131
|
+
offsets; JSON output (base64 payload) is selected with an
|
|
132
|
+
`Accept: application/json` header, binary is the default.
|
|
133
|
+
- The frame returned by `video/frame` is the clean emulator output:
|
|
134
|
+
on-screen overlays the engine draws for the human watching are never
|
|
135
|
+
in it.
|
|
136
|
+
- Validation lives engine-side. The engine is the trust boundary;
|
|
137
|
+
a client talking HTTP directly must be subject to exactly the same
|
|
138
|
+
limits as one going through a bridge.
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
## Conformance
|
|
142
|
+
|
|
143
|
+
An engine conforms to protocol 1.0 when it:
|
|
144
|
+
|
|
145
|
+
1. serves the session, screen, capture, and script groups,
|
|
146
|
+
2. serves `dosbox/info` with a `features` object and (from now on)
|
|
147
|
+
the `mcp_protocol` field,
|
|
148
|
+
3. gates optional groups behind their feature flags,
|
|
149
|
+
4. authenticates every route except `hello` with the bearer token,
|
|
150
|
+
5. binds to loopback only,
|
|
151
|
+
6. serves its OpenAPI document at `/openapi.json`.
|
|
152
|
+
|
|
153
|
+
A client (bridge) conforms when it negotiates the version as specified,
|
|
154
|
+
refuses non-loopback targets, treats feature flags as call-time gates,
|
|
155
|
+
and keeps the token out of command lines, logs, and outputs.
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
## Changelog
|
|
159
|
+
|
|
160
|
+
Every revision of this specification gets an entry here stating what
|
|
161
|
+
changed and the version it produces.
|
|
162
|
+
|
|
163
|
+
- **1.0.0 (2026-07-17)** - initial specification: versioning and
|
|
164
|
+
negotiation rules, transport and token requirements, hello route,
|
|
165
|
+
feature flags, and the 1.0 route groups as shipped by
|
|
166
|
+
dosbox-automation 0.84.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# dosbox-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for [dosbox-automation](https://dosbox-automation.org):
|
|
4
|
+
drive a DOS machine from an AI agent.
|
|
5
|
+
|
|
6
|
+
dosbox-automation is a DOSBox fork with a built-in automation API.
|
|
7
|
+
This bridge connects any MCP-capable agent runtime (Claude Code and
|
|
8
|
+
friends) to a running emulator. The agent can type into DOS programs,
|
|
9
|
+
read the screen, capture frames and video, run sandboxed Lua scripts,
|
|
10
|
+
and inspect memory: everything the automation API offers, as MCP tools.
|
|
11
|
+
|
|
12
|
+
## Quick start
|
|
13
|
+
|
|
14
|
+
1. Install dosbox-automation and start it with the API enabled. The
|
|
15
|
+
[manual](https://dosbox-automation.org) covers setup for all
|
|
16
|
+
platforms.
|
|
17
|
+
|
|
18
|
+
2. Add the bridge to your MCP client. For Claude Code:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
claude mcp add dosbox -- uvx dosbox-mcp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or in a JSON MCP configuration:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"dosbox": { "command": "uvx", "args": ["dosbox-mcp"] }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`pipx install dosbox-mcp` or `pip install dosbox-mcp` work just as
|
|
35
|
+
well; the command is `dosbox-mcp` either way.
|
|
36
|
+
|
|
37
|
+
That's it. The bridge finds the running emulator, checks what it can
|
|
38
|
+
do, and registers the matching tools. Ask your agent for `bridge_status`
|
|
39
|
+
to confirm the connection.
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
Optional. Write a commented config file to tune the port, spawn an
|
|
44
|
+
emulator on demand, or limit what an agent may do:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
dosbox-mcp setup --init
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The comments in the generated file explain each setting. Environment
|
|
51
|
+
variables `DOSBOX_API_URL` and `DOSBOX_API_TOKEN` override it. The
|
|
52
|
+
bridge talks to loopback addresses only; it will not reach out to a
|
|
53
|
+
remote machine.
|
|
54
|
+
|
|
55
|
+
## What the agent gets
|
|
56
|
+
|
|
57
|
+
Tools for session control, screen text and captures, video recording,
|
|
58
|
+
Lua scripting, keyboard and mouse input, and memory access. You choose
|
|
59
|
+
how much reach an agent has, from read-only observation to full
|
|
60
|
+
control.
|
|
61
|
+
|
|
62
|
+
## An open protocol
|
|
63
|
+
|
|
64
|
+
The contract between this bridge and the emulator is written down in
|
|
65
|
+
[PROTOCOL.md](PROTOCOL.md) and versioned independently. dosbox-automation
|
|
66
|
+
is the reference implementation; any DOSBox variant that speaks the
|
|
67
|
+
protocol works with this bridge.
|
|
68
|
+
|
|
69
|
+
## In the box
|
|
70
|
+
|
|
71
|
+
Like the games it drives, this one ships with its paper. The `feelies/`
|
|
72
|
+
folder holds the manual, a reference card for every tool, an install
|
|
73
|
+
sheet, and a tech-support page.
|
|
74
|
+
|
|
75
|
+
## Requirements
|
|
76
|
+
|
|
77
|
+
- Python 3.11 or newer
|
|
78
|
+
- A running dosbox-automation (0.84-da2 or newer) on the same machine
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
GPL-2.0-or-later. See LICENSE. The protocol specification
|
|
83
|
+
(PROTOCOL.md) is licensed CC-BY-SA-4.0.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
This project is developed with tooled assistance, but tested, reviewed and
|
|
87
|
+
signed off by a human developer.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
================================================================================
|
|
2
|
+
dosbox-mcp THIRD-PARTY LICENSES
|
|
3
|
+
================================================================================
|
|
4
|
+
|
|
5
|
+
Runtime dependencies, installed from PyPI:
|
|
6
|
+
|
|
7
|
+
httpx HTTP client for Python
|
|
8
|
+
Copyright (c) Tom Christie
|
|
9
|
+
License: BSD-3-Clause
|
|
10
|
+
Homepage: https://github.com/encode/httpx
|
|
11
|
+
|
|
12
|
+
mcp Model Context Protocol SDK
|
|
13
|
+
Copyright (c) Anthropic, PBC.
|
|
14
|
+
License: MIT
|
|
15
|
+
Homepage: https://modelcontextprotocol.io
|
|
16
|
+
|
|
17
|
+
platformdirs Platform-specific user directories
|
|
18
|
+
Copyright (c) 2010-202x The platformdirs developers
|
|
19
|
+
License: MIT
|
|
20
|
+
Homepage: https://github.com/tox-dev/platformdirs
|
|
21
|
+
|
|
22
|
+
tomlkit Style-preserving TOML library
|
|
23
|
+
Copyright (c) Sebastien Eustace
|
|
24
|
+
License: MIT
|
|
25
|
+
Homepage: https://github.com/python-poetry/tomlkit
|
|
26
|
+
|
|
27
|
+
All licenses are compatible with GPL-2.0-or-later.
|
|
28
|
+
|
|
29
|
+
END OF THIRD-PARTY LICENSES
|
|
30
|
+
================================================================================
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# This file is part of the dosbox-mcp Project.
|
|
2
|
+
# License: GPL-2.0-or-later. Contact: dosbox-mcp@trinity2k.net
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
# Maps a bridge tool group to the feature flag that must be true for it
|
|
6
|
+
# to register. Groups always present (session, screen, script, media)
|
|
7
|
+
# are not gated.
|
|
8
|
+
GROUP_CAPABILITY = {
|
|
9
|
+
"memory": "memory",
|
|
10
|
+
"input": "input",
|
|
11
|
+
"freeze": "freeze",
|
|
12
|
+
"cpu_registers": "cpu_registers",
|
|
13
|
+
"cpu_control": "cpu_control",
|
|
14
|
+
"port_io": "port_io",
|
|
15
|
+
"debugger": "debugger",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
ALWAYS_ON = {"session", "screen", "script", "media"}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def registered_groups(features: dict) -> set:
|
|
22
|
+
groups = set(ALWAYS_ON)
|
|
23
|
+
for group, flag in GROUP_CAPABILITY.items():
|
|
24
|
+
if features.get(flag) is True:
|
|
25
|
+
groups.add(group)
|
|
26
|
+
return groups
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# This file is part of the dosbox-mcp Project.
|
|
2
|
+
# License: GPL-2.0-or-later. Contact: dosbox-mcp@trinity2k.net
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
"""Command line entry point.
|
|
6
|
+
|
|
7
|
+
`dosbox-mcp` with no arguments serves MCP over stdio (what an MCP client
|
|
8
|
+
spawns). `setup` and `probe` are the human-facing configuration
|
|
9
|
+
commands; there is deliberately no interactive dialog - the commented
|
|
10
|
+
config template plays that role.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from .client import DosboxClient
|
|
19
|
+
from .config import (
|
|
20
|
+
Config,
|
|
21
|
+
default_config_path,
|
|
22
|
+
update_config_file,
|
|
23
|
+
write_config_template,
|
|
24
|
+
)
|
|
25
|
+
from .connection import Connection, NotConnected
|
|
26
|
+
from .lifecycle import InstanceManager, LifecycleError
|
|
27
|
+
from .protocol import BRIDGE_PROTOCOL
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _serve() -> int:
|
|
31
|
+
# Local import: the MCP server pulls in asyncio machinery the other
|
|
32
|
+
# subcommands never need.
|
|
33
|
+
from .server import main as serve_main
|
|
34
|
+
serve_main()
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _connection_factory(config: Config) -> Connection:
|
|
39
|
+
return Connection(config)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _cmd_setup(args) -> int:
|
|
43
|
+
path = default_config_path()
|
|
44
|
+
if args.init:
|
|
45
|
+
try:
|
|
46
|
+
write_config_template(path)
|
|
47
|
+
except FileExistsError as e:
|
|
48
|
+
print(str(e), file=sys.stderr)
|
|
49
|
+
return 2
|
|
50
|
+
print(f"wrote {path} - edit it to suit; commented lines show defaults")
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
changes = {}
|
|
54
|
+
for key in ("binary", "port", "protocol", "mode", "token_file"):
|
|
55
|
+
value = getattr(args, key)
|
|
56
|
+
if value is not None:
|
|
57
|
+
changes[key] = value
|
|
58
|
+
if args.headless is not None:
|
|
59
|
+
changes["headless"] = args.headless
|
|
60
|
+
|
|
61
|
+
if not changes:
|
|
62
|
+
config = Config.load()
|
|
63
|
+
current = {
|
|
64
|
+
"config_file": str(path),
|
|
65
|
+
"binary": str(config.binary) if config.binary else None,
|
|
66
|
+
"port": config.port,
|
|
67
|
+
"headless": config.headless,
|
|
68
|
+
"protocol": config.protocol,
|
|
69
|
+
"mode": config.mode,
|
|
70
|
+
"token_file": str(config.token_file) if config.token_file else None,
|
|
71
|
+
}
|
|
72
|
+
print(json.dumps(current, indent=2))
|
|
73
|
+
return 0
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
update_config_file(path, changes)
|
|
77
|
+
except ValueError as e:
|
|
78
|
+
print(str(e), file=sys.stderr)
|
|
79
|
+
return 2
|
|
80
|
+
print(f"saved to {path}")
|
|
81
|
+
return 0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _cmd_probe(args) -> int:
|
|
85
|
+
try:
|
|
86
|
+
config = Config.load()
|
|
87
|
+
except ValueError as e:
|
|
88
|
+
print(str(e), file=sys.stderr)
|
|
89
|
+
return 2
|
|
90
|
+
|
|
91
|
+
print(f"bridge protocol: {BRIDGE_PROTOCOL}")
|
|
92
|
+
print(f"config file: {default_config_path()}")
|
|
93
|
+
print(f"target: {config.base_url} (mode {config.mode})")
|
|
94
|
+
|
|
95
|
+
if config.binary is not None:
|
|
96
|
+
return _probe_binary(config)
|
|
97
|
+
print("no binary configured - trying to attach to a running instance")
|
|
98
|
+
return _probe_attach(config)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _probe_binary(config: Config) -> int:
|
|
102
|
+
def attach(base_url, token):
|
|
103
|
+
client = DosboxClient(base_url, token)
|
|
104
|
+
return client.get("/api/v1/dosbox/info")
|
|
105
|
+
|
|
106
|
+
manager = InstanceManager(config, attach=attach)
|
|
107
|
+
try:
|
|
108
|
+
info = manager.start()
|
|
109
|
+
except LifecycleError as e:
|
|
110
|
+
print(str(e), file=sys.stderr)
|
|
111
|
+
return 1
|
|
112
|
+
try:
|
|
113
|
+
print(f"binary: {config.binary}")
|
|
114
|
+
print(f"engine version: {info.get('version', '?')}")
|
|
115
|
+
print(f"engine protocol: {info.get('mcp_protocol', 'implicit 1.0')}")
|
|
116
|
+
finally:
|
|
117
|
+
manager.stop()
|
|
118
|
+
return 0
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _probe_attach(config: Config) -> int:
|
|
122
|
+
conn = _connection_factory(config)
|
|
123
|
+
try:
|
|
124
|
+
conn.ensure_connected()
|
|
125
|
+
except NotConnected as e:
|
|
126
|
+
print(f"not connected: {e}")
|
|
127
|
+
return 1
|
|
128
|
+
status = conn.status()
|
|
129
|
+
print(f"engine version: {status['engine_version']}")
|
|
130
|
+
print(f"protocol: {status['protocol']}")
|
|
131
|
+
print(f"features: {json.dumps(status['features'])}")
|
|
132
|
+
return 0
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
136
|
+
parser = argparse.ArgumentParser(
|
|
137
|
+
prog="dosbox-mcp",
|
|
138
|
+
description="MCP bridge for dosbox. Without a subcommand, serves "
|
|
139
|
+
"MCP over stdio.",
|
|
140
|
+
)
|
|
141
|
+
sub = parser.add_subparsers(dest="command")
|
|
142
|
+
|
|
143
|
+
setup = sub.add_parser(
|
|
144
|
+
"setup",
|
|
145
|
+
help="write config values; --init writes a commented template; "
|
|
146
|
+
"no flags prints the current configuration",
|
|
147
|
+
)
|
|
148
|
+
setup.add_argument("--init", action="store_true",
|
|
149
|
+
help="write the commented default config")
|
|
150
|
+
setup.add_argument("--binary", help="dosbox binary that bridge_start spawns")
|
|
151
|
+
setup.add_argument("--port", type=int, help="webserver port on 127.0.0.1")
|
|
152
|
+
setup.add_argument("--headless", action=argparse.BooleanOptionalAction,
|
|
153
|
+
default=None, help="spawn without a window")
|
|
154
|
+
setup.add_argument("--protocol", help='protocol pin, "major.minor"')
|
|
155
|
+
setup.add_argument("--mode", help="capability mode: observe/interact/full")
|
|
156
|
+
setup.add_argument("--token-file", dest="token_file",
|
|
157
|
+
help="token file of an already-running instance")
|
|
158
|
+
|
|
159
|
+
sub.add_parser(
|
|
160
|
+
"probe",
|
|
161
|
+
help="validate the config and probe the binary (or a running "
|
|
162
|
+
"instance) for version and protocol",
|
|
163
|
+
)
|
|
164
|
+
return parser
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def main(argv=None) -> int:
|
|
168
|
+
args = _build_parser().parse_args(argv)
|
|
169
|
+
if args.command == "setup":
|
|
170
|
+
return _cmd_setup(args)
|
|
171
|
+
if args.command == "probe":
|
|
172
|
+
return _cmd_probe(args)
|
|
173
|
+
return _serve()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if __name__ == "__main__":
|
|
177
|
+
sys.exit(main())
|