@silver886/mcp-proxy 0.1.1 → 0.1.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 +178 -178
- package/mcp/dist/proxy.js +61 -9
- package/mcp/dist/shared/generated.d.ts +1 -1
- package/mcp/dist/shared/generated.js +1 -1
- package/mcp/dist/shared/protocol.d.ts +4 -0
- package/mcp/dist/shared/protocol.js +5 -1
- package/package.json +14 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Leonard Liu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,178 +1,178 @@
|
|
|
1
|
-
# MCP Proxy
|
|
2
|
-
|
|
3
|
-
MCP proxy bridge that forwards [Model Context Protocol](https://modelcontextprotocol.io/) requests across network boundaries via [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/).
|
|
4
|
-
|
|
5
|
-
Works with any MCP client (Claude Code, Cursor, Windsurf, Cline, etc.) and any OS.
|
|
6
|
-
|
|
7
|
-
## Why
|
|
8
|
-
|
|
9
|
-
MCP servers that need local resources (Chrome browser, filesystem, GPU, etc.) can't run inside containers or remote environments. This proxy bridges the gap:
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
MCP Client (container/remote)
|
|
13
|
-
| stdio
|
|
14
|
-
Proxy Server (same machine as client)
|
|
15
|
-
| HTTP via Cloudflare Tunnel
|
|
16
|
-
Host Agent (machine with the resources)
|
|
17
|
-
| stdio
|
|
18
|
-
Real MCP Servers (chrome-devtools, filesystem, etc.)
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Quick start
|
|
22
|
-
|
|
23
|
-
### 1. Start the host agent
|
|
24
|
-
|
|
25
|
-
On the machine where your MCP servers run:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npx -p @silver886/mcp-proxy host --config config.json --tunnel
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Example `config.json`:
|
|
32
|
-
|
|
33
|
-
```json
|
|
34
|
-
{
|
|
35
|
-
"servers": {
|
|
36
|
-
"chrome-devtools": {
|
|
37
|
-
"command": "npx",
|
|
38
|
-
"args": ["-y", "chrome-devtools-mcp@latest"],
|
|
39
|
-
"shell": true
|
|
40
|
-
},
|
|
41
|
-
"filesystem": {
|
|
42
|
-
"command": "npx",
|
|
43
|
-
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
|
|
44
|
-
"shell": true
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
The host agent prints a tunnel URL and auth token. Keep it running.
|
|
51
|
-
|
|
52
|
-
### 2. Configure your MCP client
|
|
53
|
-
|
|
54
|
-
Add the proxy as a stdio MCP server. The client launches it automatically.
|
|
55
|
-
|
|
56
|
-
**Claude Code** (`claude mcp add` or `.claude.json`):
|
|
57
|
-
|
|
58
|
-
```json
|
|
59
|
-
{
|
|
60
|
-
"mcpServers": {
|
|
61
|
-
"chrome-devtools": {
|
|
62
|
-
"type": "stdio",
|
|
63
|
-
"command": "npx",
|
|
64
|
-
"args": ["-y", "-p", "@silver886/mcp-proxy", "proxy"]
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**Cursor / Windsurf / other MCP clients** — same pattern, add as a stdio server with `npx -p @silver886/mcp-proxy proxy` as the command.
|
|
71
|
-
|
|
72
|
-
### 3. Pair
|
|
73
|
-
|
|
74
|
-
When the MCP client spawns the proxy, the proxy prints a setup URL to stderr:
|
|
75
|
-
|
|
76
|
-
```
|
|
77
|
-
Configure at: https://mcp-proxy.pages.dev/setup.html#code=...&key=...
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Open the URL in a browser. Enter the tunnel URL and auth token from step 1, discover servers, and select tools. The proxy picks up the config automatically and starts forwarding MCP requests.
|
|
81
|
-
|
|
82
|
-
## Architecture
|
|
83
|
-
|
|
84
|
-
### Components
|
|
85
|
-
|
|
86
|
-
| Component | Role | Runs on |
|
|
87
|
-
|-----------|------|---------|
|
|
88
|
-
| **Host Agent** (`host`) | HTTP-to-stdio bridge. Spawns MCP servers, manages sessions, serves MCP Streamable HTTP. | Machine with resources |
|
|
89
|
-
| **Proxy Server** (`proxy`) | Stdio MCP server that forwards requests to the host agent via tunnel. | Machine with MCP client |
|
|
90
|
-
| **Config Page** (Cloudflare Pages) | Device-code pairing. Stores encrypted config in KV with 15-min TTL. | Cloudflare edge |
|
|
91
|
-
|
|
92
|
-
### Pairing flow
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
1. MCP client spawns the proxy (stdio)
|
|
96
|
-
2. Proxy generates pairing code + encryption key, polls Pages RPC
|
|
97
|
-
3. User opens setup URL in browser (code + key in URL hash, never sent to server)
|
|
98
|
-
4. User enters tunnel URL + auth token, discovers servers, selects tools
|
|
99
|
-
5. Setup page encrypts config client-side, stores ciphertext in KV via RPC
|
|
100
|
-
6. Proxy polls, decrypts config, discovers servers, starts forwarding
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Protocol
|
|
104
|
-
|
|
105
|
-
- **Client <-> Proxy**: stdio (JSON-RPC, newline-delimited)
|
|
106
|
-
- **Proxy <-> Host Agent**: HTTP via Cloudflare Tunnel (MCP Streamable HTTP)
|
|
107
|
-
- **Host Agent <-> MCP Servers**: stdio (JSON-RPC, newline-delimited)
|
|
108
|
-
- **Session management**: `Mcp-Session-Id` header between proxy and host agent
|
|
109
|
-
|
|
110
|
-
## Configuration
|
|
111
|
-
|
|
112
|
-
### Host agent config
|
|
113
|
-
|
|
114
|
-
```json
|
|
115
|
-
{
|
|
116
|
-
"servers": {
|
|
117
|
-
"server-name": {
|
|
118
|
-
"command": "node",
|
|
119
|
-
"args": ["path/to/server.js"],
|
|
120
|
-
"env": { "API_KEY": "..." },
|
|
121
|
-
"shell": false
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
"host": "127.0.0.1",
|
|
125
|
-
"port": 6270
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
| Field | Default | Description |
|
|
130
|
-
|-------|---------|-------------|
|
|
131
|
-
| `servers` | _(required)_ | Map of server name to spawn config |
|
|
132
|
-
| `servers.*.command` | _(required)_ | Executable to spawn |
|
|
133
|
-
| `servers.*.args` | `[]` | Command arguments |
|
|
134
|
-
| `servers.*.env` | `{}` | Extra environment variables |
|
|
135
|
-
| `servers.*.shell` | `false` | Use shell for PATH resolution (set `true` for `npx`, etc.) |
|
|
136
|
-
| `host` | `127.0.0.1` | Listen address |
|
|
137
|
-
| `port` | `6270` | Listen port |
|
|
138
|
-
|
|
139
|
-
### CLI
|
|
140
|
-
|
|
141
|
-
**Host agent:**
|
|
142
|
-
|
|
143
|
-
```
|
|
144
|
-
host [options]
|
|
145
|
-
|
|
146
|
-
--config <path> Config file (default: config.json)
|
|
147
|
-
--tunnel Start a Cloudflare quick tunnel
|
|
148
|
-
--timeout <ms> MCP request timeout (default: 120000)
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
**Proxy server:**
|
|
152
|
-
|
|
153
|
-
```
|
|
154
|
-
proxy [options]
|
|
155
|
-
|
|
156
|
-
--pages-url <url> Config page URL (default: https://mcp-proxy.pages.dev)
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
Also reads `MCP_PROXY_PAGES_URL` environment variable.
|
|
160
|
-
|
|
161
|
-
## Error codes
|
|
162
|
-
|
|
163
|
-
| Code | Name | Meaning |
|
|
164
|
-
|------|------|---------|
|
|
165
|
-
| `-32603` | `INTERNAL` | Unhandled server error |
|
|
166
|
-
| `-32001` | `PROXY_NOT_CONFIGURED` | Proxy hasn't been paired yet |
|
|
167
|
-
| `-32002` | `HOST_UNREACHABLE` | Can't reach host agent via tunnel |
|
|
168
|
-
| `-32003` | `PROCESS_EXITED` | MCP server child process died |
|
|
169
|
-
| `-32004` | `PROCESS_NOT_RUNNING` | Child process isn't running |
|
|
170
|
-
| `-32005` | `REQUEST_TIMEOUT` | MCP server didn't respond in time |
|
|
171
|
-
|
|
172
|
-
## Development
|
|
173
|
-
|
|
174
|
-
```bash
|
|
175
|
-
pnpm install
|
|
176
|
-
pnpm run build
|
|
177
|
-
pnpm publish --access public --no-git-checks
|
|
178
|
-
```
|
|
1
|
+
# MCP Proxy
|
|
2
|
+
|
|
3
|
+
MCP proxy bridge that forwards [Model Context Protocol](https://modelcontextprotocol.io/) requests across network boundaries via [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/).
|
|
4
|
+
|
|
5
|
+
Works with any MCP client (Claude Code, Cursor, Windsurf, Cline, etc.) and any OS.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
MCP servers that need local resources (Chrome browser, filesystem, GPU, etc.) can't run inside containers or remote environments. This proxy bridges the gap:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
MCP Client (container/remote)
|
|
13
|
+
| stdio
|
|
14
|
+
Proxy Server (same machine as client)
|
|
15
|
+
| HTTP via Cloudflare Tunnel
|
|
16
|
+
Host Agent (machine with the resources)
|
|
17
|
+
| stdio
|
|
18
|
+
Real MCP Servers (chrome-devtools, filesystem, etc.)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
### 1. Start the host agent
|
|
24
|
+
|
|
25
|
+
On the machine where your MCP servers run:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx -p @silver886/mcp-proxy host --config config.json --tunnel
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Example `config.json`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"servers": {
|
|
36
|
+
"chrome-devtools": {
|
|
37
|
+
"command": "npx",
|
|
38
|
+
"args": ["-y", "chrome-devtools-mcp@latest"],
|
|
39
|
+
"shell": true
|
|
40
|
+
},
|
|
41
|
+
"filesystem": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
|
|
44
|
+
"shell": true
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The host agent prints a tunnel URL and auth token. Keep it running.
|
|
51
|
+
|
|
52
|
+
### 2. Configure your MCP client
|
|
53
|
+
|
|
54
|
+
Add the proxy as a stdio MCP server. The client launches it automatically.
|
|
55
|
+
|
|
56
|
+
**Claude Code** (`claude mcp add` or `.claude.json`):
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"mcpServers": {
|
|
61
|
+
"chrome-devtools": {
|
|
62
|
+
"type": "stdio",
|
|
63
|
+
"command": "npx",
|
|
64
|
+
"args": ["-y", "-p", "@silver886/mcp-proxy", "proxy"]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Cursor / Windsurf / other MCP clients** — same pattern, add as a stdio server with `npx -p @silver886/mcp-proxy proxy` as the command.
|
|
71
|
+
|
|
72
|
+
### 3. Pair
|
|
73
|
+
|
|
74
|
+
When the MCP client spawns the proxy, the proxy prints a setup URL to stderr:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Configure at: https://mcp-proxy.pages.dev/setup.html#code=...&key=...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Open the URL in a browser. Enter the tunnel URL and auth token from step 1, discover servers, and select tools. The proxy picks up the config automatically and starts forwarding MCP requests.
|
|
81
|
+
|
|
82
|
+
## Architecture
|
|
83
|
+
|
|
84
|
+
### Components
|
|
85
|
+
|
|
86
|
+
| Component | Role | Runs on |
|
|
87
|
+
|-----------|------|---------|
|
|
88
|
+
| **Host Agent** (`host`) | HTTP-to-stdio bridge. Spawns MCP servers, manages sessions, serves MCP Streamable HTTP. | Machine with resources |
|
|
89
|
+
| **Proxy Server** (`proxy`) | Stdio MCP server that forwards requests to the host agent via tunnel. | Machine with MCP client |
|
|
90
|
+
| **Config Page** (Cloudflare Pages) | Device-code pairing. Stores encrypted config in KV with 15-min TTL. | Cloudflare edge |
|
|
91
|
+
|
|
92
|
+
### Pairing flow
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
1. MCP client spawns the proxy (stdio)
|
|
96
|
+
2. Proxy generates pairing code + encryption key, polls Pages RPC
|
|
97
|
+
3. User opens setup URL in browser (code + key in URL hash, never sent to server)
|
|
98
|
+
4. User enters tunnel URL + auth token, discovers servers, selects tools
|
|
99
|
+
5. Setup page encrypts config client-side, stores ciphertext in KV via RPC
|
|
100
|
+
6. Proxy polls, decrypts config, discovers servers, starts forwarding
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Protocol
|
|
104
|
+
|
|
105
|
+
- **Client <-> Proxy**: stdio (JSON-RPC, newline-delimited)
|
|
106
|
+
- **Proxy <-> Host Agent**: HTTP via Cloudflare Tunnel (MCP Streamable HTTP)
|
|
107
|
+
- **Host Agent <-> MCP Servers**: stdio (JSON-RPC, newline-delimited)
|
|
108
|
+
- **Session management**: `Mcp-Session-Id` header between proxy and host agent
|
|
109
|
+
|
|
110
|
+
## Configuration
|
|
111
|
+
|
|
112
|
+
### Host agent config
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"servers": {
|
|
117
|
+
"server-name": {
|
|
118
|
+
"command": "node",
|
|
119
|
+
"args": ["path/to/server.js"],
|
|
120
|
+
"env": { "API_KEY": "..." },
|
|
121
|
+
"shell": false
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"host": "127.0.0.1",
|
|
125
|
+
"port": 6270
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
| Field | Default | Description |
|
|
130
|
+
|-------|---------|-------------|
|
|
131
|
+
| `servers` | _(required)_ | Map of server name to spawn config |
|
|
132
|
+
| `servers.*.command` | _(required)_ | Executable to spawn |
|
|
133
|
+
| `servers.*.args` | `[]` | Command arguments |
|
|
134
|
+
| `servers.*.env` | `{}` | Extra environment variables |
|
|
135
|
+
| `servers.*.shell` | `false` | Use shell for PATH resolution (set `true` for `npx`, etc.) |
|
|
136
|
+
| `host` | `127.0.0.1` | Listen address |
|
|
137
|
+
| `port` | `6270` | Listen port |
|
|
138
|
+
|
|
139
|
+
### CLI
|
|
140
|
+
|
|
141
|
+
**Host agent:**
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
host [options]
|
|
145
|
+
|
|
146
|
+
--config <path> Config file (default: config.json)
|
|
147
|
+
--tunnel Start a Cloudflare quick tunnel
|
|
148
|
+
--timeout <ms> MCP request timeout (default: 120000)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Proxy server:**
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
proxy [options]
|
|
155
|
+
|
|
156
|
+
--pages-url <url> Config page URL (default: https://mcp-proxy.pages.dev)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Also reads `MCP_PROXY_PAGES_URL` environment variable.
|
|
160
|
+
|
|
161
|
+
## Error codes
|
|
162
|
+
|
|
163
|
+
| Code | Name | Meaning |
|
|
164
|
+
|------|------|---------|
|
|
165
|
+
| `-32603` | `INTERNAL` | Unhandled server error |
|
|
166
|
+
| `-32001` | `PROXY_NOT_CONFIGURED` | Proxy hasn't been paired yet |
|
|
167
|
+
| `-32002` | `HOST_UNREACHABLE` | Can't reach host agent via tunnel |
|
|
168
|
+
| `-32003` | `PROCESS_EXITED` | MCP server child process died |
|
|
169
|
+
| `-32004` | `PROCESS_NOT_RUNNING` | Child process isn't running |
|
|
170
|
+
| `-32005` | `REQUEST_TIMEOUT` | MCP server didn't respond in time |
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
pnpm install
|
|
176
|
+
pnpm run build
|
|
177
|
+
pnpm publish --access public --no-git-checks
|
|
178
|
+
```
|
package/mcp/dist/proxy.js
CHANGED
|
@@ -111,37 +111,75 @@ class ProxyServer {
|
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
const id = parsed.id ?? null;
|
|
114
|
+
// Handle client notifications (no id)
|
|
114
115
|
if (id === null)
|
|
115
116
|
return;
|
|
116
|
-
if (!this.config) {
|
|
117
|
-
process.stdout.write((0, protocol_js_1.jsonRpcError)(protocol_js_1.ErrorCode.PROXY_NOT_CONFIGURED, `Visit ${this.setupUrl}`, id) + "\n");
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
117
|
switch (parsed.method) {
|
|
118
|
+
// initialize always succeeds — proxy is a valid server even before pairing
|
|
121
119
|
case "initialize":
|
|
122
120
|
this.sendResult(id, {
|
|
123
121
|
protocolVersion: "2024-11-05",
|
|
124
|
-
capabilities: { tools: {} },
|
|
122
|
+
capabilities: { tools: { listChanged: true }, prompts: {}, logging: {} },
|
|
125
123
|
serverInfo: { name: protocol_js_1.PACKAGE_NAME, version: protocol_js_1.PACKAGE_VERSION },
|
|
126
124
|
});
|
|
127
125
|
return;
|
|
126
|
+
// tools/list returns configure tool before pairing, real tools after
|
|
128
127
|
case "tools/list":
|
|
128
|
+
if (!this.config) {
|
|
129
|
+
this.sendResult(id, { tools: [{
|
|
130
|
+
name: "configure",
|
|
131
|
+
description: "Get the setup URL to pair this MCP proxy with a host agent. Call this tool to see the pairing URL.",
|
|
132
|
+
inputSchema: { type: "object", properties: {} },
|
|
133
|
+
}] });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
129
136
|
if (!this.initialized)
|
|
130
137
|
await this.discoverServers();
|
|
131
138
|
this.sendResult(id, { tools: this.getFilteredTools() });
|
|
132
139
|
return;
|
|
133
|
-
|
|
140
|
+
// prompts/list always available
|
|
141
|
+
case "prompts/list":
|
|
142
|
+
this.sendResult(id, { prompts: [{
|
|
143
|
+
name: "configure",
|
|
144
|
+
description: this.config
|
|
145
|
+
? "Reconfigure the MCP proxy (change tunnel URL, auth token, or tool selection)"
|
|
146
|
+
: "Get the setup URL to pair this MCP proxy with a host agent",
|
|
147
|
+
}] });
|
|
148
|
+
return;
|
|
149
|
+
case "prompts/get": {
|
|
150
|
+
const promptName = parsed.params?.name;
|
|
151
|
+
if (promptName === "configure") {
|
|
152
|
+
this.sendResult(id, {
|
|
153
|
+
messages: [{ role: "user", content: { type: "text", text: this.getConfigureText() } }],
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.sendError(protocol_js_1.ErrorCode.INVALID_PARAMS, `Unknown prompt: ${promptName}`, id);
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
case "tools/call": {
|
|
162
|
+
const toolName = parsed.params?.name;
|
|
163
|
+
if (toolName === "configure") {
|
|
164
|
+
this.sendResult(id, { content: [{ type: "text", text: this.getConfigureText() }] });
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (!this.config) {
|
|
168
|
+
this.sendError(protocol_js_1.ErrorCode.PROXY_NOT_CONFIGURED, `Visit ${this.setupUrl}`, id);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
134
171
|
await this.handleToolCall(id, parsed.params);
|
|
135
172
|
return;
|
|
173
|
+
}
|
|
136
174
|
default:
|
|
137
|
-
|
|
175
|
+
this.sendError(protocol_js_1.ErrorCode.METHOD_NOT_FOUND, parsed.method, id);
|
|
138
176
|
}
|
|
139
177
|
}
|
|
140
178
|
async handleToolCall(id, params) {
|
|
141
179
|
const prefixedName = params.name;
|
|
142
180
|
const serverName = this.toolRoute.get(prefixedName);
|
|
143
181
|
if (!serverName) {
|
|
144
|
-
|
|
182
|
+
this.sendError(protocol_js_1.ErrorCode.INVALID_PARAMS, `Unknown tool: ${prefixedName}`, id);
|
|
145
183
|
return;
|
|
146
184
|
}
|
|
147
185
|
const originalName = prefixedName.slice(serverName.length + TOOL_SEPARATOR.length);
|
|
@@ -164,7 +202,7 @@ class ProxyServer {
|
|
|
164
202
|
process.stdout.write(responseBody + "\n");
|
|
165
203
|
}
|
|
166
204
|
catch (err) {
|
|
167
|
-
|
|
205
|
+
this.sendError(protocol_js_1.ErrorCode.HOST_UNREACHABLE, err.message, id);
|
|
168
206
|
}
|
|
169
207
|
}
|
|
170
208
|
getFilteredTools() {
|
|
@@ -261,6 +299,17 @@ class ProxyServer {
|
|
|
261
299
|
sendResult(id, result) {
|
|
262
300
|
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, result }) + "\n");
|
|
263
301
|
}
|
|
302
|
+
sendError(code, detail, id) {
|
|
303
|
+
process.stdout.write((0, protocol_js_1.jsonRpcError)(code, detail, id) + "\n");
|
|
304
|
+
}
|
|
305
|
+
sendNotification(method) {
|
|
306
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", method }) + "\n");
|
|
307
|
+
}
|
|
308
|
+
getConfigureText() {
|
|
309
|
+
return this.config
|
|
310
|
+
? `MCP Proxy is connected to ${this.config.tunnelUrl}.\nTo reconfigure, open: ${this.setupUrl}`
|
|
311
|
+
: `MCP Proxy is not configured yet.\nOpen this URL to pair:\n${this.setupUrl}`;
|
|
312
|
+
}
|
|
264
313
|
async startPairing() {
|
|
265
314
|
if (this.pollTimer)
|
|
266
315
|
clearInterval(this.pollTimer);
|
|
@@ -290,6 +339,8 @@ class ProxyServer {
|
|
|
290
339
|
this.pollTimer = setInterval(() => this.pollConfig(), POLL_INTERVAL);
|
|
291
340
|
}
|
|
292
341
|
async pollConfig() {
|
|
342
|
+
if (this.config)
|
|
343
|
+
return; // Already paired — guard against overlapping async polls
|
|
293
344
|
try {
|
|
294
345
|
const { aesKey, codeId, authHash } = await this.ensureDerivedKeys();
|
|
295
346
|
const result = await rpc(this.pagesUrl, codeId, authHash, "read");
|
|
@@ -304,6 +355,7 @@ class ProxyServer {
|
|
|
304
355
|
this.pollTimer = null;
|
|
305
356
|
process.stderr.write(` Paired! tunnel=${data.tunnelUrl}\n`);
|
|
306
357
|
await this.discoverServers();
|
|
358
|
+
this.sendNotification("notifications/tools/list_changed");
|
|
307
359
|
}
|
|
308
360
|
}
|
|
309
361
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const PACKAGE_NAME = "@silver886/mcp-proxy";
|
|
2
|
-
export declare const PACKAGE_VERSION = "0.1.
|
|
2
|
+
export declare const PACKAGE_VERSION = "0.1.2";
|
|
@@ -4,6 +4,8 @@ export declare const DEFAULT_HOST = "127.0.0.1";
|
|
|
4
4
|
export declare const DEFAULT_PORT = 6270;
|
|
5
5
|
export declare const DEFAULT_PAGES_URL = "https://mcp-proxy.pages.dev";
|
|
6
6
|
export declare const ErrorCode: {
|
|
7
|
+
readonly METHOD_NOT_FOUND: -32601;
|
|
8
|
+
readonly INVALID_PARAMS: -32602;
|
|
7
9
|
readonly INTERNAL: -32603;
|
|
8
10
|
readonly PROXY_NOT_CONFIGURED: -32001;
|
|
9
11
|
readonly HOST_UNREACHABLE: -32002;
|
|
@@ -12,6 +14,8 @@ export declare const ErrorCode: {
|
|
|
12
14
|
readonly REQUEST_TIMEOUT: -32005;
|
|
13
15
|
};
|
|
14
16
|
export declare const ErrorMessage: {
|
|
17
|
+
readonly [-32601]: "Method not found";
|
|
18
|
+
readonly [-32602]: "Invalid params";
|
|
15
19
|
readonly [-32603]: "Internal error";
|
|
16
20
|
readonly [-32001]: "Proxy not configured";
|
|
17
21
|
readonly [-32002]: "Host agent unreachable";
|
|
@@ -12,8 +12,10 @@ Object.defineProperty(exports, "PACKAGE_VERSION", { enumerable: true, get: funct
|
|
|
12
12
|
exports.DEFAULT_HOST = "127.0.0.1";
|
|
13
13
|
exports.DEFAULT_PORT = 6270;
|
|
14
14
|
exports.DEFAULT_PAGES_URL = "https://mcp-proxy.pages.dev";
|
|
15
|
-
// JSON-RPC error codes
|
|
15
|
+
// JSON-RPC error codes: -32600..-32603 = spec-defined, -32000..-32099 = server-defined
|
|
16
16
|
exports.ErrorCode = {
|
|
17
|
+
METHOD_NOT_FOUND: -32601, // JSON-RPC spec: method not found
|
|
18
|
+
INVALID_PARAMS: -32602, // JSON-RPC spec: invalid params
|
|
17
19
|
INTERNAL: -32603, // JSON-RPC spec: internal error
|
|
18
20
|
PROXY_NOT_CONFIGURED: -32001, // Proxy has not been paired yet
|
|
19
21
|
HOST_UNREACHABLE: -32002, // Cannot reach the host agent via tunnel
|
|
@@ -22,6 +24,8 @@ exports.ErrorCode = {
|
|
|
22
24
|
REQUEST_TIMEOUT: -32005, // MCP server did not respond in time
|
|
23
25
|
};
|
|
24
26
|
exports.ErrorMessage = {
|
|
27
|
+
[exports.ErrorCode.METHOD_NOT_FOUND]: "Method not found",
|
|
28
|
+
[exports.ErrorCode.INVALID_PARAMS]: "Invalid params",
|
|
25
29
|
[exports.ErrorCode.INTERNAL]: "Internal error",
|
|
26
30
|
[exports.ErrorCode.PROXY_NOT_CONFIGURED]: "Proxy not configured",
|
|
27
31
|
[exports.ErrorCode.HOST_UNREACHABLE]: "Host agent unreachable",
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silver886/mcp-proxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "MCP proxy bridge: forward MCP requests across network boundaries via Cloudflare tunnel",
|
|
5
|
-
"
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/silver886/mcp-proxy.git"
|
|
8
|
+
},
|
|
6
9
|
"keywords": [
|
|
7
10
|
"mcp",
|
|
8
11
|
"model-context-protocol",
|
|
@@ -13,6 +16,15 @@
|
|
|
13
16
|
"wsl",
|
|
14
17
|
"docker"
|
|
15
18
|
],
|
|
19
|
+
"author": {
|
|
20
|
+
"name": "L",
|
|
21
|
+
"email": "11664880+silver886@users.noreply.github.com"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/silver886/mcp-proxy/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/silver886/mcp-proxy#readme",
|
|
16
28
|
"bin": {
|
|
17
29
|
"host": "mcp/dist/host.js",
|
|
18
30
|
"proxy": "mcp/dist/proxy.js"
|