Skip to main content
Glama
hyunjongmoon

mcp-mssql-server

by hyunjongmoon

mcp-mssql-server

SQL Server의 메타데이터(테이블 구조·SP 정의·의존성)가드가 걸린 SELECT 실행을 MCP(Model Context Protocol) 도구로 노출하는 read-only 서버. Claude Code 같은 MCP 호스트가 이 도구를 호출해 스키마와 데이터를 조회할 수 있다.

설계 목표는 LLM에게 DB를 열어주되, 열어준 만큼만 열리게 하는 것이다:

  • 쓰기 도구를 아예 만들지 않는다 (READ ONLY)

  • 모든 응답이 PII 마스킹을 통과한다 (우회 불가)

  • 자유 SQL 도구에는 5중 가드와 감사 로그를 건다

  • 접속정보는 응답·에러 어디에도 나타나지 않는다

사이트별 값(민감 객체 목록, 차단 DB, 예시 객체명)은 소스가 아니라 config.json 에 둔다.


노출 도구 (5종, 모두 조회 전용)

이름

입력

출력

비고

list_databases

(없음)

DB 키 배열

접속정보 비노출, 키만

get_table_metadata

db, table (스키마.테이블명)

컬럼 메타데이터 배열 (이름·타입·길이·NULL·PK)

행 데이터 미반환

get_sp_definition

db, sp (스키마.SP명), backup?

정의(definition) + 파라미터 + 메타

정의 텍스트 자동 PII 마스킹. backup=true 시 raw 원본 백업 (아래)

get_sp_dependency

db, sp

참조 객체 (sys.dm_sql_referenced_entities) 유형별 그룹

권한 부족 시 referencesError

exec_query_select

db, sql, maxRows?

SELECT 결과 행 (컬럼 기반 PII 마스킹 적용)

5중 가드 + 감사 로그. 자세한 내용 아래

접속 대상 정의

DB 목록은 소스에 없다. config.jsonDB 키 → .env 변수 접두사를 정의하고, lib/db.js 가 그것을 순회한다.

// config.json
"databases": {
  "primary":   { "envPrefix": "DB" },
  "reporting": { "envPrefix": "REPORTING_DB",
                 "options": { "encrypt": true, "trustServerCertificate": false } }
}
# .env — 접두사 P 에 대해 P_USER / P_PASSWORD / P_SERVER / P_NAME
DB_USER=…            DB_PASSWORD=…            DB_SERVER=…            DB_NAME=…
REPORTING_DB_USER=…  REPORTING_DB_PASSWORD=…  REPORTING_DB_SERVER=…  REPORTING_DB_NAME=…
  • DB 개수·이름이 배포마다 달라도 코드는 그대로다. 도구의 db 인자에 쓰는 값이 곧 이 키다(소문자 정규화).

  • databases 를 생략하면 primaryDB_* 단일 DB 로 동작한다.

  • options 는 mssql 드라이버 옵션 override. Azure SQL 등 TLS 필수 환경은 encrypt: true 로 설정해야 한다 (기본값은 사내망 SQL Server 를 상정한 encrypt: false, trustServerCertificate: true).

  • 정의가 잘못되면(접두사 누락·형식 오류·빈 객체) 조용히 무시하지 않고 기동을 실패시킨다. DB 하나가 말없이 사라지면 원인 추적에 시간을 버리기 때문이다.

  • exec_query_select 로 조회를 막을 DB 는 blockedDatabases 에 키를 넣는다.

exec_query_select — 5중 가드

자유 SQL 도구는 위험도가 높으므로 다층 방어 적용. 모든 호출(차단·허용)은 ~/.claude/logs/mcp-exec-query.log 에 JSON Lines 로 감사 기록된다 (SQL 샘플은 PII 마스킹 후 500자 한도).

#

가드

거부 사유 예시

1

SELECT/WITH(CTE) 만 허용

INSERT, UPDATE, DELETE, MERGE, DROP, ALTER, CREATE, TRUNCATE, EXEC, EXECUTE, INTO, DECLARE, GRANT, REVOKE, OPENROWSET, OPENDATASOURCE, OPENQUERY, OPENXML, sp_executesql, xp_cmdshell 키워드 차단

2

행 수 캡

기본 50행, maxRows 로 최대 500까지 조정. 초과분은 잘리고 truncated=true 표기

3

컬럼 기반 PII 마스킹

lib/sensitive-mask.jscreateMasker().maskRecordset() 강제. 우회 불가

4

민감 객체 denylist

config.jsondenylist 에 나열한 객체명 접두사를 직접 조회하면 차단 (정규화된 substring 검사 — 대괄호·공백 변형 무시). 미설정 시 비활성

5

차단 DB

config.jsonblockedDatabases 에 나열한 DB 키는 이 도구로 조회 불가. 미설정 시 비활성

-

다중 statement 차단

세미콜론으로 구분된 두 개 이상의 statement 거부 (trailing semicolon 1개는 허용)

가드의 한계 (반드시 인지):

  • 가드 1/4는 정규식·substring 기반. 의도적 우회(예: 동적 SQL 생성, 다른 뷰를 거친 간접 조회)는 못 막는다. 모델의 우발적 실수 방어가 목적이지, 적대적 사용자 방어가 아니다.

  • 가드 4는 SQL 텍스트 검사. 차단된 뷰를 내부 참조하는 다른 뷰가 있으면 우회 가능 (transitive 참조 추적 X).

  • 가드 2는 recordset.slice() 로 잘라낸다 — 서버는 풀 결과를 스캔할 수 있음. 성능 보호는 mssql 기본 requestTimeout(15s) 에 의존.


Related MCP server: mcp-sqlserver-readonly

보안 노트 (3원칙)

  1. READ ONLY — INSERT/UPDATE/DELETE/EXEC 도구는 만들지 않는다.

  2. 마스킹 강제 — 모든 응답이 PII 마스킹 wrapper를 통과. SENSITIVE_MASK=0 같은 환경변수 우회는 MCP 경로에서 지원하지 않는다 (secure default). 대상 카테고리:

    • RRN (주민번호 패턴: 6자리-7자리, 7번째 자리 1~8)

    • PHONE (한국 휴대폰: 010/011/016~019)

    • EMAIL

    • Stable pseudonym: 응답 1건 내에서 동일 값은 동일 placeholder ([주민번호_1] 등)

  3. DB 접속정보 비노출DB_CONFIGS의 값(user/password/server)은 응답·에러 어디에도 포함되지 않는다. 에러 메시지는 sanitizeError를 통해 잠재 접속정보 substring을 ***로 치환.


설치 및 등록

1) 의존성 설치와 설정

npm install
cp .env.example config.json.example 2>/dev/null || true
cp .env.example .env                # DB 접속정보 입력
cp config.example.json config.json  # 사이트별 가드 설정
  • .env — DB 접속정보. lib/db.js 가 저장소 루트의 .env 를 읽는다.

  • config.jsondenylist(민감 객체 접두사), blockedDatabases(조회 금지 DB), examples(도구 설명에 쓰일 예시 표기).

    • ⚠️ 이 파일이 없으면 가드 4·5 가 동작하지 않는다. 기본값으로 막아줄 수 있는 값이 없기 때문이다 (사이트마다 객체명이 다르다). 대신 기동 시 stderr 에 경고가 출력되므로 조용히 열리지는 않는다.

    • 둘 다 .gitignore 처리되어 있다.

2) MCP 호스트에 등록

프로젝트 루트(<프로젝트>/.mcp.json)에 등록 예:

{
  "mcpServers": {
    "mssql": {
      "command": "node",
      "args": ["<이 저장소 절대경로>/server.js"]
    }
  }
}

.envconfig.jsonserver.js 기준 상대경로로 찾으므로 cwd 를 따로 잡을 필요가 없다. 다른 위치의 설정을 쓰려면 MCP_CONFIG 환경변수로 경로를 지정한다.

등록 후 Claude Code를 재시작하면 tools/list에 5개 도구가 잡힌다.

3) 동작 검증

npm test          # 또는 node smoke_test.js

스모크 테스트는 stdio JSON-RPC로 서버를 직접 호출해 검증하고, 실패가 있으면 종료 코드 1로 끝난다. 두 가지 모드로 자동 전환된다:

모드

조건

검증 범위

오프라인

DB_SERVER 미설정

프로토콜 핸드셰이크, tools/list 5종, 보안 가드 1·4·5, 채널 위생

전체

DB_SERVER 설정됨

위 + 도구 4종 실제 조회 + 가드 2(행 캡)·3(마스킹)

보안 가드는 getPool() 호출 이전에 평가되므로, DB에 접근할 수 없는 환경에서도 가드 회귀를 잡을 수 있다. 대상 객체는 TEST_DB / TEST_TABLE / TEST_SP / TEST_SAFE_TABLE 등 환경변수로 바꿀 수 있다.


알려진 한계

  • PII 마스킹은 정규식 기반. 자유 텍스트 패턴은 lib/mask-patterns.js 한 곳에만 정의된다. 이 패턴은 L2 hook(sensitive-prompt-scan.js)의 VALUE_PATTERNS와 동일해야 하는데, 훅은 이 저장소 에 있어 import로 묶을 수 없다. 대신 npm test가 훅 파일을 읽어 정규식을 대조하고 어긋나면 실패시킨다(훅이 없는 환경에서는 SKIP). 즉 동기화는 여전히 수동이지만, 어긋난 상태가 조용히 유지되지는 않는다.

  • 13자리 BIGINT가 RRN 패턴(7번째 자리 1~8)에 우연히 일치하면 [주민번호_N]으로 마스킹된다. SP 정의 안 하드코딩된 큰 ID에서 발생 가능. 발생 시 운영상 부작용 없음(가독성만 저하).

  • 이름 라벨 기반 마스킹(성명/학번/주소 등)은 자유 텍스트 경로에 미적용. L2 훅에는 LABEL_PATTERNS (성명: 홍길동 형태)가 있으나 이 서버에는 의도적으로 옮기지 않았다 — 훅 소스 주석이 지적하듯 오탐이 알려져 있기 때문이다. 따라서 get_sp_definition이 반환하는 SP 원문 주석에 -- 담당자: 홍길동 같은 표기가 있으면 마스킹되지 않는다.

    • 경로별 차이에 주의: 레코드셋(exec_query_select)은 lib/sensitive-mask.js의 컬럼 기반 PERSON 규칙이 적용되어 이름이 마스킹된다. 마스킹되지 않는 것은 자유 텍스트 경로다.

    • ⚠️ 이전 판 README는 "본 MCP는 raw 데이터 행을 반환하지 않으므로 위험이 낮다"고 적었으나, 이는 도구가 4종이던 v0.1.0 기준 서술이다. v0.2.0의 exec_query_select는 실제로 행을 반환한다. 위험을 낮추는 것은 "행을 반환하지 않음"이 아니라 컬럼 기반 마스킹이다.

  • get_sp_dependencysys.dm_sql_referenced_entities는 권한이 필요. 권한 부족 시 referencesError 필드에 메시지가 담기고 빈 결과가 반환된다.


get_sp_definition 의 원본 백업 (backup=true)

SP 수정 전 원본 보존이 필요할 때 backup: true 로 호출하면 .sql 파일을 저장한다.

// 호출
{ "db": "mydb", "sp": "dbo.usp_GetUser", "backup": true }

// 응답 (추가 필드)
{
  "backedUp": true,
  "backupPath": "<SP_BACKUP_DIR>/mydb_dbo_usp_GetUser.sql",
  "backupError": null,
  "definition": "...(마스킹본)..."
}

핵심 동작:

  • 디스크엔 raw(마스킹 전) 원본을 저장한다. 모델 응답의 definition 만 마스킹된다. → 마스킹본을 저장하면 placeholder가 코드 자리에 박힌 깨진 백업이 되므로, 원본 보존 목적상 raw 필수.

  • 저장 위치: SP_BACKUP_DIR 환경변수, 기본값은 저장소 안의 ./backup (.gitignore 처리됨).

  • 파일명: {db}_{스키마}_{SP명}.sql (점→언더스코어, 파일명 불가문자/경로구분자는 _ 치환 — path traversal 방지).

  • 같은 SP를 다시 백업하면 덮어쓴다 (current = 현재 DB 버전). *원본 보존은 SP 수정 에 호출하는 것으로 보장*.

  • 백업 실패는 도구를 막지 않는다 — backedUp:false, backupError 로 사유 전달.

보안 노트: 이 기능은 DB를 변경하지 않는다(여전히 read-only). 로컬 파일 1개 쓰기뿐이다. 다만 디스크에 저장되는 것은 마스킹 전 raw 원본이므로, SP_BACKUP_DIR 은 접근 통제된 경로여야 한다. 모델이 받는 것은 마스킹본과 백업 경로 문자열뿐이다.

감사 로그

exec_query_select 호출은 모두 ~/.claude/logs/mcp-exec-query.log (JSON Lines) 에 기록된다.

엔트리 형태:

// 허용
{"ts":"...","db":"mydb","sqlSample":"SELECT … (마스킹 후 500자)","blocked":false,
 "rowsAvailable":3,"rowsReturned":3,"truncated":false,"maskingCategories":["PERSON","STUDENT_NUM"]}

// 차단
{"ts":"...","db":"mydb","sqlSample":"INSERT INTO …","blocked":true,
 "reason":"SELECT 또는 WITH(CTE)로 시작하는 단일 쿼리만 허용됩니다."}

운영 활용:

  • 차단 사유별 빈도 분석 → 모델 호출 패턴 분포 파악

  • 마스킹 카테고리 빈도 → 어떤 종류 PII가 자주 회수되는지 → denylist 보강 근거

변경 이력

  • 0.4.0 (2026-07-20) — 사이트별 값을 config.json 으로 분리. DB 목록(databases)·민감 객체 denylist·차단 DB(blockedDatabases)·도구 설명 예시가 더 이상 소스에 하드코딩되지 않는다. lib/db.js 는 설정을 순회해 접속정보를 구성하므로 DB 개수·이름이 배포마다 달라도 코드 수정이 필요 없다. 설정 누락 시 가드는 비활성이며 기동 시 stderr 경고를 출력한다(조용히 열리지 않음). 잘못된 설정은 기본값으로 흐르지 않고 기동 실패. 자유 텍스트 PII 패턴을 lib/mask-patterns.js 단일 출처로 추출하고, L2 훅과 어긋나면 테스트가 실패하도록 대조 검사 추가.

  • 0.3.1 (2026-07-20) — 독립 저장소로 분리. package.json 에 누락돼 있던 mssql·dotenv 의존성 선언(원본에선 상위 node_modules 로 해결되던 것). 서버 identity 를 mcp-mssql-server 0.3.0 으로 정정(기존 ijis-db 0.1.0 과 드리프트). SP_BACKUP_DIR 기본값을 절대경로에서 ./backup 으로 변경. 스모크 테스트를 단언·종료코드 기반으로 재작성하고 오프라인 모드 추가.

  • 0.3.0 (2026-05-21) — get_sp_definitionbackup 옵션 추가 (raw 원본을 current 폴더에 백업, SP 수정 전 보존용). 디스크엔 raw / 모델엔 마스킹본 분리.

  • 0.2.0 (2026-05-20) — exec_query_select 도구 추가. 5중 가드 + 감사 로그. 자유 텍스트 마스커 명명 정리 (createFreeTextMasker).

  • 0.1.0 (2026-05-19) — 초기 작성. 4개 read-only 도구, 마스킹 wrapper, .env 격리.

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hyunjongmoon/mcp-mssql-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server