ma-mcp-remote 0.1.41

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.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/client.js ADDED
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ Client,
4
+ ListResourcesResultSchema,
5
+ ListToolsResultSchema,
6
+ NodeOAuthClientProvider,
7
+ connectToRemoteServer,
8
+ createLazyAuthCoordinator,
9
+ debugLog,
10
+ discoverOAuthServerInfo,
11
+ log,
12
+ parseCommandLineArgs,
13
+ setupSignalHandlers,
14
+ version
15
+ } from "./chunk-AO2MMDOT.js";
16
+
17
+ // src/client.ts
18
+ import { EventEmitter } from "events";
19
+ async function runClient(serverUrl, callbackPort, headers, transportStrategy = "http-first", host, staticOAuthClientMetadata, staticOAuthClientInfo, authTimeoutMs, serverUrlHash) {
20
+ const events = new EventEmitter();
21
+ const authCoordinator = createLazyAuthCoordinator(serverUrlHash, callbackPort, events, authTimeoutMs);
22
+ log("Discovering OAuth server configuration...");
23
+ const discoveryResult = await discoverOAuthServerInfo(serverUrl, headers);
24
+ if (discoveryResult.protectedResourceMetadata) {
25
+ log(`Discovered authorization server: ${discoveryResult.authorizationServerUrl}`);
26
+ if (discoveryResult.protectedResourceMetadata.scopes_supported) {
27
+ debugLog("Protected Resource Metadata scopes", {
28
+ scopes_supported: discoveryResult.protectedResourceMetadata.scopes_supported
29
+ });
30
+ }
31
+ } else {
32
+ debugLog("No Protected Resource Metadata found, using server URL as authorization server");
33
+ }
34
+ const authProvider = new NodeOAuthClientProvider({
35
+ serverUrl: discoveryResult.authorizationServerUrl,
36
+ callbackPort,
37
+ host,
38
+ clientName: "MCP CLI Client",
39
+ staticOAuthClientMetadata,
40
+ staticOAuthClientInfo,
41
+ serverUrlHash,
42
+ authorizationServerMetadata: discoveryResult.authorizationServerMetadata,
43
+ protectedResourceMetadata: discoveryResult.protectedResourceMetadata,
44
+ wwwAuthenticateScope: discoveryResult.wwwAuthenticateScope
45
+ });
46
+ const client = new Client(
47
+ {
48
+ name: "mcp-remote",
49
+ version
50
+ },
51
+ {
52
+ capabilities: {}
53
+ }
54
+ );
55
+ let server = null;
56
+ const authInitializer = async () => {
57
+ const authState = await authCoordinator.initializeAuth();
58
+ server = authState.server;
59
+ if (authState.skipBrowserAuth) {
60
+ log("Authentication was completed by another instance - will use tokens from disk...");
61
+ await new Promise((res) => setTimeout(res, 1e3));
62
+ }
63
+ return {
64
+ waitForAuthCode: authState.waitForAuthCode,
65
+ skipBrowserAuth: authState.skipBrowserAuth
66
+ };
67
+ };
68
+ try {
69
+ const transport = await connectToRemoteServer(client, serverUrl, authProvider, headers, authInitializer, transportStrategy);
70
+ transport.onmessage = (message) => {
71
+ log("Received message:", JSON.stringify(message, null, 2));
72
+ };
73
+ transport.onerror = (error) => {
74
+ log("Transport error:", error);
75
+ };
76
+ transport.onclose = () => {
77
+ log("Connection closed.");
78
+ process.exit(0);
79
+ };
80
+ const cleanup = async () => {
81
+ log("\nClosing connection...");
82
+ await client.close();
83
+ if (server) {
84
+ server.close();
85
+ }
86
+ };
87
+ setupSignalHandlers(cleanup);
88
+ log("Connected successfully!");
89
+ try {
90
+ log("Requesting tools list...");
91
+ const tools = await client.request({ method: "tools/list" }, ListToolsResultSchema);
92
+ log("Tools:", JSON.stringify(tools, null, 2));
93
+ } catch (e) {
94
+ log("Error requesting tools list:", e);
95
+ }
96
+ try {
97
+ log("Requesting resource list...");
98
+ const resources = await client.request({ method: "resources/list" }, ListResourcesResultSchema);
99
+ log("Resources:", JSON.stringify(resources, null, 2));
100
+ } catch (e) {
101
+ log("Error requesting resources list:", e);
102
+ }
103
+ log("Exiting OK...");
104
+ if (server) {
105
+ server.close();
106
+ }
107
+ process.exit(0);
108
+ } catch (error) {
109
+ log("Fatal error:", error);
110
+ if (server) {
111
+ server.close();
112
+ }
113
+ process.exit(1);
114
+ }
115
+ }
116
+ parseCommandLineArgs(process.argv.slice(2), "Usage: npx tsx client.ts <https://server-url> [callback-port] [--debug]").then(
117
+ ({
118
+ serverUrl,
119
+ callbackPort,
120
+ headers,
121
+ transportStrategy,
122
+ host,
123
+ staticOAuthClientMetadata,
124
+ staticOAuthClientInfo,
125
+ authTimeoutMs,
126
+ serverUrlHash
127
+ }) => {
128
+ return runClient(
129
+ serverUrl,
130
+ callbackPort,
131
+ headers,
132
+ transportStrategy,
133
+ host,
134
+ staticOAuthClientMetadata,
135
+ staticOAuthClientInfo,
136
+ authTimeoutMs,
137
+ serverUrlHash
138
+ );
139
+ }
140
+ ).catch((error) => {
141
+ console.error("Fatal error:", error);
142
+ process.exit(1);
143
+ });
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/proxy.js ADDED
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ JSONRPCMessageSchema,
4
+ NodeOAuthClientProvider,
5
+ connectToRemoteServer,
6
+ createLazyAuthCoordinator,
7
+ debugLog,
8
+ discoverOAuthServerInfo,
9
+ log,
10
+ mcpProxy,
11
+ parseCommandLineArgs,
12
+ setupSignalHandlers
13
+ } from "./chunk-AO2MMDOT.js";
14
+
15
+ // src/proxy.ts
16
+ import { EventEmitter } from "events";
17
+
18
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
19
+ import process2 from "process";
20
+
21
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
22
+ var ReadBuffer = class {
23
+ append(chunk) {
24
+ this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
25
+ }
26
+ readMessage() {
27
+ if (!this._buffer) {
28
+ return null;
29
+ }
30
+ const index = this._buffer.indexOf("\n");
31
+ if (index === -1) {
32
+ return null;
33
+ }
34
+ const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
35
+ this._buffer = this._buffer.subarray(index + 1);
36
+ return deserializeMessage(line);
37
+ }
38
+ clear() {
39
+ this._buffer = void 0;
40
+ }
41
+ };
42
+ function deserializeMessage(line) {
43
+ return JSONRPCMessageSchema.parse(JSON.parse(line));
44
+ }
45
+ function serializeMessage(message) {
46
+ return JSON.stringify(message) + "\n";
47
+ }
48
+
49
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
50
+ var StdioServerTransport = class {
51
+ constructor(_stdin = process2.stdin, _stdout = process2.stdout) {
52
+ this._stdin = _stdin;
53
+ this._stdout = _stdout;
54
+ this._readBuffer = new ReadBuffer();
55
+ this._started = false;
56
+ this._ondata = (chunk) => {
57
+ this._readBuffer.append(chunk);
58
+ this.processReadBuffer();
59
+ };
60
+ this._onerror = (error) => {
61
+ this.onerror?.(error);
62
+ };
63
+ }
64
+ /**
65
+ * Starts listening for messages on stdin.
66
+ */
67
+ async start() {
68
+ if (this._started) {
69
+ throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
70
+ }
71
+ this._started = true;
72
+ this._stdin.on("data", this._ondata);
73
+ this._stdin.on("error", this._onerror);
74
+ }
75
+ processReadBuffer() {
76
+ while (true) {
77
+ try {
78
+ const message = this._readBuffer.readMessage();
79
+ if (message === null) {
80
+ break;
81
+ }
82
+ this.onmessage?.(message);
83
+ } catch (error) {
84
+ this.onerror?.(error);
85
+ }
86
+ }
87
+ }
88
+ async close() {
89
+ this._stdin.off("data", this._ondata);
90
+ this._stdin.off("error", this._onerror);
91
+ const remainingDataListeners = this._stdin.listenerCount("data");
92
+ if (remainingDataListeners === 0) {
93
+ this._stdin.pause();
94
+ }
95
+ this._readBuffer.clear();
96
+ this.onclose?.();
97
+ }
98
+ send(message) {
99
+ return new Promise((resolve) => {
100
+ const json = serializeMessage(message);
101
+ if (this._stdout.write(json)) {
102
+ resolve();
103
+ } else {
104
+ this._stdout.once("drain", resolve);
105
+ }
106
+ });
107
+ }
108
+ };
109
+
110
+ // src/proxy.ts
111
+ async function runProxy(serverUrl, callbackPort, headers, transportStrategy = "http-first", host, staticOAuthClientMetadata, staticOAuthClientInfo, authorizeResource, ignoredTools, authTimeoutMs, serverUrlHash) {
112
+ const events = new EventEmitter();
113
+ const authCoordinator = createLazyAuthCoordinator(serverUrlHash, callbackPort, events, authTimeoutMs);
114
+ log("Discovering OAuth server configuration...");
115
+ const discoveryResult = await discoverOAuthServerInfo(serverUrl, headers);
116
+ if (discoveryResult.protectedResourceMetadata) {
117
+ log(`Discovered authorization server: ${discoveryResult.authorizationServerUrl}`);
118
+ if (discoveryResult.protectedResourceMetadata.scopes_supported) {
119
+ debugLog("Protected Resource Metadata scopes", {
120
+ scopes_supported: discoveryResult.protectedResourceMetadata.scopes_supported
121
+ });
122
+ }
123
+ } else {
124
+ debugLog("No Protected Resource Metadata found, using server URL as authorization server");
125
+ }
126
+ const authProvider = new NodeOAuthClientProvider({
127
+ serverUrl: discoveryResult.authorizationServerUrl,
128
+ callbackPort,
129
+ host,
130
+ clientName: "MCP CLI Proxy",
131
+ staticOAuthClientMetadata,
132
+ staticOAuthClientInfo,
133
+ authorizeResource,
134
+ serverUrlHash,
135
+ authorizationServerMetadata: discoveryResult.authorizationServerMetadata,
136
+ protectedResourceMetadata: discoveryResult.protectedResourceMetadata,
137
+ wwwAuthenticateScope: discoveryResult.wwwAuthenticateScope
138
+ });
139
+ const localTransport = new StdioServerTransport();
140
+ let server = null;
141
+ const authInitializer = async () => {
142
+ const authState = await authCoordinator.initializeAuth();
143
+ server = authState.server;
144
+ if (authState.skipBrowserAuth) {
145
+ log("Authentication was completed by another instance - will use tokens from disk");
146
+ await new Promise((res) => setTimeout(res, 1e3));
147
+ }
148
+ return {
149
+ waitForAuthCode: authState.waitForAuthCode,
150
+ skipBrowserAuth: authState.skipBrowserAuth
151
+ };
152
+ };
153
+ try {
154
+ const remoteTransport = await connectToRemoteServer(null, serverUrl, authProvider, headers, authInitializer, transportStrategy);
155
+ mcpProxy({
156
+ transportToClient: localTransport,
157
+ transportToServer: remoteTransport,
158
+ ignoredTools
159
+ });
160
+ await localTransport.start();
161
+ log("Local STDIO server running");
162
+ log(`Proxy established successfully between local STDIO and remote ${remoteTransport.constructor.name}`);
163
+ log("Press Ctrl+C to exit");
164
+ const cleanup = async () => {
165
+ await remoteTransport.close();
166
+ await localTransport.close();
167
+ if (server) {
168
+ server.close();
169
+ }
170
+ };
171
+ setupSignalHandlers(cleanup);
172
+ } catch (error) {
173
+ log("Fatal error:", error);
174
+ if (error instanceof Error && error.message.includes("self-signed certificate in certificate chain")) {
175
+ log(`You may be behind a VPN!
176
+
177
+ If you are behind a VPN, you can try setting the NODE_EXTRA_CA_CERTS environment variable to point
178
+ to the CA certificate file. If using claude_desktop_config.json, this might look like:
179
+
180
+ {
181
+ "mcpServers": {
182
+ "\${mcpServerName}": {
183
+ "command": "npx",
184
+ "args": [
185
+ "mcp-remote",
186
+ "https://remote.mcp.server/sse"
187
+ ],
188
+ "env": {
189
+ "NODE_EXTRA_CA_CERTS": "\${your CA certificate file path}.pem"
190
+ }
191
+ }
192
+ }
193
+ }
194
+ `);
195
+ }
196
+ if (server) {
197
+ server.close();
198
+ }
199
+ process.exit(1);
200
+ }
201
+ }
202
+ parseCommandLineArgs(process.argv.slice(2), "Usage: npx tsx proxy.ts <https://server-url> [callback-port] [--debug]").then(
203
+ ({
204
+ serverUrl,
205
+ callbackPort,
206
+ headers,
207
+ transportStrategy,
208
+ host,
209
+ debug,
210
+ staticOAuthClientMetadata,
211
+ staticOAuthClientInfo,
212
+ authorizeResource,
213
+ ignoredTools,
214
+ authTimeoutMs,
215
+ serverUrlHash
216
+ }) => {
217
+ return runProxy(
218
+ serverUrl,
219
+ callbackPort,
220
+ headers,
221
+ transportStrategy,
222
+ host,
223
+ staticOAuthClientMetadata,
224
+ staticOAuthClientInfo,
225
+ authorizeResource,
226
+ ignoredTools,
227
+ authTimeoutMs,
228
+ serverUrlHash
229
+ );
230
+ }
231
+ ).catch((error) => {
232
+ log("Fatal error:", error);
233
+ process.exit(1);
234
+ });
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "ma-mcp-remote",
3
+ "version": "0.1.41",
4
+ "description": "Remote proxy for Model Context Protocol, allowing local-only clients to connect to remote servers using oAuth",
5
+ "keywords": [
6
+ "mcp",
7
+ "stdio",
8
+ "sse",
9
+ "remote",
10
+ "oauth"
11
+ ],
12
+ "author": "tomerr",
13
+ "license": "MIT",
14
+ "repository": "https://github.com/geelen/mcp-remote",
15
+ "type": "module",
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "main": "dist/index.js",
22
+ "bin": {
23
+ "mcp-remote": "dist/proxy.js",
24
+ "mcp-remote-client": "dist/client.js"
25
+ },
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "build:watch": "tsup --watch",
29
+ "check": "prettier --check . && tsc",
30
+ "lint-fix": "prettier --check . --write",
31
+ "test:unit": "vitest run",
32
+ "test:unit:watch": "vitest"
33
+ },
34
+ "dependencies": {
35
+ "express": "^4.21.2",
36
+ "open": "^10.1.0",
37
+ "strict-url-sanitise": "^0.0.1",
38
+ "undici": "^7.12.0"
39
+ },
40
+ "devDependencies": {
41
+ "@modelcontextprotocol/sdk": "^1.23.0",
42
+ "zod": "^4.0.0",
43
+ "@types/express": "^5.0.0",
44
+ "@types/node": "^22.13.10",
45
+ "prettier": "^3.5.3",
46
+ "tsup": "^8.4.0",
47
+ "tsx": "^4.19.3",
48
+ "typescript": "^5.8.2",
49
+ "vitest": "^3.2.3"
50
+ },
51
+ "tsup": {
52
+ "entry": [
53
+ "src/client.ts",
54
+ "src/proxy.ts"
55
+ ],
56
+ "format": [
57
+ "esm"
58
+ ],
59
+ "dts": true,
60
+ "clean": true,
61
+ "outDir": "dist",
62
+ "external": []
63
+ },
64
+ "packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977"
65
+ }