Model or dataset
open-webui/mcpo avatar
open-webui/mcpo

mcpo: turning MCP stdio servers into OpenAPI endpoints

A simple, secure MCP-to-OpenAPI proxy server

4,375 stars494 forksPythonMIT

At a glance

What is it?
mcpo is a Python proxy that wraps an MCP server command and re-exposes its tools over HTTP with an auto-generated OpenAPI schema. It is a small adapter with a narrow job, and the interesting questions are about what the proxy layer adds and what it hides.
Who is it for?
Adopt mcpo if you already have MCP servers configured in Claude Desktop format and need them reachable over HTTP by an OpenAPI client such as Open WebUI, and you are comfortable with the proxy holding credentials on its behalf. Do not adopt it if you need per-user authorization inside the MCP server, or if your client can speak MCP directly.
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 121 days ago.
What is it written in?
Mainly Python, 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 stdio problem mcpo is built around

MCP servers commonly communicate over stdio. That transport is fine when a single local process launches the server as a child process, and awkward everywhere else. An HTTP client cannot attach to a pipe. A web UI cannot enumerate tools that only exist as a subprocess of someone else's terminal session. The README lists three complaints about raw stdio: that it is insecure, incompatible with most tools, and missing standard features such as docs and auth. The first claim is arguable in the abstract, since stdio is not inherently less safe than a socket, but the second and third are concrete. Most HTTP tooling expects a URL and a schema.

mcpo's answer is to keep the MCP server exactly as it is and put a translation layer in front of it. The MCP server still speaks its own protocol; mcpo speaks to it, then publishes the same tools as REST endpoints described by OpenAPI. The target user is someone wiring an MCP tool into an application that already knows how to consume OpenAPI servers. The README points at Open WebUI specifically, and the repository lives under the open-webui organization, so the primary consumer is clear even though the tool is not restricted to it.

What the proxy actually does with a tool call

The mechanism is a per-server mount. When you pass a command after the double dash, mcpo starts that command as a subprocess and talks MCP to it. It then generates an OpenAPI schema describing the tools that server exposes and serves a FastAPI-style application that maps HTTP requests onto MCP calls. The README describes the result as each tool being available under its own unique route with a dedicated OpenAPI schema and proxy handler.

With a config file the fan-out is per server entry. A config containing a memory server and a time server produces http://localhost:8000/memory and http://localhost:8000/time, each with its own schema UI at /memory/docs and /time/docs. That per-server isolation matters: a tool named the same thing in two servers does not collide, because the route prefix is the server key from the config. The proxy is stateless from the client's perspective, but not from its own, since it holds a live MCP session to each child process for the lifetime of the mcpo process. If that child dies, the route does not have a documented recovery path in the README; the material does not describe a health check or restart policy for crashed MCP subprocesses.

Config format: Claude Desktop's, with extras

The config file follows the Claude Desktop mcpServers format, which lowers the cost of trying mcpo on an existing setup: you can often reuse a config you already have. Two keys go beyond that format. The first is disabledTools, shown in the README's example on the time server as ["convert_time"], which removes specific tools from the generated schema without editing the MCP server. The second is type, which selects the transport. Omitting type means the command-based stdio path. Setting it to sse or streamable-http means the url field is used instead, and for SSE you can pass a headers object with an Authorization bearer token and custom headers.

Remote servers also support an oauth block. The README states the implementation defaults to dynamic client registration, so the minimal config is a server_url and nothing else. Options include storage_type as file or memory (file is the default), callback_port defaulting to 3030, and use_loopback defaulting to true to open a browser for authorization. For servers without dynamic registration there is a client_metadata escape hatch. The README explicitly warns against setting scope, authorization_endpoint or token_endpoint in config, because they are discovered from the server's OAuth metadata. That warning is worth taking literally; hand-setting discovery fields is the usual way to break a dynamic registration flow.

Running it: the commands the README gives

The fastest path is uvx, which the README recommends for startup speed and zero config: uvx mcpo --port 8000 --api-key "top-secret" -- your_mcp_server_command. The pip equivalent is pip install mcpo followed by the same invocation. A concrete example from the README wraps the time server: uvx mcpo --port 8000 --api-key "top-secret" -- uvx mcp-server-time --local-timezone=America/New_York. There is also a container image at ghcr.io/open-webui/mcpo:main, run with docker run -p 8000:8000 and the same trailing arguments.

Two flags deserve attention. --root-path serves everything under a prefix for reverse proxy deployments, so --root-path "/api/mcpo" moves the memory route to http://localhost:8000/api/mcpo/memory. --hot-reload watches the config file and reloads servers without downtime when you start with --config. Requirements are Python 3.8 or newer, with uv optional. For local development the README gives uv sync --dev and uv run pytest. The api-key flag is the only access control shown in the quick usage section, and the README does not describe a multi-key or per-user model, so treat it as a shared secret for the whole proxy.

Where the proxy layer becomes the weak point

Every request that reaches an MCP tool passes through mcpo, and mcpo is the component holding the credentials. For SSE servers the upstream Authorization header lives in the config file. For OAuth servers the tokens are stored on disk in ~/.mcpo/tokens/ when storage_type is file, which the README lists as the default. Anyone who can read that directory, or read the config, can act as the proxy against those upstream servers. The README does not describe token encryption at rest or a key rotation procedure.

A second limitation is that the OpenAPI surface is generated from MCP tool definitions, and the README does not document how complex or nested argument schemas are translated, nor what happens when a tool's input cannot be expressed cleanly. If your tools take deeply structured arguments, verify the generated schema at /<tool>/docs before you build a client against it. Third, OAuth support is scoped to streamable-http server types per the README, so an SSE server behind an OAuth-protected endpoint is not covered by that path. Finally, the loopback flow assumes a browser is available on the machine running mcpo. On a headless server, use_loopback: true opens nothing, and the README does not spell out the alternative.

Compared with running an MCP client directly

The obvious alternative is to skip the proxy and let the consuming application speak MCP natively. That is the right call when your client already supports MCP, because it removes a process, a port and a credential store from the path. The difference in approach is real: a native MCP client holds the stdio session itself, so there is no shared api-key, no token directory, and no schema translation step that might reshape a tool's arguments. You also avoid the question of what happens when the mcpo process restarts while a client is mid-session.

mcpo earns its place when the client cannot speak MCP and can speak OpenAPI. That is the Open WebUI case the README targets, and it is a large class of tools: HTTP clients, API gateways, generated SDKs and anything that reads an OpenAPI document. The trade is one extra hop and one extra secret store in exchange for not writing glue code per tool. If you find yourself building an MCP client anyway to get behaviour mcpo does not expose, the proxy has stopped paying for itself.

Maintenance, versioning and licence

mcpo is MIT licensed, which permits commercial use and modification with the licence and copyright notice retained. This is not legal advice; read the LICENSE file in the repository for the operative terms. The version history in the supplied material shows v0.0.18 and v0.0.19 released on the same day in October 2025, then v0.0.20 in February 2026, with a push to main in May 2026. The 0.0.x versioning is a signal about API stability expectations, not a guarantee either way: the config format follows Claude Desktop's, which is an external contract mcpo does not control, so a change there propagates into your config.

Upgrade cost is mostly in the config schema. The oauth block and the type field are recent enough that a config written against an older release may not carry them, and the README's warning about not setting discovery fields suggests the OAuth code has moved. Pin a version in your container tag or lockfile rather than tracking main, and re-read the config example after each bump. Because the proxy is a single process fronting all your MCP servers, an upgrade is also a single point of failure for every tool route at once.

Editorial conclusion

Adopt mcpo if you already have MCP servers configured in Claude Desktop format and need them reachable over HTTP by an OpenAPI client such as Open WebUI, and you are comfortable with the proxy holding credentials on its behalf. Do not adopt it if you need per-user authorization inside the MCP server, or if your client can speak MCP directly. Before committing, verify that the tools you need survive schema generation intact, that your reverse proxy passes the root path you configured, and that OAuth token storage in ~/.mcpo/tokens/ meets your requirements.

Official sources

  1. License: MIT
  2. open-webui/mcpo on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes