@geohar/pi-mcp-combiner 0.0.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 +151 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +419 -0
- package/dist/index.js.map +1 -0
- package/dist/pi.d.ts +70 -0
- package/dist/pi.d.ts.map +1 -0
- package/dist/pi.js +10 -0
- package/dist/pi.js.map +1 -0
- package/dist/sharedserver-resolve.d.ts +19 -0
- package/dist/sharedserver-resolve.d.ts.map +1 -0
- package/dist/sharedserver-resolve.js +188 -0
- package/dist/sharedserver-resolve.js.map +1 -0
- package/instructions.txt +27 -0
- package/mcp.json.example +7 -0
- package/package.json +58 -0
- package/src/index.ts +484 -0
- package/src/pi.ts +70 -0
- package/src/sharedserver-resolve.ts +221 -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,151 @@
|
|
|
1
|
+
# @geohar/pi-mcp-combiner
|
|
2
|
+
|
|
3
|
+
A [Pi](https://pi.dev) extension that makes the **`mcp-combiner`** MCP aggregator
|
|
4
|
+
available to Pi: it starts the combiner (supervised by
|
|
5
|
+
[`sharedserver`](https://github.com/georgeharker/sharedserver)) and appends the
|
|
6
|
+
combiner's tool-discovery directive to the system prompt.
|
|
7
|
+
|
|
8
|
+
It is the Pi counterpart of the
|
|
9
|
+
[Claude Code](https://github.com/georgeharker/mcp-companion/tree/main/plugins/claude)
|
|
10
|
+
and
|
|
11
|
+
[OpenCode](https://github.com/georgeharker/mcp-companion/tree/main/plugins/opencode)
|
|
12
|
+
plugins, and shares the same combiner and the same `sharedserver` instance — so Pi,
|
|
13
|
+
Claude Code, OpenCode, and Neovim can all talk to **one** refcounted combiner process.
|
|
14
|
+
|
|
15
|
+
## How it fits together
|
|
16
|
+
|
|
17
|
+
Pi has no MCP of its own. Two pieces give it the combiner:
|
|
18
|
+
|
|
19
|
+
1. **[`pi-mcp-adapter`](https://pi.dev/packages/pi-mcp-adapter)** — the Pi package that
|
|
20
|
+
actually speaks MCP. It reads its own `mcp.json` and connects to the combiner over
|
|
21
|
+
HTTP. **This extension does not replace it — you install both.**
|
|
22
|
+
2. **This extension** — the process + instructions half, mirroring the sibling plugins:
|
|
23
|
+
- **Run the combiner** — on `session_start` it drives
|
|
24
|
+
```
|
|
25
|
+
sharedserver use <name> --pid <pi-pid> --grace-period <g> \
|
|
26
|
+
-- <combiner> --mcp --config <servers.json> --port <port>
|
|
27
|
+
```
|
|
28
|
+
`sharedserver` refcounts by PID with a grace period, so the combiner is shared
|
|
29
|
+
across clients and outlives any single one. It releases the refcount on
|
|
30
|
+
`session_shutdown` — but only when `reason === "quit"`, since reload/resume/fork
|
|
31
|
+
keep the same Pi process and a fresh `session_start` re-attaches.
|
|
32
|
+
- **Inject instructions** — on `before_agent_start` it appends the combiner's
|
|
33
|
+
`<server>_`-prefix / "discover before assuming" directive to the system prompt. The
|
|
34
|
+
combiner also serves the same text as its MCP `instructions`, which `pi-mcp-adapter`
|
|
35
|
+
surfaces on connect, so this is a belt to those braces.
|
|
36
|
+
|
|
37
|
+
`sharedserver` itself is fetched automatically if it is not already installed (a pinned
|
|
38
|
+
release via the cargo-dist installer — no Rust toolchain needed), using the exact same
|
|
39
|
+
resolver as the other two plugins.
|
|
40
|
+
|
|
41
|
+
## Requirements
|
|
42
|
+
|
|
43
|
+
- **`pi-mcp-adapter`** installed in Pi: `pi install npm:pi-mcp-adapter`.
|
|
44
|
+
- **`mcp-combiner`** available as a command (`uv tool install mcp-combiner`), or just
|
|
45
|
+
**`uv`** on PATH — a pinned release is fetched from PyPI on demand. Requires combiner
|
|
46
|
+
≥ 0.8.0 (the `--mcp` serve flag; version-gated automatically).
|
|
47
|
+
- A combiner **`servers.json`** (see auto-probe locations below).
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
### 1. Point `pi-mcp-adapter` at the combiner
|
|
52
|
+
|
|
53
|
+
Drop the combiner entry into one of `pi-mcp-adapter`'s `mcp.json` locations — e.g.
|
|
54
|
+
project-local `.pi/mcp.json`, or global `~/.config/mcp/mcp.json`:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"mcp-combiner": {
|
|
60
|
+
"url": "http://127.0.0.1:9741/mcp"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
(See [`mcp.json.example`](./mcp.json.example).) To give this Pi instance its own chat
|
|
67
|
+
identity toward the combiner — parking its isolated upstream sessions separately — add a
|
|
68
|
+
path token: `"url": "http://127.0.0.1:9741/mcp/pi-<yourname>"`. The combiner gives URL
|
|
69
|
+
tokens first priority.
|
|
70
|
+
|
|
71
|
+
If you edit `mcp.json` while Pi is running, run `/reload` (or `mcp({ connect:
|
|
72
|
+
"mcp-combiner" })`) so the adapter re-reads it.
|
|
73
|
+
|
|
74
|
+
### 2. Install this extension
|
|
75
|
+
|
|
76
|
+
Any of Pi's extension-loading mechanisms — all support a **local directory**:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
# a) drop-in package dir (uses "main": dist/index.js — run `npm run build` first)
|
|
80
|
+
npm --prefix plugins/pi run build
|
|
81
|
+
ln -s "$PWD/plugins/pi" ~/.pi/agent/extensions/mcp-combiner
|
|
82
|
+
|
|
83
|
+
# b) one-off, build-free live dev — Pi loads the TS source directly
|
|
84
|
+
pi -e ./plugins/pi/src/index.ts
|
|
85
|
+
|
|
86
|
+
# c) settings.json — list a local path under "extensions"
|
|
87
|
+
# { "extensions": ["/abs/path/to/mcp-companion/plugins/pi/src/index.ts"] }
|
|
88
|
+
|
|
89
|
+
# d) published package — list under "packages" in settings.json
|
|
90
|
+
# { "packages": ["@geohar/pi-mcp-combiner@latest"] }
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
Everything is via the `PI_MCP_COMBINER_*` environment namespace, which mirrors the Claude
|
|
96
|
+
plugin's `CLAUDE_MCP_COMBINER_*` and the OpenCode plugin's `OPENCODE_MCP_COMBINER_*` — so
|
|
97
|
+
running several clients means one namespace per client.
|
|
98
|
+
|
|
99
|
+
| Variable | Default | Effect |
|
|
100
|
+
|----------|---------|--------|
|
|
101
|
+
| `PI_MCP_COMBINER_PORT` | `9741` | HTTP port the combiner serves on. |
|
|
102
|
+
| `PI_MCP_COMBINER_HOST` | `127.0.0.1` | HTTP host the combiner binds. |
|
|
103
|
+
| `PI_MCP_COMBINER_CONFIG` | *(auto-probed)* | Path to the combiner's `servers.json`. |
|
|
104
|
+
| `PI_MCP_COMBINER_COMMAND` / `_ARGS` | *(auto-resolved)* | Override the combiner invocation. |
|
|
105
|
+
| `PI_MCP_COMBINER_CHECKOUT` | — | Checkout for `uv run --project <checkout> python -m mcp_combiner`. |
|
|
106
|
+
| `PI_MCP_COMBINER_NAME` | `mcp-combiner` | `sharedserver` instance name. |
|
|
107
|
+
| `PI_MCP_COMBINER_GRACE` | `30m` | `sharedserver` grace period. |
|
|
108
|
+
| `PI_MCP_COMBINER_LOG` | `~/.local/state/mcp-combiner/mcp-combiner.log` | Capture the combiner's stdout/stderr; `"none"` disables. |
|
|
109
|
+
| `PI_MCP_COMBINER_PYLOG` | `~/.local/state/mcp-combiner/mcp-combiner-py.log` | The combiner's own `--log-file`; `"none"` disables. |
|
|
110
|
+
| `PI_MCP_COMBINER_LOG_LEVEL` | `info` | The combiner's `--log-level`. |
|
|
111
|
+
| `PI_MCP_COMBINER_MANAGE` | `true` | `false` → don't launch (assume the combiner runs elsewhere); instructions only. |
|
|
112
|
+
| `PI_MCP_COMBINER_INSTRUCTIONS` | `true` | `false` → don't append the directive to the system prompt. |
|
|
113
|
+
| `PI_MCP_COMBINER_NOTIFY` | `true` | `false` → don't surface attach/health messages via the Pi UI. |
|
|
114
|
+
| `SHAREDSERVER_BIN` | *(auto-resolved)* | Path to the `sharedserver` binary. |
|
|
115
|
+
| `SHAREDSERVER_LOCKDIR` | — | `sharedserver` lock directory. |
|
|
116
|
+
|
|
117
|
+
### `servers.json` auto-probe
|
|
118
|
+
|
|
119
|
+
`$PI_MCP_COMBINER_CONFIG` → `~/.cache/secrets/<user>.mcpservers.json` →
|
|
120
|
+
`~/.config/mcp-combiner/servers.json` → `~/.config/mcp/servers.json`.
|
|
121
|
+
|
|
122
|
+
### Combiner command resolution
|
|
123
|
+
|
|
124
|
+
`$PI_MCP_COMBINER_COMMAND` (+`$PI_MCP_COMBINER_ARGS`) → `mcp-combiner` on `PATH` (only
|
|
125
|
+
when ≥ 0.8.0) → `uv run --project <checkout> python -m mcp_combiner` → a pinned release
|
|
126
|
+
via `uvx`. If the `mcp-combiner` on PATH is older than 0.8.0, the extension warns and
|
|
127
|
+
falls back to a pinned `uvx` release rather than silently using a stale binary.
|
|
128
|
+
|
|
129
|
+
## Host-owned mode
|
|
130
|
+
|
|
131
|
+
If **`$MCP_COMPANION_COMBINER_URL`** is set, an editor/host (e.g. Neovim) already owns
|
|
132
|
+
and refcounts the combiner. The extension then **only injects instructions** and never
|
|
133
|
+
starts or stops the process — the same early-exit as the sibling plugins. Equivalent to
|
|
134
|
+
`PI_MCP_COMBINER_MANAGE=false`.
|
|
135
|
+
|
|
136
|
+
## Development
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
npm install
|
|
140
|
+
npm run typecheck
|
|
141
|
+
npm run build # emits dist/ (not committed; built on publish)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The `src/sharedserver-resolve.ts` file is **vendored byte-identical** from
|
|
145
|
+
[`georgeharker/sharedserver`](https://github.com/georgeharker/sharedserver) (via
|
|
146
|
+
`scripts/sync-vendored.sh`), shared with the OpenCode plugin so both answer "which
|
|
147
|
+
sharedserver, and why" identically. Edit it upstream; re-sync here.
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
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":"AAiCA,OAAO,KAAK,EAER,YAAY,EAIf,MAAM,SAAS,CAAA;AA6NhB,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA+I1D"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
// Pi extension: run the `mcp-combiner` MCP aggregator via the `sharedserver` CLI so it
|
|
2
|
+
// is available to Pi through `pi-mcp-adapter`.
|
|
3
|
+
//
|
|
4
|
+
// Pi has no MCP of its own; `pi-mcp-adapter` (a separate Pi package) is what actually
|
|
5
|
+
// talks MCP, reading its own `mcp.json`. This extension is the OTHER half — the exact
|
|
6
|
+
// counterpart of the process side of the Claude Code and OpenCode plugins:
|
|
7
|
+
//
|
|
8
|
+
// 1. Process — drive `sharedserver use … -- <combiner> --mcp --config … --port …` so
|
|
9
|
+
// the combiner is running and refcounted, SHARED with any other client (Claude
|
|
10
|
+
// Code, OpenCode, Neovim) that uses the same sharedserver name. Launched in
|
|
11
|
+
// `session_start` (Pi forbids background startup from the factory) and released in
|
|
12
|
+
// `session_shutdown` — but only on `reason === "quit"`, since reload/new/resume/
|
|
13
|
+
// fork keep the same Pi process alive and a fresh `session_start` re-attaches.
|
|
14
|
+
// 2. Instructions — append the combiner's tool-discovery directive to the system
|
|
15
|
+
// prompt via `before_agent_start` (analogue of the CC plugin's SessionStart
|
|
16
|
+
// additionalContext and the OpenCode plugin's system.transform hook). The combiner
|
|
17
|
+
// also serves the same text as its MCP `instructions`, which pi-mcp-adapter
|
|
18
|
+
// surfaces on connect — this is the guaranteed, client-native belt to that braces.
|
|
19
|
+
//
|
|
20
|
+
// Registration itself (pointing pi-mcp-adapter at the combiner) is a single `mcp.json`
|
|
21
|
+
// entry — see mcp.json.example and the README. That is static, CC-style, by design;
|
|
22
|
+
// this extension deliberately does not write another extension's config.
|
|
23
|
+
//
|
|
24
|
+
// The sharedserver resolution/fetch and the combiner command ladder are ported
|
|
25
|
+
// faithfully from plugins/opencode/src/index.ts — same floor, same warnings, same
|
|
26
|
+
// degrade-rather-than-die behaviour, so a user running several clients gets one answer
|
|
27
|
+
// to "which combiner / which sharedserver am I on, and why".
|
|
28
|
+
import { spawnSync } from "node:child_process";
|
|
29
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
30
|
+
import { homedir } from "node:os";
|
|
31
|
+
import { dirname, join } from "node:path";
|
|
32
|
+
import { fileURLToPath } from "node:url";
|
|
33
|
+
import { resolveSharedserver } from "./sharedserver-resolve.js";
|
|
34
|
+
const DEFAULT_PORT = 9741;
|
|
35
|
+
const DEFAULT_NAME = "mcp-combiner";
|
|
36
|
+
const DEFAULT_GRACE = "30m";
|
|
37
|
+
// The `--mcp` serve flag arrived in combiner 0.8.0; older versions serve with a bare
|
|
38
|
+
// `--config`. That boundary is also the floor for trusting a PATH install — below it we
|
|
39
|
+
// fetch a known-good release via uvx rather than limp along. NOT the lockstep release
|
|
40
|
+
// version; it moves only when this extension depends on newer combiner behaviour.
|
|
41
|
+
const MIN_COMBINER_VERSION = [0, 8, 0];
|
|
42
|
+
// Floor-only against sharedserver's latest release: this repo consumes sharedserver
|
|
43
|
+
// rather than shipping it. Kept equal to the OpenCode plugin's value (they resolve the
|
|
44
|
+
// same binary the same way).
|
|
45
|
+
const SHAREDSERVER_MIN_VERSION = "0.6.7";
|
|
46
|
+
// ── the tool-discovery directive ───────────────────────────────────
|
|
47
|
+
// Appended to the system prompt so the agent knows combined tools arrive under a
|
|
48
|
+
// `<server>_` prefix (and, through pi-mcp-adapter, are reached via its `mcp()` proxy or
|
|
49
|
+
// promoted `directTools`), and looks before deciding a capability is absent. Canonical
|
|
50
|
+
// source: CLAUDE.md.example at the repo root; a release-time `prepack` copies it to this
|
|
51
|
+
// package's root as instructions.txt (see package.json). A dev/unbuilt run without the
|
|
52
|
+
// copy falls back to empty and simply injects nothing.
|
|
53
|
+
const COMBINER_DIRECTIVE = (() => {
|
|
54
|
+
try {
|
|
55
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
56
|
+
// dist/index.js and the packed instructions.txt both sit one level up from here
|
|
57
|
+
// when built (dist/), and the source layout mirrors it (src/ → package root).
|
|
58
|
+
return readFileSync(join(here, "..", "instructions.txt"), "utf8");
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
64
|
+
// First line of the directive — used to detect an already-appended prompt so repeated
|
|
65
|
+
// `before_agent_start` turns do not stack duplicate copies.
|
|
66
|
+
const DIRECTIVE_MARKER = COMBINER_DIRECTIVE.split("\n", 1)[0] ?? "";
|
|
67
|
+
// ── env configuration ──────────────────────────────────────────────
|
|
68
|
+
// The PI_MCP_COMBINER_* namespace mirrors the Claude plugin's CLAUDE_MCP_COMBINER_* and
|
|
69
|
+
// the OpenCode plugin's OPENCODE_MCP_COMBINER_*, so a user running several clients keeps
|
|
70
|
+
// one namespace per client rather than options in one place and env vars in another.
|
|
71
|
+
function env(name) {
|
|
72
|
+
const v = process.env[name];
|
|
73
|
+
return v !== undefined && v !== "" ? v : undefined;
|
|
74
|
+
}
|
|
75
|
+
function splitArgs(value) {
|
|
76
|
+
if (!value)
|
|
77
|
+
return [];
|
|
78
|
+
return value.split(/\s+/).filter((s) => s.length > 0);
|
|
79
|
+
}
|
|
80
|
+
function parseVersion(text) {
|
|
81
|
+
const m = text.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
82
|
+
if (!m)
|
|
83
|
+
return undefined;
|
|
84
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
85
|
+
}
|
|
86
|
+
function gte(a, b) {
|
|
87
|
+
for (let i = 0; i < 3; i++) {
|
|
88
|
+
if (a[i] !== b[i])
|
|
89
|
+
return a[i] > b[i];
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
/** Probe a command once for BOTH facts: is it runnable, and which version. Presence is
|
|
94
|
+
* "spawn did not fail", NOT "exited 0" — a pre-0.8.0 combiner exits non-zero on
|
|
95
|
+
* `--version` (the flag did not exist), which is exactly the stale install the floor
|
|
96
|
+
* exists to catch. Probes with ONLY the structural args; the user's extras are never
|
|
97
|
+
* folded in, so one arg the CLI rejects cannot make a healthy install look absent. */
|
|
98
|
+
function probe(cmd) {
|
|
99
|
+
const r = spawnSync(cmd.cmd, [...cmd.args, "--version"], { env: process.env });
|
|
100
|
+
if (r.error)
|
|
101
|
+
return { present: false };
|
|
102
|
+
if (r.status !== 0)
|
|
103
|
+
return { present: true };
|
|
104
|
+
return { present: true, version: parseVersion(`${r.stdout?.toString() ?? ""}${r.stderr?.toString() ?? ""}`) };
|
|
105
|
+
}
|
|
106
|
+
function probeUsable(cmd) {
|
|
107
|
+
const { version } = probe(cmd);
|
|
108
|
+
if (!version || !gte(version, MIN_COMBINER_VERSION))
|
|
109
|
+
return undefined;
|
|
110
|
+
return { ...cmd, version };
|
|
111
|
+
}
|
|
112
|
+
/** This extension's own version, kept equal to the published PyPI mcp-combiner by
|
|
113
|
+
* lockstep releases (scripts/bump-version.sh). Used as the uvx pin — read from the
|
|
114
|
+
* manifest, never duplicated as a constant. */
|
|
115
|
+
const PLUGIN_VERSION = (() => {
|
|
116
|
+
try {
|
|
117
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
118
|
+
const pkg = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8"));
|
|
119
|
+
return pkg.version;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
})();
|
|
125
|
+
/** Resolve how to invoke the combiner, mirroring the sibling plugins' priority:
|
|
126
|
+
* explicit env command → `mcp-combiner` on PATH (only when new enough) →
|
|
127
|
+
* `uv run --project <checkout> python -m mcp_combiner` → a pinned release via uvx. The
|
|
128
|
+
* uvx tail is what makes a bare install work with nothing installed by hand. */
|
|
129
|
+
function resolveCombiner(log) {
|
|
130
|
+
const extra = splitArgs(env("PI_MCP_COMBINER_ARGS"));
|
|
131
|
+
const command = env("PI_MCP_COMBINER_COMMAND");
|
|
132
|
+
if (command) {
|
|
133
|
+
return { cmd: command, args: [], extra };
|
|
134
|
+
}
|
|
135
|
+
const onPath = { cmd: "mcp-combiner", args: [] };
|
|
136
|
+
const pathProbe = probe(onPath);
|
|
137
|
+
if (pathProbe.present) {
|
|
138
|
+
if (pathProbe.version && gte(pathProbe.version, MIN_COMBINER_VERSION)) {
|
|
139
|
+
return { ...onPath, extra, version: pathProbe.version };
|
|
140
|
+
}
|
|
141
|
+
log("warn", `mcp-combiner on PATH reports ${pathProbe.version?.join(".") ?? "a pre-0.8.0 version"}, older than ` +
|
|
142
|
+
`${MIN_COMBINER_VERSION.join(".")} — ignoring it and fetching a pinned release instead. ` +
|
|
143
|
+
"Upgrade with: uv tool install --upgrade mcp-combiner");
|
|
144
|
+
}
|
|
145
|
+
const checkout = env("PI_MCP_COMBINER_CHECKOUT");
|
|
146
|
+
if (checkout && existsSync(checkout) && probe({ cmd: "uv", args: [] }).present) {
|
|
147
|
+
return { cmd: "uv", args: ["run", "--project", checkout, "python", "-m", "mcp_combiner"], extra };
|
|
148
|
+
}
|
|
149
|
+
// Out-of-the-box path: no install required, fetch from PyPI. Pinned to this
|
|
150
|
+
// extension's version so the pair moves in lockstep; if that exact release is
|
|
151
|
+
// missing, fall back to latest rather than failing. The probe both validates the pin
|
|
152
|
+
// and warms uv's cache, so the real spawn is a cache hit.
|
|
153
|
+
if (probe({ cmd: "uvx", args: [] }).present) {
|
|
154
|
+
if (PLUGIN_VERSION) {
|
|
155
|
+
const pinned = probeUsable({ cmd: "uvx", args: [`mcp-combiner@${PLUGIN_VERSION}`] });
|
|
156
|
+
if (pinned)
|
|
157
|
+
return { ...pinned, extra };
|
|
158
|
+
log("warn", `pinned mcp-combiner@${PLUGIN_VERSION} unavailable or too old; trying latest from PyPI`);
|
|
159
|
+
}
|
|
160
|
+
const latest = probeUsable({ cmd: "uvx", args: ["mcp-combiner"] });
|
|
161
|
+
if (latest)
|
|
162
|
+
return { ...latest, extra };
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
/** True if the resolved combiner supports (needs) the `--mcp` serve flag. Unknown
|
|
167
|
+
* version → assume NO: a version we cannot read is a pre-0.8.0 build serving on a bare
|
|
168
|
+
* `--config`, and a bare `--config` still serves on newer releases (with a deprecation
|
|
169
|
+
* warning), so guessing low degrades gracefully whereas guessing high does not. */
|
|
170
|
+
function combinerNeedsMcpFlag(cmd) {
|
|
171
|
+
const ver = cmd.version ?? probe({ cmd: cmd.cmd, args: cmd.args }).version;
|
|
172
|
+
return ver ? gte(ver, MIN_COMBINER_VERSION) : false;
|
|
173
|
+
}
|
|
174
|
+
// ── servers.json resolution (mirrors the sibling plugins) ──
|
|
175
|
+
function resolveConfig(log) {
|
|
176
|
+
const explicit = env("PI_MCP_COMBINER_CONFIG");
|
|
177
|
+
if (explicit)
|
|
178
|
+
return explicit;
|
|
179
|
+
const user = process.env.USER ?? process.env.LOGNAME ?? "";
|
|
180
|
+
const candidates = [
|
|
181
|
+
join(homedir(), ".cache", "secrets", `${user}.mcpservers.json`),
|
|
182
|
+
join(homedir(), ".config", "mcp-combiner", "servers.json"),
|
|
183
|
+
join(homedir(), ".config", "mcp", "servers.json"),
|
|
184
|
+
];
|
|
185
|
+
const found = candidates.find(existsSync);
|
|
186
|
+
if (!found) {
|
|
187
|
+
log("error", "no combiner servers.json found; set $PI_MCP_COMBINER_CONFIG (probed " +
|
|
188
|
+
"~/.cache/secrets/<user>.mcpservers.json, ~/.config/mcp-combiner/servers.json, " +
|
|
189
|
+
"~/.config/mcp/servers.json)");
|
|
190
|
+
}
|
|
191
|
+
return found;
|
|
192
|
+
}
|
|
193
|
+
let attachment = null;
|
|
194
|
+
let cleanupInstalled = false;
|
|
195
|
+
function installProcessCleanup() {
|
|
196
|
+
if (cleanupInstalled)
|
|
197
|
+
return;
|
|
198
|
+
cleanupInstalled = true;
|
|
199
|
+
// Belt to the session_shutdown("quit") braces: if Pi is killed hard enough that
|
|
200
|
+
// session_shutdown never fires, still release the refcount. Idempotent via `detach`.
|
|
201
|
+
process.on("exit", () => detach());
|
|
202
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
203
|
+
process.on(sig, () => {
|
|
204
|
+
detach();
|
|
205
|
+
process.kill(process.pid, sig);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function detach() {
|
|
210
|
+
if (!attachment)
|
|
211
|
+
return;
|
|
212
|
+
const { binary, name } = attachment;
|
|
213
|
+
attachment = null;
|
|
214
|
+
spawnSync(binary, ["unuse", name, "--pid", String(process.pid)], { stdio: "ignore", env: process.env });
|
|
215
|
+
}
|
|
216
|
+
// ── the extension ──────────────────────────────────────────────────
|
|
217
|
+
export default function mcpCombiner(pi) {
|
|
218
|
+
const notify = env("PI_MCP_COMBINER_NOTIFY") !== "false";
|
|
219
|
+
const wantInstructions = env("PI_MCP_COMBINER_INSTRUCTIONS") !== "false";
|
|
220
|
+
const manage = env("PI_MCP_COMBINER_MANAGE") !== "false";
|
|
221
|
+
// Host-owned mode: a set $MCP_COMPANION_COMBINER_URL means an editor/host (e.g.
|
|
222
|
+
// Neovim) already owns and refcounts the combiner. Then we NEVER launch — only the
|
|
223
|
+
// instructions half runs. The host never sets a port var, mirroring the sibling
|
|
224
|
+
// plugins' distinction.
|
|
225
|
+
const hostOwned = env("MCP_COMPANION_COMBINER_URL") !== undefined && env("PI_MCP_COMBINER_PORT") === undefined;
|
|
226
|
+
// ── instructions: appended every turn (analogue of the sibling plugins) ──
|
|
227
|
+
pi.on("before_agent_start", (event) => {
|
|
228
|
+
if (!wantInstructions || !COMBINER_DIRECTIVE)
|
|
229
|
+
return;
|
|
230
|
+
// Do not stack duplicates across turns: the chained prompt carries forward.
|
|
231
|
+
if (DIRECTIVE_MARKER && event.systemPrompt.includes(DIRECTIVE_MARKER))
|
|
232
|
+
return;
|
|
233
|
+
return { systemPrompt: `${event.systemPrompt}\n\n${COMBINER_DIRECTIVE}` };
|
|
234
|
+
});
|
|
235
|
+
// ── /mcp-combiner command: inspect the extension (verb: system-prompt) ──
|
|
236
|
+
pi.registerCommand("mcp-combiner", {
|
|
237
|
+
description: "mcp-combiner extension — verb: system-prompt (show the injected directive)",
|
|
238
|
+
getArgumentCompletions: (prefix) => completeVerbs(prefix),
|
|
239
|
+
handler: (args, ctx) => {
|
|
240
|
+
const verb = args.trim();
|
|
241
|
+
if (verb === "" || verb === "system-prompt") {
|
|
242
|
+
showDirective(ctx, "mcp-combiner", COMBINER_DIRECTIVE, wantInstructions);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
ctx.ui?.notify?.(`mcp-combiner: unknown verb "${verb}". Try: system-prompt`, "warn");
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
if (hostOwned || !manage) {
|
|
249
|
+
// Registration is static (mcp.json) and the combiner is someone else's to run:
|
|
250
|
+
// nothing to launch. The instructions handler above still applies.
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// ── process: launch on session_start, release on session_shutdown("quit") ──
|
|
254
|
+
// Pi forbids starting background resources from the factory, so all of this is
|
|
255
|
+
// deferred to session_start. session_start fires again on reload/new/resume/fork
|
|
256
|
+
// within the same process; the `attachment` guard makes re-entry a no-op attach.
|
|
257
|
+
pi.on("session_start", (_event, ctx) => {
|
|
258
|
+
if (attachment)
|
|
259
|
+
return; // already attached in this process
|
|
260
|
+
const log = makeLog(ctx, notify);
|
|
261
|
+
const binary = resolveSharedserver({
|
|
262
|
+
label: "mcp-combiner",
|
|
263
|
+
minVersion: SHAREDSERVER_MIN_VERSION,
|
|
264
|
+
installerUrl: "https://github.com/georgeharker/sharedserver/releases/latest/download/sharedserver-installer.sh",
|
|
265
|
+
}, env("SHAREDSERVER_BIN"), process.env, log);
|
|
266
|
+
if (!binary) {
|
|
267
|
+
log("error", "sharedserver binary not found; set $SHAREDSERVER_BIN, or PI_MCP_COMBINER_MANAGE=false");
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const combiner = resolveCombiner(log);
|
|
271
|
+
if (!combiner) {
|
|
272
|
+
log("error", "mcp-combiner could not be found or fetched; install uv and it is fetched from PyPI on demand, " +
|
|
273
|
+
"or install mcp-combiner, or set $PI_MCP_COMBINER_COMMAND / $PI_MCP_COMBINER_CHECKOUT");
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const cfgPath = resolveConfig(log);
|
|
277
|
+
if (!cfgPath)
|
|
278
|
+
return; // resolveConfig already logged the specifics
|
|
279
|
+
const name = env("PI_MCP_COMBINER_NAME") ?? DEFAULT_NAME;
|
|
280
|
+
const port = resolvePort(log);
|
|
281
|
+
const grace = env("PI_MCP_COMBINER_GRACE") ?? DEFAULT_GRACE;
|
|
282
|
+
// Assemble: <combiner> [--mcp] --config <cfg> --port <port> [--host <host>]
|
|
283
|
+
const serve = [];
|
|
284
|
+
const modern = combinerNeedsMcpFlag(combiner);
|
|
285
|
+
if (modern)
|
|
286
|
+
serve.push("--mcp");
|
|
287
|
+
serve.push("--config", cfgPath, "--port", String(port));
|
|
288
|
+
const host = env("PI_MCP_COMBINER_HOST");
|
|
289
|
+
if (host)
|
|
290
|
+
serve.push("--host", host);
|
|
291
|
+
// Logging parity with the sibling plugins' two-file scheme: sharedserver captures
|
|
292
|
+
// raw stdout/stderr (--log-file on `use`), and the combiner writes its own
|
|
293
|
+
// --log-file. Both default under $XDG_STATE_HOME/mcp-combiner; "none" disables.
|
|
294
|
+
// Combiner-side flags ride the same version gate as --mcp.
|
|
295
|
+
const logDir = join(process.env.XDG_STATE_HOME || join(homedir(), ".local", "state"), "mcp-combiner");
|
|
296
|
+
const pyLogFile = env("PI_MCP_COMBINER_PYLOG") ?? join(logDir, "mcp-combiner-py.log");
|
|
297
|
+
if (modern) {
|
|
298
|
+
if (pyLogFile !== "none")
|
|
299
|
+
serve.push("--log-file", pyLogFile);
|
|
300
|
+
serve.push("--log-level", env("PI_MCP_COMBINER_LOG_LEVEL") ?? "info");
|
|
301
|
+
}
|
|
302
|
+
const wrappedArgs = [...combiner.args, ...(combiner.extra ?? []), ...serve];
|
|
303
|
+
const useArgs = [
|
|
304
|
+
"use",
|
|
305
|
+
name,
|
|
306
|
+
"--pid",
|
|
307
|
+
String(process.pid),
|
|
308
|
+
"--grace-period",
|
|
309
|
+
grace,
|
|
310
|
+
"--metadata",
|
|
311
|
+
`pi-${process.pid}`,
|
|
312
|
+
];
|
|
313
|
+
const logFile = env("PI_MCP_COMBINER_LOG") ?? join(logDir, "mcp-combiner.log");
|
|
314
|
+
if (logFile !== "none") {
|
|
315
|
+
try {
|
|
316
|
+
mkdirSync(dirname(logFile), { recursive: true });
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
// best-effort — a failed mkdir just means sharedserver may drop the capture
|
|
320
|
+
}
|
|
321
|
+
useArgs.push("--log-file", logFile);
|
|
322
|
+
}
|
|
323
|
+
useArgs.push("--", combiner.cmd, ...wrappedArgs);
|
|
324
|
+
installProcessCleanup();
|
|
325
|
+
const result = spawnSync(binary, useArgs, { stdio: "pipe", env: process.env });
|
|
326
|
+
if (result.error) {
|
|
327
|
+
log("error", `${name}: failed to spawn sharedserver (${result.error.message})`);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (result.status !== 0) {
|
|
331
|
+
const stderr = result.stderr?.toString().trim();
|
|
332
|
+
log("error", `${name}: sharedserver use exited ${result.status}${stderr ? ` (${stderr})` : ""}`);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
attachment = { binary, name };
|
|
336
|
+
log("info", `combiner "${name}" attached on port ${port} (${combiner.cmd} ${wrappedArgs.join(" ")})`);
|
|
337
|
+
});
|
|
338
|
+
pi.on("session_shutdown", (event) => {
|
|
339
|
+
// Only "quit" means the Pi process is actually leaving. reload/new/resume/fork
|
|
340
|
+
// keep the process alive and a fresh session_start re-attaches — releasing the
|
|
341
|
+
// refcount on those would needlessly drop (and re-take) the shared combiner.
|
|
342
|
+
if (event.reason === "quit")
|
|
343
|
+
detach();
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
// ── helpers ────────────────────────────────────────────────────────
|
|
347
|
+
// The verbs the extension's slash command understands. `system-prompt` shows the
|
|
348
|
+
// directive this extension injects — the show-command pattern from pi-custom-system-prompt,
|
|
349
|
+
// since `before_agent_start` injections are per-turn and never appear in Pi's own
|
|
350
|
+
// `/system-prompt` (which reports the base prompt only).
|
|
351
|
+
const COMMAND_VERBS = ["system-prompt"];
|
|
352
|
+
function completeVerbs(prefix) {
|
|
353
|
+
const p = prefix.trim();
|
|
354
|
+
const matches = COMMAND_VERBS.filter((v) => v.startsWith(p));
|
|
355
|
+
return matches.length ? matches.map((v) => ({ value: v, label: v })) : null;
|
|
356
|
+
}
|
|
357
|
+
const SHOW_LIMIT = 1600;
|
|
358
|
+
function showDirective(ctx, label, directive, enabled) {
|
|
359
|
+
if (!directive) {
|
|
360
|
+
ctx.ui?.notify?.(`${label}: no directive bundled (instructions.txt missing)`, "warn");
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const head = enabled
|
|
364
|
+
? `${label} directive — injected into the system prompt on every turn (before_agent_start):`
|
|
365
|
+
: `${label} directive — injection is DISABLED this session; it would be:`;
|
|
366
|
+
const body = directive.length > SHOW_LIMIT
|
|
367
|
+
? `${directive.slice(0, SHOW_LIMIT)}\n\n… (${directive.length} chars total)`
|
|
368
|
+
: directive;
|
|
369
|
+
ctx.ui?.notify?.(`${head}\n\n${body}`, "info");
|
|
370
|
+
}
|
|
371
|
+
function makeLog(ctx, notify) {
|
|
372
|
+
return (level, message) => {
|
|
373
|
+
const line = `mcp-combiner: ${message}`;
|
|
374
|
+
// Pi has no structured plugin log sink like OpenCode's client.app.log; surface
|
|
375
|
+
// through the UI when there is one (and the user has not opted out), else stderr.
|
|
376
|
+
if (notify && ctx.hasUI && ctx.ui?.notify) {
|
|
377
|
+
ctx.ui.notify(line, level === "error" ? "error" : level === "warn" ? "warn" : "info");
|
|
378
|
+
}
|
|
379
|
+
else if (level === "error" || level === "warn") {
|
|
380
|
+
process.stderr.write(`${line}\n`);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
/** The port to serve on, reconciling PI_MCP_COMBINER_PORT with any explicit
|
|
385
|
+
* $MCP_COMPANION_COMBINER_URL (which is what pi-mcp-adapter registers, so serving
|
|
386
|
+
* anywhere else would be unreachable — the URL wins, same rule as the sibling plugins). */
|
|
387
|
+
function resolvePort(log) {
|
|
388
|
+
const raw = env("PI_MCP_COMBINER_PORT");
|
|
389
|
+
let port = DEFAULT_PORT;
|
|
390
|
+
if (raw !== undefined) {
|
|
391
|
+
const n = Number(raw);
|
|
392
|
+
if (Number.isInteger(n) && n > 0)
|
|
393
|
+
port = n;
|
|
394
|
+
else
|
|
395
|
+
log("warn", `PI_MCP_COMBINER_PORT=${raw} is not a positive integer; using ${DEFAULT_PORT}`);
|
|
396
|
+
}
|
|
397
|
+
const url = env("MCP_COMPANION_COMBINER_URL");
|
|
398
|
+
if (url && raw !== undefined) {
|
|
399
|
+
let urlPort = Number.NaN;
|
|
400
|
+
try {
|
|
401
|
+
urlPort = Number(new URL(url).port);
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
// malformed URL — fall through to the no-usable-port warning below
|
|
405
|
+
}
|
|
406
|
+
if (!Number.isInteger(urlPort) || urlPort <= 0) {
|
|
407
|
+
log("warn", `MCP_COMPANION_COMBINER_URL=${url} has no explicit port; serving on ${port}. ` +
|
|
408
|
+
`Use an explicit port in the URL (e.g. http://127.0.0.1:${port}/mcp).`);
|
|
409
|
+
}
|
|
410
|
+
else if (urlPort !== port) {
|
|
411
|
+
log("warn", `PI_MCP_COMBINER_PORT=${port} disagrees with MCP_COMPANION_COMBINER_URL=${url}; the URL is what ` +
|
|
412
|
+
`pi-mcp-adapter registers, so serving on ${port} would be unreachable — using ${urlPort}. ` +
|
|
413
|
+
"Set them to the same port.");
|
|
414
|
+
port = urlPort;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return port;
|
|
418
|
+
}
|
|
419
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|