Skip to content

Can't Run Multiple mcp-remote Servers at the Same Time #25

Closed

Description

@CahidArda

Hi there,

Thanks for the library, it's been great to use!

I have encountered a problem however. It doesn't work when I start mcp-remote in two terminals at the same time. This also causes issues when I try using copilot/claude/cursor while developing my server. I have to close the mcp-remote in my terminal each time.

Steps to Reproduce:

Run npx mcp-remote in two terminals at the same time. Second one doesn't work.

Details

I am connecting to an SSE server at port 8080 with:

npx mcp-remote http://localhost:8080/sse --header "Authorization: Bearer authToken"

First time I run it, it works fine. I get these logs (I changed the port numbers a little bit):

[43434] Using automatically selected callback port: 12121
[43434] Using custom headers: {"Authorization":" Bearer authToken"}
[43434] Creating lockfile for server 4f9e15cb6 with process 43434 on port 12121
[43434] OAuth callback server running at http://127.0.0.1:12121
[43434] [43434] Connecting to remote server: http://localhost:8080/sse
[43434] Connected to remote server
[43434] Local STDIO server running
[43434] Proxy established successfully between local STDIO and remote SSE
[43434] Press Ctrl+C to exit

While the server is still open in the terminal, I run the same command in another terminal and get:

[78787] Using automatically selected callback port: 90909
[78787] Using custom headers: {"Authorization":" Bearer authToken"}
[78787] Another instance is handling authentication on port 12121
[78787] Waiting for authentication from the server on port 12121...
[78787] Querying: http://127.0.0.1:12121/wait-for-auth
[78787] Authentication still in progress
[78787] Querying: http://127.0.0.1:12121/wait-for-auth
[78787] Authentication still in progress
[78787] Querying: http://127.0.0.1:12121/wait-for-auth

Note that 12121 ports are the same in both terminals.

While the second terminal sends the queries, the first terminal starts printing these logs:

[43434] Client requested no long poll, responding with 202
[43434] Long poll timeout reached, responding with 202
[43434] Long poll timeout reached, responding with 202
[43434] Long poll timeout reached, responding with 202

Activity

  1. geelen commented on Apr 16, 2025

    @geelen
    Collaborator

    Damn, so this is something I added to stop Claude Code firing up heaps of authorization windows (it forks 2 processes every time it boots, I have no idea why, and when your computer wakes from sleep you often see dozens of windows having opened up) It has been an improvement, but what I was hoping was that the two processes would coordinate and share the auth tokens once they appear on disk. That's been flaky from the beginning.

    I think a --allow-concurrent flag or similar that just skips that whole deduping might be worth doing, or make the current behaviour opt-in for Claude only 🤔

    Oh and it's only relevant to auth, so I suppose if you have a Bearer token it should be automatically disabled in the first place...

  2. glenngillen commented on Apr 16, 2025

    @glenngillen

    Damn, so this is something I added to stop Claude Code firing up heaps of authorization windows (it forks 2 processes every time it boots, I have no idea why, and when your computer wakes from sleep you often see dozens of windows having opened up)

    Thank you for working out what this and putting a fix in place (even if it's imperfect). I was losing my mind about how/why I was ending up with so many auth tabs open.

  3. jmorrell-cloudflare commented on Apr 16, 2025

    @jmorrell-cloudflare
    Contributor

    @geelen We could block windows based on PID maybe? 🤔

  4. geelen commented on Apr 17, 2025

    @geelen
    Collaborator

    @CahidArda can you try mcp-remote@latest? I've pushed the authorization flow later, so in the case where you're passing your own bearer token it shouldn't hit the dupe-prevention logic at all.

    Let me know how you go!

  5. geelen commented on Apr 17, 2025

    @geelen
    Collaborator

    @geelen We could block windows based on PID maybe? 🤔

    The way I've come to understand it, the really pernicious bug is when the mcp-remote process lives only long enough to open the browser window then dies. So there's nothing to block on, the browser tab is actually useless since it's redirecting to a localhost port that's no longer open, but still when you wake your laptop from sleep you have a thousand tabs open 🫠

    With the change in #32 there's a chance that the problem goes away as I'm being far less eager about trying to handshake the oauth. So, we'll see...

  6. iWayne commented on Apr 18, 2025

    @iWayne

    Face the same issue while quitting Claude desktop app doesn't kill the running mcp-remote. Once re-open Claude app, new mcp-remote process will get stuck.

  7. lzilioli commented on Apr 24, 2025

    @lzilioli

    Face the same issue while quitting Claude desktop app doesn't kill the running mcp-remote. Once re-open Claude app, new mcp-remote process will get stuck.

    I'm having the same issue. Very annoying. I constantly have to check the logs and kill the process listening on the stale port.

  8. emilisb commented on Apr 25, 2025

    @emilisb

    Hi! First of all, thank you for this amazing library - good job!

    We as well ran into issues with multiple mcp-remote servers. More specifically:

    • After a while you can no longer connect to a remote mcp and you need to remove ~/.mcp-auth folder.
    • (might be related to same issue - maybe not) Sometimes when a Mac is put to sleep the process opens hundreds of auth tabs in the browser - colleagues mostly encountered this using Claude Desktop.

    I did some debugging and these are my findings:

    Issue 1

    1. When the first mcp-remote process is started, the auth http server is listening on port 3334 (default).
      1.1. A new lockfile is created (with current timestamp and port 3334).
      1.2. If user has not authenticated before - auth flow will start and new token files will be written to disk.
      1.3. client_info.json will contain callback url http://127.0.0.1:3334/oauth/callback <- please pay attention to the port being 3334 here.
    2. When additional mcp-remote processes start, they check for an existing lock file. If the lock file is valid, auth flow uses the original process (using port 3334). Everything is working fine here.

    The tricky part begins when more than 30 minutes have passed since the original process start time. Lock files older than 30 minutes are considered invalid (link to code), therefore a new lock file will be created. However, this time the port might not be 3334 - if the original process using 3334 is still running, the process will choose a random port and this new process will become the new "master" process (responsible for auth of other processes).

    The problem is that now the new process has a different port than the previous one, therefore the new callback url is different. However, client_info.json still contains http://127.0.0.1:3334/oauth/callback. This causes oauth token verification to fail on the server and you can no longer connect to the MCP server until you remove old tokens from ~/.mcp-auth.

    In my opinion, the following changes might help:

    1. Remove the 30 minutes timestamp limit - I believe pid + port checks might be good enough. @geelen do you remember if there was any specific reason for this check?
    2. If a new "master' process is started on a new port, we should delete old auth tokens to avoid the issue with different callback url.

    Issue 2

    I think this is the issue that OP had:

    1. mcp-remote is started for the first time and no auth token exists.
    2. User logs in. Auth token is saved.
    3. New mcp-remote processes will work (if created within 30 minutes of first process start).
    4. All mcp-remote process are killed.
    5. One new mcp-remote process is created (on default port 3334) - we now reuse the previously created tokens.
    6. New mcp-remote processes will fail to log in because authCompletedPromise is never resolved.
  9. emilisb commented on Apr 25, 2025

    @emilisb

    I opened #39 with my proposed solutions

  10. unforced commented on Apr 30, 2025

    @unforced

    Appreciate yah'll looking at this!

    Bumped into this here, trying to set up an mcp-remote server in AnythingLLM.
    Even after I changed the port to use 3339 it still keeps giving me the same error with 3334.
    I'm assuming now that this is likely due to the lockfile?

    {
      "mcpServers": {
    
        "n8n-mcp-server": {
          "command": "npx",
          "args": [
            "n8n-mcp-server"
          ],
          "env": {
            "NODE_PATH": "/usr/bin/node",
            "PATH": "/usr/bin/node:/usr/local/bin:/usr/bin:/bin",
            "N8N_API_URL": "http://n8n:5678/api/v1",
            "N8N_API_KEY": "[REDACTED]",
            "N8N_WEBHOOK_USERNAME": "AnythingLLM",
            "N8N_WEBHOOK_PASSWORD": "[REDACTED]"
          }
        },
        "n8n-docs": {
          "command": "npx",
          "args": [
            "mcp-remote",
            "https://gitmcp.io/n8n-io/n8n-docs",
            "3339"
          ]
        }
      }
    }
    
  11. unforced commented on Apr 30, 2025

    @unforced

    Waited the night and tried again and it seems to be working, so yep seems lockfile was probably the issue for me.

  12. clouatre commented on Sep 13, 2025

    @clouatre
    Contributor

    Concrete Multi-Instance Implementation & Findings

    We've implemented mcp-remote across multiple MCP clients (Claude Code, OpenCode, Amazon Q) and discovered specific technical limitations with multiple Atlassian instances.

    Working Multi-Instance Example (Claude Code)

    Claude Code handles this scenario seamlessly with simple configuration (~/.claude.json):

    {
      "mcpServers": {
        "atlassian_confluence": {
          "command": "mcp-remote",
          "args": ["https://mcp.atlassian.com/v1/sse"]
        },
        "atlassian_jira": {
          "command": "mcp-remote",
          "args": ["https://mcp.atlassian.com/v1/sse"]
        },
        "atlassian_tenant2": {
          "command": "mcp-remote",
          "args": ["https://mcp.atlassian.com/v1/sse"]
        }
      }
    }

    Claude Code provides:

    • Separate OAuth sessions for each server instance
    • Built-in re-authentication menu when tokens expire
    • No coordination conflicts between instances

    OpenCode Attempted Implementation

    OpenCode configuration (~/.config/opencode/opencode.json):

    {
      "mcp": {
        "atlassian_confluence": {
          "type": "local",
          "command": ["mcp-remote", "https://mcp.atlassian.com/v1/sse"],
          "enabled": true
        },
        "atlassian_jira": {
          "type": "local",
          "command": ["mcp-remote", "https://mcp.atlassian.com/v1/sse"],
          "enabled": true
        },
        "atlassian_tenant2": {
          "type": "local",
          "command": ["mcp-remote", "https://mcp.atlassian.com/v1/sse"],
          "enabled": true
        }
      }
    }

    Observed Behavior Difference

    Claude Code (Working): Each server connects to different Atlassian tenants during OAuth
    OpenCode (Limited): All three servers share the same OAuth session and connect to whichever tenant was authorized first

    Technical Root Cause

    Authentication coordination in coordination.ts:

    • Single lockfile coordinates all mcp-remote processes
    • Shared OAuth session prevents instance-specific authentication
    • 30-minute lockfile expiry (as @unforced discovered) temporarily blocks new instances

    Evidence from Testing

    1. Single instance: Works perfectly with full OAuth flow
    2. Multiple instances: getAccessibleAtlassianResources returns same tenant for all servers
    3. Cache location: ~/.mcp-auth/mcp-remote-*/ contains shared session data

    Enterprise Impact & User Expectations

    Multiple Atlassian tenants are common in enterprise environments:

    • Different client projects
    • Separate business units
    • Internal vs external documentation

    User Expectation: Based on Claude Code's seamless multi-instance support, users expect the same behavior across MCP clients.

    Since Atlassian officially endorses mcp-remote (Getting Started Guide), this limitation affects the official enterprise integration path.

    Potential Technical Solutions

    1. Instance-specific cache directories based on server name/config hash
    2. --allow-concurrent flag to bypass coordination entirely
    3. Tenant parameter in mcp-remote command to isolate OAuth sessions
  13. clouatre commented on Sep 13, 2025

    @clouatre
    Contributor

    Concrete Multi-Instance Implementation & Findings

    We've tested mcp-remote across multiple MCP clients and discovered specific technical limitations with multiple Atlassian instances. The issue is specifically with mcp-remote's authentication coordination, not the Atlassian MCP server itself.

    Working Multi-Instance Example (Claude Code - Native SSE)

    Claude Code handles multiple Atlassian instances seamlessly because it has native SSE support and connects directly to the server:

    {
      "mcpServers": {
        "atlassian_slalom": {
          "type": "sse",
          "url": "https://mcp.atlassian.com/v1/sse"
        },
        "atlassian_plusgrade": {
          "type": "sse",
          "url": "https://mcp.atlassian.com/v1/sse"
        },
        "atlassian_pointsbu": {
          "type": "sse",
          "url": "https://mcp.atlassian.com/v1/sse"
        }
      }
    }

    Claude Code provides:

    • Direct SSE connection to Atlassian MCP server (no proxy needed)
    • Separate OAuth sessions for each server instance
    • Built-in re-authentication menu when tokens expire
    • No coordination conflicts between instances

    Limited Multi-Instance Example (OpenCode - using mcp-remote)

    OpenCode requires a proxy for SSE transport. We chose mcp-remote (other options like mcp-proxy exist):

    {
      "mcp": {
        "atlassian_slalom": {
          "type": "local",
          "command": ["mcp-remote", "https://mcp.atlassian.com/v1/sse"],
          "enabled": true
        },
        "atlassian_plusgrade": {
          "type": "local",
          "command": ["mcp-remote", "https://mcp.atlassian.com/v1/sse"],
          "enabled": true
        },
        "atlassian_pointsbu": {
          "type": "local",
          "command": ["mcp-remote", "https://mcp.atlassian.com/v1/sse"],
          "enabled": true
        }
      }
    }

    Observed Behavior Difference

    Claude Code (Native SSE): Each server connects to different Atlassian tenants during OAuth
    OpenCode (via mcp-remote): All three servers share the same OAuth session and connect to whichever tenant was authorized first

    Technical Root Cause

    Authentication coordination in coordination.ts:

    • Single lockfile coordinates all mcp-remote processes
    • Shared OAuth session prevents instance-specific authentication
    • 30-minute lockfile expiry (as @unforced discovered) temporarily blocks new instances

    Evidence from Testing

    1. Single instance: Works perfectly with full OAuth flow
    2. Multiple instances: getAccessibleAtlassianResources returns same tenant for all servers
    3. Cache location: ~/.mcp-auth/mcp-remote-*/ contains shared session data

    Enterprise Impact & User Expectations

    Multiple Atlassian tenants are common in enterprise environments:

    • Different client projects
    • Separate business units
    • Internal vs external documentation

    User Expectation: Based on Claude Code's seamless multi-instance support, users expect the same behavior when using mcp-remote.

    Since Atlassian officially endorses mcp-remote (Getting Started Guide), this limitation affects the official enterprise integration path for MCP clients using mcp-remote as their SSE proxy.

    Potential Technical Solutions

    1. Instance-specific cache directories based on server name/config hash
    2. --allow-concurrent flag to bypass coordination entirely
    3. Tenant parameter in mcp-remote command to isolate OAuth sessions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions