@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/README.md +85 -42
- package/assets/remcp-icon.png +0 -0
- package/mcp.json +9 -0
- package/package.json +4 -2
- package/plugin.json +37 -0
- package/public/app.js +25 -24
- package/public/assets/art/hero-relay.webp +0 -0
- package/public/assets/art/relay-detail.webp +0 -0
- package/public/authorize.html +10 -21
- package/public/copy.js +9 -18
- package/public/docs.html +31 -32
- package/public/index.html +38 -58
- package/public/oauth-app.js +22 -13
- package/public/privacy.html +34 -13
- package/public/security.html +25 -12
- package/public/style.css +34 -32
- package/public/support.html +30 -14
- package/public/terms.html +31 -14
- package/skills/remcp-operator/SKILL.md +1 -1
- package/src/auth.mjs +1 -0
- package/src/config.mjs +1 -1
- package/src/db.mjs +8 -5
- package/src/mcp.mjs +1 -1
- package/src/oauth.mjs +24 -5
- package/src/util.mjs +1 -1
- package/chatgpt-app-submission.json +0 -356
package/README.md
CHANGED
|
@@ -1,90 +1,116 @@
|
|
|
1
1
|
# ReMCP
|
|
2
2
|
|
|
3
|
-
ReMCP is an open-source remote MCP bridge for computers you control. A
|
|
3
|
+
ReMCP is an open-source remote MCP bridge for computers you control. A lightweight device agent connects **outbound** to a ReMCP relay, so ChatGPT, Codex, or another MCP client can work with local files, directories, searches, terminal sessions, and processes without exposing an inbound port on the computer.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
- Hosted service: <https://remcp.delio24.com>
|
|
6
|
+
- MCP endpoint: `https://remcp.delio24.com/mcp`
|
|
7
|
+
- npm: `@remcp/remcp`
|
|
8
|
+
- License: MIT
|
|
7
9
|
|
|
8
|
-
##
|
|
10
|
+
## Quick start
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
ChatGPT / MCP client ── OAuth + HTTPS ──> ReMCP server
|
|
12
|
-
│
|
|
13
|
-
├── WSS ──> laptop agent ──> local MCP
|
|
14
|
-
└── WSS ──> server agent ──> local MCP
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
The central service stores account/device metadata, hashed reusable credentials and OAuth state. Actual filesystem and process access stays on the paired computer.
|
|
12
|
+
Requires Node.js 22.5 or newer.
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Requires Node.js 22.5 or newer. Generate a one-time pairing code after signing in at https://remcp.delio24.com, then run:
|
|
14
|
+
1. Install the CLI:
|
|
22
15
|
|
|
23
16
|
```bash
|
|
24
|
-
|
|
17
|
+
npm install --global @remcp/remcp@latest
|
|
25
18
|
```
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
2. Open <https://remcp.delio24.com/#connect>, sign in with Google or GitHub, and choose **Generate pairing command**. Run the exact generated command on the computer you want to pair.
|
|
28
21
|
|
|
29
|
-
Verify:
|
|
22
|
+
3. Verify:
|
|
30
23
|
|
|
31
24
|
```bash
|
|
32
25
|
remcp --version
|
|
33
26
|
remcp status
|
|
34
27
|
```
|
|
35
28
|
|
|
29
|
+
4. Add the MCP endpoint to your client:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
https://remcp.delio24.com/mcp
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Authentication uses OAuth authorization code + PKCE with rotating refresh credentials.
|
|
36
|
+
|
|
36
37
|
## Update
|
|
37
38
|
|
|
38
39
|
```bash
|
|
39
40
|
remcp update
|
|
40
41
|
```
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
`remcp update` refreshes the global CLI and compatible local runtime, then restarts the installed user service.
|
|
43
44
|
|
|
44
45
|
## CLI
|
|
45
46
|
|
|
46
47
|
```text
|
|
47
48
|
remcp start Run the device agent in the foreground
|
|
48
|
-
remcp status
|
|
49
|
+
remcp status Show version, pairing and server health
|
|
49
50
|
remcp doctor Alias for status
|
|
50
|
-
remcp update Update ReMCP and restart the
|
|
51
|
+
remcp update Update ReMCP and restart the user service
|
|
51
52
|
remcp install Install or repair the user service
|
|
52
53
|
remcp uninstall Remove the user service
|
|
53
54
|
remcp uninstall --purge Remove the service and global ReMCP packages
|
|
54
55
|
remcp --version Print the installed CLI version
|
|
55
56
|
```
|
|
56
57
|
|
|
57
|
-
##
|
|
58
|
+
## Architecture
|
|
58
59
|
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
```text
|
|
61
|
+
ChatGPT / Codex / MCP client
|
|
62
|
+
│
|
|
63
|
+
│ OAuth 2.1 + HTTPS
|
|
64
|
+
▼
|
|
65
|
+
ReMCP relay /mcp
|
|
66
|
+
│
|
|
67
|
+
│ authenticated WSS (outbound from device)
|
|
68
|
+
▼
|
|
69
|
+
paired computer
|
|
70
|
+
│
|
|
71
|
+
└── local file / search / terminal / process runtime
|
|
65
72
|
```
|
|
66
73
|
|
|
67
|
-
|
|
74
|
+
The hosted relay stores account/device metadata, one-way hashes of reusable credentials, OAuth client metadata, and refresh-token hashes. Device credentials are independently revocable. MCP sessions are scoped to the authenticated ReMCP account.
|
|
75
|
+
|
|
76
|
+
## OpenAI Plugin package
|
|
68
77
|
|
|
69
|
-
|
|
78
|
+
The repository is prepared for the current OpenAI Plugins format:
|
|
70
79
|
|
|
71
|
-
|
|
80
|
+
- `plugin.json` — portable Agent Plugins manifest;
|
|
81
|
+
- `mcp.json` — production Streamable HTTP MCP configuration;
|
|
82
|
+
- `skills/remcp-operator/SKILL.md` — bundled operator skill;
|
|
83
|
+
- `assets/remcp-icon.png` — plugin icon;
|
|
84
|
+
- `docs/OPENAI_PLUGIN.md` — submission and reviewer checklist;
|
|
85
|
+
- `submission/openai-plugin-review.json` — generated tool annotations plus five positive and three negative review cases.
|
|
72
86
|
|
|
73
|
-
ReMCP
|
|
87
|
+
ReMCP has no plugin UI, so the public submission should not include UI screenshots. The remote MCP server uses a universal production URL and OAuth. See [`docs/OPENAI_PLUGIN.md`](docs/OPENAI_PLUGIN.md) before submitting.
|
|
74
88
|
|
|
75
|
-
The repository includes `chatgpt-app-submission.json` plus a reusable ReMCP operator skill for submission/testing workflows.
|
|
76
89
|
## Security model
|
|
77
90
|
|
|
78
|
-
-
|
|
79
|
-
-
|
|
91
|
+
- no inbound port is required on paired computers;
|
|
92
|
+
- one revocable credential is issued per paired device;
|
|
93
|
+
- reusable credentials are hashed server-side;
|
|
80
94
|
- pairing codes are short-lived and single-use;
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
- the public review
|
|
85
|
-
-
|
|
95
|
+
- OAuth uses PKCE and rotating refresh credentials;
|
|
96
|
+
- OAuth metadata advertises `openid` and `email`, with a UserInfo endpoint for verified-email workspace restrictions;
|
|
97
|
+
- MCP tools carry explicit `readOnlyHint`, `openWorldHint`, and `destructiveHint` annotations;
|
|
98
|
+
- the public review fixture is isolated per authenticated account and intentionally restricts terminal commands;
|
|
99
|
+
- the supplied server container runs read-only with dropped Linux capabilities.
|
|
86
100
|
|
|
87
|
-
See https://remcp.delio24.com/security
|
|
101
|
+
ReMCP tools should not be used to request or process passwords, MFA codes, API/private keys, payment-card data, protected health information, or government identifiers. See [`SECURITY.md`](SECURITY.md) and the hosted <https://remcp.delio24.com/security> page.
|
|
102
|
+
|
|
103
|
+
## Self-hosting
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
git clone https://github.com/antonbaider/remcp.git
|
|
107
|
+
cd remcp
|
|
108
|
+
cp .env.example .env
|
|
109
|
+
# Fill in your public URL and Firebase Web App configuration.
|
|
110
|
+
docker compose up -d --build
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Put a TLS reverse proxy in front of ReMCP and proxy WebSocket upgrades for `/agent`. The supplied Compose configuration binds the application to localhost by default. ReMCP verifies Firebase ID tokens using Google's public signing keys and does not require a Firebase Admin private key.
|
|
88
114
|
|
|
89
115
|
## Development
|
|
90
116
|
|
|
@@ -93,8 +119,25 @@ npm ci
|
|
|
93
119
|
npm run check
|
|
94
120
|
npm test
|
|
95
121
|
npm audit --omit=dev
|
|
122
|
+
npm pack --dry-run
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
For server changes:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
docker build -t remcp:test .
|
|
96
129
|
```
|
|
97
130
|
|
|
131
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for contribution and security rules.
|
|
132
|
+
|
|
133
|
+
## Project docs
|
|
134
|
+
|
|
135
|
+
- [`docs/OPENAI_PLUGIN.md`](docs/OPENAI_PLUGIN.md) — OpenAI Plugins submission
|
|
136
|
+
- [`SECURITY.md`](SECURITY.md) — vulnerability reporting and operational security
|
|
137
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — development checks
|
|
138
|
+
- [`CHANGELOG.md`](CHANGELOG.md) — release notes
|
|
139
|
+
- [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md) — third-party license notices
|
|
140
|
+
|
|
98
141
|
## License
|
|
99
142
|
|
|
100
|
-
ReMCP is MIT licensed. Third-party notices for the local device runtime are preserved in `THIRD_PARTY_NOTICES.md`.
|
|
143
|
+
ReMCP is MIT licensed. Third-party notices for the local device runtime are preserved in `THIRD_PARTY_NOTICES.md`.
|
|
Binary file
|
package/mcp.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remcp/remcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Self-hosted remote MCP bridge for securely controlling your own computers from MCP clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
"bin",
|
|
16
16
|
"src",
|
|
17
17
|
"public",
|
|
18
|
+
"plugin.json",
|
|
19
|
+
"mcp.json",
|
|
20
|
+
"assets",
|
|
18
21
|
"skills/remcp-operator",
|
|
19
|
-
"chatgpt-app-submission.json",
|
|
20
22
|
"LICENSE",
|
|
21
23
|
"THIRD_PARTY_NOTICES.md",
|
|
22
24
|
"README.md"
|
package/plugin.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "remcp",
|
|
4
|
+
"version": "0.1.3",
|
|
5
|
+
"description": "Securely use files, directories, searches, terminal sessions, and processes on computers you have paired with ReMCP.",
|
|
6
|
+
"homepage": "https://remcp.delio24.com",
|
|
7
|
+
"repository": "https://github.com/antonbaider/remcp",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"mcp",
|
|
11
|
+
"remote-computer",
|
|
12
|
+
"developer-tools",
|
|
13
|
+
"files",
|
|
14
|
+
"terminal"
|
|
15
|
+
],
|
|
16
|
+
"extensions": {
|
|
17
|
+
"com.openai": {
|
|
18
|
+
"interface": {
|
|
19
|
+
"displayName": "ReMCP",
|
|
20
|
+
"shortDescription": "Use your paired computers through MCP.",
|
|
21
|
+
"longDescription": "ReMCP securely connects ChatGPT and Codex to computers you have explicitly paired. Inspect files and directories, search local content, and manage terminal sessions and processes through an authenticated remote MCP endpoint.",
|
|
22
|
+
"category": "Developer Tools",
|
|
23
|
+
"websiteURL": "https://remcp.delio24.com",
|
|
24
|
+
"privacyPolicyURL": "https://remcp.delio24.com/privacy",
|
|
25
|
+
"termsOfServiceURL": "https://remcp.delio24.com/terms",
|
|
26
|
+
"defaultPrompt": [
|
|
27
|
+
"Show my paired ReMCP computers and tell me which ones are online.",
|
|
28
|
+
"On my laptop, list the files in my project folder and summarize what is there without changing anything.",
|
|
29
|
+
"On my server, list the running processes and help me identify the one using the most memory."
|
|
30
|
+
],
|
|
31
|
+
"brandColor": "#4C50D8",
|
|
32
|
+
"composerIcon": "./assets/remcp-icon.png",
|
|
33
|
+
"logo": "./assets/remcp-icon.png"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
package/public/app.js
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-app.js';
|
|
2
|
-
import { getAuth, GoogleAuthProvider, onAuthStateChanged, signInWithPopup,
|
|
2
|
+
import { getAuth, GoogleAuthProvider, GithubAuthProvider, onAuthStateChanged, signInWithPopup, signOut } 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 $ = selector => document.querySelector(selector);
|
|
7
|
-
const showError = error => { $('#auth-error').textContent = error?.message || String(error); };
|
|
8
7
|
const googleProvider = new GoogleAuthProvider();
|
|
8
|
+
const githubProvider = new GithubAuthProvider();
|
|
9
|
+
googleProvider.setCustomParameters({ prompt: 'select_account' });
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const friendlyAuthError = error => {
|
|
12
|
+
const code = error?.code || '';
|
|
13
|
+
if (code === 'auth/popup-closed-by-user' || code === 'auth/cancelled-popup-request') return '';
|
|
14
|
+
if (code === 'auth/account-exists-with-different-credential') return 'This email is already linked to another sign-in provider.';
|
|
15
|
+
if (code === 'auth/operation-not-allowed') return 'This sign-in provider is not enabled.';
|
|
16
|
+
return error?.message || String(error);
|
|
17
|
+
};
|
|
18
|
+
const showError = error => { $('#auth-error').textContent = friendlyAuthError(error); };
|
|
19
|
+
|
|
20
|
+
async function signIn(button, provider, idleLabel) {
|
|
13
21
|
const label = button.querySelector('span');
|
|
14
22
|
button.disabled = true;
|
|
15
23
|
$('#auth-error').textContent = '';
|
|
16
|
-
label.textContent = 'Opening
|
|
17
|
-
try { await signInWithPopup(auth,
|
|
24
|
+
label.textContent = 'Opening…';
|
|
25
|
+
try { await signInWithPopup(auth, provider); }
|
|
18
26
|
catch (error) { showError(error); }
|
|
19
|
-
finally { button.disabled = false; label.textContent =
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
$('#
|
|
27
|
+
finally { button.disabled = false; label.textContent = idleLabel; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
$('#mcp-url').textContent = cfg.mcpUrl;
|
|
31
|
+
$('#google').onclick = () => signIn($('#google'), googleProvider, 'Continue with Google');
|
|
32
|
+
$('#github').onclick = () => signIn($('#github'), githubProvider, 'Continue with GitHub');
|
|
23
33
|
$('#logout').onclick = () => signOut(auth);
|
|
24
34
|
|
|
25
|
-
document.addEventListener('click', async event => {
|
|
26
|
-
const button = event.target.closest('[data-copy]');
|
|
27
|
-
if (!button) return;
|
|
28
|
-
const target = document.querySelector(button.dataset.copy);
|
|
29
|
-
if (!target) return;
|
|
30
|
-
await navigator.clipboard.writeText(target.textContent.trim());
|
|
31
|
-
const original = button.textContent;
|
|
32
|
-
button.textContent = 'Copied';
|
|
33
|
-
setTimeout(() => { button.textContent = original; }, 1200);
|
|
34
|
-
});
|
|
35
35
|
async function api(path, options = {}) {
|
|
36
36
|
const token = await auth.currentUser.getIdToken();
|
|
37
37
|
const response = await fetch(path, { ...options, headers: { ...(options.headers || {}), authorization: `Bearer ${token}`, 'content-type': 'application/json' } });
|
|
@@ -70,16 +70,17 @@ async function refreshDevices() {
|
|
|
70
70
|
const data = await api('/api/me');
|
|
71
71
|
const root = $('#devices');
|
|
72
72
|
root.replaceChildren();
|
|
73
|
-
if (!data.devices.length) { root.textContent = 'No devices
|
|
73
|
+
if (!data.devices.length) { root.textContent = 'No paired devices.'; return; }
|
|
74
74
|
for (const device of data.devices) root.append(deviceRow(device));
|
|
75
75
|
}
|
|
76
|
+
|
|
76
77
|
$('#pair').onclick = async () => {
|
|
77
78
|
try {
|
|
78
79
|
const pairing = await api('/api/pair/create', { method: 'POST', body: '{}' });
|
|
79
80
|
$('#pairing').classList.remove('hidden');
|
|
80
81
|
$('#pair-command').textContent = pairing.command;
|
|
81
82
|
const expiry = new Intl.DateTimeFormat(undefined, { hour: 'numeric', minute: '2-digit' }).format(new Date(pairing.expiresAt));
|
|
82
|
-
$('#pair-expiry').textContent = `
|
|
83
|
+
$('#pair-expiry').textContent = `One-time command · expires ${expiry}`;
|
|
83
84
|
} catch (error) { showError(error); }
|
|
84
85
|
};
|
|
85
86
|
|
|
@@ -90,6 +91,6 @@ onAuthStateChanged(auth, async user => {
|
|
|
90
91
|
$('#dashboard').classList.toggle('hidden', !signedIn);
|
|
91
92
|
$('#auth-error').textContent = '';
|
|
92
93
|
if (!signedIn) return;
|
|
93
|
-
$('#user-label').textContent = user.email || user.uid;
|
|
94
|
+
$('#user-label').textContent = user.email || user.displayName || user.uid;
|
|
94
95
|
try { await refreshDevices(); } catch (error) { showError(error); }
|
|
95
96
|
});
|
|
Binary file
|
|
Binary file
|
package/public/authorize.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Authorize · ReMCP</title>
|
|
9
9
|
<meta name="description" content="Authorize an MCP client to access computers paired to your ReMCP account.">
|
|
10
10
|
<link rel="preconnect" href="https://www.gstatic.com" crossorigin>
|
|
11
|
-
<link rel="stylesheet" href="/style.css?v=remcp-
|
|
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,43 +28,32 @@
|
|
|
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="/
|
|
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="oauth-main" class="oauth-shell">
|
|
34
34
|
<section class="oauth-card">
|
|
35
35
|
<div id="oauth-login">
|
|
36
36
|
<p class="eyebrow">Secure authorization</p>
|
|
37
37
|
<h2 id="oauth-title">Sign in to ReMCP</h2>
|
|
38
|
-
<p class="muted">
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<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"/>
|
|
44
|
-
<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"/>
|
|
45
|
-
</svg>
|
|
46
|
-
<span>Continue with Google</span>
|
|
47
|
-
</button>
|
|
48
|
-
<div class="divider"><span>or use email</span></div>
|
|
49
|
-
<label for="email">Email</label>
|
|
50
|
-
<input id="email" name="email" type="email" autocomplete="email" inputmode="email" autocapitalize="none" spellcheck="false">
|
|
51
|
-
<label for="password">Password</label>
|
|
52
|
-
<input id="password" name="password" type="password" autocomplete="current-password">
|
|
53
|
-
<div class="button-row"><button id="email-login" class="button secondary" type="button">Sign In</button><button id="email-register" class="button ghost" type="button">Create Account</button></div>
|
|
38
|
+
<p class="muted">Continue with Google or GitHub, then review the MCP client’s access request.</p>
|
|
39
|
+
<div class="provider-stack">
|
|
40
|
+
<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>
|
|
41
|
+
<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>
|
|
42
|
+
</div>
|
|
54
43
|
</div>
|
|
55
44
|
<div id="oauth-consent" class="hidden">
|
|
56
45
|
<p class="eyebrow">Access request</p>
|
|
57
46
|
<h2>Authorize ReMCP Access</h2>
|
|
58
47
|
<p class="muted">This MCP client is requesting access to operate computers paired to this ReMCP account.</p>
|
|
59
|
-
<ul class="scope-list"><li><span>Control paired devices</span><strong>remcp:control</strong></li><li><span>Maintain the authorized connection</span><strong>offline_access</strong></li></ul>
|
|
48
|
+
<ul class="scope-list"><li><span>Identify your ReMCP account</span><strong>openid</strong></li><li><span>Share your verified email for workspace access rules</span><strong>email</strong></li><li><span>Control paired devices</span><strong>remcp:control</strong></li><li><span>Maintain the authorized connection</span><strong>offline_access</strong></li></ul>
|
|
60
49
|
<p class="oauth-meta">Client: <span id="client-id"></span></p>
|
|
61
50
|
<div class="oauth-actions"><button id="authorize" class="button" type="button">Authorize ReMCP Access</button><a class="button ghost" href="/">Not Now</a></div>
|
|
62
51
|
</div>
|
|
63
52
|
<p id="status" class="muted" aria-live="polite"></p>
|
|
64
53
|
<p id="error" class="error" role="alert" aria-live="assertive"></p>
|
|
65
|
-
<p class="footer-mini"><a href="/docs">Docs</a> · <a href="/
|
|
54
|
+
<p class="footer-mini"><a href="/docs">Docs</a> · <a href="/security">Security</a> · <a href="/privacy">Privacy</a> · <a href="/terms">Terms</a></p>
|
|
66
55
|
</section>
|
|
67
56
|
</main>
|
|
68
|
-
<script type="module" src="/oauth-app.js?v=
|
|
57
|
+
<script type="module" src="/oauth-app.js?v=remcp-013"></script>
|
|
69
58
|
</body>
|
|
70
59
|
</html>
|
package/public/copy.js
CHANGED
|
@@ -14,13 +14,7 @@ function legacyCopy(text) {
|
|
|
14
14
|
|
|
15
15
|
async function copyText(text) {
|
|
16
16
|
if (window.isSecureContext && navigator.clipboard?.writeText) {
|
|
17
|
-
try {
|
|
18
|
-
await navigator.clipboard.writeText(text);
|
|
19
|
-
return;
|
|
20
|
-
} catch {
|
|
21
|
-
legacyCopy(text);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
17
|
+
try { await navigator.clipboard.writeText(text); return; } catch {}
|
|
24
18
|
}
|
|
25
19
|
legacyCopy(text);
|
|
26
20
|
}
|
|
@@ -30,20 +24,17 @@ document.addEventListener('click', async event => {
|
|
|
30
24
|
if (!button) return;
|
|
31
25
|
const target = document.querySelector(button.dataset.copy);
|
|
32
26
|
if (!target) return;
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const originalLabel = button.getAttribute('aria-label') || 'Copy command';
|
|
27
|
+
const text = ('value' in target ? target.value : target.textContent).trim();
|
|
28
|
+
const label = button.getAttribute('aria-label') || 'Copy';
|
|
36
29
|
try {
|
|
37
|
-
await copyText(
|
|
38
|
-
button.
|
|
39
|
-
button.setAttribute('aria-label', 'Copied
|
|
30
|
+
await copyText(text);
|
|
31
|
+
button.dataset.copied = 'true';
|
|
32
|
+
button.setAttribute('aria-label', 'Copied');
|
|
40
33
|
} catch {
|
|
41
|
-
button.
|
|
42
|
-
button.setAttribute('aria-label', 'Copy failed. Select the command manually.');
|
|
34
|
+
button.setAttribute('aria-label', 'Copy failed');
|
|
43
35
|
}
|
|
44
|
-
|
|
45
36
|
window.setTimeout(() => {
|
|
46
|
-
button.
|
|
47
|
-
button.setAttribute('aria-label',
|
|
37
|
+
delete button.dataset.copied;
|
|
38
|
+
button.setAttribute('aria-label', label);
|
|
48
39
|
}, 1400);
|
|
49
40
|
});
|
package/public/docs.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Docs · ReMCP</title>
|
|
9
9
|
<meta name="description" content="Connect a computer to ReMCP and configure the hosted MCP endpoint.">
|
|
10
10
|
<link rel="preconnect" href="https://www.gstatic.com" crossorigin>
|
|
11
|
-
<link rel="stylesheet" href="/style.css?v=remcp-
|
|
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,57 +28,56 @@
|
|
|
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" aria-current="page">Docs</a><a href="/
|
|
31
|
+
<nav aria-label="Primary"><a href="/docs" aria-current="page">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" aria-current="page">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">Privacy</a><a href="/terms">Terms</a></aside>
|
|
35
35
|
<article class="prose">
|
|
36
36
|
<p class="eyebrow">Documentation</p>
|
|
37
|
-
<h1>Install
|
|
38
|
-
<p>
|
|
37
|
+
<h1>Install and pair ReMCP.</h1>
|
|
38
|
+
<p>Install the CLI first. Pairing happens from the signed-in workspace, which generates the real one-time command for your computer.</p>
|
|
39
39
|
|
|
40
|
-
<h2 id="requirements">
|
|
41
|
-
<p>
|
|
40
|
+
<h2 id="requirements">Requirements</h2>
|
|
41
|
+
<p>Node.js 22.5 or newer. The computer needs outbound HTTPS/WSS access to <code>remcp.delio24.com</code>. No inbound device port is required.</p>
|
|
42
42
|
|
|
43
|
-
<h2 id="
|
|
44
|
-
<
|
|
43
|
+
<h2 id="install">1. Install the CLI</h2>
|
|
44
|
+
<div class="copy-field"><code id="docs-install">npm install --global @remcp/remcp@latest</code><button class="copy-icon-button" type="button" data-copy="#docs-install" 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>
|
|
45
|
+
<p>This installs the <code>remcp</code> command. It does not pair a computer and does not contain a placeholder code.</p>
|
|
45
46
|
|
|
46
|
-
<h2 id="
|
|
47
|
-
<
|
|
48
|
-
<
|
|
47
|
+
<h2 id="pair">2. Pair a computer</h2>
|
|
48
|
+
<p>Open the <a href="/#connect">ReMCP workspace</a>, continue with Google or GitHub, and choose <strong>Generate pairing command</strong>. Run that exact generated command on the computer you want to pair.</p>
|
|
49
|
+
<div class="notice"><strong>Use the generated command.</strong><p>Pairing codes are short-lived and single-use. Do not type a made-up code or reuse an expired command.</p></div>
|
|
49
50
|
|
|
50
|
-
<
|
|
51
|
-
<div class="
|
|
52
|
-
remcp status</code></pre><button class="copy-button
|
|
53
|
-
<p><code>status</code> shows the installed CLI version,
|
|
51
|
+
<h2 id="verify">3. Verify</h2>
|
|
52
|
+
<div class="copy-field multiline"><pre><code id="docs-verify">remcp --version
|
|
53
|
+
remcp status</code></pre><button class="copy-icon-button" type="button" data-copy="#docs-verify" aria-label="Copy verification commands"><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>
|
|
54
|
+
<p><code>status</code> shows the installed CLI version, pairing state and server health.</p>
|
|
54
55
|
|
|
55
56
|
<h2 id="mcp">4. Add the MCP endpoint</h2>
|
|
56
|
-
<div class="
|
|
57
|
-
<p>
|
|
57
|
+
<div class="copy-field"><code id="docs-mcp">https://remcp.delio24.com/mcp</code><button class="copy-icon-button" type="button" data-copy="#docs-mcp" 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>
|
|
58
|
+
<p>Add this endpoint to your MCP client and use OAuth when prompted. ReMCP uses authorization code flow with PKCE and refresh-token rotation.</p>
|
|
58
59
|
|
|
59
|
-
<h2 id="update">Update
|
|
60
|
-
<div class="
|
|
61
|
-
<p>
|
|
60
|
+
<h2 id="update">Update</h2>
|
|
61
|
+
<div class="copy-field"><code id="docs-update">remcp update</code><button class="copy-icon-button" type="button" data-copy="#docs-update" 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>
|
|
62
|
+
<p>Updates the installed ReMCP CLI and compatible local runtime, then restarts the user service when present.</p>
|
|
62
63
|
|
|
63
64
|
<h2 id="service">Service commands</h2>
|
|
64
|
-
<div class="
|
|
65
|
+
<div class="copy-field multiline"><pre><code id="docs-service">remcp status
|
|
65
66
|
remcp doctor
|
|
66
67
|
remcp install
|
|
67
|
-
remcp start</code></pre><button class="copy-button
|
|
68
|
-
<p><code>
|
|
68
|
+
remcp start</code></pre><button class="copy-icon-button" type="button" data-copy="#docs-service" aria-label="Copy service commands"><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><code>install</code> installs or repairs the background user service for an already paired computer. <code>start</code> runs the agent in the foreground.</p>
|
|
69
70
|
|
|
70
71
|
<h2 id="uninstall">Uninstall</h2>
|
|
71
|
-
<div class="
|
|
72
|
-
<p>
|
|
73
|
-
<div class="
|
|
74
|
-
<p><code>--purge</code> also removes the globally installed ReMCP packages. The local pairing configuration remains in <code>~/.config/remcp</code> so it is not silently destroyed.</p>
|
|
72
|
+
<div class="copy-field"><code id="docs-uninstall">remcp uninstall</code><button class="copy-icon-button" type="button" data-copy="#docs-uninstall" aria-label="Copy uninstall 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>
|
|
73
|
+
<p>Removes the background service. To also remove globally installed ReMCP packages:</p>
|
|
74
|
+
<div class="copy-field"><code id="docs-purge">remcp uninstall --purge</code><button class="copy-icon-button" type="button" data-copy="#docs-purge" aria-label="Copy purge 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>
|
|
75
75
|
|
|
76
76
|
<h2 id="connection-model">Connection model</h2>
|
|
77
|
-
<p>The device agent
|
|
78
|
-
<div class="notice"><strong>Review mutating actions.</strong><p>Some tools can write files or run processes. Review the action requested by your MCP client before approving it.</p></div>
|
|
77
|
+
<p>The device agent opens an outbound secure WebSocket connection to the relay. MCP clients connect to the HTTPS MCP endpoint through OAuth. Only devices paired to the signed-in account are exposed to that account.</p>
|
|
79
78
|
|
|
80
79
|
<h2 id="self-hosting">Self-hosting</h2>
|
|
81
|
-
<p>The
|
|
80
|
+
<p>The server can be self-hosted with Docker. Keep it behind TLS, persist its data volume, and proxy WebSocket upgrades for the device-agent route. See the repository README for deployment variables.</p>
|
|
82
81
|
</article>
|
|
83
82
|
</main>
|
|
84
83
|
<footer class="site-footer">
|
|
@@ -86,9 +85,9 @@ remcp start</code></pre><button class="copy-button code-copy" type="button" data
|
|
|
86
85
|
<img class="brand-mark" src="/android-chrome-192x192.png" width="30" height="30" alt="" aria-hidden="true">
|
|
87
86
|
<span translate="no">ReMCP</span>
|
|
88
87
|
</div>
|
|
89
|
-
<nav aria-label="Footer"><a href="/docs">Docs</a><a href="/
|
|
88
|
+
<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>
|
|
90
89
|
<p>Open-source remote MCP infrastructure.</p>
|
|
91
90
|
</footer>
|
|
92
|
-
<script type="module" src="/copy.js?v=remcp-
|
|
91
|
+
<script type="module" src="/copy.js?v=remcp-013"></script>
|
|
93
92
|
</body>
|
|
94
93
|
</html>
|