@remcp/remcp 0.1.1 → 0.1.3

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/public/index.html CHANGED
@@ -8,7 +8,7 @@
8
8
  <title>ReMCP — Your computers over MCP</title>
9
9
  <meta name="description" content="Securely pair your own computers and use their files, terminal and processes through authenticated MCP clients.">
10
10
  <link rel="preconnect" href="https://www.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/style.css?v=remcp-012">
11
+ <link rel="stylesheet" href="/style.css?v=remcp-013">
12
12
  <meta property="og:site_name" content="ReMCP">
13
13
  <meta property="og:image" content="https://remcp.delio24.com/assets/og-remcp.png">
14
14
  <link rel="icon" href="/favicon.ico" sizes="any">
@@ -28,17 +28,17 @@
28
28
  <img class="brand-mark" src="/android-chrome-192x192.png" width="30" height="30" alt="" aria-hidden="true">
29
29
  <span translate="no">ReMCP</span>
30
30
  </a>
31
- <nav aria-label="Primary"><a href="/docs">Docs</a><a href="/docs#install">Install</a><a href="/docs#update">Update</a><a href="/security">Security</a><a href="/support">Support</a></nav>
31
+ <nav aria-label="Primary"><a href="/docs">Docs</a><a href="/security">Security</a><a href="/support">Support</a></nav>
32
32
  </header>
33
33
  <main id="main">
34
34
  <section class="hero">
35
35
  <div class="hero-copy">
36
- <p class="eyebrow">Remote MCP, without exposed device ports</p>
37
- <h1>Your computers.<br>Available to MCP.</h1>
38
- <p class="lede">Pair a device once. ReMCP routes authenticated tool calls to its local agent while the device keeps an outbound-only connection.</p>
36
+ <p class="eyebrow">Remote MCP for your computers</p>
37
+ <h1>Use your computers from any MCP client.</h1>
38
+ <p class="lede">ReMCP pairs a computer to one authenticated MCP endpoint. The local agent connects outbound, so the computer does not need a public port.</p>
39
39
  <div class="hero-actions">
40
- <a class="button" href="#connect">Connect a Device</a>
41
- <a class="text-link" href="/docs">Read Setup Guide <span aria-hidden="true">→</span></a>
40
+ <a class="button" href="#connect">Open workspace</a>
41
+ <a class="text-link" href="/docs">Read docs <span aria-hidden="true">→</span></a>
42
42
  </div>
43
43
  </div>
44
44
  <div class="hero-visual" role="img" aria-label="ReMCP connection diagram">
@@ -50,66 +50,52 @@
50
50
  </div>
51
51
  </section>
52
52
 
53
- <section class="principles" aria-label="Product principles">
54
- <div><span>01</span><h2>Private by default</h2><p>Accounts and devices are isolated by authenticated user identity and revocable device credentials.</p></div>
55
- <div><span>02</span><h2>No inbound laptop port</h2><p>The device agent connects outward to ReMCP. Your laptop does not need a public IP or open listener.</p></div>
56
- <div><span>03</span><h2>Explicit control</h2><p>Read, write and destructive tools declare their behavior so MCP clients can apply the right review flow.</p></div>
53
+ <section class="principles" aria-label="Connection facts">
54
+ <div><span>Connection</span><h2>Outbound only</h2><p>The device agent opens the connection to ReMCP. No inbound device port is required.</p></div>
55
+ <div><span>Authentication</span><h2>OAuth + PKCE</h2><p>MCP clients authorize against your ReMCP account. Device credentials are independent and revocable.</p></div>
56
+ <div><span>Control</span><h2>Your paired devices</h2><p>Tool calls are routed only to computers paired with the signed-in account.</p></div>
57
57
  </section>
58
58
 
59
59
  <section id="install" class="quickstart">
60
60
  <div class="section-heading">
61
- <p class="eyebrow">Quick start</p>
62
- <h2>One command to pair. One command to update.</h2>
63
- <p>Use npx only for the first pairing. <code>--install</code> creates a persistent <code>remcp</code> CLI and background user service.</p>
61
+ <p class="eyebrow">Setup</p>
62
+ <h2>Install first. Pair from the workspace.</h2>
63
+ <p>The public install command contains no pairing code. Sign in below to generate the exact one-time command for your computer.</p>
64
64
  </div>
65
65
  <div class="quickstart-grid">
66
66
  <article class="command-card">
67
- <span>01</span><h3>Install &amp; pair</h3>
68
- <div class="command-row"><code id="install-command">npx --yes @remcp/remcp@latest connect --server https://remcp.delio24.com --code YOUR_CODE --install</code><button class="copy-button" type="button" data-copy="#install-command">Copy</button></div>
69
- <p>Generate <code>YOUR_CODE</code> in the workspace below.</p>
67
+ <span>01</span><h3>Install the CLI</h3>
68
+ <div class="copy-field"><code id="install-command">npm install --global @remcp/remcp@latest</code><button class="copy-icon-button" type="button" data-copy="#install-command" aria-label="Copy install command"><svg class="icon-copy" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3"/></svg><svg class="icon-check" viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12 4 4L19 6"/></svg></button></div>
69
+ <p>Installs the current <code>remcp</code> command globally.</p>
70
70
  </article>
71
71
  <article class="command-card">
72
- <span>02</span><h3>Check</h3>
73
- <div class="command-row"><code id="status-command">remcp status</code><button class="copy-button" type="button" data-copy="#status-command">Copy</button></div>
74
- <p>Confirm the local CLI version, pairing and server health.</p>
72
+ <span>02</span><h3>Pair a computer</h3>
73
+ <p>Sign in to the workspace, choose <strong>Generate pairing command</strong>, then run the generated command on that computer.</p>
75
74
  </article>
76
- <article id="update" class="command-card">
77
- <span>03</span><h3>Update</h3>
78
- <div class="command-row"><code id="update-command">remcp update</code><button class="copy-button" type="button" data-copy="#update-command">Copy</button></div>
79
- <p>Updates the CLI and restarts the installed user service.</p>
75
+ <article class="command-card">
76
+ <span>03</span><h3>Verify</h3>
77
+ <div class="copy-field"><code id="status-command">remcp status</code><button class="copy-icon-button" type="button" data-copy="#status-command" aria-label="Copy status command"><svg class="icon-copy" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3"/></svg><svg class="icon-check" viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12 4 4L19 6"/></svg></button></div>
78
+ <p>Checks the installed version, pairing state and server health.</p>
80
79
  </article>
81
80
  </div>
82
- <a class="text-link" href="/docs">Full installation and CLI guide <span aria-hidden="true">→</span></a>
81
+ <div class="update-line"><span>Already installed?</span><div class="copy-field compact"><code id="update-command">remcp update</code><button class="copy-icon-button" type="button" data-copy="#update-command" aria-label="Copy update command"><svg class="icon-copy" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3"/></svg><svg class="icon-check" viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12 4 4L19 6"/></svg></button></div></div>
82
+ <a class="text-link" href="/docs">Installation and CLI reference <span aria-hidden="true">→</span></a>
83
83
  </section>
84
84
 
85
85
  <section id="connect" class="workspace">
86
86
  <div class="section-heading">
87
87
  <p class="eyebrow">Workspace</p>
88
- <h2>Pair and manage devices</h2>
89
- <p>Sign in to generate one-time pairing commands and revoke computers you no longer trust.</p>
88
+ <h2>Workspace</h2>
89
+ <p>Sign in with Google or GitHub to generate pairing commands and manage your devices.</p>
90
90
  </div>
91
91
  <div class="workspace-grid">
92
92
  <section class="auth-pane" aria-labelledby="account-title">
93
93
  <h3 id="account-title">Account</h3>
94
94
  <div id="signed-out" class="stack">
95
- <p class="muted">Sign in to access your ReMCP device workspace.</p>
96
- <button id="google" class="button google-button" type="button">
97
- <svg class="google-icon" viewBox="0 0 18 18" aria-hidden="true" focusable="false">
98
- <path fill="#4285F4" d="M17.64 9.205c0-.638-.057-1.252-.164-1.841H9v3.481h4.844a4.14 4.14 0 0 1-1.796 2.715v2.259h2.909c1.702-1.567 2.683-3.874 2.683-6.614Z"/>
99
- <path fill="#34A853" d="M9 18c2.43 0 4.468-.806 5.957-2.181l-2.909-2.259c-.806.54-1.835.859-3.048.859-2.344 0-4.328-1.585-5.037-3.714H.956v2.332A9 9 0 0 0 9 18Z"/>
100
- <path fill="#FBBC05" d="M3.963 10.705A5.41 5.41 0 0 1 3.682 9c0-.592.102-1.168.281-1.705V4.963H.956A9 9 0 0 0 0 9c0 1.452.347 2.827.956 4.037l3.007-2.332Z"/>
101
- <path fill="#EA4335" d="M9 3.581c1.321 0 2.507.454 3.441 1.346l2.581-2.581C13.464.892 11.426 0 9 0A9 9 0 0 0 .956 4.963l3.007 2.332C4.672 5.166 6.656 3.581 9 3.581Z"/>
102
- </svg>
103
- <span>Continue with Google</span>
104
- </button>
105
- <div class="divider"><span>or use email</span></div>
106
- <label for="email">Email</label>
107
- <input id="email" name="email" type="email" autocomplete="email" inputmode="email" autocapitalize="none" spellcheck="false">
108
- <label for="password">Password</label>
109
- <input id="password" name="password" type="password" autocomplete="current-password">
110
- <div class="button-row">
111
- <button id="email-login" class="button secondary" type="button">Sign In</button>
112
- <button id="email-register" class="button ghost" type="button">Create Account</button>
95
+ <p class="muted">Sign in to manage your paired computers.</p>
96
+ <div class="provider-stack">
97
+ <button id="google" class="provider-button google-button" type="button"><svg class="provider-icon" viewBox="0 0 18 18" aria-hidden="true"><path fill="#4285F4" d="M17.64 9.205c0-.638-.057-1.252-.164-1.841H9v3.481h4.844a4.14 4.14 0 0 1-1.796 2.715v2.259h2.909c1.702-1.567 2.683-3.874 2.683-6.614Z"/><path fill="#34A853" d="M9 18c2.43 0 4.468-.806 5.957-2.181l-2.909-2.259c-.806.54-1.835.859-3.048.859-2.344 0-4.328-1.585-5.037-3.714H.956v2.332A9 9 0 0 0 9 18Z"/><path fill="#FBBC05" d="M3.963 10.705A5.41 5.41 0 0 1 3.682 9c0-.592.102-1.168.281-1.705V4.963H.956A9 9 0 0 0 0 9c0 1.452.347 2.827.956 4.037l3.007-2.332Z"/><path fill="#EA4335" d="M9 3.581c1.321 0 2.507.454 3.441 1.346l2.581-2.581C13.464.892 11.426 0 9 0A9 9 0 0 0 .956 4.963l3.007 2.332C4.672 5.166 6.656 3.581 9 3.581Z"/></svg><span>Continue with Google</span></button>
98
+ <button id="github" class="provider-button github-button" type="button"><svg class="provider-icon" viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M12 .7a11.5 11.5 0 0 0-3.64 22.41c.58.11.79-.25.79-.56v-2.24c-3.22.7-3.9-1.37-3.9-1.37-.53-1.34-1.29-1.7-1.29-1.7-1.05-.72.08-.71.08-.71 1.16.08 1.77 1.19 1.77 1.19 1.04 1.77 2.72 1.26 3.38.96.1-.75.4-1.26.73-1.55-2.57-.29-5.27-1.29-5.27-5.68 0-1.26.45-2.29 1.19-3.1-.12-.29-.52-1.47.11-3.06 0 0 .97-.31 3.16 1.18a10.9 10.9 0 0 1 5.76 0c2.19-1.49 3.16-1.18 3.16-1.18.63 1.59.23 2.77.11 3.06.74.81 1.19 1.84 1.19 3.1 0 4.4-2.71 5.38-5.29 5.67.42.36.79 1.07.79 2.16v3.21c0 .31.21.68.8.56A11.5 11.5 0 0 0 12 .7Z"/></svg><span>Continue with GitHub</span></button>
113
99
  </div>
114
100
  <p id="auth-error" class="error" role="alert" aria-live="polite"></p>
115
101
  </div>
@@ -120,35 +106,29 @@
120
106
  </section>
121
107
  <section class="endpoint-pane" aria-labelledby="endpoint-title">
122
108
  <div><h3 id="endpoint-title">MCP endpoint</h3><p class="muted">Use this HTTPS endpoint when adding ReMCP to an MCP client.</p></div>
123
- <div class="command-row"><code id="mcp-url">https://remcp.delio24.com/mcp</code><button class="copy-button" type="button" data-copy="#mcp-url" aria-label="Copy MCP endpoint">Copy</button></div>
109
+ <div class="copy-field"><code id="mcp-url">https://remcp.delio24.com/mcp</code><button class="copy-icon-button" type="button" data-copy="#mcp-url" aria-label="Copy MCP endpoint"><svg class="icon-copy" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3"/></svg><svg class="icon-check" viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12 4 4L19 6"/></svg></button></div>
124
110
  <dl class="endpoint-facts"><div><dt>Transport</dt><dd>Streamable HTTP</dd></div><div><dt>Authentication</dt><dd>OAuth 2.1 + PKCE</dd></div></dl>
125
111
  </section>
126
112
  </div>
127
113
  <section id="dashboard" class="hidden device-workspace" aria-label="Device management">
128
114
  <div class="pair-panel">
129
- <div><p class="eyebrow">Add a computer</p><h3>Generate a one-time pairing command</h3><p class="muted">The code expires automatically and can only be claimed once.</p></div>
130
- <button id="pair" class="button" type="button">Generate Command</button>
131
- <div id="pairing" class="hidden pairing-output"><code id="pair-command"></code><button class="copy-button" type="button" data-copy="#pair-command">Copy</button><p id="pair-expiry" class="muted"></p></div>
115
+ <div><p class="eyebrow">Add a computer</p><h3>Generate pairing command</h3><p class="muted">The generated command contains a short-lived one-time code. Run it only on the computer you want to pair.</p></div>
116
+ <button id="pair" class="button" type="button">Generate pairing command</button>
117
+ <div id="pairing" class="hidden pairing-output"><div class="copy-field"><code id="pair-command"></code><button class="copy-icon-button" type="button" data-copy="#pair-command" aria-label="Copy pairing command"><svg class="icon-copy" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3"/></svg><svg class="icon-check" viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12 4 4L19 6"/></svg></button></div><p id="pair-expiry" class="muted"></p></div>
132
118
  </div>
133
119
  <div class="devices-panel"><div class="panel-title"><h3>Paired devices</h3><span class="muted">Online state updates when the page refreshes.</span></div><div id="devices" class="device-list" aria-live="polite">No devices yet.</div></div>
134
120
  </section>
135
121
  </section>
136
-
137
- <section class="final-cta">
138
- <p class="eyebrow">Self-hostable architecture</p>
139
- <h2>Keep the relay yours.</h2>
140
- <p>ReMCP can run as a small Docker service while each computer runs its own local agent.</p>
141
- <a class="text-link" href="/security">Review the Security Model <span aria-hidden="true">→</span></a>
142
- </section>
143
122
  </main>
144
123
  <footer class="site-footer">
145
124
  <div class="wordmark">
146
125
  <img class="brand-mark" src="/android-chrome-192x192.png" width="30" height="30" alt="" aria-hidden="true">
147
126
  <span translate="no">ReMCP</span>
148
127
  </div>
149
- <nav aria-label="Footer"><a href="/docs">Docs</a><a href="/docs#install">Install</a><a href="/docs#update">Update</a><a href="/security">Security</a><a href="/support">Support</a><a href="/privacy">Privacy</a><a href="/terms">Terms</a></nav>
128
+ <nav aria-label="Footer"><a href="/docs">Docs</a><a href="/security">Security</a><a href="/support">Support</a><a href="/privacy">Privacy</a><a href="/terms">Terms</a></nav>
150
129
  <p>Open-source remote MCP infrastructure.</p>
151
130
  </footer>
152
- <script type="module" src="/app.js?v=14a728f07dd1"></script>
131
+ <script type="module" src="/copy.js?v=remcp-013"></script>
132
+ <script type="module" src="/app.js?v=remcp-013"></script>
153
133
  </body>
154
134
  </html>
@@ -1,35 +1,44 @@
1
1
  import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-app.js';
2
- import { getAuth, GoogleAuthProvider, onAuthStateChanged, signInWithPopup, signInWithEmailAndPassword, createUserWithEmailAndPassword } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-auth.js';
2
+ import { getAuth, GoogleAuthProvider, GithubAuthProvider, onAuthStateChanged, signInWithPopup } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-auth.js';
3
3
 
4
4
  const cfg = await fetch(`/api/config?fresh=${Date.now()}`, { cache: 'no-store' }).then(r => r.json());
5
5
  const auth = getAuth(initializeApp(cfg.firebase));
6
6
  const query = new URLSearchParams(location.search);
7
7
  const $ = selector => document.querySelector(selector);
8
- const showError = error => { $('#error').textContent = error?.message || String(error); };
8
+ const googleProvider = new GoogleAuthProvider();
9
+ const githubProvider = new GithubAuthProvider();
10
+ googleProvider.setCustomParameters({ prompt: 'select_account' });
9
11
  let currentUser = null;
10
12
  let completing = false;
11
- const googleProvider = new GoogleAuthProvider();
12
13
 
13
- $('#client-id').textContent = query.get('client_id') || 'MCP client';
14
- $('#google').onclick = async () => {
15
- const button = $('#google');
14
+ const showError = error => {
15
+ const code = error?.code || '';
16
+ if (code === 'auth/popup-closed-by-user' || code === 'auth/cancelled-popup-request') return;
17
+ $('#error').textContent = code === 'auth/account-exists-with-different-credential'
18
+ ? 'This email is already linked to another sign-in provider.'
19
+ : error?.message || String(error);
20
+ };
21
+ async function signIn(button, provider, idleLabel) {
16
22
  const label = button.querySelector('span');
17
23
  button.disabled = true;
18
24
  $('#error').textContent = '';
19
- label.textContent = 'Opening Google…';
20
- try { await signInWithPopup(auth, googleProvider); }
25
+ label.textContent = 'Opening…';
26
+ try { await signInWithPopup(auth, provider); }
21
27
  catch (error) { showError(error); }
22
- finally { button.disabled = false; label.textContent = 'Continue with Google'; }
23
- };
24
- $('#email-login').onclick = () => signInWithEmailAndPassword(auth, $('#email').value, $('#password').value).catch(showError);
25
- $('#email-register').onclick = () => createUserWithEmailAndPassword(auth, $('#email').value, $('#password').value).catch(showError);
28
+ finally { button.disabled = false; label.textContent = idleLabel; }
29
+ }
30
+
31
+ $('#client-id').textContent = query.get('client_id') || 'MCP client';
32
+ $('#google').onclick = () => signIn($('#google'), googleProvider, 'Continue with Google');
33
+ $('#github').onclick = () => signIn($('#github'), githubProvider, 'Continue with GitHub');
26
34
 
27
35
  onAuthStateChanged(auth, user => {
28
36
  currentUser = user;
29
37
  $('#oauth-login').classList.toggle('hidden', Boolean(user));
30
38
  $('#oauth-consent').classList.toggle('hidden', !user);
31
- $('#status').textContent = user ? `Signed in as ${user.email || 'ReMCP user'}. Review access before continuing.` : '';
39
+ $('#status').textContent = user ? `Signed in as ${user.email || user.displayName || 'ReMCP user'}.` : '';
32
40
  });
41
+
33
42
  $('#authorize').onclick = async () => {
34
43
  if (!currentUser || completing) return;
35
44
  completing = true;
@@ -8,7 +8,7 @@
8
8
  <title>Privacy Policy · ReMCP</title>
9
9
  <meta name="description" content="How ReMCP processes account, device, OAuth and remote-computer data.">
10
10
  <link rel="preconnect" href="https://www.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/style.css?v=remcp-012">
11
+ <link rel="stylesheet" href="/style.css?v=remcp-013">
12
12
  <meta property="og:site_name" content="ReMCP">
13
13
  <meta property="og:image" content="https://remcp.delio24.com/assets/og-remcp.png">
14
14
  <link rel="icon" href="/favicon.ico" sizes="any">
@@ -28,23 +28,44 @@
28
28
  <img class="brand-mark" src="/android-chrome-192x192.png" width="30" height="30" alt="" aria-hidden="true">
29
29
  <span translate="no">ReMCP</span>
30
30
  </a>
31
- <nav aria-label="Primary"><a href="/docs">Docs</a><a href="/docs#install">Install</a><a href="/docs#update">Update</a><a href="/security">Security</a><a href="/support">Support</a></nav>
31
+ <nav aria-label="Primary"><a href="/docs">Docs</a><a href="/security">Security</a><a href="/support">Support</a></nav>
32
32
  </header>
33
33
  <main id="main" class="doc-shell">
34
34
  <aside class="doc-nav" aria-label="Documentation"><a href="/docs">Setup</a><a href="/docs#install">Install</a><a href="/docs#update">Update</a><a href="/security">Security</a><a href="/support">Support</a><a href="/privacy" aria-current="page">Privacy</a><a href="/terms">Terms</a></aside>
35
35
  <article class="prose">
36
36
  <p class="eyebrow">Privacy</p>
37
37
  <h1>Privacy Policy</h1>
38
- <p><strong>Effective September 16, 2026.</strong> This policy describes the hosted ReMCP service at remcp.delio24.com. A self-hosted deployment is operated by its own administrator and may have different practices.</p>
39
- <h2>What ReMCP does</h2><p>ReMCP authenticates an MCP client, identifies computers you have paired, and relays authorized tool calls between that client and the selected computer. Device agents connect outbound to the ReMCP service.</p>
40
- <h2>Account and Google Sign-In data</h2><p>Authentication is provided by Firebase Authentication. If you choose Google Sign-In, Firebase may provide ReMCP with your Firebase user identifier, email address and display name. ReMCP uses this information only to authenticate you, keep your devices separated from other accounts, and administer your ReMCP account. ReMCP does not request Gmail, Google Drive, Google Calendar or other Google API data.</p>
41
- <h2>Device and security metadata</h2><p>ReMCP stores device identifiers, device names and basic platform metadata, one-way hashes of device credentials, pairing records, OAuth client metadata, and one-way hashes of refresh credentials. Pairing codes are short-lived and single-use. Raw device credentials are returned to the paired device and are not stored in plaintext by the service.</p>
42
- <h2>Files, commands and tool results</h2><p>When you use an MCP tool, its request and response may pass through the ReMCP relay. This can include filenames, file contents, command output and process information that you explicitly ask the paired computer to process. ReMCP does not intentionally retain those tool payloads as product data. Operational security logs may retain limited metadata such as timestamps, status codes and errors.</p>
43
- <h2>How data is used and shared</h2><p>Data is used to provide authentication, device pairing, OAuth authorization, request routing, abuse prevention, security and reliability. ReMCP does not sell personal information and does not serve advertising. Data may be processed by infrastructure and authentication providers only as needed to operate the service, including Firebase/Google for authentication.</p>
44
- <h2>Retention and control</h2><p>Device records remain until revoked or removed. OAuth grants remain until revoked, expired or rotated. Security and operational metadata may be retained for a limited period appropriate to troubleshooting and abuse prevention. You can revoke a paired device from the ReMCP workspace. Requests concerning account data or deletion can be made through the support channel identified on the Support page or in the app listing.</p>
45
- <h2>Security and international processing</h2><p>ReMCP uses TLS for network transport, hashes reusable credentials before storage, and scopes device access to the authenticated account. Internet services may process data in more than one country depending on infrastructure and authentication providers. See the <a href="/security">Security page</a> for the current security model.</p>
46
- <h2>Children</h2><p>ReMCP is a developer and systems-administration tool and is not directed to children under 13.</p>
47
- <h2>Changes</h2><p>Material changes to this policy will be published at this URL with an updated effective date.</p>
38
+ <p><strong>Effective September 16, 2026.</strong> This policy covers the hosted ReMCP service at <code>remcp.delio24.com</code>. Self-hosted deployments are controlled by their own operators.</p>
39
+
40
+ <h2>Account data</h2>
41
+ <p>Hosted sign-in is provided by Firebase Authentication using Google or GitHub. ReMCP may receive your authentication user id, email address, email-verification status and display name. These data are used to authenticate you and separate your devices and OAuth grants from other accounts.</p>
42
+
43
+ <h2>Google and GitHub</h2>
44
+ <p>ReMCP uses Google and GitHub only as sign-in providers through Firebase Authentication. The hosted ReMCP application does not use Google access to read Gmail, Drive, Calendar or Contacts, and it does not use GitHub provider tokens to read repositories or perform GitHub actions.</p>
45
+
46
+ <h2>Device and OAuth metadata</h2>
47
+ <p>ReMCP stores device identifiers, device names, basic platform metadata, one-way hashes of device credentials, pairing records, OAuth client metadata and one-way hashes of refresh credentials. Raw reusable device credentials are not stored in plaintext by the relay.</p>
48
+
49
+ <h2>Files, commands and tool results</h2>
50
+ <p>When you invoke an MCP tool, the request and result pass through the relay to the selected device. This can include paths, file contents, command output and process information you asked ReMCP to handle. ReMCP does not intentionally retain MCP tool payloads as product data.</p>
51
+
52
+ <h2>Use and sharing</h2>
53
+ <p>Data are used to provide authentication, pairing, OAuth authorization, request routing, abuse prevention, security and reliability. ReMCP does not sell personal information and does not serve advertising. Infrastructure and authentication providers process data only as needed to operate the service.</p>
54
+
55
+ <h2>Retention and control</h2>
56
+ <p>Pairing codes expire after 10 minutes by default. MCP access tokens expire after 1 hour by default. Refresh credentials expire after 30 days by default and rotate on use. Device records remain until revoked or removed. Hosted Nginx access and error logs rotate daily with 14 rotated copies. Application container logs are limited to three files of at most 50 MB each. You can revoke paired devices from the workspace.</p>
57
+
58
+ <h2>Restricted data</h2>
59
+ <p>Do not send passwords, MFA or OTP codes, API/private keys, payment-card data, protected health information or government identifiers through ReMCP tool calls.</p>
60
+
61
+ <h2>Security and international processing</h2>
62
+ <p>ReMCP uses TLS for network transport and one-way hashes for reusable server-side credentials. Internet infrastructure and authentication providers may process data in more than one country. See the <a href="/security">Security page</a> for the current model.</p>
63
+
64
+ <h2>Children</h2>
65
+ <p>ReMCP is a developer and systems-administration tool and is not directed to children under 13.</p>
66
+
67
+ <h2>Changes and requests</h2>
68
+ <p>Material changes are published at this URL with an updated effective date. Account-data or deletion requests can be made through the <a href="/support">Support page</a>.</p>
48
69
  </article>
49
70
  </main>
50
71
  <footer class="site-footer">
@@ -52,7 +73,7 @@
52
73
  <img class="brand-mark" src="/android-chrome-192x192.png" width="30" height="30" alt="" aria-hidden="true">
53
74
  <span translate="no">ReMCP</span>
54
75
  </div>
55
- <nav aria-label="Footer"><a href="/docs">Docs</a><a href="/docs#install">Install</a><a href="/docs#update">Update</a><a href="/security">Security</a><a href="/support">Support</a><a href="/privacy">Privacy</a><a href="/terms">Terms</a></nav>
76
+ <nav aria-label="Footer"><a href="/docs">Docs</a><a href="/security">Security</a><a href="/support">Support</a><a href="/privacy">Privacy</a><a href="/terms">Terms</a></nav>
56
77
  <p>Open-source remote MCP infrastructure.</p>
57
78
  </footer>
58
79
  </body>
@@ -8,7 +8,7 @@
8
8
  <title>Security · ReMCP</title>
9
9
  <meta name="description" content="How ReMCP isolates accounts, devices, credentials and remote MCP tool access.">
10
10
  <link rel="preconnect" href="https://www.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/style.css?v=remcp-012">
11
+ <link rel="stylesheet" href="/style.css?v=remcp-013">
12
12
  <meta property="og:site_name" content="ReMCP">
13
13
  <meta property="og:image" content="https://remcp.delio24.com/assets/og-remcp.png">
14
14
  <link rel="icon" href="/favicon.ico" sizes="any">
@@ -28,21 +28,34 @@
28
28
  <img class="brand-mark" src="/android-chrome-192x192.png" width="30" height="30" alt="" aria-hidden="true">
29
29
  <span translate="no">ReMCP</span>
30
30
  </a>
31
- <nav aria-label="Primary"><a href="/docs">Docs</a><a href="/docs#install">Install</a><a href="/docs#update">Update</a><a href="/security" aria-current="page">Security</a><a href="/support">Support</a></nav>
31
+ <nav aria-label="Primary"><a href="/docs">Docs</a><a href="/security" aria-current="page">Security</a><a href="/support">Support</a></nav>
32
32
  </header>
33
33
  <main id="main" class="doc-shell">
34
34
  <aside class="doc-nav" aria-label="Documentation"><a href="/docs">Setup</a><a href="/docs#install">Install</a><a href="/docs#update">Update</a><a href="/security" aria-current="page">Security</a><a href="/support">Support</a><a href="/privacy">Privacy</a><a href="/terms">Terms</a></aside>
35
35
  <article class="prose">
36
36
  <p class="eyebrow">Security</p>
37
- <h1>A narrow relay between the model and your machine.</h1>
38
- <p>ReMCP treats remote computer control as privileged access. Authentication, device isolation and explicit tool behavior are part of the core design.</p>
39
- <h2>Identity and tenancy</h2><p>User authentication is handled with Firebase Authentication. MCP sessions and paired device records are scoped to the same authenticated user identity.</p>
40
- <h2>Device credentials</h2><p>Pairing codes are short-lived and one-time. Each paired computer receives an independent random credential. Server storage keeps a one-way hash of that credential.</p>
41
- <h2>Network model</h2><p>The local agent opens an outbound secure WebSocket connection to the relay. No inbound port needs to be opened on the laptop or workstation.</p>
42
- <h2>Tool risk</h2><p>Tool descriptors explicitly declare read-only, destructive and open-world hints. Terminal execution can change local or external state depending on the command supplied.</p>
43
- <h2>Revocation</h2><p>Revoke a device from the signed-in workspace if a computer is lost, retired, or no longer trusted. Revoked credentials cannot reconnect.</p>
44
- <h2>Transport</h2><p>Production traffic uses HTTPS/WSS. OAuth uses PKCE S256. Tokens and device credentials are not placed in public source or page markup.</p>
45
- <div class="notice"><strong>Operator trust.</strong><p>If you use a third-party ReMCP deployment, that deployment operator controls the relay infrastructure. For highest control, run your own deployment.</p></div>
37
+ <h1>Security model.</h1>
38
+ <p>ReMCP is remote access infrastructure. Account identity, device credentials and tool risk are kept separate so each layer can be revoked independently.</p>
39
+
40
+ <h2>Account identity</h2>
41
+ <p>The hosted service uses Firebase Authentication with Google and GitHub sign-in. ReMCP does not provide hosted email/password sign-in. MCP authorization is scoped to the authenticated ReMCP account.</p>
42
+
43
+ <h2>Device pairing</h2>
44
+ <p>Pairing commands contain short-lived, single-use codes. A successfully paired computer receives its own random device credential; the server stores only a one-way hash of that credential.</p>
45
+
46
+ <h2>Network model</h2>
47
+ <p>The local agent opens an outbound WSS connection to the relay. The computer does not need a public IP address or inbound listening port.</p>
48
+
49
+ <h2>MCP authorization</h2>
50
+ <p>The public MCP endpoint uses OAuth authorization code flow with PKCE S256. Access tokens are short-lived and refresh credentials rotate on use.</p>
51
+
52
+ <h2>Tool risk</h2>
53
+ <p>MCP tool descriptors declare read-only, destructive and open-world hints. File writes, configuration changes and process execution can change local state; terminal commands can also reach external systems.</p>
54
+
55
+ <h2>Revocation</h2>
56
+ <p>Revoke a paired device from the workspace if it is lost, retired or no longer trusted. Revoked device credentials cannot reconnect.</p>
57
+
58
+ <div class="notice"><strong>Relay operator trust.</strong><p>A hosted relay can observe requests while routing them. For the highest infrastructure control, self-host ReMCP and secure that deployment as privileged infrastructure.</p></div>
46
59
  </article>
47
60
  </main>
48
61
  <footer class="site-footer">
@@ -50,7 +63,7 @@
50
63
  <img class="brand-mark" src="/android-chrome-192x192.png" width="30" height="30" alt="" aria-hidden="true">
51
64
  <span translate="no">ReMCP</span>
52
65
  </div>
53
- <nav aria-label="Footer"><a href="/docs">Docs</a><a href="/docs#install">Install</a><a href="/docs#update">Update</a><a href="/security">Security</a><a href="/support">Support</a><a href="/privacy">Privacy</a><a href="/terms">Terms</a></nav>
66
+ <nav aria-label="Footer"><a href="/docs">Docs</a><a href="/security">Security</a><a href="/support">Support</a><a href="/privacy">Privacy</a><a href="/terms">Terms</a></nav>
54
67
  <p>Open-source remote MCP infrastructure.</p>
55
68
  </footer>
56
69
  </body>
package/public/style.css CHANGED
@@ -46,7 +46,7 @@ img, svg { display: block; }
46
46
  .site-header nav a[aria-current="page"] { font-weight: 680; }
47
47
 
48
48
  main { display: block; }
49
- .hero { max-width: var(--container); min-height: 650px; margin: 0 auto; padding: clamp(72px, 9vw, 112px) 20px; display: grid; grid-template-columns: minmax(0, 1.03fr) minmax(360px, .97fr); align-items: center; gap: clamp(48px, 7vw, 96px); }
49
+ .hero { max-width: var(--container); min-height: 560px; margin: 0 auto; padding: clamp(64px, 8vw, 92px) 20px; display: grid; grid-template-columns: minmax(0, 1.03fr) minmax(360px, .97fr); align-items: center; gap: clamp(48px, 7vw, 96px); }
50
50
  .hero-copy { max-width: 650px; }
51
51
  .eyebrow { margin: 0 0 16px; color: var(--accent); font-size: 12px; font-weight: 760; letter-spacing: .13em; text-transform: uppercase; }
52
52
  .hero h1, .section-heading h2, .final-cta h2, .prose h1 { text-wrap: balance; }
@@ -64,10 +64,14 @@ main { display: block; }
64
64
  .text-link { display: inline-flex; align-items: center; gap: 6px; padding: 7px 0; color: var(--ink); font-weight: 650; border-bottom: 1px solid var(--line-strong); }
65
65
  .text-link:hover { border-color: var(--ink); }
66
66
 
67
- .google-button { position: relative; width: min(100%, 400px); min-height: 44px; padding: 9px 16px 9px 44px; border: 1px solid #747775; border-radius: 4px; background: #fff; color: #1f1f1f; font-family: Roboto, Arial, sans-serif; font-size: 14px; font-weight: 500; letter-spacing: .1px; box-shadow: none; }
68
- .google-button:hover { border-color: #6f7274; background: #f8fafd; color: #1f1f1f; }
69
- .google-button:active { background: #f1f3f4; transform: none; }
70
- .google-icon { position: absolute; left: 12px; top: 50%; width: 20px; height: 20px; transform: translateY(-50%); }
67
+ .provider-stack { width: min(100%, 400px); display: grid; gap: 10px; margin-top: 4px; }
68
+ .provider-button { position: relative; width: 100%; min-height: 46px; display: flex; align-items: center; justify-content: center; padding: 10px 16px 10px 48px; border-radius: 7px; font-size: 14px; font-weight: 600; line-height: 1; text-align: center; box-shadow: none; }
69
+ .provider-button:active { transform: none; }
70
+ .provider-icon { position: absolute; left: 14px; top: 50%; width: 20px; height: 20px; transform: translateY(-50%); }
71
+ .google-button { border: 1px solid #747775; background: #fff; color: #1f1f1f; font-family: Roboto, Arial, sans-serif; font-weight: 500; }
72
+ .google-button:hover { border-color: #5f6368; background: #f8fafd; color: #1f1f1f; }
73
+ .github-button { border: 1px solid #1f2328; background: #24292f; color: #fff; }
74
+ .github-button:hover { border-color: #1f2328; background: #1f2328; color: #fff; }
71
75
 
72
76
  .hero-visual { width: 100%; max-width: 520px; margin-left: auto; }
73
77
  .node { min-height: 62px; display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 17px 18px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
@@ -96,7 +100,6 @@ main { display: block; }
96
100
  .command-card > span { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
97
101
  .command-card h3 { margin: 18px 0 8px; font-size: 20px; letter-spacing: -.025em; }
98
102
  .command-card p { margin: 12px 0 0; color: var(--muted); font-size: 14px; }
99
- .command-card .command-row { margin-top: 14px; }
100
103
  .site-header nav { min-width: 0; overflow-x: auto; scrollbar-width: none; }
101
104
  .site-header nav::-webkit-scrollbar { display: none; }
102
105
 
@@ -112,32 +115,35 @@ main { display: block; }
112
115
  .auth-pane h3, .endpoint-pane h3, .pair-panel h3, .devices-panel h3, .oauth-card h2 { margin: 0 0 9px; font-size: 20px; line-height: 1.25; letter-spacing: -.025em; }
113
116
  .stack { display: flex; flex-direction: column; gap: 10px; }
114
117
  .muted { color: var(--muted); }
115
- label { display: block; margin: 10px 0 6px; color: #343438; font-size: 13px; font-weight: 650; }
116
- .stack label { margin: 3px 0 -4px; }
117
- input { width: 100%; min-height: 44px; padding: 10px 12px; border: 1px solid var(--line-strong); border-radius: 8px; background: var(--surface); color: var(--ink); transition: border-color 140ms ease, box-shadow 140ms ease; }
118
- input:hover { border-color: #b9b9b2; }
119
- input:focus-visible { border-color: var(--accent); }
120
- .button-row { display: flex; gap: 9px; margin-top: 5px; }
121
- .divider { display: flex; align-items: center; gap: 11px; margin: 7px 0; color: var(--muted); font-size: 12px; }
122
- .divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--line); }
123
118
  .error { margin: 2px 0 0; color: var(--danger); font-size: 13px; }
124
119
  p:empty, .error:empty, #status:empty { display: none; }
125
120
  .identity { display: flex; align-items: center; gap: 11px; }
126
121
  .identity small { display: block; color: var(--muted); }
127
122
  .status-dot { width: 9px; height: 9px; flex: 0 0 9px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 4px var(--ok-soft); }
128
123
 
129
- .command-row, .pairing-output { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 9px; align-items: start; margin-top: 20px; }
130
- .command-row code, .pairing-output code, .code-block { overflow: auto; padding: 12px 13px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface-subtle); font: 12.5px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; word-break: break-all; }
131
- .copy-button { min-height: 42px; padding: 9px 12px; border: 1px solid var(--line-strong); border-radius: 8px; background: var(--surface); color: var(--ink); font-weight: 650; }
132
- .copy-button:hover { background: var(--surface-subtle); }
124
+ .copy-field { position: relative; min-width: 0; display: flex; align-items: center; margin-top: 14px; border: 1px solid var(--line-strong); border-radius: 9px; background: var(--surface); overflow: hidden; }
125
+ .copy-field code { min-width: 0; flex: 1; overflow-x: auto; padding: 12px 48px 12px 13px; color: #2e2e33; font: 12.5px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; white-space: pre; scrollbar-width: thin; }
126
+ .copy-field pre { min-width: 0; flex: 1; margin: 0; overflow: auto; }
127
+ .copy-field pre code { display: block; }
128
+ .copy-field.multiline { align-items: stretch; }
129
+ .copy-field.compact { width: min(100%, 300px); margin-top: 0; }
130
+ .copy-icon-button { position: absolute; top: 50%; right: 6px; width: 34px; height: 34px; display: grid; place-items: center; padding: 0; transform: translateY(-50%); border: 0; border-radius: 7px; background: transparent; color: #65656b; }
131
+ .copy-icon-button:hover { background: var(--surface-subtle); color: var(--ink); }
132
+ .copy-icon-button svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
133
+ .copy-icon-button .icon-check { display: none; }
134
+ .copy-icon-button[data-copied="true"] { color: var(--ok); }
135
+ .copy-icon-button[data-copied="true"] .icon-copy { display: none; }
136
+ .copy-icon-button[data-copied="true"] .icon-check { display: block; }
137
+ .pairing-output { grid-column: 1 / -1; margin-top: 4px; }
138
+ .pairing-output .copy-field { width: 100%; }
139
+ .pairing-output p { margin: 8px 0 0; }
140
+ .update-line { display: flex; align-items: center; gap: 14px; margin: 20px 0 24px; color: var(--muted); font-size: 13px; }
133
141
  .endpoint-facts { margin: 28px 0 0; border-top: 1px solid var(--line); }
134
142
  .endpoint-facts div { display: flex; justify-content: space-between; gap: 20px; padding: 12px 0; border-bottom: 1px solid var(--line); }
135
143
  .endpoint-facts dt { color: var(--muted); }
136
144
  .endpoint-facts dd { margin: 0; text-align: right; font-weight: 650; }
137
145
  .device-workspace { margin-top: 52px; }
138
146
  .pair-panel { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 20px; align-items: center; padding-bottom: 34px; border-bottom: 1px solid var(--line); }
139
- .pairing-output { grid-column: 1 / -1; }
140
- .pairing-output p { grid-column: 1 / -1; margin: 0; }
141
147
  .devices-panel { padding-top: 34px; }
142
148
  .panel-title { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; }
143
149
  .device-list { margin-top: 14px; border-top: 1px solid var(--line); }
@@ -168,10 +174,9 @@ p:empty, .error:empty, #status:empty { display: none; }
168
174
  .prose p { margin: 0 0 12px; }
169
175
  .prose a { color: #3538ae; text-decoration: underline; text-decoration-color: #a5a7e9; text-underline-offset: 3px; }
170
176
  .prose a:hover { text-decoration-color: #3538ae; }
171
- .prose pre { min-width: 0; overflow: auto; margin: 0; padding: 13px 14px; background: transparent; color: #f4f4f2; }
172
- .prose .code-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 8px; margin: 12px 0 16px; overflow: hidden; border-radius: 10px; background: #1e1e21; }
173
- .prose .code-row .code-copy { min-height: 34px; margin-right: 8px; padding: 7px 10px; border-color: #4b4b50; background: #2b2b2f; color: #f4f4f2; font-size: 12px; white-space: nowrap; }
174
- .prose .code-row .code-copy:hover { border-color: #66666d; background: #38383d; }
177
+ .prose pre { margin: 0; }
178
+ .prose .copy-field { margin: 12px 0 16px; background: #fbfbfa; }
179
+ .prose .copy-field code { font-size: 12.5px; }
175
180
  .prose code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .9em; }
176
181
  .prose :not(pre) > code { padding: 2px 5px; border-radius: 5px; background: var(--surface-subtle); color: #34343a; }
177
182
  .prose hr { margin: 46px 0; border: 0; border-top: 1px solid var(--line); }
@@ -182,7 +187,7 @@ p:empty, .error:empty, #status:empty { display: none; }
182
187
  .oauth-card { width: min(100%, 520px); padding: 30px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); }
183
188
  .oauth-card .eyebrow { margin-bottom: 11px; }
184
189
  .oauth-card > p:first-child { margin-top: 0; }
185
- .oauth-card .google-button { width: 100%; }
190
+ .oauth-card .provider-stack { width: 100%; margin-top: 20px; }
186
191
  .scope-list { margin: 24px 0; padding: 0; list-style: none; border-top: 1px solid var(--line); }
187
192
  .scope-list li { display: flex; justify-content: space-between; gap: 20px; padding: 13px 0; border-bottom: 1px solid var(--line); }
188
193
  .scope-list strong { color: var(--muted); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; font-weight: 600; }
@@ -229,13 +234,13 @@ input:focus-visible, button:focus-visible, a:focus-visible { outline: 3px solid
229
234
  .section-heading { margin-bottom: 30px; }
230
235
  .section-heading h2, .final-cta h2 { font-size: 36px; }
231
236
  .section-heading > p:last-child, .final-cta > p { margin-top: 12px; font-size: 16px; }
232
- .button-row, .oauth-actions { flex-direction: column; }
233
- .button-row .button, .oauth-actions .button { width: 100%; }
237
+ .oauth-actions { flex-direction: column; }
238
+ .oauth-actions .button { width: 100%; }
239
+ .update-line { align-items: stretch; flex-direction: column; gap: 8px; }
240
+ .copy-field.compact { width: 100%; }
234
241
  .pair-panel { grid-template-columns: 1fr; }
235
242
  .pair-panel .button { justify-self: start; }
236
243
  .panel-title { align-items: flex-start; flex-direction: column; }
237
- .command-row, .pairing-output { grid-template-columns: minmax(0, 1fr) auto; }
238
- .copy-button { justify-self: stretch; }
239
244
  .endpoint-facts div { align-items: flex-start; flex-direction: column; gap: 2px; }
240
245
  .endpoint-facts dd { text-align: left; }
241
246
  .site-footer { padding: 24px 18px; }
@@ -245,9 +250,6 @@ input:focus-visible, button:focus-visible, a:focus-visible { outline: 3px solid
245
250
  .prose h2 { margin-top: 28px; font-size: 21px; }
246
251
  .prose h3 { margin-top: 21px; }
247
252
  .prose p, .prose li { font-size: 15px; }
248
- .prose .code-row { margin: 10px 0 14px; }
249
- .prose .code-row pre { padding: 12px 10px 12px 13px; }
250
- .prose .code-row .code-copy { margin-right: 7px; }
251
253
  .oauth-shell { min-height: calc(100svh - 62px); padding: 28px 14px 60px; place-items: start center; }
252
254
  .oauth-card { padding: 22px 18px; }
253
255
  .scope-list li { align-items: flex-start; flex-direction: column; gap: 4px; }