diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..c58f197
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,27 @@
+name: Publish Any Commit
+on:
+ pull_request:
+ push:
+ branches:
+ - "**"
+ tags:
+ - "!**"
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup pnpm & install
+ uses: wyvox/action-setup-pnpm@v3
+ with:
+ node-version: 22
+ pnpm-version: 10
+
+ - name: Build
+ run: pnpm build
+
+ - run: pnpm dlx pkg-pr-new publish --compact --bin
diff --git a/README.md b/README.md
index 2afc364..f721e98 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ To bypass authentication, or to emit custom headers on all requests to your remo
}
```
-**Note:** Cursor has a bug where spaces inside `args` aren't escaped when it invokes `npx`, which ends up mangling these values. You can work around it using:
+**Note:** Cursor and Claude Desktop (Windows) have a bug where spaces inside `args` aren't escaped when it invokes `npx`, which ends up mangling these values. You can work around it using:
```jsonc
{
diff --git a/package.json b/package.json
index bb010a7..4c7a40d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mcp-remote",
- "version": "0.1.1",
+ "version": "0.1.4",
"description": "Remote proxy for Model Context Protocol, allowing local-only clients to connect to remote servers using oAuth",
"keywords": [
"mcp",
@@ -35,9 +35,7 @@
"@modelcontextprotocol/sdk": "^1.10.2",
"@types/express": "^5.0.0",
"@types/node": "^22.13.10",
- "@types/react": "^19.0.12",
"prettier": "^3.5.3",
- "react": "^19.0.0",
"tsup": "^8.4.0",
"tsx": "^4.19.3",
"typescript": "^5.8.2"
@@ -53,8 +51,6 @@
"dts": true,
"clean": true,
"outDir": "dist",
- "external": [
- "react"
- ]
+ "external": []
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 48d8491..a987cf4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -24,15 +24,9 @@ importers:
'@types/node':
specifier: ^22.13.10
version: 22.13.10
- '@types/react':
- specifier: ^19.0.12
- version: 19.0.12
prettier:
specifier: ^3.5.3
version: 3.5.3
- react:
- specifier: ^19.0.0
- version: 19.0.0
tsup:
specifier: ^8.4.0
version: 8.4.0(tsx@4.19.3)(typescript@5.8.2)
@@ -350,9 +344,6 @@ packages:
'@types/range-parser@1.2.7':
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
- '@types/react@19.0.12':
- resolution: {integrity: sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==}
-
'@types/send@0.17.4':
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
@@ -479,9 +470,6 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
- csstype@3.1.3:
- resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
-
debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
@@ -899,10 +887,6 @@ packages:
resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==}
engines: {node: '>= 0.8'}
- react@19.0.0:
- resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
- engines: {node: '>=0.10.0'}
-
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
@@ -1334,10 +1318,6 @@ snapshots:
'@types/range-parser@1.2.7': {}
- '@types/react@19.0.12':
- dependencies:
- csstype: 3.1.3
-
'@types/send@0.17.4':
dependencies:
'@types/mime': 1.3.5
@@ -1474,8 +1454,6 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
- csstype@3.1.3: {}
-
debug@2.6.9:
dependencies:
ms: 2.0.0
@@ -1896,8 +1874,6 @@ snapshots:
iconv-lite: 0.6.3
unpipe: 1.0.0
- react@19.0.0: {}
-
readdirp@4.1.2: {}
resolve-from@5.0.0: {}
diff --git a/src/lib/node-oauth-client-provider.ts b/src/lib/node-oauth-client-provider.ts
index 1e58b7e..806f3af 100644
--- a/src/lib/node-oauth-client-provider.ts
+++ b/src/lib/node-oauth-client-provider.ts
@@ -37,7 +37,7 @@ export class NodeOAuthClientProvider implements OAuthClientProvider {
}
get redirectUrl(): string {
- return `http://127.0.0.1:${this.options.callbackPort}${this.callbackPath}`
+ return `http://localhost:${this.options.callbackPort}${this.callbackPath}`
}
get clientMetadata() {
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 28ef0e5..28fb4b3 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -32,14 +32,21 @@ export function mcpProxy({ transportToClient, transportToServer }: { transportTo
let transportToClientClosed = false
let transportToServerClosed = false
- transportToClient.onmessage = (message) => {
- // @ts-expect-error TODO
+ transportToClient.onmessage = (_message) => {
+ // TODO: fix types
+ const message = _message as any
log('[Local→Remote]', message.method || message.id)
+ if (message.method === 'initialize') {
+ const { clientInfo } = message.params
+ if (clientInfo) clientInfo.name = `${clientInfo.name} (via mcp-remote ${MCP_REMOTE_VERSION})`
+ log(JSON.stringify(message, null, 2))
+ }
transportToServer.send(message).catch(onServerError)
}
- transportToServer.onmessage = (message) => {
- // @ts-expect-error TODO: fix this type
+ transportToServer.onmessage = (_message) => {
+ // TODO: fix types
+ const message = _message as any
log('[Remote→Local]', message.method || message.id)
transportToClient.send(message).catch(onClientError)
}
@@ -305,7 +312,16 @@ export function setupOAuthCallbackServerWithLongPoll(options: OAuthCallbackServe
log(`Redirecting to post-auth redirect URI: ${postAuthRedirectUri}`)
res.redirect(postAuthRedirectUri)
} else {
- res.send('Authorization successful! You may close this window and return to the CLI.')
+ res.send(`
+ Authorization successful!
+ You may close this window and return to the CLI.
+
+ `)
}
// Notify main flow that auth code is available
@@ -382,8 +398,9 @@ export async function findAvailablePort(preferredPort?: number): Promise
export async function parseCommandLineArgs(args: string[], defaultPort: number, usage: string) {
// Process headers
const headers: Record = {}
- args.forEach((arg, i) => {
- if (arg === '--header' && i < args.length - 1) {
+ let i = 0
+ while (i < args.length) {
+ if (args[i] === '--header' && i < args.length - 1) {
const value = args[i + 1]
const match = value.match(/^([A-Za-z0-9_-]+):(.*)$/)
if (match) {
@@ -392,8 +409,11 @@ export async function parseCommandLineArgs(args: string[], defaultPort: number,
log(`Warning: ignoring invalid header argument: ${value}`)
}
args.splice(i, 2)
+ // Do not increment i, as the array has shifted
+ continue
}
- })
+ i++
+ }
const serverUrl = args[0]
const specifiedPort = args[1] ? parseInt(args[1]) : undefined
@@ -470,6 +490,11 @@ export function setupSignalHandlers(cleanup: () => Promise) {
// Keep the process alive
process.stdin.resume()
+ process.stdin.on('end', async () => {
+ log('\nShutting down...')
+ await cleanup()
+ process.exit(0)
+ })
}
/**
diff --git a/tsconfig.json b/tsconfig.json
index cd9cfa1..9bfece1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,7 +7,7 @@
"esModuleInterop": true,
"noEmit": true,
"lib": ["ES2022", "DOM"],
- "types": ["node", "react"],
+ "types": ["node"],
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
}