콘텐츠로 이동

외부 MCP 서버 연결

stdio/http/sse MCP 서버 설정, 지연 로딩, tool_search 활성화 흐름을 설명합니다.

Revka는 MCP 서버인 동시에 MCP 클라이언트이기도 합니다. 클라이언트로서 임의의 수의 외부 Model Context Protocol 서버에 연결할 수 있습니다. stdio를 통한 로컬 프로세스이든, HTTP 또는 SSE를 통한 원격 서비스이든, 해당 서버의 도구들을 마치 Revka 기본 도구처럼 에이전트 루프에 노출합니다. 파일 시스템 서버, 데이터베이스 서버, 사내 전용 도구, 또는 수천 가지 커뮤니티 MCP 서버 등 Revka에 기본 탑재되지 않은 기능을 추가하는 방법이 바로 이것입니다.

외부 MCP 서버를 에이전트에 연결하려는 경우 이 페이지를 참고하세요. [mcp] 설정 섹션, 세 가지 전송 유형, 외부 도구가 래핑되는 방식, 도구 스키마가 온디맨드로 로드되는 이유, 그리고 모델이 tool_search로 도구를 활성화하는 방법을 다룹니다. 반대로 Revka의 도구를 다른 클라이언트(Claude Code, Codex, Gemini CLI)에 노출하고 싶다면 MCP 서버로서의 Revka를 참고하세요.

데몬이 [mcp] enabled = true로 부팅되면 Revka는 각 [[mcp.servers]] 항목을 읽고 해당 서버에 연결합니다. 연결은 최선 노력(best-effort) 방식으로 이루어집니다. 서버 하나가 시작되지 않거나 응답하지 않아도 Revka는 오류를 로그에 기록하고 나머지 서버를 계속 처리합니다. 문제가 생긴 서버가 에이전트 전체를 막지 않습니다. 각 서버의 initializetools/list 결과는 한 번만 읽히며, 서버가 광고하는 모든 도구는 기본 Revka Tool로 래핑되어 레지스트리에 추가됩니다.

서버 간 이름 충돌을 방지하기 위해 모든 외부 도구는 <server_name>__<tool_name> 형식으로 이름이 변경됩니다. 설정의 name에 해당하는 서버 이름, 밑줄 두 개, 그리고 도구 자체의 이름 순서입니다. filesystem이라는 서버의 read_file 도구는 filesystem__read_file이 됩니다.

프로토콜은 MCP 프로토콜 버전 2024-11-05 기반의 JSON-RPC 2.0입니다.

외부 서버는 ~/.revka/config.toml[mcp] 아래에 선언하며, 서버당 하나의 [[mcp.servers]] 블록을 작성합니다.

[mcp]
enabled = true
deferred_loading = true # default; load schemas on demand via tool_search
[[mcp.servers]]
name = "filesystem"
transport = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
env = { MCP_LOG = "debug" }
tool_timeout_secs = 120
[[mcp.servers]]
name = "remote-tools"
transport = "http"
url = "https://example.com/mcp"
headers = { "Authorization" = "Bearer mytoken" }
[[mcp.servers]]
name = "sse-server"
transport = "sse"
url = "https://example.com/sse"
타입기본값설명
enabledbooleanfalseMCP 클라이언트의 마스터 스위치입니다. 이 값이 true가 아니면 외부 서버에 연결되지 않습니다.
deferred_loadingbooleantrue모든 스키마를 시스템 프롬프트에 포함하는 대신 tool_search를 통해 온디맨드로 도구 스키마를 로드합니다. 강력히 권장됩니다. 지연 로딩을 참고하세요.
타입기본값설명
namestring필수서버 식별자로, 모든 도구 이름의 <name>__<tool> 접두사가 됩니다.
transportstring"stdio"stdio, http, sse 중 하나입니다. 전송 유형을 참고하세요.
commandstring""실행할 바이너리 경로입니다(stdio 전송 전용).
argsarray[]command에 전달되는 인수입니다(stdio 전용).
envtable{}스폰된 프로세스의 환경 변수입니다(stdio 전용).
urlstring미설정엔드포인트 URL입니다(http 및 sse 전송).
headerstable{}모든 요청에 전송되는 HTTP 헤더입니다(http 및 sse).
tool_timeout_secsinteger180이 서버 도구의 호출당 타임아웃입니다. 최대 600초로 제한됩니다.

transport 필드는 Revka가 서버와 통신하는 방식을 결정합니다.

command를 (argsenv와 함께) 자식 프로세스로 생성하고 stdin/stdout을 통해 JSON-RPC로 통신합니다. 연결이 끊기면 자식 프로세스가 종료되므로 프로세스 수명 주기가 데몬에 종속됩니다. 각 응답 라인은 4 MB로 제한됩니다.

[[mcp.servers]]
name = "filesystem"
transport = "stdio"
command = "/usr/local/bin/mcp-filesystem"
args = ["--read-only"]
env = { MCP_LOG = "debug" }

npm 또는 Python 패키지로 배포되는 커뮤니티 MCP 서버처럼 Revka와 함께 로컬에서 실행하는 도구에는 stdio를 사용하세요. 가장 일반적인 케이스입니다.

단계타임아웃
연결, initialize, tools/list60초 (전송 레이어 30초)
도구 호출tool_timeout_secs (기본값 180, 최대 600)

각 외부 도구는 McpToolWrapper에 의해 Revka의 도구 모델로 적응되어, 기본 제공 도구와 동일하게 레지스트리, 보안 정책, 에이전트 루프를 통해 처리됩니다. 다운스트림에서 별도 처리가 필요하지 않습니다. 래퍼는 서버의 initialize / tools/list 응답에서 이름(<server>__<tool>), 설명, 파라미터 스키마를 가져오며, 모든 호출에서 세 가지 작업을 수행합니다.

  • approved 필드 제거. Revka의 보안 모델은 감독 모드 게이팅을 위해 도구 호출에 approved: bool 인수를 주입합니다. MCP 서버는 이 필드를 알지 못하므로 래퍼가 전달 전에 이를 제거하여 “예상치 못한 인수” 오류를 방지합니다.
  • 문자열로 인코딩된 값 강제 변환. LLM은 스키마에 정수를 선언한 곳에 "5"를, 불리언에 "true"를 자주 전달합니다. 래퍼는 문자열로 인코딩된 숫자, 불리언, 배열, 객체를 스키마에 선언된 타입으로 강제 변환한 뒤 전달합니다.
  • 오류를 전파하지 않음. MCP 호출 실패 시 Err 대신 비치명적인 ToolResult { success: false, ... }를 반환하므로, 오작동하는 서버는 루프 충돌이 아닌 모델이 반응할 수 있는 도구 오류로 나타납니다.

서버 하나가 수십 또는 수백 개의 도구를 광고할 수 있습니다. 모든 스키마를 시스템 프롬프트에 포함하면 컨텍스트 윈도우가 급격히 늘어나고, 정의가 ~100개를 넘으면 소형 또는 로컬 모델(Gemma, Llama)이 도구 이름을 환각하기 시작합니다. 지연 로딩은 이 문제를 해결합니다.

mcp.deferred_loading = true(기본값)로 설정하면 외부 MCP 도구 스키마는 시작 시 시스템 프롬프트에 포함되지 않습니다. 대신 모델에는 도구 이름만 나열된 경량 <available-deferred-tools> 섹션이 표시되며, 사용하려는 도구의 전체 스키마를 로드하려면 기본 제공 tool_search 도구를 호출하라는 안내가 함께 제공됩니다. 트레이드오프: 지연 로딩은 프롬프트를 간결하게 유지하는 대신, 새 도구를 처음 호출하기 전에 tool_search 왕복이 한 번 발생합니다. 즉시 로딩(deferred_loading = false)은 이 왕복을 없애지만 모든 스키마에 컨텍스트를 소비합니다.

에이전트가 매 턴 자주 사용하는 일부 도구는 지연 모드에서도 즉시 로드됩니다. 매번 tool_search 홉을 거치면 비효율적이기 때문입니다.

  • 로컬 모델은 에이전트 수명 주기 서브셋을 즉시 유지합니다: create_agent, wait_for_agent, send_agent_prompt, get_agent_activity, list_agents, cancel_agent, resolve_outcome, get_workflow_context, save_plan, recall_plans, compact_conversation (LOCAL_MODEL_EAGER_SUFFIXES 접미사로 매칭).
  • 오퍼레이터 시트(클라우드 또는 로컬)는 동일한 수명 주기 서브셋에 더해 Kumiho 메모리 반사 도구인 kumiho-memory__kumiho_memory_engagekumiho-memory__kumiho_memory_reflect도 즉시 유지합니다.

나머지는 모두 tool_search 뒤에 지연됩니다.

도구가 활성화되면 모델은 보통 접두사 없이 접미사만으로 도구를 호출할 수 있습니다. 예를 들어 extract_text로 끝나는 활성화된 도구가 정확히 하나뿐이라면, 모델이 <server>__ 접두사 없이 extract_text를 호출해도 Revka가 자동으로 해석합니다. 동일한 접미사가 두 서버에 존재하면 참조가 모호해져 해석에 실패하므로, 모델은 전체 접두사 이름을 사용해야 합니다.

tool_search는 지연된 MCP 도구의 전체 JSON 스키마 정의를 가져와 대화의 나머지 부분에 대해 활성화하는 기본 제공 도구입니다. 모델이 외부 도구를 온디맨드로 탐색하고 활성화하는 수단입니다.

파라미터타입기본값설명
querystring필수정확한 선택을 위해 select:<name>[,<name>...] 형식을 사용하거나, 순위 검색을 위해 자유 텍스트 키워드를 사용합니다.
max_resultsnumber5키워드 검색 모드에서 반환되는 최대 결과 수입니다.

정확한 선택select: 접두사 뒤에 활성화할 전체 접두사 도구 이름을 쉼표로 구분하여 나열합니다.

{ "query": "select:filesystem__read_file,git__status" }

키워드 검색 — 자유 텍스트를 전달하면 Revka가 쿼리 단어와 일치하는 지연된 스텁을 순위화하여 가장 적합한 것을 활성화합니다.

{ "query": "database query", "max_results": 3 }

tool_search는 활성화된 각 도구의 전체 JSON 스키마가 담긴 <functions>...</functions> 블록을 반환합니다. 형식은 모델이 기본 도구를 읽을 때와 동일합니다. select: 모드에서는 해석되지 못한 이름이 Not found: x, y 라인으로 추가됩니다. 도구가 활성화되면 스키마는 대화 전체에 유지되므로, 동일한 도구에 대해 tool_search를 다시 실행할 필요가 없습니다.

  1. 모델이 <available-deferred-tools>에서 지연된 도구 이름을 읽습니다(예: filesystem__read_file).

  2. { "query": "select:filesystem__read_file" }(또는 키워드 검색)로 tool_search를 호출합니다.

  3. Revka가 <functions> 블록에 도구의 전체 스키마를 반환하며, 도구가 이제 활성화됩니다.

  4. 모델이 실제 인수로 filesystem__read_file을 호출합니다. 스키마는 대화가 끝날 때까지 활성 상태로 유지됩니다.

설정에 서버를 추가하기 전에 연결 여부와 도구 목록을 확인할 수 있습니다. 게이트웨이는 전체 initialize + tools/list 핸드셰이크를 실행하고 결과를 보고하는 테스트 엔드포인트를 제공합니다. 이는 대시보드 MCP 편집기의 Test 버튼의 백엔드입니다.

POST /api/mcp/servers/test
Authorization: Bearer <gateway-token>
Content-Type: application/json

요청 본문 — [[mcp.servers]] 항목과 동일한 형태이며, 선택적으로 timeout_ms를 포함할 수 있습니다.

{
"name": "my-server",
"transport": "stdio",
"command": "/usr/bin/mcp-fs",
"args": ["--arg"],
"env": { "KEY": "val" },
"url": null,
"headers": {},
"timeout_ms": 30000
}

성공 응답:

{ "ok": true, "tool_count": 5, "tools": ["read_file", "write_file", "..."], "latency_ms": 234 }

실패 응답:

{ "ok": false, "error": "connection refused", "latency_ms": 10 }

핸드셰이크는 최대 10초로 제한되며, timeout_ms 값이 1000 미만이면 1초로 올림 처리됩니다. 게이트웨이 측 MCP 프록시 및 디스커버리 라우트 전체는 MCP 프록시 및 디스커버리 API를 참고하세요.