MCP 서버를 기본으로 띄우고 있었습니다 — 부르는 클라이언트는 0이었습니다
사내 문서 지식그래프 검색 도구를 두 갈래로 갈라 이관하는 중입니다. 사람이 쓰는 업무 화면 쪽과, 문서를 다루는 엔진 쪽입니다. 엔진 쪽에 도구 연동 서버를 새로 붙이면서 기동 스크립트를 '기본으로 띄우고, 필요 없으면 건너뛰기 플래그로 끈다'로 만들어 뒀습니다.
같은 날 도구 사용량 화면도 함께 만들었습니다. 어느 도구가 얼마나 불렸는지, 마지막으로 언제 불렸는지 한 표로 보는 화면입니다. 그 화면을 채우다가 질문 하나가 걸렸습니다. 이 엔진 쪽 도구들은 누가 부르는가 하는 것입니다.
왜 기본으로 띄우는가. 이 질문 하나로 기동 스크립트의 기본값이 뒤집혔습니다.
프로토콜에는 서버가 자기를 알릴 수단이 없습니다
도구 연동 규격인 'MCP'(모델이 외부 도구를 부를 때 쓰는 연결 규격)에는 서버가 자신을 클라이언트 목록에 넣게 하는 수단이 없습니다. 서버 목록을 가진 쪽이 주소를 적어 붙는 구조입니다. 서버는 자기가 잘 떠 있다는 사실만 알고, 누가 자기를 볼지는 정하지 못합니다.
그러니까 질문의 모양이 바뀝니다. '서버가 떠 있나'가 아니라 '누가 누구의 목록에 적혀 있나'입니다. 서버가 아무리 건강하게 떠 있어도 어느 클라이언트 설정에도 주소가 없으면 그 연결은 존재하지 않습니다.
저는 이걸 거꾸로 알고 있었습니다. 서비스 등록소를 쓰는 구조에서는 서버가 등록소에 자기를 올리고 클라이언트가 그 목록을 조회합니다. 그런 방식을 오래 쓰다 오면 '서버를 띄워 두면 발견된다'는 감각이 몸에 남습니다. 여기서는 그 방향이 반대였습니다.
주소를 적는 쪽이 클라이언트라는 이 방향은 처음 도구를 붙일 때부터 걸리는 지점입니다. 내부 도구를 데스크톱 클라이언트에 연결하면서 서버 쪽 코드는 한 줄도 바꾸지 않았던 기록이 있습니다.

같은 기계에 산다고 같은 역할이 아닙니다
그래서 설정 파일을 직접 열어 봤습니다. 제가 '그 에이전트의 설정'이라고만 부르고 한 번도 열어 본 적 없는 파일이었습니다. 열어 보니 그 파일이 바로 클라이언트 설정이었습니다. 그 에이전트가 도구 서버와 같은 컨테이너 안에 살아서, 클라이언트 설정이 서버 쪽 설정처럼 보였을 뿐입니다.
그 파일에 적힌 도구 서버 주소는 옛 세대 제품의 서버 하나뿐이었습니다. 새로 만든 문서 엔진의 주소를 적어 둔 곳은 어디에도 없었습니다. 업무 화면의 채팅도 도구 서버를 거치지 않습니다. 내부에서 직접 호출합니다.
사용량 화면 기준으로 도구는 두 군데에 살고 있었습니다. 업무 화면 쪽 2개, 문서 엔진 쪽 5개입니다. 뒤의 5개는 부르는 클라이언트가 없어서 호출 수가 0입니다. 고장이 아니라 아직 아무도 안 붙었다는 사실의 표시입니다.
파일을 열어 보는 데 몇 분이면 됐습니다. 열어 보기 전에 제가 가지고 있던 답은 그럴듯했고, 틀려 있었습니다. 위치가 아니라 역할로 파일을 읽어야 한다는 것을 여기서 다시 확인했습니다.
기본값을 뒤집었습니다
선택지는 둘이었습니다.
선택 | 내용 | 실제 결과 |
|---|---|---|
기본으로 띄운다 | 처음 만든 대로. 있으면 언젠가 쓰게 된다는 전제 | 아무 클라이언트도 주소를 모르므로, 아무도 부르지 않는 프로세스가 상시로 떠 있습니다. 포트를 하나 차지하고, 재시작 대상 목록에 들어가고, 로그를 쌓습니다 |
기본으로 안 띄운다 | 붙일 클라이언트가 생기면 그때 켠다 | 클라이언트 설정에 주소를 적는 순간이 곧 이제 필요하다는 순간입니다. 그 신호가 오기 전에는 띄우지 않습니다 |
'기본으로 안 띄운다'를 골랐습니다. 기동 스크립트의 '건너뛰기 플래그'를 '띄우기 플래그'로 바꿔, 명시하지 않으면 뜨지 않게 했습니다. 아무도 부르지 않는 프로세스를 기본으로 띄우면 '쓰이지 않음'이 '떠 있음'에 가려 안 보입니다. 그 가림막을 걷어내는 것이 이 변경의 목적입니다.
끄기는 두 동작입니다
'기본으로 안 띄운다'를 고른 뒤에 갈래가 하나 더 남았습니다. 안 띄운다는 말의 뜻이 두 가지였습니다.
새로 안 띄우기만 한다 — 이미 떠 있던 프로세스는 그대로 둡니다
새로 안 띄우고, 떠 있던 프로세스도 내린다 — 이쪽을 골랐습니다
앞쪽이 위험한 이유는 조용해서입니다. 옛 코드로 도는 프로세스가 계속 살아서 꺼진 줄 알았는데 답하는 상태가 됩니다. 스크립트는 안 띄웠다고 말하는데 실제로는 떠 있고, 그 떠 있는 것이 옛 버전입니다.
그래서 안 띄우는 경로에서 정지까지 수행하게 했습니다. 끄기는 한 동작이 아니라 두 동작입니다. 앞만 하면 옛 코드가 계속 답합니다.

도구가 0개인 에이전트는 실패로 보이지 않습니다
남은 일도 같이 적어 뒀습니다. 그리고 여기에 순서 함정이 있었습니다.
옛 세대 제품의 도구 서버는 아직 떠 있습니다. 개인 비서 에이전트가 그 주소를 보고 있어서입니다. 그런데 그 서버를 그냥 내리면 에이전트가 도구 0개인 상태로 뜹니다. 이때 에이전트는 오류를 내지 않습니다. 자기 메모리로 그럴듯하게 답합니다.
에이전트 쪽에서 이것은 도구를 못 불렀다는 실패가 아니라 부를 도구가 아예 없는 상태이고, 그래서 정상 경로로 흡수됩니다. 그래서 순서가 있습니다.
클라이언트 설정에서 그 서버 주소를 먼저 지웁니다
클라이언트를 재시작합니다
그다음에 서버 프로세스를 내립니다
서버부터 내리면 1번과 2번 사이의 시간 동안 조용히 틀린 답이 나갑니다. 그리고 그 답은 맞는 답과 겉모습이 같습니다. 오류 화면도 없고 경고도 없으니, 틀린 답을 받은 사람은 틀렸다는 사실을 모릅니다.
이 결정과 사용량 화면을 합쳐 85분이 걸렸습니다. 테스트는 양쪽 합쳐 12건을 새로 붙였습니다.

도구 서버는 안 띄우는 게 원칙이라는 뜻은 아닙니다
이 판단에는 전제가 붙어 있습니다. 전제가 다르면 결론도 달라집니다.
클라이언트가 서버를 직접 띄우는 방식(표준 입출력으로 붙는 로컬 연동)이면 전제 자체가 다릅니다. 그때는 미리 띄워 둔다는 선택이 애초에 성립하지 않습니다
여러 클라이언트가 언제든 붙는 공용 서버라면 기본 기동이 맞습니다. 여기서 안 띄우기로 한 것은 지금 붙는 곳이 0이어서입니다
기본을 끄면 켜는 것을 잊는 사고가 생깁니다. 이 비용을 감수한 근거는 켜야 할 때가 곧 클라이언트 설정을 고치는 순간이라 잊기 어렵다는 것입니다. 그 순간이 다른 사람 손에 있으면 이 근거는 무너집니다
연결의 주체가 늘 클라이언트인 것도 아닙니다. 서비스 등록소를 쓰는 구조에서는 서버가 등록소에 자기를 올립니다. 제가 방향을 거꾸로 알고 있던 이유도 거기에 있습니다. 그러니 규격을 바꿀 때는 방향을 한 번 확인하고 넘어가는 편이 낫습니다.
같은 규격을 놓고도 무엇과 무엇을 갈라야 답이 나오는 질문이 또 있습니다. AI 서비스를 바꿀 수 있냐는 물음을 도구 연동과 플러그인으로 갈라 정리한 기록입니다.
점검 항목
AI 에이전트에 도구 서버를 붙여 운영한다면 아래 다섯 개를 한 번 확인해 보시면 좋겠습니다.
지금 떠 있는 도구 서버의 주소가 어느 클라이언트 설정에 적혀 있는지 파일을 열어 확인했습니까
클라이언트 설정과 서버 설정을 같은 파일로 읽고 있지는 않습니까. 둘이 한 기계에 살면 그렇게 보입니다
기동 스크립트의 기본값이 띄우는 쪽입니까, 켜야 뜨는 쪽입니까
안 띄우는 경로가 이미 떠 있는 프로세스까지 정지시킵니까
클라이언트에서 서버를 떼어낼 때 설정 수정과 재시작이 서버 정지보다 앞에 있습니까
사용량 화면에 호출 수 0으로 남아 있는 도구가 있다면 지우지 않고 두시는 편을 권합니다. 0은 쓰이지 않는다는 뜻이 아니라 아직 아무도 붙지 않았다는 뜻일 수 있습니다. 둘은 다른 상태고, 지우면 구분할 근거가 사라집니다.
마무리
기본으로 띄우던 것을 끄고, 떠 있던 것도 내리게 했습니다. 바뀐 코드는 기동 스크립트의 조건 몇 줄이고, 바뀐 것은 질문의 방향입니다. 서버가 잘 떠 있나를 묻는 대신 누가 누구의 목록에 적혀 있나를 묻게 됐습니다.
이 질문은 파일 하나를 열어 보는 것으로 끝났습니다. 열어 보기 전까지 저는 그럴듯하게 틀린 답을 들고 있었고, 그 답이 기동 스크립트의 기본값으로 굳어 있었습니다. 도구 연동을 설계할 때 연결의 주체를 먼저 확인하면, 끄고 켜는 절차가 통째로 틀어지는 일을 줄일 수 있습니다.