Model or dataset
punkpeye/mcp-remote avatar
punkpeye/mcp-remote

mcp-remote: a stdio-to-HTTP bridge for MCP clients that cannot do OAuth yet

Connect an MCP Client that only supports local (stdio) servers to a Remote MCP Server.

1,592 stars292 forksTypeScriptMIT

At a glance

What is it?
mcp-remote is a TypeScript shim that lets stdio-only MCP clients reach remote MCP servers, including the OAuth handshake. It is a stopgap with a clear expiry date, and its own README says so.
Who is it for?
Adopt mcp-remote if you are running Claude Desktop, Cursor or Windsurf against a remote MCP server and your client has no HTTP or OAuth support. Do not adopt it as a permanent layer in a server you control, because the project positions itself as removable once clients catch up.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap mcp-remote was written to fill

The MCP ecosystem grew up on stdio. Servers were installed locally, the client and server trusted each other because the user had granted both permission to run, API keys lived in environment variables, and npx or uvx meant nobody had to think about an install step. That model has a real cost for server authors: shipping a bug fix means every user reinstalls, and there is no single deploy that reaches everyone. The README makes the comparison directly, noting that most software that could move to the web did move to the web for exactly this reason. The MCP authorization specification (the README links the 2025-03-26 revision) gives a way to expose an MCP server without code running on user laptops. The blocker is client-side. Most popular MCP clients are stdio-only, and the ones that speak HTTP+SSE do not yet implement the OAuth flows the spec requires. mcp-remote sits in that gap. It is a local process that your client launches over stdio, and it speaks the remote protocol on the other side. The intended audience is anyone whose client config has an mcpServers block and a command field, which in practice means Claude Desktop, Cursor and Windsurf users pointing at a hosted server. The README is explicit that this is temporary: as soon as your chosen client supports remote, authorized servers, you can remove it.

What actually runs between your client and the remote server

The architecture is a man-in-the-middle by design, not by accident. Your MCP client spawns npx mcp-remote <url> as a child process and talks to it over stdio, which is the only transport the client understands. mcp-remote holds the HTTP connection to the remote server and, when the server demands it, runs the OAuth authorization flow on your behalf. That flow needs a redirect target, so mcp-remote starts a local listener for the OAuth callback. The default port is derived from the server URL, which means every server gets a stable port of its own somewhere in the 3335-49150 range, and mcp-remote walks up to 8 ports from there if the first is taken. That detail matters more than it looks. A derived port is stable across restarts, so a previously registered redirect_uri stays valid. An explicitly passed port is used as-is and implies a redirect_uri the authorization server has already been given, so mcp-remote fails rather than quietly moving to a different one. The same pinning behaviour applies to --static-oauth-client-info. Session state is keyed on more than the URL: each unique combination of server URL, resource, custom headers and --authorize-param values maintains separate OAuth sessions and token storage. That is what makes the multi-tenant Atlassian example in the README work, and it is the mechanism to understand before you run two configurations of the same server side by side.

Configuration that works in a real mcpServers block

The minimal config is one command and one URL. Under mcpServers, set command to npx and args to ["mcp-remote", "https://remote.mcp.server/sse"]. If npx is erroring out, the README suggests adding -y as the first argument to auto-accept installation of the package, and mcp-remote@latest forces npx to check for a newer version each time. For servers that need a bearer token, --header takes a Name: value pair. There is a client bug worth knowing about: Cursor, Codex-Cli and Claude Desktop on Windows do not escape spaces inside args when invoking npx, which mangles header values. The workaround in the README moves the space into an environment variable and writes Authorization:${AUTH_HEADER} with no space around the colon, since env vars handle spaces fine. If you would rather not put a credential in process arguments at all, where any other user on the machine can read it from the process list, use --header-file with a path. The file format is one Name: value per line and # starts a comment. An unreadable file is treated as an error rather than a warning, so a mistyped path fails immediately instead of sending the request unauthenticated. For servers that need extra authorization parameters, --authorize-param key=value repeats as needed. The README gives access_type=offline and prompt=consent as the pair Google wants before it issues a refresh token, and audience=https://your-api for Auth0 to return a JWT rather than an opaque token. These apply to the authorization request only. resource is the exception: --resource is also sent as the RFC 8707 resource indicator on the token and refresh requests, so all three agree. Parameters the flow derives per request, such as state, code_challenge, client_id, redirect_uri and response_type, are refused outright, on the reasoning that a value disagreeing with the real one surfaces as an opaque server error.

Where mcp-remote breaks, and when it is the wrong tool

The sharpest limitation is stated in the README as a compatibility fact: some authorization servers reject the resource parameter outright, and Microsoft Entra ID v2 answers AADSTS9010010. The escape hatch is --disable-resource-parameter, which omits it entirely. If you are targeting Entra ID, that flag is not optional. The second limitation is that changing authorization parameters starts a new sign-in. This is not a bug. A parameter like audience decides which API the token is for, and a token issued for one audience is not valid for another, so the flow cannot silently reuse the old session. Users who tweak --authorize-param and expect the existing token to keep working will be surprised. Third, the port behaviour cuts both ways. An explicit port is rigid by design, but that rigidity means a port collision is a hard failure rather than a retry, which is the correct trade for OAuth but still a failure mode you have to plan around. Fourth, this is the wrong tool if your client already speaks HTTP with OAuth, because you are adding a local process, a local listening port and a token store for no benefit. It is also the wrong tool if you control both ends and can simply run the server locally, since the stdio trust model it works around is genuinely simpler. And it is the wrong tool for anyone who needs it to be a permanent part of their deployment: the README frames the whole project as something to delete later.

How it compares to building the bridge yourself

The obvious alternative is writing a small stdio-to-HTTP proxy in your own client config, or using whatever HTTP transport your client grows next. The difference is not the transport hop, which is trivial. It is the OAuth surface. Doing this yourself means implementing the authorization flow, the callback listener, port selection stable enough that a registered redirect_uri stays valid, RFC 8707 resource indicators on three different request types, refresh token handling, and per-configuration session isolation so that two tenants of the same server do not share a token. That is the actual work mcp-remote is doing, and the README's level of detail about which parameters go where suggests it was learned from real authorization servers rather than from the spec alone. A second alternative is a client-side remote transport, which is where this ends up. The difference there is that you wait for the client vendor rather than running a shim, and you get no local process and no local port. The trade is availability: if your client has not shipped it, you cannot use it today. A third option, for servers you operate, is to expose a local stdio entry point alongside the remote one, which sidesteps OAuth entirely for users who are willing to install. That only works if the server can run locally, which is the case mcp-remote exists to avoid.

Maintenance cost, the npx version pin and the MIT licence

The upgrade story is npx-shaped, which has a specific consequence. Without a version pin, npx resolves mcp-remote at launch, so a client restart can pick up a new release, and the README offers mcp-remote@latest to force that check deliberately. If you want the opposite, pin the version in the args array. Nothing in the supplied material describes a lockfile, a vendored install or a bundled binary, so the version you get is the version npx resolves at process start. That is convenient and it is also the thing to pin in a managed fleet. The release cadence visible from the repository is fast: v0.8.4, v0.8.5 and v0.8.6 landed on 7, 8 and 9 September 2026, three patch releases in three days. Nothing in the material says what changed in them, so treat the cadence as a reason to pin rather than as a reason to trust or distrust the code. The licence is MIT, which is permissive and places few obligations on how you redistribute or modify it. That is a statement about the licence text, not legal advice, and it says nothing about the terms of the remote MCP servers you point it at or the authorization servers you register a client with. Those are separate agreements and mcp-remote does not mediate them.

Editorial conclusion

Adopt mcp-remote if you are running Claude Desktop, Cursor or Windsurf against a remote MCP server and your client has no HTTP or OAuth support. Do not adopt it as a permanent layer in a server you control, because the project positions itself as removable once clients catch up. Before rolling it out, verify three things: whether your authorization server rejects the RFC 8707 resource parameter (Microsoft Entra ID v2 answers AADSTS9010010, per the README, and needs --disable-resource-parameter), whether your client mangles spaces in args (Cursor, Codex-Cli and Claude Desktop on Windows do, so use the Authorization:${AUTH_HEADER} form), and whether your credentials belong in --header-file rather than --header, since process arguments are readable by other users on the machine.

Official sources

  1. License: MIT
  2. Project website
  3. punkpeye/mcp-remote on GitHub
  4. README
  5. Releases
Community notes

Community notes