Replace the abandoned llms-txt-mcp/Chroma docs backend with an in-repo MCP service: SQLite WAL + FTS5 + sentence-transformer embeddings, transactional source replacement, persisted state across restarts, singleflight refresh with conditional requests, hybrid lexical/semantic ranking with exact-duplicate collapse, source/host filters, and explicit-by-default content retrieval. Add docs_rebuild and a docs-rebuild CLI command. Add deterministic llms-full.txt snapshot generation for machine-local menus with hash-validated provenance manifests; lifecycle commands promote a local menu to its snapshot only when the manifest validates. Switch public source profiles to content-bearing llms-full.txt feeds. Improve web search: bounded provider fallback with per-attempt diagnostics and cancellation, an optional Brave Search API provider, strict SearXNG engine selection, capped link/media extraction, and a real engine=browser renderer that routes every request through the existing SSRF vetting while blocking WebSockets, non-GET traffic, and private destinations. Extend release checks with offline unit suites and isolated candidate container tests for both images.
98 lines
4.4 KiB
YAML
98 lines
4.4 KiB
YAML
name: context-kit
|
|
|
|
services:
|
|
searxng:
|
|
image: docker.io/searxng/searxng@sha256:e37c25170d9f5947b16713af33e0ab41f0e6e6e73685e19c30fc6bb63562f801
|
|
restart: unless-stopped
|
|
ports:
|
|
- "127.0.0.1:${CONTEXT_KIT_SEARXNG_PORT:-8099}:8080"
|
|
environment:
|
|
BASE_URL: "http://127.0.0.1:${CONTEXT_KIT_SEARXNG_PORT:-8099}/"
|
|
INSTANCE_NAME: "context-kit-search"
|
|
volumes:
|
|
- ./docker/web-search/searxng/settings.yml:/etc/searxng/settings.yml:ro
|
|
- searxng-cache:/var/cache/searxng
|
|
labels:
|
|
dev.context-kit: "true"
|
|
|
|
web-search-mcp:
|
|
build:
|
|
context: ./docker/web-search
|
|
args:
|
|
MCP_WEB_SEARCH_MAX_BYTES: "${CONTEXT_KIT_WEB_SEARCH_MAX_BYTES:-52428800}"
|
|
image: ${CONTEXT_KIT_WEB_SEARCH_IMAGE:-context-kit/web-search-mcp:latest}
|
|
# Long-lived shared web-search MCP. Clients connect directly over
|
|
# Streamable HTTP; stdio callers use a disposable proxy bridge.
|
|
restart: unless-stopped
|
|
init: true
|
|
depends_on:
|
|
- searxng
|
|
ports:
|
|
- "127.0.0.1:${CONTEXT_KIT_WEB_SEARCH_PORT:-8777}:8000"
|
|
environment:
|
|
DEFAULT_SEARCH_PROVIDER: "${CONTEXT_KIT_WEB_SEARCH_PROVIDER:-searxng}"
|
|
SEARXNG_URL: "http://searxng:8080"
|
|
CHROME_PATH: "${CONTEXT_KIT_WEB_SEARCH_CHROME_PATH:-/usr/bin/chromium}"
|
|
HTTP_TIMEOUT: "${CONTEXT_KIT_WEB_SEARCH_HTTP_TIMEOUT:-15000}"
|
|
MAX_BYTES: "${CONTEXT_KIT_WEB_SEARCH_MAX_BYTES:-52428800}"
|
|
MAX_RESULTS: "${CONTEXT_KIT_WEB_SEARCH_MAX_RESULTS:-10}"
|
|
MAX_PROVIDER_ATTEMPTS: "${CONTEXT_KIT_WEB_SEARCH_MAX_PROVIDER_ATTEMPTS:-4}"
|
|
SEARCH_PROVIDER_TIMEOUT_MS: "${CONTEXT_KIT_WEB_SEARCH_PROVIDER_TIMEOUT:-15000}"
|
|
BRAVE_SEARCH_API_KEY: "${CONTEXT_KIT_BRAVE_SEARCH_API_KEY:-}"
|
|
BROWSER_SEARCH_USER_AGENT: "${CONTEXT_KIT_WEB_SEARCH_BROWSER_USER_AGENT:-}"
|
|
MCP_COMPAT_MODE: "${CONTEXT_KIT_WEB_SEARCH_MCP_COMPAT_MODE:-}"
|
|
healthcheck:
|
|
test: ["CMD", "node", "/usr/local/lib/context-kit/mcp-probe.mjs", "http://127.0.0.1:8000/mcp"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 15s
|
|
labels:
|
|
dev.context-kit: "true"
|
|
dev.context-kit.lifecycle: "shared"
|
|
dev.context-kit.owner: "host"
|
|
dev.context-kit.uid: "${CONTEXT_KIT_HOST_UID:-1000}"
|
|
|
|
docs-mcp:
|
|
build:
|
|
context: ./docker/docs
|
|
image: ${CONTEXT_KIT_DOCS_IMAGE:-context-kit/docs-mcp:latest}
|
|
# Long-lived shared docs MCP. One container owns the transactional SQLite
|
|
# index and embedding model; clients connect over Streamable HTTP.
|
|
restart: unless-stopped
|
|
ports:
|
|
- "127.0.0.1:${CONTEXT_KIT_DOCS_PORT:-8776}:8000"
|
|
# Run as the host user so bind-mounted /data isn't owned by root.
|
|
user: "${CONTEXT_KIT_DOCS_UID:-1000}:${CONTEXT_KIT_DOCS_GID:-1000}"
|
|
environment:
|
|
HOME: /tmp
|
|
USER: context-kit
|
|
LOGNAME: context-kit
|
|
TORCHINDUCTOR_CACHE_DIR: /tmp/torchinductor
|
|
DOCS_MCP_TTL: "${CONTEXT_KIT_DOCS_TTL:-24h}"
|
|
DOCS_MCP_MAX_GET_BYTES: "${CONTEXT_KIT_DOCS_MAX_GET_BYTES:-75000}"
|
|
DOCS_MCP_EMBED_MODEL: "${CONTEXT_KIT_DOCS_EMBED_MODEL:-BAAI/bge-small-en-v1.5}"
|
|
DOCS_MCP_ALLOW_ORIGIN: "${CONTEXT_KIT_DOCS_ALLOW_ORIGIN:-}"
|
|
DOCS_MCP_LOCAL_SOURCES_PORT: "${CONTEXT_KIT_DOCS_LOCAL_SOURCES_PORT:-8769}"
|
|
# Preindex on startup is off by default; use the docs_refresh tool to
|
|
# refresh on demand. Set CONTEXT_KIT_DOCS_PREINDEX=1 to restore eager.
|
|
DOCS_MCP_PREINDEX: "${CONTEXT_KIT_DOCS_PREINDEX:-0}"
|
|
volumes:
|
|
- ${CONTEXT_KIT_DATA_DIR:-${HOME:?Set HOME or CONTEXT_KIT_DATA_DIR, or run bin/context-kit instead}/.local/share/context-kit}/docs:/data
|
|
- ${CONTEXT_KIT_DATA_DIR:-${HOME:?Set HOME or CONTEXT_KIT_DATA_DIR, or run bin/context-kit instead}/.local/share/context-kit}/models:/models
|
|
- ${CONTEXT_KIT_DATA_DIR:-${HOME:?Set HOME or CONTEXT_KIT_DATA_DIR, or run bin/context-kit instead}/.local/share/context-kit}/docs-sources.txt:/etc/context-kit/docs-sources.txt:ro
|
|
- ${CONTEXT_KIT_DOCS_LOCAL_SOURCES_DIR:-${CONTEXT_KIT_DATA_DIR:-${HOME:?Set HOME or CONTEXT_KIT_DATA_DIR, or run bin/context-kit instead}/.local/share/context-kit}/local-sources}:/etc/context-kit/local-sources:ro
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python -c \"import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/status', timeout=2).status < 500 else 1)\""]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 60s
|
|
labels:
|
|
dev.context-kit: "true"
|
|
|
|
volumes:
|
|
searxng-cache:
|
|
labels:
|
|
dev.context-kit: "true"
|