Files
grist-mcp-server/README.md
Bill 93d43eac99 docs: update README for SSE transport
- Update running instructions to show SSE endpoints
- Replace stdio-based MCP client config with URL-based SSE config
- Add examples for local and remote deployment
2025-12-29 22:03:23 -05:00

257 lines
6.4 KiB
Markdown

# grist-mcp
MCP server for AI agents to interact with Grist documents.
## Overview
grist-mcp is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that enables AI agents to read, write, and modify Grist spreadsheets. It provides secure, token-based access control with granular permissions per document.
## Features
- **Discovery**: List accessible documents with permissions
- **Read Operations**: List tables, describe columns, fetch records, run SQL queries
- **Write Operations**: Add, update, and delete records
- **Schema Operations**: Create tables, add/modify/delete columns
- **Security**: Token-based authentication with per-document permission scopes (read, write, schema)
- **Multi-tenant**: Support multiple Grist instances and documents
## Requirements
- Python 3.14+
- Access to one or more Grist documents with API keys
## Installation
```bash
# Clone the repository
git clone https://github.com/your-org/grist-mcp.git
cd grist-mcp
# Install with uv
uv sync --dev
```
## Configuration
Create a `config.yaml` file based on the example:
```bash
cp config.yaml.example config.yaml
```
### Configuration Structure
```yaml
# Document definitions
documents:
my-document:
url: https://docs.getgrist.com # Grist instance URL
doc_id: abcd1234 # Document ID from URL
api_key: ${GRIST_API_KEY} # API key (supports env vars)
# Agent tokens with access scopes
tokens:
- token: your-secret-token # Unique token for this agent
name: my-agent # Human-readable name
scope:
- document: my-document
permissions: [read, write] # Allowed: read, write, schema
```
### Generating Tokens
```bash
python -c "import secrets; print(secrets.token_urlsafe(32))"
# or
openssl rand -base64 32
```
### Environment Variables
- `CONFIG_PATH`: Path to config file (default: `/app/config.yaml`)
- `GRIST_MCP_TOKEN`: Agent token for authentication
- Config file supports `${VAR}` syntax for API keys
## Usage
### Running the Server
The server uses SSE (Server-Sent Events) transport over HTTP:
```bash
# Set your agent token
export GRIST_MCP_TOKEN="your-agent-token"
# Run with custom config path (defaults to port 3000)
CONFIG_PATH=./config.yaml uv run python -m grist_mcp.main
# Or specify a custom port
PORT=8080 CONFIG_PATH=./config.yaml uv run python -m grist_mcp.main
```
The server exposes two endpoints:
- `http://localhost:3000/sse` - SSE connection endpoint
- `http://localhost:3000/messages` - Message posting endpoint
### MCP Client Configuration
Add to your MCP client configuration (e.g., Claude Desktop):
```json
{
"mcpServers": {
"grist": {
"url": "http://localhost:3000/sse"
}
}
}
```
For remote deployments, use the server's public URL:
```json
{
"mcpServers": {
"grist": {
"url": "https://your-server.example.com/sse"
}
}
}
```
## Available Tools
### Discovery
| Tool | Description |
|------|-------------|
| `list_documents` | List documents accessible to this agent with their permissions |
### Read Operations (requires `read` permission)
| Tool | Description |
|------|-------------|
| `list_tables` | List all tables in a document |
| `describe_table` | Get column information (id, type, formula) for a table |
| `get_records` | Fetch records with optional filter, sort, and limit |
| `sql_query` | Run a read-only SELECT query against a document |
### Write Operations (requires `write` permission)
| Tool | Description |
|------|-------------|
| `add_records` | Add new records to a table |
| `update_records` | Update existing records by ID |
| `delete_records` | Delete records by ID |
### Schema Operations (requires `schema` permission)
| Tool | Description |
|------|-------------|
| `create_table` | Create a new table with specified columns |
| `add_column` | Add a column to an existing table |
| `modify_column` | Change a column's type or formula |
| `delete_column` | Remove a column from a table |
## Security
- **Token-based auth**: Each agent has a unique token with specific document access
- **Permission scopes**: Granular control with `read`, `write`, and `schema` permissions
- **SQL validation**: Only SELECT queries allowed, no multi-statement queries
- **API key isolation**: Each document can use a different Grist API key
- **No token exposure**: Tokens are validated at startup, not stored in responses
## Development
### Running Tests
```bash
uv run pytest -v
```
### Project Structure
```
grist-mcp/
├── src/grist_mcp/
│ ├── __init__.py
│ ├── main.py # Entry point
│ ├── server.py # MCP server setup and tool registration
│ ├── config.py # Configuration loading
│ ├── auth.py # Authentication and authorization
│ ├── grist_client.py # Grist API client
│ └── tools/
│ ├── discovery.py # list_documents
│ ├── read.py # Read operations
│ ├── write.py # Write operations
│ └── schema.py # Schema operations
├── tests/
├── config.yaml.example
└── pyproject.toml
```
## Docker Deployment
### Prerequisites
- Docker and Docker Compose
### Quick Start
```bash
# 1. Copy example files
cp .env.example .env
cp config.yaml.example config.yaml
# 2. Edit .env with your tokens and API keys
# - Set GRIST_MCP_TOKEN to a secure agent token
# - Set your Grist API keys
# 3. Edit config.yaml with your document settings
# - Configure your Grist documents
# - Set up token scopes and permissions
# 4. Start the server
docker compose up -d
```
### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `PORT` | Server port | `3000` |
| `GRIST_MCP_TOKEN` | Agent authentication token (required) | - |
| `CONFIG_PATH` | Path to config file inside container | `/app/config.yaml` |
| `GRIST_*_API_KEY` | Grist API keys referenced in config.yaml | - |
### Using Prebuilt Images
To use a prebuilt image from a container registry:
```yaml
# docker-compose.yaml
services:
grist-mcp:
image: your-registry/grist-mcp:latest
ports:
- "${PORT:-3000}:3000"
volumes:
- ./config.yaml:/app/config.yaml:ro
env_file:
- .env
restart: unless-stopped
```
### Building Locally
```bash
# Build the image
docker build -t grist-mcp .
# Run directly
docker run -p 3000:3000 \
-v $(pwd)/config.yaml:/app/config.yaml:ro \
--env-file .env \
grist-mcp
```
## License
MIT