Files
webfetch-mcpo-docker/README.md
T
aneurinandClaude Sonnet 5.5 291703ecc3
Build and push image / build (push) Successful in 3m59s
Expose native MCP endpoint alongside mcpo
Run supergateway on port 8080 (streamable HTTP at /mcp) next to mcpo on
8000, each spawning its own webfetch-mcp child. Supervise both under tini.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 17:27:28 +01:00

4.7 KiB

webfetch-mcpo-docker

A single Docker image exposing webfetch-mcp's web_search and web_fetch tools in two ways at once:

Port Protocol For
8000 OpenAPI, via mcpo Open WebUI
8080 Native MCP (streamable HTTP) at /mcp, via supergateway Coding agents (opencode etc.)

webfetch-mcp is a stdio-only MCP server with no HTTP mode of its own. mcpo and supergateway each expose a wrapped stdio server over HTTP by spawning it as a local child process, so they have to live in the same image as webfetch-mcp. Each spawns its own node server.mjs.

Build

docker build -t webfetch-mcpo-docker .

Run

docker run -p 8000:8000 -p 8080:8080 \
  -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 <MCPO_API_KEY>.

opencode (or any MCP client)

In opencode.json:

{
  "mcp": {
    "webfetch": { "type": "remote", "url": "http://<host>:8080/mcp" }
  }
}

The :8080 endpoint has no authentication; MCPO_API_KEY applies only to :8000. /healthz on :8080 returns ok.

Environment variables

Variable Effect
MCPO_API_KEY If set, tool endpoints require Authorization: Bearer <value>. If unset, mcpo serves unauthenticated (its own default behavior).
MCPO_PORT Port mcpo listens on inside the container. Default 8000.
MCP_PORT Port the native MCP endpoint listens on. Default 8080.
ENABLE_MCPO / ENABLE_MCP Set either to false to run only the other side. Both default true.
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: both mcpo and supergateway start the child with the container's environment inherited, so anything set on the container's environment reaches node server.mjs automatically. (Verified against mcpo's MCPConnectionManager source, not assumed.)

Process supervision

entrypoint.sh runs both servers under tini. If either one exits, the other is stopped and the container exits non-zero, so the orchestrator restarts the whole thing rather than leaving it half up.

Known characteristics

  • The :8080 endpoint is unauthenticated. Intended for a trusted LAN only.

  • 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.

  • supergateway: pinned via ARG SUPERGATEWAY_VERSION in the Dockerfile, installed from npm.

Bump any of them 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, --); for supergateway, --stdio, --outputTransport, --port and --healthEndpoint.

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.