# webfetch-mcpo-docker A single Docker image exposing [`webfetch-mcp`](https://code.aneur.in/mirrors/webfetch-mcp)'s `web_search` and `web_fetch` tools over HTTP/OpenAPI, via [`mcpo`](https://github.com/open-webui/mcpo). `webfetch-mcp` is a stdio-only MCP server; `mcpo` exposes a wrapped MCP server over HTTP by spawning it as a local child process. Since that spawn happens in-process (not over the network) and `webfetch-mcp` has no HTTP/SSE mode of its own, both have to live in one image — that's what this repo builds. ## Build ```sh docker build -t webfetch-mcpo-docker . ``` ## Run ```sh docker run -p 8000:8000 \ -e MCPO_API_KEY=some-secret \ -e SEARXNG_BASE=http://searxng.example.internal:8080 \ webfetch-mcpo-docker ``` Interactive API docs: `http://localhost:8000/docs` (and `/openapi.json`). These are served without authentication even when `MCPO_API_KEY` is set — only the generated tool endpoints (`/web_search`, `/web_fetch`) require it, as `Authorization: Bearer `. ## Environment variables | Variable | Effect | |---|---| | `MCPO_API_KEY` | If set, tool endpoints require `Authorization: Bearer `. If unset, `mcpo` serves unauthenticated (its own default behavior). | | `MCPO_PORT` | Port `mcpo` listens on inside the container. Default `8000`. | | `SEARXNG_BASE` | Passed through to `webfetch-mcp`; its own default (`http://localhost:8080`) applies if unset. | | `DEBUG` | Passed through to `webfetch-mcp`. `"true"` enables debug logging. | | `DETAILED_LOG` | Passed through to `webfetch-mcp`. Defaults on; set to `"false"` to disable its detailed log file. | No explicit plumbing is needed to get these into `webfetch-mcp`: `mcpo` builds the spawned child process's environment as the union of its own process environment and any per-server overrides, so anything set on the container's environment is inherited by the `node server.mjs` child automatically. (Verified against `mcpo`'s `MCPConnectionManager` source, not assumed.) ## Known characteristics - **No URL filtering / SSRF protection.** `webfetch-mcp`'s `web_fetch` tool fetches whatever URL it's given, including private/internal addresses. This is a known, accepted characteristic of `webfetch-mcp` itself — out of scope for this image to fix. - **No TLS termination.** This image serves plain HTTP; put a reverse proxy or firewall in front of it if you need TLS or network restriction. - `webfetch-mcp` writes a detailed log file next to `server.mjs` inside the image by default (`DETAILED_LOG` defaults on). That directory needs to stay writable at runtime — don't run this image with a read-only root filesystem without accounting for that (e.g. a volume mount, or set `DETAILED_LOG=false`). ## Pinned revisions - `webfetch-mcp`: pinned via `ARG WEBFETCH_MCP_COMMIT` in the `Dockerfile`, vendored from the Gitea mirror at `https://code.aneur.in/mirrors/webfetch-mcp`, not upstream GitHub. - `mcpo`: pinned via `ARG MCPO_VERSION` in the `Dockerfile`, installed from PyPI. Bump either by updating the corresponding `ARG` default in the `Dockerfile`. When bumping `mcpo`, double check its CLI flags still match what `entrypoint.sh` assumes (`--host`, `--port`, `--api-key`, `--`). ## CI `.gitea/workflows/docker-build.yml` builds and pushes `code.aneur.in/cloud/webfetch-mcpo-docker:latest` on every push to `main`. It authenticates using the repo secrets `PACKAGE_REGISTRY_USER` and `PACKAGE_REGISTRY_TOKEN` — the default Gitea Actions token doesn't have package-registry write permission on this instance, so these must be created manually under repo Settings → Actions → Secrets, backed by a personal access token with package-write scope.