* Add a guided Hola Poke Yeoksam skill without widening repo scope Issue #120 only needs a repository skill payload, discoverability docs, and regression coverage. This change adds the new skill, wires it into existing docs surfaces, and locks the remote-MCP-only contract in tests so future edits keep the phone-only event flow and verbatim message relay behavior. Constraint: The upstream Hola Poke flow lives on a remote MCP server, so this repo should not add proxy/runtime code Constraint: Tests must be written before refining the new docs/skill wording Rejected: Add local package or proxy support for Hola Poke | would over-scope a docs-only skill addition Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep this skill limited to 올라포케 역삼점 and treat the MCP response message as the event source of truth Tested: node --test scripts/skill-docs.test.js --test-name-pattern='hola-poke-yeoksam' Tested: npm run ci Tested: Live MCP initialize/tools/list/get_menu/get_shop_info/enter_event(phone_format) smoke checks against https://hola-poke-yeoksam-skill.onrender.com/mcp Not-tested: Successful live event entry with a real phone number * Help users find nearby public restrooms from Korean location queries This adds a new public-restroom-nearby skill and reusable package that resolves a user-provided location, narrows the official 공중화장실정보 dataset by region when possible, and ranks nearby restroom results with opening-time hints and map links. Constraint: Must use free official/open surfaces without introducing new dependencies Constraint: Must follow TDD and keep release/docs metadata aligned in the same change Rejected: Add a proxy route first | direct official CSV access already works and keeps scope narrower Rejected: Use nationwide-only ranking without regional narrowing | too much noisy data for dense urban anchors Confidence: high Scope-risk: moderate Reversibility: clean Directive: If Kakao place-panel or localdata CSV schema changes, update parser fixtures before broad logic changes Tested: npm run ci; live smoke via searchNearbyPublicRestroomsByLocationQuery('광화문', { limit: 3 }); architect review APPROVED Not-tested: Non-Seoul live smoke across every regional orgCode * Pin the Hola Poke MCP contract in repo-owned regression fixtures The earlier issue #120 regression only matched prose, so this follow-up records the verified remote MCP tool/result snapshot in a checked-in fixture and makes both docs surfaces byte-align to it. That keeps the discoverability docs honest while turning the review claim into a real contract lock for tools/list, get_menu, get_shop_info, and the invalid-phone event flow. Constraint: The upstream remote MCP server can change independently of this repo Rejected: Keep prose-only regex checks | would not catch contract drift Confidence: high Scope-risk: narrow Reversibility: clean Directive: Refresh the fixture, both JSON fences, and the live-smoke evidence together whenever the upstream contract changes Tested: node --test scripts/skill-docs.test.js --test-name-pattern='hola-poke-yeoksam'; npm run ci; live MCP smoke check against https://hola-poke-yeoksam-skill.onrender.com/mcp (initialize, tools/list, get_menu, get_shop_info, invalid enter_event) Not-tested: Successful enter_event with a real phone number (intentionally avoided to prevent live event participation) * Keep nearby restroom lookups resilient to flaky Kakao place panels The review caught two regressions in the new public-restroom-nearby package: a single broken Kakao panel aborted anchor resolution, and coordinate search dropped maxDistanceMeters before normalization. This change adds targeted regression coverage first, keeps per-candidate HTTP failures recoverable, and hardens request errors with explicit status/url metadata so fallback logic no longer depends on parsing error strings. Constraint: Must preserve the published package surface and keep the fix scoped to PR #123 follow-up Rejected: Swallow all panel errors | would hide non-HTTP failures like network faults Rejected: Parse request error messages for status codes | brittle coupling to string formatting Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep recoverable Kakao panel handling aligned with request() error annotations if request() changes again Tested: npm test --workspace public-restroom-nearby Tested: npm run ci Tested: live smoke searchNearbyPublicRestroomsByLocationQuery('광화문', { limit: 3 }) Tested: LSP diagnostics on packages/public-restroom-nearby/src/index.js and test/index.test.js Not-tested: Live Kakao fallback against a real upstream 5xx place-panel response * Keep the Hola Poke contract claims aligned with verified coverage The reviewed fixture-based regression already locks the documented remote snapshot, but the docs still implied the enter_event success path had live proof. Narrow the docs and the regression so they explicitly say the success fields are pinned by the recorded snapshot while the live smoke only verifies the invalid-phone retry path. Constraint: Live success-path verification would trigger a real event entry and is intentionally avoided Rejected: Leave the broader wording in place | review feedback showed it overstated the live evidence Confidence: high Scope-risk: narrow Reversibility: clean Directive: If a safe non-mutating success-path probe becomes available, update the docs and fixture wording together Tested: node --test scripts/skill-docs.test.js --test-name-pattern='hola-poke-yeoksam'; npm run ci; live MCP smoke against https://hola-poke-yeoksam-skill.onrender.com/mcp (initialize, tools/list, get_menu subset, get_shop_info subset, invalid enter_event) Not-tested: Real enter_event success-path invocation * Document the restroom distance-cap contract with regression coverage The approved issue-117 code fix already restored maxDistanceMeters behavior, but the published docs did not lock or explain that contract. This follow-up adds a failing-first doc regression, then updates the feature guide and package README with the verified 100m example so users and future reviewers see the same behavior the package now ships. Constraint: Must stay scoped to the existing PR #123 follow-up without reopening the implementation surface Rejected: Leave the behavior implicit in code/tests only | published docs would lag the verified contract Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep the public-restroom-nearby docs and skill-docs regression aligned with live maxDistanceMeters smoke evidence if the sample query changes Tested: node --test scripts/skill-docs.test.js (red then green) Tested: npm test --workspace public-restroom-nearby Tested: npm run ci Tested: live smoke searchNearbyPublicRestroomsByLocationQuery('광화문', { limit: 3 }) Tested: live smoke searchNearbyPublicRestroomsByLocationQuery('광화문', { limit: 3, maxDistanceMeters: 100 }) Tested: architect review APPROVED Not-tested: Alternative landmark queries with a non-zero maxDistanceMeters hit set * Expose KRX partial failures instead of misreporting stock lookups The Korean stock proxy used to silently drop failed market snapshots during search and could turn an empty holiday trade snapshot into a 502 by falling back into base-info lookup. This change surfaces degraded market metadata on partial search success, short-circuits empty trade snapshots to not_found, and refreshes the user docs to use a real trading day in examples. Constraint: KOSPI base-info approval is granted separately from other KRX routes Constraint: Healthy markets should still return usable search results during a partial outage Rejected: Return 502 on every partial search failure | hides still-usable markets and breaks current clients unnecessarily Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep degraded search metadata when any market snapshot fetch fails so partial outages stay visible Tested: npm test --workspace k-skill-proxy Tested: node --test scripts/skill-docs.test.js Tested: npm run ci Not-tested: Live KOSPI base-info behavior after the new KRX permission is approved * Adopt kordoc for the hwp skill workflow Issue #119 replaces the previous HWP guidance with kordoc so the skill matches the newer agent-native document flow. The docs and regression tests now center the HWP skill on kordoc parsing, JSON extraction, diffing, form filling, and Markdown-to-HWPX round-tripping, while the install/source references stay in sync. Constraint: The repository treats skill behavior as documentation contracts backed by regression tests Constraint: The requested branch/PR flow must target dev with TDD and verified execution evidence Rejected: Keep @ohah/hwpjs or hwp-mcp as fallback guidance | issue #119 explicitly approves replacing the prior stack with kordoc Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep future hwp skill/docs/tests aligned to a single kordoc-first contract unless a new issue explicitly reintroduces multi-backend routing Tested: node --test scripts/skill-docs.test.js; npm run ci; temp-dir kordoc roundtrip via markdownToHwpx -> sample.hwpx -> kordoc CLI markdown output; architect review APPROVED Not-tested: Live parsing of user-provided proprietary HWP/HWPX samples outside the generated roundtrip fixture * Prevent degraded stock search outages from sticking in cache Reviewer feedback showed that partial KRX market failures could be cached as full search answers, masking recovery on the next identical request. This change adds a regression that fails first, skips route-level caching for degraded search payloads, and keeps the trade-info empty-snapshot contract documented alongside the partial-failure response semantics. Constraint: Existing PR #124 already targets dev and must remain the follow-up lane for issue #99 Constraint: Proxy behavior must stay read-only and dependency-free Rejected: Cache degraded search payloads for a short TTL | still risks transient false negatives during the TTL window Rejected: Broaden trade-info fallback behavior | empty snapshots should stay explicit not_found results Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep degraded search responses out of the long-lived route cache unless a future design adds explicit revalidation semantics Tested: npm test --workspace k-skill-proxy; node --test scripts/skill-docs.test.js; npm run ci; explicit buildServer degraded-search recovery repro Not-tested: Live KRX production endpoints from this branch * Align HWP docs with the published kordoc surface The issue #119 follow-up needs the repository contract to match what the currently published kordoc package actually supports. This narrows the HWP skill/docs/tests to the verified install requirement and supported CLI/Node API surfaces, and removes unsupported fill/mcp claims. Constraint: Published kordoc CLI fails at startup without pdfjs-dist Constraint: Docs/tests must reflect the current npm package behavior, not intended future features Rejected: Keep fill/mcp examples with caveats | still documents unsupported entrypoints Confidence: high Scope-risk: narrow Directive: Reintroduce fill/mcp docs only after verifying the published package exposes them in both CLI and Node API Tested: node --test scripts/skill-docs.test.js; npm run ci; temp-dir clean install smoke; temp-dir kordoc+pdfjs-dist watch/parse/extractFormFields/compare/markdownToHwpx/roundtrip smoke; Claude architect review Not-tested: Real-world HWPX template that produces non-empty extractFormFields output * Keep HWP docs runnable against the published kordoc package The follow-up closes the last runnable-contract gaps from review by documenting the working one-shot npx form and separating Node API examples into a local project install path. The regression suite now locks both install notes so future edits do not drift back to broken command shapes. Constraint: Published kordoc CLI still requires pdfjs-dist at startup Constraint: Global NODE_PATH does not make ESM imports from kordoc resolvable in the documented examples Rejected: Keep bare `npx kordoc` examples | fails in a clean environment Rejected: Keep global-install Node API guidance | ESM import remains unresolved Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep HWP docs aligned to verified published kordoc surfaces until the package contract changes upstream Tested: node --test scripts/skill-docs.test.js Tested: npm run ci Tested: temp-dir local npm install kordoc pdfjs-dist plus markdownToHwpx -> sample.hwpx -> one-shot kordoc roundtrip smoke Not-tested: upstream unpublished kordoc features beyond the verified CLI and Node API surfaces * Add Korean scholarship search skill and reporting workflow (#116) * Add nationwide scholarship search skill workflow * Rename scholarship skill to 장학금 주세요 쮜에발 * Fix scholarship skill validation in CI * Trigger GitHub PR diff refresh after dev rebase on main * Fix scholarship helper status handling and test coverage * Use KST as scholarship helper default date basis * Rename scholarship skill display name --------- Co-authored-by: Jeffrey (Dongkyu) Kim <vkehfdl1@gmail.com> * Feature/#121 (#127) * Recover KakaoTalk mac skill auth when upstream user_id detection fails Issue #121 reproduces on a real MacBook because `kakaocli auth` can fail even when the encrypted hex-named DB exists. This change adds a thin repo-owned helper that recovers the active user_id from plist revision hashes, caches the validated DB/key tuple, and reuses it for read-only `kakaocli` commands. The skill and feature docs now steer users to the helper when upstream auto-detection stops at candidate key mismatch, and regression tests lock the recovery flow before the implementation. Constraint: Must stay a thin adapter around upstream kakaocli rather than forking the CLI Constraint: Must verify on a real local macOS KakaoTalk install where issue #121 reproduces Rejected: Full kakaocli reimplementation inside k-skill | too broad for the user_id/key-derivation failure scope Rejected: Docs-only workaround | does not actually fix the broken auth path for users Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep this helper limited to auth/key recovery and read-only passthrough unless upstream gaps widen materially Tested: python3 -m unittest scripts.test_kakaotalk_mac Tested: node --test scripts/skill-docs.test.js Tested: npm run ci Tested: python3 scripts/kakaotalk_mac.py auth --refresh --max-user-id 800000000 --workers 8 --chunk-size 2000000 Tested: python3 scripts/kakaotalk_mac.py chats --limit 1 --json Not-tested: Other kakaocli subcommands beyond auth/chats/messages/search/query/schema * Protect the KakaoTalk helper's safe recovery path Address the PR follow-up by treating malformed auth cache files as cache misses, removing write-capable passthrough from the wrapper surface, and redacting human-readable auth output so the cached SQLCipher key is not echoed back into terminal history. The docs and regression suite now describe and enforce the read-only contract that the helper is meant to preserve. Constraint: Helper must remain a read-only recovery wrapper around local kakaocli access Rejected: Keep query support with SQL validation | still leaves a risky write-capable escape hatch Confidence: high Scope-risk: narrow Reversibility: clean Directive: Do not re-expose arbitrary SQL passthrough or print the SQLCipher key in default text output Tested: python3 -m unittest scripts.test_kakaotalk_mac; node --test scripts/skill-docs.test.js; npm run ci; python3 scripts/kakaotalk_mac.py auth --refresh --max-user-id 800000000 --workers 8 --chunk-size 2000000; python3 scripts/kakaotalk_mac.py chats --limit 1 --json; python3 scripts/kakaotalk_mac.py auth --cache-path <bad-json>; python3 scripts/kakaotalk_mac.py query --help Not-tested: External automation consumers that depend on shell/json auth output beyond the documented helper flows * Lock the helper CLI surface against accidental regressions The approved issue #121 fixes already hardened the KakaoTalk Mac helper, but the test suite still only exercised the passthrough validator directly. Add an explicit parser-level regression so the public CLI contract stays read-only and `query` cannot quietly reappear in future edits. Constraint: Follow-up is on the existing feature/#121 PR branch and must stay minimal Rejected: Re-open helper implementation changes | current code already satisfies the approved review findings Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep parser exposure tests aligned with READ_ONLY_COMMANDS whenever helper subcommands change Tested: python3 -m unittest scripts.test_kakaotalk_mac; node --test scripts/skill-docs.test.js; npm run ci; python3 scripts/kakaotalk_mac.py auth --refresh --max-user-id 800000000 --workers 8 --chunk-size 2000000; python3 scripts/kakaotalk_mac.py chats --limit 1 --json; python3 scripts/kakaotalk_mac.py auth --cache-path <bad-json> Not-tested: No new production code paths changed in this follow-up * Honor explicit Kakao auth recovery overrides The helper now treats manual auth overrides as a cache-bypassing recovery request and rejects invalid brute-force tuning flags at the CLI boundary so users get deterministic behavior instead of stale cached tuples or Python tracebacks. Regression coverage locks both paths before the PR follow-up lands. Constraint: The helper must remain a thin read-only wrapper around kakaocli auth recovery Rejected: Require --refresh whenever --user-id/--uuid is passed | worse UX than honoring overrides directly Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep explicit auth overrides ahead of cache reuse unless the CLI contract is redesigned and documented Tested: python3 -m unittest scripts.test_kakaotalk_mac; node --test scripts/skill-docs.test.js; npm run ci; python3 scripts/kakaotalk_mac.py auth --refresh --max-user-id 800000000 --workers 8 --chunk-size 2000000; python3 scripts/kakaotalk_mac.py chats --limit 1 --json; python3 scripts/kakaotalk_mac.py auth --cache-path <bad-json>; python3 scripts/kakaotalk_mac.py auth --refresh --max-user-id -1; python3 scripts/kakaotalk_mac.py auth --refresh --workers 2 --chunk-size 0 --max-user-id 10; python3 scripts/kakaotalk_mac.py auth --cache-path <temp-cache> --user-id 999; python3 scripts/kakaotalk_mac.py auth --cache-path <temp-cache> --uuid <live-uuid> Not-tested: Manual override success with a truly alternate valid user_id/uuid pair on a multi-account local install * Feature/#129 (#131) * Add official KBL results support so basketball queries use live league data Issue #129 needs a read-only skill and reusable package for KBL schedules, results, and standings. The implementation follows the existing sports package pattern and uses the league's live JSON APIs after verifying they respond successfully in real requests. Constraint: Must use official KBL JSON surfaces before considering scraping Constraint: Packaging changes must pass npm run ci and include docs plus Changesets updates Rejected: Browser scraping first | official api.kbl.or.kr endpoints are live and simpler to maintain Rejected: Reuse KBO/K League package shapes verbatim | KBL payload and team/status fields differ materially Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep seasonGrade=1 as the default KBL path unless future docs/tests explicitly widen to D-League flows Tested: npm run ci; npm run lint --workspace kbl-results; npm test --workspace kbl-results; live getKBLSummary("2026-04-01", { team: "KCC", includeStandings: true }) Not-tested: Historical standings snapshots for past seasons via alternative KBL endpoints * Prevent optional standings lookups from over-fetching the KBL API The new kbl-results summary helper exposes includeStandings=false, so the regression suite now proves that path stays schedule-only and never calls the standings endpoint when the caller opts out. Constraint: The KBL package should preserve the caller's no-standings contract Rejected: Rely on manual inspection of the helper options | a targeted test is cheaper and safer Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep includeStandings=false side-effect free unless the public API contract changes explicitly Tested: npm test --workspace kbl-results; npm run lint --workspace kbl-results Not-tested: Full-repo CI before stacking this commit onto the rebased branch * Add Naver Shopping price comparison skill * Use Naver Shopping BFF fallback * Fix naver shopping BFF page and sort fallback * Clarify Naver OpenAPI review sort fallback * Add library book search skill * Add Data4Library route regression coverage * Fix Data4Library book-exists ISBN-10 handling * Refactor Coupang skill to retention MCP layer * Add Coupang MCP wrapper follow-up coverage * Clarify Coupang wrapper init guidance * Document Coupang MCP init examples * Add parking lot search skill * Add korean-privacy-terms skill regression tests * Add korean-privacy-terms thin-wrapper skill * Document korean-privacy-terms skill across repo docs * Bundle Apache-2.0 LICENSE with korean-privacy-terms wrapper Addresses PR #149 review SHOULD FIX: ship the Apache-2.0 LICENSE text alongside the thin wrapper so Apache License 2.0 §4(a) ('give any other recipients of the Work or Derivative Works a copy of this License') is satisfied even before `install.sh` fetches the upstream payload. - Copy upstream LICENSE verbatim to `korean-privacy-terms/LICENSE.upstream` (byte-for-byte identical to upstream at pinned SHA e390f7b9feb825e368c26726363ea5ce11a34083; SHA256 35ef947614c2f14df01c5fc553f987f644f0c9f6b011adda397bd788a87f1510). - Update SKILL.md Notes to link LICENSE.upstream, clarify that repo-root LICENSE (MIT) is k-skill's own license not this skill's, and document that nested upstream SKILL.md is not discovered by agent platforms. - Document the home-path `bash ~/.claude/skills/.../install.sh` variant in SKILL.md so users who pulled the wrapper via `npx skills add --skill` can install without a repo checkout (installer already resolves ${BASH_SOURCE[0]} absolutely). - Update docs/features/korean-privacy-terms.md to document LICENSE.upstream and the §4(a) rationale. - Strengthen skill-docs regression tests (NICE TO HAVE items from review): * Reject placeholder pins (all-zero / all-f 40-char strings). * Assert the literal upstream clone URL (https://github.com/kimlawtech/korean-privacy-terms.git). * Assert `git clone --filter=blob:none` is used for blobless fetches. * Add new regression test that verifies LICENSE.upstream exists, matches the Apache-2.0 preamble / §4 / APPENDIX structure, and is referenced from both SKILL.md and the feature doc. * Assert APPENDIX anchor in korean-privacy-terms LICENSE.upstream Close Round 3 NICE TO HAVE from PR #149. The LICENSE.upstream regression block asserted preamble, Version 2.0, Redistribution, END OF TERMS, and Copyright 2026 kimlawtech but not the APPENDIX anchor at LICENSE.upstream:179, even though the Round 1 follow-up and Round 2 review collectively described 'APPENDIX structure verification'. Adding this one assertion closes that claim/test parity gap and acts as tamper-detection if upstream reformats LICENSE later. Verified with TDD: temporarily stripped APPENDIX line from LICENSE.upstream, confirmed test 108 FAILS with the expected regex mismatch, then restored and re-confirmed 109/109 GREEN. Byte-for-byte identity with upstream LICENSE still holds (SHA256 35ef947614c2f14df01c5fc553f987f644f0c9f6b011adda397bd788a87f1510). npm run ci exit 0 with 357 ok subtests (unchanged baseline, additive assertion within existing test block). * Fix extractDataGoItems to handle current data.go.kr JSON shapes The MFDS data.go.kr drug and food endpoints now return body.items as a flat array (DrbEasyDrugInfoService, SafeStadDrugService) or an array of {item: {...}} wrappers (PrsecImproptFoodInfoService03), instead of the legacy {items: {item: [...]}} XML→JSON auto-convert shape. Our extractDataGoItems was still looking for body.items.item, so it returned [] for every entry, silently breaking: - /v1/mfds/drug-safety/lookup - /v1/mfds/food-safety/search (improperFood portion) Update extractDataGoItems to accept all three shapes and refresh the mock fixtures in server.test.js to match what upstream actually returns, while adding a backward-compat test for the legacy shape. Note: this does not resolve the remaining FOODSAFETYKOREA_API_KEY being rejected by upstream (issue #148 core symptom) - that is a separate operational key rotation on the proxy server. * Make proxy cache failure-aware and require route-prefixed cache keys Two related issues surfaced while investigating issue #148: 1. Transient upstream failures were being cached for the full 5-minute TTL because every route handler called cache.set() unconditionally with whatever payload came back - including empty items + warnings from a flaky upstream like openapi.foodsafetykorea.go.kr. The user would then see "empty + warning" for 5 minutes even after upstream recovered. 2. makeCacheKey(payload) hashes the whole payload, but fine-dust/report was the only route calling it without a "route" prefix (makeCacheKey(normalized) instead of makeCacheKey({ route: "fine-dust-report", ...normalized })). Different routes with the same normalized shape could collide. Fix both globally in the cache layer so every current and future route benefits without per-route edits: - createMemoryCache.set rejects any payload that isFailureResponse considers a failure (explicit error field, upstream.degraded flag, or empty items alongside warnings). Returns false on reject, true on accept, so callers can observe the decision if needed. - makeCacheKey now throws if payload.route is missing or empty. This catches the fine-dust inconsistency and prevents new routes from reintroducing it. - fine-dust/report now passes `route: "fine-dust-report"` like every other route. New tests: - makeCacheKey asserts distinct routes produce distinct keys and throws without a route. - isFailureResponse covers all failure signatures plus graceful- fallback cases (items present alongside warnings) that must stay cacheable. - createMemoryCache.set refuses each failure shape and still stores healthy payloads. - End-to-end: food-safety/search with a flaky recall upstream serves the upstream failure, retries live when upstream recovers, and only caches once the payload is healthy. TTL itself is unchanged - the value still protects upstream rate limits; it just no longer amplifies transient errors. * Document Coupang hosted fallback contract and affiliate disclosure retention-corp/coupang_partners#1 is merged, so upstream now transparently falls back to the Retention Corp hosted backend at https://a.retn.kr/v1/public/assist when Coupang Partners API credentials are missing. The k-skill wrapper already passes environment variables through unchanged, so this commit lines up the documented contract with the actual two-path behavior without changing runtime logic. - SKILL.md and docs/features/coupang-product-search.md describe both execution paths (operator local HMAC vs credentialless hosted fallback), the honored OPENCLAW_SHOPPING_* env vars, the allowlist client-id convention including the k-skill-specific coupang-mcp-fallback value, and the mandatory affiliate disclosure when a.retn.kr/s/ shortlinks or lptag=AF deeplinks appear in responses. - docs/sources.md adds the hosted assist endpoint and the merged upstream PR so the source surface stays truthful. - README.md reflects the 선택사항 semantics for the 쿠팡 상품 검색 row and extends the column legend so 선택사항 is distinct from 불필요. - coupang_partners_mcp.py expands its --help epilog so operators discover the honored upstream env vars without reading the wrapper source; no runtime behavior change. - scripts/test_coupang_partners_mcp_wrapper.py locks env pass-through as a regression, asserts the new --help contract, and adds an opt-in K_SKILL_COUPANG_SMOKE=1 live smoke test that verifies the credentialless hosted path returns a Coupang deeplink. - scripts/skill-docs.test.js extends the docs regression to require the hosted assist URL, OPENCLAW_SHOPPING_* env prefix, affiliate disclosure wording, and hosted fallback concept while keeping the yuju777 HF Space negative assertion. Verified: npm run ci exits 0, live smoke test (K_SKILL_COUPANG_SMOKE=1) returns a.retn.kr/s/ shortlinks via credentialless wrapper, and manual env -u COUPANG_ACCESS_KEY -u COUPANG_SECRET_KEY call returns isRocket+lptag=AF3727577 responses through the hosted fallback. Refs: #134 * Drop non-allowlisted coupang-mcp-fallback recommendation from hosted fallback docs Direct probes against https://a.retn.kr/v1/public/assist confirmed that X-OpenClaw-Client-Id: coupang-mcp-fallback returns HTTP 403 Client is not allowlisted, while the upstream default openclaw-skill returns HTTP 200. The default wrapper path already works because upstream falls back to openclaw-skill, but the explicit recommendation in SKILL.md and the feature doc was luring users to a 403 path. Remove the dead recommendation and lock in the working configuration: - Docs describe openclaw-skill as the upstream-allowlisted default and note that k-skill does not override OPENCLAW_SHOPPING_CLIENT_ID. - Wrapper --help epilog drops the Suggested k-skill value line and documents openclaw-skill as the allowlist value in play. - New skill-docs regression asserts coupang-mcp-fallback is absent from SKILL.md, the feature doc, the wrapper, and docs/sources.md while openclaw-skill is documented across all three narrative surfaces. - New Python wrapper regression asserts --help drops the dead value and surfaces openclaw-skill so the constraint stays locked. - Existing env-forwarding test uses openclaw-skill as the pass-through sentinel so the repo no longer ships the non-allowlisted string at all. --------- Co-authored-by: minsing-jin <ironman0722@naver.com>
9.5 KiB
쿠팡 상품 검색 가이드
이 기능으로 할 수 있는 일
retention-corp/coupang_partners의 로컬 Coupang MCP 호환 레이어를 이용해 쿠팡 상품 조회 도구를 실행한다. 기존 HF Space 기반 coupang-mcp 서버 대신 upstream 저장소의 bin/coupang_mcp.py를 local://coupang-mcp 계약으로 호출한다.
- 키워드 상품 검색
- 로켓배송 전용 필터 검색
- 가격대 범위 검색
- 상품 비교표 생성
- 카테고리별 베스트 상품, 골드박스 당일 특가
- 인기 검색어/계절 상품 추천
동작 방식
Codex/Claude Code → coupang_partners_mcp.py → retention-corp/coupang_partners checkout → bin/coupang_mcp.py → local://coupang-mcp
├─ operator: Coupang Partners API (local HMAC)
└─ credentialless: hosted fallback → https://a.retn.kr/v1/public/assist
- 구형 hosted endpoint 제거 — 이전 HF Space 기반 MCP 서버를 사용하지 않는다.
- upstream 고정 — 래퍼는
https://github.com/retention-corp/coupang_partners.git를 clone/update한 뒤 upstream CLI에 위임한다. - 이중 실행 경로 —
COUPANG_ACCESS_KEY/COUPANG_SECRET_KEY가 둘 다 있으면 upstream이 로컬 HMAC으로 Coupang Partners API를 호출하고, 없으면 자동으로 Retention Corp의 hosted 백엔드(https://a.retn.kr/v1/public/assist)로 떨어져 공개 추천/검색을 반환한다(hosted fallback). 래퍼는 두 경로를 자동 선택한다. - allowlist — hosted fallback은
X-OpenClaw-Client-Idallowlist로 게이트되어 있다. upstream이 기본으로 실어 보내는openclaw-skill값이 현재 Retention Corp allowlist에 등록되어 있어 credentialless 호출이 200을 받는다. k-skill 래퍼는 이 기본값을 그대로 사용하고OPENCLAW_SHOPPING_CLIENT_ID를 오버라이드하지 않는다. Retention Corp 측 allowlist 정책이 바뀌면 그때 맞춰 가이드를 갱신한다. - secret은 runtime 환경변수 — 운영자 모드에서는
COUPANG_ACCESS_KEY,COUPANG_SECRET_KEY를 환경변수로 주입한다. 키를 저장소나 답변에 노출하지 않는다. - 계약 확인 우선 —
tools/init명령으로 로컬 MCP 호환 도구 목록과 JSON-RPC payload 형태를 먼저 확인한다.
MCP 계약
local://coupang-mcp
프로토콜 호환 버전: MCP 2025-03-26. 네트워크 Streamable HTTP 서버가 아니라 upstream 저장소의 로컬 CLI가 같은 도구 이름을 제공한다.
환경변수
| 환경변수 | 역할 | 기본값 |
|---|---|---|
COUPANG_ACCESS_KEY, COUPANG_SECRET_KEY |
운영자 Coupang Partners API 크리덴셜. 둘 다 있을 때만 로컬 HMAC 경로가 활성화된다. | 없음 (없으면 hosted fallback) |
OPENCLAW_SHOPPING_CLIENT_ID |
hosted fallback의 X-OpenClaw-Client-Id. upstream이 openclaw-skill을 기본으로 실어 보내며 이 값이 현재 Retention Corp allowlist에 등록되어 있다. k-skill 래퍼는 이 변수를 오버라이드하지 않는다. |
openclaw-skill |
OPENCLAW_SHOPPING_FORCE_HOSTED |
1이면 키가 있어도 hosted 경로를 강제한다. |
비어있음 |
OPENCLAW_SHOPPING_BASE_URL |
hosted 백엔드 base URL 오버라이드. 스테이징/로컬 backend 테스트용. | https://a.retn.kr |
k-skill 쪽 래퍼(coupang_partners_mcp.py)는 위 환경변수를 오버라이드/디폴트 설정 없이 그대로 upstream에 전달한다. 사용자가 export한 값이 최종 결정을 가져간다.
사용 가능한 도구
| 도구명 | CLI 명령 | 기능 | 사용 예시 |
|---|---|---|---|
search_coupang_products |
search |
일반 상품 검색 | python3 coupang-product-search/scripts/coupang_partners_mcp.py search "맥북" |
search_coupang_rocket |
rocket |
로켓배송만 필터링 | python3 coupang-product-search/scripts/coupang_partners_mcp.py rocket "에어팟" |
search_coupang_budget |
budget |
가격대 범위 검색 | python3 coupang-product-search/scripts/coupang_partners_mcp.py budget "키보드" --max-price 100000 |
compare_coupang_products |
compare |
상품 비교표 생성 | python3 coupang-product-search/scripts/coupang_partners_mcp.py compare "아이패드 vs 갤럭시탭" |
get_coupang_recommendations |
recommendations |
인기 검색어 제안 | python3 coupang-product-search/scripts/coupang_partners_mcp.py recommendations --category 전자제품 |
get_coupang_seasonal |
seasonal |
계절/상황별 추천 | python3 coupang-product-search/scripts/coupang_partners_mcp.py seasonal "설날 선물" |
get_coupang_best_products |
best |
카테고리별 베스트 | python3 coupang-product-search/scripts/coupang_partners_mcp.py best --category-id 1016 |
get_coupang_goldbox |
goldbox |
당일 특가 정보 | python3 coupang-product-search/scripts/coupang_partners_mcp.py goldbox --limit 10 |
주의: get_coupang_goldbox와 get_coupang_best_products는 upstream 기준 Coupang Partners API 권한이 필요한 경로이므로, 키 없이 hosted fallback으로만 실행 중이면 실패할 수 있다. 실패 메시지를 그대로 전달하고 hosted fallback이 커버하는 search/rocket/budget/compare로 우회 제안한다.
기본 흐름
- 검색어를 받는다. 너무 넓으면 용도/예산/브랜드를 먼저 물어본다.
tools와init명령으로 retention-corp/coupang_partners 로컬 MCP 도구 목록과 handshake payload를 확인한다.- 요청에 맞는 CLI 명령을 실행한다(키가 없어도 hosted fallback으로
search/rocket/budget/compare는 작동한다). data.result를 읽고 로켓배송/일반배송을 구분하여 정리한다.- 상위 3~5개 추천과 함께 가격/배송 정보, 변동 가능성, affiliate 고지를 제공한다.
호출 예시
# 1. 최초 실행: upstream checkout을 자동 clone하고 도구 목록 확인
python3 coupang-product-search/scripts/coupang_partners_mcp.py tools
python3 coupang-product-search/scripts/coupang_partners_mcp.py init
# 2. 이미 clone된 upstream을 명시해서 네트워크 없이 계약 확인
python3 coupang-product-search/scripts/coupang_partners_mcp.py \
--repo-dir ~/.cache/k-skill/coupang_partners \
--no-clone \
tools
python3 coupang-product-search/scripts/coupang_partners_mcp.py \
--repo-dir ~/.cache/k-skill/coupang_partners \
--no-clone \
init
# 3. 기존 checkout을 fast-forward로 최신화한 뒤 계약 확인
python3 coupang-product-search/scripts/coupang_partners_mcp.py \
--repo-dir ~/.cache/k-skill/coupang_partners \
--update \
tools
# 4. 상품 검색 (키 없이도 hosted fallback으로 동작)
python3 coupang-product-search/scripts/coupang_partners_mcp.py search "생수"
# 5. 로켓배송 필터
python3 coupang-product-search/scripts/coupang_partners_mcp.py rocket "에어팟"
# 6. hosted fallback 강제 (upstream 기본 allowlist client-id 유지)
OPENCLAW_SHOPPING_FORCE_HOSTED=1 \
python3 coupang-product-search/scripts/coupang_partners_mcp.py search "무선청소기"
결과 형식
upstream CLI는 다음과 같은 JSON envelope를 반환한다.
{
"ok": true,
"data": {
"session_id": "session-...",
"tool": "search_coupang_products",
"payload": {
"jsonrpc": "2.0",
"result": {
"content": [
{"type": "text", "text": "[...]"}
]
}
},
"result": []
}
}
- hosted fallback 경로는 각 상품의
productUrl에https://a.retn.kr/s/...형태의 short deeplink를 붙여 돌려준다. - operator (로컬 HMAC) 경로는
https://link.coupang.com/...?lptag=AF...형태의 직접 딥링크를 돌려준다. - 두 경로 모두 Retention Corp의 쿠팡 파트너스 채널로 트래킹된다. affiliate 고지를 반드시 포함한다.
사용자 답변은 짧은 비교표 형태로 정리한다.
## rocket (상위 후보)
1) LG전자 4K UHD 모니터
가격: 397,750원 (참고용)
보러가기: https://a.retn.kr/s/...
## normal (상위 후보)
1) 삼성전자 QHD 오디세이 G5 게이밍 모니터
가격: 283,000원 (참고용)
보러가기: https://a.retn.kr/s/...
Affiliate 고지 (필수)
hosted fallback이 반환하는 응답에는 disclosure 필드가 포함될 수 있다. 예시 문구: "파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음". 이 문구가 오면 답변에 그대로 노출한다. 응답에 disclosure가 없더라도 a.retn.kr/s/ shortlink나 lptag=AF 딥링크가 포함되는 이상 같은 취지의 문구를 반드시 포함한다.
제한사항
- 가격/품절/배송 정보는 실시간으로 바뀔 수 있다.
- 로그인, 장바구니, 결제 자동화는 지원하지 않는다.
- hosted fallback(
https://a.retn.kr/v1/public/assist)은 allowlist로 게이트되어 있어 upstream 정책에 따라 응답 형태나 client-id 요구가 바뀔 수 있다. goldbox/best-products등 Coupang Partners API 권한이 필요한 도구는 hosted fallback에서는 대체되지 않으므로 실패할 수 있다.- upstream checkout이 없고 네트워크 clone도 막힌 환경에서는
--repo-dir로 기존 checkout을 지정해야 한다.
출처
- retention-corp/coupang_partners GitHub
- 로컬 MCP 계약:
local://coupang-mcp - hosted fallback endpoint:
https://a.retn.kr/v1/public/assist - hosted fallback을 upstream에 추가한 PR: https://github.com/retention-corp/coupang_partners/pull/1 (merged)
- 래퍼:
coupang-product-search/scripts/coupang_partners_mcp.py