Skip to main content
Glama

tutu-mcp-proxy

lint lint-pages tests pages

mcp.tutu.ru 앞에 두는 압축/근거 확인(grounding) MCP-프록시. 투투 해커톤(트랙 2 — "도구 최적화")을 위해 만들었다. 동일한 16개 도구, 동일한 동작, 그리고 추가로:

  • 항상 로드되는 카탈로그 축소. 실제 서버의 tools/list는 첫 검색 전에 이미 ~108KB다 (uv run python tutu.py measure, fixtures/에서 재현 가능). 가장 무거운 세 도구(search_rail, get_rail_seatmap, search_hotels)는 짧은 top-level description을 받고, 잘려나간 설명은 사라지지 않고 짝이 되는 get_<domain>_instructions의 호출 결과로 이동한다 (tutu_mcp/proxy/compact_tools.py) — 실제로 읽는 세션만 비용을 지불한다. inputSchema는 어디서도 건드리지 않았다.

  • check_groundedness. 답변 초안이 의존하는 tool_result와 결정론적으로 대조한다 — 텍스트에서 가격/시간/기차-항공편 번호/링크를 추출하여 JSON에 실제로 존재하는지 확인한다. LLM 심사자 없이 (tutu_mcp/groundedness.py).

  • 빈 결과에 대한 설명. 가장 흔한 실패: 에이전트가 빈 필터링 검색 결과를 "이 기차는 운행하지 않는다"로 읽지만, 도구는 판매 중인 것만 반환했다. 투투 자체 카운터 meta.post_filter_dropped_*가 어떤 필터가 목록을 비웠는지 알려준다 — 프록시는 이를 문장으로 펼쳐 결과에 첨부한다 (_empty_result_note). 에이전트는 받지도 않은 운행 정보를 추측하는 대신 사실을 말하게 된다 (tutu_mcp/proxy/empty_results.py).

  • Premise gate + assess_request. check_groundedness는 턴의 출력을 검사하고, 이것은 입력을 검사한다: 검색을 좁히는 값은 반드시 사용자 또는 이전 tool_result에서 와야 한다. 에이전트가 지어낸 필터(전형적인 예: 이벤트 종료 시간을 조용히 가정하고 그걸로 역방향 항공편을 필터링)는 데이터 대신 clarification_required를 받는다 (tutu_mcp/premises.py).

  • Mock 모드. 실제 서버 대신 기록된 픽스처로 응답한다 — 해커톤 공용 레이트 리밋을 건드리지 않고 원하는 만큼 자주 실행할 수 있다.

트레이스 뷰어

각 이벌 실행은 하나의 자족적 HTML 파일로 변환된다: 더블 클릭, 서버도 네트워크도 필요 없다. 게시된 쇼케이스: https://trum-ok.github.io/tutu-mcp-hackathon/trace-viewer.html.

트레이스 뷰어: 실행 메트릭 헤더와 시나리오 하나의 분석

make viewer        # из последнего настоящего прогона эвалов
make viewer-demo   # из рукописных демо-трейсов — без модели и без ключа

인터페이스에서: 개요 모드 — 시나리오 × 변형 전체 매트릭스가 하나의 테이블에; 실패만 은 목록을 실패한 항목으로 좁히고; 나란히 는 두 변형의 시나리오 하나를 나란히 배치하고, 어긋난 검사를 강조한다. 응답에서 강조된 값 하나를 클릭하면 해당 값이 나온 서버 응답의 정확한 조각이 담긴 패널이 열린다 — 또는 어느 응답에도 없다는 직접적인 확인이 표시된다. 합성 실행(demo:/scripted:)은 "측정 아님" 앰버 배지로 표시된다 — 손으로 쓴 데모를 측정과 혼동할 수 없다.

빌드 방법 — docs/deploy.md.

Related MCP server: TripWeaver MCP Server

결과

gpt-5.6-luna에서 --effort low로 22개 시나리오를 4회 실행, 2026년 8월 19일. 실행 간 편차는 모델 자체에서 온다: 백엔드는 동일하고 시나리오 세트도 동일하다.

메트릭

baseline

proxy

도구 표면, 토큰

25 269

15 364

동일, 바이트

115 329

74 971

태스크 성공

17–18 / 22

19–21 / 22

주장의 근거 확인

97–98 %

99 %

실행당 지어낸 주장

4

1

실행당 입력 토큰

3,3–4,4백만

0,43–0,67백만 적음

전제 게이트 발동

0

8–12

불필요한 확인 질문

0

0

지어낸 주장 행은 백분율보다 먼저 읽어야 한다: 검사 가능한 주장 189개 중 4개와 184개 중 1개는 97.9% 대 99.5%로, 차이가 노이즈처럼 보인다. 절대값으로는 사용자에게 도달한 잘못된 사실이 4배 적으며, 사용자가 받는 것은 백분율이 아니라 바로 그 사실들이다. 백분율은 검사 가능한 주장 기준으로 계산된다: 사용자가 직접 말한 임계값("3000₽보다 저렴")은 payload가 확인할 의무가 없으며 분모에 포함되지 않는다.

표면 절감은 39%이며 실행에 의존하지 않는다: 에이전트가 매 세션 첫 검색 전에 지불하는 정적 값이다. 나머지는 모두 모델 동작이므로 범위로 표시했다.

마지막 행은 첫 행만큼 중요하다: 게이트는 실행당 8–12회 발동했지만, 물어볼 것이 없는 시나리오에서는 단 한 번도 질문을 던지지 않았다(부정 대조군 no_overask + did_not_over_ask 검사). 모든 것을 확인하는 메커니즘은 완벽한 프레미스 메트릭을 얻고 제품을 망쳤을 것이다.

다섯 시나리오에서 변형이 갈리며, 다섯 모두 proxy가 이긴다: 빈 필터링 결과가 "기차가 운행하지 않는다"로 읽히지 않고, 요일 오타가 검색 전에 잡히고, 조용히 대입된 게스트 수는 게이트가 막고, 인접 좌석은 올바른 도구로 검색된다. proxy의 유일한 지속적 실패는 multitransport_basic: 에이전트가 가격 차이(2 275,07 − 1 700 = 575)를 출력하는데, 두 피연산자는 모두 확인되었지만 숫자 자체는 payload에 없다. 이러한 산수를 잘못된 산수(호텔 가격에 숙박일 수를 곱하는 것 — 별도 시나리오이며 거기서는 오류)와 구별하는 것은 결정론적 검사로 불가능하다; 이것은 방법의 한계이지 프록시의 결함이 아니다.

보고서의 정직성을 위한 두 가지: 픽스처 미스는 도구 오류와 별도로 집계된다 — 기록의 구멍이 투투의 실패로 읽히지 않아야 하고; 토큰 수는 추정치일 때 ~로 표시된다 — OpenAI에는 토큰 계산 엔드포인트가 없어 정확한 수치는 실제 테스트 요청 하나(usage.prompt_tokens)에서 가져오고, --estimate-tokens는 대신 tiktoken의 offline 추정치를 대입한다.

모델과 추론 노력은 실행 단위로 지정한다 (--model/OPENAI_MODEL, --effort/OPENAI_EFFORT); 둘 다 없으면 reasoning 필드가 아예 전송되지 않고 모델이 자체 기본값을 적용한다 — 명시적 --effort none과는 다르다. 러너는 기본적으로 /v1/responses를 호출한다 — Chat Completions는 현재 reasoning 모델에서 function tools를 reasoning과 함께 받지 않는다; --api chat은 /v1/responses가 없는 OpenAI 호환 게이트웨이용이다. 픽스처 매칭은 inputSchema의 기본값을 무시한다(모델은 page: 1, sort: "price_asc" 등을 적는데, 사람이 픽스처를 기록할 때는 아무것도 쓰지 않는다) — 그렇지 않으면 모델 실행의 거의 모든 호출이 기록과 어긋났을 것이다.

실행 구조, 각 메트릭이 계산하는 것, 그리고 하네스 자체 검증이 CI에 있는 이유 — docs/evals.md.

빠른 시작

uv와 Python ≥ 3.13 필요(uv sync가 직접 설치).

git clone https://github.com/Trum-ok/tutu-mcp-hackathon
cd tutu-mcp-hackathon
uv sync
uv run python tutu.py serve            # mock-режим (по умолчанию) — http://127.0.0.1:8800/mcp
TUTU_PROXY_MODE=live uv run python tutu.py serve   # проксирует настоящий mcp.tutu.ru

아무 MCP 클라이언트나 — http://127.0.0.1:8800/mcp (Streamable HTTP, upstream과 동일하게 인증 없음). 아래 <URL>은 이 주소 또는 배포된 프록시의 주소다 (docs/deploy.md 참조).

claude mcp add --transport http tutu <URL>          # Claude Code
// Cursor · ~/.cursor/mcp.json
{ "mcpServers": { "tutu": { "url": "<URL>" } } }

// Claude Desktop · claude_desktop_config.json — через mcp-remote, он не умеет HTTP напрямую
{ "mcpServers": { "tutu": { "command": "npx", "args": ["-y", "mcp-remote", "<URL>"] } } }

기대 결과. 로그에 두 줄: 모드와 수신 주소. 클라이언트는 연결 후 18개 도구를 표시한다: 투투 고유 16개 + assess_request와 check_groundedness. 16개라면 클라이언트가 프록시가 아닌 투투 자체에 연결된 것이다.

얼마나 잘랐는가

tools/list: 110 164 → 79 411 바이트 (−27.9 %), 양쪽 initialize 지침까지 포함하면 −33.1 % (프록시는 투투의 11.2KB 대신 1.9KB의 자체 지침 블록을 제공). 두 숫자 모두 자체 도구 두 개(assess_request 1 313바이트, check_groundedness 1 100)를 추가한 후의 값이다.

카탈로그 계층별 분석, 명명된 압축 비용, 그리고 의도적으로 넘지 않은 경계 — docs/compaction.md.

문서

사용자용 분석은 별도 페이지: make docs가 site/index.html을 빌드하거나, 이미 게시된 것을 열어라: https://trum-ok.github.io/tutu-mcp-hackathon/.

파일

내용

docs/findings.md

실제 서버에 대한 원시 측정과 동기 부여 사례

docs/compaction.md

정확히 무엇이 압축되는지, 대가, 하지 않은 것

docs/evals.md

이벌 하네스 구조, 픽스처, 실행 스냅샷

docs/structure.md

저장소 구조와 의존성 방향

docs/configuration.md

환경 변수와 모든 make 목표

docs/deploy.md

Docker, Render, GitHub Pages, 두 페이지 빌드

rezo 팀

라이선스

MIT

Related MCP Connectors

Related MCP Servers