Databricks MCP
README.md
# Databricks MCP
Read-only [Model Context Protocol](https://modelcontextprotocol.io/) server for
Databricks. It exposes catalog metadata and bounded SQL tools over Streamable
HTTP while preserving the caller's identity through an OAuth-protected resource
server.
Authorization is optional in MCP, but this service requires it in production
because it exposes private workspace data. The authorization server may be
self-hosted or provided by a third party. See
[`docs/authorization.md`](docs/authorization.md).
## Tools
| Area | Tools |
| --- | --- |
| Catalog | `list_catalogs`, `list_schemas`, `list_tables`, `describe_table`, `search_tables` |
| SQL | `run_readonly_sql`, `sample_table` |
| Warehouses | `list_warehouses` |
SQL is limited to one `SELECT`, `WITH`, `SHOW`, `DESCRIBE`, or `EXPLAIN`
statement. Results are bounded, table sampling requires a three-part Unity
Catalog identifier, and no write, job, cluster, DBFS, or secret tools are
exposed.
## Requirements
- Python 3.12+
- A Databricks workspace
- An MCP-compatible OAuth authorization server
- Network access to fetch the pinned `mcp-auth-client` dependency from GitHub
## Configuration
Copy [`deploy/service.env.example`](deploy/service.env.example) and set the
deployment-specific values:
| Variable | Purpose |
| --- | --- |
| `DATABRICKS_HOST` | HTTPS Databricks workspace URL |
| `PUBLIC_BASE_URL` / `MCP_SERVER_URL` | Public service base URL; `/mcp` is appended for the MCP resource |
| `MCP_AUTH_ISSUER` | Authorization-server issuer |
| `MCP_AUTH_JWKS_URI` | JWKS endpoint for access-token validation |
| `MCP_AUTH_TOKEN_ENDPOINT` | Optional downstream token-exchange endpoint |
| `MCP_AUTH_CLIENT_PRIVATE_KEY_FILE` | Resource-server exchange key |
| `MCP_AUTH_CLIENT_KEY_ID_FILE` | Exchange-key identifier |
| `DATABRICKS_REQUIRED_GROUP` | Required Databricks group |
| `DATABRICKS_ALLOWED_WAREHOUSE_IDS` | Explicit warehouse allowlist |
| `MCP_ALLOWED_HOSTS` | Allowed `Host` header values, comma-separated. Must match the incoming `Host` header exactly, including the port whenever the service is reached on a non-standard one (`mcp.example.com:6328` for a direct port, vs. the bare `mcp.example.com` a standard-port HTTPS reverse proxy sends). The default only covers `MCP_PORT`; remapping the published port without updating this is the classic way to turn a healthy deployment into a 421. |
| `MCP_SERVER_WEBSITE` | Required. Shown to users in client UI when authorizing. Startup fails on an `example.com` placeholder, since it would point users at a domain you don't control. |
| `MCP_SERVER_DISPLAY_NAME` | Server name shown to clients (default `Databricks MCP`) |
| `MCP_PATH` | Where the MCP endpoint is mounted below `PUBLIC_BASE_URL` (default `/mcp`). This is also the token audience and the path the protected-resource metadata is published under, so changing it moves all three together; the authorization server must issue tokens for the same `PUBLIC_BASE_URL` + `MCP_PATH`. |
| `MCP_HOST` / `MCP_PORT` | Listen address (defaults `127.0.0.1` / `6328`; the container image sets `MCP_HOST=0.0.0.0`) |
| `MCP_LOG_LEVEL` | Level for this service's own loggers and uvicorn's (default `info`; `debug` shows per-request auth decisions) |
| `MCP_MAX_ROWS` / `MCP_MAX_SAMPLE_ROWS` / `MCP_SQL_WAIT_TIMEOUT` | Query bounds (defaults `200` / `100` / `10s`). `MCP_MAX_SAMPLE_ROWS` is a ceiling on the caller's requested sample size, never a floor. |
`list_warehouses` only ever returns warehouses already on that allowlist, so
run [`scripts/list_all_warehouses.py`](scripts/list_all_warehouses.py) once,
directly against the workspace, to find the IDs to put in it — see
[Bootstrapping DATABRICKS_ALLOWED_WAREHOUSE_IDS](docs/authorization.md#bootstrapping-databricks_allowed_warehouse_ids).
Keep real credentials, tokens, keys, and deployment env files out of Git.
## Run locally
```bash
uv run uvicorn mcp_databricks.server:app --host 127.0.0.1 --port 6328
```
Or:
```bash
python -m mcp_databricks
```
The MCP endpoint is `/mcp` by default. The canonical ASGI entrypoint is
`mcp_databricks.server:app`.
## Development checks
```bash
ruff check .
ruff format --check .
python -m compileall -q mcp_databricks tests
uv run pytest
```
The first three checks run in public CI without service credentials. `uv sync`
fetches the pinned authorization-client SDK before running the full test suite.
## Release
Push a tag such as `v0.2.3`; the release workflow reruns validation, builds
the Python distributions, and publishes a GitHub Release with the artifacts.
## Deployment
- **Docker:** build `deploy/Dockerfile`, inject a secret-managed env file, and
mount the resource-server key read-only.
- **Kubernetes:** run the container as a non-root Deployment, store env values
and keys in Secrets, and expose it through an HTTPS Ingress.
- [`docs/authorization.md`](docs/authorization.md): MCP OAuth flow and provider integration
- [`deploy/README.md`](deploy/README.md): Docker and reverse-proxy checklist
- [`deploy/service.env.example`](deploy/service.env.example): neutral configuration template
The authorization server owns login, consent, client registration, token
issuance, and signing keys. This repository owns the Databricks MCP tools,
resource-server policy, and downstream workspace integration.
This server cannot be deployed
Maintenance
ActivityNo data
ResponsivenessNo issues