mcp-mssql-server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-mssql-servershow columns of Sales.Orders"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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종, 모두 조회 전용)
이름 | 입력 | 출력 | 비고 |
| (없음) | DB 키 배열 | 접속정보 비노출, 키만 |
|
| 컬럼 메타데이터 배열 (이름·타입·길이·NULL·PK) | 행 데이터 미반환 |
|
| 정의(definition) + 파라미터 + 메타 | 정의 텍스트 자동 PII 마스킹. |
|
| 참조 객체 ( | 권한 부족 시 |
|
| SELECT 결과 행 (컬럼 기반 PII 마스킹 적용) | 5중 가드 + 감사 로그. 자세한 내용 아래 |
접속 대상 정의
DB 목록은 소스에 없다. config.json 이 DB 키 → .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를 생략하면primary←DB_*단일 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) 만 허용 |
|
2 | 행 수 캡 | 기본 50행, |
3 | 컬럼 기반 PII 마스킹 |
|
4 | 민감 객체 denylist |
|
5 | 차단 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원칙)
READ ONLY — INSERT/UPDATE/DELETE/EXEC 도구는 만들지 않는다.
마스킹 강제 — 모든 응답이 PII 마스킹 wrapper를 통과.
SENSITIVE_MASK=0같은 환경변수 우회는 MCP 경로에서 지원하지 않는다 (secure default). 대상 카테고리:RRN(주민번호 패턴: 6자리-7자리, 7번째 자리 1~8)PHONE(한국 휴대폰: 010/011/016~019)EMAILStable pseudonym: 응답 1건 내에서 동일 값은 동일 placeholder (
[주민번호_1]등)
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.json—denylist(민감 객체 접두사),blockedDatabases(조회 금지 DB),examples(도구 설명에 쓰일 예시 표기).⚠️ 이 파일이 없으면 가드 4·5 가 동작하지 않는다. 기본값으로 막아줄 수 있는 값이 없기 때문이다 (사이트마다 객체명이 다르다). 대신 기동 시 stderr 에 경고가 출력되므로 조용히 열리지는 않는다.
둘 다
.gitignore처리되어 있다.
2) MCP 호스트에 등록
프로젝트 루트(<프로젝트>/.mcp.json)에 등록 예:
{
"mcpServers": {
"mssql": {
"command": "node",
"args": ["<이 저장소 절대경로>/server.js"]
}
}
}
.env와config.json은server.js기준 상대경로로 찾으므로cwd를 따로 잡을 필요가 없다. 다른 위치의 설정을 쓰려면MCP_CONFIG환경변수로 경로를 지정한다.
등록 후 Claude Code를 재시작하면 tools/list에 5개 도구가 잡힌다.
3) 동작 검증
npm test # 또는 node smoke_test.js스모크 테스트는 stdio JSON-RPC로 서버를 직접 호출해 검증하고, 실패가 있으면 종료 코드 1로 끝난다. 두 가지 모드로 자동 전환된다:
모드 | 조건 | 검증 범위 |
오프라인 |
| 프로토콜 핸드셰이크, |
전체 |
| 위 + 도구 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_dependency의sys.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-server0.3.0으로 정정(기존ijis-db0.1.0과 드리프트).SP_BACKUP_DIR기본값을 절대경로에서./backup으로 변경. 스모크 테스트를 단언·종료코드 기반으로 재작성하고 오프라인 모드 추가.0.3.0 (2026-05-21) —
get_sp_definition에backup옵션 추가 (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 격리.
Maintenance
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
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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