[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-cheot-beonjjae-mcp-server-guchwuk-gaebaljayong-siljeongaide":3},{"article":4,"author":50},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":7,"meta_description":16,"focus_keyword":17,"og_image":18,"canonical_url":18,"robots_meta":19,"created_at":15,"updated_at":15,"tags":20,"category_name":30,"related_articles":31},"d0000000-0000-0000-0000-000000000510","a0000000-0000-0000-0000-000000000056","첫 번째 MCP 서버 구축: 개발자를 위한 실전 가이드","cheot-beonjjae-mcp-server-guchwuk-gaebaljayong-siljeongaide","TypeScript와 Python으로 Model Context Protocol(MCP) 서버를 구축하고, Claude Desktop 또는 Cursor에 연결한 뒤, 프로덕션에 배포하는 단계별 실전 튜토리얼.","## Model Context Protocol(MCP)이란?\n\n**Model Context Protocol(MCP)**은 Anthropic이 만든 오픈 스탠다드로, AI 애플리케이션이 외부 데이터 소스 및 도구와 통신하는 방법을 정의합니다. MCP를 **AI 통합을 위한 USB-C**라고 생각하세요. USB-C가 충전, 데이터 전송, 디스플레이 출력을 위한 단일 유니버설 커넥터를 제공하는 것처럼, MCP는 AI 모델을 데이터베이스, API, 파일 시스템 및 기타 서비스에 연결하기 위한 단일 유니버설 프로토콜을 제공합니다. MCP는 모든 AI 애플리케이션과 모든 도구 간의 커스텀 통합 필요성을 제거합니다.\n\n2026년 현재, MCP는 AI-도구 통신의 지배적인 스탠다드가 되었습니다. Gartner는 **2026년 말까지 엔터프라이즈 애플리케이션의 40%가 AI 에이전트를 포함할 것**이라고 예측하며, MCP는 이를 대규모로 실현 가능하게 만드는 프로토콜입니다. MCP 이전에는 AI 모델을 N개의 도구에 연결하려면 N개의 커스텀 통합이 필요했습니다. M개의 AI 애플리케이션과 N개의 도구가 있으면 M x N개의 통합 어댑터가 필요했습니다. MCP는 이를 M + N으로 줄입니다. 모든 AI 앱이 하나의 MCP client를 구현하고, 모든 도구가 하나의 MCP server를 구현합니다.\n\n## MCP 아키텍처: Host, Client, Server, Transport\n\n코드를 작성하기 전에 MCP 아키텍처를 이해하는 것이 필수적입니다. 프로토콜은 네 가지 핵심 역할을 정의합니다:\n\n| 구성 요소 | 역할 | 예시 |\n|-----------|------|------|\n| **Host** | 최종 사용자가 상호작용하는 AI 애플리케이션 | Claude Desktop, Cursor, VS Code Copilot |\n| **Client** | Host 내부의 MCP 프로토콜 핸들러 | Host 애플리케이션에 내장 |\n| **Server** | MCP를 통해 tools, resources, prompts를 노출 | 커스텀 서버(우리가 구축할 것) |\n| **Transport** | Client와 Server 간의 통신 레이어 | stdio, HTTP+SSE, Streamable HTTP |\n\n통신 흐름은 다음과 같이 작동합니다:\n\n1. **Host**가 시작되고 구성된 각 서버에 대해 MCP **Client**를 초기화합니다.\n2. **Client**가 **Transport**(로컬은 stdio, 원격은 HTTP)를 통해 **Server**에 연결합니다.\n3. **Client**가 `initialize` 요청을 보내 프로토콜 버전과 기능을 협상합니다.\n4. **Server**가 사용 가능한 **tools**, **resources**, **prompts**로 응답합니다.\n5. AI 모델이 외부 데이터나 작업이 필요할 때, **Client**가 적절한 서버 메서드를 호출합니다.\n6. **Server**가 작업을 실행하고 JSON-RPC 2.0 메시지로 결과를 반환합니다.\n\n### Tools vs Resources vs Prompts\n\nMCP server는 세 가지 유형의 기능을 노출할 수 있습니다:\n\n- **Tools**는 AI 모델이 호출할 수 있는 함수입니다. 구조화된 입력을 받고 구조화된 출력을 반환합니다. 예: `query_database(sql: string)` 또는 `send_email(to: string, subject: string, body: string)`.\n- **Resources**는 AI 모델이 읽을 수 있는 데이터입니다. URI로 식별되며 콘텐츠를 반환합니다. 예: `file:\u002F\u002F\u002Fpath\u002Fto\u002Fdocument.md` 또는 `postgres:\u002F\u002Flocalhost\u002Fmydb\u002Fusers`.\n- **Prompts**는 서버가 제공하는 재사용 가능한 프롬프트 템플릿입니다. AI가 서버의 도메인과 상호작용하는 방법을 표준화합니다. 예: Jira MCP server를 위한 `summarize_ticket` 프롬프트 템플릿.\n\n## 단계별: TypeScript로 MCP 서버 구축하기\n\nSQLite 데이터베이스에 쿼리를 실행하는 도구를 제공하는 실용적인 MCP 서버를 구축해 봅시다. 이것은 일반적인 사용 사례로, AI 모델에게 애플리케이션 데이터에 대한 안전한 읽기 전용 접근을 제공합니다.\n\n### 1단계: 프로젝트 초기화\n\n```bash\nmkdir mcp-sqlite-server && cd mcp-sqlite-server\nnpm init -y\nnpm install @modelcontextprotocol\u002Fsdk better-sqlite3\nnpm install -D typescript @types\u002Fnode @types\u002Fbetter-sqlite3\nnpx tsc --init\n```\n\n`tsconfig.json` 설정:\n\n```json\n{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"Node16\",\n    \"moduleResolution\": \"Node16\",\n    \"outDir\": \".\u002Fdist\",\n    \"rootDir\": \".\u002Fsrc\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"declaration\": true\n  },\n  \"include\": [\"src\u002F**\u002F*\"]\n}\n```\n\n### 2단계: 서버 구현\n\n`src\u002Findex.ts` 생성:\n\n```typescript\nimport { McpServer } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fmcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fstdio.js\";\nimport Database from \"better-sqlite3\";\nimport { z } from \"zod\";\n\nconst db = new Database(process.env.DB_PATH || \".\u002Fdata.db\", {\n  readonly: true,\n});\n\nconst server = new McpServer({\n  name: \"sqlite-query\",\n  version: \"1.0.0\",\n});\n\nserver.tool(\n  \"query\",\n  \"SQLite 데이터베이스에 대해 읽기 전용 SQL 쿼리 실행\",\n  {\n    sql: z.string().describe(\"실행할 SQL SELECT 쿼리\"),\n  },\n  async ({ sql }) => {\n    const normalized = sql.trim().toUpperCase();\n    if (!normalized.startsWith(\"SELECT\")) {\n      return {\n        content: [{ type: \"text\", text: \"오류: SELECT 쿼리만 허용됩니다.\" }],\n        isError: true,\n      };\n    }\n    try {\n      const rows = db.prepare(sql).all();\n      return {\n        content: [{ type: \"text\", text: JSON.stringify(rows, null, 2) }],\n      };\n    } catch (error) {\n      return {\n        content: [{ type: \"text\", text: `쿼리 오류: ${(error as Error).message}` }],\n        isError: true,\n      };\n    }\n  }\n);\n\nserver.tool(\n  \"list_tables\",\n  \"데이터베이스의 모든 테이블과 스키마 목록 표시\",\n  {},\n  async () => {\n    const tables = db\n      .prepare(\n        `SELECT name, sql FROM sqlite_master\n         WHERE type='table' AND name NOT LIKE 'sqlite_%'\n         ORDER BY name`\n      )\n      .all();\n    return {\n      content: [{ type: \"text\", text: JSON.stringify(tables, null, 2) }],\n    };\n  }\n);\n```\n\n### 3단계: 빌드 및 로컬 테스트\n\n```bash\nnpx tsc\necho '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0\"}}}' | node dist\u002Findex.js\n```\n\n서버의 기능이 포함된 JSON-RPC 응답이 표시됩니다.\n\n### 4단계: Python으로 같은 서버 구축\n\nPython 개발자를 위해, 공식 MCP Python SDK를 사용한 동일한 구현을 보여드립니다:\n\n```python\nimport sqlite3\nimport json\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"sqlite-query\")\nDB_PATH = \"data.db\"\n\n@mcp.tool()\ndef query(sql: str) -> str:\n    \"\"\"SQLite 데이터베이스에 대해 읽기 전용 SQL 쿼리를 실행합니다.\"\"\"\n    normalized = sql.strip().upper()\n    if not normalized.startswith(\"SELECT\"):\n        raise ValueError(\"SELECT 쿼리만 허용됩니다.\")\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(sql)\n        rows = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(rows, indent=2, default=str)\n    finally:\n        conn.close()\n\n@mcp.tool()\ndef list_tables() -> str:\n    \"\"\"데이터베이스의 모든 테이블과 스키마를 나열합니다.\"\"\"\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(\n            \"SELECT name, sql FROM sqlite_master \"\n            \"WHERE type='table' AND name NOT LIKE 'sqlite_%'\"\n        )\n        tables = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(tables, indent=2)\n    finally:\n        conn.close()\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"stdio\")\n```\n\n## Claude Desktop에 연결하기\n\nClaude Desktop은 MCP 서버를 네이티브로 지원합니다. 서버를 연결하려면 Claude Desktop 설정 파일을 편집합니다:\n\n**macOS:** `~\u002FLibrary\u002FApplication Support\u002FClaude\u002Fclaude_desktop_config.json`\n**Windows:** `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n```json\n{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\"\u002Fabsolute\u002Fpath\u002Fto\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \"\u002Fabsolute\u002Fpath\u002Fto\u002Fyour\u002Fdata.db\"\n      }\n    }\n  }\n}\n```\n\nClaude Desktop을 재시작합니다. 채팅 인터페이스에 사용 가능한 MCP 도구를 나타내는 망치 아이콘이 표시됩니다. 이제 Claude에게 \"데이터베이스에 어떤 테이블이 있나요?\" 또는 \"가입일 기준 상위 10명의 사용자를 보여주세요\"와 같이 질문하면 MCP 서버를 사용하여 데이터베이스에 직접 쿼리합니다.\n\n## Cursor에 연결하기\n\nCursor도 MCP 서버를 지원합니다. 프로젝트 루트의 `.cursor\u002Fmcp.json`에 설정을 추가합니다:\n\n```json\n{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\".\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \".\u002Fdata.db\"\n      }\n    }\n  }\n}\n```\n\n저장 후 Cursor를 재시작합니다. MCP 도구가 AI 어시스턴트 패널에 표시되며 코드 생성 및 디버깅 세션 중에 호출할 수 있습니다.\n\n## MCP 서버 테스트 및 디버깅\n\n### MCP Inspector 사용하기\n\nMCP Inspector는 공식 디버깅 도구입니다. 서버와 상호작용하기 위한 웹 인터페이스를 제공합니다:\n\n```bash\nnpx @modelcontextprotocol\u002Finspector node dist\u002Findex.js\n```\n\n`http:\u002F\u002Flocalhost:5173`에서 브라우저 인터페이스가 열리며 다음을 수행할 수 있습니다:\n\n- 등록된 모든 tools, resources, prompts 조회\n- 커스텀 입력으로 도구를 호출하고 응답 검사\n- JSON-RPC 메시지 스트림을 실시간으로 모니터링\n- 잘못된 요청을 보내 오류 처리 테스트\n\n### 일반적인 디버깅 문제\n\n| 문제 | 원인 | 해결책 |\n|------|------|--------|\n| 서버가 Claude Desktop에 나타나지 않음 | 설정 경로 또는 JSON 구문 오류 | JSON 검증, 절대 경로 확인 |\n| \"Tool not found\" 오류 | 연결 전에 도구가 등록되지 않음 | `server.connect()` 호출 전에 도구 등록 |\n| 도구 호출 시 타임아웃 | 진행 없는 장시간 작업 | `server.sendProgress()`로 진행 알림 추가 |\n| stderr 출력이 프로토콜 손상 | console.log가 stdout에 기록(stdio 트랜스포트) | stdio에서는 로깅에 `console.error()` 사용 |\n| 유휴 후 연결 끊김 | 트랜스포트 타임아웃 | 하트비트 구현 또는 HTTP 트랜스포트 사용 |\n\n## 프로덕션 모범 사례\n\n1. **입력 검증**: 항상 도구 입력을 검증하고 살균합니다. TypeScript에서는 Zod 스키마, Python에서는 Pydantic 모델을 사용하여 엄격한 타입 검사를 수행합니다.\n\n2. **기본 읽기 전용**: 읽기 전용 접근으로 시작합니다. 쓰기 기능은 정말 필요한 경우에만 추가하고, 파괴적 작업에는 항상 확인을 요청합니다.\n\n3. **오류 처리**: `isError: true`로 구조화된 오류 메시지를 반환합니다. 내부 스택 트레이스나 데이터베이스 연결 문자열을 노출하지 마세요.\n\n4. **로깅**: 모든 도구 호출을 타임스탬프, 입력, 실행 시간과 함께 기록합니다. stdio 트랜스포트 사용 시 로깅에 stderr(stdout이 아닌)을 사용합니다.\n\n5. **속도 제한**: 제어되지 않는 AI 루프가 백엔드 서비스를 과부하시키는 것을 방지하기 위해 도구별 속도 제한을 구현합니다.\n\n6. **타임아웃**: 모든 도구 핸들러에 실행 타임아웃을 설정합니다. AI 모델이 비용이 많이 드는 쿼리를 트리거하는 도구를 호출할 수 있으므로 인프라를 보호하세요.\n\n7. **환경 분리**: 모든 설정에 환경 변수를 사용합니다. 데이터베이스 URL, API 키, 파일 경로를 하드코딩하지 마세요.\n\n8. **버전 관리**: MCP 서버에 시맨틱 버전 관리를 사용합니다. `initialize` 핸드셰이크에는 버전 협상이 포함되며, 호환성을 깨는 변경에는 메이저 버전 범프가 필요합니다.\n\n## FAQ\n\n**Q: MCP와 function calling의 차이점은 무엇인가요?**\nA: Function calling(OpenAI, Anthropic 등이 사용)은 각 API 요청에 인라인으로 도구를 정의합니다. MCP는 도구 정의를 독립 서버로 외부화하여 MCP 호환 호스트가 발견하고 사용할 수 있게 합니다. Function calling은 요청별이고, MCP는 스테이트풀 세션이 있는 영구적인 프로토콜입니다.\n\n**Q: Claude 외의 모델에서도 MCP를 사용할 수 있나요?**\nA: 네. MCP는 오픈 프로토콜입니다. OpenAI, Google DeepMind, Microsoft는 2026년 초부터 플랫폼에 MCP 지원을 채택했습니다. MCP client를 구현하는 모든 AI 애플리케이션은 어떤 MCP server에도 연결할 수 있습니다.\n\n**Q: MCP는 로컬 도구 전용인가요?**\nA: 아닙니다. stdio 트랜스포트는 로컬 서버용으로 설계되었지만, HTTP+SSE 및 Streamable HTTP 트랜스포트는 원격 MCP 서버를 지원합니다. MCP 서버를 네트워크를 통해 접근 가능한 클라우드 서비스로 배포할 수 있습니다.\n\n**Q: MCP는 인증을 어떻게 처리하나요?**\nA: 원격 서버에는 OAuth 2.0을 지원합니다. 로컬 stdio 서버는 호스트 프로세스의 보안 컨텍스트를 상속합니다. 엔터프라이즈 배포에서는 MCP 게이트웨이가 인증 및 권한 부여를 중앙화할 수 있습니다.\n\n**Q: MCP 서버를 구축하는 데 어떤 언어를 사용할 수 있나요?**\nA: 공식 SDK는 TypeScript, Python, Java, Kotlin, C#, Swift에서 사용 가능합니다. 커뮤니티 SDK에는 Rust, Go, Ruby, PHP가 포함됩니다. 프로토콜은 언어에 구애받지 않으며, stdio 또는 HTTP를 통해 JSON-RPC를 읽고 쓸 수 있는 모든 언어로 MCP 서버를 구현할 수 있습니다.\n\n**Q: 호스트를 재시작하지 않고 MCP 서버를 업데이트하는 방법은?**\nA: MCP는 기능 변경 알림을 지원합니다. 서버의 도구가 변경되면 서버가 `notifications\u002Ftools\u002Flist_changed` 메시지를 보내 client가 도구 목록을 다시 가져오도록 합니다. stdio 서버의 경우 보통 호스트를 재시작해야 합니다. HTTP 서버는 호스트 재시작 없이 업데이트할 수 있습니다.","\u003Ch2 id=\"model-context-protocol-mcp\">Model Context Protocol(MCP)이란?\u003C\u002Fh2>\n\u003Cp>**Model Context Protocol(MCP)**은 Anthropic이 만든 오픈 스탠다드로, AI 애플리케이션이 외부 데이터 소스 및 도구와 통신하는 방법을 정의합니다. MCP를 \u003Cstrong>AI 통합을 위한 USB-C\u003C\u002Fstrong>라고 생각하세요. USB-C가 충전, 데이터 전송, 디스플레이 출력을 위한 단일 유니버설 커넥터를 제공하는 것처럼, MCP는 AI 모델을 데이터베이스, API, 파일 시스템 및 기타 서비스에 연결하기 위한 단일 유니버설 프로토콜을 제공합니다. MCP는 모든 AI 애플리케이션과 모든 도구 간의 커스텀 통합 필요성을 제거합니다.\u003C\u002Fp>\n\u003Cp>2026년 현재, MCP는 AI-도구 통신의 지배적인 스탠다드가 되었습니다. Gartner는 \u003Cstrong>2026년 말까지 엔터프라이즈 애플리케이션의 40%가 AI 에이전트를 포함할 것\u003C\u002Fstrong>이라고 예측하며, MCP는 이를 대규모로 실현 가능하게 만드는 프로토콜입니다. MCP 이전에는 AI 모델을 N개의 도구에 연결하려면 N개의 커스텀 통합이 필요했습니다. M개의 AI 애플리케이션과 N개의 도구가 있으면 M x N개의 통합 어댑터가 필요했습니다. MCP는 이를 M + N으로 줄입니다. 모든 AI 앱이 하나의 MCP client를 구현하고, 모든 도구가 하나의 MCP server를 구현합니다.\u003C\u002Fp>\n\u003Ch2 id=\"mcp-host-client-server-transport\">MCP 아키텍처: Host, Client, Server, Transport\u003C\u002Fh2>\n\u003Cp>코드를 작성하기 전에 MCP 아키텍처를 이해하는 것이 필수적입니다. 프로토콜은 네 가지 핵심 역할을 정의합니다:\u003C\u002Fp>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>구성 요소\u003C\u002Fth>\u003Cth>역할\u003C\u002Fth>\u003Cth>예시\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>\u003Cstrong>Host\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>최종 사용자가 상호작용하는 AI 애플리케이션\u003C\u002Ftd>\u003Ctd>Claude Desktop, Cursor, VS Code Copilot\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>Client\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>Host 내부의 MCP 프로토콜 핸들러\u003C\u002Ftd>\u003Ctd>Host 애플리케이션에 내장\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>Server\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>MCP를 통해 tools, resources, prompts를 노출\u003C\u002Ftd>\u003Ctd>커스텀 서버(우리가 구축할 것)\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>Transport\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>Client와 Server 간의 통신 레이어\u003C\u002Ftd>\u003Ctd>stdio, HTTP+SSE, Streamable HTTP\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>통신 흐름은 다음과 같이 작동합니다:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Host\u003C\u002Fstrong>가 시작되고 구성된 각 서버에 대해 MCP \u003Cstrong>Client\u003C\u002Fstrong>를 초기화합니다.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Client\u003C\u002Fstrong>가 \u003Cstrong>Transport\u003C\u002Fstrong>(로컬은 stdio, 원격은 HTTP)를 통해 \u003Cstrong>Server\u003C\u002Fstrong>에 연결합니다.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Client\u003C\u002Fstrong>가 \u003Ccode>initialize\u003C\u002Fcode> 요청을 보내 프로토콜 버전과 기능을 협상합니다.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Server\u003C\u002Fstrong>가 사용 가능한 \u003Cstrong>tools\u003C\u002Fstrong>, \u003Cstrong>resources\u003C\u002Fstrong>, \u003Cstrong>prompts\u003C\u002Fstrong>로 응답합니다.\u003C\u002Fli>\n\u003Cli>AI 모델이 외부 데이터나 작업이 필요할 때, \u003Cstrong>Client\u003C\u002Fstrong>가 적절한 서버 메서드를 호출합니다.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Server\u003C\u002Fstrong>가 작업을 실행하고 JSON-RPC 2.0 메시지로 결과를 반환합니다.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>Tools vs Resources vs Prompts\u003C\u002Fh3>\n\u003Cp>MCP server는 세 가지 유형의 기능을 노출할 수 있습니다:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Tools\u003C\u002Fstrong>는 AI 모델이 호출할 수 있는 함수입니다. 구조화된 입력을 받고 구조화된 출력을 반환합니다. 예: \u003Ccode>query_database(sql: string)\u003C\u002Fcode> 또는 \u003Ccode>send_email(to: string, subject: string, body: string)\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Resources\u003C\u002Fstrong>는 AI 모델이 읽을 수 있는 데이터입니다. URI로 식별되며 콘텐츠를 반환합니다. 예: \u003Ccode>file:\u002F\u002F\u002Fpath\u002Fto\u002Fdocument.md\u003C\u002Fcode> 또는 \u003Ccode>postgres:\u002F\u002Flocalhost\u002Fmydb\u002Fusers\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Prompts\u003C\u002Fstrong>는 서버가 제공하는 재사용 가능한 프롬프트 템플릿입니다. AI가 서버의 도메인과 상호작용하는 방법을 표준화합니다. 예: Jira MCP server를 위한 \u003Ccode>summarize_ticket\u003C\u002Fcode> 프롬프트 템플릿.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"typescript-mcp\">단계별: TypeScript로 MCP 서버 구축하기\u003C\u002Fh2>\n\u003Cp>SQLite 데이터베이스에 쿼리를 실행하는 도구를 제공하는 실용적인 MCP 서버를 구축해 봅시다. 이것은 일반적인 사용 사례로, AI 모델에게 애플리케이션 데이터에 대한 안전한 읽기 전용 접근을 제공합니다.\u003C\u002Fp>\n\u003Ch3>1단계: 프로젝트 초기화\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">mkdir mcp-sqlite-server &amp;&amp; cd mcp-sqlite-server\nnpm init -y\nnpm install @modelcontextprotocol\u002Fsdk better-sqlite3\nnpm install -D typescript @types\u002Fnode @types\u002Fbetter-sqlite3\nnpx tsc --init\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>tsconfig.json\u003C\u002Fcode> 설정:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"Node16\",\n    \"moduleResolution\": \"Node16\",\n    \"outDir\": \".\u002Fdist\",\n    \"rootDir\": \".\u002Fsrc\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"declaration\": true\n  },\n  \"include\": [\"src\u002F**\u002F*\"]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>2단계: 서버 구현\u003C\u002Fh3>\n\u003Cp>\u003Ccode>src\u002Findex.ts\u003C\u002Fcode> 생성:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-typescript\">import { McpServer } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fmcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fstdio.js\";\nimport Database from \"better-sqlite3\";\nimport { z } from \"zod\";\n\nconst db = new Database(process.env.DB_PATH || \".\u002Fdata.db\", {\n  readonly: true,\n});\n\nconst server = new McpServer({\n  name: \"sqlite-query\",\n  version: \"1.0.0\",\n});\n\nserver.tool(\n  \"query\",\n  \"SQLite 데이터베이스에 대해 읽기 전용 SQL 쿼리 실행\",\n  {\n    sql: z.string().describe(\"실행할 SQL SELECT 쿼리\"),\n  },\n  async ({ sql }) =&gt; {\n    const normalized = sql.trim().toUpperCase();\n    if (!normalized.startsWith(\"SELECT\")) {\n      return {\n        content: [{ type: \"text\", text: \"오류: SELECT 쿼리만 허용됩니다.\" }],\n        isError: true,\n      };\n    }\n    try {\n      const rows = db.prepare(sql).all();\n      return {\n        content: [{ type: \"text\", text: JSON.stringify(rows, null, 2) }],\n      };\n    } catch (error) {\n      return {\n        content: [{ type: \"text\", text: `쿼리 오류: ${(error as Error).message}` }],\n        isError: true,\n      };\n    }\n  }\n);\n\nserver.tool(\n  \"list_tables\",\n  \"데이터베이스의 모든 테이블과 스키마 목록 표시\",\n  {},\n  async () =&gt; {\n    const tables = db\n      .prepare(\n        `SELECT name, sql FROM sqlite_master\n         WHERE type='table' AND name NOT LIKE 'sqlite_%'\n         ORDER BY name`\n      )\n      .all();\n    return {\n      content: [{ type: \"text\", text: JSON.stringify(tables, null, 2) }],\n    };\n  }\n);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>3단계: 빌드 및 로컬 테스트\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">npx tsc\necho '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0\"}}}' | node dist\u002Findex.js\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>서버의 기능이 포함된 JSON-RPC 응답이 표시됩니다.\u003C\u002Fp>\n\u003Ch3>4단계: Python으로 같은 서버 구축\u003C\u002Fh3>\n\u003Cp>Python 개발자를 위해, 공식 MCP Python SDK를 사용한 동일한 구현을 보여드립니다:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-python\">import sqlite3\nimport json\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"sqlite-query\")\nDB_PATH = \"data.db\"\n\n@mcp.tool()\ndef query(sql: str) -&gt; str:\n    \"\"\"SQLite 데이터베이스에 대해 읽기 전용 SQL 쿼리를 실행합니다.\"\"\"\n    normalized = sql.strip().upper()\n    if not normalized.startswith(\"SELECT\"):\n        raise ValueError(\"SELECT 쿼리만 허용됩니다.\")\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(sql)\n        rows = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(rows, indent=2, default=str)\n    finally:\n        conn.close()\n\n@mcp.tool()\ndef list_tables() -&gt; str:\n    \"\"\"데이터베이스의 모든 테이블과 스키마를 나열합니다.\"\"\"\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(\n            \"SELECT name, sql FROM sqlite_master \"\n            \"WHERE type='table' AND name NOT LIKE 'sqlite_%'\"\n        )\n        tables = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(tables, indent=2)\n    finally:\n        conn.close()\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"stdio\")\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"claude-desktop\">Claude Desktop에 연결하기\u003C\u002Fh2>\n\u003Cp>Claude Desktop은 MCP 서버를 네이티브로 지원합니다. 서버를 연결하려면 Claude Desktop 설정 파일을 편집합니다:\u003C\u002Fp>\n\u003Cp>\u003Cstrong>macOS:\u003C\u002Fstrong> \u003Ccode>~\u002FLibrary\u002FApplication Support\u002FClaude\u002Fclaude_desktop_config.json\u003C\u002Fcode>\n\u003Cstrong>Windows:\u003C\u002Fstrong> \u003Ccode>%APPDATA%\\Claude\\claude_desktop_config.json\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\"\u002Fabsolute\u002Fpath\u002Fto\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \"\u002Fabsolute\u002Fpath\u002Fto\u002Fyour\u002Fdata.db\"\n      }\n    }\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Claude Desktop을 재시작합니다. 채팅 인터페이스에 사용 가능한 MCP 도구를 나타내는 망치 아이콘이 표시됩니다. 이제 Claude에게 “데이터베이스에 어떤 테이블이 있나요?” 또는 “가입일 기준 상위 10명의 사용자를 보여주세요“와 같이 질문하면 MCP 서버를 사용하여 데이터베이스에 직접 쿼리합니다.\u003C\u002Fp>\n\u003Ch2 id=\"cursor\">Cursor에 연결하기\u003C\u002Fh2>\n\u003Cp>Cursor도 MCP 서버를 지원합니다. 프로젝트 루트의 \u003Ccode>.cursor\u002Fmcp.json\u003C\u002Fcode>에 설정을 추가합니다:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\".\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \".\u002Fdata.db\"\n      }\n    }\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>저장 후 Cursor를 재시작합니다. MCP 도구가 AI 어시스턴트 패널에 표시되며 코드 생성 및 디버깅 세션 중에 호출할 수 있습니다.\u003C\u002Fp>\n\u003Ch2 id=\"mcp\">MCP 서버 테스트 및 디버깅\u003C\u002Fh2>\n\u003Ch3>MCP Inspector 사용하기\u003C\u002Fh3>\n\u003Cp>MCP Inspector는 공식 디버깅 도구입니다. 서버와 상호작용하기 위한 웹 인터페이스를 제공합니다:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">npx @modelcontextprotocol\u002Finspector node dist\u002Findex.js\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>http:\u002F\u002Flocalhost:5173\u003C\u002Fcode>에서 브라우저 인터페이스가 열리며 다음을 수행할 수 있습니다:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>등록된 모든 tools, resources, prompts 조회\u003C\u002Fli>\n\u003Cli>커스텀 입력으로 도구를 호출하고 응답 검사\u003C\u002Fli>\n\u003Cli>JSON-RPC 메시지 스트림을 실시간으로 모니터링\u003C\u002Fli>\n\u003Cli>잘못된 요청을 보내 오류 처리 테스트\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>일반적인 디버깅 문제\u003C\u002Fh3>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>문제\u003C\u002Fth>\u003Cth>원인\u003C\u002Fth>\u003Cth>해결책\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>서버가 Claude Desktop에 나타나지 않음\u003C\u002Ftd>\u003Ctd>설정 경로 또는 JSON 구문 오류\u003C\u002Ftd>\u003Ctd>JSON 검증, 절대 경로 확인\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>“Tool not found” 오류\u003C\u002Ftd>\u003Ctd>연결 전에 도구가 등록되지 않음\u003C\u002Ftd>\u003Ctd>\u003Ccode>server.connect()\u003C\u002Fcode> 호출 전에 도구 등록\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>도구 호출 시 타임아웃\u003C\u002Ftd>\u003Ctd>진행 없는 장시간 작업\u003C\u002Ftd>\u003Ctd>\u003Ccode>server.sendProgress()\u003C\u002Fcode>로 진행 알림 추가\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>stderr 출력이 프로토콜 손상\u003C\u002Ftd>\u003Ctd>console.log가 stdout에 기록(stdio 트랜스포트)\u003C\u002Ftd>\u003Ctd>stdio에서는 로깅에 \u003Ccode>console.error()\u003C\u002Fcode> 사용\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>유휴 후 연결 끊김\u003C\u002Ftd>\u003Ctd>트랜스포트 타임아웃\u003C\u002Ftd>\u003Ctd>하트비트 구현 또는 HTTP 트랜스포트 사용\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Ch2 id=\"\">프로덕션 모범 사례\u003C\u002Fh2>\n\u003Col>\n\u003Cli>\n\u003Cp>\u003Cstrong>입력 검증\u003C\u002Fstrong>: 항상 도구 입력을 검증하고 살균합니다. TypeScript에서는 Zod 스키마, Python에서는 Pydantic 모델을 사용하여 엄격한 타입 검사를 수행합니다.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>기본 읽기 전용\u003C\u002Fstrong>: 읽기 전용 접근으로 시작합니다. 쓰기 기능은 정말 필요한 경우에만 추가하고, 파괴적 작업에는 항상 확인을 요청합니다.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>오류 처리\u003C\u002Fstrong>: \u003Ccode>isError: true\u003C\u002Fcode>로 구조화된 오류 메시지를 반환합니다. 내부 스택 트레이스나 데이터베이스 연결 문자열을 노출하지 마세요.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>로깅\u003C\u002Fstrong>: 모든 도구 호출을 타임스탬프, 입력, 실행 시간과 함께 기록합니다. stdio 트랜스포트 사용 시 로깅에 stderr(stdout이 아닌)을 사용합니다.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>속도 제한\u003C\u002Fstrong>: 제어되지 않는 AI 루프가 백엔드 서비스를 과부하시키는 것을 방지하기 위해 도구별 속도 제한을 구현합니다.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>타임아웃\u003C\u002Fstrong>: 모든 도구 핸들러에 실행 타임아웃을 설정합니다. AI 모델이 비용이 많이 드는 쿼리를 트리거하는 도구를 호출할 수 있으므로 인프라를 보호하세요.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>환경 분리\u003C\u002Fstrong>: 모든 설정에 환경 변수를 사용합니다. 데이터베이스 URL, API 키, 파일 경로를 하드코딩하지 마세요.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>버전 관리\u003C\u002Fstrong>: MCP 서버에 시맨틱 버전 관리를 사용합니다. \u003Ccode>initialize\u003C\u002Fcode> 핸드셰이크에는 버전 협상이 포함되며, 호환성을 깨는 변경에는 메이저 버전 범프가 필요합니다.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"faq\">FAQ\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Q: MCP와 function calling의 차이점은 무엇인가요?\u003C\u002Fstrong>\nA: Function calling(OpenAI, Anthropic 등이 사용)은 각 API 요청에 인라인으로 도구를 정의합니다. MCP는 도구 정의를 독립 서버로 외부화하여 MCP 호환 호스트가 발견하고 사용할 수 있게 합니다. Function calling은 요청별이고, MCP는 스테이트풀 세션이 있는 영구적인 프로토콜입니다.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q: Claude 외의 모델에서도 MCP를 사용할 수 있나요?\u003C\u002Fstrong>\nA: 네. MCP는 오픈 프로토콜입니다. OpenAI, Google DeepMind, Microsoft는 2026년 초부터 플랫폼에 MCP 지원을 채택했습니다. MCP client를 구현하는 모든 AI 애플리케이션은 어떤 MCP server에도 연결할 수 있습니다.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q: MCP는 로컬 도구 전용인가요?\u003C\u002Fstrong>\nA: 아닙니다. stdio 트랜스포트는 로컬 서버용으로 설계되었지만, HTTP+SSE 및 Streamable HTTP 트랜스포트는 원격 MCP 서버를 지원합니다. MCP 서버를 네트워크를 통해 접근 가능한 클라우드 서비스로 배포할 수 있습니다.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q: MCP는 인증을 어떻게 처리하나요?\u003C\u002Fstrong>\nA: 원격 서버에는 OAuth 2.0을 지원합니다. 로컬 stdio 서버는 호스트 프로세스의 보안 컨텍스트를 상속합니다. 엔터프라이즈 배포에서는 MCP 게이트웨이가 인증 및 권한 부여를 중앙화할 수 있습니다.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q: MCP 서버를 구축하는 데 어떤 언어를 사용할 수 있나요?\u003C\u002Fstrong>\nA: 공식 SDK는 TypeScript, Python, Java, Kotlin, C#, Swift에서 사용 가능합니다. 커뮤니티 SDK에는 Rust, Go, Ruby, PHP가 포함됩니다. 프로토콜은 언어에 구애받지 않으며, stdio 또는 HTTP를 통해 JSON-RPC를 읽고 쓸 수 있는 모든 언어로 MCP 서버를 구현할 수 있습니다.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q: 호스트를 재시작하지 않고 MCP 서버를 업데이트하는 방법은?\u003C\u002Fstrong>\nA: MCP는 기능 변경 알림을 지원합니다. 서버의 도구가 변경되면 서버가 \u003Ccode>notifications\u002Ftools\u002Flist_changed\u003C\u002Fcode> 메시지를 보내 client가 도구 목록을 다시 가져오도록 합니다. stdio 서버의 경우 보통 호스트를 재시작해야 합니다. HTTP 서버는 호스트 재시작 없이 업데이트할 수 있습니다.\u003C\u002Fp>\n","ko","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:38.916618Z","TypeScript와 Python으로 MCP 서버를 구축하는 단계별 튜토리얼. Claude Desktop 또는 Cursor에 연결하고, MCP Inspector로 테스트하고, 프로덕션에 배포.","mcp server 튜토리얼",null,"index, follow",[21,26],{"id":22,"name":23,"slug":24,"created_at":25},"c0000000-0000-0000-0000-000000000008","AI","ai","2026-03-28T10:44:21.513630Z",{"id":27,"name":28,"slug":29,"created_at":25},"c0000000-0000-0000-0000-000000000002","TypeScript","typescript","엔지니어링",[32,38,44],{"id":33,"title":34,"slug":35,"excerpt":36,"locale":12,"category_name":30,"published_at":37},"d0000000-0000-0000-0000-000000000674","2026년, Bali가 동남아시아의 임팩트 테크 허브가 되고 있는 이유","bali-2026-dongnamasia-impaekteu-tekeu-heobeu-iyu","Bali는 동남아시아 스타트업 생태계에서 16위를 차지하고 있습니다. Web3 빌더, AI 지속가능성 스타트업, 에코 여행 테크 기업이 집중되면서, 이 섬은 지역 임팩트 테크의 수도로 자리매김하고 있습니다.","2026-03-28T10:44:49.294484Z",{"id":39,"title":40,"slug":41,"excerpt":42,"locale":12,"category_name":30,"published_at":43},"d0000000-0000-0000-0000-000000000673","ASEAN 데이터 보호 패치워크: 개발자를 위한 컴플라이언스 체크리스트","asean-deiteo-boho-paechiwokeu-gaebaljaleul-wihan-keompeullaieonseuchekeuriseuteu","7개 ASEAN 국가가 포괄적인 데이터 보호법을 시행하고 있으며, 각각 다른 동의 모델, 현지화 요건, 벌칙 구조를 가지고 있습니다. 다중 국가 애플리케이션을 구축하는 개발자를 위한 실용적인 컴플라이언스 체크리스트입니다.","2026-03-28T10:44:49.286400Z",{"id":45,"title":46,"slug":47,"excerpt":48,"locale":12,"category_name":30,"published_at":49},"d0000000-0000-0000-0000-000000000672","Indonesia 290억 달러 디지털 전환: 소프트웨어 기업을 위한 기회","indonesia-290eok-dallleo-dijiteol-jeonhwan-sopeuteuweo-gieopui-gihoe","Indonesia IT 서비스 시장은 2026년 290.3억 달러에 달할 것으로 예상되며, 이는 2025년 243.7억 달러에서 증가한 수치입니다. 클라우드 인프라, AI, 전자상거래, 데이터센터가 동남아시아에서 가장 빠른 성장을 주도하고 있습니다.","2026-03-28T10:44:49.265609Z",{"id":13,"name":51,"slug":52,"bio":53,"photo_url":18,"linkedin":18,"role":54,"created_at":55,"updated_at":55},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]