Skip to main content
Glama
dhipskind253

mssql-mcp

by dhipskind253
README.md
# mssql-mcp

A read-only Microsoft SQL Server MCP server using connection
details from **AWS Secrets Manager** at tool-call time — nothing
is read from disk or environment variables except the secret reference
itself.

This is a light-weight mcp meant for local development use. It avoids database credentials in your agent config, works nicely with a credential process and tools like aws-vault, and allows for readonly database access and schema discovery.

Cross-platform: it's a pure-JavaScript MCP server (no native or ODBC
dependencies), so it runs identically on macOS, Linux, and Windows via
`npx`.

## Configure in `.claude.json`

```json
{
  "mcpServers": {
    "my-db": {
      "command": "npx",
      "args": ["-y", "@dhipskind253/mssql-mcp"],
      "env": {
        "AWS_ACCOUNT_ID": "123456789012",
        "SECRET_NAME": "my-aws-secret",
        "AWS_REGION": "us-east-1",
        "TRUST_SERVER_CERTIFICATE": "false"
      }
    }
  }
}
```

| Env var                     | Required | Default | Description                                                                         |
| --------------------------- | -------- | ------- | ----------------------------------------------------------------------------------- |
| `AWS_ACCOUNT_ID`            | yes      | —       | The AWS account where the secret lives. Combined with name + region into a full ARN. |
| `SECRET_NAME`               | yes      | —       | The Secrets Manager secret name (no ARN suffix needed).                             |
| `AWS_REGION`                | yes      | —       | AWS region the secret is in (e.g. `us-east-1`). Also picked up by the AWS SDK as its default region. |
| `TRUST_SERVER_CERTIFICATE`  | no       | `false` | Skip TLS cert validation to the SQL Server. Accepts `true`/`false`/`1`/`0`/`yes`/`no`. Set `true` only if you understand why. |

Standard AWS SDK env vars (`AWS_PROFILE`, `AWS_ACCESS_KEY_ID`, etc.) are
honored via the default credential provider chain. Most users just need
`aws sso login` to be current.

## Required secret JSON

The secret value must be a JSON document with at least these fields:

```json
{
  "host": "myserver.database.windows.net",
  "port": 1433,
  "database": "mydb",
  "username": "ro_user",
  "password": "..."
}
```

`database` may also be supplied as **`dbname`** — the field name AWS uses
in its built-in RDS-credentials secret template. If both are present,
`database` wins.

Optional fields (with defaults shown):

| Field     | Default | Notes              |
| --------- | ------- | ------------------ |
| `port`    | `1433`  |                    |
| `encrypt` | `true`  | TLS to the server. |

> TLS cert trust is **not** read from the secret — set
> `TRUST_SERVER_CERTIFICATE` in the MCP server's `env` block instead.
> Any `trustServerCertificate` field in the secret JSON is ignored.

## Read-only by design

This server cannot insert, update, or delete data. Two layers enforce that:

1. The `run_select` tool lexically rejects anything that isn't a single
   `SELECT` or `WITH` (CTE) statement — including `INSERT`, `UPDATE`,
   `DELETE`, `EXEC`, `MERGE`, `DROP`, `ALTER`, `SELECT INTO`, etc.
2. No other tool emits write SQL. `get_procedure_definition` returns
   procedure source — it does not run procedures.

> **Courtesy note:** treat the lexical check as UX, not a security
> boundary. As a courtesy to your future self, configure the credentials
> you put in Secrets Manager to be a **read-only database login** — one
> with `SELECT` and `VIEW DEFINITION` only. That way an accidental write
> (or a future bug here) is rejected by SQL Server itself.

## Refreshing AWS credentials without restarting

Because the server uses the default AWS credential chain, an expired SSO
session can be recovered without restarting Claude or the MCP server:

1. Run `aws sso login` in any terminal.
2. Ask Claude to call the **`refresh_secret`** tool.
3. Continue working.

If a tool call fails because of AWS auth, the error message will tell
you exactly that and prompt the same flow. Errors are tagged with stable
prefixes:

| Prefix                    | Meaning                                            |
| ------------------------- | -------------------------------------------------- |
| `[AWS_AUTH_REQUIRED]`     | SSO session expired or no credentials available.   |
| `[AWS_ACCESS_DENIED]`     | Principal lacks `secretsmanager:GetSecretValue`.   |
| `[AWS_SECRET_NOT_FOUND]`  | Secret name / account / region mismatch.           |
| `[AWS_SECRET_INVALID]`    | Secret JSON is missing fields or malformed.        |
| `[DB_CONNECT_FAILED]`     | Could not reach the SQL Server instance.           |
| `[DB_QUERY_FAILED]`       | SQL Server returned an error executing the query.  |
| `[INVALID_QUERY]`         | The submitted query violated the read-only rules.  |

## Tools

| Tool                       | Purpose                                                  |
| -------------------------- | -------------------------------------------------------- |
| `list_schemas`             | User schemas in the database.                            |
| `list_tables`              | Tables, optionally filtered by schema.                   |
| `describe_table`           | Columns, types, nullability, identity, PK, defaults.     |
| `list_indexes`             | Indexes on a table (one row per index/column).           |
| `list_foreign_keys`        | Outgoing FKs from a table.                               |
| `list_views`               | Views, optionally filtered by schema.                    |
| `get_view_definition`      | View source SQL.                                         |
| `list_procedures`          | Stored procedures, optionally filtered by schema.        |
| `get_procedure_definition` | Procedure source SQL (does not execute).                 |
| `sample_rows`              | `SELECT TOP n * FROM schema.table` (default 10, max 100). |
| `run_select`               | Single SELECT/CTE, capped at `max_rows` (default 100, hard max 1000). |
| `refresh_secret`           | Re-fetch the secret and reconnect.                       |

## Local development

```bash
npm install
npm run build
# point your .claude.json command at the local build:
#   "command": "node",
#   "args": ["/absolute/path/to/mssql-mcp/dist/index.js"]
```

TDQS

A3.7/5.0

Scored across 12 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: listing metadata (tables, views, schemas, procedures, indexes, foreign keys), retrieving definitions, running SELECT queries, sampling rows, and refreshing credentials. No two tools can be confused.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case, e.g., 'describe_table', 'list_procedures', 'run_select'. There are no deviations or mixed conventions.

Tool Count5/5

12 tools is appropriate for a database exploration and querying server. The set covers all common operations without being excessive or sparse.

Completeness4/5

The tool surface is nearly complete for read-only database interaction: metadata listing, definition retrieval, SELECT querying, and sampling. Minor gaps include absence of database-level listing or system views, but the core use case is well-covered.

Maintenance

ActivityInactive
ResponsivenessNo issues