kmcp — MCP 서버 개발과 배포
- kmcp는 MCP 서버용 CLI와 controller다. CLI는 project 생성·로컬 실행·image build·manifest 생성을, controller는
MCPServer리소스를 Pod로 바꾸는 일을 한다. transportType이 Pod 구조를 정한다.stdio는 gateway 프로세스가 서버를 자식으로 띄우고,http는 서버가 직접 요청을 받는다.- HTTP header를 읽어야 하는 서버는
http여야 한다.stdio서버의 프로세스에는 요청 header가 닿을 길이 없다. - 우리 backend API를 감싸는 MCP 서버는 직접 만든
http서버가 맞는 선택이다.
이 장에서 처음 나오는 말5개
stdio 전송Standard Input/Output Transport- MCP client가 서버를 자식 프로세스로 띄우고 표준 입출력으로 메시지를 주고받는 방식이다. 네트워크 주소가 없다.
http 전송Streamable HTTP Transport- 서버가 독립 프로세스로 떠서 HTTP endpoint 하나로 요청을 받는 방식이다.
FastMCP- MCP 서버를 Python으로 만드는 프레임워크다.
kmcp init python이 이 구조의 project를 만든다. MCP Inspector- MCP 서버에 접속해 tool 목록을 보고 직접 실행해 보는 공식 시험 도구다.
agentgateway- MCP·A2A 트래픽용 proxy다. kmcp는 stdio 서버를 HTTP로 노출하는 변환기로 이 binary를 쓴다.
Tool 연결에서 Agent가 MCP 서버를 URL로 부른다는 것을 봤다. 그 서버를 클러스터에 띄우려면
Dockerfile, Deployment, Service, Secret 연결을 서버마다 만들어야 한다. 게다가 공개된 MCP 서버 다수는
npx·uvx로 실행하는 stdio 방식이라 네트워크 주소가 없어서, Agent Pod가 접속할 HTTP endpoint를 따로
만들어 줘야 한다. kmcp가 이 두 가지를 맡는다.
개발에서 배포까지의 흐름
섹션 제목: “개발에서 배포까지의 흐름”| 명령 | 하는 일 |
|---|---|
kmcp init python <이름> | FastMCP project를 만든다. src/tools/, Dockerfile, kmcp.yaml이 생긴다. Go는 kmcp init go |
kmcp add-tool <이름> | tool 파일의 뼈대를 추가한다 |
kmcp run | 로컬에서 서버를 띄우고 MCP Inspector를 연다 |
kmcp build -t <image> | project의 container image를 만든다 |
kmcp deploy | MCPServer 리소스를 만들어 클러스터에 적용한다. --dry-run·-o <파일>이면 manifest만 만든다 |
kmcp deploy package | image 없이 npx·uvx package로 서버를 띄우는 MCPServer를 만든다 |
kmcp secrets sync <환경> | .env 파일을 Kubernetes Secret으로 올린다 |
설치와 각 명령의 전체 옵션은 kmcp quickstart와
Deploy MCP servers를 따른다. 같은 기능이 kagent mcp 하위 명령으로도
있다(CLI reference).
kagent chart가 kmcp controller를 기본으로 함께 설치하므로, kagent를 쓰는 클러스터에서는 kmcp install을 따로
실행하지 않는다.
MCPServer 리소스
섹션 제목: “MCPServer 리소스”MCPServer 하나가 Deployment·Service·ConfigMap·ServiceAccount가 된다. 필드는
kmcp API reference에 있다.
| 필드 | 뜻 |
|---|---|
deployment.image | 서버 image. package 방식이면 생략한다 |
deployment.cmd · args | 서버를 시작하는 명령 |
deployment.port | Service가 여는 포트. 기본 3000 |
deployment.env · secretRefs | 환경 변수와, 환경 변수로 주입할 Secret 이름 |
deployment.resources · securityContext · replicas | 일반 Deployment 설정 |
transportType | stdio 또는 http |
httpTransport.targetPort · path | http일 때 서버가 듣는 포트와 경로 |
timeout | client 연결 timeout. 기본 30s |
상태는 네 condition으로 본다 — Accepted(spec 유효), ResolvedRefs(image 등 참조 확인),
Programmed(Deployment·Service 생성), Ready(Pod 준비).
kubectl -n agents get mcpserver portal-api-mcp -o jsonpath='{.status.conditions}'stdio와 http — Pod 구조가 다르다
섹션 제목: “stdio와 http — Pod 구조가 다르다”두 전송은 설정 한 줄 차이지만 Pod 안의 모양이 다르다(kmcp v0.4.0 소스에서 확인).
# 공식 문서의 예 — package로 띄우는 fetch 서버apiVersion: kagent.dev/v1alpha1kind: MCPServermetadata: name: mcp-website-fetcher namespace: agentsspec: transportType: stdio stdioTransport: {} deployment: cmd: uvx args: ["mcp-server-fetch"] port: 3000init container가 agentgateway binary를 복사해 두고, main container는 그 binary를 실행한다.
agentgateway가 port에서 HTTP 요청을 받고, MCP session마다 cmd·args로 서버 프로세스를 자식으로 띄워
표준 입출력으로 대화한다.
- 장점: 공개된 stdio 서버를 image 없이 바로 쓸 수 있다.
- 비용: session마다 프로세스를 새로 띄운다. package cache 상태에 따라 시작에 2~8초가 걸려서
timeout기본값이 30초다. - 한계: 서버 프로세스는 표준 입력으로 MCP 메시지만 받는다. HTTP 요청의 header를 볼 수 없다.
# 설명용 예제 — kmcp chart README의 형태를 따랐다apiVersion: kagent.dev/v1alpha1kind: MCPServermetadata: name: portal-api-mcp namespace: agentsspec: transportType: http httpTransport: targetPort: 8080 path: /mcp deployment: image: registry.example.com/mcp/portal-api-mcp@sha256:... cmd: python args: ["src/main.py", "--transport", "http", "--host", "0.0.0.0", "--port", "8080"] port: 8080main container가 서버 명령을 직접 실행한다. 중간 변환기가 없고 Service가 deployment.port로 서버에
바로 연결된다.
- 장점: 프로세스가 계속 떠 있어 session마다 시작 비용이 없다. 요청의 HTTP header를 서버 코드가 읽는다.
- 조건: 서버가 Streamable HTTP를 직접 구현해야 한다.
deployment.port와httpTransport.targetPort를 같은 값으로 두고, 서버가/mcp경로에서 응답하게 한다 — kagent가MCPServer를http://<이름>.<namespace>:<port>/mcp로 호출하기 때문이다. - 실행 인자:
kmcp init python이 만든 서버는 기본이 stdio이고localhost에만 bind한다.args에--transport http --host 0.0.0.0을 넣어야 Pod 밖에서 접속된다.kmcp deploy --transport http가 이 인자를 자동으로 채운다.
| 판단 질문 | stdio | http |
|---|---|---|
| 공개 package를 그대로 쓰는가 | 맞다 | 서버가 HTTP를 지원해야 한다 |
| 호출한 사용자를 구분해야 하는가 | 불가능 | 가능 |
| 호출이 잦은가 | session마다 시작 비용 | 상주 프로세스 |
우리 backend를 감싸는 서버는 http로 만든다
섹션 제목: “우리 backend를 감싸는 서버는 http로 만든다”우리가 만들 MCP 서버의 목적은 backend API를 tool로 노출하는 것이고, backend는 “어느 임직원의 요청인가”를 알아야 한다. 이 정보는 HTTP header로 온다. 따라서 선택지는 정해져 있다.
kmcp init python으로 project를 만들고 Streamable HTTP로 서버를 띄운다.- tool 함수 안에서 들어온 요청의 header를 읽어 backend 호출에 그대로 싣는다.
transportType: http로 배포한다.
FastMCP에서는 fastmcp.server.dependencies의 get_http_request()로
현재 요청의 header를 꺼낸다. 만들고 시험하는 순서는 MCP 서버 로컬 개발 흐름에서,
전체 전파 경로와 backend 쪽 검증은 임직원 토큰 전파에서 이어진다.
Secret 다루기
섹션 제목: “Secret 다루기”MCP 서버가 쓰는 API key 같은 값은 deployment.secretRefs에 Secret 이름을 적어 환경 변수로 받는다.
kmcp secrets sync는 로컬 .env 파일을 Secret으로 올려 주는 편의 명령이다
(Manage secrets).
운영에서 이 명령을 개인 PC에서 실행하면 Secret의 출처가 Git에도 secret 저장소에도 남지 않는다.
GitOps 배포에서는 Secret 값을 외부 secret 저장소에 두고, manifest에는
secretRefs의 이름만 둔다.
이해 확인
섹션 제목: “이해 확인”uvx로 띄운 stdio 서버가 “호출한 사용자의 토큰으로 API를 부르게” 만들 수 있는가? → 없다. 그 프로세스는 HTTP header를 받지 못한다. Secret으로 넣은 고정 자격만 쓸 수 있으므로 모든 사용자가 같은 권한이 된다.http서버를port: 8080으로 배포했는데 Agent의 tool 목록이 비어 있다. 무엇부터 보는가? →MCPServer의Readycondition, 서버가 실제로8080의/mcp에서 응답하는지, 그리고 Agent의toolNames.