@geohar/opencode-mcp-combiner 0.1.0-dev.0
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.
- package/LICENSE +21 -0
- package/README.md +138 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +349 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 George Harker
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# @geohar/opencode-mcp-combiner
|
|
2
|
+
|
|
3
|
+
An [OpenCode](https://opencode.ai) plugin that makes the **`mcp-combiner`** MCP
|
|
4
|
+
aggregator available to OpenCode: it starts the combiner (supervised by
|
|
5
|
+
[`sharedserver`](https://github.com/georgeharker/sharedserver)) and registers its
|
|
6
|
+
HTTP MCP endpoint with OpenCode.
|
|
7
|
+
|
|
8
|
+
It is the OpenCode counterpart of the Claude Code plugin
|
|
9
|
+
[`claude-mcp-combiner`](https://github.com/georgeharker/claude-mcp-combiner), and
|
|
10
|
+
shares the same combiner and the same `sharedserver` instance — so OpenCode,
|
|
11
|
+
Claude Code, and Neovim can all talk to **one** refcounted combiner process.
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
The plugin has two independent responsibilities (mirroring the Claude plugin,
|
|
16
|
+
which uses session hooks + a static `.mcp.json`):
|
|
17
|
+
|
|
18
|
+
1. **Run the combiner** — drives `sharedserver`:
|
|
19
|
+
```
|
|
20
|
+
sharedserver use <name> --pid <opencode-pid> --grace-period <g> \
|
|
21
|
+
-- <combiner> --mcp --config <servers.json> --port <port>
|
|
22
|
+
```
|
|
23
|
+
`sharedserver` refcounts by PID with a grace period, so the combiner is
|
|
24
|
+
shared across clients and outlives any single one. The plugin runs `unuse` on
|
|
25
|
+
OpenCode exit.
|
|
26
|
+
|
|
27
|
+
2. **Register the endpoint** — via the OpenCode `config` hook it injects a
|
|
28
|
+
remote MCP server into your config:
|
|
29
|
+
```jsonc
|
|
30
|
+
{ "mcp": { "mcp-combiner": { "type": "remote", "url": "http://127.0.0.1:9741/mcp", "enabled": true } } }
|
|
31
|
+
```
|
|
32
|
+
(OpenCode has no static `.mcp.json`, so registration is programmatic.)
|
|
33
|
+
|
|
34
|
+
The two halves are independent: registration is unconditional — OpenCode tracks
|
|
35
|
+
connection state (`McpStatus`) and reconnects if the endpoint is briefly absent
|
|
36
|
+
while the combiner binds its port. After startup the plugin polls
|
|
37
|
+
`client.mcp.status()` and toasts `connected` / `failed` accordingly.
|
|
38
|
+
|
|
39
|
+
## Requirements
|
|
40
|
+
|
|
41
|
+
- **`sharedserver`** on `PATH` (or `$SHAREDSERVER_BIN`) — `cargo install sharedserver`.
|
|
42
|
+
- **`mcp-combiner`** available as a command (`uv tool install mcp-combiner`, a
|
|
43
|
+
console script on `PATH`), or a checkout runnable via `uv run`. Requires
|
|
44
|
+
combiner ≥ 0.8.0 (the `--mcp` serve flag; version-gated automatically).
|
|
45
|
+
- A combiner **`servers.json`** (see auto-probe locations below).
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
Add it to `~/.config/opencode/config.json` under `plugin`, tuple form
|
|
50
|
+
`[spec, options]`:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"plugin": [
|
|
55
|
+
["@geohar/opencode-mcp-combiner@latest", {
|
|
56
|
+
"port": 9741,
|
|
57
|
+
"config": "~/.config/mcp-combiner/servers.json",
|
|
58
|
+
"gracePeriod": "30m"
|
|
59
|
+
}]
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Bare-string form (`"@geohar/opencode-mcp-combiner@latest"`) also works and uses
|
|
65
|
+
all defaults.
|
|
66
|
+
|
|
67
|
+
## Options
|
|
68
|
+
|
|
69
|
+
| Option | Default | Description |
|
|
70
|
+
|--------|---------|-------------|
|
|
71
|
+
| `port` | `9741` | HTTP port the combiner serves on. |
|
|
72
|
+
| `host` | `127.0.0.1` | HTTP host the combiner binds. |
|
|
73
|
+
| `config` | *(auto-probed)* | Path to the combiner's `servers.json`. |
|
|
74
|
+
| `command` | *(auto-resolved)* | Override the combiner command. |
|
|
75
|
+
| `args` | `[]` | Extra combiner args (before the serve args). |
|
|
76
|
+
| `checkout` | — | Combiner checkout for `uv run --project <checkout> python -m mcp_combiner`. |
|
|
77
|
+
| `name` | `mcp-combiner` | `sharedserver` instance name. |
|
|
78
|
+
| `gracePeriod` | `30m` | `sharedserver` grace period (`30m`, `1h`, …). |
|
|
79
|
+
| `logFile` | — | Capture the combiner's output (`sharedserver --log-file`). |
|
|
80
|
+
| `binary` | *(auto-resolved)* | Path to the `sharedserver` binary. |
|
|
81
|
+
| `lockdir` | — | Override `SHAREDSERVER_LOCKDIR`. |
|
|
82
|
+
| `mcpName` | `mcp-combiner` | Key under OpenCode's `mcp` config. |
|
|
83
|
+
| `url` | `http://127.0.0.1:<port>/mcp` | Explicit MCP URL to register. |
|
|
84
|
+
| `register` | `true` | Register the endpoint with OpenCode. |
|
|
85
|
+
| `manage` | `true` | Start the combiner. `false` → **register only** (something else runs it). |
|
|
86
|
+
| `notify` | `true` | Show TUI toasts for attach/health outcomes. |
|
|
87
|
+
|
|
88
|
+
### Combiner command resolution
|
|
89
|
+
|
|
90
|
+
In priority order: `command` option → `$OPENCODE_MCP_COMBINER_COMMAND`
|
|
91
|
+
(+`$OPENCODE_MCP_COMBINER_ARGS`) → `mcp-combiner` on `PATH` →
|
|
92
|
+
`uv run --project <checkout> python -m mcp_combiner` (when `checkout` /
|
|
93
|
+
`$OPENCODE_MCP_COMBINER_CHECKOUT` is set).
|
|
94
|
+
|
|
95
|
+
### `servers.json` auto-probe
|
|
96
|
+
|
|
97
|
+
`config` option → `$OPENCODE_MCP_COMBINER_CONFIG` →
|
|
98
|
+
`~/.cache/secrets/<user>.mcpservers.json` →
|
|
99
|
+
`~/.config/mcp-combiner/servers.json` → `~/.config/mcp/servers.json`.
|
|
100
|
+
|
|
101
|
+
## Host-owned mode
|
|
102
|
+
|
|
103
|
+
If **`$MCP_COMPANION_COMBINER_URL`** is set, an editor/host (e.g. Neovim) already
|
|
104
|
+
owns and refcounts the combiner. The plugin then **only registers** that URL and
|
|
105
|
+
never starts or stops the process — the same early-exit as `claude-mcp-combiner`.
|
|
106
|
+
Equivalent to setting `manage: false` with an explicit `url`.
|
|
107
|
+
|
|
108
|
+
## Environment variables
|
|
109
|
+
|
|
110
|
+
| Variable | Effect |
|
|
111
|
+
|----------|--------|
|
|
112
|
+
| `MCP_COMPANION_COMBINER_URL` | Host owns the combiner → register only, don't launch. |
|
|
113
|
+
| `OPENCODE_MCP_COMBINER_COMMAND` / `_ARGS` | Override the combiner invocation. |
|
|
114
|
+
| `OPENCODE_MCP_COMBINER_CHECKOUT` | Checkout path for `uv run`. |
|
|
115
|
+
| `OPENCODE_MCP_COMBINER_CONFIG` | `servers.json` path. |
|
|
116
|
+
| `SHAREDSERVER_BIN` | Path to the `sharedserver` binary. |
|
|
117
|
+
| `SHAREDSERVER_LOCKDIR` | `sharedserver` lock directory. |
|
|
118
|
+
|
|
119
|
+
## Relationship to `opencode-sharedserver`
|
|
120
|
+
|
|
121
|
+
[`@geohar/opencode-sharedserver`](https://github.com/georgeharker/opencode-sharedserver)
|
|
122
|
+
is a **generic** process supervisor plugin. This plugin is
|
|
123
|
+
**combiner-specific**: it knows the correct serve invocation (`--mcp --config
|
|
124
|
+
--port`, version-gated) and — unlike the generic one — registers the MCP
|
|
125
|
+
endpoint with OpenCode. It vendors the same `sharedserver` attach/detach logic
|
|
126
|
+
rather than depending on the generic plugin, so either can be used independently.
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
bun install # or npm install
|
|
132
|
+
npm run typecheck
|
|
133
|
+
npm run build # emits dist/ (not committed; built on publish)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT © George Harker
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAuSjD,QAAA,MAAM,iBAAiB,EAAE,MA+HxB,CAAA;AAED,eAAe,iBAAiB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
// OpenCode plugin: run the `mcp-combiner` MCP aggregator via the `sharedserver`
|
|
2
|
+
// CLI and register its HTTP endpoint with OpenCode.
|
|
3
|
+
//
|
|
4
|
+
// Two independent responsibilities (mirrors the Claude Code plugin, which uses
|
|
5
|
+
// SessionStart hooks + a static .mcp.json):
|
|
6
|
+
// 1. Process — drive `sharedserver use … -- <combiner> --mcp --config … --port …`
|
|
7
|
+
// so the combiner is running and refcounted (shared with other clients).
|
|
8
|
+
// 2. Registration — inject a `type: "remote"` entry into OpenCode's `mcp`
|
|
9
|
+
// config via the `config` hook (OpenCode has no static .mcp.json).
|
|
10
|
+
//
|
|
11
|
+
// The two halves are independent: registration is unconditional (OpenCode
|
|
12
|
+
// surfaces connection state via McpStatus and reconnects), the launch is the
|
|
13
|
+
// best-effort "make sure something is listening there" half. See README.md.
|
|
14
|
+
import { spawnSync } from "node:child_process";
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
const DEFAULT_PORT = 9741;
|
|
19
|
+
const DEFAULT_NAME = "mcp-combiner";
|
|
20
|
+
const DEFAULT_GRACE = "30m";
|
|
21
|
+
// The `--mcp` serve flag was introduced in combiner 0.8.0; older versions serve
|
|
22
|
+
// with a bare `--config`. Version-gated so this plugin works across the boundary.
|
|
23
|
+
const MIN_MCP_VERSION = [0, 8, 0];
|
|
24
|
+
// ── sharedserver binary resolution (ported from opencode-sharedserver) ──
|
|
25
|
+
const CANDIDATE_BINARIES = [
|
|
26
|
+
"sharedserver",
|
|
27
|
+
join(homedir(), ".cargo", "bin", "sharedserver"),
|
|
28
|
+
join(homedir(), ".local", "bin", "sharedserver"),
|
|
29
|
+
"/usr/local/bin/sharedserver",
|
|
30
|
+
"/opt/homebrew/bin/sharedserver",
|
|
31
|
+
];
|
|
32
|
+
function resolveBinary(override, env) {
|
|
33
|
+
const candidates = [override, env.SHAREDSERVER_BIN, ...CANDIDATE_BINARIES].filter((v) => typeof v === "string" && v.length > 0);
|
|
34
|
+
for (const candidate of candidates) {
|
|
35
|
+
if (candidate.includes("/")) {
|
|
36
|
+
if (existsSync(candidate))
|
|
37
|
+
return candidate;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const probe = spawnSync(candidate, ["--version"], { stdio: "ignore", env });
|
|
41
|
+
if (probe.status === 0)
|
|
42
|
+
return candidate;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
function onPath(cmd, env) {
|
|
47
|
+
return spawnSync(cmd, ["--version"], { stdio: "ignore", env }).status === 0;
|
|
48
|
+
}
|
|
49
|
+
function splitArgs(value) {
|
|
50
|
+
if (!value)
|
|
51
|
+
return [];
|
|
52
|
+
return value.split(/\s+/).filter((s) => s.length > 0);
|
|
53
|
+
}
|
|
54
|
+
/** Resolve how to invoke the combiner, mirroring claude-mcp-combiner's priority:
|
|
55
|
+
* explicit option → env command → `mcp-combiner` on PATH →
|
|
56
|
+
* `uv run --project <checkout> python -m mcp_combiner`. */
|
|
57
|
+
function resolveCombiner(opts, env) {
|
|
58
|
+
const extra = opts.args ?? [];
|
|
59
|
+
if (opts.command)
|
|
60
|
+
return { cmd: opts.command, args: extra };
|
|
61
|
+
if (env.OPENCODE_MCP_COMBINER_COMMAND) {
|
|
62
|
+
return {
|
|
63
|
+
cmd: env.OPENCODE_MCP_COMBINER_COMMAND,
|
|
64
|
+
args: [...splitArgs(env.OPENCODE_MCP_COMBINER_ARGS), ...extra],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (onPath("mcp-combiner", env))
|
|
68
|
+
return { cmd: "mcp-combiner", args: extra };
|
|
69
|
+
const checkout = opts.checkout ?? env.OPENCODE_MCP_COMBINER_CHECKOUT;
|
|
70
|
+
if (checkout && onPath("uv", env)) {
|
|
71
|
+
return {
|
|
72
|
+
cmd: "uv",
|
|
73
|
+
args: ["run", "--project", checkout, "python", "-m", "mcp_combiner", ...extra],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
function parseVersion(text) {
|
|
79
|
+
const m = text.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
80
|
+
if (!m)
|
|
81
|
+
return undefined;
|
|
82
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
83
|
+
}
|
|
84
|
+
function gte(a, b) {
|
|
85
|
+
for (let i = 0; i < 3; i++) {
|
|
86
|
+
if (a[i] !== b[i])
|
|
87
|
+
return a[i] > b[i];
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
/** True if the combiner supports (needs) the `--mcp` serve flag. Unknown version
|
|
92
|
+
* → assume yes (current releases are ≥0.8.0). */
|
|
93
|
+
function combinerNeedsMcpFlag(cmd, env) {
|
|
94
|
+
const r = spawnSync(cmd.cmd, [...cmd.args, "--version"], { env });
|
|
95
|
+
if (r.status !== 0)
|
|
96
|
+
return true;
|
|
97
|
+
const ver = parseVersion(`${r.stdout?.toString() ?? ""}${r.stderr?.toString() ?? ""}`);
|
|
98
|
+
return ver ? gte(ver, MIN_MCP_VERSION) : true;
|
|
99
|
+
}
|
|
100
|
+
// ── servers.json resolution (mirrors claude-mcp-combiner probing) ──
|
|
101
|
+
function resolveConfig(opts, env) {
|
|
102
|
+
if (opts.config)
|
|
103
|
+
return opts.config;
|
|
104
|
+
if (env.OPENCODE_MCP_COMBINER_CONFIG)
|
|
105
|
+
return env.OPENCODE_MCP_COMBINER_CONFIG;
|
|
106
|
+
const user = env.USER ?? env.LOGNAME ?? "";
|
|
107
|
+
const candidates = [
|
|
108
|
+
join(homedir(), ".cache", "secrets", `${user}.mcpservers.json`),
|
|
109
|
+
join(homedir(), ".config", "mcp-combiner", "servers.json"),
|
|
110
|
+
join(homedir(), ".config", "mcp", "servers.json"),
|
|
111
|
+
];
|
|
112
|
+
return candidates.find(existsSync);
|
|
113
|
+
}
|
|
114
|
+
function preCheck(binary, name, env) {
|
|
115
|
+
const result = spawnSync(binary, ["check", name], { stdio: "ignore", env });
|
|
116
|
+
switch (result.status) {
|
|
117
|
+
case 0:
|
|
118
|
+
return "active";
|
|
119
|
+
case 1:
|
|
120
|
+
return "grace";
|
|
121
|
+
case 2:
|
|
122
|
+
return "stopped";
|
|
123
|
+
default:
|
|
124
|
+
return "unknown";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function readServerInfo(binary, name, env) {
|
|
128
|
+
const result = spawnSync(binary, ["info", name, "--json"], { env });
|
|
129
|
+
if (result.status !== 0)
|
|
130
|
+
return undefined;
|
|
131
|
+
try {
|
|
132
|
+
return JSON.parse(result.stdout.toString());
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function isPidAlive(pid) {
|
|
139
|
+
try {
|
|
140
|
+
process.kill(pid, 0);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const attached = [];
|
|
148
|
+
let cleanupInstalled = false;
|
|
149
|
+
function installCleanup() {
|
|
150
|
+
if (cleanupInstalled)
|
|
151
|
+
return;
|
|
152
|
+
cleanupInstalled = true;
|
|
153
|
+
const drain = () => {
|
|
154
|
+
while (attached.length) {
|
|
155
|
+
const s = attached.pop();
|
|
156
|
+
spawnSync(s.binary, ["unuse", s.name, "--pid", String(process.pid)], {
|
|
157
|
+
stdio: "ignore",
|
|
158
|
+
env: s.env,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
process.on("exit", drain);
|
|
163
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
164
|
+
process.on(sig, () => {
|
|
165
|
+
drain();
|
|
166
|
+
process.kill(process.pid, sig);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ── health checks ──────────────────────────────────────────────────
|
|
171
|
+
/** Process-level: confirm the wrapped combiner is still alive (sharedserver info). */
|
|
172
|
+
function scheduleProcessHealthCheck(binary, name, env, log, toast, delayMs) {
|
|
173
|
+
setTimeout(() => {
|
|
174
|
+
const info = readServerInfo(binary, name, env);
|
|
175
|
+
if (!info) {
|
|
176
|
+
log("warn", `${name}: process health check returned no data`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (info.state && info.state !== "active") {
|
|
180
|
+
const msg = `${name}: not active after start (state: ${info.state})`;
|
|
181
|
+
log("error", msg);
|
|
182
|
+
toast("error", msg);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (info.pid && !isPidAlive(info.pid)) {
|
|
186
|
+
const msg = `${name}: PID ${info.pid} died shortly after start`;
|
|
187
|
+
log("error", msg);
|
|
188
|
+
toast("error", msg);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
log("info", `${name}: process healthy (pid=${info.pid}, state=${info.state})`);
|
|
192
|
+
}, delayMs).unref();
|
|
193
|
+
}
|
|
194
|
+
/** OpenCode-level: confirm OpenCode actually connected to the combiner MCP server. */
|
|
195
|
+
function scheduleMcpHealthCheck(client, mcpName, log, toast, delayMs) {
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
client.mcp
|
|
198
|
+
.status()
|
|
199
|
+
.then((res) => {
|
|
200
|
+
const st = res.data?.[mcpName];
|
|
201
|
+
if (!st) {
|
|
202
|
+
log("warn", `${mcpName}: not present in OpenCode mcp status yet`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
switch (st.status) {
|
|
206
|
+
case "connected":
|
|
207
|
+
log("info", `${mcpName}: connected`);
|
|
208
|
+
toast("success", `${mcpName}: connected`);
|
|
209
|
+
break;
|
|
210
|
+
case "failed":
|
|
211
|
+
toast("error", `${mcpName}: failed — ${st.error ?? "unknown error"}`);
|
|
212
|
+
break;
|
|
213
|
+
case "needs_auth":
|
|
214
|
+
case "needs_client_registration":
|
|
215
|
+
toast("warning", `${mcpName}: ${st.status}`);
|
|
216
|
+
break;
|
|
217
|
+
default:
|
|
218
|
+
log("info", `${mcpName}: status ${st.status}`);
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
.catch((err) => {
|
|
222
|
+
log("warn", `${mcpName}: mcp status check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
223
|
+
});
|
|
224
|
+
}, delayMs).unref();
|
|
225
|
+
}
|
|
226
|
+
// ── plugin ─────────────────────────────────────────────────────────
|
|
227
|
+
const McpCombinerPlugin = async ({ client }, options) => {
|
|
228
|
+
const opts = (options ?? {});
|
|
229
|
+
const notify = opts.notify !== false;
|
|
230
|
+
const log = (level, message) => {
|
|
231
|
+
client.app.log({ body: { service: "mcp-combiner", level, message } }).catch(() => { });
|
|
232
|
+
};
|
|
233
|
+
const toast = (variant, message) => {
|
|
234
|
+
if (!notify)
|
|
235
|
+
return;
|
|
236
|
+
// Deferred: the plugin runs inside InstanceBootstrap, before the TUI bus
|
|
237
|
+
// subscribers are wired up. Best-effort (no-op if headless).
|
|
238
|
+
setTimeout(() => {
|
|
239
|
+
client.tui.showToast({ body: { title: "mcp-combiner", message, variant } }).catch(() => { });
|
|
240
|
+
}, 1500).unref();
|
|
241
|
+
};
|
|
242
|
+
const port = opts.port ?? DEFAULT_PORT;
|
|
243
|
+
const mcpName = opts.mcpName ?? DEFAULT_NAME;
|
|
244
|
+
const name = opts.name ?? DEFAULT_NAME;
|
|
245
|
+
const register = opts.register !== false;
|
|
246
|
+
const hostOwnedUrl = process.env.MCP_COMPANION_COMBINER_URL;
|
|
247
|
+
const url = opts.url ?? hostOwnedUrl ?? `http://127.0.0.1:${port}/mcp`;
|
|
248
|
+
// The registration half — always applied (unless disabled). OpenCode surfaces
|
|
249
|
+
// connection state itself, so a briefly-absent endpoint recovers.
|
|
250
|
+
const configHook = async (cfg) => {
|
|
251
|
+
if (!register)
|
|
252
|
+
return;
|
|
253
|
+
cfg.mcp ??= {};
|
|
254
|
+
if (cfg.mcp[mcpName]) {
|
|
255
|
+
log("info", `mcp "${mcpName}" already configured by the user; leaving as-is`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
cfg.mcp[mcpName] = { type: "remote", url, enabled: true };
|
|
259
|
+
log("info", `registered mcp "${mcpName}" → ${url}`);
|
|
260
|
+
};
|
|
261
|
+
const hooks = { config: configHook };
|
|
262
|
+
const env = { ...process.env };
|
|
263
|
+
if (opts.lockdir)
|
|
264
|
+
env.SHAREDSERVER_LOCKDIR = opts.lockdir;
|
|
265
|
+
// The process half — skipped when the host owns the combiner or manage=false.
|
|
266
|
+
const manage = opts.manage !== false;
|
|
267
|
+
if (hostOwnedUrl) {
|
|
268
|
+
log("info", `host owns the combiner ($MCP_COMPANION_COMBINER_URL=${hostOwnedUrl}); registering only`);
|
|
269
|
+
scheduleMcpHealthCheck(client, mcpName, log, toast, 5000);
|
|
270
|
+
return hooks;
|
|
271
|
+
}
|
|
272
|
+
if (!manage) {
|
|
273
|
+
log("info", `manage=false; registering ${url} only (assuming the combiner is started elsewhere)`);
|
|
274
|
+
scheduleMcpHealthCheck(client, mcpName, log, toast, 5000);
|
|
275
|
+
return hooks;
|
|
276
|
+
}
|
|
277
|
+
const binary = resolveBinary(opts.binary, env);
|
|
278
|
+
if (!binary) {
|
|
279
|
+
const msg = "sharedserver binary not found; set `binary`/`$SHAREDSERVER_BIN`, or use manage:false";
|
|
280
|
+
log("error", msg);
|
|
281
|
+
toast("error", msg);
|
|
282
|
+
return hooks;
|
|
283
|
+
}
|
|
284
|
+
const combiner = resolveCombiner(opts, env);
|
|
285
|
+
if (!combiner) {
|
|
286
|
+
const msg = "mcp-combiner command not found; install `mcp-combiner`, set `command`/`checkout`, " +
|
|
287
|
+
"or set $OPENCODE_MCP_COMBINER_COMMAND / $OPENCODE_MCP_COMBINER_CHECKOUT";
|
|
288
|
+
log("error", msg);
|
|
289
|
+
toast("error", msg);
|
|
290
|
+
return hooks;
|
|
291
|
+
}
|
|
292
|
+
const cfgPath = resolveConfig(opts, env);
|
|
293
|
+
if (!cfgPath) {
|
|
294
|
+
const msg = "no combiner servers.json found; set `config` or $OPENCODE_MCP_COMBINER_CONFIG " +
|
|
295
|
+
"(probed ~/.cache/secrets/<user>.mcpservers.json, ~/.config/mcp-combiner/servers.json, ~/.config/mcp/servers.json)";
|
|
296
|
+
log("error", msg);
|
|
297
|
+
toast("error", msg);
|
|
298
|
+
return hooks;
|
|
299
|
+
}
|
|
300
|
+
// Assemble the wrapped command: <combiner> [--mcp] --config <cfg> --port <port> [--host <host>]
|
|
301
|
+
const serve = [];
|
|
302
|
+
if (combinerNeedsMcpFlag(combiner, env))
|
|
303
|
+
serve.push("--mcp");
|
|
304
|
+
serve.push("--config", cfgPath, "--port", String(port));
|
|
305
|
+
if (opts.host)
|
|
306
|
+
serve.push("--host", opts.host);
|
|
307
|
+
const wrapped = { cmd: combiner.cmd, args: [...combiner.args, ...serve] };
|
|
308
|
+
const useArgs = [
|
|
309
|
+
"use",
|
|
310
|
+
name,
|
|
311
|
+
"--pid",
|
|
312
|
+
String(process.pid),
|
|
313
|
+
"--grace-period",
|
|
314
|
+
opts.gracePeriod ?? DEFAULT_GRACE,
|
|
315
|
+
"--metadata",
|
|
316
|
+
`opencode-${process.pid}`,
|
|
317
|
+
];
|
|
318
|
+
if (opts.logFile)
|
|
319
|
+
useArgs.push("--log-file", opts.logFile);
|
|
320
|
+
useArgs.push("--", wrapped.cmd, ...wrapped.args);
|
|
321
|
+
installCleanup();
|
|
322
|
+
const pre = preCheck(binary, name, env);
|
|
323
|
+
const result = spawnSync(binary, useArgs, { stdio: "pipe", env });
|
|
324
|
+
if (result.error) {
|
|
325
|
+
const msg = `${name}: failed to spawn sharedserver (${result.error.message})`;
|
|
326
|
+
log("error", msg);
|
|
327
|
+
toast("error", msg);
|
|
328
|
+
return hooks;
|
|
329
|
+
}
|
|
330
|
+
if (result.status !== 0) {
|
|
331
|
+
const stderr = result.stderr?.toString().trim();
|
|
332
|
+
const msg = `${name}: sharedserver use exited ${result.status}${stderr ? ` (${stderr})` : ""}`;
|
|
333
|
+
log("error", msg);
|
|
334
|
+
toast("error", msg);
|
|
335
|
+
return hooks;
|
|
336
|
+
}
|
|
337
|
+
attached.push({ binary, name, env });
|
|
338
|
+
if (pre === "stopped" || pre === "unknown") {
|
|
339
|
+
log("info", `started combiner "${name}" (${wrapped.cmd} ${wrapped.args.join(" ")})`);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
log("info", `attached to running combiner "${name}" (was ${pre})`);
|
|
343
|
+
}
|
|
344
|
+
scheduleProcessHealthCheck(binary, name, env, log, toast, 2500);
|
|
345
|
+
scheduleMcpHealthCheck(client, mcpName, log, toast, 5000);
|
|
346
|
+
return hooks;
|
|
347
|
+
};
|
|
348
|
+
export default McpCombinerPlugin;
|
|
349
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geohar/opencode-mcp-combiner",
|
|
3
|
+
"version": "0.1.0-dev.0",
|
|
4
|
+
"description": "OpenCode plugin: run the mcp-combiner MCP aggregator via sharedserver and register its endpoint with OpenCode.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENSE"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"clean": "rm -rf dist",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
17
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"opencode",
|
|
21
|
+
"opencode-plugin",
|
|
22
|
+
"mcp",
|
|
23
|
+
"mcp-combiner",
|
|
24
|
+
"sharedserver",
|
|
25
|
+
"plugin"
|
|
26
|
+
],
|
|
27
|
+
"author": "George Harker <george@georgeharker.com>",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/georgeharker/opencode-mcp-combiner.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/georgeharker/opencode-mcp-combiner/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/georgeharker/opencode-mcp-combiner#readme",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@opencode-ai/plugin": ">=1.3.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@opencode-ai/plugin": "^1.3.0",
|
|
45
|
+
"@types/node": "^22.0.0",
|
|
46
|
+
"typescript": "^5.6.0"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18.0.0"
|
|
50
|
+
}
|
|
51
|
+
}
|