카테고리 없음

Claude와 외부 도구를 연결하는 MCP 서버 연동 기초

seunghyeonlab 2026. 6. 4. 10:04

REST API 래퍼를 매번 새로 짜는 대신, 서버 하나를 등록하면 Claude가 그 서버의 모든 도구를 즉시 쓸 수 있다. MCP 서버 연동 기초를 제대로 이해하면 반복 연동 작업을 구조적으로 끊어낼 수 있다.


한눈에 보는 답

  • MCP란 Claude가 외부 시스템과 통신하는 표준 인터페이스다. 서버를 한 번 등록하면 tool과 resource 전체를 Claude가 자동으로 인식한다.
  • 언제 쓸 만한가 DB, 파일 시스템, 사내 API 등 반복 연동이 필요한 시스템이 있을 때, REST 래퍼를 직접 짜는 대신 MCP 서버를 만들면 유지보수가 줄어든다.
  • 어떻게 검증하는가 settings.json에 등록 → Claude Code에서 /mcp 명령으로 상태 확인 → tool 하나를 수동 호출해 응답 확인 순서로 진행한다.

인용 가능한 핵심 정리

검증일: 2026-06-04

정의: MCP(Model Context Protocol)는 Claude(Host)와 외부 시스템(Server) 사이의 JSON-RPC 2.0 기반 표준 통신 프로토콜로, tool 호출과 resource 읽기를 단일 인터페이스로 통합한다.

핵심 결론: MCP 서버를 등록하면 Claude는 tools/list로 가용 도구를 파악하고 tools/call로 실행한다. 개발자가 별도 프롬프트 엔지니어링 없이도 Claude가 올바른 도구를 선택한다.

적용 조건: Claude Code 또는 Claude.ai 에이전트 환경에서 작동하며, @modelcontextprotocol/sdk 기준 최신 안정 버전을 사용해야 한다. 원격 SSE/HTTP 서버는 CORS와 인증 설정이 서버 측에서 완료되어 있어야 한다.


핵심 용어 정리

  • MCP(Model Context Protocol): Claude와 외부 도구 사이의 표준 통신 규격. REST로 치면 OpenAPI Spec에 해당하는 역할을 한다.
  • Host: Claude Code나 Claude.ai 에이전트처럼 MCP 클라이언트 역할을 하는 실행 환경.
  • Tool: 사이드이펙트가 있는 액션 단위. DB 쓰기, API 호출, 파일 수정 등이 여기 해당한다.
  • Resource: 읽기 전용 데이터 소스. URI 형태로 노출되며 Claude가 컨텍스트로 자동 포함할 수 있다.

1. 왜 지금 이걸 봐야 하나

Claude를 활용한 자동화를 처음 설계할 때 가장 흔하게 마주치는 문제가 있다. DB에서 데이터를 가져오거나 사내 API를 호출해야 할 때, Claude에게 그 능력을 부여하는 방법이 마땅치 않다는 점이다. 프롬프트에 API 응답을 붙여넣거나, 매번 Python 스크립트를 중간에 끼워넣는 방식으로 이어 붙이다 보면 복잡도가 급격히 올라간다.

MCP는 이 문제를 구조적으로 해결한다. 서버를 한 번 작성해 등록하면, Claude가 어떤 도구를 언제 쓸지를 스스로 판단한다. 개발자가 "이 함수를 호출하라"고 프롬프트에 일일이 명시할 필요가 없어진다.

실무에서 가장 체감이 큰 장면은 "같은 DB 연동을 여러 Claude 기반 프로젝트에서 재사용할 때"다. MCP 서버 하나를 만들어두면, 어떤 프로젝트든 settings.json에 한 줄만 추가하면 그 연동을 즉시 가져다 쓸 수 있다.


2. 핵심 아이디어

MCP를 한 문장으로 정리하면: Claude가 외부 시스템을 도구로 인식하도록 하는 공통 규격이다.

아키텍처를 계층으로 보면 이렇다.

계층 역할 예시
Host (Claude) 도구를 인식하고 호출 결정 Claude Code, Claude.ai
MCP Server 도구 목록 제공 + 실행 처리 직접 작성한 Node.js 서버
External System 실제 데이터/액션의 주체 PostgreSQL, GitHub API, 파일 시스템

통신은 JSON-RPC 2.0 위에서 일어나고, 전송 계층은 세 가지 중 선택한다. stdio는 로컬 프로세스를 Claude Code가 직접 실행하는 방식이라 빠르고 설정이 단순하다. SSE는 서버가 원격에 있을 때 쓴다. Streamable HTTP는 양방향 HTTP 스트림이 필요한 경우다.

핵심은 inputSchema다. tool이 받을 수 있는 인자를 JSON Schema로 선언해두면, Claude가 잘못된 인자를 보내는 상황이 크게 줄어든다. 스키마가 느슨하면 Claude의 추론 결과도 느슨해진다.


3. 바로 따라하는 방법

1단계: settings.json에 서버 등록

로컬 서버(stdio)와 원격 서버(SSE) 두 가지를 함께 등록하는 예시다.

{
  "mcpServers": {
    "my-db": {
      "command": "node",
      "args": ["/path/to/mcp-db-server/index.js"],
      "env": {
        "DATABASE_URL": "postgres://user:pass@localhost:5432/mydb"
      }
    },
    "remote-api": {
      "type": "sse",
      "url": "https://mcp.example.com/sse",
      "headers": {
        "Authorization": "Bearer ${API_KEY}"
      }
    }
  }
}

${API_KEY} 문법으로 셸 환경 변수를 주입받을 수 있다. 자격 증명을 파일에 하드코딩하지 않아도 된다.

2단계: 최소 MCP 서버 구현

Node.js와 @modelcontextprotocol/sdk로 도구 하나짜리 서버를 만든다. 전체 흐름이 가장 짧게 드러나는 구조다.

npm install @modelcontextprotocol/sdk
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  { name: "my-db", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// 도구 목록 노출
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "query_users",
      description: "users 테이블에서 조건에 맞는 행을 반환한다. 쓰기 작업은 수행하지 않는다.",
      inputSchema: {
        type: "object",
        properties: {
          limit: { type: "number", default: 10, description: "최대 반환 행 수" },
          filter: { type: "string", description: "name 컬럼 부분 일치 필터" },
        },
        required: [],
      },
    },
  ],
}));

// 도구 실행 처리
server.setRequestHandler(CallToolRequestSchema, async (req) => {
  if (req.params.name === "query_users") {
    const { limit = 10, filter = "" } = req.params.arguments ?? {};

// 허용 목록으로 입력 검증 (아래 보안 섹션 참고)
    const rows = await db.query(
      `SELECT * FROM users WHERE name LIKE $1 LIMIT $2`,
      [`%${filter}%`, limit]
    );

return {
      content: [{ type: "text", text: JSON.stringify(rows) }],
    };
  }
  throw new Error(`Unknown tool: ${req.params.name}`);
});

const transport = new StdioServerTransport();
await server.connect(transport);

3단계: 연결 상태 확인

Claude Code 에서 /mcp 명령을 실행하면 등록된 서버 목록과 connected / error 상태가 표시된다. error가 뜨면 서버를 직접 실행해 stderr 출력을 확인한다.

# stdio 서버 직접 실행해서 에러 확인
node /path/to/mcp-db-server/index.js

# 원격 SSE 서버 핸드셰이크 테스트
curl -N -H "Accept: text/event-stream" https://mcp.example.com/sse

curl 응답에 initialize 메시지가 없고 연결이 바로 끊기면, 서버 측 CORS 설정이나 인증 헤더 문제일 가능성이 높다.


4. 운영할 때 조심할 점

로그 채널을 반드시 분리한다. stdio 서버에서 console.log를 쓰면 stdout이 오염된다. JSON-RPC 파서가 로그 문자열을 프로토콜 메시지로 읽으려다 파싱에 실패한다. 로그는 전부 process.stderr.write()로 보낸다.

// 올바른 방법
process.stderr.write(`[debug] query: ${sql}\n`);

// 절대 금지 — JSON-RPC 파싱 오류 발생
console.log("query:", sql);

tool 설명에 사이드이펙트를 명시한다. Claude는 description을 보고 tool 호출 여부를 판단한다. "이 도구는 파일을 삭제합니다"처럼 결과를 명확하게 적어두면, Claude가 사용자 확인 없이 무분별하게 호출하는 상황이 줄어든다.

SQL 인젝션과 테이블명 동적 삽입을 막는다. 사용자 입력을 쿼리에 직접 붙이면 공격 표면이 Claude 세션 전체로 연결된다.

// 위험 — 사용자가 임의 테이블에 접근 가능
const rows = await db.query(`SELECT * FROM ${req.params.table}`);

// 안전 — 허용 목록으로 검증
const ALLOWED_TABLES = ["users", "products"];
if (!ALLOWED_TABLES.includes(req.params.table)) {
  throw new Error("허용되지 않은 테이블 접근");
}
const rows = await db.query(`SELECT * FROM ${req.params.table}`);

tool과 resource를 목적에 맞게 분리한다. 파일 읽기는 resource, 파일 쓰기는 tool로 구분해야 Claude가 의미론적으로 올바르게 판단한다.

구분 Tool Resource
사이드이펙트 있음 (쓰기, 삭제, API 호출) 없음 (읽기 전용)
호출 방식 tools/call (명시적 호출) resources/read (자동 컨텍스트 포함 가능)
URI 없음 scheme://path 형태
적합한 용도 DB 쓰기, 파일 수정, 외부 API POST 문서 읽기, 설정 조회, 로그 확인

이 구분이 흐릿하면 Claude가 읽기만 하면 되는 상황에서 불필요하게 tool을 호출하거나, 반대로 변경이 필요한데 resource만 참조하는 실수가 생긴다.

환경별 차이도 미리 확인한다. Mac에서는 ~/.claude/settings.json 경로가 기본이지만, Docker 컨테이너 안에서 Claude Code를 실행하는 경우 마운트 경로가 달라질 수 있다. stdio 서버를 컨테이너 외부에서 실행하면 command 경로도 그에 맞게 조정해야 한다.


근거와 검증 기준

검증일: 2026-06-04

주장 근거 확인 방법 한계
MCP 서버는 tools/listtools/call만 구현하면 최소 동작한다 @modelcontextprotocol/sdk 공식 예제 및 타입 정의 ListToolsRequestSchema 핸들러 등록 후 /mcp 명령으로 tool 목록 확인 SDK 버전이 바뀌면 스키마 이름이 달라질 수 있음
stdio 서버에서 console.log는 JSON-RPC 파싱을 깨뜨린다 JSON-RPC 2.0 spec — stdout은 메시지 전용 채널 서버 실행 후 Claude가 tool 호출 시 parse error 발생 여부 확인 stderr 용량 제한이 있는 환경에서는 별도 로그 파일 필요
inputSchema 정밀도가 Claude 호출 정확도에 영향을 준다 Claude tool use 문서 및 실무 관찰 느슨한 스키마와 엄격한 스키마를 동일 프롬프트로 비교 테스트 모델 버전마다 스키마 활용 방식이 다를 수 있음
원격 SSE 서버 연결 실패는 CORS 또는 인증 문제일 가능성이 높다 Claude Code 공식 문서 curl -N -H "Accept: text/event-stream" <URL>로 핸드셰이크 직접 확인 방화벽이나 프록시 환경에서는 네트워크 계층 문제일 수도 있음

자주 묻는 질문

MCP 서버 연동은 언제 쓰는 게 좋을까?

같은 외부 시스템(DB, API, 파일 시스템)을 여러 Claude 기반 프로젝트에서 반복해서 쓸 때 효과가 가장 크다. 단발성 스크립트나 한 번만 쓸 연동이라면 굳이 MCP 서버를 만들 필요 없이 프롬프트에 결과를 붙여넣는 게 빠르다. 반복 패턴이 보이기 시작할 때 MCP로 전환하는 게 실용적이다.

MCP 서버 연동을 적용하기 전에 무엇을 확인해야 할까?

서버가 노출할 도구의 경계를 먼저 정해야 한다. 무엇이 tool이고 무엇이 resource인지, 각 도구가 어떤 사이드이펙트를 가지는지를 description에 명시할 수 있을 정도로 정리한 다음 구현에 들어가는 게 좋다. description이 불명확한 채로 배포하면 Claude가 의도와 다른 도구를 선택하는 상황이 반복된다.

결과가 제대로 나왔는지 어떻게 검증할까?

세 단계로 확인한다. 첫째, Claude Code에서 /mcp를 실행해 서버 상태가 connected인지 확인한다. 둘째, Claude에게 직접 "query_users 도구를 써서 이름에 'kim'이 들어간 사용자 10명을 찾아줘"처럼 도구 이름을 명시해서 호출해본다. 셋째, 서버 stderr 로그에서 실제 실행된 쿼리와 반환된 행 수를 대조한다. 이 세 단계가 모두 일치해야 연동이 정상 작동하는 것으로 볼 수 있다.


마무리

MCP 서버 연동 기초의 핵심은 단순하다. 서버를 한 번 만들어 등록하면 Claude가 도구를 스스로 인식하고 호출한다. 이 구조가 잡히면 새로운 외부 시스템을 연동할 때마다 settings.json 한 줄만 추가하면 된다.

다음에는 여러 MCP 서버를 조합해서 멀티 스텝 자동화 워크플로를 구성하는 방법을 다룰 예정이다.


🐦 X에서 더 빠르게: @baegseungh7061
📚 이 시리즈 더 보기: 전체 글 목록
💌 새 글 알림: X 팔로우 또는 블로그 RSS 구독