@vdoninja/mcp 0.4.0 → 0.4.1
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 +661 -0
- package/LICENSE-SDK-EXCEPTION +54 -0
- package/README.md +324 -0
- package/SKILL.md +8 -6
- package/package.json +21 -7
- package/references/advanced-diagnostics.md +4 -4
- package/references/client-config-examples.md +251 -0
- package/references/mcp-tool-contract.md +5 -5
- package/references/quickstart-and-compat.md +15 -6
- package/references/vdo-ninja-p2p-data-notes.md +1 -1
- package/scripts/demo-file-workflow.js +22 -6
- package/scripts/demo-message-workflow.js +22 -6
- package/scripts/install-mcp.js +50 -2
- package/scripts/vdo-mcp-server.js +38 -2
- package/server.json +6 -8
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# VDO.Ninja MCP Client Config Examples
|
|
2
|
+
|
|
3
|
+
Ready-to-paste config for popular MCP clients.
|
|
4
|
+
|
|
5
|
+
## Prereqs (No Clone)
|
|
6
|
+
|
|
7
|
+
Install in the project/folder where you want to use the MCP:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i @vdoninja/mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Server script path used below:
|
|
14
|
+
|
|
15
|
+
- Linux/macOS: `./node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js`
|
|
16
|
+
- Windows: `.\node_modules\@vdoninja\mcp\scripts\vdo-mcp-server.js`
|
|
17
|
+
|
|
18
|
+
Use an absolute path if your client launches from another working directory.
|
|
19
|
+
|
|
20
|
+
## Onboarding Pattern (What Works Best)
|
|
21
|
+
|
|
22
|
+
Popular MCP servers with strong onboarding docs generally do these things:
|
|
23
|
+
|
|
24
|
+
1. Give one copy/paste install path.
|
|
25
|
+
2. Provide per-client config snippets.
|
|
26
|
+
3. Tell users the first tool call to run.
|
|
27
|
+
4. Show one deterministic "it works" workflow with expected output.
|
|
28
|
+
|
|
29
|
+
This file follows that same pattern.
|
|
30
|
+
|
|
31
|
+
## Generic `stdio` MCP Template
|
|
32
|
+
|
|
33
|
+
Most MCP clients need the same three things:
|
|
34
|
+
|
|
35
|
+
1. `command` (usually `node`)
|
|
36
|
+
2. `args` (script path + optional flags)
|
|
37
|
+
3. `env` (optional)
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"command": "node",
|
|
44
|
+
"args": [
|
|
45
|
+
"/ABSOLUTE/PATH/node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js",
|
|
46
|
+
"--tool-profile",
|
|
47
|
+
"core"
|
|
48
|
+
],
|
|
49
|
+
"env": {
|
|
50
|
+
"VDON_MCP_TOOL_PROFILE": "core"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Cursor
|
|
56
|
+
|
|
57
|
+
Cursor reads MCP config from:
|
|
58
|
+
|
|
59
|
+
- Global: `~/.cursor/mcp.json`
|
|
60
|
+
- Project: `.cursor/mcp.json`
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"mcpServers": {
|
|
67
|
+
"vdo-ninja-mcp": {
|
|
68
|
+
"command": "node",
|
|
69
|
+
"args": [
|
|
70
|
+
"/ABSOLUTE/PATH/node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js"
|
|
71
|
+
],
|
|
72
|
+
"env": {
|
|
73
|
+
"VDON_MCP_TOOL_PROFILE": "core"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Gemini CLI
|
|
81
|
+
|
|
82
|
+
Gemini CLI supports MCP via `mcpServers` in `settings.json`.
|
|
83
|
+
|
|
84
|
+
Common config file locations:
|
|
85
|
+
|
|
86
|
+
- Project: `.gemini/settings.json`
|
|
87
|
+
- Linux/macOS: `~/.gemini/settings.json`
|
|
88
|
+
- Windows: `%USERPROFILE%\\.gemini\\settings.json`
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"mcpServers": {
|
|
95
|
+
"vdo-ninja-mcp": {
|
|
96
|
+
"command": "node",
|
|
97
|
+
"args": [
|
|
98
|
+
"/ABSOLUTE/PATH/node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js"
|
|
99
|
+
],
|
|
100
|
+
"env": {
|
|
101
|
+
"VDON_MCP_TOOL_PROFILE": "core"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
After restarting Gemini CLI, use `/mcp` to confirm server status.
|
|
109
|
+
|
|
110
|
+
## OpenCode
|
|
111
|
+
|
|
112
|
+
OpenCode MCP config lives under `mcp` in OpenCode config.
|
|
113
|
+
|
|
114
|
+
Common config file locations:
|
|
115
|
+
|
|
116
|
+
- Global: `~/.config/opencode/opencode.json`
|
|
117
|
+
- Project: `opencode.json` (project root)
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"$schema": "https://opencode.ai/config.json",
|
|
124
|
+
"mcp": {
|
|
125
|
+
"vdo-ninja-mcp": {
|
|
126
|
+
"type": "local",
|
|
127
|
+
"command": [
|
|
128
|
+
"node",
|
|
129
|
+
"/ABSOLUTE/PATH/node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js",
|
|
130
|
+
"--tool-profile",
|
|
131
|
+
"core"
|
|
132
|
+
],
|
|
133
|
+
"enabled": true,
|
|
134
|
+
"environment": {
|
|
135
|
+
"VDON_MCP_TOOL_PROFILE": "core"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Codex CLI and Claude Code (CLI Automation)
|
|
143
|
+
|
|
144
|
+
Auto-register if either/both CLIs are installed:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npx vdon-mcp-install
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
If only one CLI is installed:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npx vdon-mcp-install --codex-only
|
|
154
|
+
npx vdon-mcp-install --claude-only
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Sanity Check (All Clients)
|
|
158
|
+
|
|
159
|
+
First tool call should be:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{ "name": "vdo_capabilities", "arguments": {} }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Then connect:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{ "name": "vdo_connect", "arguments": { "room": "lab_1" } }
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
If `vdo_capabilities` works, your MCP wiring is correct.
|
|
172
|
+
|
|
173
|
+
## Context Budget Note
|
|
174
|
+
|
|
175
|
+
Adding MCP servers increases tool/context footprint. Start with one MCP and least-privilege profile (`core`) first, then add more only as needed.
|
|
176
|
+
|
|
177
|
+
## First Functional Workflows (Clear Result)
|
|
178
|
+
|
|
179
|
+
Use unique room names to avoid collisions.
|
|
180
|
+
|
|
181
|
+
### Codex to Codex (Two Terminals)
|
|
182
|
+
|
|
183
|
+
1. Install/register once:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
npm i @vdoninja/mcp
|
|
187
|
+
npx vdon-mcp-install --codex-only
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
2. Open two Codex sessions (`A` and `B`) with MCP enabled.
|
|
191
|
+
3. In session `A`, call:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{ "name": "vdo_connect", "arguments": { "room": "codex_lab_001", "stream_id": "codex_a" } }
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
4. In session `B`, call:
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{ "name": "vdo_connect", "arguments": { "room": "codex_lab_001", "stream_id": "codex_b", "target_stream_id": "codex_a" } }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
5. In session `A`, send:
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"name": "vdo_send",
|
|
208
|
+
"arguments": {
|
|
209
|
+
"session_id": "<codex_a_session_id>",
|
|
210
|
+
"data": { "type": "chat.message", "text": "hello from codex_a" }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
6. In session `B`, receive:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"name": "vdo_receive",
|
|
220
|
+
"arguments": {
|
|
221
|
+
"session_id": "<codex_b_session_id>",
|
|
222
|
+
"wait_ms": 1000
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Expected result: a `data_received` event containing `"hello from codex_a"`.
|
|
228
|
+
|
|
229
|
+
### Claude to Claude (Two Terminals)
|
|
230
|
+
|
|
231
|
+
1. Install/register once:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
npm i @vdoninja/mcp
|
|
235
|
+
npx vdon-mcp-install --claude-only
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
2. Repeat the same tool flow as above with a new room name, e.g. `claude_lab_001`.
|
|
239
|
+
|
|
240
|
+
Expected result: the second Claude session receives the message in `vdo_receive` with `event.type = "data_received"`.
|
|
241
|
+
|
|
242
|
+
### Fast Scripted Proof (No Manual Tool Calls)
|
|
243
|
+
|
|
244
|
+
If you just want a quick pass/fail:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
npx vdon-mcp-demo-message
|
|
248
|
+
npx vdon-mcp-demo-file
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Expected result: each script exits successfully and reports message/file workflow completion.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Implemented by:
|
|
4
4
|
|
|
5
|
-
- `
|
|
6
|
-
- Installer automation: `
|
|
7
|
-
- Discovery manifest: `
|
|
5
|
+
- `scripts/vdo-mcp-server.js`
|
|
6
|
+
- Installer automation: `scripts/install-mcp.js`
|
|
7
|
+
- Discovery manifest: `server.json`
|
|
8
8
|
|
|
9
9
|
## Transport
|
|
10
10
|
|
|
@@ -23,7 +23,7 @@ Implemented by:
|
|
|
23
23
|
|
|
24
24
|
## Server CLI Flags
|
|
25
25
|
|
|
26
|
-
Supported by `
|
|
26
|
+
Supported by `scripts/vdo-mcp-server.js`:
|
|
27
27
|
|
|
28
28
|
- `--tool-profile <core|file|state|full>`
|
|
29
29
|
- `--max-message-bytes <bytes>`
|
|
@@ -140,7 +140,7 @@ Tools return `isError: true` with:
|
|
|
140
140
|
Automated registration:
|
|
141
141
|
|
|
142
142
|
```bash
|
|
143
|
-
npm i @vdoninja/mcp
|
|
143
|
+
npm i @vdoninja/mcp
|
|
144
144
|
npx vdon-mcp-install
|
|
145
145
|
```
|
|
146
146
|
|
|
@@ -13,7 +13,7 @@ This is the fast path for users and AI agents.
|
|
|
13
13
|
Install locally (no clone), including a Node WebRTC implementation:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npm i @vdoninja/mcp
|
|
16
|
+
npm i @vdoninja/mcp
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
Register MCP for Codex/Claude:
|
|
@@ -28,6 +28,9 @@ Start the local MCP server:
|
|
|
28
28
|
npx vdon-mcp-server
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
If started directly in a terminal, it prints a short `Server running (stdio)` banner and a first-call hint.
|
|
32
|
+
If started by an MCP client, no interactive banner may appear, which is normal.
|
|
33
|
+
|
|
31
34
|
Optional least-privilege profile:
|
|
32
35
|
|
|
33
36
|
```bash
|
|
@@ -38,7 +41,7 @@ Repo-local equivalents still work when developing in this repo:
|
|
|
38
41
|
|
|
39
42
|
```bash
|
|
40
43
|
npm run mcp:install
|
|
41
|
-
node
|
|
44
|
+
node scripts/vdo-mcp-server.js
|
|
42
45
|
```
|
|
43
46
|
|
|
44
47
|
AI-first sanity check (recommended first call):
|
|
@@ -186,13 +189,19 @@ npm run test:mcp:all
|
|
|
186
189
|
|
|
187
190
|
## Compatibility Notes
|
|
188
191
|
|
|
189
|
-
- Codex
|
|
190
|
-
-
|
|
191
|
-
-
|
|
192
|
+
- Works with any MCP client that supports local `stdio` transport (Codex CLI, Claude Code, Cursor, and other MCP-compatible CLIs).
|
|
193
|
+
- Codex and Claude can be auto-registered via `npx vdon-mcp-install`.
|
|
194
|
+
- Cursor and other GUI tools typically need manual MCP JSON config using:
|
|
195
|
+
- `command`: your Node executable
|
|
196
|
+
- `args`: path to `.../node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js` plus optional flags
|
|
197
|
+
- Ready-to-paste per-client examples are in `references/client-config-examples.md`.
|
|
198
|
+
- Registry metadata is in `server.json`.
|
|
199
|
+
- Tool contracts are in `references/mcp-tool-contract.md`.
|
|
192
200
|
- `@vdoninja/mcp` depends on `@vdoninja/sdk`, so SDK is installed transitively.
|
|
201
|
+
- License model matches SDK packaging: AGPL-3.0-only with an additional unmodified-distribution exception (`LICENSE-SDK-EXCEPTION`).
|
|
193
202
|
|
|
194
203
|
## Optional Advanced Diagnostics
|
|
195
204
|
|
|
196
205
|
For soak/matrix/preset-matrix/sweep tuning and all reliability knobs, use:
|
|
197
206
|
|
|
198
|
-
- `
|
|
207
|
+
- `references/advanced-diagnostics.md`
|
|
@@ -71,7 +71,7 @@ This file captures operational constraints from this repository's SDK docs.
|
|
|
71
71
|
|
|
72
72
|
## Bridge Runtime Knobs
|
|
73
73
|
|
|
74
|
-
`
|
|
74
|
+
`scripts/vdo-data-bridge.js` supports:
|
|
75
75
|
|
|
76
76
|
- `VDON_ROOM` (required in real deployments)
|
|
77
77
|
- `VDON_STREAM_ID` (optional; random if omitted)
|
|
@@ -90,10 +90,22 @@ async function main() {
|
|
|
90
90
|
serverA.callTool('vdo_status', { session_id: sessionA }),
|
|
91
91
|
serverB.callTool('vdo_status', { session_id: sessionB })
|
|
92
92
|
]);
|
|
93
|
-
return
|
|
93
|
+
return (
|
|
94
|
+
statusA.connected &&
|
|
95
|
+
statusB.connected &&
|
|
96
|
+
statusA.peer_count > 0 &&
|
|
97
|
+
statusB.peer_count > 0
|
|
98
|
+
);
|
|
94
99
|
}, connectTimeoutMs, 200);
|
|
95
100
|
if (!connected) {
|
|
96
|
-
|
|
101
|
+
const [statusA, statusB] = await Promise.all([
|
|
102
|
+
serverA.callTool('vdo_status', { session_id: sessionA }),
|
|
103
|
+
serverB.callTool('vdo_status', { session_id: sessionB })
|
|
104
|
+
]);
|
|
105
|
+
throw new Error(
|
|
106
|
+
`demo-file connect timeout (peer_count a=${statusA.peer_count} b=${statusB.peer_count} ` +
|
|
107
|
+
`connected a=${statusA.connected} b=${statusB.connected})`
|
|
108
|
+
);
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
const payload = Buffer.from(fileText, 'utf8').toString('base64');
|
|
@@ -162,7 +174,11 @@ async function main() {
|
|
|
162
174
|
}
|
|
163
175
|
}
|
|
164
176
|
|
|
165
|
-
main()
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
})
|
|
177
|
+
main()
|
|
178
|
+
.then(() => {
|
|
179
|
+
process.exit(0);
|
|
180
|
+
})
|
|
181
|
+
.catch((error) => {
|
|
182
|
+
console.error(error.message);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
});
|
|
@@ -94,10 +94,22 @@ async function main() {
|
|
|
94
94
|
serverA.callTool('vdo_status', { session_id: sessionA }),
|
|
95
95
|
serverB.callTool('vdo_status', { session_id: sessionB })
|
|
96
96
|
]);
|
|
97
|
-
return
|
|
97
|
+
return (
|
|
98
|
+
statusA.connected &&
|
|
99
|
+
statusB.connected &&
|
|
100
|
+
statusA.peer_count > 0 &&
|
|
101
|
+
statusB.peer_count > 0
|
|
102
|
+
);
|
|
98
103
|
}, connectTimeoutMs, 200);
|
|
99
104
|
if (!connected) {
|
|
100
|
-
|
|
105
|
+
const [statusA, statusB] = await Promise.all([
|
|
106
|
+
serverA.callTool('vdo_status', { session_id: sessionA }),
|
|
107
|
+
serverB.callTool('vdo_status', { session_id: sessionB })
|
|
108
|
+
]);
|
|
109
|
+
throw new Error(
|
|
110
|
+
`demo-message connect timeout (peer_count a=${statusA.peer_count} b=${statusB.peer_count} ` +
|
|
111
|
+
`connected a=${statusA.connected} b=${statusB.connected})`
|
|
112
|
+
);
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
const id = `demo_msg_${Date.now()}`;
|
|
@@ -161,7 +173,11 @@ async function main() {
|
|
|
161
173
|
}
|
|
162
174
|
}
|
|
163
175
|
|
|
164
|
-
main()
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
})
|
|
176
|
+
main()
|
|
177
|
+
.then(() => {
|
|
178
|
+
process.exit(0);
|
|
179
|
+
})
|
|
180
|
+
.catch((error) => {
|
|
181
|
+
console.error(error.message);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
});
|
package/scripts/install-mcp.js
CHANGED
|
@@ -69,6 +69,7 @@ function parseArgs(argv) {
|
|
|
69
69
|
serverArgs: [],
|
|
70
70
|
codex: true,
|
|
71
71
|
claude: true,
|
|
72
|
+
targetSelectionExplicit: false,
|
|
72
73
|
claudeScope: 'local',
|
|
73
74
|
dryRun: false
|
|
74
75
|
};
|
|
@@ -106,11 +107,13 @@ function parseArgs(argv) {
|
|
|
106
107
|
if (arg === '--codex-only') {
|
|
107
108
|
out.claude = false;
|
|
108
109
|
out.codex = true;
|
|
110
|
+
out.targetSelectionExplicit = true;
|
|
109
111
|
continue;
|
|
110
112
|
}
|
|
111
113
|
if (arg === '--claude-only') {
|
|
112
114
|
out.codex = false;
|
|
113
115
|
out.claude = true;
|
|
116
|
+
out.targetSelectionExplicit = true;
|
|
114
117
|
continue;
|
|
115
118
|
}
|
|
116
119
|
if (arg === '--claude-scope') {
|
|
@@ -181,6 +184,16 @@ function assertBinaryExists(name, options) {
|
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
|
|
187
|
+
function binaryExists(name, options) {
|
|
188
|
+
const check = runCommand(name, ['--version'], {
|
|
189
|
+
dryRun: options.dryRun,
|
|
190
|
+
quiet: true,
|
|
191
|
+
env: options.env,
|
|
192
|
+
cwd: options.cwd
|
|
193
|
+
});
|
|
194
|
+
return check.ok;
|
|
195
|
+
}
|
|
196
|
+
|
|
184
197
|
function codexServerExists(name, options) {
|
|
185
198
|
if (options.dryRun) return false;
|
|
186
199
|
const result = runCommand('codex', ['mcp', 'get', name, '--json'], {
|
|
@@ -278,8 +291,33 @@ function main() {
|
|
|
278
291
|
process.stdout.write('Warning: secure preset selected but VDON_MCP_JOIN_TOKEN_SECRET is not set in this shell.\n');
|
|
279
292
|
}
|
|
280
293
|
|
|
281
|
-
if (config.codex
|
|
282
|
-
|
|
294
|
+
if (config.codex && !binaryExists('codex', options)) {
|
|
295
|
+
if (config.targetSelectionExplicit && !config.claude) {
|
|
296
|
+
assertBinaryExists('codex', options);
|
|
297
|
+
}
|
|
298
|
+
process.stdout.write('Warning: Codex CLI not found in PATH. Skipping Codex install target.\n');
|
|
299
|
+
process.stdout.write('Tip: install Codex CLI, or rerun with --claude-only.\n');
|
|
300
|
+
config.codex = false;
|
|
301
|
+
}
|
|
302
|
+
if (config.claude && !binaryExists('claude', options)) {
|
|
303
|
+
if (config.targetSelectionExplicit && !config.codex) {
|
|
304
|
+
assertBinaryExists('claude', options);
|
|
305
|
+
}
|
|
306
|
+
process.stdout.write('Warning: Claude Code CLI not found in PATH. Skipping Claude install target.\n');
|
|
307
|
+
process.stdout.write('Tip: install Claude Code CLI, or rerun with --codex-only.\n');
|
|
308
|
+
config.claude = false;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!config.codex && !config.claude) {
|
|
312
|
+
if (config.action === 'uninstall') {
|
|
313
|
+
process.stdout.write('No supported CLI targets found in PATH; nothing to uninstall.\n\nDone.\n');
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
throw new Error(
|
|
317
|
+
'No supported CLI targets found in PATH. Install Codex CLI or Claude Code CLI, ' +
|
|
318
|
+
'or configure GUI clients manually (e.g. Cursor via mcp.json).'
|
|
319
|
+
);
|
|
320
|
+
}
|
|
283
321
|
|
|
284
322
|
if (config.action === 'install') {
|
|
285
323
|
if (config.codex) installCodex(config, options);
|
|
@@ -290,6 +328,16 @@ function main() {
|
|
|
290
328
|
}
|
|
291
329
|
|
|
292
330
|
process.stdout.write('\nDone.\n');
|
|
331
|
+
if (config.action === 'install') {
|
|
332
|
+
process.stdout.write('\nUsage quickstart:\n');
|
|
333
|
+
process.stdout.write(' 1) Start server: npx vdon-mcp-server\n');
|
|
334
|
+
process.stdout.write(' 2) In your MCP client, call: {"name":"vdo_capabilities","arguments":{}}\n');
|
|
335
|
+
process.stdout.write(' 3) Generic stdio MCP config:\n');
|
|
336
|
+
process.stdout.write(` command: ${config.nodeCmd}\n`);
|
|
337
|
+
process.stdout.write(` args: [${JSON.stringify(config.serverScript)}${config.serverArgs.map((arg) => `, ${JSON.stringify(arg)}`).join('')}]\n`);
|
|
338
|
+
process.stdout.write(' 4) Client-specific examples (Cursor/Gemini/OpenCode):\n');
|
|
339
|
+
process.stdout.write(' https://github.com/steveseguin/ninjamcp/blob/main/references/client-config-examples.md\n');
|
|
340
|
+
}
|
|
293
341
|
}
|
|
294
342
|
|
|
295
343
|
if (require.main === module) {
|
|
@@ -24,7 +24,7 @@ const {
|
|
|
24
24
|
const SUPPORTED_PROTOCOL_VERSIONS = ['2025-06-18', '2024-11-05'];
|
|
25
25
|
const SERVER_INFO = {
|
|
26
26
|
name: 'vdo-ninja-mcp',
|
|
27
|
-
version: '0.4.
|
|
27
|
+
version: '0.4.1'
|
|
28
28
|
};
|
|
29
29
|
const DEFAULT_MAX_MESSAGE_BYTES = 1024 * 1024;
|
|
30
30
|
|
|
@@ -500,6 +500,38 @@ Options:
|
|
|
500
500
|
`.trim();
|
|
501
501
|
}
|
|
502
502
|
|
|
503
|
+
function isInteractiveTerminal(stream) {
|
|
504
|
+
return !!(stream && stream.isTTY);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function buildStartupBanner(server) {
|
|
508
|
+
const profile = server && server.toolProfile && server.toolProfile.name
|
|
509
|
+
? server.toolProfile.name
|
|
510
|
+
: DEFAULT_TOOL_PROFILE;
|
|
511
|
+
return [
|
|
512
|
+
'[vdon-mcp] Server running (stdio) and waiting for MCP client input.',
|
|
513
|
+
`[vdon-mcp] Active tool profile: ${profile}`,
|
|
514
|
+
'[vdon-mcp] First tool call: {"name":"vdo_capabilities","arguments":{}}',
|
|
515
|
+
'[vdon-mcp] Stop with Ctrl+C'
|
|
516
|
+
];
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function maybeWriteStartupBanner(options, server) {
|
|
520
|
+
const env = options.env || process.env;
|
|
521
|
+
const disabled = toBool(env.VDON_MCP_NO_STARTUP_BANNER, false);
|
|
522
|
+
if (disabled) return false;
|
|
523
|
+
|
|
524
|
+
const input = options.input || process.stdin;
|
|
525
|
+
const output = options.output || process.stdout;
|
|
526
|
+
const errorOutput = options.errorOutput || process.stderr;
|
|
527
|
+
|
|
528
|
+
const interactive = isInteractiveTerminal(input) && isInteractiveTerminal(output) && isInteractiveTerminal(errorOutput);
|
|
529
|
+
if (!interactive) return false;
|
|
530
|
+
|
|
531
|
+
errorOutput.write(`${buildStartupBanner(server).join('\n')}\n`);
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
|
|
503
535
|
class VdoMcpServer {
|
|
504
536
|
constructor(options = {}) {
|
|
505
537
|
this.protocolVersion = SUPPORTED_PROTOCOL_VERSIONS[0];
|
|
@@ -1328,6 +1360,8 @@ async function runMcpStdioLoop(options = {}) {
|
|
|
1328
1360
|
onExit
|
|
1329
1361
|
});
|
|
1330
1362
|
|
|
1363
|
+
maybeWriteStartupBanner(options, server);
|
|
1364
|
+
|
|
1331
1365
|
const parser = new McpStdioParser({ maxMessageBytes });
|
|
1332
1366
|
let chain = Promise.resolve();
|
|
1333
1367
|
let closed = false;
|
|
@@ -1421,5 +1455,7 @@ module.exports = {
|
|
|
1421
1455
|
buildTools,
|
|
1422
1456
|
makeToolResult,
|
|
1423
1457
|
classifyToolError,
|
|
1424
|
-
parseCliArgs
|
|
1458
|
+
parseCliArgs,
|
|
1459
|
+
buildStartupBanner,
|
|
1460
|
+
maybeWriteStartupBanner
|
|
1425
1461
|
};
|
package/server.json
CHANGED
|
@@ -3,19 +3,17 @@
|
|
|
3
3
|
"name": "io.github.steveseguin/vdo-ninja-mcp",
|
|
4
4
|
"title": "VDO.Ninja MCP Bridge",
|
|
5
5
|
"description": "WebRTC data-channel MCP bridge for VDO.Ninja with reliable messages, files, and room sync.",
|
|
6
|
-
"version": "0.4.
|
|
7
|
-
"websiteUrl": "https://github.com/steveseguin/
|
|
6
|
+
"version": "0.4.1",
|
|
7
|
+
"websiteUrl": "https://github.com/steveseguin/ninjamcp",
|
|
8
8
|
"repository": {
|
|
9
|
-
"url": "https://github.com/steveseguin/
|
|
10
|
-
"source": "github"
|
|
11
|
-
"id": "1006762093",
|
|
12
|
-
"subfolder": "MCP"
|
|
9
|
+
"url": "https://github.com/steveseguin/ninjamcp",
|
|
10
|
+
"source": "github"
|
|
13
11
|
},
|
|
14
12
|
"packages": [
|
|
15
13
|
{
|
|
16
14
|
"registryType": "npm",
|
|
17
15
|
"identifier": "@vdoninja/mcp",
|
|
18
|
-
"version": "0.4.
|
|
16
|
+
"version": "0.4.1",
|
|
19
17
|
"runtimeHint": "node",
|
|
20
18
|
"runtimeArguments": [
|
|
21
19
|
{
|
|
@@ -81,7 +79,7 @@
|
|
|
81
79
|
"url": "http://127.0.0.1:{http_port}/mcp",
|
|
82
80
|
"variables": {
|
|
83
81
|
"http_port": {
|
|
84
|
-
"description": "Port used by
|
|
82
|
+
"description": "Port used by scripts/vdo-mcp-streamable-http.js.",
|
|
85
83
|
"format": "number",
|
|
86
84
|
"default": "8787",
|
|
87
85
|
"isRequired": false
|