남이 만든 도구를 내 Agent에 꽂으면 무엇을 조심해야 할까요?

MCP 서버를 Agent에 연결하는 일은 몇 줄이면 됩니다. 조심할 일은 연결한 뒤에 생깁니다. 남이 만든 서버의 도구를 내 Agent가 내 권한으로 부르게 되기 때문입니다. 삭제하는 도구가 섞여 있을 수도 있고 서버가 갑자기 꺼질 수도 있고 도구가 오류를 낼 수도 있습니다.

그래서 Agent와 서버 사이에 관문을 둡니다. 허용한 도구만 통과시키고 서버가 응답하지 않을 때를 대비하고 오류 메시지는 모델이 읽을 수 있게 돌려줍니다. 아래에서 상황을 하나 골라 호출이 어디서 멈추는지 보세요.

상황

설명을 위한 예시 장면입니다. 세 상황은 모두 langchain-mcp-adapters와 Python MCP SDK로 직접 실행해 확인했습니다.

서버를 연결하고 도구를 Agent에 줍니다

langchain-mcp-adapters의 MultiServerMCPClient는 여러 MCP 서버에 한꺼번에 연결해 도구를 LangChain 도구로 바꿔 줍니다. 서버마다 이름과 연결 방식을 적습니다. 내 컴퓨터의 서버는 stdio로 명령을 주고 원격 서버는 streamable_http로 주소와 인증 헤더와 시간 한도를 줍니다.

앞 글에서 만든 티켓 서버를 이 방식으로 연결해 직접 실행했습니다. 어댑터 0.3.2는 MCP SDK 1.x를 함께 설치하는데 2.3으로 만든 서버에도 stdio와 HTTP 둘 다 연결됐습니다.

agent.pypython
client = MultiServerMCPClient(
    {"tickets": {"transport": "stdio", "command": "python",
                 "args": ["ticket_server.py"]},          # 1 내 컴퓨터의 서버
     "wiki": {"transport": "streamable_http",
              "url": "https://wiki.example.com/mcp",     # 예시 주소
              "headers": {"Authorization": f"Bearer {TOKEN}"},
              "timeout": 10}},                           # 2 원격 서버
    tool_interceptors=[allowlist],                       # 3 호출 전에 거르기
)
tools = await client.get_tools()                         # 4 서버마다 도구 찾기
agent = create_agent(model, tools=tools)
터미널
$ python agent.py 도구 이름: ['get_ticket', 'list_tickets'] 허용된 호출: {"id": "T-1043", "title": "메일 용량 증설", ...} 막힌 호출: list_tickets은 허용 목록에 없어 실행하지 않았습니다 Agent 답: T-1042는 부품 대기 중입니다.

출력은 예시입니다. 위 결과는 티켓 서버(stdio, HTTP)로 확인했고 wiki 주소는 예시라 실행하지 않았습니다. Agent 답은 모의 서버로 만든 것입니다.

  1. 내 컴퓨터의 서버입니다. command와 args로 서버를 실행하는 명령을 줍니다. 이 명령이 내 권한으로 실행되므로 출처를 모르는 명령은 넣지 않습니다.
  2. 원격 서버입니다. 주소와 인증 헤더와 시간 한도를 줍니다. 시간 한도가 없으면 응답이 없는 서버 때문에 Agent가 오래 멈출 수 있습니다.
  3. 인터셉터를 겁니다. 도구를 부르기 직전에 실행되는 함수입니다. 도구 이름을 보고 막거나 기록하거나 인자를 고칠 수 있습니다.
  4. 도구를 찾아 Agent에게 줍니다. get_tools()가 서버마다 tools/list를 불러 도구를 모읍니다. 이 도구들을 create_agent에 그대로 넘깁니다.
allowlist.pypython
ALLOW = {"get_ticket", "list_tickets"}                  # 1 읽기 도구만

async def allowlist(request, handler):
    if request.name not in ALLOW:                        # 2 목록에 없으면
        return CallToolResult(isError=True, content=[TextContent(
            type="text", text=f"{request.name}은 허용 목록에 없어 실행하지 않았습니다")])
    return await handler(request)                        # 3 있으면 그대로 통과
  1. 허용할 도구를 적습니다. 읽기만 하는 도구부터 허용하고 쓰기와 삭제는 필요할 때 더합니다.
  2. 막을 때는 결과로 돌려줍니다. 예외를 던지면 Agent가 멈출 수 있어서 오류 표시가 켜진 결과를 돌려주었습니다. 모델이 이유를 읽고 다른 길을 찾습니다. 직접 실행해 이 결과가 모델에게 전달되는 것을 확인했습니다.
  3. 통과시킵니다. handler(request)를 부르면 원래대로 서버에 호출이 나갑니다.

서버가 둘이면 도구 이름이 겹칠 수 있습니다

티켓 서버에도 위키 서버에도 search라는 도구가 있을 수 있습니다. 두 서버를 함께 연결하면 모델이 보는 목록에 같은 이름이 둘 나옵니다. 어느 서버의 검색인지 구분할 수 없습니다.

tool_name_prefix=True를 주면 서버 이름을 앞에 붙입니다. 직접 실행해 티켓 서버의 도구가 tickets_get_ticket과 tickets_list_tickets로 바뀌는 것을 확인했습니다. 허용 목록의 인터셉터는 접두어가 붙기 전 서버 쪽 이름(get_ticket)을 받는 것도 확인했습니다. 서버가 여럿이면 request.server_name까지 함께 보고 거릅니다.

도구마다 허용할지 말지를 정합니다

MCP 공식 보안 문서는 권한 범위를 줄이라고 거듭 말합니다. 처음에는 읽기 같은 위험이 낮은 것만 열고 필요할 때 한 단계씩 넓힙니다. 넓은 권한을 한 번에 받아 두면 토큰이나 도구 하나가 새는 순간 피해가 커지기 때문입니다.

티켓 시스템에 도구가 다섯 개 있다고 해 봅니다. 도구를 눌러 허용을 바꾸고 모델이 모두 호출해 보게 해 보세요.

원격 서버는 OAuth로 누구인지 확인합니다

HTTP로 연결하는 원격 서버가 사용자 데이터를 다룬다면 인증이 필요합니다. MCP 규격은 OAuth 2.1을 바탕으로 한 흐름을 정의합니다. 서버는 보호된 자원을 지키는 쪽이고 클라이언트는 사용자를 대신해 접근을 요청하는 쪽이며 사용자의 동의와 토큰 발급은 인증 서버가 맡습니다.

인증은 선택 사항입니다. 내 컴퓨터에서 도는 stdio 서버는 이 흐름을 따르지 않고 환경에서 자격 증명을 가져옵니다. 단계를 눌러 흐름을 따라가 보세요.

믿지 말아야 할 것도 있습니다

MCP 서버는 내 Agent에게 도구와 설명을 건네는 외부 프로그램입니다. 공식 문서는 도구는 임의의 코드 실행에 해당하니 신중히 다루고 도구 동작을 설명한 글은 믿을 만한 서버에서 온 것이 아니라면 믿지 말라고 말합니다. 도구를 실행하기 전에 사용자의 명시적인 동의를 받아야 한다고도 적혀 있습니다.

위험을 네 가지로 나눠 정리했습니다. 하나씩 눌러 무슨 일이 생기고 어떻게 막는지 보세요.

safe_start.pypython
try:
    tools = await client.get_tools()          # 1 서버가 꺼져 있으면 여기서 실패
except Exception as e:
    tools = []                                # 2 도구 없이 시작하고
    notice = f"일부 도구를 쓸 수 없습니다: {type(e).__name__}"   # 사용자에게 알린다
터미널
$ python safe_start.py 일부 도구를 쓸 수 없습니다: ExceptionGroup

출력은 예시입니다. 응답하지 않는 주소로 연결해 확인한 결과이고 오류 이름은 어댑터 버전에 따라 다를 수 있습니다.

  1. 연결 실패는 예외로 옵니다. 서버가 꺼져 있으면 get_tools()가 예외 묶음(ExceptionGroup)을 던졌습니다. 잡지 않으면 Agent 프로그램 전체가 시작하지 못합니다.
  2. 도구 없이도 시작합니다. 일부 서버가 안 되어도 나머지 일은 계속하게 하고 어떤 도구를 쓸 수 없는지 사용자에게 알립니다.

다음 글에서는 만든 Agent가 잘 동작하는지 점검하는 평가와 실행 기록(트레이싱)을 알아보겠습니다.

참고 자료

출처 5개 펼치기