Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@stackone/mcp-remote

Package Overview
Dependencies
Maintainers
3
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stackone/mcp-remote - npm Package Compare versions

Comparing version
0.0.1
to
0.0.4
+671
dist/chunk-2SYX4XUF.js
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
// src/lib/bearer-token-provider.ts
var BearerTokenProvider = class {
constructor(options) {
this.options = options;
}
// Required by OAuthClientProvider interface but not used for bearer token auth
get redirectUrl() {
return "http://localhost:0/unused";
}
// Required by OAuthClientProvider interface but not used for bearer token auth
get clientMetadata() {
return {
redirect_uris: [this.redirectUrl],
token_endpoint_auth_method: "none",
grant_types: [],
response_types: [],
client_name: "Bearer Token Client",
client_uri: ""
};
}
async clientInformation() {
return void 0;
}
async saveClientInformation() {
}
async tokens() {
return {
access_token: this.options.bearerToken,
token_type: "Bearer",
expires_in: 0,
// Never expires
refresh_token: ""
// No refresh token needed
};
}
async saveTokens() {
}
async redirectToAuthorization() {
}
async saveCodeVerifier() {
}
async codeVerifier() {
throw new Error("Code verifier not supported with bearer token authentication");
}
};
// src/lib/utils.ts
import crypto from "node:crypto";
import net from "node:net";
import {
UnauthorizedError
} from "@modelcontextprotocol/sdk/client/auth.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import express from "express";
// src/lib/version.ts
import { readFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
var __filename = fileURLToPath(import.meta.url);
var __dirname = dirname(__filename);
var packageJsonPath = join(__dirname, "../../package.json");
var getPackageVersion = async () => {
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
return packageJson.version;
};
// src/lib/utils.ts
var MCP_REMOTE_VERSION = "0.0.0";
getPackageVersion().then((version) => {
MCP_REMOTE_VERSION = version;
});
var pid = process.pid;
function log(str, ...rest) {
console.error(`[${pid}] ${str}`, ...rest);
}
function mcpProxy({
transportToClient,
transportToServer
}) {
let transportToClientClosed = false;
let transportToServerClosed = false;
transportToClient.onmessage = (message) => {
log("[Local\u2192Remote]", message.method || message.id);
transportToServer.send(message).catch(onServerError);
};
transportToServer.onmessage = (message) => {
log("[Remote\u2192Local]", message.method || message.id);
transportToClient.send(message).catch(onClientError);
};
transportToClient.onclose = () => {
if (transportToServerClosed) {
return;
}
transportToClientClosed = true;
transportToServer.close().catch(onServerError);
};
transportToServer.onclose = () => {
if (transportToClientClosed) {
return;
}
transportToServerClosed = true;
transportToClient.close().catch(onClientError);
};
transportToClient.onerror = onClientError;
transportToServer.onerror = onServerError;
function onClientError(error) {
log("Error from local client:", error);
}
function onServerError(error) {
log("Error from remote server:", error);
}
}
async function connectToRemoteServer(serverUrl, authProvider, waitForAuthCode, skipBrowserAuth = false) {
log(`[${pid}] Connecting to remote server: ${serverUrl}`);
const url = new URL(serverUrl);
const transport = new SSEClientTransport(url, { authProvider });
try {
await transport.start();
log("Connected to remote server");
return transport;
} catch (error) {
if (error instanceof UnauthorizedError || error instanceof Error && error.message.includes("Unauthorized")) {
if (skipBrowserAuth) {
log("Authentication required but skipping browser auth - using shared auth");
} else {
log("Authentication required. Waiting for authorization...");
}
const code = await waitForAuthCode();
try {
log("Completing authorization...");
await transport.finishAuth(code);
const newTransport = new SSEClientTransport(url, { authProvider });
await newTransport.start();
log("Connected to remote server after authentication");
return newTransport;
} catch (authError) {
log("Authorization error:", authError);
throw authError;
}
} else {
log("Connection error:", error);
throw error;
}
}
}
function setupOAuthCallbackServerWithLongPoll(options) {
let authCode = null;
const app = express();
let authCompletedResolve;
const authCompletedPromise = new Promise((resolve) => {
authCompletedResolve = resolve;
});
app.get("/wait-for-auth", (req, res) => {
if (authCode) {
log("Auth already completed, returning 200");
res.status(200).send("Authentication completed");
return;
}
if (req.query.poll === "false") {
log("Client requested no long poll, responding with 202");
res.status(202).send("Authentication in progress");
return;
}
const longPollTimeout = setTimeout(() => {
log("Long poll timeout reached, responding with 202");
res.status(202).send("Authentication in progress");
}, 3e4);
authCompletedPromise.then(() => {
clearTimeout(longPollTimeout);
if (!res.headersSent) {
log("Auth completed during long poll, responding with 200");
res.status(200).send("Authentication completed");
}
}).catch(() => {
clearTimeout(longPollTimeout);
if (!res.headersSent) {
log("Auth failed during long poll, responding with 500");
res.status(500).send("Authentication failed");
}
});
});
app.get(options.path, (req, res) => {
const code = req.query.code;
if (!code) {
res.status(400).send("Error: No authorization code received");
return;
}
authCode = code;
log("Auth code received, resolving promise");
authCompletedResolve(code);
res.send(
"Authorization successful! You may close this window and return to the CLI."
);
options.events.emit("auth-code-received", code);
});
const server = app.listen(options.port, () => {
log(`OAuth callback server running at http://127.0.0.1:${options.port}`);
});
const waitForAuthCode = () => {
return new Promise((resolve) => {
if (authCode) {
resolve(authCode);
return;
}
options.events.once("auth-code-received", (code) => {
resolve(code);
});
});
};
return { server, authCode, waitForAuthCode, authCompletedPromise };
}
async function findAvailablePort(preferredPort) {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.on("error", (err) => {
if (err.code === "EADDRINUSE") {
server.listen(0);
} else {
reject(err);
}
});
server.on("listening", () => {
const { port } = server.address();
server.close(() => {
resolve(port);
});
});
server.listen(preferredPort || 0);
});
}
async function parseCommandLineArgs(args, defaultPort, usage) {
const cleanIndex = args.indexOf("--clean");
const clean = cleanIndex !== -1;
if (clean) {
args.splice(cleanIndex, 1);
}
const bearerIndex = args.indexOf("--bearer");
const hasBearer = bearerIndex !== -1;
let bearerToken;
if (hasBearer) {
if (bearerIndex + 1 >= args.length) {
log("Error: --bearer flag requires a token value");
process.exit(1);
}
bearerToken = args[bearerIndex + 1];
args.splice(bearerIndex, 2);
}
const serverUrl = args[0];
const specifiedPort = args[1] ? Number.parseInt(args[1]) : void 0;
if (!serverUrl) {
log(usage);
process.exit(1);
}
const url = new URL(serverUrl);
const isLocalhost = (url.hostname === "localhost" || url.hostname === "127.0.0.1") && url.protocol === "http:";
if (!(url.protocol === "https:" || isLocalhost)) {
log(usage);
process.exit(1);
}
const callbackPort = specifiedPort || await findAvailablePort(defaultPort);
if (specifiedPort) {
log(`Using specified callback port: ${callbackPort}`);
} else {
log(`Using automatically selected callback port: ${callbackPort}`);
}
if (clean) {
log("Clean mode enabled: config files will be reset before reading");
}
return { serverUrl, callbackPort, clean, bearerToken };
}
function setupSignalHandlers(cleanup) {
process.on("SIGINT", async () => {
log("\nShutting down...");
await cleanup();
process.exit(0);
});
process.stdin.resume();
}
function getServerUrlHash(serverUrl) {
return crypto.createHash("md5").update(serverUrl).digest("hex");
}
// src/lib/coordination.ts
import express2 from "express";
// src/lib/mcp-auth-config.ts
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
var knownConfigFiles = [
"client_info.json",
"tokens.json",
"code_verifier.txt",
"lock.json"
];
async function createLockfile(serverUrlHash, pid2, port) {
const lockData = {
pid: pid2,
port,
timestamp: Date.now()
};
await writeJsonFile(serverUrlHash, "lock.json", lockData);
}
async function checkLockfile(serverUrlHash) {
try {
const lockfile = await readJsonFile(serverUrlHash, "lock.json", {
async parseAsync(data) {
if (typeof data !== "object" || data === null) return null;
if (typeof data.pid !== "number" || typeof data.port !== "number" || typeof data.timestamp !== "number") {
return null;
}
return data;
}
});
return lockfile || null;
} catch {
return null;
}
}
async function deleteLockfile(serverUrlHash) {
await deleteConfigFile(serverUrlHash, "lock.json");
}
async function cleanServerConfig(serverUrlHash) {
log(`Cleaning configuration files for server: ${serverUrlHash}`);
for (const filename of knownConfigFiles) {
await deleteConfigFile(serverUrlHash, filename);
}
}
function getConfigDir() {
const baseConfigDir = process.env.MCP_REMOTE_CONFIG_DIR || path.join(os.homedir(), ".mcp-auth");
return path.join(baseConfigDir, `mcp-remote-${MCP_REMOTE_VERSION}`);
}
async function ensureConfigDir() {
try {
const configDir = getConfigDir();
await fs.mkdir(configDir, { recursive: true });
} catch (error) {
log("Error creating config directory:", error);
throw error;
}
}
function getConfigFilePath(serverUrlHash, filename) {
const configDir = getConfigDir();
return path.join(configDir, `${serverUrlHash}_${filename}`);
}
async function deleteConfigFile(serverUrlHash, filename) {
try {
const filePath = getConfigFilePath(serverUrlHash, filename);
await fs.unlink(filePath);
} catch (error) {
if (error.code !== "ENOENT") {
log(`Error deleting ${filename}:`, error);
}
}
}
async function readJsonFile(serverUrlHash, filename, schema, clean = false) {
try {
await ensureConfigDir();
if (clean) {
await deleteConfigFile(serverUrlHash, filename);
return void 0;
}
const filePath = getConfigFilePath(serverUrlHash, filename);
const content = await fs.readFile(filePath, "utf-8");
const result = await schema.parseAsync(JSON.parse(content));
return result;
} catch (error) {
if (error.code === "ENOENT") {
return void 0;
}
log(`Error reading ${filename}:`, error);
return void 0;
}
}
async function writeJsonFile(serverUrlHash, filename, data) {
try {
await ensureConfigDir();
const filePath = getConfigFilePath(serverUrlHash, filename);
await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
} catch (error) {
log(`Error writing ${filename}:`, error);
throw error;
}
}
async function readTextFile(serverUrlHash, filename, errorMessage, clean = false) {
try {
await ensureConfigDir();
if (clean) {
await deleteConfigFile(serverUrlHash, filename);
throw new Error("File deleted due to clean flag");
}
const filePath = getConfigFilePath(serverUrlHash, filename);
return await fs.readFile(filePath, "utf-8");
} catch (error) {
throw new Error(errorMessage || `Error reading ${filename}`);
}
}
async function writeTextFile(serverUrlHash, filename, text) {
try {
await ensureConfigDir();
const filePath = getConfigFilePath(serverUrlHash, filename);
await fs.writeFile(filePath, text, "utf-8");
} catch (error) {
log(`Error writing ${filename}:`, error);
throw error;
}
}
// src/lib/coordination.ts
async function isPidRunning(pid2) {
try {
process.kill(pid2, 0);
return true;
} catch {
return false;
}
}
async function isLockValid(lockData) {
const MAX_LOCK_AGE = 30 * 60 * 1e3;
if (Date.now() - lockData.timestamp > MAX_LOCK_AGE) {
log("Lockfile is too old");
return false;
}
if (!await isPidRunning(lockData.pid)) {
log("Process from lockfile is not running");
return false;
}
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 1e3);
const response = await fetch(
`http://127.0.0.1:${lockData.port}/wait-for-auth?poll=false`,
{
signal: controller.signal
}
);
clearTimeout(timeout);
return response.status === 200 || response.status === 202;
} catch (error) {
log(`Error connecting to auth server: ${error.message}`);
return false;
}
}
async function waitForAuthentication(port) {
log(`Waiting for authentication from the server on port ${port}...`);
try {
while (true) {
const url = `http://127.0.0.1:${port}/wait-for-auth`;
log(`Querying: ${url}`);
const response = await fetch(url);
if (response.status === 200) {
log("Authentication completed by other instance");
return true;
}
if (response.status === 202) {
log("Authentication still in progress");
await new Promise((resolve) => setTimeout(resolve, 1e3));
} else {
log(`Unexpected response status: ${response.status}`);
return false;
}
}
} catch (error) {
log(`Error waiting for authentication: ${error.message}`);
return false;
}
}
async function coordinateAuth(serverUrlHash, callbackPort, events) {
const lockData = process.platform === "win32" ? null : await checkLockfile(serverUrlHash);
if (lockData && await isLockValid(lockData)) {
log(`Another instance is handling authentication on port ${lockData.port}`);
try {
const authCompleted = await waitForAuthentication(lockData.port);
if (authCompleted) {
log("Authentication completed by another instance");
const dummyServer = express2().listen(0);
const dummyWaitForAuthCode = () => {
log(
"WARNING: waitForAuthCode called in secondary instance - this is unexpected"
);
return new Promise(() => {
});
};
return {
server: dummyServer,
waitForAuthCode: dummyWaitForAuthCode,
skipBrowserAuth: true
};
}
log("Taking over authentication process...");
} catch (error) {
log(`Error waiting for authentication: ${error}`);
}
await deleteLockfile(serverUrlHash);
} else if (lockData) {
log("Found invalid lockfile, deleting it");
await deleteLockfile(serverUrlHash);
}
const { server, waitForAuthCode, authCompletedPromise } = setupOAuthCallbackServerWithLongPoll({
port: callbackPort,
path: "/oauth/callback",
events
});
const address = server.address();
const actualPort = address.port;
log(
`Creating lockfile for server ${serverUrlHash} with process ${process.pid} on port ${actualPort}`
);
await createLockfile(serverUrlHash, process.pid, actualPort);
const cleanupHandler = async () => {
try {
log(`Cleaning up lockfile for server ${serverUrlHash}`);
await deleteLockfile(serverUrlHash);
} catch (error) {
log(`Error cleaning up lockfile: ${error}`);
}
};
process.once("exit", () => {
try {
const configPath = getConfigFilePath(serverUrlHash, "lock.json");
__require("node:fs").unlinkSync(configPath);
} catch {
}
});
process.once("SIGINT", async () => {
await cleanupHandler();
});
return {
server,
waitForAuthCode,
skipBrowserAuth: false
};
}
// src/lib/node-oath-client-provider.ts
import {
OAuthClientInformationSchema,
OAuthTokensSchema
} from "@modelcontextprotocol/sdk/shared/auth.js";
import open from "open";
var NodeOAuthClientProvider = class {
/**
* Creates a new NodeOAuthClientProvider
* @param options Configuration options for the provider
*/
constructor(options) {
this.options = options;
this.serverUrlHash = getServerUrlHash(options.serverUrl);
this.callbackPath = options.callbackPath || "/oauth/callback";
this.clientName = options.clientName || "MCP CLI Client";
this.clientUri = options.clientUri || "https://github.com/modelcontextprotocol/mcp-cli";
if (options.clean) {
cleanServerConfig(this.serverUrlHash).catch((err) => {
log("Error cleaning server config:", err);
});
}
}
serverUrlHash;
callbackPath;
clientName;
clientUri;
get redirectUrl() {
return `http://127.0.0.1:${this.options.callbackPort}${this.callbackPath}`;
}
get clientMetadata() {
return {
redirect_uris: [this.redirectUrl],
token_endpoint_auth_method: "none",
grant_types: ["authorization_code", "refresh_token"],
response_types: ["code"],
client_name: this.clientName,
client_uri: this.clientUri
};
}
/**
* Gets the client information if it exists
* @returns The client information or undefined
*/
async clientInformation() {
return readJsonFile(
this.serverUrlHash,
"client_info.json",
OAuthClientInformationSchema,
this.options.clean
);
}
/**
* Saves client information
* @param clientInformation The client information to save
*/
async saveClientInformation(clientInformation) {
await writeJsonFile(this.serverUrlHash, "client_info.json", clientInformation);
}
/**
* Gets the OAuth tokens if they exist
* @returns The OAuth tokens or undefined
*/
async tokens() {
return readJsonFile(
this.serverUrlHash,
"tokens.json",
OAuthTokensSchema,
this.options.clean
);
}
/**
* Saves OAuth tokens
* @param tokens The tokens to save
*/
async saveTokens(tokens) {
await writeJsonFile(this.serverUrlHash, "tokens.json", tokens);
}
/**
* Redirects the user to the authorization URL
* @param authorizationUrl The URL to redirect to
*/
async redirectToAuthorization(authorizationUrl) {
log(`
Please authorize this client by visiting:
${authorizationUrl.toString()}
`);
try {
await open(authorizationUrl.toString());
log("Browser opened automatically.");
} catch (error) {
log(
"Could not open browser automatically. Please copy and paste the URL above into your browser."
);
}
}
/**
* Saves the PKCE code verifier
* @param codeVerifier The code verifier to save
*/
async saveCodeVerifier(codeVerifier) {
await writeTextFile(this.serverUrlHash, "code_verifier.txt", codeVerifier);
}
/**
* Gets the PKCE code verifier
* @returns The code verifier
*/
async codeVerifier() {
return await readTextFile(
this.serverUrlHash,
"code_verifier.txt",
"No code verifier saved for session",
this.options.clean
);
}
};
export {
BearerTokenProvider,
MCP_REMOTE_VERSION,
log,
mcpProxy,
connectToRemoteServer,
parseCommandLineArgs,
setupSignalHandlers,
getServerUrlHash,
coordinateAuth,
NodeOAuthClientProvider
};
#!/usr/bin/env node
#!/usr/bin/env node
import {
BearerTokenProvider,
MCP_REMOTE_VERSION,
NodeOAuthClientProvider,
coordinateAuth,
getServerUrlHash,
log,
parseCommandLineArgs,
setupSignalHandlers
} from "./chunk-2SYX4XUF.js";
// src/client.ts
import { EventEmitter } from "node:events";
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import {
ListResourcesResultSchema,
ListToolsResultSchema
} from "@modelcontextprotocol/sdk/types.js";
import express from "express";
async function runClient(serverUrl, callbackPort, clean = false, bearerToken) {
const events = new EventEmitter();
const authProvider = bearerToken ? new BearerTokenProvider({ serverUrl, bearerToken }) : new NodeOAuthClientProvider({
serverUrl,
callbackPort,
clientName: "MCP CLI Client",
clean
});
const { server, waitForAuthCode, skipBrowserAuth } = bearerToken ? {
server: express().listen(0),
// Use imported express instead of require
waitForAuthCode: () => Promise.resolve(""),
// Dummy function
skipBrowserAuth: true
} : await coordinateAuth(getServerUrlHash(serverUrl), callbackPort, events);
if (skipBrowserAuth && !bearerToken) {
log(
"Authentication was completed by another instance - will use tokens from disk..."
);
await new Promise((res) => setTimeout(res, 1e3));
}
const client = new Client(
{
name: "mcp-remote",
version: MCP_REMOTE_VERSION
},
{
capabilities: {}
}
);
const url = new URL(serverUrl);
function initTransport() {
const transport2 = new SSEClientTransport(url, { authProvider });
transport2.onmessage = (message) => {
log("Received message:", JSON.stringify(message, null, 2));
};
transport2.onerror = (error) => {
log("Transport error:", error);
};
transport2.onclose = () => {
log("Connection closed.");
process.exit(0);
};
return transport2;
}
const transport = initTransport();
const cleanup = async () => {
log("\nClosing connection...");
await client.close();
server.close();
};
setupSignalHandlers(cleanup);
try {
log("Connecting to server...");
await client.connect(transport);
log("Connected successfully!");
} catch (error) {
if (error instanceof UnauthorizedError || error instanceof Error && error.message.includes("Unauthorized")) {
log("Authentication required. Waiting for authorization...");
const code = await waitForAuthCode();
try {
log("Completing authorization...");
await transport.finishAuth(code);
log("Connecting after authorization...");
await client.connect(initTransport());
log("Connected successfully!");
log("Requesting tools list...");
const tools = await client.request(
{ method: "tools/list" },
ListToolsResultSchema
);
log("Tools:", JSON.stringify(tools, null, 2));
log("Requesting resource list...");
const resources = await client.request(
{ method: "resources/list" },
ListResourcesResultSchema
);
log("Resources:", JSON.stringify(resources, null, 2));
log("Listening for messages. Press Ctrl+C to exit.");
} catch (authError) {
log("Authorization error:", authError);
server.close();
process.exit(1);
}
} else {
log("Connection error:", error);
server.close();
process.exit(1);
}
}
try {
log("Requesting tools list...");
const tools = await client.request({ method: "tools/list" }, ListToolsResultSchema);
log("Tools:", JSON.stringify(tools, null, 2));
} catch (e) {
log("Error requesting tools list:", e);
}
try {
log("Requesting resource list...");
const resources = await client.request(
{ method: "resources/list" },
ListResourcesResultSchema
);
log("Resources:", JSON.stringify(resources, null, 2));
} catch (e) {
log("Error requesting resources list:", e);
}
log("Listening for messages. Press Ctrl+C to exit.");
}
parseCommandLineArgs(
process.argv.slice(2),
3333,
"Usage: npx tsx client.ts [--clean] [--bearer <token>] <https://server-url> [callback-port]"
).then(({ serverUrl, callbackPort, clean, bearerToken }) => {
return runClient(serverUrl, callbackPort, clean, bearerToken);
}).catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
#!/usr/bin/env node
#!/usr/bin/env node
import {
BearerTokenProvider,
NodeOAuthClientProvider,
connectToRemoteServer,
coordinateAuth,
getServerUrlHash,
log,
mcpProxy,
parseCommandLineArgs,
setupSignalHandlers
} from "./chunk-2SYX4XUF.js";
// src/proxy.ts
import { EventEmitter } from "node:events";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import express from "express";
async function runProxy(serverUrl, callbackPort, clean = false, bearerToken) {
const events = new EventEmitter();
const authProvider = bearerToken ? new BearerTokenProvider({ serverUrl, bearerToken }) : new NodeOAuthClientProvider({
serverUrl,
callbackPort,
clientName: "MCP CLI Proxy",
clean
});
const { server, waitForAuthCode, skipBrowserAuth } = bearerToken ? {
server: express().listen(0),
// Use imported express instead of require
waitForAuthCode: () => Promise.resolve(""),
// Dummy function
skipBrowserAuth: true
} : await coordinateAuth(getServerUrlHash(serverUrl), callbackPort, events);
if (skipBrowserAuth && !bearerToken) {
log("Authentication was completed by another instance - will use tokens from disk");
await new Promise((res) => setTimeout(res, 1e3));
}
const localTransport = new StdioServerTransport();
try {
const remoteTransport = await connectToRemoteServer(
serverUrl,
authProvider,
waitForAuthCode,
skipBrowserAuth
);
mcpProxy({
transportToClient: localTransport,
transportToServer: remoteTransport
});
await localTransport.start();
log("Local STDIO server running");
log("Proxy established successfully between local STDIO and remote SSE");
log("Press Ctrl+C to exit");
const cleanup = async () => {
await remoteTransport.close();
await localTransport.close();
server.close();
};
setupSignalHandlers(cleanup);
} catch (error) {
log("Fatal error:", error);
if (error instanceof Error && error.message.includes("self-signed certificate in certificate chain")) {
log(`You may be behind a VPN!
If you are behind a VPN, you can try setting the NODE_EXTRA_CA_CERTS environment variable to point
to the CA certificate file. If using claude_desktop_config.json, this might look like:
{
"mcpServers": {
"\${mcpServerName}": {
"command": "npx",
"args": [
"mcp-remote",
"https://remote.mcp.server/sse"
],
"env": {
"NODE_EXTRA_CA_CERTS": "\${your CA certificate file path}.pem"
}
}
}
}
`);
}
server.close();
process.exit(1);
}
}
parseCommandLineArgs(
process.argv.slice(2),
3334,
"Usage: npx tsx proxy.ts [--clean] [--bearer <token>] <https://server-url> [callback-port]"
).then(({ serverUrl, callbackPort, clean, bearerToken }) => {
return runProxy(serverUrl, callbackPort, clean, bearerToken);
}).catch((error) => {
log("Fatal error:", error);
process.exit(1);
});
+2
-2
{
"name": "@stackone/mcp-remote",
"version": "0.0.1",
"version": "0.0.4",
"type": "module",

@@ -56,3 +56,3 @@ "main": "./dist/index.js",

"typecheck": "tsc --noEmit",
"publish-release": "pnpm publish --no-git-checks --access public",
"publish-release": "pnpm publish --access public",
"client": "tsx src/client.ts",

@@ -59,0 +59,0 @@ "proxy": "tsx src/proxy.ts"

@@ -30,3 +30,3 @@ # MCP Remote

"command": "npx",
"args": ["mcp-remote", "https://remote.mcp.server/sse"]
"args": ["@stackone/mcp-remote", "https://remote.mcp.server/sse"]
}

@@ -52,3 +52,3 @@ }

"args": [
"mcp-remote",
"@stackone/mcp-remote",
"--bearer",

@@ -71,3 +71,3 @@ "your-bearer-token",

"-y",
"mcp-remote",
"@stackone/mcp-remote",
"https://remote.mcp.server/sse"

@@ -90,3 +90,3 @@ ]

"args": [
"mcp-remote",
"@stackone/mcp-remote",
"https://remote.mcp.server/sse",

@@ -101,3 +101,3 @@ "--clean"

"args": [
"mcp-remote",
"@stackone/mcp-remote",
"https://remote.mcp.server/sse",

@@ -167,3 +167,3 @@ "9696"

"command": "npx",
"args": ["mcp-remote", "https://remote.mcp.server/sse"],
"args": ["@stackone/mcp-remote", "https://remote.mcp.server/sse"],
"env": {

@@ -170,0 +170,0 @@ "NODE_EXTRA_CA_CERTS": "{your CA certificate file path}.pem"