모델에게 강력한 범용 도구를 주면 데모는 빠르게 만들어진다. SQL 실행 도구 하나로 모든 테이블을 조회하고, HTTP 도구 하나로 여러 서비스를 호출할 수 있다. 그러나 권한 검토 단계에서는 “무엇이든 할 수 있다”는 특성이 그대로 위험이 된다. 허용 가능한 쿼리를 프롬프트로 설명해야 하고, 모델이 생성한 문자열을 다시 분석해야 하며, 감사 로그에는 업무 의미 대신 원시 명령만 남는다.
enterprise-agent-reference는 반대 방향을 택한다. 모델이 사용할 수 있는 도구는 고객 조회, 열린 사례 목록, 사례 조회, 후속 작업 생성, 정책 검색, 정책 문서 조회로 제한된다. 각각은 사람이 읽을 수 있는 업무 동작이며 입력과 출력이 타입으로 정의된다.
도구 이름이 권한의 단위가 된다
get_customer(customer_id)는 고객 한 명을 가져온다. list_open_cases(customer_id)는 열린 사례만 반환한다. 이 둘은 SQL보다 표현력이 낮지만 그래서 검토하기 쉽다. 어떤 에이전트가 어떤 도구를 받을지 이름으로 allowlist를 만들 수 있고, 로그에서 호출 목적도 바로 이해할 수 있다.
범용 접근과 비교하면 차이가 선명하다.
- SQL 도구는 테이블, 열, 조인과 쓰기 가능 범위를 별도로 제한해야 한다.
- HTTP 도구는 목적지, 메서드, 헤더와 응답 크기를 모두 통제해야 한다.
- 파일 도구는 경로 탐색과 예상치 못한 비밀 노출을 막아야 한다.
- 업무 도구는 허용된 입력을 받아 하나의 검증된 동작만 수행한다.
입력과 출력 모두 엄격하게 만든다
MCP 계약은 Pydantic 모델을 사용하고 extra="forbid"를 적용한다. 예상하지 않은 필드가 들어오면 조용히 무시하지 않고 실패한다. 고객, 사례, 후속 작업, 정책 검색 결과가 어떤 필드를 갖는지도 명시한다.
class StrictModel(BaseModel):
model_config = ConfigDict(extra="forbid")
class PolicySearchResult(StrictModel):
document_id: str
tenant_id: str
title: str
version: str
excerpt: str
source: str타입이 있다고 보안이 자동으로 해결되는 것은 아니다. 하지만 입력 검증, 응답 크기 제한, API 문서화, 테스트 fixture가 같은 계약을 바라볼 수 있다. 모델의 JSON 출력이 조금 달라져도 어디서 실패했는지 찾기 쉬워진다.
업무 규칙은 도구 구현 뒤에 둔다
도구 서버는 단순히 서비스 함수를 노출한다. 실제 tenant 조건, 고객과 사례 관계 확인, 멱등성, 감사 이벤트 기록은 RecordsService에 있다. MCP가 아닌 다른 호출 경로가 생겨도 서비스 규칙을 재사용할 수 있고, 테스트에서는 네트워크 없이 규칙 자체를 검증할 수 있다.
Knowledge MCP도 검색 엔진의 원시 query DSL을 모델에게 받지 않는다. 모델은 검색어, 결과 수, 허용된 필터만 전달한다. tenant 필터는 서비스 코드가 삽입한다. 사용자가 검색 요청에 다른 tenant를 끼워 넣을 공간을 계약에서 제거한 것이다.
좁은 도구가 불편해지는 순간
업무가 늘면 도구 수도 늘어난다. 비슷한 도구가 중복되고 새 요구마다 배포가 필요할 수 있다. 이 비용은 실제다. 그래서 도구를 데이터베이스의 CRUD와 일대일로 만들기보다 사용자가 달성하려는 안정적인 업무 능력에 맞춰야 한다.
좋은 MCP 도구를 검토할 때는 다음을 확인한다.
- 이름만 보고 업무 목적을 이해할 수 있는가?
- 호출자가 tenant나 권한 조건을 임의로 선택할 수 없는가?
- 입력 크기와 출력 크기에 상한이 있는가?
- 쓰기 작업은 재시도와 중복 실행을 견디는가?
- 도구 없이 서비스 규칙을 독립적으로 테스트할 수 있는가?
에이전트의 유연성은 모든 기반 시스템을 직접 조작하는 데서 나오지 않는다. 작고 예측 가능한 능력을 안전하게 조합할 수 있을 때 더 오래 유지된다.