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

mcpo puts a FastAPI OpenAPI surface in front of a stdio MCP server

A simple, secure MCP-to-OpenAPI proxy server

4,427 stars511 forksPythonMIT

At a glance

What is it?
mcpo wraps any stdio MCP command in a FastAPI server with generated OpenAPI docs, so Open WebUI and other HTTP clients can call tools they cannot speak to natively. The wrapper is small and MIT licensed, but it has two configuration constraints and a documentation mismatch worth checking before you deploy it.
Who is it for?
Adopt mcpo if you need Open WebUI or another HTTP client to reach a stdio MCP server today, and accept that the child process, its stdout and its credentials all live in one long-running process. Do not adopt it for Python 3.8 deployments, for SSE servers behind a subpath you cannot control, or where per tool rate limiting matters.
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?
Activity is slowing. The repository last received commits 6 months 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 November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • open-webui/mcpo4,427 ★ · +58
Star history
Repository2026-09-112026-11-14
open-webui/mcpo4.4k4.4k

58 Stars in the last 64 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Why a stdio MCP server cannot be handed to Open WebUI directly

Most MCP servers start as a subprocess and talk over stdin and stdout. A browser based chat interface cannot spawn that subprocess, cannot speak its framed JSON messages, and has no place to type an API key. The README states the case bluntly: raw stdio is incompatible with most tools, and missing standard features like docs, auth and error handling.

That gap is what Open WebUI users hit when they try to register an MCP server and the connection fails. The Open WebUI documentation page for mcpo exists because the two halves are separate products with a gap between them.

The audience is narrow and specific: someone who already has a working MCP server, knows it works from a terminal, and now needs the same tool callable over HTTP from a chat UI, a script, or an agent framework that expects an OpenAPI document. If you do not have a working MCP server already, mcpo has nothing to wrap.

How mcpo turns one child process into FastAPI routes

The mechanism is a proxy, not a reimplementation. mcpo launches the command you place after the double dash, connects to it as an MCP client over stdio, discovers the tools that server advertises, and mounts each one as a route on a FastAPI application. The schema is derived from the tool declarations, so the interactive docs are generated rather than configured.

The stack underneath is ordinary Python web machinery. pyproject.toml requires fastapi, uvicorn, click, typer, pydantic, the mcp client library, watchdog for file watching, and pyjwt with crypto plus passlib with bcrypt. The console entry point is declared as a script, so installing the package puts a single mcpo executable on the path.

Routing is per tool, not per process. With a config file each server in the list gets its own route and its own docs page, which is why the README points at a path of the form http://localhost:8000/memory/docs instead of a single global schema.

Three install paths, three different operational footprints

The uvx path is the shortest. uv is optional according to the requirements section but the project recommends it for startup time, and the example wraps a second uvx call to launch the MCP server:

bash
uvx mcpo --port 8000 --api-key "top-secret" -- uvx mcp-server-time --local-timezone=America/New_York

Once that process starts, the tool sits at http://localhost:8000 and the schema browser is at http://localhost:8000/docs. Everything after the double dash belongs to the MCP server, not to mcpo, which means server specific flags such as the timezone value above pass through untouched.

The pip path installs the package into an environment you control:

bash
pip install mcpo
mcpo --port 8000 --api-key "top-secret" -- your_mcp_server_command

The container image copies uv, Node.js 22 and git into a python:3.12-slim-bookworm base and sets the entrypoint to mcpo, so the command becomes an argument list:

bash
docker run -p 8000:8000 ghcr.io/open-webui/mcpo:main --api-key "top-secret" -- your_mcp_server_command

The tradeoff is real. uvx and docker both add a download step on first run, and docker bundles Node.js and git because so many MCP servers are npx packages. If your MCP server is already a compiled binary on the host, the container image is the heaviest of the three for no benefit.

One config file replaces one process per server

The config file follows the Claude Desktop format, so an existing MCP configuration can be reused rather than rewritten. Each entry under the mcpServers key becomes a named route:

json
{
  "mcpServers": {
    "memory": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-memory"]
    }
  }
}

Command entries use command and args. Remote servers use a type of sse or streamable-http together with a url, and can carry a headers object. Entry names become the path segment, so a server named time is reachable at http://localhost:8000/time.

A per server disabledTools array removes individual tools from the generated schema, which matters when a server ships a converter or a formatting helper you do not want an agent calling. The README also offers hot-reload mode, which watches the file and restarts servers when it changes:

bash
mcpo --config /path/to/config.json --hot-reload

This is the feature that pushes mcpo past a demo. Without it, editing a config means killing the process and any agent mid conversation loses its connection.

Serving under a subpath without breaking reverse proxy routing

Deployments rarely give a tool server the root of a hostname. The root-path argument moves every route, including the generated docs pages, under that prefix:

bash
mcpo --port 8000 --root-path "/api/mcpo" --api-key "top-secret" -- your_mcp_server_command

After that the tool answer lives at a path ending in the tool name beneath the prefix, and the schema browser follows it. Ingress configs that strip or preserve the prefix have to agree with this setting.

The api-key flag is the only authentication in the common path. It gates the HTTP surface, not the child process, so anything that can start the same command locally bypasses it entirely.

Where the package metadata contradicts the stated requirements

The requirements section says Python 3.8+. The packaging metadata says otherwise: pyproject.toml declares requires-python as greater than or equal to 3.11, and the container base image is Python 3.12. On a host below that floor, pip refuses the install or uv reports an unsupported interpreter, and the requirements section will not warn you first.

Two more constraints come from the same files. OAuth 2.1 support is documented for the streamable-http server type only, so an sse server behind a token provider has no documented path. And the callback port for the OAuth flow defaults to 3030 with use_loopback defaulting to true, which means the flow opens a browser on the machine running mcpo and expects the callback to land back there. On a headless box that assumption breaks.

Version 0.0.20 in pyproject.toml matches the most recent tagged release, and the licence is MIT with a single named author. Treat the 0.0.x number as the signal it is: the surface area has had little time to settle.

What writing the FastAPI wrapper yourself actually costs

The alternative is not another package. It is a small FastAPI app that spawns the subprocess, speaks the MCP client protocol, and declares a response model per tool. If you have two tools and a fixed audience, that is a defensible afternoon.

The difference is where the work lands. A hand written wrapper makes auth, per tool timeouts and logging yours to design, and the schema you publish is the schema you chose. mcpo inverts that: the schema comes from whatever the MCP server declares, the auth surface is a single shared key, and tool metadata such as descriptions is whatever the server author wrote, including its gaps.

So mcpo earns its place when the MCP servers are already written by other people and change independently of your deployment. It earns less when you own the tools and want the HTTP contract to be a design decision rather than a reflection.

Editorial conclusion

Adopt mcpo if you need Open WebUI or another HTTP client to reach a stdio MCP server today, and accept that the child process, its stdout and its credentials all live in one long-running process. Do not adopt it for Python 3.8 deployments, for SSE servers behind a subpath you cannot control, or where per tool rate limiting matters. Before the first deploy, run the README example command and open the generated docs route at http://localhost:8000/docs to confirm the tool list matches what the MCP server advertises.

Frequently asked questions

How do Open WebUI and mcpo work together?

Open WebUI consumes tools over HTTP as OpenAPI servers, while most MCP servers speak stdio to a subprocess. mcpo sits between them, launching the MCP command and serving each discovered tool as a FastAPI route with generated docs.

Why does Open WebUI say "Failed to connect to MCP server"?

The README states that raw stdio MCP servers are incompatible with most tools. Running the server command under mcpo gives it an OpenAPI endpoint at http://localhost:8000 that the UI can call instead.

Can Open WebUI use MCP servers?

Yes, through a proxy that exposes them as OpenAPI servers. The project documentation points readers to its Open WebUI integration page for the connection settings after launching mcpo.

Official sources

  1. License: MIT
  2. open-webui/mcpo on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/open-webui-mcpo.svg)](https://hysenlabs.com/projects/open-webui-mcpo)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/open-webui-mcpo.svg)](https://hysenlabs.com/projects/open-webui-mcpo)

Was this useful?