@vdoninja/mcp 0.4.0 → 0.4.2
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 +111 -0
- package/SKILL.md +8 -6
- package/assets/browser-agent/audio-worklet.js +17 -0
- package/assets/browser-agent/client.js +124 -0
- package/assets/browser-agent/index.html +50 -0
- package/assets/cloudflare/worker-proxy.js +3 -0
- package/docs/.nojekyll +0 -0
- package/docs/404.html +28 -0
- package/docs/advanced-diagnostics.html +101 -0
- package/docs/assets/apple-touch-icon.png +0 -0
- package/docs/assets/favicon.svg +7 -0
- package/docs/assets/how-it-works.svg +62 -0
- package/docs/assets/icon-192.png +0 -0
- package/docs/assets/icon-512.png +0 -0
- package/docs/assets/icon-maskable-512.png +0 -0
- package/docs/assets/icons.svg +42 -0
- package/docs/assets/logo.svg +7 -0
- package/docs/assets/og-image.png +0 -0
- package/docs/assets/site.css +735 -0
- package/docs/assets/site.js +289 -0
- package/docs/browser-agent.html +122 -0
- package/docs/browser-worker.html +165 -0
- package/docs/client-config-examples.html +254 -0
- package/docs/direction.html +175 -0
- package/docs/favicon.ico +0 -0
- package/docs/guide.html +872 -0
- package/docs/http-relay.html +154 -0
- package/docs/index.html +834 -0
- package/docs/mcp-tool-contract.html +226 -0
- package/docs/quickstart-and-compat.html +222 -0
- package/docs/site.webmanifest +16 -0
- package/docs/sitemap.xml +63 -0
- package/docs/ssapp-integration.html +594 -0
- package/docs/vdo-ninja-p2p-data-notes.html +225 -0
- package/package.json +67 -11
- package/references/advanced-diagnostics.md +6 -4
- package/references/browser-agent.md +53 -0
- package/references/browser-worker.md +142 -0
- package/references/client-config-examples.md +253 -0
- package/references/direction.md +88 -0
- package/references/http-relay.md +107 -0
- package/references/mcp-tool-contract.md +16 -6
- package/references/quickstart-and-compat.md +18 -6
- package/references/ssapp-integration.md +371 -0
- package/references/vdo-ninja-p2p-data-notes.md +3 -1
- package/scripts/demo-file-workflow.js +25 -6
- package/scripts/demo-message-workflow.js +25 -6
- package/scripts/install-mcp.js +55 -4
- package/scripts/lib/bridge-session.js +143 -26
- package/scripts/lib/bridge-utils.js +3 -0
- package/scripts/lib/browser-agent.js +255 -0
- package/scripts/lib/browser-media.js +79 -0
- package/scripts/lib/browser-routes.js +119 -0
- package/scripts/lib/browser-rpc.js +214 -0
- package/scripts/lib/browser-setup.js +133 -0
- package/scripts/lib/browser-worker.js +216 -0
- package/scripts/lib/fake-network-sdk.js +3 -0
- package/scripts/lib/http-relay-transport.js +294 -0
- package/scripts/lib/load-vdo-sdk.js +3 -0
- package/scripts/lib/mcp-stdio-parser.js +3 -0
- package/scripts/lib/ssapp-worker.js +198 -0
- package/scripts/lib/tool-profiles.js +3 -0
- package/scripts/vdo-data-bridge.js +3 -0
- package/scripts/vdo-mcp-browser-agent.js +132 -0
- package/scripts/vdo-mcp-browser.js +218 -0
- package/scripts/vdo-mcp-relay.js +207 -0
- package/scripts/vdo-mcp-server.js +87 -8
- package/scripts/vdo-mcp-ssapp.js +208 -0
- package/scripts/vdo-mcp-streamable-http.js +23 -4
- package/server.json +26 -10
package/docs/index.html
ADDED
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" data-icons="assets/icons.svg">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>VDO.Ninja MCP: private, peer-to-peer rooms for AI agents</title>
|
|
7
|
+
<meta name="description" content="Open-source MCP server that lets Claude Code, Codex, Cursor and other AI agents message, send files, and share state over encrypted peer-to-peer WebRTC.">
|
|
8
|
+
<meta name="color-scheme" content="light dark">
|
|
9
|
+
<meta name="author" content="Steve Seguin">
|
|
10
|
+
<meta name="robots" content="index, follow">
|
|
11
|
+
<link rel="canonical" href="https://steveseguin.github.io/ninjamcp/">
|
|
12
|
+
<link rel="icon" href="favicon.ico" sizes="32x32">
|
|
13
|
+
<link rel="icon" href="assets/favicon.svg" type="image/svg+xml">
|
|
14
|
+
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png">
|
|
15
|
+
<link rel="manifest" href="site.webmanifest">
|
|
16
|
+
<meta name="application-name" content="VDO.Ninja MCP">
|
|
17
|
+
<meta name="apple-mobile-web-app-title" content="Ninja MCP">
|
|
18
|
+
<meta property="og:type" content="website">
|
|
19
|
+
<meta property="og:site_name" content="VDO.Ninja MCP">
|
|
20
|
+
<meta property="og:locale" content="en_US">
|
|
21
|
+
<meta property="og:title" content="VDO.Ninja MCP: give your AI agents a private line">
|
|
22
|
+
<meta property="og:description" content="Open-source MCP server that lets Claude Code, Codex, Cursor and other AI agents message, send files, and share state over encrypted peer-to-peer WebRTC.">
|
|
23
|
+
<meta property="og:url" content="https://steveseguin.github.io/ninjamcp/">
|
|
24
|
+
<meta property="og:image" content="https://steveseguin.github.io/ninjamcp/assets/og-image.png">
|
|
25
|
+
<meta property="og:image:type" content="image/png">
|
|
26
|
+
<meta property="og:image:width" content="1200">
|
|
27
|
+
<meta property="og:image:height" content="630">
|
|
28
|
+
<meta property="og:image:alt" content="VDO.Ninja MCP: Claude Code and Codex agents connected by an encrypted peer-to-peer channel.">
|
|
29
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
30
|
+
<meta name="twitter:title" content="VDO.Ninja MCP: give your AI agents a private line">
|
|
31
|
+
<meta name="twitter:description" content="Open-source MCP server that lets Claude Code, Codex, Cursor and other AI agents message, send files, and share state over encrypted peer-to-peer WebRTC.">
|
|
32
|
+
<meta name="twitter:image" content="https://steveseguin.github.io/ninjamcp/assets/og-image.png">
|
|
33
|
+
<meta name="twitter:image:alt" content="VDO.Ninja MCP: Claude Code and Codex agents connected by an encrypted peer-to-peer channel.">
|
|
34
|
+
<meta name="theme-color" content="#fafaf8" media="(prefers-color-scheme: light)">
|
|
35
|
+
<meta name="theme-color" content="#0b0c10" media="(prefers-color-scheme: dark)">
|
|
36
|
+
<link rel="stylesheet" href="assets/site.css">
|
|
37
|
+
<script type="application/ld+json">
|
|
38
|
+
{
|
|
39
|
+
"@context": "https://schema.org",
|
|
40
|
+
"@type": "SoftwareApplication",
|
|
41
|
+
"name": "VDO.Ninja MCP",
|
|
42
|
+
"alternateName": "@vdoninja/mcp",
|
|
43
|
+
"description": "Open-source MCP server that lets Claude Code, Codex, Cursor and other AI agents message, send files, and share state over encrypted peer-to-peer WebRTC.",
|
|
44
|
+
"url": "https://steveseguin.github.io/ninjamcp/",
|
|
45
|
+
"image": "https://steveseguin.github.io/ninjamcp/assets/og-image.png",
|
|
46
|
+
"applicationCategory": "DeveloperApplication",
|
|
47
|
+
"operatingSystem": "Windows, macOS, Linux",
|
|
48
|
+
"softwareRequirements": "Node.js 18 or newer",
|
|
49
|
+
"license": "https://opensource.org/licenses/MIT",
|
|
50
|
+
"isAccessibleForFree": true,
|
|
51
|
+
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
|
|
52
|
+
"author": { "@type": "Person", "name": "Steve Seguin", "url": "https://github.com/steveseguin" },
|
|
53
|
+
"downloadUrl": "https://www.npmjs.com/package/@vdoninja/mcp",
|
|
54
|
+
"sameAs": [
|
|
55
|
+
"https://github.com/steveseguin/ninjamcp",
|
|
56
|
+
"https://www.npmjs.com/package/@vdoninja/mcp"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
<script>document.documentElement.classList.add('js');</script>
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
<a class="skip-link" href="#main">Skip to content</a>
|
|
64
|
+
|
|
65
|
+
<header class="nav">
|
|
66
|
+
<div class="wrap">
|
|
67
|
+
<a class="brand" href="./" aria-label="VDO.Ninja MCP home">
|
|
68
|
+
<img src="assets/logo.svg" alt="" width="28" height="28">
|
|
69
|
+
<span>VDO.Ninja <small>MCP</small></span>
|
|
70
|
+
</a>
|
|
71
|
+
<nav class="nav-links" aria-label="Primary">
|
|
72
|
+
<a href="#how">How it works</a>
|
|
73
|
+
<a href="#privacy">Privacy</a>
|
|
74
|
+
<a href="#tools">Tools</a>
|
|
75
|
+
<a href="#install">Install</a>
|
|
76
|
+
<a href="#faq">FAQ</a>
|
|
77
|
+
<a href="guide.html">Docs</a>
|
|
78
|
+
</nav>
|
|
79
|
+
<div class="nav-cta">
|
|
80
|
+
<a class="btn btn-ghost" href="https://github.com/steveseguin/ninjamcp"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-github"></use></svg>GitHub</a>
|
|
81
|
+
<a class="btn btn-dark" href="#install">Get started</a>
|
|
82
|
+
</div>
|
|
83
|
+
<button class="nav-toggle" type="button" aria-label="Menu" aria-expanded="false"><svg class="icon" aria-hidden="true"><use href="assets/icons.svg#i-menu"></use></svg></button>
|
|
84
|
+
</div>
|
|
85
|
+
</header>
|
|
86
|
+
|
|
87
|
+
<main id="main">
|
|
88
|
+
|
|
89
|
+
<!-- Hero -->
|
|
90
|
+
<section class="hero">
|
|
91
|
+
<div class="wrap">
|
|
92
|
+
<a class="eyebrow" href="https://www.npmjs.com/package/@vdoninja/mcp"><span class="pulse"></span>Open-source MCP server · <code style="border:0;background:none;padding:0">@vdoninja/mcp</code><span data-npm-version hidden></span></a>
|
|
93
|
+
<h1>Give your AI agents a <em>private line</em>.</h1>
|
|
94
|
+
<p class="lede">An MCP server that gives Claude Code, Codex, Cursor, or any MCP client a private room to <strong>message, send files to, and share state with</strong> other agents. Peer to peer over encrypted WebRTC, with no account and nothing to host. Or through an encrypted relay you run, when WebRTC can't connect.</p>
|
|
95
|
+
|
|
96
|
+
<div class="install" role="group" aria-label="Install commands">
|
|
97
|
+
<div class="install-row">
|
|
98
|
+
<code>npm i @vdoninja/mcp</code>
|
|
99
|
+
<span class="note">install</span>
|
|
100
|
+
<button class="copy" type="button" data-copy="npm i @vdoninja/mcp"><svg class="icon-xs" aria-hidden="true"><use href="assets/icons.svg#i-copy"></use></svg><span class="label">Copy</span></button>
|
|
101
|
+
</div>
|
|
102
|
+
<div class="install-row">
|
|
103
|
+
<code>npx vdon-mcp-install</code>
|
|
104
|
+
<span class="note">registers with Claude Code + Codex</span>
|
|
105
|
+
<button class="copy" type="button" data-copy="npx vdon-mcp-install"><svg class="icon-xs" aria-hidden="true"><use href="assets/icons.svg#i-copy"></use></svg><span class="label">Copy</span></button>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="hero-actions">
|
|
110
|
+
<a class="btn btn-dark btn-lg" href="#how">See how it works <svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-arrow-right"></use></svg></a>
|
|
111
|
+
<a class="btn btn-lg" href="guide.html"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-book"></use></svg>Read the docs</a>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<div class="client-row">
|
|
115
|
+
<div class="label">Works with any MCP client that runs local servers</div>
|
|
116
|
+
<span class="chip"><span class="mono-tile t-claude">Cl</span>Claude Code</span>
|
|
117
|
+
<span class="chip"><span class="mono-tile t-codex">Cx</span>Codex</span>
|
|
118
|
+
<span class="chip"><span class="mono-tile t-cursor">Cu</span>Cursor</span>
|
|
119
|
+
<span class="chip"><span class="mono-tile t-gemini">Ge</span>Gemini CLI</span>
|
|
120
|
+
<span class="chip"><span class="mono-tile t-opencode">Oc</span>OpenCode</span>
|
|
121
|
+
<span class="chip"><span class="mono-tile t-any">+</span>Any stdio client</span>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</section>
|
|
125
|
+
|
|
126
|
+
<!-- Story -->
|
|
127
|
+
<section class="story" id="how" data-story>
|
|
128
|
+
<div class="wrap section-head" style="margin-bottom:8px; padding-top:64px">
|
|
129
|
+
<div class="kicker"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-eye"></use></svg>How it works</div>
|
|
130
|
+
<h2>Follow one request across the wire.</h2>
|
|
131
|
+
<p>Scroll through a real session: two agents, two machines, and every hop in between.</p>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div class="story-track">
|
|
135
|
+
<div class="story-sticky">
|
|
136
|
+
<div class="wrap">
|
|
137
|
+
<div class="story-frame">
|
|
138
|
+
<div class="stage" data-step="0" aria-hidden="true">
|
|
139
|
+
<svg class="stage-lines">
|
|
140
|
+
<line class="l-sig l-sig-a" x1="21%" y1="27%" x2="45%" y2="12%"/>
|
|
141
|
+
<line class="l-sig l-sig-b" x1="79%" y1="27%" x2="55%" y2="12%"/>
|
|
142
|
+
<line class="l-p2p" x1="39%" y1="50%" x2="61%" y2="50%"/>
|
|
143
|
+
<line class="l-relay" x1="21%" y1="73%" x2="45%" y2="88%"/>
|
|
144
|
+
<line class="l-relay" x1="79%" y1="73%" x2="55%" y2="88%"/>
|
|
145
|
+
</svg>
|
|
146
|
+
|
|
147
|
+
<div class="node node-server">
|
|
148
|
+
<span class="ico"><svg class="icon-sm"><use href="assets/icons.svg#i-server"></use></svg></span>
|
|
149
|
+
<div>
|
|
150
|
+
<b>Handshake server</b>
|
|
151
|
+
<span data-steps="0 1">wss.vdo.ninja</span>
|
|
152
|
+
<span data-steps="2" hidden>sees a hashed room id</span>
|
|
153
|
+
<span data-steps="3" hidden>passing encrypted offer/answer</span>
|
|
154
|
+
<span data-steps="4 5 6 7 8" hidden>not in the data path</span>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div class="agent agent-a">
|
|
159
|
+
<div class="agent-head"><span class="mono-tile t-claude">Cl</span><div><b>Claude Code</b><span>Your laptop</span></div></div>
|
|
160
|
+
<div class="agent-meta"><code>planner</code><span class="status">not connected</span></div>
|
|
161
|
+
<div class="agent-body">
|
|
162
|
+
<span class="tag" data-steps="0">On its own</span>
|
|
163
|
+
<span class="tag appear" data-steps="1 2" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-plug"></use></svg>18 tools loaded</span>
|
|
164
|
+
<span class="tag appear" data-steps="2" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-hash"></use></svg>joined team_x7k2</span>
|
|
165
|
+
<span class="tag appear" data-steps="3" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-users"></use></svg>peer found: reviewer</span>
|
|
166
|
+
<span class="tag appear" data-steps="4" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-lock"></use></svg>channel open</span>
|
|
167
|
+
<div class="bubble out appear" data-steps="5 6" hidden><span class="mono">help.request</span>Review my patch for a race condition?</div>
|
|
168
|
+
<div class="bubble appear d1" data-steps="6" hidden><span class="mono">help.reply</span>Found 2 edge cases. Add a timeout guard.</div>
|
|
169
|
+
<span class="tag appear" data-steps="7" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-file"></use></svg>sending build.log</span>
|
|
170
|
+
<div class="progress" data-steps="7" hidden><i></i></div>
|
|
171
|
+
<div class="kv appear d1" data-steps="8" hidden><div><span>task.status</span><span>"approved"</span></div><div><span>clock 3</span><span>reviewer</span></div></div>
|
|
172
|
+
<span class="tag appear d2" data-steps="8" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-refresh"></use></svg>merged from peer</span>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="agent agent-b">
|
|
177
|
+
<div class="agent-head"><span class="mono-tile t-codex">Cx</span><div><b>Codex</b><span>Build server</span></div></div>
|
|
178
|
+
<div class="agent-meta"><code>reviewer</code><span class="status">not connected</span></div>
|
|
179
|
+
<div class="agent-body">
|
|
180
|
+
<span class="tag" data-steps="0">On its own</span>
|
|
181
|
+
<span class="tag appear" data-steps="1 2" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-plug"></use></svg>18 tools loaded</span>
|
|
182
|
+
<span class="tag appear" data-steps="3" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-hash"></use></svg>joined team_x7k2</span>
|
|
183
|
+
<span class="tag appear d1" data-steps="3" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-arrow-right"></use></svg>asking for planner</span>
|
|
184
|
+
<span class="tag appear" data-steps="4" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-lock"></use></svg>channel open</span>
|
|
185
|
+
<div class="bubble appear d1" data-steps="5 6" hidden><span class="mono">help.request</span>Review my patch for a race condition?</div>
|
|
186
|
+
<div class="bubble out appear" data-steps="6" hidden><span class="mono">help.reply</span>Found 2 edge cases. Add a timeout guard.</div>
|
|
187
|
+
<span class="tag appear" data-steps="7" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-file"></use></svg>receiving build.log</span>
|
|
188
|
+
<div class="progress" data-steps="7" hidden><i></i></div>
|
|
189
|
+
<span class="tag appear d3" data-steps="7" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-check"></use></svg>SHA-256 verified, saved</span>
|
|
190
|
+
<div class="kv appear" data-steps="8" hidden><div><span>task.status</span><span>"approved"</span></div><div><span>clock 3</span><span>reviewer</span></div></div>
|
|
191
|
+
<span class="tag appear d1" data-steps="8" hidden><svg class="icon-xs"><use href="assets/icons.svg#i-layers"></use></svg>set here, broadcast</span>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<div class="p2p-label">encrypted · P2P</div>
|
|
196
|
+
<div class="packets"></div>
|
|
197
|
+
|
|
198
|
+
<div class="node node-relay">
|
|
199
|
+
<span class="ico"><svg class="icon-sm"><use href="assets/icons.svg#i-relay"></use></svg></span>
|
|
200
|
+
<div>
|
|
201
|
+
<b>TURN relay</b>
|
|
202
|
+
<span data-steps="0 1 2 3 5 6 7 8">only if direct fails</span>
|
|
203
|
+
<span data-steps="4" hidden>standby: carries ciphertext only</span>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div class="term" aria-hidden="true">
|
|
209
|
+
<div class="term-bar"><span class="term-dots"><i></i><i></i><i></i></span><span class="term-title">Two machines</span></div>
|
|
210
|
+
<div class="term-body">
|
|
211
|
+
<div class="term-step is-active" data-step="0" data-title="Two machines">
|
|
212
|
+
<span class="ln dim"># Agent A: Claude Code on your laptop</span><span class="ln dim"># Agent B: Codex on a build server</span><span class="ln dim"># Different networks. No VPN. No open ports.</span><span class="ln cmd"><i class="cursor"></i></span></div>
|
|
213
|
+
<div class="term-step" data-step="1" data-title="Your laptop · terminal">
|
|
214
|
+
<span class="ln cmd">npm i @vdoninja/mcp</span><span class="ln dim"># MCP server + Node WebRTC runtime</span><span class="ln cmd">npx vdon-mcp-install</span><span class="ln">Installing Codex MCP server 'vdo-ninja-mcp'...</span><span class="ln">Installing Claude MCP server 'vdo-ninja-mcp' (scope=local)...</span><span class="ln ok">Done.</span></div>
|
|
215
|
+
<div class="term-step" data-step="2" data-title="planner · Claude Code · tool call">
|
|
216
|
+
<span class="ln who">→ CALL</span><span class="ln json">{ "name": "vdo_connect",
|
|
217
|
+
"arguments": {
|
|
218
|
+
"room": "team_x7k2",
|
|
219
|
+
"stream_id": "planner" } }</span><span class="ln who">← RESULT</span><span class="ln json">{ "ok": true, "session_id": "vdo_3f1c…", … }</span></div>
|
|
220
|
+
<div class="term-step" data-step="3" data-title="reviewer · Codex · tool call">
|
|
221
|
+
<span class="ln who">→ CALL</span><span class="ln json">{ "name": "vdo_connect",
|
|
222
|
+
"arguments": {
|
|
223
|
+
"room": "team_x7k2",
|
|
224
|
+
"stream_id": "reviewer",
|
|
225
|
+
"target_stream_id": "planner" } }</span><span class="ln who">← RESULT</span><span class="ln json">{ "ok": true, "session_id": "vdo_9a0e…", … }</span></div>
|
|
226
|
+
<div class="term-step" data-step="4" data-title="reviewer · Codex · vdo_receive">
|
|
227
|
+
<span class="ln who">→ CALL</span><span class="ln json">{ "name": "vdo_receive",
|
|
228
|
+
"arguments": {
|
|
229
|
+
"session_id": "vdo_9a0e…",
|
|
230
|
+
"wait_ms": 1000 } }</span><span class="ln who">← EVENTS</span><span class="ln json">{ "type": "peer_connected" }</span><span class="ln json">{ "type": "data_channel_open" }</span><span class="ln json">{ "type": "sync_peer_updated" }</span></div>
|
|
231
|
+
<div class="term-step" data-step="5" data-title="planner · Claude Code · vdo_send">
|
|
232
|
+
<span class="ln who">→ CALL</span><span class="ln json">{ "name": "vdo_send",
|
|
233
|
+
"arguments": {
|
|
234
|
+
"session_id": "vdo_3f1c…",
|
|
235
|
+
"target": "reviewer",
|
|
236
|
+
"data": { "type": "help.request",
|
|
237
|
+
"task": "review patch for race condition" } } }</span><span class="ln who">← RESULT</span><span class="ln json">{ "ok": true, "used_target": "reviewer" }</span><span class="ln dim"># ok = accepted for sending. The reply is the receipt.</span></div>
|
|
238
|
+
<div class="term-step" data-step="6" data-title="reviewer · Codex · receive + reply">
|
|
239
|
+
<span class="ln who">← vdo_receive</span><span class="ln json">{ "type": "data_received",
|
|
240
|
+
"streamID": "planner",
|
|
241
|
+
"data": { "type": "help.request", … } }</span><span class="ln who">→ vdo_send</span><span class="ln json">{ "target": "planner",
|
|
242
|
+
"data": { "type": "help.reply",
|
|
243
|
+
"summary": "found 2 edge cases",
|
|
244
|
+
"next_steps": ["add timeout guard"] } }</span></div>
|
|
245
|
+
<div class="term-step" data-step="7" data-title="planner → reviewer · file transfer">
|
|
246
|
+
<span class="ln who">planner → vdo_file_send_path</span><span class="ln json">{ "session_id": "vdo_3f1c…",
|
|
247
|
+
"file_path": "./logs/build.log" }</span><span class="ln dim"># offer → accept → 64 KB chunks → ack/nack → complete</span><span class="ln who">reviewer → vdo_file_save</span><span class="ln json">{ "transfer_id": "…",
|
|
248
|
+
"output_path": "./inbox/build.log" }</span><span class="ln ok">✓ hash matches · written to disk, never loaded into context</span></div>
|
|
249
|
+
<div class="term-step" data-step="8" data-title="reviewer · Codex · vdo_state_set">
|
|
250
|
+
<span class="ln who">→ CALL</span><span class="ln json">{ "name": "vdo_state_set",
|
|
251
|
+
"arguments": {
|
|
252
|
+
"session_id": "vdo_9a0e…",
|
|
253
|
+
"key": "task.status",
|
|
254
|
+
"value": "approved" } }</span><span class="ln who">← RESULT</span><span class="ln json">{ "ok": true,
|
|
255
|
+
"entry": { "key": "task.status",
|
|
256
|
+
"actor": "reviewer", "clock": 3, … } }</span><span class="ln dim"># higher clock wins; ties break by actor name</span></div>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
<div class="story-caption">
|
|
262
|
+
<div class="caption-pill" aria-live="polite"><span class="num">00</span><span class="txt">Two agents on two machines. Neither can reach the other.</span></div>
|
|
263
|
+
<div class="dashes" aria-label="Story steps"></div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<ol class="story-steps">
|
|
268
|
+
<li>Two agents on two machines. Neither can reach the other.</li>
|
|
269
|
+
<li>One install registers the MCP server with Claude Code and Codex.</li>
|
|
270
|
+
<li><code>planner</code> calls <code>vdo_connect</code> and joins a room through the handshake server.</li>
|
|
271
|
+
<li><code>reviewer</code> joins the same room and asks for <code>planner</code>'s stream.</li>
|
|
272
|
+
<li>WebRTC opens a direct, encrypted channel. The handshake server steps out.</li>
|
|
273
|
+
<li><code>vdo_send</code>: the request goes straight to the other agent.</li>
|
|
274
|
+
<li><code>reviewer</code> reads it with <code>vdo_receive</code> and replies the same way.</li>
|
|
275
|
+
<li>Files move in 64 KB chunks: hashed, acknowledged, resumable.</li>
|
|
276
|
+
<li>Shared state merges on every peer. The higher clock wins.</li>
|
|
277
|
+
</ol>
|
|
278
|
+
</div>
|
|
279
|
+
</section>
|
|
280
|
+
|
|
281
|
+
<!-- Connections -->
|
|
282
|
+
<section class="section-tight" id="connect">
|
|
283
|
+
<div class="wrap">
|
|
284
|
+
<div class="section-head" style="margin-bottom:36px">
|
|
285
|
+
<div class="kicker"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-globe"></use></svg>Three ways to connect</div>
|
|
286
|
+
<h2>Direct first. A relay when you need one.</h2>
|
|
287
|
+
<p>Both agents pick the same path. There's no automatic switching yet.</p>
|
|
288
|
+
</div>
|
|
289
|
+
<div class="grid grid-3">
|
|
290
|
+
<div class="card">
|
|
291
|
+
<div class="card-icon ok"><svg class="icon"><use href="assets/icons.svg#i-zap"></use></svg></div>
|
|
292
|
+
<h3>WebRTC <span class="muted" style="font-weight:450">· default</span></h3>
|
|
293
|
+
<p>A direct, DTLS-encrypted data channel between the two agents. Nothing to host. Fastest on a LAN.</p>
|
|
294
|
+
<div class="code-mini"><code>"transport": "webrtc"</code></div>
|
|
295
|
+
</div>
|
|
296
|
+
<div class="card">
|
|
297
|
+
<div class="card-icon neutral"><svg class="icon"><use href="assets/icons.svg#i-relay"></use></svg></div>
|
|
298
|
+
<h3>WebRTC through TURN</h3>
|
|
299
|
+
<p>Relays the encrypted channel so peers never see your IP. Still needs WebRTC on both ends.</p>
|
|
300
|
+
<div class="code-mini"><code>"force_turn": true</code></div>
|
|
301
|
+
</div>
|
|
302
|
+
<div class="card">
|
|
303
|
+
<div class="card-icon neutral"><svg class="icon"><use href="assets/icons.svg#i-server"></use></svg></div>
|
|
304
|
+
<h3>Your own HTTP relay</h3>
|
|
305
|
+
<p>For machines or networks where WebRTC can't run. Long polling through a relay you host, encrypted with a shared room key.</p>
|
|
306
|
+
<div class="code-mini"><code>"transport": "http"</code></div>
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
<p class="center mt-24"><a class="link" href="guide.html#relay">Set up the HTTP relay</a></p>
|
|
310
|
+
</div>
|
|
311
|
+
</section>
|
|
312
|
+
|
|
313
|
+
<!-- Who sees what -->
|
|
314
|
+
<section class="section section-alt" id="privacy">
|
|
315
|
+
<div class="wrap">
|
|
316
|
+
<div class="section-head">
|
|
317
|
+
<div class="kicker"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-shield"></use></svg>Transparency</div>
|
|
318
|
+
<h2>Who sees what.</h2>
|
|
319
|
+
<p>Every network path has other parties in it. Here is each one, when it is involved, and what it can and can't see.</p>
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
<div class="sees">
|
|
323
|
+
<article class="card party">
|
|
324
|
+
<div class="party-head"><span class="card-icon neutral"><svg class="icon"><use href="assets/icons.svg#i-server"></use></svg></span><div><b>Handshake server</b><span>wss.vdo.ninja</span></div></div>
|
|
325
|
+
<div class="when">While peers find each other</div>
|
|
326
|
+
<h4>Sees</h4>
|
|
327
|
+
<ul class="yes">
|
|
328
|
+
<li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Your IP address</li>
|
|
329
|
+
<li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>A hashed room ID, and stream IDs</li>
|
|
330
|
+
<li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Encrypted connection offers</li>
|
|
331
|
+
</ul>
|
|
332
|
+
<h4>Never sees</h4>
|
|
333
|
+
<ul class="no"><li><svg class="icon-sm"><use href="assets/icons.svg#i-eye-off"></use></svg>Messages, files, or state</li></ul>
|
|
334
|
+
</article>
|
|
335
|
+
|
|
336
|
+
<article class="card party">
|
|
337
|
+
<div class="party-head"><span class="card-icon neutral"><svg class="icon"><use href="assets/icons.svg#i-globe"></use></svg></span><div><b>STUN servers</b><span>Google · Cloudflare</span></div></div>
|
|
338
|
+
<div class="when">During connection setup</div>
|
|
339
|
+
<h4>Sees</h4>
|
|
340
|
+
<ul class="yes"><li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Your public IP and port, to find a route</li></ul>
|
|
341
|
+
<h4>Never sees</h4>
|
|
342
|
+
<ul class="no"><li><svg class="icon-sm"><use href="assets/icons.svg#i-eye-off"></use></svg>Anything you send</li></ul>
|
|
343
|
+
</article>
|
|
344
|
+
|
|
345
|
+
<article class="card party">
|
|
346
|
+
<div class="party-head"><span class="card-icon neutral"><svg class="icon"><use href="assets/icons.svg#i-relay"></use></svg></span><div><b>TURN relay</b><span>VDO.Ninja TURN</span></div></div>
|
|
347
|
+
<div class="when">Only if a direct path fails, or with <code>force_turn</code></div>
|
|
348
|
+
<h4>Sees</h4>
|
|
349
|
+
<ul class="yes">
|
|
350
|
+
<li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Both peers' IPs</li>
|
|
351
|
+
<li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Encrypted packets</li>
|
|
352
|
+
</ul>
|
|
353
|
+
<h4>Never sees</h4>
|
|
354
|
+
<ul class="no"><li><svg class="icon-sm"><use href="assets/icons.svg#i-eye-off"></use></svg>Contents: DTLS is end to end</li></ul>
|
|
355
|
+
</article>
|
|
356
|
+
|
|
357
|
+
<article class="card party">
|
|
358
|
+
<div class="party-head"><span class="card-icon neutral"><svg class="icon"><use href="assets/icons.svg#i-server"></use></svg></span><div><b>Your HTTP relay</b><span>vdon-mcp-relay</span></div></div>
|
|
359
|
+
<div class="when">Only with <code>transport: "http"</code></div>
|
|
360
|
+
<h4>Sees</h4>
|
|
361
|
+
<ul class="yes">
|
|
362
|
+
<li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Connection addresses</li>
|
|
363
|
+
<li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Opaque room IDs, timing, sizes</li>
|
|
364
|
+
</ul>
|
|
365
|
+
<h4>Never sees</h4>
|
|
366
|
+
<ul class="no"><li><svg class="icon-sm"><use href="assets/icons.svg#i-eye-off"></use></svg>The room key or plaintext (AES-256-GCM)</li></ul>
|
|
367
|
+
</article>
|
|
368
|
+
|
|
369
|
+
<article class="card party">
|
|
370
|
+
<div class="party-head"><span class="card-icon neutral"><svg class="icon"><use href="assets/icons.svg#i-users"></use></svg></span><div><b>The other agent</b><span>your peer</span></div></div>
|
|
371
|
+
<div class="when">Once connected</div>
|
|
372
|
+
<h4>Sees</h4>
|
|
373
|
+
<ul class="yes">
|
|
374
|
+
<li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Everything you send it</li>
|
|
375
|
+
<li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Your IP, on direct connections</li>
|
|
376
|
+
</ul>
|
|
377
|
+
<h4>Never sees</h4>
|
|
378
|
+
<ul class="no"><li><svg class="icon-sm"><use href="assets/icons.svg#i-eye-off"></use></svg>Your files, shell, or network, unless a tool sends them</li></ul>
|
|
379
|
+
</article>
|
|
380
|
+
|
|
381
|
+
<article class="card party">
|
|
382
|
+
<div class="party-head"><span class="card-icon neutral"><svg class="icon"><use href="assets/icons.svg#i-cloud"></use></svg></span><div><b>Your AI provider</b><span>model API</span></div></div>
|
|
383
|
+
<div class="when">On every tool call</div>
|
|
384
|
+
<h4>Sees</h4>
|
|
385
|
+
<ul class="yes"><li><svg class="icon-sm"><use href="assets/icons.svg#i-eye"></use></svg>Tool calls and results your agent reads, like any MCP tool</li></ul>
|
|
386
|
+
<h4>Never sees</h4>
|
|
387
|
+
<ul class="no"><li><svg class="icon-sm"><use href="assets/icons.svg#i-eye-off"></use></svg>Files your agent moves disk to disk without reading them</li></ul>
|
|
388
|
+
</article>
|
|
389
|
+
</div>
|
|
390
|
+
|
|
391
|
+
<div class="grid grid-2 mt-24">
|
|
392
|
+
<div class="note-box warn" style="margin:0">
|
|
393
|
+
<svg class="icon" aria-hidden="true"><use href="assets/icons.svg#i-alert"></use></svg>
|
|
394
|
+
<div><strong>The default handshake key is public.</strong> Without a <code>password</code>, VDO.Ninja's built-in key encrypts the handshake. That stops casual snooping but isn't a secret. For private work, pass your own <code>password</code> to <code>vdo_connect</code>; peers without it can't even find the room.</div>
|
|
395
|
+
</div>
|
|
396
|
+
<div class="note-box" style="margin:0">
|
|
397
|
+
<svg class="icon" aria-hidden="true"><use href="assets/icons.svg#i-info"></use></svg>
|
|
398
|
+
<div><strong>App data stays off the WebSocket.</strong> Messages, files, and state only travel over the WebRTC data channel. Falling back through the handshake server is off unless you explicitly allow it on a send.</div>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
|
|
402
|
+
<h3 class="center mt-48" style="font-size:24px">Lock it down when it matters</h3>
|
|
403
|
+
<p class="center muted mt-8">Four controls, all optional, all set per session. Or install with <code>--preset secure-full</code>.</p>
|
|
404
|
+
<div class="grid grid-4 mt-32">
|
|
405
|
+
<div class="card">
|
|
406
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-lock"></use></svg></div>
|
|
407
|
+
<h3>Room password</h3>
|
|
408
|
+
<p>Encrypts the handshake and hashes your room name with a secret only your peers know.</p>
|
|
409
|
+
</div>
|
|
410
|
+
<div class="card">
|
|
411
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-key"></use></svg></div>
|
|
412
|
+
<h3>Join tokens</h3>
|
|
413
|
+
<p>HMAC-signed, bound to the room, expire after 10 minutes by default. Enforce them to reject anyone without one.</p>
|
|
414
|
+
</div>
|
|
415
|
+
<div class="card">
|
|
416
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-users"></use></svg></div>
|
|
417
|
+
<h3>Peer allowlist</h3>
|
|
418
|
+
<p><code>allow_peer_stream_ids</code> limits which stream IDs can talk to you or send files.</p>
|
|
419
|
+
</div>
|
|
420
|
+
<div class="card">
|
|
421
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-shield"></use></svg></div>
|
|
422
|
+
<h3>Session MAC</h3>
|
|
423
|
+
<p>Per-peer X25519 key agreement signs control and file messages with HMAC-SHA256. Require it and unsigned ones are dropped.</p>
|
|
424
|
+
</div>
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
</section>
|
|
428
|
+
|
|
429
|
+
<!-- Tools -->
|
|
430
|
+
<section class="section" id="tools">
|
|
431
|
+
<div class="wrap">
|
|
432
|
+
<div class="section-head">
|
|
433
|
+
<div class="kicker"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-sliders"></use></svg>18 tools, 4 profiles</div>
|
|
434
|
+
<h2>A small surface. Load only the slice you need.</h2>
|
|
435
|
+
<p>Profiles decide which tools your agent can see. Fewer tools means less context spent and fewer ways to go wrong.</p>
|
|
436
|
+
</div>
|
|
437
|
+
<div class="center">
|
|
438
|
+
<div class="profiles" data-profiles role="group" aria-label="Tool profile">
|
|
439
|
+
<button type="button" data-profile="core">core</button>
|
|
440
|
+
<button type="button" data-profile="file">file</button>
|
|
441
|
+
<button type="button" data-profile="state">state</button>
|
|
442
|
+
<button type="button" data-profile="full" aria-pressed="true">full</button>
|
|
443
|
+
</div>
|
|
444
|
+
<div class="profile-note">All 18 tools. The default when no profile is set.</div>
|
|
445
|
+
</div>
|
|
446
|
+
|
|
447
|
+
<div class="tool-groups">
|
|
448
|
+
<div class="card tool-group" data-group="session">
|
|
449
|
+
<h3><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-message"></use></svg>Session<span class="count">7</span></h3>
|
|
450
|
+
<div class="tool"><code>vdo_connect</code><p>Join a room; optionally auto-view a peer.</p></div>
|
|
451
|
+
<div class="tool"><code>vdo_send</code><p>Send JSON or text to one peer or all.</p></div>
|
|
452
|
+
<div class="tool"><code>vdo_receive</code><p>Poll messages and events. Waits up to 30 s.</p></div>
|
|
453
|
+
<div class="tool"><code>vdo_status</code><p>Live status for one session.</p></div>
|
|
454
|
+
<div class="tool"><code>vdo_list_sessions</code><p>Every active session.</p></div>
|
|
455
|
+
<div class="tool"><code>vdo_disconnect</code><p>Leave the room and clean up.</p></div>
|
|
456
|
+
<div class="tool"><code>vdo_capabilities</code><p>Limits and active profile. Call it first.</p></div>
|
|
457
|
+
</div>
|
|
458
|
+
<div class="card tool-group" data-group="sync">
|
|
459
|
+
<h3><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-refresh"></use></svg>Peer sync<span class="count">2</span></h3>
|
|
460
|
+
<div class="tool"><code>vdo_sync_peers</code><p>Each peer's handshake, auth, and capabilities.</p></div>
|
|
461
|
+
<div class="tool"><code>vdo_sync_announce</code><p>Re-announce now instead of waiting for the heartbeat.</p></div>
|
|
462
|
+
</div>
|
|
463
|
+
<div class="card tool-group" data-group="file">
|
|
464
|
+
<h3><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-file"></use></svg>Files<span class="count">6</span></h3>
|
|
465
|
+
<div class="tool"><code>vdo_file_send_path</code><p>Stream a local file without loading it into context.</p></div>
|
|
466
|
+
<div class="tool"><code>vdo_file_send</code><p>Send from base64 data.</p></div>
|
|
467
|
+
<div class="tool"><code>vdo_file_transfers</code><p>Progress, both directions.</p></div>
|
|
468
|
+
<div class="tool"><code>vdo_file_resume</code><p>Pick up an interrupted send.</p></div>
|
|
469
|
+
<div class="tool"><code>vdo_file_receive</code><p>Read a finished file as utf8, json, or base64.</p></div>
|
|
470
|
+
<div class="tool"><code>vdo_file_save</code><p>Write a finished file straight to disk.</p></div>
|
|
471
|
+
</div>
|
|
472
|
+
<div class="card tool-group" data-group="state">
|
|
473
|
+
<h3><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-layers"></use></svg>Shared state<span class="count">3</span></h3>
|
|
474
|
+
<div class="tool"><code>vdo_state_set</code><p>Set a shared key and broadcast it.</p></div>
|
|
475
|
+
<div class="tool"><code>vdo_state_get</code><p>Read one key or the whole snapshot.</p></div>
|
|
476
|
+
<div class="tool"><code>vdo_state_sync</code><p>Pull or push a full snapshot after a reconnect.</p></div>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
<p class="center mt-24"><a class="link" href="guide.html#tools">Full tool reference with every argument</a></p>
|
|
480
|
+
</div>
|
|
481
|
+
</section>
|
|
482
|
+
|
|
483
|
+
<!-- Use cases -->
|
|
484
|
+
<section class="section section-alt" id="uses">
|
|
485
|
+
<div class="wrap">
|
|
486
|
+
<div class="section-head">
|
|
487
|
+
<div class="kicker"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-zap"></use></svg>Use cases</div>
|
|
488
|
+
<h2>What people wire up first.</h2>
|
|
489
|
+
</div>
|
|
490
|
+
<div class="grid grid-3">
|
|
491
|
+
<div class="card">
|
|
492
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-users"></use></svg></div>
|
|
493
|
+
<h3>Second opinion</h3>
|
|
494
|
+
<p>Claude Code asks Codex to review a patch. The reply lands back in the same session, ready to act on.</p>
|
|
495
|
+
</div>
|
|
496
|
+
<div class="card">
|
|
497
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-file"></use></svg></div>
|
|
498
|
+
<h3>Artifact hand-off</h3>
|
|
499
|
+
<p>Send logs, builds, and datasets agent to agent. Large files spool to disk and resume after drops.</p>
|
|
500
|
+
</div>
|
|
501
|
+
<div class="card">
|
|
502
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-git-branch"></use></svg></div>
|
|
503
|
+
<h3>Reviewer panel</h3>
|
|
504
|
+
<p>One writer, a few reviewers, one decider, all in one short-lived room.</p>
|
|
505
|
+
</div>
|
|
506
|
+
<div class="card">
|
|
507
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-layers"></use></svg></div>
|
|
508
|
+
<h3>Shared task board</h3>
|
|
509
|
+
<p>Agents keep status in shared keys and catch up with one sync after a reconnect.</p>
|
|
510
|
+
</div>
|
|
511
|
+
<div class="card">
|
|
512
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-laptop"></use></svg></div>
|
|
513
|
+
<h3>Across machines</h3>
|
|
514
|
+
<p>An agent on your laptop works with one on a GPU box or a teammate's desktop. No VPN, no open ports.</p>
|
|
515
|
+
</div>
|
|
516
|
+
<div class="card">
|
|
517
|
+
<div class="card-icon"><svg class="icon"><use href="assets/icons.svg#i-clock"></use></svg></div>
|
|
518
|
+
<h3>A room per task</h3>
|
|
519
|
+
<p>Spin up a room for one job and disconnect when it's done. Nothing to provision or tear down.</p>
|
|
520
|
+
</div>
|
|
521
|
+
</div>
|
|
522
|
+
</div>
|
|
523
|
+
</section>
|
|
524
|
+
|
|
525
|
+
<!-- Compare -->
|
|
526
|
+
<section class="section">
|
|
527
|
+
<div class="wrap wrap-narrow" style="max-width:900px">
|
|
528
|
+
<div class="section-head">
|
|
529
|
+
<div class="kicker"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-scale"></use></svg>Honest comparison</div>
|
|
530
|
+
<h2>Not a VPN. On purpose.</h2>
|
|
531
|
+
<p>A VPN connects machines. This connects agents. They solve different problems and work fine together.</p>
|
|
532
|
+
</div>
|
|
533
|
+
<div class="table-wrap">
|
|
534
|
+
<table>
|
|
535
|
+
<thead><tr><th></th><th>VDO.Ninja MCP</th><th>VPN overlay (e.g. Tailscale)</th></tr></thead>
|
|
536
|
+
<tbody>
|
|
537
|
+
<tr><td>Agent messages, files, shared state</td><td>Built in, as MCP tools</td><td>Build it yourself on top</td></tr>
|
|
538
|
+
<tr><td>Network reach between machines</td><td>None. Only what tools send</td><td>Full IP access</td></tr>
|
|
539
|
+
<tr><td>Per-agent permissions</td><td>Tool profiles, tokens, allowlists</td><td>Network ACLs</td></tr>
|
|
540
|
+
<tr><td>Accounts and setup</td><td>None. npm install</td><td>Account, client on every host</td></tr>
|
|
541
|
+
<tr><td>SSH, databases, internal web apps</td><td>Not supported</td><td>Yes</td></tr>
|
|
542
|
+
<tr><td>Lifetime</td><td>Per task. Rooms come and go</td><td>Always on</td></tr>
|
|
543
|
+
</tbody>
|
|
544
|
+
</table>
|
|
545
|
+
</div>
|
|
546
|
+
|
|
547
|
+
<h3 class="center mt-48" style="font-size:24px">Which VDO.Ninja package do I need?</h3>
|
|
548
|
+
<div class="grid grid-3 mt-24">
|
|
549
|
+
<div class="card pkg">
|
|
550
|
+
<span class="badge">You are here</span>
|
|
551
|
+
<span class="pkg-name">@vdoninja/mcp</span>
|
|
552
|
+
<p>Tools any MCP client can call: connect, send, files, state.</p>
|
|
553
|
+
</div>
|
|
554
|
+
<a class="card pkg" href="https://github.com/steveseguin/ninja-p2p">
|
|
555
|
+
<span class="badge neutral">Always-on rooms</span>
|
|
556
|
+
<span class="pkg-name">@vdoninja/ninja-p2p</span>
|
|
557
|
+
<p>Keep an agent room running, with a local inbox and wake hooks.</p>
|
|
558
|
+
<span class="more">View on GitHub <svg class="icon-xs" aria-hidden="true"><use href="assets/icons.svg#i-arrow-up-right"></use></svg></span>
|
|
559
|
+
</a>
|
|
560
|
+
<a class="card pkg" href="https://github.com/steveseguin/ninjasdk">
|
|
561
|
+
<span class="badge neutral">Build your own</span>
|
|
562
|
+
<span class="pkg-name">@vdoninja/sdk</span>
|
|
563
|
+
<p>WebRTC media and data channels, directly.</p>
|
|
564
|
+
<span class="more">View on GitHub <svg class="icon-xs" aria-hidden="true"><use href="assets/icons.svg#i-arrow-up-right"></use></svg></span>
|
|
565
|
+
</a>
|
|
566
|
+
</div>
|
|
567
|
+
</div>
|
|
568
|
+
</section>
|
|
569
|
+
|
|
570
|
+
<!-- Install -->
|
|
571
|
+
<section class="section section-alt" id="install">
|
|
572
|
+
<div class="wrap">
|
|
573
|
+
<div class="section-head">
|
|
574
|
+
<div class="kicker"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-terminal"></use></svg>Get started</div>
|
|
575
|
+
<h2>Set up in a minute.</h2>
|
|
576
|
+
<p>Node 18 or newer. Install in a folder that will stick around: the installer saves an absolute path.</p>
|
|
577
|
+
</div>
|
|
578
|
+
<div class="grid grid-3 steps">
|
|
579
|
+
<div class="card step">
|
|
580
|
+
<span class="n">01</span>
|
|
581
|
+
<h3>Install</h3>
|
|
582
|
+
<p>Pulls in the MCP server, the VDO.Ninja SDK, and a Node WebRTC runtime. Relay-only machines can skip WebRTC with <code>--omit=optional</code>.</p>
|
|
583
|
+
<div class="code-mini"><code>npm i @vdoninja/mcp</code><button class="copy" type="button" data-copy="npm i @vdoninja/mcp"><svg class="icon-xs" aria-hidden="true"><use href="assets/icons.svg#i-copy"></use></svg><span class="label">Copy</span></button></div>
|
|
584
|
+
</div>
|
|
585
|
+
<div class="card step">
|
|
586
|
+
<span class="n">02</span>
|
|
587
|
+
<h3>Register</h3>
|
|
588
|
+
<p>Adds the server to Claude Code and Codex, whichever are on your PATH.</p>
|
|
589
|
+
<div class="code-mini"><code>npx vdon-mcp-install</code><button class="copy" type="button" data-copy="npx vdon-mcp-install"><svg class="icon-xs" aria-hidden="true"><use href="assets/icons.svg#i-copy"></use></svg><span class="label">Copy</span></button></div>
|
|
590
|
+
</div>
|
|
591
|
+
<div class="card step">
|
|
592
|
+
<span class="n">03</span>
|
|
593
|
+
<h3>Say hello</h3>
|
|
594
|
+
<p>Ask your agent to call this tool. If it answers, you're wired up.</p>
|
|
595
|
+
<div class="code-mini"><code>vdo_capabilities</code><button class="copy" type="button" data-copy='{ "name": "vdo_capabilities", "arguments": {} }'><svg class="icon-xs" aria-hidden="true"><use href="assets/icons.svg#i-copy"></use></svg><span class="label">Copy</span></button></div>
|
|
596
|
+
</div>
|
|
597
|
+
</div>
|
|
598
|
+
|
|
599
|
+
<div class="tabs" data-tabs>
|
|
600
|
+
<div class="tablist" role="tablist" aria-label="Client setup">
|
|
601
|
+
<button role="tab" id="t-claude" aria-controls="p-claude" aria-selected="true"><span class="mono-tile t-claude">Cl</span>Claude Code</button>
|
|
602
|
+
<button role="tab" id="t-codex" aria-controls="p-codex" aria-selected="false" tabindex="-1"><span class="mono-tile t-codex">Cx</span>Codex</button>
|
|
603
|
+
<button role="tab" id="t-cursor" aria-controls="p-cursor" aria-selected="false" tabindex="-1"><span class="mono-tile t-cursor">Cu</span>Cursor</button>
|
|
604
|
+
<button role="tab" id="t-gemini" aria-controls="p-gemini" aria-selected="false" tabindex="-1"><span class="mono-tile t-gemini">Ge</span>Gemini CLI</button>
|
|
605
|
+
<button role="tab" id="t-opencode" aria-controls="p-opencode" aria-selected="false" tabindex="-1"><span class="mono-tile t-opencode">Oc</span>OpenCode</button>
|
|
606
|
+
<button role="tab" id="t-other" aria-controls="p-other" aria-selected="false" tabindex="-1"><span class="mono-tile t-any">+</span>Other</button>
|
|
607
|
+
</div>
|
|
608
|
+
<div class="tabpanel" id="p-claude" role="tabpanel" aria-labelledby="t-claude">
|
|
609
|
+
<p>The installer runs <code>claude mcp add</code> for you. Default scope is <code>local</code>; use <code>--claude-scope user</code> to make it available everywhere.</p>
|
|
610
|
+
<pre><code class="lang-sh">npx vdon-mcp-install --claude-only
|
|
611
|
+
# least privilege: messaging only
|
|
612
|
+
npx vdon-mcp-install --claude-only --preset core</code></pre>
|
|
613
|
+
</div>
|
|
614
|
+
<div class="tabpanel" id="p-codex" role="tabpanel" aria-labelledby="t-codex" hidden>
|
|
615
|
+
<p>The installer runs <code>codex mcp add</code> with an absolute path to the server script.</p>
|
|
616
|
+
<pre><code class="lang-sh">npx vdon-mcp-install --codex-only
|
|
617
|
+
# remove it again
|
|
618
|
+
npx vdon-mcp-install --codex-only --uninstall</code></pre>
|
|
619
|
+
</div>
|
|
620
|
+
<div class="tabpanel" id="p-cursor" role="tabpanel" aria-labelledby="t-cursor" hidden>
|
|
621
|
+
<p>Add to <code>~/.cursor/mcp.json</code> (global) or <code>.cursor/mcp.json</code> (project). Use an absolute path.</p>
|
|
622
|
+
<pre><code class="lang-json">{
|
|
623
|
+
"mcpServers": {
|
|
624
|
+
"vdo-ninja-mcp": {
|
|
625
|
+
"command": "node",
|
|
626
|
+
"args": ["/ABSOLUTE/PATH/node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js"],
|
|
627
|
+
"env": { "VDON_MCP_TOOL_PROFILE": "core" }
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}</code></pre>
|
|
631
|
+
</div>
|
|
632
|
+
<div class="tabpanel" id="p-gemini" role="tabpanel" aria-labelledby="t-gemini" hidden>
|
|
633
|
+
<p>Add to <code>.gemini/settings.json</code> (project) or <code>~/.gemini/settings.json</code>. Restart, then run <code>/mcp</code> to confirm.</p>
|
|
634
|
+
<pre><code class="lang-json">{
|
|
635
|
+
"mcpServers": {
|
|
636
|
+
"vdo-ninja-mcp": {
|
|
637
|
+
"command": "node",
|
|
638
|
+
"args": ["/ABSOLUTE/PATH/node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js"],
|
|
639
|
+
"env": { "VDON_MCP_TOOL_PROFILE": "core" }
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}</code></pre>
|
|
643
|
+
</div>
|
|
644
|
+
<div class="tabpanel" id="p-opencode" role="tabpanel" aria-labelledby="t-opencode" hidden>
|
|
645
|
+
<p>Add under <code>mcp</code> in <code>opencode.json</code> (project) or <code>~/.config/opencode/opencode.json</code>.</p>
|
|
646
|
+
<pre><code class="lang-json">{
|
|
647
|
+
"$schema": "https://opencode.ai/config.json",
|
|
648
|
+
"mcp": {
|
|
649
|
+
"vdo-ninja-mcp": {
|
|
650
|
+
"type": "local",
|
|
651
|
+
"command": ["node", "/ABSOLUTE/PATH/node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js",
|
|
652
|
+
"--tool-profile", "core"],
|
|
653
|
+
"enabled": true
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}</code></pre>
|
|
657
|
+
</div>
|
|
658
|
+
<div class="tabpanel" id="p-other" role="tabpanel" aria-labelledby="t-other" hidden>
|
|
659
|
+
<p>Any client that launches local stdio servers needs the same three things: a command, the script path, and optional flags or env.</p>
|
|
660
|
+
<pre><code class="lang-json">{
|
|
661
|
+
"command": "node",
|
|
662
|
+
"args": ["/ABSOLUTE/PATH/node_modules/@vdoninja/mcp/scripts/vdo-mcp-server.js",
|
|
663
|
+
"--tool-profile", "core"]
|
|
664
|
+
}</code></pre>
|
|
665
|
+
<p>Remote client? See <a href="guide.html#mcp-http">MCP over HTTP</a>.</p>
|
|
666
|
+
</div>
|
|
667
|
+
</div>
|
|
668
|
+
|
|
669
|
+
<div class="note-box mt-24">
|
|
670
|
+
<svg class="icon" aria-hidden="true"><use href="assets/icons.svg#i-terminal"></use></svg>
|
|
671
|
+
<div><strong>Want proof before wiring up agents?</strong> <code>npx vdon-mcp-demo-message</code> and <code>npx vdon-mcp-demo-file</code> run a full exchange on a local simulated network. Prefix with <code>MCP_DEMO_FAKE=0</code> to use the real one.</div>
|
|
672
|
+
</div>
|
|
673
|
+
</div>
|
|
674
|
+
</section>
|
|
675
|
+
|
|
676
|
+
<!-- Direction -->
|
|
677
|
+
<section class="section" id="next">
|
|
678
|
+
<div class="wrap">
|
|
679
|
+
<div class="section-head">
|
|
680
|
+
<div class="kicker"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-git-branch"></use></svg>Where it's going</div>
|
|
681
|
+
<h2>A private room for people and their agents.</h2>
|
|
682
|
+
<p>Two agents trading a short message should stay this easy. Around that, here's what's planned. None of it ships today.</p>
|
|
683
|
+
</div>
|
|
684
|
+
<div class="grid grid-4">
|
|
685
|
+
<div class="card">
|
|
686
|
+
<span class="badge-planned">Planned</span>
|
|
687
|
+
<h3>Presence and receipts</h3>
|
|
688
|
+
<p>Recognizable agent names, capabilities, and availability. Clear stages from queued to read to done.</p>
|
|
689
|
+
</div>
|
|
690
|
+
<div class="card">
|
|
691
|
+
<span class="badge-planned">Planned</span>
|
|
692
|
+
<h3>One conversation, any route</h3>
|
|
693
|
+
<p>Keep message identity across WebRTC, TURN, and relay, with P2P-only, relay-only, or automatic policies.</p>
|
|
694
|
+
</div>
|
|
695
|
+
<div class="card">
|
|
696
|
+
<span class="badge-planned">Planned</span>
|
|
697
|
+
<h3>Talk and show</h3>
|
|
698
|
+
<p>A browser room with microphone, camera, and screen. Interruptible speech. Recording off by default.</p>
|
|
699
|
+
</div>
|
|
700
|
+
<div class="card">
|
|
701
|
+
<span class="badge-planned">Planned</span>
|
|
702
|
+
<h3>Tasks with outcomes</h3>
|
|
703
|
+
<p>Requests, progress, artifacts, and cancellation, with an optional A2A adapter.</p>
|
|
704
|
+
</div>
|
|
705
|
+
</div>
|
|
706
|
+
<p class="center mt-24"><a class="link" href="direction.html">Read the direction guide</a></p>
|
|
707
|
+
</div>
|
|
708
|
+
</section>
|
|
709
|
+
|
|
710
|
+
<!-- FAQ -->
|
|
711
|
+
<section class="section section-alt" id="faq">
|
|
712
|
+
<div class="wrap wrap-narrow">
|
|
713
|
+
<div class="section-head">
|
|
714
|
+
<div class="kicker"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-info"></use></svg>FAQ</div>
|
|
715
|
+
<h2>Questions.</h2>
|
|
716
|
+
</div>
|
|
717
|
+
<div class="faq">
|
|
718
|
+
<details>
|
|
719
|
+
<summary>What is this, in one sentence?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
720
|
+
<div class="answer"><p>An MCP server that gives AI agents tools to message, send files to, and share state with other agents over VDO.Ninja's peer-to-peer WebRTC data channels.</p></div>
|
|
721
|
+
</details>
|
|
722
|
+
<details>
|
|
723
|
+
<summary>Do I need an account, an API key, or my own server?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
724
|
+
<div class="answer"><p>No. Install the npm package and go. VDO.Ninja's free public handshake server introduces peers, then data flows directly between them. If you run your own handshake server, point <code>host</code> at it. The optional HTTP relay is the only piece you'd host yourself.</p></div>
|
|
725
|
+
</details>
|
|
726
|
+
<details>
|
|
727
|
+
<summary>Is it free?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
728
|
+
<div class="answer"><p>Yes. <code>@vdoninja/mcp</code> is MIT licensed and free to use. Its <code>@vdoninja/sdk</code> dependency is MPL-2.0.</p></div>
|
|
729
|
+
</details>
|
|
730
|
+
<details>
|
|
731
|
+
<summary>Can VDO.Ninja read what my agents send?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
732
|
+
<div class="answer"><p>No. Messages, files, and state travel over the WebRTC data channel, encrypted with DTLS, directly between peers. If a TURN relay is needed, it only forwards ciphertext. The handshake server helps peers find each other and never carries app data unless you explicitly allow fallback on a send.</p></div>
|
|
733
|
+
</details>
|
|
734
|
+
<details>
|
|
735
|
+
<summary>Does my AI provider see what agents send each other?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
736
|
+
<div class="answer"><p>Anything your agent reads becomes part of its context, like any other tool result. To keep large or sensitive files out of the model, use <code>vdo_file_send_path</code> and <code>vdo_file_save</code>: they stream from disk and to disk.</p></div>
|
|
737
|
+
</details>
|
|
738
|
+
<details>
|
|
739
|
+
<summary>Could a stranger join my room?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
740
|
+
<div class="answer"><p>A room name is just a string, so a guessable one can collide. For anything private:</p>
|
|
741
|
+
<ul>
|
|
742
|
+
<li>use a unique room name and your own <code>password</code></li>
|
|
743
|
+
<li>turn on join tokens with <code>enforce_join_token</code></li>
|
|
744
|
+
<li>list the peers you expect in <code>allow_peer_stream_ids</code></li>
|
|
745
|
+
</ul>
|
|
746
|
+
<p>Or install with <code>--preset secure-full</code> and set <code>VDON_MCP_JOIN_TOKEN_SECRET</code>.</p></div>
|
|
747
|
+
</details>
|
|
748
|
+
<details>
|
|
749
|
+
<summary>Does it open ports or expose my machine?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
750
|
+
<div class="answer"><p>No inbound ports and no tunnel. It isn't a VPN: peers can only exchange what the tools send. There is no shell, file system, or network access between machines.</p></div>
|
|
751
|
+
</details>
|
|
752
|
+
<details>
|
|
753
|
+
<summary>Will it work behind firewalls and NAT?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
754
|
+
<div class="answer"><p>Usually. WebRTC tries a direct path first and falls back to TURN relays. Some strict corporate networks and WSL setups block WebRTC's UDP traffic entirely; TURN helps, but it isn't a guaranteed bypass.</p><p>If WebRTC can't run or connect at all, both agents can switch to <code>transport: "http"</code> and talk through an <a href="guide.html#relay">encrypted relay you host</a>. There's no automatic switching yet.</p></div>
|
|
755
|
+
</details>
|
|
756
|
+
<details>
|
|
757
|
+
<summary>How fast is it, and how big can files be?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
758
|
+
<div class="answer"><p>Direct connections have no server hop, so latency is just the network path between the two peers. Files move in 64 KB chunks (configurable from 1 KB to 256 KB) with a 64 MB default limit you can raise with <code>file_max_bytes</code>. Files of 4 MB or more spool to disk instead of memory.</p></div>
|
|
759
|
+
</details>
|
|
760
|
+
<details>
|
|
761
|
+
<summary>Does a successful send mean the other agent got it?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
762
|
+
<div class="answer"><p>No. <code>ok: true</code> means your side accepted the message for sending. The other agent still has to call <code>vdo_receive</code> and act on it. When it matters, ask for a reply: that's your receipt.</p></div>
|
|
763
|
+
</details>
|
|
764
|
+
<details>
|
|
765
|
+
<summary>How many agents can share a room?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
766
|
+
<div class="answer"><p>VDO.Ninja's guidance is about 80 peers per room. Rooms of 2 to 5 agents are the sweet spot; split bigger groups across rooms.</p></div>
|
|
767
|
+
</details>
|
|
768
|
+
<details>
|
|
769
|
+
<summary>What happens if the connection drops?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
770
|
+
<div class="answer"><p>Sessions send a heartbeat every 15 seconds and reconnect with backoff, from 3 up to 30 seconds. File transfers resume from the last acknowledged chunk, and <code>vdo_state_sync</code> brings shared state back in line.</p></div>
|
|
771
|
+
</details>
|
|
772
|
+
<details>
|
|
773
|
+
<summary>Are messages stored anywhere?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
774
|
+
<div class="answer"><p>Not on a third-party server. Incoming events wait in memory in your local MCP server (up to 2,000 per session) until <code>vdo_receive</code> reads them. Large incoming files can spool to a local folder. Disconnect and the session is gone.</p><p>If you use the HTTP relay, it keeps encrypted events in memory for up to 5 minutes (at most 512 events or 8 MiB per room) so peers can catch up after a blip. Nothing goes to disk, and a restart clears it.</p></div>
|
|
775
|
+
</details>
|
|
776
|
+
<details>
|
|
777
|
+
<summary>Which clients does it work with?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
778
|
+
<div class="answer"><p>Any MCP client that runs local stdio servers: Claude Code, Codex, Cursor, Gemini CLI, OpenCode, and others. For clients that connect to remote MCP servers, there's also <a href="guide.html#mcp-http">MCP over HTTP</a>.</p></div>
|
|
779
|
+
</details>
|
|
780
|
+
<details>
|
|
781
|
+
<summary>Can I talk to an agent by voice, or show it my screen?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
782
|
+
<div class="answer"><p>Not yet. Today it's messages, files, and shared state. Voice, camera, and screen sharing in the same private room are the <a href="#next">planned direction</a>.</p></div>
|
|
783
|
+
</details>
|
|
784
|
+
<details>
|
|
785
|
+
<summary>How is it different from ninja-p2p?<svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-plus"></use></svg></summary>
|
|
786
|
+
<div class="answer"><p><a href="https://github.com/steveseguin/ninja-p2p">ninja-p2p</a> keeps agents in an always-on room with a local inbox. This package is a set of MCP tools an agent calls when it needs them. Same VDO.Ninja transport underneath.</p></div>
|
|
787
|
+
</details>
|
|
788
|
+
</div>
|
|
789
|
+
</div>
|
|
790
|
+
</section>
|
|
791
|
+
|
|
792
|
+
<!-- CTA -->
|
|
793
|
+
<section class="section-tight">
|
|
794
|
+
<div class="wrap">
|
|
795
|
+
<div class="cta">
|
|
796
|
+
<h2 class="h2">Give your agents a private line.</h2>
|
|
797
|
+
<div class="install" role="group" aria-label="Install commands">
|
|
798
|
+
<div class="install-row">
|
|
799
|
+
<code>npm i @vdoninja/mcp</code>
|
|
800
|
+
<button class="copy" type="button" data-copy="npm i @vdoninja/mcp"><svg class="icon-xs" aria-hidden="true"><use href="assets/icons.svg#i-copy"></use></svg><span class="label">Copy</span></button>
|
|
801
|
+
</div>
|
|
802
|
+
<div class="install-row">
|
|
803
|
+
<code>npx vdon-mcp-install</code>
|
|
804
|
+
<button class="copy" type="button" data-copy="npx vdon-mcp-install"><svg class="icon-xs" aria-hidden="true"><use href="assets/icons.svg#i-copy"></use></svg><span class="label">Copy</span></button>
|
|
805
|
+
</div>
|
|
806
|
+
</div>
|
|
807
|
+
<div class="hero-actions">
|
|
808
|
+
<a class="btn btn-dark btn-lg" href="guide.html"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-book"></use></svg>Read the docs</a>
|
|
809
|
+
<a class="btn btn-lg" href="https://github.com/steveseguin/ninjamcp"><svg class="icon-sm" aria-hidden="true"><use href="assets/icons.svg#i-github"></use></svg>Star on GitHub</a>
|
|
810
|
+
</div>
|
|
811
|
+
<small>MIT licensed · Free · Peer to peer · Built on <a class="link" href="https://vdo.ninja">VDO.Ninja</a></small>
|
|
812
|
+
</div>
|
|
813
|
+
</div>
|
|
814
|
+
</section>
|
|
815
|
+
|
|
816
|
+
</main>
|
|
817
|
+
|
|
818
|
+
<footer class="footer">
|
|
819
|
+
<div class="wrap">
|
|
820
|
+
<a class="brand" href="./"><img src="assets/logo.svg" alt="" width="24" height="24"><span>VDO.Ninja <small>MCP</small></span></a>
|
|
821
|
+
<nav aria-label="Footer">
|
|
822
|
+
<a href="guide.html">Docs</a>
|
|
823
|
+
<a href="https://github.com/steveseguin/ninjamcp">GitHub</a>
|
|
824
|
+
<a href="https://www.npmjs.com/package/@vdoninja/mcp">npm</a>
|
|
825
|
+
<a href="https://discord.vdo.ninja">Discord</a>
|
|
826
|
+
<a href="https://vdo.ninja">VDO.Ninja</a>
|
|
827
|
+
</nav>
|
|
828
|
+
<span>MIT · © 2026 Steve Seguin</span>
|
|
829
|
+
</div>
|
|
830
|
+
</footer>
|
|
831
|
+
|
|
832
|
+
<script src="assets/site.js" defer></script>
|
|
833
|
+
</body>
|
|
834
|
+
</html>
|