요약 (TL;DR)
run_test, validate_schema, list_environments 세 가지 도구를 노출하는 TypeScript MCP 서버를 구축하세요. Claude Code의 경우 ~/.claude/settings.json에, Cursor의 경우 .cursor/mcp.json에 구성합니다. 그러면 AI 에이전트가 채팅 인터페이스를 벗어나지 않고도 Apidog 테스트를 실행하고, OpenAPI 스키마를 검증하며, 환경을 가져올 수 있습니다. 전체 소스 코드는 약 150줄이며 @modelcontextprotocol/sdk 패키지를 사용합니다.
Claude Code, Cursor 및 기타 AI 에이전트가 채팅 인터페이스를 벗어나지 않고 Apidog API 테스트를 실행하고, 스키마를 검증하고, 응답을 비교할 수 있도록 하는 MCP 서버를 구축하세요.
이것이 바로 Model Context Protocol (MCP)이 가능하게 하는 것입니다. MCP는 AI 에이전트가 표준화된 인터페이스를 통해 외부 도구에 접근할 수 있도록 합니다. Apidog용 MCP 서버를 구축하면 AI 에이전트가 컨텍스트 전환 없이 테스트를 실행하고, 스키마를 검증하며, 환경을 가져올 수 있습니다.
MCP란 무엇인가요?
MCP (Model Context Protocol)는 AI 에이전트가 외부 도구 및 데이터 소스에 접근하기 위한 프로토콜입니다. Claude Code, Cursor 및 기타 MCP 호환 클라이언트에서 작동하는 플러그인 시스템으로 생각할 수 있습니다.
MCP 서버는 도구 (에이전트가 호출할 수 있는 함수)와 리소스 (에이전트가 읽을 수 있는 데이터)를 노출합니다. Apidog MCP 서버는 API 테스트를 위한 도구를 노출할 것입니다.
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ AI 에이전트 │ │ MCP 서버 │ │ Apidog │
│ (Claude Code) │◄───────►│ (사용자 코드) │◄───────►│ API │
└─────────────────┘ JSON └──────────────────┘ HTTP └─────────────┘
1단계: 프로젝트 설정
새로운 TypeScript 프로젝트를 생성하세요:
mkdir apidog-mcp-server
cd apidog-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
tsconfig.json을 생성하세요:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
package.json에 빌드 스크립트를 추가하세요:
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
2단계: MCP 서버 스켈레톤 생성
src/index.ts를 생성하세요:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "apidog",
version: "1.0.0",
description: "AI 에이전트를 위한 Apidog API 테스트 도구"
});
// 도구는 여기에 정의됩니다
const transport = new StdioServerTransport();
await server.connect(transport);
이 스켈레톤은 MCP 서버를 생성하고 이를 stdio 트랜스포트에 연결합니다. 트랜스포트는 표준 입출력을 통해 AI 에이전트와 서버 간의 통신을 처리합니다.
3단계: run_test 도구 정의
src/index.ts에 첫 번째 도구를 추가하세요:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "apidog",
version: "1.0.0",
description: "AI 에이전트를 위한 Apidog API 테스트 도구"
});
// 도구: run_test
server.tool(
"run_test",
{
projectId: z.string().describe("Apidog 프로젝트 ID (프로젝트 URL에서 찾을 수 있음)"),
environmentId: z.string().optional().describe("테스트 실행을 위한 선택적 환경 ID"),
testSuiteId: z.string().optional().describe("특정 테스트 스위트를 실행하기 위한 선택적 테스트 스위트 ID")
},
async ({ projectId, environmentId, testSuiteId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "오류: APIDOG_API_KEY 환경 변수가 설정되지 않았습니다"
}]
};
}
// API URL 구축
let url = `https://api.apidog.com/v1/projects/${projectId}/tests/run`;
const params = new URLSearchParams();
if (environmentId) params.append("environmentId", environmentId);
if (testSuiteId) params.append("testSuiteId", testSuiteId);
if (params.toString()) url += `?${params.toString()}`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
}
});
if (!response.ok) {
const error = await response.text();
return {
content: [{
type: "text",
text: `API 오류: ${response.status} ${error}`
}]
};
}
const results = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `요청 실패: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
도구 정의는 세 부분으로 구성됩니다:
- 이름 —
run_test(에이전트는 이름으로 도구를 선택하므로 설명적으로 만드세요) - 스키마 — 설명이 포함된 파라미터에 대한 Zod 유효성 검사
- 핸들러 — Apidog API를 호출하는 비동기 함수
4단계: validate_schema 도구 추가
배포 전에 OpenAPI 오류를 포착하기 위해 스키마 유효성 검사를 추가하세요:
// 도구: validate_schema
server.tool(
"validate_schema",
{
schema: z.object({}).describe("유효성 검사할 OpenAPI 3.x 스키마 객체"),
strict: z.boolean().optional().default(false).describe("추가 검사를 위해 엄격 모드 활성화")
},
async ({ schema, strict }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "오류: APIDOG_API_KEY 환경 변수가 설정되지 않았습니다"
}]
};
}
try {
const response = await fetch("https://api.apidog.com/v1/schemas/validate", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ schema, strict })
});
const result = await response.json();
if (!response.ok) {
return {
content: [{
type: "text",
text: `유효성 검사 실패: ${JSON.stringify(result.errors, null, 2)}`
}]
};
}
return {
content: [{
type: "text",
text: result.valid
? "스키마가 유효한 OpenAPI 3.x입니다"
: `경고: ${JSON.stringify(result.warnings, null, 2)}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `유효성 검사 실패: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
5단계: list_environments 도구 추가
사용 가능한 테스트 환경을 가져오는 도구를 추가하세요:
// 도구: list_environments
server.tool(
"list_environments",
{
projectId: z.string().describe("Apidog 프로젝트 ID")
},
async ({ projectId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "오류: APIDOG_API_KEY 환경 변수가 설정되지 않았습니다"
}]
};
}
try {
const response = await fetch(
`https://api.apidog.com/v1/projects/${projectId}/environments`,
{
headers: {
"Authorization": `Bearer ${apiKey}`
}
}
);
if (!response.ok) {
const error = await response.text();
return {
content: [{
type: "text",
text: `API 오류: ${response.status} ${error}`
}]
};
}
const environments = await response.json();
return {
content: [{
type: "text",
text: environments.length === 0
? "이 프로젝트에 대한 환경을 찾을 수 없습니다"
: environments.map((e: any) =>
`- ${e.name} (ID: ${e.id})${e.isDefault ? " [기본값]" : ""}`
).join("\n")
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `요청 실패: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
6단계: 빌드 및 테스트
서버를 빌드하세요:
npm run build
간단한 MCP 클라이언트로 테스트하세요. test-client.js를 생성하세요:
import { spawn } from "child_process";
const server = spawn("node", ["dist/index.js"], {
env: { ...process.env, APIDOG_API_KEY: "your-api-key" }
});
server.stdout.on("data", (data) => {
console.log(`서버 출력: ${data}`);
});
server.stderr.on("data", (data) => {
console.error(`서버 오류: ${data}`);
});
// 테스트 메시지 전송
const message = {
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {
protocolVersion: "2024-11-05",
capabilities: {},
clientInfo: { name: "test-client", version: "1.0.0" }
}
};
server.stdin.write(JSON.stringify(message) + "\n");
7단계: Claude Code 구성
Claude Code 구성에 MCP 서버를 추가하세요:
~/.claude/settings.json을 생성하거나 편집하세요:
{
"mcpServers": {
"apidog": {
"command": "node",
"args": ["/absolute/path/to/apidog-mcp-server/dist/index.js"],
"env": {
"APIDOG_API_KEY": "your-api-key-here"
}
}
}
}
Claude Code를 다시 시작하세요. API 테스트 지원을 요청하면 Apidog 도구가 이제 나타나야 합니다.
Claude Code에서 사용법:
run_test 도구를 사용하여 Apidog 프로젝트에서 테스트를 실행하세요.
프로젝트 ID: proj_12345
환경: staging
이 OpenAPI 스키마를 Apidog 규칙에 따라 검증하세요:
[스키마 붙여넣기]
proj_12345 프로젝트의 모든 환경을 나열하세요
8단계: Cursor 구성
Cursor는 유사한 MCP 구성을 사용합니다. 프로젝트의 .cursor/mcp.json을 생성하세요:
{
"mcpServers": {
"apidog": {
"command": "node",
"args": ["/absolute/path/to/apidog-mcp-server/dist/index.js"],
"env": {
"APIDOG_API_KEY": "your-api-key-here"
}
}
}
}
Cursor에서 사용법:
@apidog run_test projectId="proj_12345" environmentId="staging"
완전한 소스 코드
전체 src/index.ts는 다음과 같습니다:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "apidog",
version: "1.0.0",
description: "AI 에이전트를 위한 Apidog API 테스트 도구"
});
// 도구: run_test
server.tool(
"run_test",
{
projectId: z.string().describe("Apidog 프로젝트 ID"),
environmentId: z.string().optional().describe("환경 ID"),
testSuiteId: z.string().optional().describe("테스트 스위트 ID")
},
async ({ projectId, environmentId, testSuiteId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "오류: APIDOG_API_KEY가 설정되지 않았습니다"
}]
};
}
let url = `https://api.apidog.com/v1/projects/${projectId}/tests/run`;
const params = new URLSearchParams();
if (environmentId) params.append("environmentId", environmentId);
if (testSuiteId) params.append("testSuiteId", testSuiteId);
if (params.toString()) url += `?${params.toString()}`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
}
});
const results = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `요청 실패: ${error instanceof Error ? error.message : String(error)}`
}]
};
}
}
);
// 도구: validate_schema
server.tool(
"validate_schema",
{
schema: z.object({}).describe("OpenAPI 스키마"),
strict: z.boolean().optional().default(false)
},
async ({ schema, strict }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "오류: APIDOG_API_KEY가 설정되지 않았습니다"
}]
};
}
const response = await fetch("https://api.apidog.com/v1/schemas/validate", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ schema, strict })
});
const result = await response.json();
return {
content: [{
type: "text",
text: result.valid
? "스키마가 유효합니다"
: `문제: ${JSON.stringify(result.errors || result.warnings, null, 2)}`
}]
};
}
);
// 도구: list_environments
server.tool(
"list_environments",
{
projectId: z.string().describe("Apidog 프로젝트 ID")
},
async ({ projectId }) => {
const apiKey = process.env.APIDOG_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "오류: APIDOG_API_KEY가 설정되지 않았습니다"
}]
};
}
const response = await fetch(
`https://api.apidog.com/v1/projects/${projectId}/environments`,
{
headers: { "Authorization": `Bearer ${apiKey}` }
}
);
const environments = await response.json();
return {
content: [{
type: "text",
text: environments.map((e: any) =>
`- ${e.name} (${e.id})${e.isDefault ? " [기본값]" : ""}`
).join("\n")
}]
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
구축한 것
| 구성 요소 | 목적 |
|---|---|
| MCP 서버 | AI 에이전트를 Apidog API에 연결 |
run_test |
테스트 컬렉션을 프로그램적으로 실행 |
validate_schema |
배포 전 OpenAPI 오류 포착 |
list_environments |
사용 가능한 테스트 환경 검색 |
| Zod 유효성 검사 | 타입 안정성 파라미터 처리 |
| Stdio 트랜스포트 | Claude Code, Cursor, 모든 MCP 클라이언트와 호환 |
다음 단계
서버 확장:
- 환경 간 테스트 결과 차이를 비교하는
compare_responses도구 추가 - 과거 테스트 실행을 가져오는
get_test_history추가 - 모의 엔드포인트를 시작/중지하는
trigger_mock_server추가
운영 고려 사항:
- 불안정한 네트워크 요청에 대한 재시도 로직 추가
- API 스로틀링 방지를 위한 속도 제한 구현
- 실패한 도구 호출 디버깅을 위한 로깅 추가
- API 키를 환경 변수 대신 안전한 저장소에 저장
팀과 공유:
@your-org/apidog-mcp-server로 npm에 게시- 필요한 환경 변수 문서화
- 일반 클라이언트를 위한 MCP 구성 예시 포함
일반적인 문제 해결
Claude Code에서 MCP 서버가 로드되지 않음:
~/.claude/settings.json의 경로가 절대 경로인지 확인하세요 (상대 경로 아님).node가 PATH에 있는지 확인하세요:which node- 빌드된
dist/index.js가 존재하는지 확인하세요:ls -la dist/ - Claude Code의 MCP 로그에서 오류를 찾으세요.
구성 후 도구가 나타나지 않음:
- Claude Code를 완전히 다시 시작하세요 (종료 후 다시 열기).
npm run build를 실행하여 TypeScript가 컴파일되었는지 확인하세요.- 세 가지 도구가 모두
server.connect()전에 정의되었는지 확인하세요. - 서버가 오류 없이 시작되는지 확인하세요:
node dist/index.js
API 요청이 401 오류로 실패함:
- 구성에서
APIDOG_API_KEY가 설정되었는지 확인하세요. - 키 값 주위에 불필요한 공백이나 따옴표가 없는지 확인하세요.
- Apidog 계정에 API 접근이 활성화되어 있는지 확인하세요.
- 키를 수동으로 테스트하세요:
curl -H "Authorization: Bearer $APIDOG_API_KEY" https://api.apidog.com/v1/user
Zod 유효성 검사 오류:
- 매개변수 이름이 스키마와 정확히 일치하는지 확인하세요.
- 필수 필드가 제공되었는지 확인하세요 (
projectId에 오타 없음). - 선택적 필드가 스키마에서
.optional()을 사용하는지 확인하세요. - 전체 오류 메시지를 읽으세요 – Zod는 어떤 필드가 실패했는지 알려줍니다.
TypeScript 컴파일 오류:
- 모든 종속성이 설치되었는지 확인하기 위해
npm install을 실행하세요. - TypeScript 버전 확인:
npx tsc --version(5.x여야 합니다). - 클리어 후 다시 빌드:
rm -rf dist && npm run build - 가져오기 응답에서 타입 불일치를 찾으세요 (
as타입 어설션 추가).
MCP 서버 로컬에서 테스트하기
운영 환경에 배포하기 전에 서버를 로컬에서 테스트하세요:
stdio를 사용한 수동 테스트:
# 서버 시작
node dist/index.js
# 다른 터미널에서 테스트 메시지 전송
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | node dist/index.js
예상 출력:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{ "name": "run_test", "description": "...", "inputSchema": {...} },
{ "name": "validate_schema", "description": "...", "inputSchema": {...} },
{ "name": "list_environments", "description": "...", "inputSchema": {...} }
]
}
}
도구 호출 테스트:
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_environments","arguments":{"projectId":"your-project-id"}}}' | node dist/index.js
이제 AI 에이전트가 Apidog의 테스트 기능에 직접 접근할 수 있습니다. 채팅과 브라우저 간에 더 이상 복사-붙여넣기를 할 필요가 없습니다. 더 이상 수동 테스트 실행도 필요 없습니다. 명령을 입력하고 결과를 받으세요.
이것이 MCP의 힘입니다: AI 에이전트를 도메인별 도구로 확장하고, 그들이 해야 할 일 — 더 빠르게 배포할 수 있도록 돕는 일 —을 하도록 하세요.
핵심 요약
- MCP 서버는 AI 에이전트를 외부 API에 연결합니다 — 한 번 구축하여 Claude Code, Cursor 및 모든 MCP 호환 클라이언트에서 사용하세요.
- 세 가지 도구로 대부분의 API 테스트 요구 사항을 충족합니다 — 실행을 위한
run_test, OpenAPI 유효성 검사를 위한validate_schema, 검색을 위한list_environments - Zod 유효성 검사는 잘못된 매개변수를 방지합니다 — 타입 안정성 도구 정의는 API 호출 전에 오류를 포착합니다.
- 구성은 도구별입니다 — Claude Code는
~/.claude/settings.json을 사용하고, Cursor는.cursor/mcp.json을 사용합니다. - 운영 환경에서는 오류 처리가 필요합니다 — 배포 전에 재시도 로직, 속도 제한, 안전한 키 저장소를 추가하세요.
자주 묻는 질문 (FAQ)
AI에서 MCP란 무엇인가요?MCP (Model Context Protocol)는 AI 에이전트가 외부 도구 및 데이터 소스에 접근할 수 있도록 하는 표준화된 프로토콜입니다. AI 에이전트를 위한 플러그인 시스템으로 생각할 수 있습니다.
Apidog용 MCP 서버는 어떻게 생성하나요?@modelcontextprotocol/sdk를 설치하고, Zod 유효성 검사를 사용하여 도구를 정의하고, Apidog API를 호출하는 핸들러를 구현하고, StdioServerTransport를 통해 연결합니다.
Cursor와 함께 사용할 수 있나요?네. 프로젝트 루트에 있는 .cursor/mcp.json에 MCP 서버 구성을 추가하세요. 동일한 서버가 Claude Code, Cursor 및 다른 MCP 클라이언트와 함께 작동합니다.
어떤 도구를 노출해야 하나요?테스트 컬렉션 실행을 위한 run_test, OpenAPI 유효성 검사를 위한 validate_schema, 사용 가능한 환경 가져오기를 위한 list_environments로 시작하세요.
Apidog MCP 서버는 운영 환경에 준비되었나요?튜토리얼 코드는 시작점입니다. 운영 환경에서 사용하기 전에 재시도 로직, 속도 제한, 적절한 오류 처리 및 안전한 API 키 저장을 추가하세요.
Apidog API 키가 필요한가요?네. APIDOG_API_KEY를 환경 변수로 설정하세요. 서버는 런타임에 이를 읽어 API 요청을 인증합니다.
이 MCP 서버를 팀과 공유할 수 있나요?네. 비공개 패키지로 npm에 게시하고, 필요한 환경 변수를 문서화하고, MCP 구성 예시를 포함하세요.
