@silver886/mcp-proxy 0.1.3 → 0.2.0
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 +62 -17
- package/dist/host/agent.d.ts +22 -0
- package/dist/host/agent.js +314 -0
- package/dist/host/cli.d.ts +1 -0
- package/dist/host/cli.js +83 -0
- package/dist/host/constants.d.ts +4 -0
- package/dist/host/constants.js +16 -0
- package/dist/host/session.d.ts +21 -0
- package/dist/host/session.js +204 -0
- package/dist/host/tunnel.d.ts +5 -0
- package/dist/host/tunnel.js +82 -0
- package/dist/host.js +8 -0
- package/dist/proxy/core/constants.d.ts +13 -0
- package/dist/proxy/core/constants.js +39 -0
- package/dist/proxy/core/fetch-timeout.d.ts +1 -0
- package/dist/proxy/core/fetch-timeout.js +15 -0
- package/dist/proxy/core/state.d.ts +25 -0
- package/dist/proxy/core/state.js +90 -0
- package/dist/proxy/core/types.d.ts +57 -0
- package/dist/proxy/core/types.js +5 -0
- package/dist/proxy/discovery/client.d.ts +42 -0
- package/dist/proxy/discovery/client.js +283 -0
- package/dist/proxy/discovery/runner.d.ts +21 -0
- package/dist/proxy/discovery/runner.js +319 -0
- package/dist/proxy/pairing/config.d.ts +9 -0
- package/dist/proxy/pairing/config.js +130 -0
- package/dist/proxy/pairing/controller.d.ts +19 -0
- package/dist/proxy/pairing/controller.js +327 -0
- package/dist/proxy/pairing/http.d.ts +70 -0
- package/dist/proxy/pairing/http.js +155 -0
- package/dist/proxy/pairing/static-assets.d.ts +4 -0
- package/dist/proxy/pairing/static-assets.js +13 -0
- package/dist/proxy/pairing/tunnel.d.ts +13 -0
- package/dist/proxy/pairing/tunnel.js +130 -0
- package/dist/proxy/pairing/validation.d.ts +2 -0
- package/dist/proxy/pairing/validation.js +62 -0
- package/dist/proxy/routing/filtering.d.ts +13 -0
- package/dist/proxy/routing/filtering.js +116 -0
- package/dist/proxy/routing/router.d.ts +17 -0
- package/dist/proxy/routing/router.js +74 -0
- package/dist/proxy/routing/uri.d.ts +7 -0
- package/dist/proxy/routing/uri.js +39 -0
- package/dist/proxy/runtime/forwarder.d.ts +15 -0
- package/dist/proxy/runtime/forwarder.js +265 -0
- package/dist/proxy/runtime/handlers.d.ts +48 -0
- package/dist/proxy/runtime/handlers.js +329 -0
- package/dist/proxy/runtime/sse.d.ts +19 -0
- package/dist/proxy/runtime/sse.js +169 -0
- package/dist/proxy/runtime/upstream-bridge.d.ts +27 -0
- package/dist/proxy/runtime/upstream-bridge.js +133 -0
- package/dist/proxy/server.d.ts +15 -0
- package/dist/proxy/server.js +167 -0
- package/dist/proxy.js +5 -0
- package/{mcp/dist → dist}/shared/protocol.d.ts +15 -3
- package/dist/shared/protocol.js +183 -0
- package/dist/wrapper.d.ts +2 -0
- package/dist/wrapper.js +72 -0
- package/package.json +15 -7
- package/static/setup.css +233 -0
- package/static/setup.html +57 -0
- package/static/setup.js +711 -0
- package/static/style.css +208 -0
- package/mcp/dist/host.js +0 -307
- package/mcp/dist/proxy.js +0 -374
- package/mcp/dist/shared/generated.d.ts +0 -2
- package/mcp/dist/shared/generated.js +0 -5
- package/mcp/dist/shared/protocol.js +0 -79
- /package/{mcp/dist → dist}/host.d.ts +0 -0
- /package/{mcp/dist → dist}/proxy.d.ts +0 -0
package/mcp/dist/proxy.js
DELETED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
const node_crypto_1 = require("node:crypto");
|
|
5
|
-
const protocol_js_1 = require("./shared/protocol.js");
|
|
6
|
-
const POLL_INTERVAL = 2000; // ms
|
|
7
|
-
const TOOL_SEPARATOR = "__";
|
|
8
|
-
const subtle = node_crypto_1.webcrypto.subtle;
|
|
9
|
-
// --- Crypto helpers (AES-256-GCM + SHA-256 + HMAC) ---
|
|
10
|
-
async function importAesKey(keyB64) {
|
|
11
|
-
const raw = Buffer.from(keyB64, "base64url");
|
|
12
|
-
return subtle.importKey("raw", raw, "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
13
|
-
}
|
|
14
|
-
async function importHmacKey(keyB64) {
|
|
15
|
-
const raw = Buffer.from(keyB64, "base64url");
|
|
16
|
-
return subtle.importKey("raw", raw, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
17
|
-
}
|
|
18
|
-
async function deriveCodeId(code) {
|
|
19
|
-
const hash = await subtle.digest("SHA-256", new TextEncoder().encode(code));
|
|
20
|
-
return Buffer.from(hash).toString("base64url");
|
|
21
|
-
}
|
|
22
|
-
async function deriveAuthHash(keyB64, code) {
|
|
23
|
-
const hmacKey = await importHmacKey(keyB64);
|
|
24
|
-
const sig = await subtle.sign("HMAC", hmacKey, new TextEncoder().encode(code));
|
|
25
|
-
return Buffer.from(sig).toString("base64url");
|
|
26
|
-
}
|
|
27
|
-
async function encrypt(key, plaintext) {
|
|
28
|
-
const iv = (0, node_crypto_1.randomBytes)(12);
|
|
29
|
-
const encoded = new TextEncoder().encode(plaintext);
|
|
30
|
-
const ciphertext = new Uint8Array(await subtle.encrypt({ name: "AES-GCM", iv }, key, encoded));
|
|
31
|
-
const combined = new Uint8Array(iv.length + ciphertext.length);
|
|
32
|
-
combined.set(iv);
|
|
33
|
-
combined.set(ciphertext, iv.length);
|
|
34
|
-
return Buffer.from(combined).toString("base64url");
|
|
35
|
-
}
|
|
36
|
-
async function decrypt(key, data) {
|
|
37
|
-
const combined = Buffer.from(data, "base64url");
|
|
38
|
-
const iv = combined.subarray(0, 12);
|
|
39
|
-
const ciphertext = combined.subarray(12);
|
|
40
|
-
const plaintext = await subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
|
|
41
|
-
return new TextDecoder().decode(plaintext);
|
|
42
|
-
}
|
|
43
|
-
// --- RPC client ---
|
|
44
|
-
async function rpc(pagesUrl, codeId, authHash, action, payload) {
|
|
45
|
-
const resp = await fetch(`${pagesUrl}/api/rpc`, {
|
|
46
|
-
method: "POST",
|
|
47
|
-
headers: { "Content-Type": "application/json" },
|
|
48
|
-
body: JSON.stringify({ codeId, authHash, action, payload }),
|
|
49
|
-
});
|
|
50
|
-
return (await resp.json());
|
|
51
|
-
}
|
|
52
|
-
// --- Proxy ---
|
|
53
|
-
class ProxyServer {
|
|
54
|
-
config = null;
|
|
55
|
-
pagesUrl;
|
|
56
|
-
code;
|
|
57
|
-
encKeyB64;
|
|
58
|
-
aesKey = null;
|
|
59
|
-
codeId = null;
|
|
60
|
-
authHash = null;
|
|
61
|
-
pollTimer = null;
|
|
62
|
-
servers = new Map();
|
|
63
|
-
toolRoute = new Map();
|
|
64
|
-
initialized = false;
|
|
65
|
-
constructor(pagesUrl) {
|
|
66
|
-
this.pagesUrl = pagesUrl.replace(/\/+$/, "");
|
|
67
|
-
this.code = (0, node_crypto_1.randomBytes)(64).toString("base64url");
|
|
68
|
-
this.encKeyB64 = (0, node_crypto_1.randomBytes)(32).toString("base64url");
|
|
69
|
-
}
|
|
70
|
-
get setupUrl() {
|
|
71
|
-
return `${this.pagesUrl}/setup.html#code=${this.code}&key=${this.encKeyB64}`;
|
|
72
|
-
}
|
|
73
|
-
get hostHeaders() {
|
|
74
|
-
return {
|
|
75
|
-
"Content-Type": "application/json",
|
|
76
|
-
Accept: "application/json, text/event-stream",
|
|
77
|
-
Authorization: `Bearer ${this.config?.authToken ?? ""}`,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
async ensureDerivedKeys() {
|
|
81
|
-
if (!this.aesKey)
|
|
82
|
-
this.aesKey = await importAesKey(this.encKeyB64);
|
|
83
|
-
if (!this.codeId)
|
|
84
|
-
this.codeId = await deriveCodeId(this.code);
|
|
85
|
-
if (!this.authHash)
|
|
86
|
-
this.authHash = await deriveAuthHash(this.encKeyB64, this.code);
|
|
87
|
-
return { aesKey: this.aesKey, codeId: this.codeId, authHash: this.authHash };
|
|
88
|
-
}
|
|
89
|
-
start() {
|
|
90
|
-
this.startPairing();
|
|
91
|
-
const stdinBuffer = new protocol_js_1.LineBuffer();
|
|
92
|
-
process.stdin.setEncoding("utf-8");
|
|
93
|
-
process.stdin.on("data", (chunk) => {
|
|
94
|
-
const lines = stdinBuffer.push(chunk);
|
|
95
|
-
for (const line of lines) {
|
|
96
|
-
this.handleLine(line).catch((err) => {
|
|
97
|
-
process.stderr.write(`Proxy error: ${err.message}\n`);
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
process.stdin.on("end", () => {
|
|
102
|
-
process.exit(0);
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
async handleLine(line) {
|
|
106
|
-
let parsed;
|
|
107
|
-
try {
|
|
108
|
-
parsed = JSON.parse(line);
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
const id = parsed.id ?? null;
|
|
114
|
-
// Handle client notifications (no id)
|
|
115
|
-
if (id === null)
|
|
116
|
-
return;
|
|
117
|
-
switch (parsed.method) {
|
|
118
|
-
// initialize always succeeds — proxy is a valid server even before pairing
|
|
119
|
-
case "initialize":
|
|
120
|
-
this.sendResult(id, {
|
|
121
|
-
protocolVersion: protocol_js_1.MCP_PROTOCOL_VERSION,
|
|
122
|
-
capabilities: { tools: { listChanged: true }, prompts: {}, logging: {} },
|
|
123
|
-
serverInfo: { name: protocol_js_1.PACKAGE_NAME, version: protocol_js_1.PACKAGE_VERSION },
|
|
124
|
-
});
|
|
125
|
-
return;
|
|
126
|
-
// tools/list returns configure tool before pairing, real tools after
|
|
127
|
-
case "tools/list":
|
|
128
|
-
if (!this.config) {
|
|
129
|
-
this.sendResult(id, { tools: [{
|
|
130
|
-
name: "configure",
|
|
131
|
-
description: "Set up or reconfigure the MCP proxy connection. Returns the setup URL.",
|
|
132
|
-
inputSchema: { type: "object", properties: {} },
|
|
133
|
-
}] });
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (!this.initialized)
|
|
137
|
-
await this.discoverServers();
|
|
138
|
-
this.sendResult(id, { tools: this.getFilteredTools() });
|
|
139
|
-
return;
|
|
140
|
-
// prompts/list always available
|
|
141
|
-
case "prompts/list":
|
|
142
|
-
this.sendResult(id, { prompts: [{
|
|
143
|
-
name: "configure",
|
|
144
|
-
description: "Set up or reconfigure the MCP proxy connection",
|
|
145
|
-
}] });
|
|
146
|
-
return;
|
|
147
|
-
case "prompts/get": {
|
|
148
|
-
const promptName = parsed.params?.name;
|
|
149
|
-
if (promptName === "configure") {
|
|
150
|
-
const text = await this.handleConfigure();
|
|
151
|
-
this.sendResult(id, {
|
|
152
|
-
messages: [{ role: "user", content: { type: "text", text } }],
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
this.sendError(protocol_js_1.ErrorCode.INVALID_PARAMS, `Unknown prompt: ${promptName}`, id);
|
|
157
|
-
}
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
case "tools/call": {
|
|
161
|
-
const toolName = parsed.params?.name;
|
|
162
|
-
if (toolName === "configure") {
|
|
163
|
-
const text = await this.handleConfigure();
|
|
164
|
-
this.sendResult(id, { content: [{ type: "text", text }] });
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (!this.config) {
|
|
168
|
-
this.sendError(protocol_js_1.ErrorCode.PROXY_NOT_CONFIGURED, `Visit ${this.setupUrl}`, id);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
await this.handleToolCall(id, parsed.params);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
default:
|
|
175
|
-
this.sendError(protocol_js_1.ErrorCode.METHOD_NOT_FOUND, parsed.method, id);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
async handleToolCall(id, params) {
|
|
179
|
-
const prefixedName = params.name;
|
|
180
|
-
const serverName = this.toolRoute.get(prefixedName);
|
|
181
|
-
if (!serverName) {
|
|
182
|
-
this.sendError(protocol_js_1.ErrorCode.INVALID_PARAMS, `Unknown tool: ${prefixedName}`, id);
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const originalName = prefixedName.slice(serverName.length + TOOL_SEPARATOR.length);
|
|
186
|
-
const server = this.servers.get(serverName);
|
|
187
|
-
const targetUrl = `${this.config.tunnelUrl}/servers/${serverName}`;
|
|
188
|
-
const headers = { ...this.hostHeaders };
|
|
189
|
-
if (server.sessionId)
|
|
190
|
-
headers["Mcp-Session-Id"] = server.sessionId;
|
|
191
|
-
try {
|
|
192
|
-
const body = JSON.stringify({
|
|
193
|
-
jsonrpc: "2.0",
|
|
194
|
-
id,
|
|
195
|
-
method: "tools/call",
|
|
196
|
-
params: { name: originalName, arguments: params.arguments },
|
|
197
|
-
});
|
|
198
|
-
const upstream = await fetch(targetUrl, { method: "POST", headers, body });
|
|
199
|
-
server.sessionId = upstream.headers.get("mcp-session-id") ?? server.sessionId;
|
|
200
|
-
const responseBody = await upstream.text();
|
|
201
|
-
if (responseBody)
|
|
202
|
-
process.stdout.write(responseBody + "\n");
|
|
203
|
-
}
|
|
204
|
-
catch (err) {
|
|
205
|
-
this.sendError(protocol_js_1.ErrorCode.HOST_UNREACHABLE, err.message, id);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
getFilteredTools() {
|
|
209
|
-
const selectedSet = this.config?.selectedTools?.length
|
|
210
|
-
? new Set(this.config.selectedTools)
|
|
211
|
-
: null;
|
|
212
|
-
const tools = [];
|
|
213
|
-
for (const [serverName, state] of this.servers) {
|
|
214
|
-
for (const tool of state.tools) {
|
|
215
|
-
const prefixed = `${serverName}${TOOL_SEPARATOR}${tool.name}`;
|
|
216
|
-
if (selectedSet && !selectedSet.has(prefixed))
|
|
217
|
-
continue;
|
|
218
|
-
tools.push({
|
|
219
|
-
...tool,
|
|
220
|
-
name: prefixed,
|
|
221
|
-
description: `[${serverName}] ${tool.description ?? ""}`.trim(),
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
return tools;
|
|
226
|
-
}
|
|
227
|
-
async discoverServers() {
|
|
228
|
-
if (!this.config)
|
|
229
|
-
return;
|
|
230
|
-
try {
|
|
231
|
-
const listResp = await fetch(`${this.config.tunnelUrl}/`, { headers: this.hostHeaders });
|
|
232
|
-
const listData = (await listResp.json());
|
|
233
|
-
const serverNames = listData.servers ?? [];
|
|
234
|
-
// Skip servers whose names contain the tool separator to prevent routing confusion
|
|
235
|
-
const safeNames = serverNames.filter((name) => {
|
|
236
|
-
if (name.includes(TOOL_SEPARATOR)) {
|
|
237
|
-
process.stderr.write(` [${name}] skipped: name contains '${TOOL_SEPARATOR}'\n`);
|
|
238
|
-
return false;
|
|
239
|
-
}
|
|
240
|
-
return true;
|
|
241
|
-
});
|
|
242
|
-
process.stderr.write(` Discovered servers: ${safeNames.join(", ")}\n`);
|
|
243
|
-
for (const name of safeNames) {
|
|
244
|
-
await this.initServer(name);
|
|
245
|
-
}
|
|
246
|
-
this.toolRoute.clear();
|
|
247
|
-
for (const [serverName, state] of this.servers) {
|
|
248
|
-
for (const tool of state.tools) {
|
|
249
|
-
this.toolRoute.set(`${serverName}${TOOL_SEPARATOR}${tool.name}`, serverName);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
this.initialized = true;
|
|
253
|
-
process.stderr.write(` Total tools: ${this.toolRoute.size}\n\n`);
|
|
254
|
-
}
|
|
255
|
-
catch (err) {
|
|
256
|
-
process.stderr.write(` Discovery failed: ${err.message}\n`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
async initServer(name) {
|
|
260
|
-
const targetUrl = `${this.config.tunnelUrl}/servers/${name}`;
|
|
261
|
-
const headers = { ...this.hostHeaders };
|
|
262
|
-
try {
|
|
263
|
-
const initResp = await fetch(targetUrl, {
|
|
264
|
-
method: "POST",
|
|
265
|
-
headers,
|
|
266
|
-
body: JSON.stringify({
|
|
267
|
-
jsonrpc: "2.0",
|
|
268
|
-
id: `init-${name}`,
|
|
269
|
-
method: "initialize",
|
|
270
|
-
params: {
|
|
271
|
-
protocolVersion: protocol_js_1.MCP_PROTOCOL_VERSION,
|
|
272
|
-
capabilities: {},
|
|
273
|
-
clientInfo: { name: protocol_js_1.PACKAGE_NAME, version: protocol_js_1.PACKAGE_VERSION },
|
|
274
|
-
},
|
|
275
|
-
}),
|
|
276
|
-
});
|
|
277
|
-
const sessionId = initResp.headers.get("mcp-session-id") ?? undefined;
|
|
278
|
-
if (sessionId)
|
|
279
|
-
headers["Mcp-Session-Id"] = sessionId;
|
|
280
|
-
await fetch(targetUrl, {
|
|
281
|
-
method: "POST",
|
|
282
|
-
headers,
|
|
283
|
-
body: JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized", params: {} }),
|
|
284
|
-
});
|
|
285
|
-
const toolsResp = await fetch(targetUrl, {
|
|
286
|
-
method: "POST",
|
|
287
|
-
headers,
|
|
288
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: `tools-${name}`, method: "tools/list", params: {} }),
|
|
289
|
-
});
|
|
290
|
-
const toolsData = (await toolsResp.json());
|
|
291
|
-
const tools = toolsData.result?.tools ?? [];
|
|
292
|
-
this.servers.set(name, { sessionId, tools });
|
|
293
|
-
process.stderr.write(` [${name}] ${tools.length} tools\n`);
|
|
294
|
-
}
|
|
295
|
-
catch (err) {
|
|
296
|
-
process.stderr.write(` [${name}] init failed: ${err.message}\n`);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
sendResult(id, result) {
|
|
300
|
-
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, result }) + "\n");
|
|
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
|
-
async handleConfigure() {
|
|
309
|
-
if (this.config) {
|
|
310
|
-
await this.startPairing();
|
|
311
|
-
this.sendNotification("notifications/tools/list_changed");
|
|
312
|
-
}
|
|
313
|
-
return `Open this URL in your browser to set up the MCP Proxy:\n\n${this.setupUrl}\n\nThe proxy will connect automatically once setup is complete.`;
|
|
314
|
-
}
|
|
315
|
-
async startPairing() {
|
|
316
|
-
if (this.pollTimer)
|
|
317
|
-
clearInterval(this.pollTimer);
|
|
318
|
-
const previousConfig = this.config;
|
|
319
|
-
this.code = (0, node_crypto_1.randomBytes)(64).toString("base64url");
|
|
320
|
-
this.encKeyB64 = (0, node_crypto_1.randomBytes)(32).toString("base64url");
|
|
321
|
-
this.aesKey = null;
|
|
322
|
-
this.codeId = null;
|
|
323
|
-
this.authHash = null;
|
|
324
|
-
this.config = null;
|
|
325
|
-
this.initialized = false;
|
|
326
|
-
this.servers.clear();
|
|
327
|
-
this.toolRoute.clear();
|
|
328
|
-
// Seed with encrypted previous config (unsealed) — skip on first pairing
|
|
329
|
-
if (previousConfig) {
|
|
330
|
-
try {
|
|
331
|
-
const { aesKey, codeId, authHash } = await this.ensureDerivedKeys();
|
|
332
|
-
const payload = await encrypt(aesKey, JSON.stringify({ ...previousConfig, sealed: false }));
|
|
333
|
-
await rpc(this.pagesUrl, codeId, authHash, "write", payload);
|
|
334
|
-
}
|
|
335
|
-
catch {
|
|
336
|
-
// Non-critical
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
process.stderr.write(`\n Configure at: ${this.setupUrl}\n\n`);
|
|
340
|
-
process.stderr.write(` Waiting for configuration...\n`);
|
|
341
|
-
this.pollTimer = setInterval(() => this.pollConfig(), POLL_INTERVAL);
|
|
342
|
-
}
|
|
343
|
-
async pollConfig() {
|
|
344
|
-
if (this.config)
|
|
345
|
-
return; // Already paired — guard against overlapping async polls
|
|
346
|
-
try {
|
|
347
|
-
const { aesKey, codeId, authHash } = await this.ensureDerivedKeys();
|
|
348
|
-
const result = await rpc(this.pagesUrl, codeId, authHash, "read");
|
|
349
|
-
if (result.payload) {
|
|
350
|
-
const plaintext = await decrypt(aesKey, result.payload);
|
|
351
|
-
const data = JSON.parse(plaintext);
|
|
352
|
-
if (data.tunnelUrl && data.authToken && data.serverName && data.sealed) {
|
|
353
|
-
data.tunnelUrl = data.tunnelUrl.replace(/\/+$/, "");
|
|
354
|
-
this.config = data;
|
|
355
|
-
if (this.pollTimer)
|
|
356
|
-
clearInterval(this.pollTimer);
|
|
357
|
-
this.pollTimer = null;
|
|
358
|
-
process.stderr.write(` Paired! tunnel=${data.tunnelUrl}\n`);
|
|
359
|
-
await this.discoverServers();
|
|
360
|
-
this.sendNotification("notifications/tools/list_changed");
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
catch {
|
|
365
|
-
// Silently retry
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
function main() {
|
|
370
|
-
const pagesUrl = (0, protocol_js_1.getArg)("--pages-url") ?? process.env.MCP_PROXY_PAGES_URL ?? protocol_js_1.DEFAULT_PAGES_URL;
|
|
371
|
-
const proxy = new ProxyServer(pagesUrl);
|
|
372
|
-
proxy.start();
|
|
373
|
-
}
|
|
374
|
-
main();
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LineBuffer = exports.ErrorMessage = exports.ErrorCode = exports.DEFAULT_PAGES_URL = exports.DEFAULT_PORT = exports.DEFAULT_HOST = exports.MCP_PROTOCOL_VERSION = exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
|
|
4
|
-
exports.jsonRpcError = jsonRpcError;
|
|
5
|
-
exports.readBody = readBody;
|
|
6
|
-
exports.getArg = getArg;
|
|
7
|
-
exports.createServer = createServer;
|
|
8
|
-
const node_http_1 = require("node:http");
|
|
9
|
-
var generated_js_1 = require("./generated.js");
|
|
10
|
-
Object.defineProperty(exports, "PACKAGE_NAME", { enumerable: true, get: function () { return generated_js_1.PACKAGE_NAME; } });
|
|
11
|
-
Object.defineProperty(exports, "PACKAGE_VERSION", { enumerable: true, get: function () { return generated_js_1.PACKAGE_VERSION; } });
|
|
12
|
-
exports.MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
13
|
-
exports.DEFAULT_HOST = "127.0.0.1";
|
|
14
|
-
exports.DEFAULT_PORT = 6270;
|
|
15
|
-
exports.DEFAULT_PAGES_URL = "https://mcp-proxy.pages.dev";
|
|
16
|
-
// JSON-RPC error codes: -32600..-32603 = spec-defined, -32000..-32099 = server-defined
|
|
17
|
-
exports.ErrorCode = {
|
|
18
|
-
METHOD_NOT_FOUND: -32601, // JSON-RPC spec: method not found
|
|
19
|
-
INVALID_PARAMS: -32602, // JSON-RPC spec: invalid params
|
|
20
|
-
INTERNAL: -32603, // JSON-RPC spec: internal error
|
|
21
|
-
PROXY_NOT_CONFIGURED: -32001, // Proxy has not been paired yet
|
|
22
|
-
HOST_UNREACHABLE: -32002, // Cannot reach the host agent via tunnel
|
|
23
|
-
PROCESS_EXITED: -32003, // MCP server child process exited unexpectedly
|
|
24
|
-
PROCESS_NOT_RUNNING: -32004, // MCP server child process is not running
|
|
25
|
-
REQUEST_TIMEOUT: -32005, // MCP server did not respond in time
|
|
26
|
-
};
|
|
27
|
-
exports.ErrorMessage = {
|
|
28
|
-
[exports.ErrorCode.METHOD_NOT_FOUND]: "Method not found",
|
|
29
|
-
[exports.ErrorCode.INVALID_PARAMS]: "Invalid params",
|
|
30
|
-
[exports.ErrorCode.INTERNAL]: "Internal error",
|
|
31
|
-
[exports.ErrorCode.PROXY_NOT_CONFIGURED]: "Proxy not configured",
|
|
32
|
-
[exports.ErrorCode.HOST_UNREACHABLE]: "Host agent unreachable",
|
|
33
|
-
[exports.ErrorCode.PROCESS_EXITED]: "Server process exited",
|
|
34
|
-
[exports.ErrorCode.PROCESS_NOT_RUNNING]: "Server process not running",
|
|
35
|
-
[exports.ErrorCode.REQUEST_TIMEOUT]: "Request timed out",
|
|
36
|
-
};
|
|
37
|
-
// JSON-RPC error response helper
|
|
38
|
-
function jsonRpcError(code, detail, id = null) {
|
|
39
|
-
const base = exports.ErrorMessage[code] ?? "Unknown error";
|
|
40
|
-
const message = detail ? `${base}: ${detail}` : base;
|
|
41
|
-
return JSON.stringify({ jsonrpc: "2.0", error: { code, message }, id });
|
|
42
|
-
}
|
|
43
|
-
// Read full request body as string
|
|
44
|
-
function readBody(req) {
|
|
45
|
-
return new Promise((resolve, reject) => {
|
|
46
|
-
const chunks = [];
|
|
47
|
-
req.on("data", (c) => chunks.push(c));
|
|
48
|
-
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
49
|
-
req.on("error", reject);
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
// Parse CLI argument by name: --flag value
|
|
53
|
-
function getArg(name) {
|
|
54
|
-
const idx = process.argv.indexOf(name);
|
|
55
|
-
return idx !== -1 && idx + 1 < process.argv.length ? process.argv[idx + 1] : undefined;
|
|
56
|
-
}
|
|
57
|
-
// Create HTTP server with async handler and error catching
|
|
58
|
-
function createServer(handler) {
|
|
59
|
-
return (0, node_http_1.createServer)((req, res) => {
|
|
60
|
-
handler(req, res).catch((err) => {
|
|
61
|
-
console.error(`Request handler error: ${err.message}`);
|
|
62
|
-
if (!res.headersSent) {
|
|
63
|
-
res.writeHead(500, { "Content-Type": "application/json" });
|
|
64
|
-
res.end(jsonRpcError(exports.ErrorCode.INTERNAL));
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
// Line-buffered reader: accumulates chunks and yields complete lines
|
|
70
|
-
class LineBuffer {
|
|
71
|
-
buffer = "";
|
|
72
|
-
push(chunk) {
|
|
73
|
-
this.buffer += chunk;
|
|
74
|
-
const parts = this.buffer.split("\n");
|
|
75
|
-
this.buffer = parts.pop(); // Keep incomplete trailing segment
|
|
76
|
-
return parts.filter((line) => line.trim().length > 0);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
exports.LineBuffer = LineBuffer;
|
|
File without changes
|
|
File without changes
|