Metadata-Version: 2.4
Name: mcp-tools-sql
Version: 0.2.0
Summary: An MCP server for safe, configurable SQL database access
Author: Marcus Jellinghaus
License-Expression: MIT
Project-URL: Homepage, https://github.com/MarcusJellinghaus/mcp-tools-sql
Project-URL: Bug Tracker, https://github.com/MarcusJellinghaus/mcp-tools-sql/issues
Project-URL: Documentation, https://github.com/MarcusJellinghaus/mcp-tools-sql#readme
Keywords: mcp,sql,database,llm,claude,ai,sql-server,postgresql,sqlite
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Operating System :: OS Independent
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp<2.0.0,>=1.3.0
Requires-Dist: mcp[cli]<2.0.0,>=1.3.0
Requires-Dist: mcp-coder-utils>=0.1.6
Requires-Dist: tabulate>=0.9.0
Requires-Dist: pydantic>=2.0
Requires-Dist: tomlkit>=0.13.0
Requires-Dist: sqlglot>=30
Provides-Extra: mssql
Requires-Dist: pyodbc>=5.0.0; extra == "mssql"
Provides-Extra: postgresql
Requires-Dist: psycopg[binary]>=3.1.0; extra == "postgresql"
Provides-Extra: all-backends
Requires-Dist: pyodbc>=5.0.0; extra == "all-backends"
Requires-Dist: psycopg[binary]>=3.1.0; extra == "all-backends"
Provides-Extra: typecheck
Requires-Dist: mypy>=1.13.0; extra == "typecheck"
Requires-Dist: pytest; extra == "typecheck"
Requires-Dist: types-tabulate>=0.9.0; extra == "typecheck"
Provides-Extra: dev
Requires-Dist: mcp-tools-sql[typecheck]; extra == "dev"
Requires-Dist: mcp-workspace; extra == "dev"
Requires-Dist: mcp-coder; extra == "dev"
Requires-Dist: mcp-tools-py; extra == "dev"
Requires-Dist: ruff~=0.17.2; extra == "dev"
Requires-Dist: tach>=0.35.0; extra == "dev"
Requires-Dist: deptry>=0.25.1; extra == "dev"
Requires-Dist: import-linter>=2.0; extra == "dev"
Requires-Dist: vulture>=2.14; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: pytest-xdist; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: diff-cover; extra == "dev"
Dynamic: license-file

# mcp-tools-sql

[![CI](https://github.com/MarcusJellinghaus/mcp-tools-sql/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/MarcusJellinghaus/mcp-tools-sql/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/mcp-tools-sql)](https://pypi.org/project/mcp-tools-sql/) [![Python](https://img.shields.io/python/required-version-toml?tomlFilePath=https://raw.githubusercontent.com/MarcusJellinghaus/mcp-tools-sql/main/pyproject.toml)](https://pypi.org/project/mcp-tools-sql/) [![License](https://img.shields.io/github/license/MarcusJellinghaus/mcp-tools-sql)](LICENSE)

Every change to mcp-tools-sql passes these checks ([details](docs/quality-and-security-checks.md)):

[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![mypy](https://img.shields.io/badge/mypy-strict-blue)](https://mypy.readthedocs.io/) [![pylint](https://img.shields.io/badge/linting-pylint-yellowgreen)](https://github.com/pylint-dev/pylint) [![architecture](https://img.shields.io/badge/architecture-enforced-blue)](docs/quality-and-security-checks.md#architecture) [![coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/MarcusJellinghaus/9a7892066964f11765a07d74285ad104/raw/mcp-tools-sql-coverage.json)](https://github.com/MarcusJellinghaus/mcp-tools-sql/actions/workflows/ci.yml) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/MarcusJellinghaus/mcp-tools-sql/badge)](https://scorecard.dev/viewer/?uri=github.com/MarcusJellinghaus/mcp-tools-sql) [![CodeQL](https://github.com/MarcusJellinghaus/mcp-tools-sql/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/MarcusJellinghaus/mcp-tools-sql/actions/workflows/github-code-scanning/codeql)

An MCP server for safe, configurable SQL database access. Exposes schema introspection, user-defined SELECT queries, and structured UPDATE operations as MCP tools for LLM-assisted workflows.

## Key Ideas

- **Configurable, not ad-hoc**: Every query the LLM can run is defined upfront in config. The config is the security boundary.
- **Queries are read-only**: Each configured query's SQL is proved read-only before it is registered as a tool; a violating query is skipped with a warning. This is unconditional — `security.allow_updates` governs `updates.*` only.
- **Schema discovery**: Built-in tools to explore schemas, tables, columns, and foreign key relations.
- **Structured updates**: UPDATE operations are defined as table + key + fields, not raw SQL. The server generates the SQL.
- **Split config**: Query definitions live in the project repo (safe to commit). Credentials live in the user's home directory (never committed).
- **Multi-backend**: MS SQL Server (primary) and SQLite. PostgreSQL is planned:
  its config is accepted, but there is no backend implementation yet.

## Architecture

```
MCP Client (Claude Code, etc.)
    ↕ STDIO/MCP
mcp-tools-sql server
    ├── Built-in tools (schema introspection)
    ├── Configured query tools (from mcp-tools-sql.toml)
    ├── Configured update tools (from mcp-tools-sql.toml)
    └── Backend abstraction
         ├── SQLite (stdlib)
         ├── MS SQL Server (pyodbc)
         └── PostgreSQL (psycopg)
```

See [docs/architecture/architecture.md](docs/architecture/architecture.md) for details.

## Installation

```bash
pip install mcp-tools-sql              # core + SQLite
pip install mcp-tools-sql[mssql]       # + SQL Server support
```

## Quick Start

```bash
# Generate starter project query config (mcp-tools-sql.toml) and a
# database config skeleton at ~/.mcp-tools-sql/config.toml
mcp-tools-sql init --backend sqlite

# Edit ~/.mcp-tools-sql/config.toml and set the SQLite path, e.g.:
#     [connections.default]
#     backend = "sqlite"
#     path = "./mydb.db"

# Validate environment, configs, dependencies, and connectivity
mcp-tools-sql verify

# Try the tools without an MCP client
mcp-tools-sql tool
mcp-tools-sql tool read_tables --help

# Start MCP server
mcp-tools-sql --config mcp-tools-sql.toml
```

`mcp-tools-sql help config` prints a complete guide to both config files —
enough for an LLM driving the CLI to set the server up on its own.

## Documentation

| Doc | Covers |
|-----|--------|
| [docs/configuration.md](docs/configuration.md) | Both config files: discovery, every key, examples, multiple databases |
| [docs/mcp-client-setup.md](docs/mcp-client-setup.md) | Wiring the server into Claude Code, Claude Desktop and other MCP clients |
| [docs/cli.md](docs/cli.md) | Every command and flag, example output, exit codes |
| [docs/architecture/architecture.md](docs/architecture/architecture.md) | Internals |
| [docs/quality-and-security-checks.md](docs/quality-and-security-checks.md) | CI quality and security checks, required repo settings |

## Logging

The MCP server writes structured JSON logs to a new
`~/.mcp-tools-sql/logs/mcp_tools_sql_<timestamp>.log` file on every launch —
MCP clients usually discard a server's stderr, so the file is the only durable
record. User-facing messages, warnings and errors still appear on the console.

```bash
mcp-tools-sql --log-file /var/log/mcp-tools-sql.log   # explicit file
mcp-tools-sql --console-only --log-level DEBUG        # no file, verbose stderr
```

`init` and `verify` log to the console only. See [docs/cli.md](docs/cli.md#logging)
for thresholds and per-command defaults.

## Configuration

Two config files:

| File | Purpose | Location |
|------|---------|----------|
| `mcp-tools-sql.toml` | Query/update definitions | Project dir (committed) |
| `~/.mcp-tools-sql/config.toml` | Database connections + credentials | User home (never committed) |

The query config may instead live in `[tool.mcp-tools-sql]` of the project's
`pyproject.toml`. `--config` and `--database-config` override both paths.

One connection can route to several databases (catalogs), and several
connections can be configured side by side. See
[docs/configuration.md](docs/configuration.md) for every key.

> **Note:** `databases` is a routing/discovery list, **not** an authorization
> boundary — a connection still reaches any catalog its login is granted. Grant
> least privilege with per-connection database credentials at the server.

## License

MIT
