Model or dataset
tbxark/mcp-proxy avatar
tbxark/mcp-proxy

mcp-proxy: One HTTP Endpoint in Front of Many MCP Servers

An MCP proxy server that aggregates and serves multiple MCP resource servers through a single HTTP server.

722 stars106 forksGoMIT

At a glance

What is it?
tbxark/mcp-proxy is a Go server that aggregates tools, prompts and resources from several MCP servers behind a single HTTP entry point, with optional OAuth handling for downstream servers such as Notion. It is useful when each client would otherwise need its own copy of every server's configuration.
Who is it for?
Adopt mcp-proxy if you run several MCP servers and want one HTTP endpoint with shared OAuth token handling instead of duplicating configuration per client. Do not adopt it if you need per-caller isolation or already depend on a client's native multi-server support.
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 received new commits within the last day.
What is it written in?
Mainly Go, 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 configuration duplication problem mcp-proxy removes

Every MCP client that speaks to more than one server needs an entry per server: a command line, a URL, credentials, sometimes an OAuth flow. That list has to be repeated in each client, and each repetition drifts. The README frames the project as an MCP proxy that aggregates multiple MCP servers behind a single HTTP entrypoint. The audience is engineers running a handful of MCP servers on a host or in a container and connecting to them from several places: a desktop client, an editor plugin, a script. Instead of teaching each caller about every downstream server, you teach the proxy once and point the callers at one URL. The README's feature list names the three things being aggregated: tools, prompts, and resources. That scope matters. This is not a gateway that rewrites tool semantics or adds policy; it collects what downstream servers expose and serves it from one place. The README also credits adamwattis/mcp-proxy-server as the inspiration, so the design lineage is a single-endpoint aggregator rather than something built around a plugin or routing layer.

How aggregation and transport work in this proxy

The proxy is a Go binary with a configuration file. Each entry describes a downstream MCP server with a client type, and the README lists three: stdio, sse, and streamable-http. On the serving side, the README says the proxy can be exposed via Server-Sent Events or streamable HTTP. So the data flow has two legs. On the client leg, the proxy speaks whichever transport the downstream server offers, launching a local process for stdio entries or connecting over HTTP for the remote ones. On the server leg, callers connect to the proxy over SSE or streamable HTTP and see the aggregated set. The README does not describe how name collisions between tools from different downstream servers are resolved, nor whether aggregation is lazy or eager at startup. Those are the details I would check in docs/CONFIGURATION.md before relying on the aggregate view, because they determine what a model actually sees when two servers expose a tool with the same name. The OAuth feature is the more interesting mechanism. The README states that for downstream servers requiring interactive OAuth, such as Notion, you authorize once and the proxy then holds and refreshes the token for every caller. That moves token custody from each client into a long-running server process, which is the main architectural decision in the project.

Building and running it: the commands in the README

Three installation paths appear in the README. From source: git clone https://github.com/tbxark/mcp-proxy.git, then cd mcp-proxy, then make build, then ./build/mcp-proxy --config path/to/config.json. Via Go: go install github.com/tbxark/mcp-proxy@latest. Via Docker: docker run -d -p 9090:9090 -v /path/to/config.json:/config/config.json ghcr.io/tbxark/mcp-proxy:latest. The Docker image, per the README, includes support for launching MCP servers through npx and uvx, which matters because stdio-type downstream servers are usually Node or Python packages. The README also shows that --config accepts a remote URL: docker run -d -p 9090:9090 ghcr.io/tbxark/mcp-proxy:latest --config https://example.com/config.json. That is a real operational choice, not a convenience. A remote config means the set of downstream servers, and any credentials embedded in that file, is fetched at startup from an HTTP endpoint you control. The README points to docs/deployment.md for docker-compose and other options, and docs/usage.md for command-line flags, endpoints, and auth examples. Port 9090 is the only port shown in the README, so treat it as the documented default rather than a fixed requirement. There is also an online Claude config converter at https://tbxark.github.io/mcp-proxy, which suggests the common starting point is an existing Claude client configuration that you convert into the proxy's format.

The OAuth token holder is the interesting bet

Most of what this project does is plumbing. The OAuth behaviour is where it takes a position. When a downstream server needs an interactive authorization, the README says you authorize once and the proxy holds and refreshes the token for every caller. Compare that with the default arrangement, where each client performs its own authorization and stores its own token. The proxy approach means one authorization event, one refresh path, and one place where the token lives. It also means every caller reaching the proxy can use that downstream server without an individual grant. Whether that is acceptable depends entirely on who can reach the proxy. The README's usage documentation is where auth examples for the proxy's own endpoints live, and that is the document to read before exposing port 9090 beyond localhost. The project does not, in the material available, describe per-caller scoping of downstream tokens. So the honest reading is: the OAuth feature solves token duplication and refresh churn, and it does so by concentrating authority in the proxy process. That is a trade, not a free win.

Where this is the wrong tool

If each caller needs a different view of the downstream servers, this proxy works against you. The README describes aggregation, not filtering or per-caller routing, so the natural deployment is one proxy serving one shared set of tools, prompts and resources. A team that wants engineer A to see only the read-only servers and engineer B to see everything will not find that in the feature list. Second, the proxy is a single process in the path of every request. The README does not describe health checks, restart behaviour, or what callers see when a downstream stdio server fails to launch. Those are questions for docs/deployment.md and for your own testing, not things the README answers. Third, the remote-config option, while convenient, adds a startup dependency on an HTTP fetch; if that endpoint is unreachable, the README gives no fallback behaviour. Fourth, stdio downstream servers are child processes of the proxy. The Docker image ships npx and uvx support precisely because those servers need a runtime present in the container, which means the image is doing more than running a Go binary. If you want a minimal container, that is a cost. Finally, if a client already manages multiple MCP servers well and you have no shared-token problem, the proxy adds a hop and a config file without removing much.

Compared with configuring each client directly

The direct alternative is not another proxy; it is the status quo of listing every MCP server in every client's own configuration. That approach keeps each caller independent: its own tokens, its own process lifecycle, no shared failure domain. The difference in approach is where state lives. With per-client configuration, OAuth tokens are held by each client and each client re-authorizes or refreshes on its own schedule. With mcp-proxy, the token is held by one long-running Go process and every caller benefits from the one authorization. The direct approach also means no aggregation step, so tool names arrive exactly as each server defines them, with no question about how collisions are handled. The proxy approach means one place to add a server and every connected client sees it immediately. If your pain is repeating the same five-server block across three clients, the proxy removes that. If your pain is that one client cannot reach one server, the proxy does not obviously fix it, because the transport types it supports are the same ones the client would use.

Maintenance, release cadence and the MIT licence

The repository is active and not archived, with a release tagged v0.58.0 on 2026-08-16 and earlier tags v0.43.2 and v0.43.0. The version numbers move in the 0.x range, and the jump from 0.43.2 to 0.58.0 over roughly seven months suggests frequent releases. For an operator, that means pinning the Docker tag rather than tracking latest is the safer default, and reading release notes before upgrading, because the README does not document a compatibility policy for the configuration schema. The configuration file is the surface most likely to change. The project is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are included. That is a permissive licence with no copyleft obligation. It says nothing about the licences of the MCP servers you proxy, and those are separate programs with their own terms, including the npx and uvx packages the Docker image launches. The MIT grant covers this proxy's code only. None of this is legal advice; if you redistribute a bundled image, check the notices for everything inside it.

Editorial conclusion

Adopt mcp-proxy if you run several MCP servers and want one HTTP endpoint with shared OAuth token handling instead of duplicating configuration per client. Do not adopt it if you need per-caller isolation or already depend on a client's native multi-server support. Before deploying, verify the aggregate tool naming in docs/CONFIGURATION.md against the tools your model already sees, and confirm the current endpoint and auth flags in docs/USAGE.md rather than relying on the Quick Start block.

Official sources

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

Community notes