Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.mcp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions docs/google_drive_connector.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion docs/mcp-servers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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/<name>/` |
Expand Down
7 changes: 5 additions & 2 deletions docs/toolhive_agent_scenario.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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).

---

Expand Down
5 changes: 4 additions & 1 deletion sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down