diff --git a/Setup.md b/Setup.md index 7be559f..b540658 100644 --- a/Setup.md +++ b/Setup.md @@ -194,7 +194,7 @@ Supported configurations: Add to your `.env`: ```env -stripe_api_key=sk_test_your_key_here +STRIPE_API_KEY=sk_test_your_key_here ``` Use a **test key** (`sk_test_...`) during development. Switch to a live key (`sk_live_...`) for production. @@ -220,6 +220,8 @@ google_drive_sa_json=/absolute/path/to/service-account.json GOOGLE_DRIVE_FOLDER_ID=your-folder-id-from-drive-url ``` +When you run the **Google Drive MCP** container via `docker-compose.mcp.yml`, use a **volume mount** for the JSON key (see `sample.env` and [docs/mcp-servers.md](docs/mcp-servers.md#run-with-docker-compose)) instead of relying on a host-only path inside the container. + --- ### FHIR Epic @@ -285,13 +287,15 @@ Each connector runs as its own independent MCP server. This is the preferred app Quick start: ```bash -# Build all three images +# Build all per-connector MCP images ./scripts/build-mcp-images.sh -# Start all three locally +# Start the stack locally (Google Drive MCP needs the service account JSON mounted — see below) docker compose -f docker-compose.mcp.yml up ``` +For the **Google Drive** MCP image (`nw-google-drive`), the service account JSON key is **mounted into the container as a volume** (host file → `/etc/secrets/google-drive-sa.json`). Set `GOOGLE_DRIVE_SA_JSON` to that in-container path and `GOOGLE_DRIVE_SA_JSON_HOST` to the key file on your machine. Details: [docs/mcp-servers.md](docs/mcp-servers.md#run-with-docker-compose). + ### Combined MCP server (all connectors in one) For simpler setups all connectors can be exposed from a single MCP server: diff --git a/docker-compose.mcp.yml b/docker-compose.mcp.yml index 1004b3f..f3dd6af 100644 --- a/docker-compose.mcp.yml +++ b/docker-compose.mcp.yml @@ -2,6 +2,8 @@ services: nw-google-drive: image: nw-google-drive:latest env_file: .env + volumes: + - ${GOOGLE_DRIVE_SA_JSON_HOST:-./service-account.json}:/etc/secrets/google-drive-sa.json:ro stdin_open: true tty: true restart: unless-stopped diff --git a/docs/google_drive_connector.md b/docs/google_drive_connector.md index a66f116..081b69c 100644 --- a/docs/google_drive_connector.md +++ b/docs/google_drive_connector.md @@ -109,6 +109,8 @@ $env:GOOGLE_DRIVE_SA_JSON = Get-Content -Path $saPath -Raw > **For ToolHive deployment:** Instead of a file path, paste the entire JSON content as a single-line string into the ToolHive secret named `GOOGLE_DRIVE_SA_JSON`. See [docs/toolhive_agent_scenario.md](toolhive_agent_scenario.md). +> **For the Google Drive MCP image with Docker Compose:** Mount the JSON key file as a **volume** into the container (see [docs/mcp-servers.md](mcp-servers.md#run-with-docker-compose) and `docker-compose.mcp.yml`) and set `GOOGLE_DRIVE_SA_JSON` to the in-container path (`/etc/secrets/google-drive-sa.json`). + ### Step 6: Share a Google Drive Folder with the Service Account The service account starts with no access to any Drive files. You must explicitly share folders or files with it, just like sharing with any other Google user. diff --git a/docs/mcp-servers.md b/docs/mcp-servers.md index 1dfe8de..ec08526 100644 --- a/docs/mcp-servers.md +++ b/docs/mcp-servers.md @@ -72,6 +72,8 @@ GOOGLE_DRIVE_SA_JSON=/absolute/path/to/service-account.json GOOGLE_DRIVE_FOLDER_ID=your-google-drive-folder-id ``` +> **Docker Compose MCP (`nw-google-drive`):** Mount the service account JSON from the host into the container and point `GOOGLE_DRIVE_SA_JSON` at the path **inside** the container. `docker-compose.mcp.yml` binds `${GOOGLE_DRIVE_SA_JSON_HOST:-./service-account.json}` → `/etc/secrets/google-drive-sa.json` (read-only). Set `GOOGLE_DRIVE_SA_JSON=/etc/secrets/google-drive-sa.json` and `GOOGLE_DRIVE_SA_JSON_HOST` to your key file on the host (see `sample.env`). + > **ToolHive note:** When running inside ToolHive, set `GOOGLE_DRIVE_SA_JSON` to the JSON *contents* (not a file path) because ToolHive injects secrets as string values, not files. #### `nw-smartonfhir-epic` @@ -208,7 +210,9 @@ docker build -f docker/smtp/Dockerfile -t nw-smtp:latest . ## Run with docker-compose -`docker-compose.mcp.yml` starts all three MCP servers as stdio containers in one command. This is useful for local validation before configuring ToolHive. +`docker-compose.mcp.yml` starts all per-connector MCP servers as stdio containers in one command. This is useful for local validation before configuring ToolHive. + +The **Google Drive** service (`nw-google-drive`) **mounts the service account JSON key as a volume**: the host file is mounted read-only at `/etc/secrets/google-drive-sa.json`. Configure `GOOGLE_DRIVE_SA_JSON_HOST` (host path to the `.json` file) and `GOOGLE_DRIVE_SA_JSON=/etc/secrets/google-drive-sa.json` in `.env` alongside `GOOGLE_DRIVE_FOLDER_ID`. This avoids baking secrets into the image and matches how a real deployment supplies credentials to the MCP container. ```bash # Ensure your .env is populated, then: @@ -338,6 +342,7 @@ python -m agents.toolhive --local --patient-id 12724066 --recipient-email you@ex | `TOOLHIVE_MCP_URL(S) is not set` | Missing env var | Set `TOOLHIVE_MCP_URL` (single) or `TOOLHIVE_MCP_URLS` (multi) in `.env` | | `Failed to list MCP tools: Connection refused` | ToolHive proxy stopped | Re-register with `thv run`; confirm the proxy URL matches what ToolHive UI shows | | Google Drive auth failures | Secret injected as a file path | For ToolHive, set `GOOGLE_DRIVE_SA_JSON` to JSON *contents* (not a path) | +| Google Drive MCP fails under Compose | Wrong path or missing mount | Set `GOOGLE_DRIVE_SA_JSON=/etc/secrets/google-drive-sa.json` and `GOOGLE_DRIVE_SA_JSON_HOST` to the host key file; ensure the file exists at the default `./service-account.json` if you use the default mount | | `fhir_epic connector not configured` | Missing Epic env vars | Ensure all `EPIC_*` variables are set and non-empty | | `fhir_cerner connector not configured` | Missing Cerner env vars | Ensure all `CERNER_*` variables are set and non-empty | | Docker build fails with `COPY src/ not found` | Wrong build context | Always run `docker build` from the **repository root**, not from `docker//` | diff --git a/docs/toolhive_agent_scenario.md b/docs/toolhive_agent_scenario.md index 666c39b..cf482ae 100644 --- a/docs/toolhive_agent_scenario.md +++ b/docs/toolhive_agent_scenario.md @@ -62,6 +62,7 @@ For modular deployments, each connector can be run as an independent MCP server - `nw-google-drive` (Google Drive) - `nw-smartonfhir-epic` (Epic SMART on FHIR) - `nw-smartonfhir-cerner` (Cerner SMART on FHIR) +- `nw-smtp` (SMTP email) When running multiple MCP servers, configure the agent with **`TOOLHIVE_MCP_URLS`** (comma-separated list of ToolHive proxy URLs). The agent will merge tools across servers. @@ -135,7 +136,7 @@ Below is the full set of environment variables used by the connector platform an | `GROQ_API_KEY` | LLM (Groq) | Your Groq API key | | `GROQ_MODEL` | LLM | Example: `openai/gpt-oss-120b` | | `MCP_TRANSPORT` | ToolHive / local | `stdio` when running in ToolHive container | -| `PYTHONPATH` | Runtime | e.g. `/app/src` for container; `d:\connector-platform\src` locally | +| `PYTHONPATH` | Runtime | e.g. `/app/src` for container; `/path/to/connector-platform/src` (macOS/Linux) or `C:\path\to\connector-platform\src` (Windows) locally | | `SMTP_HOST` | SMTP connector | Example: `sandbox.smtp.mailtrap.io` | | `SMTP_PORT` | SMTP connector | Example: `2525` | | `SMTP_USERNAME` | SMTP connector | Mailtrap / SMTP user | @@ -242,7 +243,9 @@ Gather the following values before proceeding: > **Epic users:** If using Epic instead of Cerner, replace the `CERNER_*` variables with their `EPIC_*` equivalents (`EPIC_FHIR_BASE_URL`, `EPIC_TOKEN_URL`, `EPIC_CLIENT_ID`, `EPIC_KID`, `EPIC_PRIVATE_KEY`). -> **Note on `GOOGLE_DRIVE_SA_JSON`:** Paste the **entire contents** of the service account JSON file as the secret value — not the file path. This is because the Docker container doesn't have access to your local filesystem. +> **Note on `GOOGLE_DRIVE_SA_JSON`:** Paste the **entire contents** of the service account JSON file as the secret value — not the file path. This is because the ToolHive-managed container does not see arbitrary paths on your host unless you configure mounts in ToolHive; ToolHive normally injects this credential as a string. + +> **If you run the per-connector MCP stack with `docker-compose.mcp.yml` instead:** Mount the JSON file into the `nw-google-drive` container as a volume and set `GOOGLE_DRIVE_SA_JSON` to the path inside the container — see [docs/mcp-servers.md](mcp-servers.md#run-with-docker-compose). --- diff --git a/sample.env b/sample.env index 996e670..9f3a9b6 100644 --- a/sample.env +++ b/sample.env @@ -15,6 +15,9 @@ CERNER_SCOPES="system/Patient.read system/Encounter.read system/DocumentReferenc # Google Drive GOOGLE_DRIVE_SA_JSON=/absolute/path/to/service-account.json +# For docker-compose.mcp.yml (nw-google-drive): mount the key file and use the in-container path: +# GOOGLE_DRIVE_SA_JSON=/etc/secrets/google-drive-sa.json +# GOOGLE_DRIVE_SA_JSON_HOST=/absolute/path/on/host/service-account.json GOOGLE_DRIVE_FOLDER_ID=your-google-drive-folder-id # SMTP @@ -24,7 +27,7 @@ SMTP_USERNAME=your-email@gmail.com SMTP_PASSWORD=your-gmail-app-password # Stripe (optional / legacy demo) -stripe_api_key=sk_test_your_key_here +STRIPE_API_KEY=sk_test_your_key_here # ToolHive # Single-server (backward compatible)