Skip to content

fix(mcp): 인자 검증 3건 — page:-1 이 문서 전체를 성공으로 돌려주고, '-' 로 시작하는 검색어는 아예 못 찾는다 - #3727

Closed
kevin9327 wants to merge 1 commit into
edwardkim:develfrom
kevin9327:pr/mcp-arg-validation
Closed

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

요약

MCP 인자 검증 결함 3건입니다. 공통점은 조용히 틀린 답을 성공으로 돌려준다는 것 — 호출자는 isError 도 경고도 못 받아 오타를 알아챌 방법이 없습니다.

① 강제변환이 "생략"과 "형식 오류"를 같은 None 으로 뭉갠다

hwp_doc_text {"docId":"doc-1","page": 0}      → isError=false, 1쪽        (정상)
hwp_doc_text {"docId":"doc-1","page": -1}     → isError=false, 16쪽 전체  ← 오타가 "생략"이 됨
hwp_doc_text {"docId":"doc-1","page": 2.5}    → isError=false, 16쪽 전체
hwp_doc_text {"docId":"doc-1","page": "3"}    → isError=false, 16쪽 전체

hwp_doc_search {... "caseSensitive": "false"} → isError=false, 봉투는 caseSensitive:true
hwp_doc_set_cell {"table":-1,"row":0,"col":0} → "table/row/col 이 필요합니다"  ← 셋 다 보냈는데
hwp_doc_set_cell {... "keepStyle": "true"}    → isError=false 로 통과 후 검정 정규화

한 쪽만 달라던 요청이 문서 전체를 받아 컨텍스트가 터지고, 축을 끄라고 보낸 요청이 켜진 채 실행됩니다. 치환 쪽은 문서가 바뀌는 축이라 되돌릴 수 없습니다 — caseSensitive 오타로 대상 집합이 달라진 채 IR 에 누적되고 hwp_doc_save 가 그대로 디스크에 굳힙니다. keepStyle 오타는 "스타일 상속 유지" 요청을 조용히 검정 정규화로 되돌려 셀 서식을 지웁니다.

hwp_split_document 선언이 0 기준인데 실행은 1 기준

선언:  "from": { "minimum": 0, "description": "시작 쪽 (0 기준, 포함)" }
실행:  extract-pages --from 0 --to 1  →  쪽 범위가 잘못됐습니다: 0..1 (1 기준, from <= to)   exit 1

선언된 최솟값이 100% 실패하는 값입니다. 선언만 읽고 호출을 조립하는 에이전트는 첫 호출에서 exit 1 을 밟고, 1 을 넣으면 이번엔 조용히 한 쪽씩 어긋납니다.

hwp_search 검색어가 위치 argv 라 '-' 로 시작하면 플래그로 먹힌다

query:"-i"   → exit 2  사용법: rhwp search ...
query:"-표"  → exit 2  알 수 없는 옵션: -표

# CLI 직접 호출은 더 나쁩니다 — exit 0 으로 오답을 정답처럼 돌려줍니다:
rhwp search doc.hwp "-i" "The" --json  →  {"caseSensitive":false,"query":"The",...}
rhwp search doc.hwp "The" --json       →  {"caseSensitive":true, "query":"The",...}   (대조군)

스키마는 minLength:1 문자열이면 무엇이든 약속하는데, 하이픈으로 시작하는 검색어는 아예 검색할 수 없습니다.

수정

  • opt_u64/opt_bool/req_u64 공통 계약 — 있는데 틀린 값은 어느 인자가 왜 틀렸는지 지목하며 거부하고, null 만 "생략"으로 관용합니다(다수 MCP 호스트가 미지정 선택 인자를 null 로 직렬화하므로, 이를 오류로 만들면 멀쩡한 호출이 깨집니다). 정수는 3.0 처럼 소수부 없는 음이 아닌 값까지 받습니다 — 파이썬 계열 호스트가 정수를 그렇게 보내고 JSON Schema 도 integer 로 인정합니다.
  • 선언을 실행에 맞춥니다(minimum: 1, 1 기준). 출시된 CLI 동작을 건드리지 않는 가장 싼 봉합입니다. 다만 세션 도구(hwp_doc_text·hwp_doc_render_page)의 page 는 0 기준이라 MCP 표면이 혼합 기수가 됩니다 — 고칠 수 없는 사실이므로 설명에 명시해 에이전트가 섞지 않게 했습니다.
  • search 파서에 POSIX -- 종결자 + 템플릿을 ["search","{path}","--json","--","{query}"] 로. -- 뒤는 전부 위치 인자이므로 --json 은 반드시 구분자 앞입니다(뒤에 두면 세 번째 위치 인자가 되어 "인자가 너무 많습니다").

AFTER

page:-1                     → isError:true  "page 는 0 이상의 정수여야 합니다 (받은 값: -1)"
caseSensitive:"false"       → isError:true  "caseSensitive 는 true 또는 false 여야 합니다 (받은 값: \"false\")"
hwp_search query:"-i"       → isError:false, query="-i", caseSensitive=true
search --json --ignore-case -- "The"  → query="The", caseSensitive=false   (종결자 앞 옵션은 그대로 옵션)

검증

  • cargo test --test mcp_arg_validation_contract9/9 (신설)
  • 인접 계약 mcp_server·mcp_session_query·mcp_session_setcell·mcp_session_view·search_json·split_document_tool — 32/32 무회귀
  • cargo clippy --profile release-test --bin rhwp — 경고 0 / cargo fmt --check — 통과

비호환 주의

지금까지 오타를 조용히 무시하던 호출은 이제 isError 를 받습니다 — 그것이 이 PR 의 목적입니다. 유효한 입력의 동작은 그대로이고, 기존 set_cell_rejects_numeric_indices_before_narrowing(row 65536 → u16 안내)도 통과합니다.

🤖 Generated with Claude Code

① 강제변환이 "생략"과 "형식 오류"를 같은 None 으로 뭉갰다. 실측:
   hwp_doc_text {"page":-1|2.5|"3"|true} → isError=false 로 16쪽 전체 반환
   (정상 page:0 은 1쪽). caseSensitive:"false" → 봉투가 caseSensitive:true 를
   성공으로 보고 — 검색·치환 대상 집합이 조용히 달라진다. set_cell {"table":-1,…}
   → 셋 다 보냈는데 "table/row/col 이 필요합니다". keepStyle:"true" → 통과 후
   검정 정규화(셀 서식이 말없이 사라짐).
② hwp_split_document 선언이 minimum:0·"0 기준" 인데 CLI/코어는 1 기준이라 0 을
   거부한다 — 선언 최솟값이 100% 실패하는 값이었다.
③ hwp_search 배선이 ["search","{path}","{query}","--json"] 라 '-' 로 시작하는
   검색어가 플래그로 먹혔다. query:"-i" 는 대소문자 축을 조용히 뒤집고,
   "-표" 는 "알 수 없는 옵션" 으로 죽어 아예 검색할 수 없었다.

- opt_u64/opt_bool/req_u64 공통 계약: 있는데 틀린 값은 어느 인자가 왜 틀렸는지
  지목하며 거부, null 만 "생략" 으로 관용(호스트가 미지정 인자를 null 로 보낸다).
  정수는 3.0 처럼 소수부 없는 값까지 허용.
- 선언을 실행에 맞춘다(minimum:1, "1 기준") + 세션 page 는 0 기준임을 설명에 명시.
- search 파서에 POSIX `--` 종결자. 종결자 뒤는 전부 위치 인자이므로 템플릿의
  --json 을 구분자 앞으로 옮긴다.
- 계약 테스트 9종 신설.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jangster77
jangster77 self-requested a review August 1, 2026 14:17
@jangster77 jangster77 closed this Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants