Files
grist-mcp-server/CLAUDE.md

3.6 KiB

CLAUDE.md

Project-specific instructions for Claude Code.

Project Overview

grist-mcp is an MCP (Model Context Protocol) server that enables AI agents to interact with Grist spreadsheet documents. It provides secure, token-based access with granular permissions.

Tech Stack

  • Python 3.14+
  • MCP SDK (mcp package)
  • httpx for async HTTP requests
  • PyYAML for configuration
  • pytest + pytest-asyncio + pytest-httpx for testing

Commands

# Run unit tests
make test-unit
# or: uv run pytest tests/unit/ -v

# Run integration tests (manages containers automatically)
make test-integration
# or: ./scripts/run-integration-tests.sh

# Full pre-deploy pipeline
make pre-deploy

# Development environment
make dev-up    # Start
make dev-down  # Stop

# Build Docker image
make build

# Run the server (requires config and token)
CONFIG_PATH=./config.yaml GRIST_MCP_TOKEN=your-token uv run python -m grist_mcp.main

Project Structure

src/grist_mcp/       # Source code
├── main.py          # Entry point, runs stdio server
├── server.py        # MCP server setup, tool registration, call_tool dispatch
├── config.py        # YAML config loading with env var substitution
├── auth.py          # Token auth and permission checking
├── grist_client.py  # Async Grist API client
└── tools/
    ├── discovery.py # list_documents tool
    ├── read.py      # list_tables, describe_table, get_records, sql_query
    ├── write.py     # add_records, update_records, delete_records
    └── schema.py    # create_table, add_column, modify_column, delete_column
tests/
├── unit/            # Unit tests (no containers)
└── integration/     # Integration tests (with Docker)
deploy/
├── dev/             # Development docker-compose
├── test/            # Test docker-compose (ephemeral)
└── prod/            # Production docker-compose
scripts/             # Test automation scripts

Key Patterns

Authentication Flow

  1. Token provided via GRIST_MCP_TOKEN env var or passed to create_server()
  2. Authenticator.authenticate() validates token and returns Agent object
  3. Each tool call uses Authenticator.authorize() to check document + permission

Permission Levels

  • read: Query tables and records
  • write: Add, update, delete records
  • schema: Create tables, modify columns

Tool Implementation Pattern

All tools follow this pattern:

async def tool_name(agent, auth, document, ..., client=None):
    auth.authorize(agent, document, Permission.X)
    if client is None:
        doc = auth.get_document(document)
        client = GristClient(doc)
    result = await client.some_method(...)
    return {"key": result}

The optional client parameter enables dependency injection for testing.

Testing

Unit Tests (tests/unit/)

Fast tests using pytest-httpx to mock Grist API responses. Run with make test-unit.

  • test_auth.py: Uses in-memory Config objects
  • test_grist_client.py: Uses HTTPXMock for API mocking
  • test_tools_*.py: Combine auth fixtures with mocked clients

Integration Tests (tests/integration/)

Tests against real Grist containers. Run with make test-integration.

  • Automatically manages Docker containers via scripts/run-integration-tests.sh
  • Uses environment variables for configuration (no hardcoded URLs)
  • Containers are ephemeral and cleaned up after tests

Configuration

See config.yaml.example for the configuration format. Key points:

  • Documents define Grist instance URL, doc ID, and API key
  • Tokens define agent access with document/permission scopes
  • Environment variables can be used with ${VAR} syntax