255 lines
7.1 KiB
Markdown
255 lines
7.1 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
|
|
|
|
## Quick Start (Docker)
|
|
|
|
### Prerequisites
|
|
|
|
- Docker and Docker Compose
|
|
- Access to one or more Grist documents with API keys
|
|
|
|
### 1. Create configuration directory
|
|
|
|
```bash
|
|
mkdir grist-mcp && cd grist-mcp
|
|
```
|
|
|
|
### 2. Download configuration files
|
|
|
|
```bash
|
|
# Download docker-compose.yml
|
|
curl -O https://raw.githubusercontent.com/Xe138/grist-mcp-server/master/deploy/prod/docker-compose.yml
|
|
|
|
# Download example config
|
|
curl -O https://raw.githubusercontent.com/Xe138/grist-mcp-server/master/config.yaml.example
|
|
cp config.yaml.example config.yaml
|
|
```
|
|
|
|
### 3. Generate tokens
|
|
|
|
Generate a secure token for your agent:
|
|
|
|
```bash
|
|
python -c "import secrets; print(secrets.token_urlsafe(32))"
|
|
# or
|
|
openssl rand -base64 32
|
|
```
|
|
|
|
### 4. Configure config.yaml
|
|
|
|
Edit `config.yaml` to define your Grist documents and agent tokens:
|
|
|
|
```yaml
|
|
# Document definitions
|
|
documents:
|
|
my-document: # Friendly name (used in token scopes)
|
|
url: https://docs.getgrist.com # Your Grist instance URL
|
|
doc_id: abcd1234efgh5678 # Document ID from the URL
|
|
api_key: your-grist-api-key # Grist API key (or use ${ENV_VAR} syntax)
|
|
|
|
# Agent tokens with access scopes
|
|
tokens:
|
|
- token: your-generated-token-here # The token you generated in step 3
|
|
name: my-agent # Human-readable name
|
|
scope:
|
|
- document: my-document # Must match a document name above
|
|
permissions: [read, write] # Allowed: read, write, schema
|
|
```
|
|
|
|
**Finding your Grist document ID**: Open your Grist document in a browser. The URL will look like:
|
|
`https://docs.getgrist.com/abcd1234efgh5678/My-Document` - the document ID is `abcd1234efgh5678`.
|
|
|
|
**Getting a Grist API key**: In Grist, go to Profile Settings → API → Create API Key.
|
|
|
|
### 5. Create .env file
|
|
|
|
Create a `.env` file with your agent token:
|
|
|
|
```bash
|
|
# .env
|
|
GRIST_MCP_TOKEN=your-generated-token-here
|
|
PORT=3000
|
|
```
|
|
|
|
The `GRIST_MCP_TOKEN` must match one of the tokens defined in `config.yaml`.
|
|
|
|
### 6. Start the server
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
The server will be available at `http://localhost:3000`.
|
|
|
|
### 7. Configure your MCP client
|
|
|
|
Add to your MCP client configuration (e.g., Claude Desktop):
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"grist": {
|
|
"type": "sse",
|
|
"url": "http://localhost:3000/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 |
|
|
|
|
## Configuration Reference
|
|
|
|
### 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` |
|
|
|
|
### config.yaml Structure
|
|
|
|
```yaml
|
|
# Document definitions (each is self-contained)
|
|
documents:
|
|
budget-2024:
|
|
url: https://work.getgrist.com
|
|
doc_id: mK7xB2pQ9mN4v
|
|
api_key: ${GRIST_WORK_API_KEY} # Supports environment variable substitution
|
|
|
|
personal-tracker:
|
|
url: https://docs.getgrist.com
|
|
doc_id: pN0zE5sT2qP7x
|
|
api_key: ${GRIST_PERSONAL_API_KEY}
|
|
|
|
# Agent tokens with access scopes
|
|
tokens:
|
|
- token: your-secure-token-here
|
|
name: finance-agent
|
|
scope:
|
|
- document: budget-2024
|
|
permissions: [read, write] # Can read and write
|
|
|
|
- token: another-token-here
|
|
name: readonly-agent
|
|
scope:
|
|
- document: budget-2024
|
|
permissions: [read] # Read only
|
|
- document: personal-tracker
|
|
permissions: [read, write, schema] # Full access
|
|
```
|
|
|
|
### Permission Levels
|
|
|
|
- `read`: Query tables and records, run SQL queries
|
|
- `write`: Add, update, delete records
|
|
- `schema`: Create tables, add/modify/delete columns
|
|
|
|
## 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
|
|
|
|
### Requirements
|
|
|
|
- Python 3.14+
|
|
- uv package manager
|
|
|
|
### Local Setup
|
|
|
|
```bash
|
|
# Clone the repository
|
|
git clone https://github.com/Xe138/grist-mcp-server.git
|
|
cd grist-mcp-server
|
|
|
|
# Install dependencies
|
|
uv sync --dev
|
|
|
|
# Run tests
|
|
make test-unit
|
|
```
|
|
|
|
### Running Locally
|
|
|
|
```bash
|
|
export GRIST_MCP_TOKEN="your-agent-token"
|
|
CONFIG_PATH=./config.yaml uv run python -m grist_mcp.main
|
|
```
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
grist-mcp/
|
|
├── src/grist_mcp/
|
|
│ ├── 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/
|
|
│ ├── unit/ # Unit tests
|
|
│ └── integration/ # Integration tests
|
|
├── deploy/
|
|
│ ├── dev/ # Development docker-compose
|
|
│ ├── test/ # Test docker-compose
|
|
│ └── prod/ # Production docker-compose
|
|
└── config.yaml.example
|
|
```
|
|
|
|
## License
|
|
|
|
MIT
|