From 3eee0bf2967de0ef95acfe86f05b2d0c31c147e6 Mon Sep 17 00:00:00 2001 From: Bill Date: Thu, 1 Jan 2026 09:22:06 -0500 Subject: [PATCH] feat: add dev config and graceful config handling - Add deploy/dev/config.yaml for dev environment testing - Mount config from ./config.yaml instead of project root - Create template config if missing and exit gracefully - Update .gitignore to only ignore root config.yaml --- .gitignore | 2 +- deploy/dev/config.yaml | 30 +++++++++++++++++++ deploy/dev/docker-compose.yml | 2 +- src/grist_mcp/main.py | 54 +++++++++++++++++++++++++++++++++-- 4 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 deploy/dev/config.yaml diff --git a/.gitignore b/.gitignore index 17aeb95..0149089 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ __pycache__/ *.py[cod] .venv/ .env -config.yaml +/config.yaml *.egg-info/ dist/ .pytest_cache/ diff --git a/deploy/dev/config.yaml b/deploy/dev/config.yaml new file mode 100644 index 0000000..c4692ab --- /dev/null +++ b/deploy/dev/config.yaml @@ -0,0 +1,30 @@ +# Development configuration for grist-mcp +# +# Token Generation: +# python -c "import secrets; print(secrets.token_urlsafe(32))" +# openssl rand -base64 32 + +# Document definitions +documents: + mcp-test-document: + url: https://grist.bballou.com/ + doc_id: mVQvKTAyZC1FWZQgfuVeHC + api_key: 83a03433a61ee9d2f2bf055d7f4518bedef0421a + +# Agent tokens with access scopes +tokens: + - token: test-token-all-permissions + name: dev-agent + scope: + - document: mcp-test-document + permissions: [read, write, schema] + - token: test-token-read-permissions + name: dev-agent-read + scope: + - document: mcp-test-document + permissions: [read] + - token: test-token-no-schema-permissions + name: dev-agent-no-schema + scope: + - document: mcp-test-document + permissions: [read, write] diff --git a/deploy/dev/docker-compose.yml b/deploy/dev/docker-compose.yml index 44de30b..30830b2 100644 --- a/deploy/dev/docker-compose.yml +++ b/deploy/dev/docker-compose.yml @@ -8,7 +8,7 @@ services: - "${PORT:-3000}:3000" volumes: - ../../src:/app/src:ro - - ../../config.yaml:/app/config.yaml:ro + - ./config.yaml:/app/config.yaml:ro environment: - CONFIG_PATH=/app/config.yaml healthcheck: diff --git a/src/grist_mcp/main.py b/src/grist_mcp/main.py index c26d215..94dbe63 100644 --- a/src/grist_mcp/main.py +++ b/src/grist_mcp/main.py @@ -41,13 +41,61 @@ async def send_error(send: Send, status: int, message: str) -> None: }) +CONFIG_TEMPLATE = """\ +# grist-mcp configuration +# +# Token Generation: +# python -c "import secrets; print(secrets.token_urlsafe(32))" +# openssl rand -base64 32 + +# Document definitions +documents: + my-document: + url: https://docs.getgrist.com + doc_id: YOUR_DOC_ID + api_key: ${GRIST_API_KEY} + +# Agent tokens with access scopes +tokens: + - token: REPLACE_WITH_GENERATED_TOKEN + name: my-agent + scope: + - document: my-document + permissions: [read, write] +""" + + +def _ensure_config(config_path: str) -> bool: + """Ensure config file exists. Creates template if missing. + + Returns True if config is ready, False if template was created. + """ + path = os.path.abspath(config_path) + + # Check if path is a directory (Docker creates this when mounting missing file) + if os.path.isdir(path): + os.rmdir(path) + + if os.path.exists(path): + return True + + # Create template config + with open(path, "w") as f: + f.write(CONFIG_TEMPLATE) + + print(f"Created template configuration at: {path}") + print() + print("Please edit this file to configure your Grist documents and agent tokens,") + print("then restart the server.") + return False + + def create_app(): """Create the ASGI application.""" config_path = os.environ.get("CONFIG_PATH", "/app/config.yaml") - if not os.path.exists(config_path): - print(f"Error: Config file not found at {config_path}", file=sys.stderr) - sys.exit(1) + if not _ensure_config(config_path): + sys.exit(0) config = load_config(config_path) auth = Authenticator(config)