# Drawbar deploy — `crop-chem-docs` MCP server snippet Drop these two services into Drawbar's `docker-compose.yml`. Targets the trashpanda stack: shared Docker network with the existing Drawbar services + the Cloudflare Tunnel. ## Pre-reqs (one-time on the deploy host) 1. **Docker login to the Gitea registry:** ```bash docker login git.jpaul.io -u justin # PAT for password ``` 2. **NVIDIA Container Toolkit** — already installed on trashpanda (the existing standalone `llama-rerank` container ran with `--gpus all` fine). 3. **If a standalone `llama-rerank` container is already running** (left over from earlier setup), remove it so the compose service can bind the same name: ```bash docker rm -f llama-rerank ``` ## Compose services ```yaml services: # ---- Reranker sidecar ----------------------------------------- # jina-reranker-v2-base-multilingual via llama.cpp on the Tesla P4. # Internal port only (no host port mapping needed — the MCP reaches # it via Docker DNS). ~280 MB GPU VRAM at idle, ~500 MB during a # 50-doc rerank. Co-exists fine with any other GPU users on the P4. llama-rerank: image: ghcr.io/ggml-org/llama.cpp:server-cuda container_name: llama-rerank restart: unless-stopped command: - "-hf" - "gpustack/jina-reranker-v2-base-multilingual-GGUF:Q8_0" - "--reranking" - "--host" - "0.0.0.0" - "--port" - "8080" - "-ngl" - "99" # offload all layers to GPU deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] # Model cache survives container recreates; first start downloads # the GGUF (~280 MB) from HuggingFace. volumes: - llama-rerank-cache:/root/.cache/huggingface networks: - default # ---- MCP server ------------------------------------------------ crop-chem-docs: image: git.jpaul.io/justin/crop-chem-docs:corpus-2026.05.24 # :latest for dev / Watchtower auto-pull container_name: crop-chem-docs restart: unless-stopped ports: - "8001:8000" # MCP server (streamable-http). Adjust host port. # No environment block needed — the image's defaults handle it: # OLLAMA_URL=http://ollama:11434 # RERANK_URL=http://llama-rerank:8080 # HYBRID_SEARCH=true # PRODUCT_NAME=crop_chem # Override here only if your services have different names. depends_on: - llama-rerank networks: - default labels: com.centurylinklabs.watchtower.enable: "true" volumes: llama-rerank-cache: ``` ## Note on the existing `ollama` service The Dockerfile default is `OLLAMA_URL=http://ollama:11434` — that assumes there's an `ollama` service in the same compose stack. If trashpanda's Ollama is a host-mode process (not a compose service), override the env in the `crop-chem-docs` block: ```yaml environment: OLLAMA_URL: "http://host.docker.internal:11434" extra_hosts: - "host.docker.internal:host-gateway" ``` Or just add Ollama itself to the compose stack as a sibling service. ## Test once both are up ```bash docker compose up -d llama-rerank crop-chem-docs # Wait ~10s for both to come up, then: docker exec crop-chem-docs python -c \ "from docs_mcp.server import corpus_status; print(corpus_status())" ``` Expect: `# crop-chem-docs corpus status`, 4,159 labels, 216,467 chunks, BM25 db present, `RERANK_URL=http://llama-rerank:8080`, `HYBRID_SEARCH=on`. Then a live search to verify hybrid+rerank: ```bash docker exec crop-chem-docs python -c \ "from docs_mcp.server import search_docs; print(search_docs('soybean herbicide for waterhemp', k=2))" ``` Expect: 2 hits with Sencor/Tackle/Warrant in top-2, `mode=hybrid-rrf+rerank` in the header. ## What the MCP container exposes | Tool | What it does | |---|---| | `search_docs` | Hybrid+rerank pesticide-label search with optional filters | | `get_page` | Full label markdown + metadata by `(source, source_key)` | | `list_versions` | Discover sources, product classes, signal words, registrants | | `corpus_status` | Counts + freshness; useful for health probes | | `crop_chem_api_lessons` | Curated agronomy / label-handling knowledge — call before recommending | ## Tag scheme | Tag | When | Use for | |---|---|---| | `:latest` | Every monthly refresh + every code push | Dev / Watchtower auto-pull | | `:` | Every build | Rollback pin | | `:corpus-YYYY.MM.DD` | Every build | **Production pin** (frozen corpus version) | ## Updating the corpus - **Monthly cron** — 1st @ 06:00 UTC, full re-scrape of Bayer + EPA PPLS, reindex, image push. Watchtower pulls the new `:latest` automatically. - **Manual** — Gitea Actions UI → `Monthly corpus refresh` → `Run workflow`. Optional `sources` input for single-source refresh (e.g., `bayer` only).