BYO Agent 로컬 개발 흐름
- BYO Agent는 클러스터 없이 개발한다.
kagent run이 docker-compose로 Agent와 MCP 서버를 함께 띄우고 터미널 채팅을 연다. - MCP 서버는 “클러스터의 것을 호출”하는 것이 아니라 로컬에 같이 띄운다. 서버 이름이 로컬에서는 compose 서비스 이름, 클러스터에서는 Service 이름이 되어 같은 주소로 풀린다.
- 로컬 실행에는 kagent controller가 없다. 인증, 사용자 ID, session 저장, 임직원 토큰 전파는 클러스터에서 확인한다.
kagent init이 만들어 주는 것은 ADK + Python project뿐이다. 다른 프레임워크는8080포트의 A2A 서버라는 계약만 지켜 직접 구성한다.- 배포는
kagent deploy --dry-run의 출력에서 Secret을 빼고 image를 digest로 바꿔 PR로 넘긴다.
이 장에서 처음 나오는 말4개
docker-compose- 여러 container를 파일 하나로 정의해 함께 띄우는 도구다. 로컬에서 Agent와 MCP 서버를 같은 네트워크에 올리는 데 쓴다.
kagent-adk- kagent가 제공하는 Python package이자 base image다. ADK로 만든 Agent를 A2A 서버로 감싸 실행한다.
in-memory session- 대화 상태를 프로세스 메모리에만 두는 방식이다. 프로세스가 끝나면 사라진다. 로컬 실행에서 DB 대신 쓴다.
toolset- ADK에서 MCP 서버 하나에 대한 연결과 그 서버의 tool 묶음을 나타내는 객체다.
Agent 페이지에서 BYO Agent는 “우리가 만든 image를 kagent가 배포만 한다”고 했다. 그러면 그 image는 어디서 어떻게 만들고 시험하는가. Agent가 클러스터의 MCP 서버를 불러야 한다면 개발할 때마다 클러스터에 올려야 하는가. 이 페이지가 답하는 질문이다.
답은 “로컬에서 대부분 된다”이다. 다만 로컬과 클러스터의 구성이 다르므로, 어디까지가 로컬에서 확인되는지를 알고 써야 한다.
로컬과 클러스터의 구성 차이
섹션 제목: “로컬과 클러스터의 구성 차이”로컬 (kagent run) | 클러스터 | |
|---|---|---|
| Agent 앞에 있는 것 | 없음. CLI가 localhost:8080에 직접 접속 | kagent controller |
| 인증·사용자 ID | 없음 | controller가 Authorization·X-User-Id를 붙여 넘긴다 |
| session 저장 | 메모리. 끝나면 사라진다 | controller를 거쳐 PostgreSQL |
| MCP 서버 | compose 서비스 | MCPServer·RemoteMCPServer의 Service |
| 모델 key | shell의 환경 변수 | Secret |
같은 것은 Agent의 코드와 image다. 로컬에서 확인하는 대상도 그것이다.
전체 순서
섹션 제목: “전체 순서”kagent init adk python <이름>으로 project를 만든다.agent.py에 지시문과 tool을 쓴다.- 필요한 MCP 서버를
kagent add-mcp로 등록한다. kagent build후kagent run으로 띄워 터미널에서 대화한다. 고치고 다시 build·run을 반복한다.kagent deploy --dry-run으로 manifest를 만들어 GitOps PR로 넘긴다.
준비물은 kagent CLI, Python, Docker와 docker compose다.
project 구조
섹션 제목: “project 구조”kagent init adk python reviewer --model-provider OpenAI --model-name gpt-4디렉터리reviewer/
디렉터리reviewer/
- agent.py Agent 정의. 우리가 고치는 파일
- mcp_tools.py 자동 생성.
kagent.yaml의 MCP 서버를 toolset으로 만든다 - agent-card.json Agent Card
- __init__.py
- kagent.yaml Agent 이름·모델·MCP 서버 목록
- docker-compose.yaml 자동 생성
- Dockerfile 자동 생성
- pyproject.toml
- README.md
Agent 이름에는 영문자·숫자·밑줄만 쓸 수 있다(CLI가 검사한다). 이 이름이 Python package 이름이자 배포될
Agent 리소스의 이름이 되는데, Kubernetes 리소스 이름에는 밑줄을 쓸 수 없다. reviewer처럼 구분자 없는
소문자 이름이 양쪽에서 안전하다.
mcp_tools.py·docker-compose.yaml·Dockerfile은 머리에 “AUTOGENERATED FILE: DO NOT EDIT”가 적혀 있고,
CLI가 kagent.yaml에서 다시 만든다. 직접 고치면 다음 kagent add-mcp나 build에서 덮어써진다.
고치는 곳은 agent.py와 kagent.yaml이다.
Dockerfile은 ghcr.io/kagent-dev/kagent/kagent-adk image를 base로 쓴다. 사내 mirror를 쓰려면 build 인자
DOCKER_REGISTRY로 registry를 바꾼다.
Agent를 쓴다
섹션 제목: “Agent를 쓴다”생성된 agent.py는 ADK의 Agent 객체 하나를 root_agent로 내보낸다. 지시문, 모델, tool이 여기에 있다.
# 생성된 agent.py의 끝부분 — 발췌mcp_tools = get_mcp_tools()root_agent = Agent( model=create_model(), name="reviewer_agent", description="reviewer agent.", instruction=""" ... """, tools=[roll_die, check_prime] + (mcp_tools if mcp_tools else []),)- Python 함수를
tools에 넣으면 그 함수가 tool이 된다. 예제의roll_die·check_prime을 지우고 우리 함수를 넣는다. get_mcp_tools()가kagent.yaml에 등록된 MCP 서버 전체를 toolset으로 돌려준다.- 모델은 코드에서 정한다. Declarative Agent의
ModelConfig를 쓰지 않는다.
이것이 BYO Agent의 tool이 리소스에 드러나지 않는 이유다. tool 목록이 manifest가 아니라 이 파일에 있다.
MCP 서버를 붙인다
섹션 제목: “MCP 서버를 붙인다”kagent add-mcp가 kagent.yaml에 서버를 등록하고 mcp_tools.py와 docker-compose.yaml을 다시 만든다.
방식은 둘이다.
| command 방식 | remote 방식 | |
|---|---|---|
| 등록 | kagent add-mcp fetch --command uvx --arg mcp-server-fetch | kagent add-mcp portal --remote <URL> |
| 로컬에서 | compose 서비스로 함께 뜬다 | 뜨지 않는다. 적은 URL로 접속한다 |
| Agent가 접속하는 주소 | http://<서버 이름>:3000/mcp | 적은 URL 그대로 |
| 배포하면 | MCPServer(stdio) 리소스가 된다 | RemoteMCPServer 리소스가 된다 |
| header | — | --header KEY=VALUE. 값에 ${VAR}로 환경 변수를 쓸 수 있다 |
command 방식은 주소가 양쪽에서 같다
섹션 제목: “command 방식은 주소가 양쪽에서 같다”command 방식의 주소 http://<서버 이름>:3000/mcp는 고정 규칙이다(생성되는 mcp_tools.py에서 확인).
- 로컬: compose가
<서버 이름>이라는 서비스를 만들고, 같은 compose 네트워크의 Agent가 그 이름으로 접속한다. - 클러스터:
kagent deploy가 같은 이름의MCPServer를 만들고, kmcp가 같은 이름의 Service를 만든다. 같은 namespace의 Agent Pod가 그 이름으로 접속한다.
그래서 코드를 바꾸지 않고 양쪽에서 동작한다. “클러스터에 떠 있는 MCP를 호출해야 해서 로컬 시험이 안 된다”는 걱정은 이 방식에서는 생기지 않는다. MCP 서버도 로컬에 뜬다.
쓸 tool만 고른다
섹션 제목: “쓸 tool만 고른다”기본으로는 서버의 모든 tool이 Agent에 주어진다. agent.py에서 좁힌다.
# 발췌 — fetch 서버에서 fetch tool만mcp_tools = get_mcp_tools(server_filters={"fetch": ["fetch"]})Declarative Agent의 toolNames에 해당하는 것이 BYO에서는 이 한 줄이다.
allowlist가 manifest가 아니라 코드에 있으므로, PR 리뷰에서 이 줄을 본다.
우리 http MCP 서버를 붙일 때
섹션 제목: “우리 http MCP 서버를 붙일 때”앞 페이지에서 만든 portal-api-mcp는 http 전송이라 command 방식에 맞지 않는다.
command 방식은 stdio 서버를 gateway로 감싸 3000 포트에 여는 구조이기 때문이다. remote 방식으로 붙인다.
걸리는 점은 remote 방식의 URL이 kagent.yaml에 고정된다는 것이다. ${VAR} 치환은 header에만 적용되고
URL에는 적용되지 않는다(소스에서 확인).
| 환경 | Agent container에서 본 MCP 서버 주소 |
|---|---|
| 로컬 | host에서 띄운 서버: http://host.docker.internal:8080/mcp |
| 클러스터 | http://portal-api-mcp.agents:8080/mcp |
두 주소가 다르므로 kagent.yaml 하나로 양쪽을 덮을 수 없다. 선택지는 둘이다.
get_mcp_tools()를 쓰지 않고agent.py에서 toolset을 직접 만들며 URL을 환경 변수로 받는다. 로컬과 클러스터에서 환경 변수만 다르게 준다. 배포 manifest의 MCP 서버 리소스는 우리가 따로 관리한다.- 로컬에서도 클러스터와 같은 이름·포트로 풀리게 한다.
docker-compose.yaml은 자동 생성 파일이라 직접 고칠 수 없으므로, 별도 compose 파일로 서버를 띄워 같은 네트워크에 붙이는 식의 구성이 필요하다.
첫 번째가 단순하다. 어느 쪽이든 이 Agent의 MCP 서버는 Agent project와 따로 배포된다는 점은 같다 —
portal-api-mcp는 여러 Agent가 공유하는 서버이므로 자기 저장소와 자기 PR을 갖는다.
로컬에서 실행한다
섹션 제목: “로컬에서 실행한다”export OPENAI_API_KEY=... # 선택한 provider의 keycd reviewerkagent buildkagent runkagent run은 docker compose up -d로 container를 띄운 뒤 http://localhost:8080의 Agent에 A2A로 접속해
터미널 채팅을 연다. 채팅을 끝내면 compose를 내린다. 코드를 고쳤으면 kagent build를 다시 하거나
kagent run --build로 실행한다.
Agent container는 --local 옵션으로 시작한다. 이 옵션은 session을 메모리에 두게 해서 kagent controller 없이
동작하게 한다(소스에서 확인).
문제가 생기면 container 로그를 본다.
docker psdocker logs <container ID>임직원 토큰 전파를 켜는 방법
섹션 제목: “임직원 토큰 전파를 켜는 방법”Declarative Agent는 tool마다 allowedHeaders를
적었다. kagent init으로 만든 BYO Agent는 kagent-adk runtime이 실행하므로 다른 수단을 쓴다.
# 발췌 — Agent 리소스spec: type: BYO byo: deployment: image: registry.example.com/agents/reviewer@sha256:... env: - name: KAGENT_PROPAGATE_TOKEN value: "true"이 환경 변수가 있으면 runtime이 A2A 요청의 Authorization을 Agent의 모든 MCP toolset에 싣는다
(kagent-adk 소스에서 확인. 0.x 문서에는 설명이 없다).
- 서버별로 고를 수 없다. command 방식으로 붙인 공개 MCP 서버에도 임직원 토큰이 간다. 임직원 토큰을 전파하는 Agent에는 우리가 운영하는 MCP 서버만 붙인다.
- 로컬의
kagent run채팅은 토큰을 보내지 않는다. 전파를 로컬에서 보려면localhost:8080에Authorizationheader를 넣은 A2A 요청을 직접 보내야 하는데, 이 경로는 검증하지 않았다. 전파의 확인은 클러스터에서 확인 순서대로 한다.
배포 manifest로 넘긴다
섹션 제목: “배포 manifest로 넘긴다”kagent build --image registry.example.com/agents/reviewer:3f9c2e1 --pushkagent deploy . --image registry.example.com/agents/reviewer:3f9c2e1 \ --env-file .env.production --namespace agents --dry-run > manifests.yaml출력에는 세 종류가 들어 있다(공식 문서의 예).
| 출력되는 리소스 | 그대로 쓰는가 |
|---|---|
Secret (<이름>-env) | 쓰지 않는다. .env 파일의 값이 그대로 들어 있다. Git에 넣지 않고 secret 저장소에서 가져오게 바꾼다 |
Agent (type: BYO) | image를 digest로 바꾸고 namespace·label을 플랫폼 규칙에 맞춘다 |
MCPServer 또는 RemoteMCPServer | Agent 전용 서버면 함께 넣는다. 공용 서버면 뺀다 |
--dry-run 없이 실행하면 CLI가 현재 kubeconfig의 클러스터에 직접 적용한다. 개발용 클러스터에서 빨리
확인할 때만 쓰고, 승인이 필요한 환경에는 PR 경로로만 넣는다.
로컬에서 확인되는 것과 안 되는 것
섹션 제목: “로컬에서 확인되는 것과 안 되는 것”| 확인 대상 | 로컬 | 클러스터 |
|---|---|---|
| 지시문과 대화 흐름, tool 선택 | 된다 | — |
| Python 함수 tool, command 방식 MCP 서버 | 된다 | — |
image가 뜨고 8080에서 A2A로 응답하는가 | 된다 | — |
controller를 거친 호출, 인증, X-User-Id | 안 된다 | controller가 있어야 한다 |
| session 저장과 대화 이력 | 안 된다 (메모리) | PostgreSQL |
| 임직원 토큰 전파 | 검증하지 않았다 | 확인 순서 |
| Secret 주입, NetworkPolicy, Service 이름 | 안 된다 | 클러스터 설정 |
현실적인 순서는 이렇다. 로컬에서 Agent의 동작을 다듬고, 개발용 클러스터에 올려 controller를 거친 호출과 토큰 전파를 확인한 뒤, PR로 승인 환경에 넣는다.
ADK + Python이 아닐 때
섹션 제목: “ADK + Python이 아닐 때”kagent init이 지원하는 조합은 v0.10.2 기준 adk python 하나다(CLI 소스에서 확인). LangGraph나 CrewAI로
만들 때는 이 scaffold와 kagent run·kagent add-mcp를 쓸 수 없다.
- kagent가 요구하는 것은 여전히 하나다 — image가
8080포트에서 A2A로 응답할 것. - 공식 예제가 프레임워크별로 있다: LangGraph, CrewAI.
- 로컬 실행, MCP 연결, 토큰 전파는 직접 구성한다. 로컬에서는 image를
docker run으로 띄우고 A2A client로 호출한다.
이해 확인
섹션 제목: “이해 확인”- 로컬에서
kagent run으로 잘 동작하던 Agent가 클러스터에서 “사용자를 알 수 없다”는 오류를 낸다. 로컬에서 못 본 이유는? → 로컬에는 controller가 없어X-User-Id와Authorization이 애초에 오지 않는다. 사용자 신원에 의존하는 동작은 클러스터에서만 확인된다. mcp_tools.py에서 MCP 서버 URL을 직접 고쳤는데 다음 날 원래대로 돌아와 있다. 왜인가? → 자동 생성 파일이다.kagent add-mcp나 build가kagent.yaml에서 다시 만든다.kagent.yaml이나agent.py를 고친다.KAGENT_PROPAGATE_TOKEN=true인 BYO Agent에uvx로 띄우는 공개 MCP 서버를 하나 더 붙였다. 무엇이 문제인가? → 임직원 토큰이 그 서버로도 간다. stdio 서버의 프로세스는 header를 못 보지만 앞단의 gateway까지는 도착하고, remote 방식의 외부 서버라면 그 운영자가 토큰을 받는다.