Skip to main content
Glama
Fattan-malva

mcp-sqlserv

by Fattan-malva

mcp-sqlserv

MCP Server for read-only access to SQL Server databases — anti SQL-injection by construction, managed via the Web Admin UI.

License: MIT Node TypeScript Docker MCP Tests

Zero raw SQL · Default deny · Bind parameter 100% · Full audit


About

mcp-sqlserv lets AI agents (Claude, Cursor, Claude Code, any MCP client) read SQL Server databases safely and in a controlled way:

  • All queries are built structurally by the server — the AI never writes raw SQL.

  • Identifiers (tables/columns) are validated against real database metadata (sys.tables, sys.columns).

  • Values are always bound parametersSQL injection is impossible by construction.

  • Per-table permissions are default deny: without explicit permission, a table cannot be touched.

  • Every request is recorded in the audit log — key, tool, filter, row count, and duration.

Related MCP server: SQL Server MCP

Features

Feature

Description

MCP Streamable HTTP

/mcp endpoint, compatible with all MCP clients via HTTP

Multi-project

Per-project URL /mcp/<projectId>, separate storage & permissions

API Key

Create / revoke key per AI consumer

OAuth 2.1

Authorization Code + PKCE, DCR (RFC 7591), refresh rotation, revoke

SQL Server Connection

Host/port/user/pass (encrypted AES-256-GCM), optional TLS

Granular permissions

Per table: read data and/or view metadata. Default = DENY

Audit log

All AI requests are logged: key, tool, table, filter, rows, duration, status

Rate limit

60 requests/min per API key (configurable)

Fully read-only

Tools only produce SELECT; no write path exists anywhere

Agent Test

Chat with the Gemini model directly from the Web UI for end-to-end testing

Architecture

┌──────────────┐   HTTPS    ┌─────────────┐          ┌──────────────────────────────┐
│  AI Agent    ├───────────►│    nginx    ├─────────►│  mcp-sqlserv (Docker)        │
│  (MCP client)│  Bearer    │  reverse    │ app-net  │  Express + MCP + OAuth       │
└──────────────┘  token     │  proxy+SSL  │  work    │      │            │          │
                            └─────────────┘          │      ▼            ▼          │
┌──────────────┐   HTTPS                              │  SQLite         mssql pool   │
│ Web Admin UI ├─────────────────────────────────────►│  (data/, keys,   │           │
│  (browser)   │            REST /api/*               │   audit, izin)   ▼           │
└──────────────┘                                      │              ┌──────────┐    │
                                                      │              │ SQL Srvr │    │
                                                      └──────────────┴──────────┴────┘

Quick To Start

# 1. Clone & siapkan environment
git clone https://github.com/<username>/mcp-sqlserv.git
cd mcp-sqlserv
cp .env.example .env            # isi ADMIN_USER / ADMIN_PASSWORD (min 8 karakter)

# 2. Build & jalankan
docker compose up -d --build

# 3. Verifikasi
curl http://localhost:4000/healthz

Server runs on http://localhost:4000 — Web admin UI at /, MCP endpoint at /mcp.

Environment Variables

Variable

Default

Description

PORT

4000

Server port

DATA_DIR

./data

SQLite data folder (mounted as /mnt/data volume in Compose)

ADMIN_USER

admin

Web admin UI user

ADMIN_PASSWORD

required

Password for admin UI (minimum 8 characters юq)

SESSION_SECRET

automatic

JWT/encryption secret (auto-generated & persisted if empty)

QUERY_TIMEOUT_MS

30000

SQL query timeout

RATE_LIMIT_PER_MIN

60

Rate limit limiting per API key

OAUTH_ENABLED

1

Disable OAuth by setting 0

OAUTH_CODE_TTL_S

600

Authorization code lifetime (seconds)

OAUTH_ACCESS_TTL_S

3600

Access token lifetime (seconds)

OAUTH_REFRESH_TTL_S

2592000

Refresh token lifetime (seconds; 30 days)

Usage Flow

  1. Log in to the Web UI → DB Connection menu → fill in host/port/user/pass/database, safest option — Test Connection.

    For Docker cases, SQL Server on the host can be reached via host.docker.internal.

  2. API Keys menu → create a key (view once, store it!).

  3. Table Permissions menu → check the maritime identifier table allowed for AISave Permissions. Default deny.

  4. Connect the Agent to URL https://<domain>/mcp with a header Authorization: Bearer <api-key>.

Connecting an Arbitrary MCP Client

{
  "mcpServers": {
    "sql-server": {
      "url": "https://<domain>/mcp",
      "headers": { "Authorization": "Bearer sk-xxxx" }
    }
  }
}

Quick test with curl:

curl -X POST https://<domain>/mcp \
  -H "Authorization: Bearer sk-xxxx" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

Claude Custom Connector (claude.ai / Desktop)

  1. Open Customize → Connectors → Add custom connector.

  2. Set Remote MCP server URL: https://<domain>/mcp.

  3. In Advanced settings, fill in an OAuth Client ID + Secret from the OAuth Clients menu (redirect URI: https://claude.ai/api/mcp/auth_callback).

    Could be left blank — Claude auto registers via Dynamic Client Registration (RFC 7591).

  4. Click Add → Connect — browser opens the administrator login page → Allow access.

  5. Claude stores a refresh token and sends MCP requests with a bearer token.

Claude Code (CLI):

claude mcp add mcp-sqlserv https://<domain>/mcp --transport http \
  ... # bila client pre-registered: --client-id <id> --client-secret --callback-port

Endpoints OAuth

Endpoint

Standard

GET /.well-known/oauth-protected-resource

RFC 9728

GET /.well-known/oauth-authorization-server

RFC 8414

POST /oauth/register

RFC 7591 (DCR, public + confidential)

GET /oauth/authorize (operator login + consent)

RFC 6749 + PKCE S256

POST /oauth/token (code exchange + refresh rotation)

RFC 6749 / 7636

POST /oauth/revoke

RFC 7009

OAuth identity = operator session. Access token maps mapped to an internal API key oauth:<client_id>` — all table permissions, rate limit, and audit logging also apply to Claude connection. Revoking the client invalidates this client’s tokens instantly.

MCP Tools

Tool

Description

list_tables

Returns the permitted tables with approximate row counts

get_table_schema

Columns: type, nullable, identity, primary key, indexes

read_records

Read lines, applied structured filter, ORDER BY, pagination

count_records

Count rows with optional filter

get_record_by_pk

Fetch one row via primary key

server_info

Server / database info

Table names must be specified without schema prefix (users, not dbo.users). Columns are validated against sys.columns; all values are 100% bound parameters.

Supported structured filters: eq, neq, lt, lte, gt, gte, like, startsWith, endsWith, in, between, isNull, isNotNull.

Security

  • No raw SQL — only the structured query builder

  • Identifier allowlist — regex + check against real DB metadata

  • Default deny — without grant, a table cannot be touched

  • Hard limits — max 1000 rows/query, 20 filters, 50 INS · time out 30s

  • API key + rate limiter per key + audit log of all requests

  • Read-only (recommend through SQL Server-only GRANT SELECT)

  • DB password stored encrypted AES-256-GCM in SQLite

Deployment

Deploy with Docker to the app-network, behind nginx, as a reverse proxy (wildcard SSL cert, SSE-buffering disabled, CORS for web‑based MCP clients).

Migrating Between VPS

Code and Docker will run automatically, but the following — not track in Git (they’re in .gitignore) — have to be carried manually:

Item

Contents

How to move

.env

Admin credentials & secrets

Copy from old server, or regenerate from .env.example

data/

SQLite storage (API keys, grants, audit log, DB connection)

rsync / copy old folder

# Di VPS baru
git clone https://github.com/<username>/mcp-sqlserv.git && cd mcp-sqlserv

# Migrasi state dari VPS lama (opsional)
rsync -av vps-lama:/path/mcp-sqlserv/.env .env
rsync -av vps-lama:/path/mcp-sqlserv/data ./data

# Network eksternal harus ada dulu (dipakai docker-compose.yaml)
docker network create app-network   # abaikan jika sudah ada

docker compose up -d --build

Leaving data/ behind means the server still runs — you only have to recreate the DB connection, API keys, grant list from admin UI.

Project Structure

mcp-sqlserv/
├── src/
│   ├── index.ts            # Bootstrap Express + routing
│   ├── config.ts           # Env config
│   ├── db/storage.ts       # SQLite: api_keys, db_config, permissions, audit_log
│   ├── sqlserver/          # Connection pool, metadata (sys.tables), query builder
│   ├── mcp/                # MCP server (per-session) + tools
│   ├── oauth/              # OAuth 2.1: router, PKCE, discovery
│   ├── api/                # REST admin (auth, config, keys, permissions, audit)
│   └── ui/                 # SPA vanilla JS (public/)
├── public/                 # Web UI admin (tanpa build step)
├── test/                   # Test suite keamanan + OAuth + smoke
├── Dockerfile              # Multi-stage build (node:20-alpine)
├── docker-compose.yaml     # Attach ke app-network, host.docker.internal
└── LICENSE                 # MIT

Admin REST API

Method

Path

Description

POST

/api/auth/login

Admin login (httpOnly cookie)

GET

/api/status

DB status, key, permissions

GET/PUT

/api/config : full-photo

Read / save DB connection config

POST

/api/config/test

Test connection

GET/POST/

/api/keys

List / create API key

PUT/DELETE

/api/keys/:id

Rename / revoke

GET/PUT

/api/activity

List / save table-security rules

GET

/api/audit

Audit log

GET

/api/info

MCP URL info + sample configuration

GET

/healthz

No auth available

Testing

npm run test:smoke      # smoke test dasar
npm run test:security   # 29 test: injection, permission, limit, pagination, auth
npm run test:oauth      # 46 test: discovery, DCR, PKCE, consent, token, refresh, revoke

test/oauth.mjs starts its own server (port: 4100, data-dir: oauth-test-data/) — no further config required.

Contribution

Contributions are welcome ! Open an issue or a pull request. For larger changes, please discuss the idea first via Issue, to stay aligned with the product principle: Security is the product. Every surface (MCP, UI, Agent Test) must meet the same standards:

License

This project is licensed under the [MIT License

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables AI agents to securely connect to and query Microsoft SQL Server databases with read-only access, schema discovery, and relationship mapping. Features advanced security protections, health monitoring, and bulk operations for production environments.
    9
    64 npm
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    An MCP server that connects AI assistants to Microsoft SQL Server databases, enabling schema exploration and read-only queries safely.
    49
    5 npm
    4
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides read-only Microsoft SQL Server database access to AI agents with row-level security, enabling SELECT queries, table metadata, and schema introspection through MCP.
    10 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    A read-only MCP server for Microsoft SQL Server that enables AI agents to safely explore and query SQL Server databases.
    623 npm
    MIT