Make ag-bids-mcp a standalone compose project on .0.2
Reflect the deploy decision: ag-bids-mcp lives in its own ~/ag-bids-mcp/ folder on 192.168.0.2 (NOT inside zerto-docs-rag's compose). It joins the existing mcp-servers_mcp Docker network as external so MetaMCP can still reach it at http://ag-bids-mcp:8000/mcp. Updated: - deploy/docker-compose.snippet.yml — now a self-contained compose project file with `networks.mcp.external: true; name: mcp-servers_mcp` - deploy/README.md — full runbook for the standalone-folder deploy, smoke-test commands that match the actual network name, and a base64-encoded Authorization-header recipe for testing without fighting curl's -u quoting Verified live: container on .0.2 returns 401 anonymous and 200 with real MCP initialize handshake from inside mcp-servers_mcp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+63
-43
@@ -1,36 +1,42 @@
|
|||||||
# Deploying `ag-bids-mcp` behind MetaMCP
|
# Deploying `ag-bids-mcp` behind MetaMCP
|
||||||
|
|
||||||
This runs on the **MetaMCP host (192.168.0.2)** alongside `zerto-docs-mcp`.
|
This runs on the **MetaMCP host (`192.168.0.2`)** as its **own standalone
|
||||||
It joins the same `mcp` Docker network so MetaMCP can proxy to it by container
|
docker-compose project** — independent from the zerto-docs-rag stack but
|
||||||
DNS name (`http://ag-bids-mcp:8000/mcp`).
|
joining the same `mcp-servers_mcp` Docker network so MetaMCP can proxy to it
|
||||||
|
by container DNS name (`http://ag-bids-mcp:8000/mcp`).
|
||||||
|
|
||||||
## One-time setup (on 192.168.0.2)
|
The deploy lives in `/home/justin/ag-bids-mcp/` on `.0.2`. Watchtower (already
|
||||||
|
running on the host) auto-pulls a new image every 5 min whenever a fresh
|
||||||
|
`git.jpaul.io/justin/ag-bids-mcp:latest` is pushed.
|
||||||
|
|
||||||
### 1. Add env vars
|
## One-time setup (already done — kept for re-runs)
|
||||||
|
|
||||||
Edit `/home/justin/zerto-docs-rag/deploy/.env`:
|
### 1. Pull the deploy snippet
|
||||||
|
|
||||||
|
`docker-compose.snippet.yml` in this directory is the ENTIRE compose file
|
||||||
|
for the deployment. Copy it to `/home/justin/ag-bids-mcp/docker-compose.yml`
|
||||||
|
on `.0.2`.
|
||||||
|
|
||||||
|
### 2. Write the `.env`
|
||||||
|
|
||||||
|
Create `/home/justin/ag-bids-mcp/.env` (mode 600) on `.0.2` with:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# Copy this from ag-monitor's .env on 192.168.0.126 (the BRIEF_API_KEY value).
|
AG_BIDS_API_URL=https://agbids.paul.farm
|
||||||
# Same key powers /api/data/* and /api/brief/*.
|
AG_BIDS_API_KEY=<copy BRIEF_API_KEY from ag-monitor .env on 192.168.0.126>
|
||||||
AG_BIDS_API_KEY=<paste>
|
AG_BIDS_API_TIMEOUT_SECS=20
|
||||||
|
AG_BIDS_MCP_USER=<pick a username; non-user-facing>
|
||||||
# Credentials MetaMCP will send to the MCP via Basic auth. Generate two
|
AG_BIDS_MCP_PASS=<32+ random bytes>
|
||||||
# random values — they're not user-facing, only MetaMCP knows them.
|
|
||||||
AG_BIDS_MCP_USER=agbids-svc
|
|
||||||
AG_BIDS_MCP_PASS=<generate: python -c "import secrets; print(secrets.token_urlsafe(32))">
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Paste the service block into compose
|
Generate a password locally:
|
||||||
|
```bash
|
||||||
Open `/home/justin/zerto-docs-rag/deploy/docker-compose.yml` and append the
|
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||||
`ag-bids-mcp:` block from [docker-compose.snippet.yml](docker-compose.snippet.yml)
|
```
|
||||||
inside `services:`, alongside `zerto-docs-mcp:`. Keep the same indentation
|
|
||||||
(2 spaces).
|
|
||||||
|
|
||||||
### 3. Build + push the image
|
### 3. Build + push the image
|
||||||
|
|
||||||
On any dev machine with `docker` and the Gitea registry login:
|
On any dev machine with the Gitea registry login:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/github/ag-bids-mcp
|
cd ~/github/ag-bids-mcp
|
||||||
@@ -39,18 +45,16 @@ docker build -t git.jpaul.io/justin/ag-bids-mcp:latest .
|
|||||||
docker push git.jpaul.io/justin/ag-bids-mcp:latest
|
docker push git.jpaul.io/justin/ag-bids-mcp:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Start the container
|
### 4. Start the container on .0.2
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/justin/zerto-docs-rag/deploy
|
ssh justin@192.168.0.2
|
||||||
docker compose pull ag-bids-mcp
|
cd ~/ag-bids-mcp
|
||||||
docker compose up -d ag-bids-mcp
|
docker compose pull
|
||||||
docker compose logs -f ag-bids-mcp # confirm "starting ag-bids MCP on streamable-http://0.0.0.0:8000 (Basic auth enforced)"
|
docker compose up -d
|
||||||
|
docker compose logs -f ag-bids-mcp # expect "starting ag-bids MCP on streamable-http://0.0.0.0:8000 (Basic auth enforced)"
|
||||||
```
|
```
|
||||||
|
|
||||||
Watchtower (already running on the host) will auto-pull updates every 5
|
|
||||||
minutes from this point forward.
|
|
||||||
|
|
||||||
### 5. Register the namespace in MetaMCP
|
### 5. Register the namespace in MetaMCP
|
||||||
|
|
||||||
In the MetaMCP web UI at `https://mcp.jpaul.io`:
|
In the MetaMCP web UI at `https://mcp.jpaul.io`:
|
||||||
@@ -67,18 +71,34 @@ In the MetaMCP web UI at `https://mcp.jpaul.io`:
|
|||||||
Public endpoint becomes:
|
Public endpoint becomes:
|
||||||
**`https://mcp.jpaul.io/metamcp/ag-bids/mcp`**
|
**`https://mcp.jpaul.io/metamcp/ag-bids/mcp`**
|
||||||
|
|
||||||
### 6. Smoke test from the LAN
|
## Smoke test
|
||||||
|
|
||||||
|
From inside the `mcp-servers_mcp` network (e.g. the `metamcp` container)
|
||||||
|
the MCP should 401 anonymous, 200 authenticated:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 401 without creds
|
ssh justin@192.168.0.2 'docker exec metamcp wget -qS -O- --tries=1 http://ag-bids-mcp:8000/mcp 2>&1 | head -3'
|
||||||
curl -i http://192.168.0.2:8000/mcp 2>&1 | head -3
|
# expect: HTTP/1.1 401 Unauthorized
|
||||||
# 200 with creds (Initialize handshake will succeed; the MCP doesn't have
|
|
||||||
# a plain GET, so a curl probe just confirms auth)
|
|
||||||
curl -i -u "$AG_BIDS_MCP_USER:$AG_BIDS_MCP_PASS" http://192.168.0.2:8000/mcp 2>&1 | head -3
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then in a real MCP client (Claude Desktop / OpenWebUI / etc.) configured against
|
Then, with creds, an MCP `initialize` handshake should return capability
|
||||||
`https://mcp.jpaul.io/metamcp/ag-bids/mcp`, try:
|
metadata:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# from the .0.2 host
|
||||||
|
USER=<env value> PASS=<env value>
|
||||||
|
CREDS=$(printf '%s:%s' "$USER" "$PASS" | base64 -w0)
|
||||||
|
docker run --rm --network mcp-servers_mcp curlimages/curl:latest \
|
||||||
|
-s -H "Authorization: Basic $CREDS" \
|
||||||
|
-H 'content-type: application/json' \
|
||||||
|
-H 'accept: application/json, text/event-stream' \
|
||||||
|
-X POST http://ag-bids-mcp:8000/mcp \
|
||||||
|
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"1"}}}'
|
||||||
|
# expect: event: message ... "serverInfo":{"name":"ag-bids", ...}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in a real MCP client (Claude Desktop / OpenWebUI / etc.) configured
|
||||||
|
against `https://mcp.jpaul.io/metamcp/ag-bids/mcp`, try:
|
||||||
|
|
||||||
- **"What's the best place to sell corn today?"** → calls `best_local_bid("corn")`
|
- **"What's the best place to sell corn today?"** → calls `best_local_bid("corn")`
|
||||||
- **"What's the current price of lime?"** → calls `current_lime_price()`
|
- **"What's the current price of lime?"** → calls `current_lime_price()`
|
||||||
@@ -86,16 +106,16 @@ Then in a real MCP client (Claude Desktop / OpenWebUI / etc.) configured against
|
|||||||
|
|
||||||
## Rotating credentials
|
## Rotating credentials
|
||||||
|
|
||||||
To rotate the Basic password: change `AG_BIDS_MCP_PASS` in the `.env` on
|
To rotate the Basic password: change `AG_BIDS_MCP_PASS` in `~/ag-bids-mcp/.env`
|
||||||
192.168.0.2 → `docker compose up -d ag-bids-mcp` to restart with the new
|
on `.0.2` → `docker compose up -d` to restart with the new value → update
|
||||||
value → update the MetaMCP namespace's upstream Basic password to match.
|
the MetaMCP namespace's upstream Basic password to match.
|
||||||
|
|
||||||
To rotate the upstream API key: change `BRIEF_API_KEY` in ag-monitor's `.env`
|
To rotate the upstream API key: change `BRIEF_API_KEY` in ag-monitor's `.env`
|
||||||
on 192.168.0.126 + restart `api` there, then update `AG_BIDS_API_KEY` on
|
on `.0.126` + restart `api` there, then update `AG_BIDS_API_KEY` in
|
||||||
192.168.0.2 + restart `ag-bids-mcp`.
|
`~/ag-bids-mcp/.env` on `.0.2` + `docker compose up -d`.
|
||||||
|
|
||||||
## Observability
|
## Observability
|
||||||
|
|
||||||
- Per-tool-call usage logs: `/home/justin/zerto-docs-rag/deploy/ag-bids-mcp-logs/usage-YYYY-MM-DD.jsonl`
|
- Per-tool-call usage logs: `/home/justin/ag-bids-mcp/ag-bids-mcp-logs/usage-YYYY-MM-DD.jsonl`
|
||||||
- Container stdout: `docker compose logs ag-bids-mcp`
|
- Container stdout: `docker compose logs ag-bids-mcp`
|
||||||
- Successful auth → no log line; failed auth → INFO line with the offending path
|
- Successful auth → no log line; failed auth → INFO line with the offending path
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
# Paste this service block into /home/justin/zerto-docs-rag/deploy/docker-compose.yml
|
# Standalone docker-compose for ag-bids-mcp.
|
||||||
# on 192.168.0.2 (the MetaMCP host), alongside zerto-docs-mcp. It joins the
|
|
||||||
# same `mcp` Docker network so MetaMCP can reach it by container DNS name.
|
|
||||||
#
|
#
|
||||||
# Required env vars (set in the same .env that already powers the rest of the
|
# This file is the ENTIRE compose project — it does NOT belong inside
|
||||||
# MetaMCP stack):
|
# zerto-docs-rag's compose. Put it in /home/justin/ag-bids-mcp/ on the
|
||||||
# AG_BIDS_API_KEY — copy from ag-monitor's .env (BRIEF_API_KEY)
|
# MetaMCP host (192.168.0.2) alongside a .env file with three secrets:
|
||||||
|
# AG_BIDS_API_KEY — copy from ag-monitor's .env (BRIEF_API_KEY) on .0.126
|
||||||
# AG_BIDS_MCP_USER — username MetaMCP will send in Basic auth
|
# AG_BIDS_MCP_USER — username MetaMCP will send in Basic auth
|
||||||
# AG_BIDS_MCP_PASS — password MetaMCP will send in Basic auth
|
# AG_BIDS_MCP_PASS — password MetaMCP will send in Basic auth
|
||||||
|
#
|
||||||
|
# Joins the EXISTING `mcp-servers_mcp` network (created by the MetaMCP
|
||||||
|
# compose project at /home/justin/mcp-servers/) as external, so MetaMCP
|
||||||
|
# can reach this container by DNS name `ag-bids-mcp`.
|
||||||
|
|
||||||
|
services:
|
||||||
ag-bids-mcp:
|
ag-bids-mcp:
|
||||||
container_name: ag-bids-mcp
|
container_name: ag-bids-mcp
|
||||||
image: git.jpaul.io/justin/ag-bids-mcp:latest
|
image: git.jpaul.io/justin/ag-bids-mcp:latest
|
||||||
pull_policy: always
|
pull_policy: always
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
env_file: .env
|
||||||
environment:
|
environment:
|
||||||
MCP_TRANSPORT: streamable-http
|
MCP_TRANSPORT: streamable-http
|
||||||
MCP_HOST: 0.0.0.0
|
MCP_HOST: 0.0.0.0
|
||||||
@@ -20,14 +25,6 @@
|
|||||||
# Behind a Docker DNS name, FastMCP's localhost-only rebinding-protection
|
# Behind a Docker DNS name, FastMCP's localhost-only rebinding-protection
|
||||||
# would 421 every call from MetaMCP. Disable it; the mcp network is private.
|
# would 421 every call from MetaMCP. Disable it; the mcp network is private.
|
||||||
MCP_DISABLE_DNS_REBINDING_PROTECTION: "1"
|
MCP_DISABLE_DNS_REBINDING_PROTECTION: "1"
|
||||||
# --- upstream ag-monitor (Cloudflare Tunnel from .0.126) ---
|
|
||||||
AG_BIDS_API_URL: https://agbids.paul.farm
|
|
||||||
AG_BIDS_API_KEY: ${AG_BIDS_API_KEY}
|
|
||||||
AG_BIDS_API_TIMEOUT_SECS: "20"
|
|
||||||
# --- HTTP Basic auth in front of this MCP ---
|
|
||||||
AG_BIDS_MCP_USER: ${AG_BIDS_MCP_USER}
|
|
||||||
AG_BIDS_MCP_PASS: ${AG_BIDS_MCP_PASS}
|
|
||||||
# --- per-tool-call JSONL usage log ---
|
|
||||||
USAGE_LOG_DIR: /app/var/logs
|
USAGE_LOG_DIR: /app/var/logs
|
||||||
USAGE_LOG_KEEP_DAYS: "90"
|
USAGE_LOG_KEEP_DAYS: "90"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -35,4 +32,13 @@
|
|||||||
- ./ag-bids-mcp-logs:/app/var/logs
|
- ./ag-bids-mcp-logs:/app/var/logs
|
||||||
networks: [mcp]
|
networks: [mcp]
|
||||||
labels:
|
labels:
|
||||||
|
# Watchtower (on the same daemon) auto-pulls new images for any container
|
||||||
|
# with this label set to "true".
|
||||||
com.centurylinklabs.watchtower.enable: "true"
|
com.centurylinklabs.watchtower.enable: "true"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
mcp:
|
||||||
|
external: true
|
||||||
|
# Confirmed on 192.168.0.2 — joined by metamcp, metamcp-pg, zerto-docs-mcp,
|
||||||
|
# jina-rerank. Created by the compose project rooted at ~/mcp-servers/.
|
||||||
|
name: mcp-servers_mcp
|
||||||
|
|||||||
Reference in New Issue
Block a user