MCP? 만들어서 사용해보자
MCP? 만들어서 사용해보자
AI Agent를 이용해서 업무를 하고 있지만, 매번 다른 사람들이 AI를 활용해서 일을 하고 무엇인가를 만들어 나가는 것을 보면 ‘아, 나도 저렇게 적극적으로 손쉽게 제어하고 싶다.’ 라는 부러움과 궁금함이 생기더라구요.
프롬프트를 잘 쓰고, 필요한 것을 직접 만들기도 하고, 다양한 AI Agent 결합 모드까지..
나도 이것저것 해봐야지, 해봐야 늘지 싶어서 직접 MCP 서버를 만들어보기로 했습니다.
먼저 용어 정리
| 용어 | 설명 |
|---|---|
| AI (Artificial Intelligence) | 인공지능. 인간의 지능을 모방한 기술 전체를 말함 |
| LLM (Large Language Model) | 대규모 언어 모델. ChatGPT, Claude 같은 것들. 텍스트를 이해하고 생성함 |
| Agent | 목표를 주면 스스로 판단하고 도구를 써서 행동하는 AI |
| MCP (Model Context Protocol) | AI가 외부 도구/데이터에 접근하는 표준 규격 |
| Prompt | AI에게 주는 지시문. 프롬프트를 어떻게 쓰느냐에 따라 결과가 달라짐 |
| Tool | AI가 사용할 수 있는 기능. 파일 읽기, DB 조회, API 호출 등 |
| RAG (Retrieval Augmented Generation) | 외부 문서를 검색해서 답변에 활용하는 방식 |
| Fine-tuning | LLM을 특정 목적에 맞게 추가 학습시키는 것 |
| Token | LLM이 텍스트를 처리하는 단위. 대략 한글 1글자 = 1~2토큰 |
| Context Window | LLM이 한 번에 처리할 수 있는 토큰 수. 대화 길이 제한과 관련 |
에이전트란?
1
2
일반 LLM: 질문하면 답변만 함 (수동적)
에이전트: 목표를 주면 스스로 판단하고 행동함 (능동적)
예를 들어:
1
2
3
일반 LLM: "파일 읽어줘" → "저는 파일을 읽을 수 없습니다"
에이전트: "파일 읽어줘" → 파일 시스템 도구 호출 → 파일 읽기 → 결과 반환
도구를 스스로 선택하고 실행할 수 있으면 에이전트인 것!
에이전트가 도구 선택하는 과정
에이전트는 ReAct 패턴으로 동작한다.
1
ReAct = Reasoning(추론) + Acting(행동)
예시로 보면:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
사용자: "2024년 12월 취소 주문 뽑아줘"
[1단계: Reasoning - 생각]
"취소 주문을 조회해야 하네"
"DB 조회가 필요하겠다"
"query 도구를 쓰면 되겠다"
[2단계: Acting - 행동]
→ query 도구 호출
→ SELECT * FROM OrderMst WHERE Status = 'CANCEL' AND ...
[3단계: Observation - 결과 확인]
"결과가 150건 나왔네"
[4단계: Reasoning - 다시 생각]
"사용자한테 결과 전달하면 되겠다"
[5단계: 응답]
"2024년 12월 취소 주문 150건입니다. [결과 표시]"
핵심은:
1
2
3
4
일반 LLM: 입력 → 출력 (끝)
에이전트: 입력 → 생각 → 행동 → 결과 확인 → 또 생각 → ... → 출력
(목표 달성할 때까지 반복)
MCP란?
1
2
MCP (Model Context Protocol)
= AI가 외부 도구/데이터에 접근하는 표준 규격
비유하면:
1
2
3
4
5
6
7
8
9
10
11
USB-C 포트 = MCP
- 충전기 연결 가능
- 모니터 연결 가능
- 외장하드 연결 가능
- 다 같은 포트로 연결
MCP도 마찬가지:
- DB 연결 가능
- 파일 시스템 연결 가능
- API 연결 가능
- 다 같은 규격으로 연결
MCP가 왜 필요해?
MCP 없던 시절:
1
2
3
4
Claude한테 DB 연결하고 싶다
→ 직접 코드 짜서 연동해야 함
→ 다른 AI 쓰려면? 또 새로 짜야 함
→ 도구마다 연동 방식이 다 다름
MCP 있으면:
1
2
3
4
MCP 규격에 맞춰서 DB 연결 한 번 만들어두면
→ Claude에서도 쓸 수 있고
→ 다른 AI에서도 쓸 수 있고
→ 표준이니까 재사용 가능
MCP 클라이언트 vs MCP 서버
1
2
MCP 서버: 도구를 제공하는 쪽
MCP 클라이언트: 도구를 사용하는 쪽
1
2
3
4
5
[Claude Desktop / Claude Code] ← MCP 클라이언트
↓ "DB 조회해줘"
[MSSQL MCP 서버] ← MCP 서버
↓
[MSSQL DB]
비유하면:
1
2
3
4
식당으로 치면:
- MCP 서버 = 주방 (요리 제공)
- MCP 클라이언트 = 홀 (손님 요청 전달)
- AI = 손님 (뭐 먹을지 결정)
MCP 서버 구조
MCP 서버가 하는 일:
1
2
1. "나 이런 도구 있어요" 선언 (tools)
2. AI가 요청하면 실행해서 결과 반환
기본 구조 (Node.js 기준):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// MCP 서버 기본 형태
const server = new McpServer({
name: "mssql-server",
version: "1.0.0"
});
// 도구 정의
server.tool(
"query", // 도구 이름
"SQL 쿼리를 실행합니다", // 설명
{
sql: { type: "string", description: "실행할 SQL" } // 파라미터
},
async ({ sql }) => {
// 실제 DB 조회 로직
const result = await db.query(sql);
return result;
}
);
AI 입장에서는:
1
2
3
"query라는 도구가 있네"
"sql 파라미터 넘기면 되네"
→ 필요할 때 호출
AI 에이전트가 MCP로 하는 것들
에이전트에 연결된 MCP들 예시:
1
2
3
파일 시스템 MCP: 파일 읽기/쓰기/생성
터미널 MCP: 명령어 실행
브라우저 MCP: 웹 조작
그래서 이런 게 가능한 거죠:
1
2
3
"이 폴더 구조 보여줘" → 파일 시스템 MCP 호출
"npm install 해줘" → 터미널 MCP 호출
"저 사이트 스크린샷 찍어줘" → 브라우저 MCP 호출
내가 만들 것
1
2
3
4
5
6
7
[AI 에이전트]
↓
[MCP 클라이언트]
↓
[MSSQL MCP 서버] ← 이거 만들면
↓
[로컬 개발 DB]
이러면:
1
2
3
4
5
6
7
8
9
"2024년 12월 A브랜드 취소 주문 뽑아줘"
↓
에이전트가 추론: "DB 조회가 필요하네"
↓
MSSQL MCP 호출: query 도구 실행
↓
결과 반환 + 쿼리문 제공
↓
검증된 쿼리 복사해서 운영 DB에서 실행
직접 만들어봤다
MSSQL용 MCP 서버를 만들었다.
구조
1
2
3
4
5
mssql-mcp/
├── src/
│ └── index.ts
├── package.json
└── tsconfig.json
만든 도구들
| 도구 | 설명 |
|---|---|
| query | SELECT 쿼리 실행 (읽기 전용) |
| execute | INSERT/UPDATE/DELETE 실행 |
| list_tables | 테이블 목록 조회 |
| describe_table | 테이블 구조 조회 |
| list_procedures | 저장 프로시저 목록 |
| get_procedure_definition | 프로시저 정의 보기 |
| execution_plan | 실행 계획 보기 |
단순 쿼리 실행만 있는 게 아니라, 테이블 구조나 프로시저, 실행 계획까지 볼 수 있게 했다.
핵심 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// MCP 서버 생성
const server = new Server(
{
name: "mssql-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 도구 정의 예시
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "query",
description: "Execute a SELECT query (read-only)",
inputSchema: {
type: "object",
properties: {
sql: {
type: "string",
description: "SQL SELECT query to execute",
},
},
required: ["sql"],
},
},
// ...
],
};
});
도구 실행 핸들러
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const p = await getPool();
switch (name) {
case "query": {
const query = args?.sql as string;
if (!query.trim().toLowerCase().startsWith("select")) {
return {
content: [{ type: "text", text: "Error: Only SELECT queries allowed" }],
isError: true,
};
}
const result = await p.request().query(query);
return {
content: [{ type: "text", text: JSON.stringify(result.recordset, null, 2) }],
};
}
// ...
}
});
에이전트가 “query” 도구를 호출하면 이 핸들러가 실행되어서 실제 DB 조회를 하고 결과를 반환한다.
마무리
정리하면:
| 개념 | 설명 |
|---|---|
| 에이전트 | 도구를 스스로 선택하고 실행하는 AI |
| MCP | AI가 도구에 접근하는 표준 규격 |
| MCP 서버 | 도구를 제공하는 쪽 |
| MCP 클라이언트 | 도구를 사용하는 쪽 |
이제 개념도 잡았고, 직접 만들어보기도 했다. 앞으로 이걸 활용해서 자연어로 쿼리 추출하는 것까지 발전시켜볼 예정이다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.