# seatable

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/seatable/seatable-mcp  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/seatable

## Description
MCP server for SeaTable — read, write, search, link, and query data in your bases

## Claude Desktop Quick Installation
Heuristic fallback — verify the package name and runner against the repository README before running it. Uses `npx` (confidence: low):

```json
"mcpServers": {
  "seatable": {
    "command": "npx",
    "args": ["-y","seatable"]
  }
}
```

## Documentation & README

# SeaTable MCP

The official Model Context Protocol (MCP) server for [SeaTable](https://seatable.com), built and maintained by SeaTable GmbH. It lets AI agents interact with data in your bases — reading, writing, searching, linking, and querying rows through a focused set of tools. The server intentionally focuses on data operations, not schema management (creating/deleting tables or columns), keeping the tool set lean and safe for autonomous agent use.

## Quick Start

The fastest way to get started depends on your setup:

- **SeaTable Cloud** — Use the hosted MCP server at `mcp.seatable.com`, no installation needed
- **Self-hosted SeaTable** — Run the MCP server locally via `npx` in your IDE

### SeaTable Cloud (hosted MCP server)

If you use [SeaTable Cloud](https://cloud.seatable.io), there is a hosted MCP server ready to use — no installation required. Configure your MCP client with the Streamable HTTP endpoint:

**Claude Desktop** — add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "seatable": {
      "type": "streamable-http",
      "url": "https://mcp.seatable.com/mcp",
      "headers": {
        "Authorization": "Bearer your-api-token"
      }
    }
  }
}
```

**Cursor / VSCode** — add to your MCP settings (JSON):

```json
{
  "mcp.servers": {
    "seatable": {
      "type": "streamable-http",
      "url": "https://mcp.seatable.com/mcp",
      "headers": {
        "Authorization": "Bearer your-api-token"
      }
    }
  }
}
```

**ChatGPT and other OAuth-compatible clients** — use the built-in OAuth flow. In ChatGPT's developer mode, configure:

- **Server URL:** `https://mcp.seatable.com/mcp`
- **Auth type:** OAuth
- **Authorization URL:** `https://mcp.seatable.com/authorize`
- **Token URL:** `https://mcp.seatable.com/token`

You will be prompted to enter your SeaTable API token during the authorization step.

### Self-hosted SeaTable

For self-hosted SeaTable instances, run the MCP server locally via `npx`. Your IDE starts and manages the process automatically.

**Claude Desktop** — add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "seatable": {
      "command": "npx",
      "args": ["-y", "@seatable/mcp-seatable"],
      "env": {
        "SEATABLE_SERVER_URL": "https://your-seatable-server.com",
        "SEATABLE_API_TOKEN": "your-api-token"
      }
    }
  }
}
```

**Cursor / VSCode** — add to your MCP settings (JSON):

```json
{
  "mcp.servers": {
    "seatable": {
      "command": "npx",
      "args": ["-y", "@seatable/mcp-seatable"],
      "env": {
        "SEATABLE_SERVER_URL": "https://your-seatable-server.com",
        "SEATABLE_API_TOKEN": "your-api-token"
      }
    }
  }
}
```

## Deployment Options

If you need to run your own server instance — for example on your own infrastructure, with multi-base support, or in multi-tenant mode — use one of the options below.

### HTTP Server (Network Access)

Run a local HTTP server with Streamable HTTP transport:

```bash
PORT=3001 npx -y @seatable/mcp-seatable --sse

# Health check
curl http://localhost:3001/health

# MCP endpoint: POST/GET/DELETE http://localhost:3001/mcp
```

### Multi-Base (Selfhosted)

Serve multiple bases from a single process:

```bash
SEATABLE_SERVER_URL=https://your-seatable-server.com \
SEATABLE_BASES='[{"base_name":"CRM","api_token":"token_abc"},{"base_name":"Projects","api_token":"token_def"}]' \
npx -y @seatable/mcp-seatable
```

Each tool automatically gets a `base` parameter. Use `list_bases` to see available bases.

### Managed Mode (Multi-Tenant HTTP)

For hosting an MCP endpoint where each client authenticates with their own SeaTable API token:

```bash
SEATABLE_MODE=managed \
SEATABLE_SERVER_URL=https://your-seatable-server.com \
SEATABLE_TOKEN_SECRET=$(openssl rand -hex 32) \
PORT=3000 npx -y @seatable/mcp-seatable --sse
```

`SEATABLE_TOKEN_SECRET` is **required** in managed mode. It seals the OAuth tokens the server issues, so the underlying SeaTable API token never has to be handed to a client. Keep it stable across restarts — changing it invalidates every issued access and refresh token and forces all clients to re-authorize.

Clients pass their credential via `Authorization: Bearer <token>` — on session initialization **and on every subsequent request**, including `GET` and `DELETE`. The `mcp-session-id` header is a routing value only; it is never accepted on its own. Each request is re-validated and must resolve to the same identity that created the session, otherwise the server answers `401` (missing/invalid credential) or `403` (valid credential, wrong session). Rate limits apply as before (60 req/min per token, 120/min per IP, 20 concurrent connections per token).

**OAuth support:** Managed mode also exposes OAuth 2.0 endpoints (`/authorize` and `/token`), enabling OAuth-compatible clients like ChatGPT to connect — no external OAuth provider required. During the flow the user enters their SeaTable API token; the server seals it into its own short-lived access token (1 h) and a rotating refresh token (14 d). The raw SeaTable API token is never returned to a client.

Clients must register at `/register` first: the returned `client_id` carries the client's name and its `redirect_uris`, and the server accepts a callback only if it is one the client registered (loopback callbacks may vary the port, per RFC 8252). PKCE with `S256` is mandatory, and every authorization code is bound to the client, the exact callback and the challenge.

**Where a code may be delivered.** With open dynamic registration, "registered client" is not a trust statement — anyone can register. What matters is whether the code leaves the user's machine:

| Callback | Behaviour |
|---|---|
| Loopback (`http://127.0.0.1:…`, `localhost`, `[::1]`) | allowed, no extra step — the code stays on the user's machine |
| Private-use scheme (`cursor://`, `vscode://`, `com.example.app:/…`) | allowed, no extra step — handed to a local application |
| `https` on a host in `SEATABLE_OAUTH_TRUSTED_REDIRECT_HOSTS` | allowed, no extra step |
| `https` on any other host | allowed **after** the user confirms the destination on a separate page |
| Remote plaintext `http`, `javascript:`, `data:`, `file:`, `blob:` | rejected |

The confirmation cannot be skipped from the entry link: it is read from the form body only, and a POST auto-submitted by a foreign page is refused via `Sec-Fetch-Site`. The trusted-host list therefore removes friction — it is not a gate, and leaving it unset breaks nothing.

The consent screen leads with the destination the authorization will be sent to. The application's name is shown as **self-reported**, because with open registration it is chosen by whoever registered the client and cannot be verified.

The OAuth endpoints are rate limited per IP (30/min overall, 10/min for token submissions), so `/authorize` cannot be used as an unthrottled oracle for testing SeaTable API tokens.

OAuth endpoints follow the MCP specification (RFC 8414 metadata discovery, PKCE, dynamic client registration):

| Endpoint | Path |
|---|---|
| Metadata Discovery | `/.well-known/oauth-authorization-server` |
| Authorization | `/authorize` |
| Token | `/token` |
| Client Registration | `/register` |

Client ID and secret are not validated — dynamic client registration generates one automatically.

### Docker

```bash
docker run -d --name seatable-mcp \
  -p 3000:3000 \
  -e SEATABLE_SERVER_URL=https://your-seatable-server.com \
  -e SEATABLE_API_TOKEN=your-api-token \
  seatable/seatable-mcp:latest

# Health check
curl http://localhost:3000/health
```

### Security Model

The security characteristics differ significantly between transport modes:

| | stdio (default) | Selfhosted HTTP | Managed HTTP |
|---|---|---|---|
| **Network exposure** | None (local process) | TCP port, **no auth** | TCP port, Bearer auth |
| **Authentication** | Not needed (local) | None | Bearer token or OAuth 2.0, validated against SeaTable |
| **Rate limiting** | None | None | Per-token, per-IP, global |
| **Connection limits** | N/A | None | 20 concurrent sessions per token |
| **Data scope** | All configured bases | All configured bases | One base per client token |

> **⚠️ Warning:** Selfhosted HTTP mode (`--sse` / `--http`) has **no authentication**. Anyone who can reach the port gets full access to all configured bases, including write and delete operations. Only run it in trusted networks (localhost, Docker-internal) or behind a reverse proxy that handles authentication. For untrusted networks, use **managed mode** instead.

### Rate Limiting

SeaTable's own API gateway enforces rate limits **per base** (default: 500 requests/minute per `base_uuid`) and **per organization** (monthly quota). These limits apply regardless of whether requests come from the MCP server, the web UI, or direct API calls. The MCP server does not duplicate these limits — instead, it retries automatically with exponential backoff when SeaTable returns `429 Too Many Requests`.

In **managed mode**, the MCP server adds its own rate limits to protect the server process itself (not the SeaTable backend): 60 req/min per token, 120/min per IP, 30/min for new session creation, and 20 concurrent connections per token.

### Input Validation

All tool inputs are validated with Zod schemas before execution. Write tools (`add_row`, `append_rows`, `update_rows`, `upsert_rows`) additionally validate row data against the table schema — unknown columns are rejected, and read-only columns (formula, auto-number, creator, etc.) are stripped with a note in the response.

Tool schemas are published with `additionalProperties: true` to remain compatible with MCP clients that may attach internal fields (e.g. `_meta`). Unexpected fields are ignored by the server — they do not cause errors but are not processed either. This is a deliberate trade-off: stricter validation would improve error messages for typos but risk breaking compatibility with MCP clients.

### Row Responses

Row responses include all columns and SeaTable system fields (`_id`, `_mtime`, `_ctime`, `_creator`, `_last_modifier`). System fields are not filtered — `_id` is required for updates and deletes, timestamps are useful for sorting and freshness checks, and creator/modifier fields can be resolved to display names via `list_collaborators`.

### Caching

The server caches base metadata (table/column definitions) for 60 seconds to avoid redundant API calls during write operations. Schema-reading tools (`get_schema`, `list_tables`) always bypass the cache and return fresh data. If a cached schema becomes stale (e.g. a column was renamed), the SeaTable API will reject the write and the AI agent can call `get_schema` to refresh.

## Environment Variables

Required:

- `SEATABLE_SERVER_URL` — Your SeaTable server URL

Authentication (one of these is required in selfhosted mode):

- `SEATABLE_API_TOKEN` — Single-base API token
- `SEATABLE_BASES` — Multi-base: JSON array (e.g. `'[{"base_name":"CRM","api_token":"..."}]'`)

Optional:

- `SEATABLE_MODE` — `selfhosted` (default) or `managed` (multi-tenant HTTP with per-client auth)
- `SEATABLE_TOKEN_SECRET` — **required in managed mode**, min. 32 chars. Seals issued OAuth tokens and client registrations; must be stable across restarts (`openssl rand -hex 32`)
- `SEATABLE_ACCESS_TOKEN_TTL` — lifetime of an issued access token in seconds (default `3600`, range `30`–`2592000`). Lower narrows the window after a SeaTable token is revoked; higher spares users a re-prompt if their client renews badly. The refresh token is never issued shorter-lived than the access token.
- `SEATABLE_MOCK=true` — Enable mock mode for offline testing
- `CORS_ALLOWED_ORIGINS` — Comma-separated list of allowed origins for CORS (HTTP mode only, disabled if unset)
- `METRICS_PORT` — Prometheus metrics port (default: `9090`, HTTP mode only)

## Monitoring

In HTTP mode, the server exposes Prometheus metrics on a separate port (default `9090`):

```bash
curl http://localhost:9090/metrics
```

Available metrics:

| Metric | Type | Description |
|---|---|---|
| `mcp_tool_calls_total{tool, status}` | Counter | Tool calls by name and result (success/error) |
| `mcp_tool_calls_by_tool_total{tool}` | Counter | Total calls per tool (regardless of outcome) |
| `mcp_tool_duration_seconds{tool}` | Histogram | Tool execution time |
| `mcp_http_requests_total{method, status}` | Counter | HTTP requests by method and status code |
| `mcp_rate_limit_exceeded_total{type}` | Counter | Rate limit rejections (global/per_ip/per_token) |
| `mcp_auth_validations_total{result}` | Counter | Auth validations (success/failure/cache_hit) |
| `mcp_active_sessions` | Gauge | Currently active HTTP sessions |
| `mcp_active_connections` | Gauge | Currently active connections |
| `seatable_api_requests_total{operation, status}` | Counter | SeaTable API calls by operation |
| `seatable_api_duration_seconds{operation}` | Histogram | SeaTable API latency |

Plus standard Node.js metrics (memory, CPU, event loop) via `prom-client`.

The metrics server only starts in HTTP mode (not stdio) and binds to `0.0.0.0` — in Docker, expose the port only within your internal network.

## MCP Tools

### Schema Introspection

- **`list_tables`** — Get all tables with metadata
- **`get_schema`** — Get complete database structure
- **`list_bases`** — List available bases (multi-base mode only)
- **`list_collaborators`** — List users with access to the base (for collaborator columns)

### Reading Data

- **`list_rows`** — Paginated row listing (use query_sql for filtering/sorting)
- **`get_row`** — Retrieve specific row by ID
- **`find_rows`** — Client-side filtering with DSL
- **`search_rows`** — Search via SQL WHERE clauses
- **`query_sql`** — Execute SQL queries with parameterized inputs

### Writing Data

- **`add_row`** — Add single new row
- **`append_rows`** — Batch insert rows
- **`update_rows`** — Batch update rows
- **`upsert_rows`** — Insert or update rows by key columns
- **`delete_rows`** — Remove rows by ID

### Files

- **`upload_file`** — Upload a file or image to a row (base64-encoded)
- **`download_file`** — Read file content from a file or image column (text files and PDFs as text, binary files as download link, max 1 MB)

### Linking

- **`link_rows`** — Create relationships between rows
- **`unlink_rows`** — Remove relationships between rows

### Utilities

- **`get_row_activities`** — Get change history of a row (who changed what, when, old/new values)
- **`create_snapshot`** — Create a snapshot of the current base (10 min cooldown)
- **`add_select_options`** — Add new options to single-select or multi-select columns (existing options are skipped, no duplicates)
- **`ping_seatable`** — Health check with latency monitoring

## Supported Column Types

SeaTable bases can contain many different column types. The following table shows which types can be written via the API and what format to use.

| Column Type | Writable | Value Format |
|---|---|---|
| Text | Yes | `"string"` |
| Long Text | Yes | `"Markdown string"` |
| Number (incl. percent, currency) | Yes | `123.45` |
| Checkbox | Yes | `true` / `false` |
| Date | Yes | `"YYYY-MM-DD"` or `"YYYY-MM-DD HH:mm"` |
| Duration | Yes | `"h:mm"` or `"h:mm:ss"` |
| Single Select | Yes | `"option name"` |
| Multiple Select | Yes | `["option a", "option b"]` |
| Email | Yes | `"user@example.com"` |
| URL | Yes | `"https://..."` |
| Rating | Yes | `4` (integer) |
| Geolocation | Yes | `{"lat": 52.52, "lng": 13.40}` |
| Collaborator | Yes | `["0b995819003140ed8e9efe05e817b000@auth.local"]` — use `list_collaborators` to get user IDs |
| Link | Yes | Use `link_rows` / `unlink_rows` tools |
| Image / File | Yes | Use `upload_file` to upload (base64), `download_file` to read content |
| Formula / Link Formula | No | Read-only, computed by SeaTable |
| Creator / Created Time / Modified Time | No | Read-only, set automatically |
| Auto Number | No | Read-only, set automatically |
| Button / Digital Signature | No | Not accessible via API |

## Tool Examples

```json
// List all tables
{ "tool": "list_tables", "args": {} }

// Get rows with pagination
{ "tool": "list_rows", "args": { "table": "Tasks", "page_size": 10 } }

// Add rows
{ "tool": "append_rows", "args": { "table": "Tasks", "rows": [{ "Title": "New Task", "Status": "Todo" }] } }

// SQL query
{ "tool": "query_sql", "args": { "sql": "SELECT Status, COUNT(*) as count FROM Tasks GROUP BY Status" } }
```

## Programmatic Usage

```typescript
import { createMcpServer } from '@seatable/mcp-seatable'

const server = await createMcpServer({
  serverUrl: 'https://your-seatable-server.com',
  apiToken: 'your-api-token',
})
```

## Mock Mode

```bash
SEATABLE_MOCK=true npm run dev
```

In-memory tables and rows for demos and tests without a live SeaTable instance.

## Development

### Prerequisites

- Node.js >= 20

### Setup

```bash
git clone https://github.com/seatable/seatable-mcp
cd seatable-mcp
npm install
cp .env.example .env   # Configure your SeaTable settings
npm run dev             # Start in watch mode
```

### Scripts

- `npm run dev` — Start server in watch mode (tsx)
- `npm run build` — Compile TypeScript
- `npm run start` — Run compiled server
- `npm test` — Run tests (vitest)
- `npm run lint` — Lint code
- `npm run typecheck` — TypeScript type check

### Testing Tools

```bash
node scripts/mcp-call.cjs ping_seatable '{}'
node scripts/mcp-call.cjs list_tables '{}'
node scripts/mcp-call.cjs list_rows '{"table": "Tasks", "page_size": 5}'
```

## Troubleshooting

| Issue | Solution |
|---|---|
| `Invalid API token` | Check `SEATABLE_API_TOKEN` |
| `Base not found` | Check API token permissions |
| `Connection timeout` | Check `SEATABLE_SERVER_URL` and network access |
| `Permission denied` | Ensure API token has required base permissions |
| `You don't have permission to perform this operation on this base.` | API token is read-only or row limit exceeded |
| `Asset quota exceeded.` | Storage quota reached — delete files or upgrade plan |
| `too many requests` | Rate-limited by SeaTable — requests are automatically retried with backoff (3 attempts) |

## License

MIT

