From 204d00caf409dd9e08277263ddebf36d5d1c68db Mon Sep 17 00:00:00 2001 From: Bill Date: Thu, 1 Jan 2026 14:06:31 -0500 Subject: [PATCH] feat: add host_header config for Docker networking When Grist validates the Host header (common with reverse proxy setups), internal Docker networking fails because requests arrive with Host: container-name instead of the external domain. The new host_header config option allows overriding the Host header sent to Grist while still connecting via internal Docker hostnames. --- config.yaml.example | 8 ++++++++ src/grist_mcp/config.py | 2 ++ src/grist_mcp/grist_client.py | 2 ++ 3 files changed, 12 insertions(+) diff --git a/config.yaml.example b/config.yaml.example index 96f50df..130180b 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -23,6 +23,14 @@ documents: doc_id: pN0zE5sT2qP7x api_key: ${GRIST_PERSONAL_API_KEY} + # Docker networking example: connect via internal hostname, + # but send the external domain in the Host header + docker-grist: + url: http://grist:8080 + doc_id: abc123 + api_key: ${GRIST_API_KEY} + host_header: grist.example.com # Required when Grist validates Host header + # Agent tokens with access scopes tokens: - token: REPLACE_WITH_GENERATED_TOKEN diff --git a/src/grist_mcp/config.py b/src/grist_mcp/config.py index a2c7c54..3ac81af 100644 --- a/src/grist_mcp/config.py +++ b/src/grist_mcp/config.py @@ -14,6 +14,7 @@ class Document: url: str doc_id: str api_key: str + host_header: str | None = None # Override Host header for Docker networking @dataclass @@ -78,6 +79,7 @@ def load_config(config_path: str) -> Config: url=doc_data["url"], doc_id=doc_data["doc_id"], api_key=doc_data["api_key"], + host_header=doc_data.get("host_header"), ) # Parse tokens diff --git a/src/grist_mcp/grist_client.py b/src/grist_mcp/grist_client.py index 97468eb..e66bfae 100644 --- a/src/grist_mcp/grist_client.py +++ b/src/grist_mcp/grist_client.py @@ -17,6 +17,8 @@ class GristClient: self._doc = document self._base_url = f"{document.url.rstrip('/')}/api/docs/{document.doc_id}" self._headers = {"Authorization": f"Bearer {document.api_key}"} + if document.host_header: + self._headers["Host"] = document.host_header self._timeout = timeout async def _request(self, method: str, path: str, **kwargs) -> dict: