@imenam/mcp-gui-interface 1.0.5 → 1.0.6
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/README.md +261 -288
- package/dist/src/logger.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,288 +1,261 @@
|
|
|
1
|
-
# @imenam/mcp-gui-interface
|
|
2
|
-
|
|
3
|
-
Shared library for MCP GUI lifecycle management — spawning, IPC, proxy registration, and logging.
|
|
4
|
-
|
|
5
|
-
This package encapsulates the boilerplate
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm install @imenam/mcp-gui-interface
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
No runtime dependencies. Requires Node.js with ESM support (`"type": "module"`).
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
Logger,
|
|
263
|
-
SetupLoggingOptions,
|
|
264
|
-
} from "@imenam/mcp-gui-interface";
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
---
|
|
268
|
-
|
|
269
|
-
## Development
|
|
270
|
-
|
|
271
|
-
```bash
|
|
272
|
-
# Build
|
|
273
|
-
npm run build
|
|
274
|
-
|
|
275
|
-
# Watch mode
|
|
276
|
-
npm run build:watch
|
|
277
|
-
|
|
278
|
-
# Release (patch bump, build, publish, push tag)
|
|
279
|
-
npm run release
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
The compiled output and type declarations are in `dist/`.
|
|
283
|
-
|
|
284
|
-
---
|
|
285
|
-
|
|
286
|
-
## License
|
|
287
|
-
|
|
288
|
-
ISC © [Matthieu Pesnot-Pin](https://github.com/Matthieu-Pesnot-Pin)
|
|
1
|
+
# @imenam/mcp-gui-interface
|
|
2
|
+
|
|
3
|
+
Shared library for MCP GUI lifecycle management — spawning, IPC, proxy registration, and logging.
|
|
4
|
+
|
|
5
|
+
This package encapsulates the boilerplate many MCP servers need when they run a companion GUI: spawning a child process safely, communicating over IPC, registering with the central HTTP proxy, and logging without polluting **stdout** (reserved for MCP JSON-RPC on the parent process).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @imenam/mcp-gui-interface
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
No runtime dependencies. Requires Node.js with ESM support (`"type": "module"` in consuming packages).
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Scope: Node only
|
|
20
|
+
|
|
21
|
+
This package targets **Node.js processes** (MCP master and GUI worker). It is **not** meant for browser or Vite bundles — do not add it as a dependency of a React/Vite front-end.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## How it fits together
|
|
26
|
+
|
|
27
|
+
1. **MCP host** (e.g. Cursor) talks **only** to the **master** process over **stdio** (JSON-RPC). Nothing from this library runs in the host.
|
|
28
|
+
2. The **master** may use **`GuiLauncher`** to **fork** a second Node process: the **GUI worker**. They talk over Node **IPC** (`process.send` / `message`).
|
|
29
|
+
3. The **worker** starts an HTTP server (Express, Hono, …) and usually uses **`ProxyClient`** to register with your **HTTP proxy** so the UI gets a stable URL. It also serves the built SPA (static files).
|
|
30
|
+
4. The **browser** loads that SPA. It talks to the worker via **HTTP** (`fetch`, SSE). It does **not** import this npm package—the worker is already a Node server.
|
|
31
|
+
|
|
32
|
+
So: **two Node processes** (master + worker) may both depend on this package; the **browser bundle** must not.
|
|
33
|
+
|
|
34
|
+
```mermaid
|
|
35
|
+
flowchart LR
|
|
36
|
+
Host[MCP host]
|
|
37
|
+
Master[Master process]
|
|
38
|
+
Worker[GUI worker Node]
|
|
39
|
+
Proxy[HTTP proxy]
|
|
40
|
+
Browser[Browser]
|
|
41
|
+
|
|
42
|
+
Host <-->|stdio JSON-RPC| Master
|
|
43
|
+
Master -->|fork + IPC| Worker
|
|
44
|
+
Worker -->|register| Proxy
|
|
45
|
+
Browser -->|HTTP| Proxy
|
|
46
|
+
Proxy --> Worker
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For a longer French walkthrough, see **MCP Documentor** → project **`mcp-gui-interface`** → *Guide d’utilisation — @imenam/mcp-gui-interface*.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Why this library exists
|
|
54
|
+
|
|
55
|
+
MCP servers talk to the host (Cursor, Claude Desktop, etc.) over **stdout** using JSON-RPC. Any stray `console.log` on stdout can corrupt the protocol.
|
|
56
|
+
|
|
57
|
+
When an MCP server also serves a GUI (Express, Hono, etc.), it typically must:
|
|
58
|
+
|
|
59
|
+
1. **Fork** the GUI as a **separate child process** so the GUI’s stdout does not mix with the parent’s JSON-RPC channel.
|
|
60
|
+
2. Exchange messages over **Node IPC** (`process.send` / `message`).
|
|
61
|
+
3. **Register** the GUI with the central proxy ([mcp-http-gateway](https://github.com/Matthieu-Pesnot-Pin/mcp-http-gateway)) so it is reachable under a stable path.
|
|
62
|
+
4. **Log safely** — stderr and/or a log file; never rely on stdout for logs in the master process.
|
|
63
|
+
|
|
64
|
+
This library supports these patterns via `GuiLauncher`, `IpcHub`, `ProxyClient`, and `setupLogging` / `createLogger`.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## API reference
|
|
69
|
+
|
|
70
|
+
### `GuiLauncher`
|
|
71
|
+
|
|
72
|
+
Spawns and supervises a GUI child process (`fork` with `stdio: ['ignore', 'pipe', 'inherit', 'ipc']` — child stdout is piped to the parent’s stderr).
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { GuiLauncher } from "@imenam/mcp-gui-interface";
|
|
76
|
+
|
|
77
|
+
const launcher = new GuiLauncher({
|
|
78
|
+
guiPath: "./dist/gui-worker.js",
|
|
79
|
+
env: { ...process.env } as Record<string, string>,
|
|
80
|
+
maxRestarts: 0, // default: 0 — no auto-restart unless you opt in
|
|
81
|
+
restartDelay: 2000, // default: 2000 ms between restart attempts
|
|
82
|
+
onMessage: (msg) => {
|
|
83
|
+
console.error("Received:", msg);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
launcher.start();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**`GuiLauncherOptions`**
|
|
91
|
+
|
|
92
|
+
| Property | Type | Default | Description |
|
|
93
|
+
|----------------|------------------------------|---------|-------------|
|
|
94
|
+
| `guiPath` | `string` | — | Path to the GUI entry script (forked with Node) |
|
|
95
|
+
| `env` | `Record<string, string>` | — | Extra env vars merged with `process.env` |
|
|
96
|
+
| `maxRestarts` | `number` | **`0`** | Auto-restarts after unexpected exit (non-zero code) |
|
|
97
|
+
| `restartDelay` | `number` | `2000` | Ms to wait before each restart |
|
|
98
|
+
| `onMessage` | `(msg: IpcMessage) => void` | — | Incoming IPC from the child |
|
|
99
|
+
|
|
100
|
+
**Methods**
|
|
101
|
+
|
|
102
|
+
| Method | Returns | Description |
|
|
103
|
+
|------------------|----------------------|-------------|
|
|
104
|
+
| `start()` | `ChildProcess` | Spawns or respawns the GUI |
|
|
105
|
+
| `send(message)` | `boolean` | Sends IPC to the child (timestamp added internally) |
|
|
106
|
+
| `getIpcHub()` | `IpcHub` | Lower-level IPC helper |
|
|
107
|
+
| `getProcess()` | `ChildProcess \| null` | Current child, if any |
|
|
108
|
+
| `cleanup()` | `Promise<void>` | SIGTERM, then SIGKILL after 3s if needed |
|
|
109
|
+
|
|
110
|
+
The launcher registers handlers for `SIGINT`, `SIGTERM`, parent `exit`, and stdin `close` to tear down the child.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### `IpcHub`
|
|
115
|
+
|
|
116
|
+
Typed IPC between parent and child. Usually obtained via `launcher.getIpcHub()`.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const ipc = launcher.getIpcHub();
|
|
120
|
+
|
|
121
|
+
ipc.send({
|
|
122
|
+
type: "CONFIG_UPDATE",
|
|
123
|
+
data: { theme: "dark" },
|
|
124
|
+
timestamp: new Date().toISOString(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const response = await ipc.request({ type: "GET_STATUS" }, 3000);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**`IpcMessage`**
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
interface IpcMessage {
|
|
134
|
+
type: string;
|
|
135
|
+
correlationId?: string;
|
|
136
|
+
data?: any;
|
|
137
|
+
error?: string;
|
|
138
|
+
timestamp: string;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Methods**
|
|
143
|
+
|
|
144
|
+
| Method | Description |
|
|
145
|
+
|----------------------------|-------------|
|
|
146
|
+
| `send(message)` | Returns `false` if not connected |
|
|
147
|
+
| `onMessage(callback)` | Only delivers objects with a `type` field |
|
|
148
|
+
| `request(message, timeout?)` | Default timeout **2000 ms** |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### `ProxyClient`
|
|
153
|
+
|
|
154
|
+
Registers / unregisters the GUI with the proxy (`POST /proxy/register`, `DELETE /proxy/unregister`).
|
|
155
|
+
|
|
156
|
+
**Current API (source / recent releases):**
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { ProxyClient } from "@imenam/mcp-gui-interface";
|
|
160
|
+
|
|
161
|
+
const proxy = new ProxyClient(process.env.PROXY_URL!);
|
|
162
|
+
|
|
163
|
+
const result = await proxy.register({ path: "/my-app", name: "My App" });
|
|
164
|
+
|
|
165
|
+
if (result.success) {
|
|
166
|
+
console.error(`Listening on port ${result.port}`, result.url);
|
|
167
|
+
} else {
|
|
168
|
+
console.error(result.error, result.port); // port is 0 on failure
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
await proxy.unregister();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
- Registration HTTP timeout: **1000 ms** (unregister: **2000 ms**).
|
|
175
|
+
- On failure, `RegisterResult.port` is **`0`** in the current implementation.
|
|
176
|
+
|
|
177
|
+
> **Older npm versions** may have exposed `register(options, fallbackPort)`. Check `node_modules/@imenam/mcp-gui-interface/dist/src/proxy-client.d.ts` for the exact signature you have installed.
|
|
178
|
+
|
|
179
|
+
**`getStatus()`:** `"connected" | "fallback" | "error"` — many failure paths set `"error"`.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### `setupLogging` / `createLogger`
|
|
184
|
+
|
|
185
|
+
Safe logging for MCP servers: protect stdout, mirror errors to a file.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { setupLogging, createLogger } from "@imenam/mcp-gui-interface";
|
|
189
|
+
|
|
190
|
+
setupLogging({
|
|
191
|
+
processLabel: "MY-MCP",
|
|
192
|
+
logDir: "./logs", // optional; overrides MCP_LOG_DIR env var and .mcp-gui/logs default
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const logger = createLogger("MyModule");
|
|
196
|
+
logger.info("Server started");
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**`setupLogging`**
|
|
200
|
+
|
|
201
|
+
- Runs once per process (subsequent calls are no-ops).
|
|
202
|
+
- Creates `logDir`, appends to **`server.log`** (single file — **no built-in rotation**).
|
|
203
|
+
- Redirects `console.log` / `info` / `warn` → `console.error`.
|
|
204
|
+
- Patches `console.error` to also append to the log file.
|
|
205
|
+
|
|
206
|
+
**Log directory resolution order:**
|
|
207
|
+
|
|
208
|
+
1. `logDir` option passed to `setupLogging`
|
|
209
|
+
2. `MCP_LOG_DIR` environment variable
|
|
210
|
+
3. `.mcp-gui/logs` relative to `process.cwd()` (default)
|
|
211
|
+
|
|
212
|
+
**`createLogger(scope)`** — logs to stderr with `[LEVEL] [scope] …`.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Full-stack usage sketch
|
|
217
|
+
|
|
218
|
+
Typical split:
|
|
219
|
+
|
|
220
|
+
1. **Master:** `setupLogging`, optional `GuiLauncher` + `start()` when `PROXY_URL` (or your policy) allows.
|
|
221
|
+
2. **GUI worker:** `setupLogging`, `ProxyClient.register`, bind HTTP server to returned port, `unregister` on shutdown.
|
|
222
|
+
|
|
223
|
+
The master example in older docs that called `ProxyClient` in the same process as `GuiLauncher` is valid for simple layouts; **Hono-based** MCPs (e.g. MCP Documentor) often register the proxy **only** in the worker.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Exported types
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import type {
|
|
231
|
+
GuiLauncherOptions,
|
|
232
|
+
IpcMessage,
|
|
233
|
+
RegisterOptions,
|
|
234
|
+
RegisterResult,
|
|
235
|
+
ProxyConfig,
|
|
236
|
+
Logger,
|
|
237
|
+
SetupLoggingOptions,
|
|
238
|
+
} from "@imenam/mcp-gui-interface";
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Development
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
npm run build # tsc → dist/
|
|
247
|
+
npm run build:watch
|
|
248
|
+
npm run release # version patch, build, publish, push tags
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Documentation interne
|
|
254
|
+
|
|
255
|
+
Un **guide d’utilisation** (comment intégrer la lib dans un MCP + GUI) est maintenu dans **MCP Documentor**, projet **`mcp-gui-interface`**, entrée *Guide d’utilisation — @imenam/mcp-gui-interface* (dossier `guide`).
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
ISC © [Matthieu Pesnot-Pin](https://github.com/Matthieu-Pesnot-Pin)
|
package/dist/src/logger.js
CHANGED
|
@@ -40,7 +40,7 @@ export function setupLogging(options) {
|
|
|
40
40
|
if (loggingInitialized)
|
|
41
41
|
return;
|
|
42
42
|
currentProcessLabel = options.processLabel;
|
|
43
|
-
const resolvedLogDir = options.logDir ?? path.join(process.cwd(), ".mcp-gui", "logs");
|
|
43
|
+
const resolvedLogDir = options.logDir ?? process.env.MCP_LOG_DIR ?? path.join(process.cwd(), ".mcp-gui", "logs");
|
|
44
44
|
fs.mkdirSync(resolvedLogDir, { recursive: true });
|
|
45
45
|
const logFilePath = path.join(resolvedLogDir, "server.log");
|
|
46
46
|
logStream = fs.createWriteStream(logFilePath, { flags: "a", encoding: "utf8" });
|
package/package.json
CHANGED