opencode-hermes-mcp 0.4.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arthur Hottier
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,289 @@
1
+ Metadata-Version: 2.4
2
+ Name: opencode-hermes-mcp
3
+ Version: 0.4.1
4
+ Summary: Deterministic MCP controller between Hermes (supervisor LLM) and the permanent OpenCode server. OpenCode pin defined in opencode_hermes_mcp/pin.txt (currently 1.18.21).
5
+ Author-email: Arthur Hottier <arthur.ho2tier@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ArthurHtr/opencode-hermes-mcp
8
+ Project-URL: Repository, https://github.com/ArthurHtr/opencode-hermes-mcp
9
+ Project-URL: Issue Tracker, https://github.com/ArthurHtr/opencode-hermes-mcp/issues
10
+ Keywords: mcp,model-context-protocol,opencode,hermes,controller,llm
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: mcp==1.12.4
22
+ Requires-Dist: rich>=13
23
+ Requires-Dist: pyyaml>=6
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8; extra == "dev"
26
+ Requires-Dist: ruff>=0.5; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # opencode-hermes-mcp
30
+
31
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
32
+ [![Python](https://img.shields.io/badge/Python-3.11%2B-blue.svg)](#prerequisites)
33
+ [![OpenCode](https://img.shields.io/badge/OpenCode-1.18.21%20(pinned)-brightgreen.svg)](#version-pin-opencode-11821)
34
+
35
+ Deterministic MCP controller between **Hermes** (supervisor LLM) and the
36
+ permanent **OpenCode** server. The controller is a state machine — no LLM —
37
+ that blocks on OpenCode turns and surfaces questions/permissions to Hermes so
38
+ the supervisor LLM can decide and resume the *same* turn.
39
+
40
+ ## Architecture
41
+
42
+ ```
43
+ Hermes (LLM) --MCP stdio--> opencode_hermes_mcp.server (FastMCP, 6 tools) --HTTP + SSE--> OpenCode server :4096
44
+ ```
45
+
46
+ - **Layer 1 — Hermes**: the supervisor LLM. It delegates a coding task with
47
+ `opencode_run` and decides when the controller reports
48
+ `needs_agent_input` (question / permission).
49
+ - **Layer 2 — this controller** (`opencode_hermes_mcp/`: `server.py` +
50
+ `controller.py` + `client.py` + `models.py`): a NO-LLM process spawned by
51
+ Hermes over MCP stdio. It submits the task, watches SSE + REST, blocks until
52
+ the turn completes / errors / needs input, and posts the supervisor's
53
+ decisions back into the same OpenCode turn (the prompt is never resubmitted).
54
+ - **Layer 3 — OpenCode server**: a permanent `opencode serve` process
55
+ (systemd user service `opencode-server`, loopback :4096, HTTP basic auth).
56
+ Its LLM is any supported provider (OpenAI-compatible endpoint, OpenAI, or
57
+ Anthropic), configured in `~/.config/opencode/opencode.json`.
58
+
59
+ Tools exposed to Hermes: `opencode_run`, `opencode_answer`,
60
+ `opencode_permission`, `opencode_abort`, `opencode_inspect` (diagnostic
61
+ only), `opencode_sessions`.
62
+
63
+ ## Prerequisites
64
+
65
+ - Hermes installed (`~/.hermes/config.yaml` present)
66
+ - `python3` >= 3.11 (with PyYAML for the Hermes config patch)
67
+ - Network access (OpenCode binary install, `mcp` package, LLM endpoint)
68
+ - `systemd` user sessions (for the `opencode-server` service)
69
+
70
+ ## Installation (2 commands)
71
+
72
+ ```sh
73
+ git clone <repo-url> opencode-hermes-mcp && cd opencode-hermes-mcp
74
+ scripts/install.sh
75
+ ```
76
+
77
+ `scripts/install.sh` is a thin wrapper around the **setup wizard**
78
+ (`opencode_hermes_mcp/installer.py`, Python + `rich`): banner, numbered
79
+ steps, styled prompts, progress, and a summary panel. The wizard bootstraps
80
+ itself — if the repo venv is missing (or lacks `rich` / `pyyaml` /
81
+ `mcp==1.12.4` / the editable package), it creates it and re-execs, so a bare
82
+ `python3` >= 3.11 is the only prerequisite.
83
+
84
+ The install is idempotent — re-running it skips what is already in place.
85
+ It installs the pinned OpenCode binary, the venv (the `opencode_hermes_mcp`
86
+ package with `mcp==1.12.4` pinned), the LLM provider config + secret, the
87
+ server credentials, the two launchers, the systemd user service, and patches
88
+ `~/.hermes/config.yaml` (backup kept as `.bak`). It finishes with a health
89
+ check (bounded `curl --max-time 3`, last error surfaced) +
90
+ `python -m opencode_hermes_mcp.smoke_client` (must print
91
+ `tool surface OK`).
92
+
93
+ ### LLM providers
94
+
95
+ The installer is provider-agnostic. Three providers are supported:
96
+
97
+ | Provider | Use | npm package |
98
+ | --- | --- | --- |
99
+ | `openai-compatible` | any OpenAI-compatible endpoint (Unsloth, Ollama, vLLM, llama-server, ...) — default | `@ai-sdk/openai-compatible` |
100
+ | `openai` | official OpenAI API | `@ai-sdk/openai` |
101
+ | `anthropic` | official Anthropic API | `@ai-sdk/anthropic` |
102
+
103
+ Interactive: pick the provider from the menu, then answer the prompts —
104
+ base URL + API key + model for `openai-compatible`, API key + model for
105
+ `openai` / `anthropic` — then the LLM speed (`slow` for a local LLM, which
106
+ adds `timeout:false` / `headerTimeout:false` / `chunkTimeout:120000` to the
107
+ provider options; `fast` is the default) and the model limits (context /
108
+ output, defaults 128000 / 32000).
109
+
110
+ Non-interactive (`--yes`), everything comes from env. Local
111
+ OpenAI-compatible endpoint (Ollama / vLLM / Unsloth / ...):
112
+
113
+ ```sh
114
+ OPENCODE_PROVIDER=openai-compatible \
115
+ OPENCODE_LLM_BASE_URL=http://127.0.0.1:11434/v1 \
116
+ OPENCODE_API_KEY=... \
117
+ OPENCODE_LLM_MODEL=qwen3.8-27b \
118
+ OPENCODE_LLM_SPEED=slow \
119
+ scripts/install.sh --yes
120
+ ```
121
+
122
+ OpenAI (cloud):
123
+
124
+ ```sh
125
+ OPENCODE_PROVIDER=openai OPENCODE_API_KEY=sk-... OPENCODE_LLM_MODEL=gpt-4o \
126
+ scripts/install.sh --yes
127
+ ```
128
+
129
+ Anthropic (cloud):
130
+
131
+ ```sh
132
+ OPENCODE_PROVIDER=anthropic OPENCODE_API_KEY=sk-ant-... \
133
+ OPENCODE_LLM_MODEL=claude-sonnet-4-5 scripts/install.sh --yes
134
+ ```
135
+
136
+ Flags: `--yes` (non-interactive, uses env `OPENCODE_PROVIDER` /
137
+ `OPENCODE_LLM_BASE_URL` / `OPENCODE_API_KEY` / `OPENCODE_LLM_MODEL` /
138
+ `OPENCODE_LLM_SPEED` / `OPENCODE_CONTEXT_LIMIT` / `OPENCODE_OUTPUT_LIMIT`),
139
+ `--port N` (default 4096), `--skip-binary`, `--force-config`, `--dry-run`,
140
+ `--skip-verify` (skip the final health + smoke verification — useful for
141
+ sandbox/CI).
142
+
143
+ `UNSLOTH_API_KEY` is still accepted as a deprecated fallback for
144
+ `OPENCODE_API_KEY` (existing scripts keep working).
145
+
146
+ **A new Hermes session is required** after installation to load the MCP
147
+ server.
148
+
149
+ ## Hermes integration (manual)
150
+
151
+ The installer patches `~/.hermes/config.yaml` for you, but it does **not**
152
+ install a Hermes skill on purpose (Hermes's skill layout may change). The
153
+ package ships the full manual instead:
154
+
155
+ - [`docs/hermes-integration.md`](docs/hermes-integration.md) — what the MCP
156
+ is for, the exact config entry written, manual integration (by hand), the
157
+ six tools, troubleshooting, uninstall.
158
+ - [`docs/skill.example.md`](docs/skill.example.md) — a ready-to-copy Hermes
159
+ skill (the delegation protocol) to drop into `~/.hermes/skills/` and adapt.
160
+
161
+ ## Usage
162
+
163
+ Hermes delegates work through the MCP tools — no manual CLI needed:
164
+
165
+ - `opencode_run(directory, task, agent)` — submit a task; blocks until the
166
+ turn completes, errors, or needs input. `agent` is required for a new
167
+ session (a primary agent of the project, e.g. `build`, `plan`, or a
168
+ project-specific agent).
169
+ - When a tool returns `state=needs_agent_input`, Hermes decides:
170
+ `opencode_answer` (pick exact option labels) or `opencode_permission`
171
+ (`once` / `always` / `reject`) — both resume the same turn.
172
+ - `opencode_abort` stops a stuck run; `opencode_sessions` lists sessions for
173
+ a directory; `opencode_inspect` is for exceptional diagnostics only (never
174
+ poll a running task).
175
+
176
+ The Hermes-side wiring (written by `scripts/install.sh` into
177
+ `~/.hermes/config.yaml`):
178
+
179
+ ```yaml
180
+ mcp_servers:
181
+ opencode:
182
+ command: ~/.local/bin/opencode-mcp-launch.sh
183
+ enabled: true
184
+ timeout: 14400
185
+ connect_timeout: 30
186
+ supports_parallel_tool_calls: false
187
+ timeouts:
188
+ tools:
189
+ sequential_call: 14400
190
+ concurrent_batch: 14400
191
+ ```
192
+
193
+ The launcher reads the OpenCode server credentials from
194
+ `~/.config/hermes/opencode-server.json` and execs
195
+ `python -m opencode_hermes_mcp.server` in the repo venv — `config.yaml` stays
196
+ secret-free.
197
+
198
+ ## TUI attach helpers (watch OpenCode live)
199
+
200
+ `install.sh` also drops two helpers into `~/.local/bin/` (sources:
201
+ `scripts/helpers/`):
202
+
203
+ ```sh
204
+ ocattach <repo-abs> [ses_...] # open the OpenCode TUI on a repo / session
205
+ oc-current # attach to the session Hermes is supervising NOW
206
+ ```
207
+
208
+ - `ocattach` opens the OpenCode TUI (`opencode attach`) against the permanent
209
+ server `:4096` — no tmux needed. Without a session id it opens the latest
210
+ session / lets you pick one.
211
+ - `oc-current` reads the newest `~/.local/state/opencode-hermes-mcp/turn_*.json`
212
+ (the controller's in-flight turn state) and attaches to that session — use
213
+ it while Hermes is driving OpenCode, to watch the reasoning live.
214
+
215
+ Both read the server credentials from `~/.config/hermes/opencode-server.json`
216
+ (same source as the controller launcher). **Do not press Esc/Ctrl+C in the TUI
217
+ while a turn is active** — that aborts the in-flight turn on the OpenCode side.
218
+
219
+ ## Upgrade / uninstall
220
+
221
+ ```sh
222
+ scripts/upgrade.sh # controller only: git pull + venv deps + restart + smoke
223
+ scripts/upgrade.sh --binary # install the PINNED OpenCode binary (idempotent) — see "Version pin" below
224
+ scripts/uninstall.sh # service, launchers, venv, hermes entry, credentials
225
+ scripts/uninstall.sh --purge # + OpenCode provider config + API key secret
226
+ scripts/uninstall.sh --purge-binary # + the OpenCode binary
227
+ ```
228
+
229
+ `uninstall.sh` never touches the git clone, the OpenCode provider config, the
230
+ API key secret, or the binary (unless the purge flags say so).
231
+
232
+ ## Version pin: OpenCode 1.18.21
233
+
234
+ The controller is **validated against OpenCode `1.18.21` only** (its endpoint
235
+ contract was verified against that binary's live `/doc`, not the web docs).
236
+ The pin is a **single source of truth** in `opencode_hermes_mcp/pin.txt`
237
+ (one line, no `v` prefix): `installer.py` and `scripts/upgrade.sh` both read
238
+ it, falling back to the built-in constant when the file is missing or empty
239
+ (e.g. pip installs where the file is not shipped next to the code). `install.sh` pins
240
+ the binary to that version; `upgrade.sh` never upgrades the binary by
241
+ default.
242
+
243
+ `scripts/upgrade.sh --binary` (no version) installs the pinned version and is
244
+ idempotent (no-op if the binary is already at the pin). `--binary latest` is
245
+ the explicit opt-in to the bleeding edge; `--binary X.Y.Z` installs the
246
+ requested version. For anything other than the pin, the script warns you and
247
+ you MUST re-validate the controller before trusting it:
248
+
249
+ ```sh
250
+ .venv/bin/python tests/run_tests.py
251
+ ```
252
+
253
+ (all checks must pass; the suite drives the controller over MCP stdio against
254
+ the live server). If it fails, pin back: `scripts/upgrade.sh --binary`.
255
+
256
+ ## Timeouts
257
+
258
+ Three independent timeouts bound the pipeline: the **controller** run timeout
259
+ (`DEFAULT_RUN_TIMEOUT` = 3600 s — a single `opencode_run`/`opencode_answer`/
260
+ `opencode_permission` call gives up after an hour), the **MCP** server
261
+ timeout in `~/.hermes/config.yaml` (`mcp_servers.opencode.timeout` = 14400 s,
262
+ `connect_timeout` = 30 s), and the **Hermes tools** timeouts
263
+ (`timeouts.tools.sequential_call` / `concurrent_batch` = 14400 s) — the outer
264
+ two are set 4x above the controller's so a long-but-healthy turn is never
265
+ killed by the supervisor layer.
266
+
267
+ ## Development
268
+
269
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the dev setup, how to run the
270
+ smoke test and the integration suite, and the contribution conventions.
271
+
272
+ ## Files
273
+
274
+ | File | Role |
275
+ | --- | --- |
276
+ | `opencode_hermes_mcp/server.py` | FastMCP stdio server (the 6 tools) |
277
+ | `opencode_hermes_mcp/controller.py` | state machine: submit / wait / resume / classify |
278
+ | `opencode_hermes_mcp/client.py` | HTTP + SSE client for the OpenCode server |
279
+ | `opencode_hermes_mcp/models.py` | data helpers for turns / interactions |
280
+ | `opencode_hermes_mcp/smoke_client.py` | no-LLM smoke test (tool surface + basic calls) |
281
+ | `tests/run_tests.py` | full integration suite (live LLM turns) |
282
+ | `opencode_hermes_mcp/installer.py` | setup wizard (Python + rich; self-bootstrapping venv) |
283
+ | `opencode_hermes_mcp/pin.txt` | the OpenCode version pin (single source of truth, one line) |
284
+ | `scripts/install.sh` / `uninstall.sh` / `upgrade.sh` | lifecycle (`install.sh` is a thin wrapper around the wizard) |
285
+ | `scripts/helpers/ocattach` / `oc-current` | TUI attach helpers (installed to `~/.local/bin/`) |
286
+
287
+ ## License
288
+
289
+ [MIT](LICENSE) — Copyright (c) 2026 Arthur Hottier.
@@ -0,0 +1,261 @@
1
+ # opencode-hermes-mcp
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
+ [![Python](https://img.shields.io/badge/Python-3.11%2B-blue.svg)](#prerequisites)
5
+ [![OpenCode](https://img.shields.io/badge/OpenCode-1.18.21%20(pinned)-brightgreen.svg)](#version-pin-opencode-11821)
6
+
7
+ Deterministic MCP controller between **Hermes** (supervisor LLM) and the
8
+ permanent **OpenCode** server. The controller is a state machine — no LLM —
9
+ that blocks on OpenCode turns and surfaces questions/permissions to Hermes so
10
+ the supervisor LLM can decide and resume the *same* turn.
11
+
12
+ ## Architecture
13
+
14
+ ```
15
+ Hermes (LLM) --MCP stdio--> opencode_hermes_mcp.server (FastMCP, 6 tools) --HTTP + SSE--> OpenCode server :4096
16
+ ```
17
+
18
+ - **Layer 1 — Hermes**: the supervisor LLM. It delegates a coding task with
19
+ `opencode_run` and decides when the controller reports
20
+ `needs_agent_input` (question / permission).
21
+ - **Layer 2 — this controller** (`opencode_hermes_mcp/`: `server.py` +
22
+ `controller.py` + `client.py` + `models.py`): a NO-LLM process spawned by
23
+ Hermes over MCP stdio. It submits the task, watches SSE + REST, blocks until
24
+ the turn completes / errors / needs input, and posts the supervisor's
25
+ decisions back into the same OpenCode turn (the prompt is never resubmitted).
26
+ - **Layer 3 — OpenCode server**: a permanent `opencode serve` process
27
+ (systemd user service `opencode-server`, loopback :4096, HTTP basic auth).
28
+ Its LLM is any supported provider (OpenAI-compatible endpoint, OpenAI, or
29
+ Anthropic), configured in `~/.config/opencode/opencode.json`.
30
+
31
+ Tools exposed to Hermes: `opencode_run`, `opencode_answer`,
32
+ `opencode_permission`, `opencode_abort`, `opencode_inspect` (diagnostic
33
+ only), `opencode_sessions`.
34
+
35
+ ## Prerequisites
36
+
37
+ - Hermes installed (`~/.hermes/config.yaml` present)
38
+ - `python3` >= 3.11 (with PyYAML for the Hermes config patch)
39
+ - Network access (OpenCode binary install, `mcp` package, LLM endpoint)
40
+ - `systemd` user sessions (for the `opencode-server` service)
41
+
42
+ ## Installation (2 commands)
43
+
44
+ ```sh
45
+ git clone <repo-url> opencode-hermes-mcp && cd opencode-hermes-mcp
46
+ scripts/install.sh
47
+ ```
48
+
49
+ `scripts/install.sh` is a thin wrapper around the **setup wizard**
50
+ (`opencode_hermes_mcp/installer.py`, Python + `rich`): banner, numbered
51
+ steps, styled prompts, progress, and a summary panel. The wizard bootstraps
52
+ itself — if the repo venv is missing (or lacks `rich` / `pyyaml` /
53
+ `mcp==1.12.4` / the editable package), it creates it and re-execs, so a bare
54
+ `python3` >= 3.11 is the only prerequisite.
55
+
56
+ The install is idempotent — re-running it skips what is already in place.
57
+ It installs the pinned OpenCode binary, the venv (the `opencode_hermes_mcp`
58
+ package with `mcp==1.12.4` pinned), the LLM provider config + secret, the
59
+ server credentials, the two launchers, the systemd user service, and patches
60
+ `~/.hermes/config.yaml` (backup kept as `.bak`). It finishes with a health
61
+ check (bounded `curl --max-time 3`, last error surfaced) +
62
+ `python -m opencode_hermes_mcp.smoke_client` (must print
63
+ `tool surface OK`).
64
+
65
+ ### LLM providers
66
+
67
+ The installer is provider-agnostic. Three providers are supported:
68
+
69
+ | Provider | Use | npm package |
70
+ | --- | --- | --- |
71
+ | `openai-compatible` | any OpenAI-compatible endpoint (Unsloth, Ollama, vLLM, llama-server, ...) — default | `@ai-sdk/openai-compatible` |
72
+ | `openai` | official OpenAI API | `@ai-sdk/openai` |
73
+ | `anthropic` | official Anthropic API | `@ai-sdk/anthropic` |
74
+
75
+ Interactive: pick the provider from the menu, then answer the prompts —
76
+ base URL + API key + model for `openai-compatible`, API key + model for
77
+ `openai` / `anthropic` — then the LLM speed (`slow` for a local LLM, which
78
+ adds `timeout:false` / `headerTimeout:false` / `chunkTimeout:120000` to the
79
+ provider options; `fast` is the default) and the model limits (context /
80
+ output, defaults 128000 / 32000).
81
+
82
+ Non-interactive (`--yes`), everything comes from env. Local
83
+ OpenAI-compatible endpoint (Ollama / vLLM / Unsloth / ...):
84
+
85
+ ```sh
86
+ OPENCODE_PROVIDER=openai-compatible \
87
+ OPENCODE_LLM_BASE_URL=http://127.0.0.1:11434/v1 \
88
+ OPENCODE_API_KEY=... \
89
+ OPENCODE_LLM_MODEL=qwen3.8-27b \
90
+ OPENCODE_LLM_SPEED=slow \
91
+ scripts/install.sh --yes
92
+ ```
93
+
94
+ OpenAI (cloud):
95
+
96
+ ```sh
97
+ OPENCODE_PROVIDER=openai OPENCODE_API_KEY=sk-... OPENCODE_LLM_MODEL=gpt-4o \
98
+ scripts/install.sh --yes
99
+ ```
100
+
101
+ Anthropic (cloud):
102
+
103
+ ```sh
104
+ OPENCODE_PROVIDER=anthropic OPENCODE_API_KEY=sk-ant-... \
105
+ OPENCODE_LLM_MODEL=claude-sonnet-4-5 scripts/install.sh --yes
106
+ ```
107
+
108
+ Flags: `--yes` (non-interactive, uses env `OPENCODE_PROVIDER` /
109
+ `OPENCODE_LLM_BASE_URL` / `OPENCODE_API_KEY` / `OPENCODE_LLM_MODEL` /
110
+ `OPENCODE_LLM_SPEED` / `OPENCODE_CONTEXT_LIMIT` / `OPENCODE_OUTPUT_LIMIT`),
111
+ `--port N` (default 4096), `--skip-binary`, `--force-config`, `--dry-run`,
112
+ `--skip-verify` (skip the final health + smoke verification — useful for
113
+ sandbox/CI).
114
+
115
+ `UNSLOTH_API_KEY` is still accepted as a deprecated fallback for
116
+ `OPENCODE_API_KEY` (existing scripts keep working).
117
+
118
+ **A new Hermes session is required** after installation to load the MCP
119
+ server.
120
+
121
+ ## Hermes integration (manual)
122
+
123
+ The installer patches `~/.hermes/config.yaml` for you, but it does **not**
124
+ install a Hermes skill on purpose (Hermes's skill layout may change). The
125
+ package ships the full manual instead:
126
+
127
+ - [`docs/hermes-integration.md`](docs/hermes-integration.md) — what the MCP
128
+ is for, the exact config entry written, manual integration (by hand), the
129
+ six tools, troubleshooting, uninstall.
130
+ - [`docs/skill.example.md`](docs/skill.example.md) — a ready-to-copy Hermes
131
+ skill (the delegation protocol) to drop into `~/.hermes/skills/` and adapt.
132
+
133
+ ## Usage
134
+
135
+ Hermes delegates work through the MCP tools — no manual CLI needed:
136
+
137
+ - `opencode_run(directory, task, agent)` — submit a task; blocks until the
138
+ turn completes, errors, or needs input. `agent` is required for a new
139
+ session (a primary agent of the project, e.g. `build`, `plan`, or a
140
+ project-specific agent).
141
+ - When a tool returns `state=needs_agent_input`, Hermes decides:
142
+ `opencode_answer` (pick exact option labels) or `opencode_permission`
143
+ (`once` / `always` / `reject`) — both resume the same turn.
144
+ - `opencode_abort` stops a stuck run; `opencode_sessions` lists sessions for
145
+ a directory; `opencode_inspect` is for exceptional diagnostics only (never
146
+ poll a running task).
147
+
148
+ The Hermes-side wiring (written by `scripts/install.sh` into
149
+ `~/.hermes/config.yaml`):
150
+
151
+ ```yaml
152
+ mcp_servers:
153
+ opencode:
154
+ command: ~/.local/bin/opencode-mcp-launch.sh
155
+ enabled: true
156
+ timeout: 14400
157
+ connect_timeout: 30
158
+ supports_parallel_tool_calls: false
159
+ timeouts:
160
+ tools:
161
+ sequential_call: 14400
162
+ concurrent_batch: 14400
163
+ ```
164
+
165
+ The launcher reads the OpenCode server credentials from
166
+ `~/.config/hermes/opencode-server.json` and execs
167
+ `python -m opencode_hermes_mcp.server` in the repo venv — `config.yaml` stays
168
+ secret-free.
169
+
170
+ ## TUI attach helpers (watch OpenCode live)
171
+
172
+ `install.sh` also drops two helpers into `~/.local/bin/` (sources:
173
+ `scripts/helpers/`):
174
+
175
+ ```sh
176
+ ocattach <repo-abs> [ses_...] # open the OpenCode TUI on a repo / session
177
+ oc-current # attach to the session Hermes is supervising NOW
178
+ ```
179
+
180
+ - `ocattach` opens the OpenCode TUI (`opencode attach`) against the permanent
181
+ server `:4096` — no tmux needed. Without a session id it opens the latest
182
+ session / lets you pick one.
183
+ - `oc-current` reads the newest `~/.local/state/opencode-hermes-mcp/turn_*.json`
184
+ (the controller's in-flight turn state) and attaches to that session — use
185
+ it while Hermes is driving OpenCode, to watch the reasoning live.
186
+
187
+ Both read the server credentials from `~/.config/hermes/opencode-server.json`
188
+ (same source as the controller launcher). **Do not press Esc/Ctrl+C in the TUI
189
+ while a turn is active** — that aborts the in-flight turn on the OpenCode side.
190
+
191
+ ## Upgrade / uninstall
192
+
193
+ ```sh
194
+ scripts/upgrade.sh # controller only: git pull + venv deps + restart + smoke
195
+ scripts/upgrade.sh --binary # install the PINNED OpenCode binary (idempotent) — see "Version pin" below
196
+ scripts/uninstall.sh # service, launchers, venv, hermes entry, credentials
197
+ scripts/uninstall.sh --purge # + OpenCode provider config + API key secret
198
+ scripts/uninstall.sh --purge-binary # + the OpenCode binary
199
+ ```
200
+
201
+ `uninstall.sh` never touches the git clone, the OpenCode provider config, the
202
+ API key secret, or the binary (unless the purge flags say so).
203
+
204
+ ## Version pin: OpenCode 1.18.21
205
+
206
+ The controller is **validated against OpenCode `1.18.21` only** (its endpoint
207
+ contract was verified against that binary's live `/doc`, not the web docs).
208
+ The pin is a **single source of truth** in `opencode_hermes_mcp/pin.txt`
209
+ (one line, no `v` prefix): `installer.py` and `scripts/upgrade.sh` both read
210
+ it, falling back to the built-in constant when the file is missing or empty
211
+ (e.g. pip installs where the file is not shipped next to the code). `install.sh` pins
212
+ the binary to that version; `upgrade.sh` never upgrades the binary by
213
+ default.
214
+
215
+ `scripts/upgrade.sh --binary` (no version) installs the pinned version and is
216
+ idempotent (no-op if the binary is already at the pin). `--binary latest` is
217
+ the explicit opt-in to the bleeding edge; `--binary X.Y.Z` installs the
218
+ requested version. For anything other than the pin, the script warns you and
219
+ you MUST re-validate the controller before trusting it:
220
+
221
+ ```sh
222
+ .venv/bin/python tests/run_tests.py
223
+ ```
224
+
225
+ (all checks must pass; the suite drives the controller over MCP stdio against
226
+ the live server). If it fails, pin back: `scripts/upgrade.sh --binary`.
227
+
228
+ ## Timeouts
229
+
230
+ Three independent timeouts bound the pipeline: the **controller** run timeout
231
+ (`DEFAULT_RUN_TIMEOUT` = 3600 s — a single `opencode_run`/`opencode_answer`/
232
+ `opencode_permission` call gives up after an hour), the **MCP** server
233
+ timeout in `~/.hermes/config.yaml` (`mcp_servers.opencode.timeout` = 14400 s,
234
+ `connect_timeout` = 30 s), and the **Hermes tools** timeouts
235
+ (`timeouts.tools.sequential_call` / `concurrent_batch` = 14400 s) — the outer
236
+ two are set 4x above the controller's so a long-but-healthy turn is never
237
+ killed by the supervisor layer.
238
+
239
+ ## Development
240
+
241
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the dev setup, how to run the
242
+ smoke test and the integration suite, and the contribution conventions.
243
+
244
+ ## Files
245
+
246
+ | File | Role |
247
+ | --- | --- |
248
+ | `opencode_hermes_mcp/server.py` | FastMCP stdio server (the 6 tools) |
249
+ | `opencode_hermes_mcp/controller.py` | state machine: submit / wait / resume / classify |
250
+ | `opencode_hermes_mcp/client.py` | HTTP + SSE client for the OpenCode server |
251
+ | `opencode_hermes_mcp/models.py` | data helpers for turns / interactions |
252
+ | `opencode_hermes_mcp/smoke_client.py` | no-LLM smoke test (tool surface + basic calls) |
253
+ | `tests/run_tests.py` | full integration suite (live LLM turns) |
254
+ | `opencode_hermes_mcp/installer.py` | setup wizard (Python + rich; self-bootstrapping venv) |
255
+ | `opencode_hermes_mcp/pin.txt` | the OpenCode version pin (single source of truth, one line) |
256
+ | `scripts/install.sh` / `uninstall.sh` / `upgrade.sh` | lifecycle (`install.sh` is a thin wrapper around the wizard) |
257
+ | `scripts/helpers/ocattach` / `oc-current` | TUI attach helpers (installed to `~/.local/bin/`) |
258
+
259
+ ## License
260
+
261
+ [MIT](LICENSE) — Copyright (c) 2026 Arthur Hottier.
@@ -0,0 +1,6 @@
1
+ """opencode-hermes-mcp — deterministic MCP controller between Hermes (supervisor
2
+ LLM) and the permanent OpenCode server. No LLM: a state machine + HTTP/SSE
3
+ client, pinned to the OpenCode version in opencode_hermes_mcp/pin.txt
4
+ (currently 1.18.21)."""
5
+
6
+ __version__ = "0.4.1"