Recipe 7: Self-host the MCP server for an internal AI agent
Run webfetch-server behind your VPN, expose it over HTTP to a team of agents, BYOK for provider auth.
You have an internal AI agent (an LLM + some tools) running in your infrastructure. You want it to fetch license-safe images, but you can't send any traffic to a third-party cloud (compliance, PII in queries, etc.). Self-host the MCP server on your own box.
#Minimal deployment — Docker Compose
# docker-compose.yml
services:
webfetch:
image: ghcr.io/ashlrai/webfetch:latest
command: ["server", "--host", "0.0.0.0", "--no-open"]
ports:
- "127.0.0.1:7600:7600"
environment:
WEBFETCH_CACHE_DIR: "/var/cache/webfetch"
WEBFETCH_ENABLE_BROWSER: "0"
UNSPLASH_ACCESS_KEY: "${UNSPLASH_ACCESS_KEY}"
PEXELS_API_KEY: "${PEXELS_API_KEY}"
SPOTIFY_CLIENT_ID: "${SPOTIFY_CLIENT_ID}"
SPOTIFY_CLIENT_SECRET: "${SPOTIFY_CLIENT_SECRET}"
volumes:
- webfetch-cache:/var/cache/webfetch
- webfetch-home:/root/.webfetch
restart: unless-stopped
volumes:
webfetch-cache:
webfetch-home:Start:
docker compose up -d
TOKEN="$(docker compose exec -T webfetch cat /root/.webfetch/server.token)"
curl -sS http://127.0.0.1:7600/providers -H "authorization: Bearer $TOKEN"#Connecting internal agents
If your agent can call HTTP tools directly, point it at the webfetch REST endpoint:
curl -sS -X POST http://webfetch.internal:7600/search \
-H "authorization: Bearer INSERT_TOKEN_HERE" \
-H "content-type: application/json" \
-d '{"query":"architecture diagram","licensePolicy":"safe-only"}'For MCP-native IDEs, run getwebfetch-mcp as a local stdio server in the IDE
and give that process the same provider keys. The current MCP package does not
proxy to a remote HTTP server.
npx -y getwebfetch-mcp#Locking down the network
- The server only needs outbound HTTPS to the configured providers (whitelist in your egress firewall:
en.wikipedia.org,api.openverse.org,api.unsplash.com,api.pexels.com, etc.). - All inbound is scoped to your VPN.
- Disable the browser layer unless you have a separate sandbox:
WEBFETCH_ENABLE_BROWSER=0.
#Team pattern: one box, many agents
Deploy behind an ALB / Caddy / Nginx with per-team tokens:
webfetch.internal {
@team-alpha header Authorization "Bearer alpha-token"
@team-beta header Authorization "Bearer beta-token"
handle @team-alpha { reverse_proxy webfetch-alpha:7600 }
handle @team-beta { reverse_proxy webfetch-beta:7600 }
}Separate deployments per team gives you separate caches and separate provider key sets. Cheaper than real multi-tenancy and keeps blast radius small.
#Observability
GET /healthfor liveness probes.- Every request emits a structured JSON log line (
{timestamp, endpoint, duration_ms, providers_called, candidates, errors}). - Ship logs to your SIEM; alert on
error_rate > 5%for 5+ minutes.
For full operational details, see Self-hosting.