269 lines
9.6 KiB
YAML
269 lines
9.6 KiB
YAML
name: mike-ai-tools
|
|
|
|
x-tool-common: &tool-common
|
|
restart: unless-stopped
|
|
read_only: true
|
|
tmpfs:
|
|
- /tmp:rw,noexec,nosuid,nodev,size=64m
|
|
security_opt: ["no-new-privileges:true"]
|
|
cap_drop: [ALL]
|
|
networks: [tools]
|
|
logging:
|
|
options:
|
|
max-size: 10m
|
|
max-file: "3"
|
|
|
|
services:
|
|
mcp-web:
|
|
<<: *tool-common
|
|
build:
|
|
context: ..
|
|
dockerfile: mcp/Dockerfile.web
|
|
image: mike-ai/mcp-web:local
|
|
container_name: mike-ai-mcp-web
|
|
# The relay fetches and validates public result pages itself. It therefore
|
|
# needs both the private tool network and the explicitly separated egress
|
|
# network; keeping it on `tools` only makes search discovery work while
|
|
# every page fetch fails.
|
|
networks: [tools, egress]
|
|
dns: ["${AI_DNS:-1.1.1.1}"]
|
|
environment:
|
|
TINYSEARCH_MCP_URL: http://tinysearch:8000/mcp
|
|
SEARXNG_URL: http://searxng:8080
|
|
WEB_SEARCH_BUDGET_MAX_RELATED: "6"
|
|
depends_on:
|
|
tinysearch:
|
|
condition: service_started
|
|
|
|
searxng:
|
|
<<: *tool-common
|
|
image: searxng/searxng@sha256:e45d5894bfaa0bf8773b9f283795ae57f1c15ddb29c8cecb70b3665b0ce9ec60
|
|
container_name: mike-ai-tools-searxng
|
|
dns: ["${AI_DNS:-1.1.1.1}"]
|
|
volumes:
|
|
- ${SEARXNG_SETTINGS_FILE:-../web-search/searxng-settings.example.yml}:/etc/searxng/settings.yml:ro
|
|
networks: [tools, egress]
|
|
|
|
tinysearch:
|
|
<<: *tool-common
|
|
image: marcellm01/tinysearch@sha256:5a03d5a1f1b0fabe48f2a26e05db4a84bcb611106a57ec51e42db4549976aa9c
|
|
container_name: mike-ai-tools-tinysearch
|
|
dns: ["${AI_DNS:-1.1.1.1}"]
|
|
# Crawl4AI keeps transient browser/session state here. The container stays
|
|
# read-only; only this disposable runtime directory (and /tmp from the
|
|
# common hardening block) is writable.
|
|
tmpfs:
|
|
- /tmp:rw,noexec,nosuid,nodev,size=64m
|
|
- /home/tinysearch/.crawl4ai:rw,nosuid,nodev,size=256m,mode=1777
|
|
shm_size: 1gb
|
|
volumes:
|
|
- tinysearch-models:/data/models
|
|
- ../web-search/tinysearch_config.json:/config/tinysearch_config.json:ro
|
|
environment:
|
|
MCP_TRANSPORT: streamable-http
|
|
MCP_HOST: 0.0.0.0
|
|
MCP_PORT: "8000"
|
|
TINYSEARCH_CONFIG_PATH: /config/tinysearch_config.json
|
|
TINYSEARCH_SEARCH_BACKEND: searxng
|
|
SEARXNG_URL: http://searxng:8080/search
|
|
depends_on: [searxng]
|
|
cap_add: [SETUID, SETGID, CHOWN]
|
|
networks: [tools, egress]
|
|
# The image's built-in `tinysearch doctor` also requires a writable
|
|
# configuration directory, although normal server operation does not.
|
|
# Check the service socket instead so read-only hardening remains intact.
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1', 8000), 2); s.close()"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 20s
|
|
|
|
mcp-homeassistant:
|
|
<<: *tool-common
|
|
build:
|
|
context: ../..
|
|
dockerfile: platform/mcp/Dockerfile.homeassistant-relay
|
|
image: mike-ai/mcp-homeassistant-relay:local
|
|
container_name: mike-ai-mcp-homeassistant
|
|
profiles: [homeassistant]
|
|
volumes:
|
|
- ${HA_ENV_FILE:-/etc/mike-ai/homeassistant-admin-mcp.env}:/run/secrets/homeassistant.env:ro
|
|
cap_add: [CHOWN, SETUID, SETGID]
|
|
networks: [tools, egress]
|
|
|
|
mcp-arr:
|
|
<<: *tool-common
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.arr
|
|
image: mike-ai/mcp-arr:1.0.1-patched
|
|
container_name: mike-ai-mcp-arr
|
|
profiles: [arr]
|
|
env_file:
|
|
- ${ARR_ENV_FILE:-/etc/mike-ai/arr-mcp.env}
|
|
volumes:
|
|
# The local fork adds bounded read-only Sonarr pseudo-actions. Keep the
|
|
# patch explicit until upstream publishes a self-contained 2.x image.
|
|
- ${ARR_SONARR_PATCH:-./patches/mcp_sonarr.py}:/usr/local/lib/python3.13/site-packages/arr_mcp/mcp/mcp_sonarr.py:ro
|
|
# Upstream's generic "Execute any Radarr API action" text gives small
|
|
# models no routing boundary. This overlay changes guidance only.
|
|
- ${ARR_RADARR_PATCH:-./patches/mcp_radarr.py}:/usr/local/lib/python3.13/site-packages/arr_mcp/mcp/mcp_radarr.py:ro
|
|
networks: [tools, egress]
|
|
|
|
mcp-navidrome:
|
|
<<: *tool-common
|
|
# Version and amd64 manifest are pinned. The image contains no mpv, so it
|
|
# cannot play audio on the headless AI host and does not expose playback
|
|
# controls. It talks to Navidrome only through its authenticated API.
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.navidrome
|
|
image: mike-ai/mcp-navidrome:2.2.0-schemafix1
|
|
container_name: mike-ai-mcp-navidrome
|
|
profiles: [navidrome]
|
|
env_file:
|
|
- ${NAVIDROME_MCP_ENV_FILE:-/etc/mike-ai/navidrome-mcp.env}
|
|
environment:
|
|
MCP_TRANSPORT: http
|
|
MCP_HTTP_EXPOSE: "true"
|
|
MCP_HTTP_PORT: "3000"
|
|
# The endpoint is not published on the host. Host filtering still makes
|
|
# accidental access from any other Docker name fail closed.
|
|
MCP_HTTP_ALLOWED_HOSTS: "mike-ai-mcp-navidrome:3000,mike-ai-mcp-navidrome"
|
|
WEBUI_ENABLED: "false"
|
|
tmpfs:
|
|
- /tmp:rw,noexec,nosuid,nodev,size=64m
|
|
- /config:rw,noexec,nosuid,nodev,size=4m,mode=0700
|
|
networks: [tools, egress]
|
|
|
|
mcp-platform-context:
|
|
<<: *tool-common
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.platform-context
|
|
image: mike-ai/mcp-platform-context:1.0.0
|
|
container_name: mike-ai-mcp-platform-context
|
|
environment:
|
|
ATHENA_REPO_ROOT: /knowledge/repo
|
|
ATHENA_DOCS_ROOT: /workspace/docs
|
|
ATHENA_RUNTIME_FILE: /runtime/runtime.json
|
|
ATHENA_CONTEXT_STATE: /state
|
|
# Writes remain confined to docs/*.md and require a prepared proposal
|
|
# plus its exact confirmation string. The MCP cannot change code,
|
|
# containers, networking, secrets, Git or recovery bundles.
|
|
ATHENA_DOC_WRITE_MODE: enabled
|
|
volumes:
|
|
- ${PLATFORM_STACK_DIR:-/opt/mike-ai/stack}:/knowledge/repo:ro
|
|
- ${PLATFORM_DOCS_DIR:-/opt/mike-ai/stack/docs}:/workspace/docs:rw
|
|
- ${PLATFORM_CONTEXT_RUNTIME_DIR:-/var/lib/mike-ai-platform-context}:/runtime:ro
|
|
- ${PLATFORM_CONTEXT_STATE_DIR:-/data/mike-ai-platform-context}:/state:rw
|
|
networks: [tools]
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1',8000),2); s.close()"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 10s
|
|
|
|
mcp-athena-terminal:
|
|
<<: *tool-common
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.athena-terminal
|
|
image: mike-ai/mcp-athena-terminal:1.0.0
|
|
container_name: mike-ai-mcp-athena-terminal
|
|
environment:
|
|
ATHENA_TERMINAL_WORKSPACE: /workspace
|
|
ATHENA_TERMINAL_RUNTIME: /runtime
|
|
volumes:
|
|
# The terminal sees only versioned source and the bounded, payload-free
|
|
# runtime snapshot. It receives no Docker socket, host filesystem,
|
|
# secrets, SSH material, devices, PID namespace or egress network.
|
|
- ${PLATFORM_STACK_DIR:-/opt/mike-ai/stack}:/workspace:ro
|
|
- ${PLATFORM_CONTEXT_RUNTIME_DIR:-/var/lib/mike-ai-platform-context}:/runtime:ro
|
|
networks: [tools]
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1',8000),2); s.close()"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 10s
|
|
|
|
mcp-github:
|
|
<<: *tool-common
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.github
|
|
image: mike-ai/mcp-github:github-v1.10.1-mcp-proxy-v0.12.0
|
|
container_name: mike-ai-mcp-github
|
|
profiles: [github]
|
|
env_file:
|
|
- ${GITHUB_MCP_ENV_FILE:-/etc/mike-ai/github-mcp.env}
|
|
environment:
|
|
# These server-side limits remain authoritative even if a client asks
|
|
# for broader toolsets. The token itself must also remain read-only.
|
|
GITHUB_TOOLS: search_repositories,get_repository_tree,get_file_contents,search_code
|
|
GITHUB_READ_ONLY: "1"
|
|
networks: [tools, egress]
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1',8000),2); s.close()"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 15s
|
|
|
|
mcp-unraid-official:
|
|
<<: *tool-common
|
|
image: debian:13-slim
|
|
container_name: mike-ai-mcp-unraid-official
|
|
profiles: [unraid]
|
|
env_file:
|
|
- ${RUNRAID_ENV_FILE:-/etc/mike-ai/runraid/.env}
|
|
environment:
|
|
UNRAID_RMCP_HOST: 0.0.0.0
|
|
UNRAID_RMCP_PORT: "8000"
|
|
UNRAID_RMCP_DISABLE_HTTP_AUTH: "true"
|
|
UNRAID_NOAUTH: "true"
|
|
UNRAID_RMCP_ALLOWED_HOSTS: "mike-ai-mcp-unraid-official:8000,mike-ai-mcp-unraid-official,localhost:8000,127.0.0.1:8000"
|
|
volumes:
|
|
- ${RUNRAID_BINARY:-/usr/local/bin/runraid}:/usr/local/bin/unraid:ro
|
|
entrypoint: ["/usr/local/bin/unraid"]
|
|
command: ["serve"]
|
|
networks: [tools, egress]
|
|
|
|
mcp-unraid-ssh:
|
|
<<: *tool-common
|
|
profiles: [extended]
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.unraid-ssh
|
|
image: mike-ai/mcp-unraid-ssh:local
|
|
container_name: mike-ai-mcp-unraid-ssh
|
|
environment:
|
|
UNRAID_MCP_CONFIG: /run/config/unraid-mcp.json
|
|
volumes:
|
|
- ${UNRAID_MCP_SOURCE:-/opt/mike-ai/unraid-agent/unraid_mcp.py}:/app/unraid_mcp.py:ro
|
|
- ${UNRAID_MCP_CONFIG:-/etc/mike-ai/unraid-mcp.json}:/run/config/unraid-mcp.json:ro
|
|
- ${UNRAID_SSH_KEY:-/etc/mike-ai/keys/unraid_root}:/etc/mike-ai/keys/unraid_root:ro
|
|
- ${UNRAID_KNOWN_HOSTS:-/etc/mike-ai/ssh/known_hosts_unraid_ai}:/etc/mike-ai/ssh/known_hosts_unraid_ai:ro
|
|
- unraid-audit:/var/log/mike-ai
|
|
networks: [tools, egress]
|
|
|
|
networks:
|
|
tools:
|
|
name: mike-ai-tools
|
|
internal: true
|
|
ipam:
|
|
config: [{subnet: 172.30.40.0/24}]
|
|
egress:
|
|
name: mike-ai-tools-egress
|
|
ipam:
|
|
config: [{subnet: 172.30.50.0/24}]
|
|
|
|
volumes:
|
|
tinysearch-models:
|
|
name: mike-ai-tools_tinysearch-models
|
|
external: true
|
|
unraid-audit:
|