SQL Anywhere MCP Server
by TeoDreams
README.md
# SQL Anywhere MCP Server (read-only)


An MCP server that lets **Claude** read a **SAP SQL Anywhere** database and answer
questions in plain language — no SQL by hand.
Example: ask *"how many customers do we have in Lombardy?"* and Claude writes and
runs the query for you.
> **Read-only by design.** It can only run `SELECT` queries — it cannot insert,
> update, delete, or alter anything. See [Security](#security).
## Requirements
- Python 3.10+
- The SQL Anywhere ODBC driver and a configured **DSN** for your database
- A dedicated, **read-only database user** (this project never creates users or changes permissions)
- Claude Code or Claude Desktop
## Install
```bash
git clone https://github.com/TeoDreams/sqlanywhere-mcp-server.git
cd sqlanywhere-mcp-server
pip install -r requirements.txt
```
## Configure
Set four values via a `.env` file or environment variables.
**`.env` file** (copy the template, then edit — it is gitignored):
```bash
cp .env.example .env
```
```ini
SQLANY_DSN=YOUR_DSN
SQLANY_UID=readonly_user
SQLANY_PWD=your_password
SQLANY_MAX_ROWS=200
SQLANY_QUERY_TIMEOUT=30
```
**Or environment variables:**
```powershell
# Windows (PowerShell)
[Environment]::SetEnvironmentVariable("SQLANY_DSN", "YOUR_DSN", "User")
[Environment]::SetEnvironmentVariable("SQLANY_UID", "readonly_user", "User")
[Environment]::SetEnvironmentVariable("SQLANY_PWD", "your_password", "User")
```
```bash
# Linux / macOS
export SQLANY_DSN=YOUR_DSN
export SQLANY_UID=readonly_user
export SQLANY_PWD=your_password
```
## Add to Claude
**Claude Code** — pick a scope:
```bash
# Global: available in all your projects
claude mcp add --scope user sqlanywhere-readonly -- python /path/to/sqlanywhere_mcp_server.py
# Project: only this repo, shared with the team via a committed .mcp.json
claude mcp add --scope project sqlanywhere-readonly -- python /path/to/sqlanywhere_mcp_server.py
claude mcp list
```
The default scope (`local`, if you omit `--scope`) registers the server only for you in the
current project. Credentials stay out of a committed `.mcp.json` because they come from your
environment variables / `.env`, not the command.
**Claude Desktop** — add this to the config file (`%APPDATA%\Claude\claude_desktop_config.json`
on Windows, `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS,
`~/.config/Claude/claude_desktop_config.json` on Linux), then restart the app:
```json
{
"mcpServers": {
"sqlanywhere-readonly": {
"command": "python",
"args": ["/path/to/sqlanywhere_mcp_server.py"]
}
}
}
```
## Usage
Ask Claude things like:
- *"Describe the structure of the customers table"*
- *"Show me the first rows of the products table"*
- *"How many orders were placed this month?"*
- *"Will this query use an index, or scan the whole table?"*
Claude uses the two tools on its own:
| Tool | What it does |
|---|---|
| `list_tables` | Lists table names (handy for large schemas) |
| `describe_schema` | Lists tables and columns (structure only, no data) |
| `run_query` | Runs a `SELECT` and returns the rows (up to the row cap) |
| `explain_query` | Shows the execution plan of a `SELECT` **without running it** |
## Security
- Only queries starting with `SELECT` run; write/DDL keywords (`INSERT`, `UPDATE`,
`DELETE`, `DROP`, `ALTER`, etc.) are rejected before connecting.
- Returned rows are capped (default 200). This caps the **response**, not the query — the
`SELECT` still runs in full on the server. A per-query timeout (`SQLANY_QUERY_TIMEOUT`,
default 30s, `0` = no limit) aborts queries that run too long.
- Credentials come only from environment variables or `.env`, and are never printed in
error messages.
## Query plans
`explain_query` uses SQL Anywhere's `EXPLANATION()` function, which returns the optimizer's
access plan **without executing the query**. (`GRAPHICAL_PLAN()` is deliberately not used —
it can execute the statement to collect statistics.)
SQL Anywhere gates plan access behind a database option that is **off by default**. If
`explain_query` reports that plans are unavailable, a DBA must enable it for your read-only user:
```sql
SET OPTION readonly_user.QUERY_PLAN_TEXT_ACCESS = ON;
```
## Troubleshooting
> If `describe_schema` returns nothing, your SQL Anywhere version may expose the schema
> through `SYS.SYSTAB` / `SYS.SYSTABCOL` instead of the `SYS.SYSTABLE` / `SYS.SYSCOLUMN`
> views used here — adjust the query in `describe_schema` accordingly.
## License
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues