{"id":"mcp-4","kind":"workflow","testedWith":[],"durationMin":null,"url":"https://opencurrent.ai/w/mcp-4","title":"MCP 서버를 4단계로 만들기: 조사, 구현, 테스트, 평가 문항 작성","summary":"외부 API를 LLM이 쓸 수 있게 TypeScript(SDK v2) 또는 Python MCP 서버로 만들고, MCP Inspector로 점검한 뒤 평가 질문 10개를 XML로 작성해요.","category":"개발","categoryId":"dev","topics":[{"id":"dev","label":"개발"},{"id":"ops","label":"업무 자동화"},{"id":"research","label":"리서치·학습"}],"difficulty":"고급","tools":["Claude Code","MCP TypeScript SDK v2","FastMCP","MCP Inspector","Zod"],"whenToUse":"외부 API나 서비스를 LLM 에이전트가 도구로 쓸 수 있게 MCP 서버로 감싸야 할 때 써요.","prerequisites":["연동할 외부 서비스의 API 문서를 볼 수 있어야 해요","Node.js와 npm 또는 Python 환경이 필요해요","웹 문서를 가져올 수 있는 도구(WebFetch 등)가 있으면 좋아요"],"inputs":[{"name":"service_name","label":"연동할 서비스","description":"MCP 서버로 감쌀 외부 API나 서비스 이름이에요.","example":"GitHub","required":true},{"name":"language","label":"구현 언어","description":"TypeScript(권장) 또는 Python이에요.","example":"TypeScript","required":true}],"howToFollow":"1. 입력값(inputs)을 사용자에게 받아 프롬프트의 {{이름}} 자리에 넣을 것. 필수값이 없으면 먼저 물어볼 것.\n2. 단계를 순서대로 진행할 것. kind가 \"ai\"면 프롬프트를 실행하고, \"command\"면 사용자의 허락을 받고 명령을 실행하고, \"human\"이면 사용자에게 할 일을 안내하고 끝났는지 물을 것. tool이 있으면 그 도구·모델로 할 것.\n3. 단계에 output(결과 이름)이 있으면 그 단계 결과를 뒤 단계 프롬프트의 {{output}} 자리에 넣을 것.\n4. 단계마다 expected(기대 결과)와 check(확인 방법)로 제대로 됐는지 확인하고, 다르면 다음 단계로 넘어가지 말고 사용자에게 알릴 것. 끝나면 example.output(있으면)과 비교할 것.\n5. 검증 상태가 verified가 아니면 그 사실을 사용자에게 먼저 알릴 것. 답에 url을 출처로 밝힐 것.","steps":[{"n":1,"kind":"human","title":"MCP 설계 원칙 정하기","body":"API 전체를 덮는 도구와 특정 작업용 워크플로 도구 사이에서 균형을 잡아요. 잘 모르겠으면 API 전체를 덮는 쪽을 먼저 고려해요. 도구 이름은 `github_create_issue`처럼 일관된 접두사와 동작 중심으로 짓고, 설명은 짧게 쓰고, 결과는 필터와 페이지 나누기를 지원해요. 오류 메시지에는 다음에 할 일을 구체적으로 넣어요.","expected":"도구 범위, 이름 규칙, 오류 메시지 방침이 정리돼요.","check":"도구 이름에 공통 접두사가 있고 동작이 드러나는지 봐요."},{"n":2,"kind":"human","title":"MCP 프로토콜 문서 읽기","body":"`https://modelcontextprotocol.io/sitemap.xml`에서 필요한 페이지를 찾고, 주소 끝에 `.md`를 붙여 마크다운으로 가져와요. 명세 개요와 구조, 전송 방식(streamable HTTP, stdio), 도구·리소스·프롬프트 정의를 확인해요.","expected":"명세 개요, 전송 방식, 도구 정의의 핵심을 파악해요.","check":"원격 서버는 stateless JSON 기반 Streamable HTTP, 로컬 서버는 stdio를 쓰기로 정했는지 확인해요.","tool":"WebFetch"},{"n":3,"kind":"human","title":"프레임워크 문서 불러오기","body":"언어를 정해요. TypeScript를 권장해요. TypeScript SDK는 새 서버라면 v2(`@modelcontextprotocol/server`)를 쓰고, `package.json`에 이미 `@modelcontextprotocol/sdk`(v1)가 있을 때만 v1을 써요. 한 프로젝트에 둘을 섞지 않아요. Python은 FastMCP를 써요. SDK README와 MCP 모범 사례 문서(`reference/mcp_best_practices.md`), 언어별 가이드를 불러와요.","expected":"사용할 SDK 버전과 참고 문서가 정해져요.","check":"v1과 v2 import가 섞여 있지 않은지 확인해요.","tool":"WebFetch"},{"n":4,"kind":"human","title":"구현 계획 세우기","body":"대상 서비스의 API 문서에서 주요 엔드포인트, 인증 방식, 데이터 모델을 확인해요. 가장 흔한 작업부터 구현할 엔드포인트 목록을 만들어요.","expected":"구현할 엔드포인트 목록과 인증 요구사항이 정리돼요.","check":"자주 쓰는 작업이 목록 앞쪽에 있는지 봐요."},{"n":5,"kind":"ai","title":"프로젝트 구조와 공통 기반 만들기","body":"언어별 가이드에 따라 프로젝트를 세팅해요. 인증이 들어간 API 클라이언트, 오류 처리 도우미, 응답 포맷(JSON/Markdown), 페이지 나누기 지원을 공통 코드로 만들어요.","expected":"공통 유틸리티를 갖춘 프로젝트 뼈대가 만들어져요.","check":"API 호출과 오류 처리가 한곳에 모여 중복이 없는지 봐요.","tool":"Claude Code"},{"n":6,"kind":"ai","title":"도구 구현하기","body":"도구마다 Zod(TypeScript) 또는 Pydantic(Python)으로 입력 스키마를 정의하고, 제약 조건과 필드 설명, 예시를 넣어요. 가능하면 `outputSchema`와 `structuredContent`로 구조화된 결과도 돌려줘요. I/O는 async/await로 처리하고, 해당하는 곳에는 페이지 나누기를 넣어요. `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint` 주석도 달아요.","expected":"엔드포인트별 도구가 스키마, 설명, 주석과 함께 구현돼요.","check":"모든 도구에 입력 스키마와 4가지 주석이 있는지 봐요.","tool":"Claude Code"},{"n":7,"kind":"human","title":"코드 품질 검토하기","body":"중복 코드, 일관된 오류 처리, 전체 타입 적용, 도구 설명의 명확성을 검토해요.","expected":"고칠 점이 정리되고 반영돼요.","check":"같은 로직이 여러 곳에 복사돼 있지 않은지 봐요."},{"n":8,"kind":"command","title":"빌드하고 테스트하기","body":"TypeScript는 `npm run build`로 컴파일을 확인해요. v2 프로젝트는 v2 가이드의 자체 점검 스크립트를 빌드된 서버에 돌려요. Python은 문법을 확인해요. 둘 다 MCP Inspector로 시험해요.","prompt":"npm run build\nnpx @modelcontextprotocol/inspector\n# Python 문법 확인\npython -m py_compile your_server.py","expected":"빌드가 성공하고 Inspector에서 도구가 호출돼요.","check":"Inspector에서 도구 목록이 보이고 호출이 정상 응답하는지 확인해요.","tool":"MCP Inspector"},{"n":9,"kind":"ai","title":"평가 질문 10개 만들기","body":"평가 가이드(`reference/evaluation.md`)를 따라요. 먼저 도구 목록을 살피고, 읽기 전용 작업으로 데이터를 탐색해요. 그다음 복잡하고 현실적인 질문 10개를 만들고 직접 풀어 답을 검증해요. 각 질문은 서로 독립적이고 읽기 전용이며, 여러 번 도구를 호출해야 풀리고, 문자열 비교로 확인되는 하나의 답이 있고, 시간이 지나도 답이 변하지 않아야 해요.","expected":"질문과 검증된 답 10쌍이 만들어져요.","check":"각 질문을 직접 풀어 답이 일치하는지, 답이 시간이 지나도 변하지 않는지 확인해요.","tool":"Claude Code","output":"evaluation_questions"},{"n":10,"kind":"human","title":"평가 XML 파일로 저장하기","body":"질문과 답을 `<evaluation>` 아래 `<qa_pair>` 구조의 XML 파일로 저장해요.","prompt":"<evaluation>\n  <qa_pair>\n    <question>Find discussions about AI model launches with animal codenames. One model needed a specific safety designation that uses the format ASL-X. What number X was being determined for the model named after a spotted wild cat?</question>\n    <answer>3</answer>\n  </qa_pair>\n<!-- More qa_pairs... -->\n</evaluation>","expected":"qa_pair 10개가 담긴 XML 평가 파일이 생겨요.","check":"XML이 올바른 구조이고 질문 10개가 모두 들어 있는지 확인해요."}],"pitfalls":["v1(`@modelcontextprotocol/sdk`)과 v2(`@modelcontextprotocol/server`) 코드를 한 프로젝트에 섞으면 v1 샘플이 v2에 맞지 않아요.","원격 서버를 stateful 세션이나 스트리밍으로 만들면 확장과 유지가 어려워져요. stateless JSON을 권장해요.","평가 질문에 쓰기·삭제 같은 파괴적 작업이 들어가면 안 돼요.","시간이 지나면 답이 바뀌는 질문은 평가에 맞지 않아요.","원본이 가리키는 `reference/` 문서는 이 문서에 없어서, 세부 내용은 원 저장소에서 따로 확인해야 해요."],"sources":[{"title":"mcp-builder","url":"https://github.com/anthropics/skills/blob/2c5958ecac7cee2b583967a2c4981d0f4a75c579/skills/mcp-builder/SKILL.md"}],"verification":{"status":"pending","label":"검수 대기","detail":"사람 확인 전","verifiedBy":0,"lastVerifiedAt":null},"aiDraft":true,"provenance":{"url":"https://github.com/anthropics/skills/blob/2c5958ecac7cee2b583967a2c4981d0f4a75c579/skills/mcp-builder/SKILL.md","repo":"anthropics/skills","path":"skills/mcp-builder/SKILL.md","commit":"2c5958ecac7cee2b583967a2c4981d0f4a75c579","license":"Apache-2.0","author":"anthropics","sourceDate":"2026-10-28T19:20:38+00:00"},"quality":{"score":29,"components":{"result":0.5,"trust":0,"freshness":0.3},"explanation":"확인 없음 · 검수 대기"},"workedFor":0,"trials":{"summary":"","lastAt":null,"total":0,"worked":0,"partial":0,"failed":0,"recent":[]},"asOf":{"date":"2026-10-28T19:20:38+00:00","basis":"source"},"updatedAt":"2026-11-08T16:44:18.971311+00:00"}