@nomadamas/k-skill 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/k-skill.js +60 -0
- package/package.json +37 -0
- package/skills/assembly-bill-vote-search/instruction.md +111 -0
- package/skills/assembly-bill-vote-search/skill.json +10 -0
- package/skills/biz-health-check/instruction.md +69 -0
- package/skills/biz-health-check/scripts/biz_health_check.py +163 -0
- package/skills/biz-health-check/skill.json +10 -0
- package/skills/bok-ecos-stats/instruction.md +95 -0
- package/skills/bok-ecos-stats/scripts/bok_ecos.py +302 -0
- package/skills/bok-ecos-stats/skill.json +10 -0
- package/skills/building-register-search/instruction.md +69 -0
- package/skills/building-register-search/scripts/building_register.py +309 -0
- package/skills/building-register-search/scripts/building_register_xml.py +32 -0
- package/skills/building-register-search/skill.json +9 -0
- package/skills/bunjang-search/instruction.md +154 -0
- package/skills/bunjang-search/skill.json +10 -0
- package/skills/catchtable-sniper/instruction.md +270 -0
- package/skills/catchtable-sniper/skill.json +9 -0
- package/skills/cheap-gas-nearby/instruction.md +107 -0
- package/skills/cheap-gas-nearby/skill.json +10 -0
- package/skills/corporate-registration-consulting/instruction.md +117 -0
- package/skills/corporate-registration-consulting/scripts/fill_official_hwp.py +106 -0
- package/skills/corporate-registration-consulting/skill.json +8 -0
- package/skills/coupang-product-search/instruction.md +219 -0
- package/skills/coupang-product-search/scripts/coupang_partners_mcp.py +146 -0
- package/skills/coupang-product-search/skill.json +10 -0
- package/skills/court-auction-notice-search/instruction.md +201 -0
- package/skills/court-auction-notice-search/skill.json +9 -0
- package/skills/court-payment-order-assistant/instruction.md +120 -0
- package/skills/court-payment-order-assistant/skill.json +10 -0
- package/skills/d2b-notice-search/instruction.md +117 -0
- package/skills/d2b-notice-search/skill.json +10 -0
- package/skills/daangn-cars-search/instruction.md +91 -0
- package/skills/daangn-cars-search/scripts/daangn_cars.py +72 -0
- package/skills/daangn-cars-search/skill.json +9 -0
- package/skills/daangn-jobs-search/instruction.md +90 -0
- package/skills/daangn-jobs-search/scripts/daangn_jobs.py +98 -0
- package/skills/daangn-jobs-search/skill.json +9 -0
- package/skills/daangn-realty-search/instruction.md +114 -0
- package/skills/daangn-realty-search/scripts/daangn_detail_ld.py +76 -0
- package/skills/daangn-realty-search/scripts/daangn_realty.py +216 -0
- package/skills/daangn-realty-search/scripts/daangn_relay_store.py +174 -0
- package/skills/daangn-realty-search/skill.json +8 -0
- package/skills/daangn-used-goods-search/instruction.md +90 -0
- package/skills/daangn-used-goods-search/scripts/daangn_used_goods.py +80 -0
- package/skills/daangn-used-goods-search/skill.json +9 -0
- package/skills/daishin-report-search/instruction.md +138 -0
- package/skills/daishin-report-search/skill.json +10 -0
- package/skills/daiso-product-search/instruction.md +172 -0
- package/skills/daiso-product-search/skill.json +8 -0
- package/skills/danawa-price-search/instruction.md +183 -0
- package/skills/danawa-price-search/scripts/danawa_search.py +354 -0
- package/skills/danawa-price-search/skill.json +8 -0
- package/skills/delivery-tracking/instruction.md +358 -0
- package/skills/delivery-tracking/skill.json +8 -0
- package/skills/donation-place-search/instruction.md +129 -0
- package/skills/donation-place-search/skill.json +9 -0
- package/skills/emergency-room-beds/instruction.md +82 -0
- package/skills/emergency-room-beds/skill.json +8 -0
- package/skills/ev-charger-nearby/instruction.md +80 -0
- package/skills/ev-charger-nearby/scripts/ev_charger.py +222 -0
- package/skills/ev-charger-nearby/skill.json +10 -0
- package/skills/ev-subsidy-status/instruction.md +165 -0
- package/skills/ev-subsidy-status/skill.json +10 -0
- package/skills/express-bus-booking/instruction.md +207 -0
- package/skills/express-bus-booking/references/kobus-http-flow.md +159 -0
- package/skills/express-bus-booking/scripts/kobus_express_booking.py +243 -0
- package/skills/express-bus-booking/skill.json +8 -0
- package/skills/fine-dust-location/instruction.md +89 -0
- package/skills/fine-dust-location/skill.json +9 -0
- package/skills/flight-ticket-search/instruction.md +237 -0
- package/skills/flight-ticket-search/scripts/flight_ticket_search.py +501 -0
- package/skills/flight-ticket-search/skill.json +9 -0
- package/skills/foresttrip-vacancy/instruction.md +167 -0
- package/skills/foresttrip-vacancy/scripts/run_foresttrip_vacancy.py +549 -0
- package/skills/foresttrip-vacancy/skill.json +10 -0
- package/skills/fsc-corporate-info/instruction.md +57 -0
- package/skills/fsc-corporate-info/scripts/fsc_corporate_info.py +113 -0
- package/skills/fsc-corporate-info/skill.json +10 -0
- package/skills/g2b-order-plan-search/instruction.md +131 -0
- package/skills/g2b-order-plan-search/scripts/g2b_order_plan.py +158 -0
- package/skills/g2b-order-plan-search/skill.json +10 -0
- package/skills/g2b-sanctioned-supplier/instruction.md +61 -0
- package/skills/g2b-sanctioned-supplier/scripts/g2b_sanctioned_supplier.py +114 -0
- package/skills/g2b-sanctioned-supplier/skill.json +10 -0
- package/skills/gangnamunni-clinic-search/instruction.md +113 -0
- package/skills/gangnamunni-clinic-search/skill.json +9 -0
- package/skills/geeknews-search/instruction.md +69 -0
- package/skills/geeknews-search/scripts/geeknews_search.py +296 -0
- package/skills/geeknews-search/skill.json +8 -0
- package/skills/gongsijiga-search/instruction.md +128 -0
- package/skills/gongsijiga-search/skill.json +9 -0
- package/skills/gov-overseas-trip-report/instruction.md +488 -0
- package/skills/gov-overseas-trip-report/scripts/gov_overseas_trip_report.py +1206 -0
- package/skills/gov-overseas-trip-report/skill.json +9 -0
- package/skills/han-river-water-level/instruction.md +83 -0
- package/skills/han-river-water-level/skill.json +10 -0
- package/skills/highway-traffic-status/instruction.md +88 -0
- package/skills/highway-traffic-status/scripts/highway_traffic.py +315 -0
- package/skills/highway-traffic-status/skill.json +11 -0
- package/skills/hipass-receipt/instruction.md +97 -0
- package/skills/hipass-receipt/skill.json +10 -0
- package/skills/hola-poke-yeoksam/instruction.md +247 -0
- package/skills/hola-poke-yeoksam/skill.json +8 -0
- package/skills/household-waste-info/instruction.md +117 -0
- package/skills/household-waste-info/skill.json +10 -0
- package/skills/housing-official-price/instruction.md +177 -0
- package/skills/housing-official-price/skill.json +10 -0
- package/skills/hwp/instruction.md +206 -0
- package/skills/hwp/skill.json +8 -0
- package/skills/intercity-bus-booking/instruction.md +189 -0
- package/skills/intercity-bus-booking/references/tmoney-intercity-http-flow.md +126 -0
- package/skills/intercity-bus-booking/scripts/intercity_bus_search.py +381 -0
- package/skills/intercity-bus-booking/skill.json +8 -0
- package/skills/iros-registry-automation/instruction.md +229 -0
- package/skills/iros-registry-automation/scripts/iros_pdf_summary.py +249 -0
- package/skills/iros-registry-automation/scripts/upstream.pin +1 -0
- package/skills/iros-registry-automation/skill.json +9 -0
- package/skills/job-posting-match/instruction.md +130 -0
- package/skills/job-posting-match/scripts/job_posting_match.py +396 -0
- package/skills/job-posting-match/scripts/test_job_posting_match.py +54 -0
- package/skills/job-posting-match/skill.json +8 -0
- package/skills/jobkorea-talent-search/instruction.md +118 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_models.py +27 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_parse.py +186 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_search.py +94 -0
- package/skills/jobkorea-talent-search/scripts/jobkorea_talent_search_condition.py +136 -0
- package/skills/jobkorea-talent-search/scripts/test_jobkorea_talent_search.py +76 -0
- package/skills/jobkorea-talent-search/skill.json +9 -0
- package/skills/joseon-sillok-search/instruction.md +76 -0
- package/skills/joseon-sillok-search/scripts/sillok_search.py +552 -0
- package/skills/joseon-sillok-search/skill.json +8 -0
- package/skills/k-dart/instruction.md +406 -0
- package/skills/k-dart/skill.json +8 -0
- package/skills/k-schoollunch-menu/instruction.md +109 -0
- package/skills/k-schoollunch-menu/skill.json +9 -0
- package/skills/k-skill-cleaner/instruction.md +80 -0
- package/skills/k-skill-cleaner/scripts/k_skill_cleaner.py +410 -0
- package/skills/k-skill-cleaner/skill.json +8 -0
- package/skills/k-skill-setup/instruction.md +253 -0
- package/skills/k-skill-setup/skill.json +11 -0
- package/skills/kakao-bar-nearby/instruction.md +76 -0
- package/skills/kakao-bar-nearby/skill.json +8 -0
- package/skills/kakao-map/instruction.md +176 -0
- package/skills/kakao-map/skill.json +10 -0
- package/skills/kakaotalk-mac/instruction.md +189 -0
- package/skills/kakaotalk-mac/skill.json +8 -0
- package/skills/kbl-results/instruction.md +89 -0
- package/skills/kbl-results/skill.json +9 -0
- package/skills/kbo-results/instruction.md +82 -0
- package/skills/kbo-results/skill.json +8 -0
- package/skills/keris-academic-search/instruction.md +81 -0
- package/skills/keris-academic-search/scripts/keris_academic.py +210 -0
- package/skills/keris-academic-search/skill.json +10 -0
- package/skills/kleague-results/instruction.md +92 -0
- package/skills/kleague-results/skill.json +8 -0
- package/skills/kopis-performance-search/instruction.md +109 -0
- package/skills/kopis-performance-search/skill.json +10 -0
- package/skills/korea-weather/instruction.md +93 -0
- package/skills/korea-weather/skill.json +10 -0
- package/skills/korean-character-count/instruction.md +87 -0
- package/skills/korean-character-count/scripts/korean_character_count.js +268 -0
- package/skills/korean-character-count/skill.json +8 -0
- package/skills/korean-cinema-search/instruction.md +177 -0
- package/skills/korean-cinema-search/skill.json +8 -0
- package/skills/korean-heritage-search/instruction.md +106 -0
- package/skills/korean-heritage-search/scripts/korean_heritage_search.py +321 -0
- package/skills/korean-heritage-search/skill.json +8 -0
- package/skills/korean-holiday-calendar/instruction.md +97 -0
- package/skills/korean-holiday-calendar/skill.json +9 -0
- package/skills/korean-humanizer/instruction.md +389 -0
- package/skills/korean-humanizer/references/ai-tell-taxonomy.md +147 -0
- package/skills/korean-humanizer/skill.json +8 -0
- package/skills/korean-jangbu-for/instruction.md +133 -0
- package/skills/korean-jangbu-for/scripts/install.sh +237 -0
- package/skills/korean-jangbu-for/scripts/upstream.pin +1 -0
- package/skills/korean-jangbu-for/skill.json +8 -0
- package/skills/korean-law-search/instruction.md +126 -0
- package/skills/korean-law-search/skill.json +10 -0
- package/skills/korean-marathon-schedule/instruction.md +111 -0
- package/skills/korean-marathon-schedule/skill.json +9 -0
- package/skills/korean-middle-korean/instruction.md +79 -0
- package/skills/korean-middle-korean/scripts/korean_middle_korean.js +214 -0
- package/skills/korean-middle-korean/skill.json +8 -0
- package/skills/korean-patent-search/instruction.md +79 -0
- package/skills/korean-patent-search/scripts/patent_search.py +409 -0
- package/skills/korean-patent-search/skill.json +9 -0
- package/skills/korean-privacy-terms/instruction.md +128 -0
- package/skills/korean-privacy-terms/scripts/install.sh +108 -0
- package/skills/korean-privacy-terms/scripts/upstream.pin +1 -0
- package/skills/korean-privacy-terms/skill.json +8 -0
- package/skills/korean-scholarship-search/instruction.md +317 -0
- package/skills/korean-scholarship-search/references/report-format.md +40 -0
- package/skills/korean-scholarship-search/references/school-discovery.md +61 -0
- package/skills/korean-scholarship-search/references/search-clues.md +58 -0
- package/skills/korean-scholarship-search/references/source-patterns.md +67 -0
- package/skills/korean-scholarship-search/scripts/scholarship_filter.py +811 -0
- package/skills/korean-scholarship-search/scripts/test_scholarship_filter.py +224 -0
- package/skills/korean-scholarship-search/scripts/university_search_plan.py +148 -0
- package/skills/korean-scholarship-search/skill.json +8 -0
- package/skills/korean-slang-writing/instruction.md +181 -0
- package/skills/korean-slang-writing/scripts/_slang_http.py +91 -0
- package/skills/korean-slang-writing/scripts/slang_lookup.py +291 -0
- package/skills/korean-slang-writing/scripts/slang_search.py +284 -0
- package/skills/korean-slang-writing/skill.json +8 -0
- package/skills/korean-spell-check/instruction.md +105 -0
- package/skills/korean-spell-check/scripts/korean_spell_check.py +523 -0
- package/skills/korean-spell-check/skill.json +9 -0
- package/skills/korean-stock-search/instruction.md +194 -0
- package/skills/korean-stock-search/skill.json +10 -0
- package/skills/korean-transit-route/instruction.md +113 -0
- package/skills/korean-transit-route/skill.json +10 -0
- package/skills/kosis-stats/instruction.md +232 -0
- package/skills/kosis-stats/references/kosis-openapi-guide.md +171 -0
- package/skills/kosis-stats/scripts/run_kosis_stats.py +896 -0
- package/skills/kosis-stats/skill.json +9 -0
- package/skills/kr-whois-lookup/instruction.md +107 -0
- package/skills/kr-whois-lookup/skill.json +10 -0
- package/skills/kstartup-search/instruction.md +186 -0
- package/skills/kstartup-search/scripts/run_kstartup.py +424 -0
- package/skills/kstartup-search/skill.json +11 -0
- package/skills/ktx-booking/instruction.md +244 -0
- package/skills/ktx-booking/skill.json +10 -0
- package/skills/lck-analytics/instruction.md +192 -0
- package/skills/lck-analytics/scripts/_lib.js +103 -0
- package/skills/lck-analytics/scripts/analyze-live-game.js +52 -0
- package/skills/lck-analytics/scripts/build-match-report.js +44 -0
- package/skills/lck-analytics/scripts/sync-oracle.js +50 -0
- package/skills/lck-analytics/skill.json +9 -0
- package/skills/lh-notice-search/instruction.md +206 -0
- package/skills/lh-notice-search/skill.json +10 -0
- package/skills/library-book-search/instruction.md +139 -0
- package/skills/library-book-search/skill.json +9 -0
- package/skills/local-election-candidate-search/instruction.md +77 -0
- package/skills/local-election-candidate-search/skill.json +8 -0
- package/skills/localdata-business-status/instruction.md +64 -0
- package/skills/localdata-business-status/scripts/localdata_business_status.py +206 -0
- package/skills/localdata-business-status/skill.json +8 -0
- package/skills/lotto-results/instruction.md +80 -0
- package/skills/lotto-results/skill.json +8 -0
- package/skills/lovebug-report/instruction.md +185 -0
- package/skills/lovebug-report/skill.json +9 -0
- package/skills/market-kurly-search/instruction.md +125 -0
- package/skills/market-kurly-search/skill.json +8 -0
- package/skills/mfds-drug-safety/instruction.md +86 -0
- package/skills/mfds-drug-safety/scripts/mfds_drug_safety.py +184 -0
- package/skills/mfds-drug-safety/skill.json +10 -0
- package/skills/mfds-food-safety/instruction.md +126 -0
- package/skills/mfds-food-safety/scripts/mfds_food_safety.py +281 -0
- package/skills/mfds-food-safety/skill.json +10 -0
- package/skills/myrealtrip-search/instruction.md +239 -0
- package/skills/myrealtrip-search/scripts/myrealtrip_mcp.py +194 -0
- package/skills/myrealtrip-search/scripts/test_myrealtrip_mcp.py +99 -0
- package/skills/myrealtrip-search/skill.json +9 -0
- package/skills/naming-house/instruction.md +146 -0
- package/skills/naming-house/skill.json +8 -0
- package/skills/national-pension-workplace/instruction.md +64 -0
- package/skills/national-pension-workplace/scripts/national_pension_workplace.py +113 -0
- package/skills/national-pension-workplace/skill.json +10 -0
- package/skills/naver-ad-performance/instruction.md +108 -0
- package/skills/naver-ad-performance/scripts/naver_ad_performance.py +240 -0
- package/skills/naver-ad-performance/skill.json +9 -0
- package/skills/naver-blog-research/instruction.md +128 -0
- package/skills/naver-blog-research/scripts/_naver_http.py +58 -0
- package/skills/naver-blog-research/scripts/naver_download_images.py +233 -0
- package/skills/naver-blog-research/scripts/naver_read.py +256 -0
- package/skills/naver-blog-research/scripts/naver_search.py +192 -0
- package/skills/naver-blog-research/skill.json +8 -0
- package/skills/naver-news-search/instruction.md +103 -0
- package/skills/naver-news-search/skill.json +10 -0
- package/skills/naver-shopping-search/instruction.md +94 -0
- package/skills/naver-shopping-search/skill.json +11 -0
- package/skills/nhis-care-checkup-search/instruction.md +116 -0
- package/skills/nhis-care-checkup-search/skill.json +10 -0
- package/skills/nts-business-registration/instruction.md +115 -0
- package/skills/nts-business-registration/scripts/nts_business_registration.py +215 -0
- package/skills/nts-business-registration/skill.json +10 -0
- package/skills/nts-tax-delinquency/instruction.md +54 -0
- package/skills/nts-tax-delinquency/scripts/nts_tax_delinquency.py +150 -0
- package/skills/nts-tax-delinquency/skill.json +8 -0
- package/skills/ohou-today-deal/instruction.md +182 -0
- package/skills/ohou-today-deal/scripts/ohou_today_deal.py +369 -0
- package/skills/ohou-today-deal/skill.json +9 -0
- package/skills/olive-young-search/instruction.md +154 -0
- package/skills/olive-young-search/skill.json +8 -0
- package/skills/parking-lot-search/instruction.md +96 -0
- package/skills/parking-lot-search/skill.json +9 -0
- package/skills/popbill/instruction.md +146 -0
- package/skills/popbill/scripts/popbill_cli.py +284 -0
- package/skills/popbill/scripts/popbill_registry.py +54 -0
- package/skills/popbill/scripts/popbill_safety.py +37 -0
- package/skills/popbill/scripts/popbill_templates.py +71 -0
- package/skills/popbill/skill.json +9 -0
- package/skills/public-restroom-nearby/instruction.md +89 -0
- package/skills/public-restroom-nearby/skill.json +9 -0
- package/skills/real-estate-search/instruction.md +172 -0
- package/skills/real-estate-search/skill.json +10 -0
- package/skills/rhwp-advanced/instruction.md +145 -0
- package/skills/rhwp-advanced/skill.json +8 -0
- package/skills/rhwp-edit/instruction.md +153 -0
- package/skills/rhwp-edit/skill.json +8 -0
- package/skills/s2b-notice-search/instruction.md +66 -0
- package/skills/s2b-notice-search/skill.json +9 -0
- package/skills/saju-fortune/instruction.md +177 -0
- package/skills/saju-fortune/skill.json +8 -0
- package/skills/saramin-talent-search/instruction.md +119 -0
- package/skills/saramin-talent-search/skill.json +9 -0
- package/skills/seoul-bike/instruction.md +83 -0
- package/skills/seoul-bike/scripts/seoul_bike.py +247 -0
- package/skills/seoul-bike/skill.json +10 -0
- package/skills/seoul-density/instruction.md +109 -0
- package/skills/seoul-density/scripts/seoul_density.py +271 -0
- package/skills/seoul-density/skill.json +10 -0
- package/skills/seoul-subway-arrival/instruction.md +85 -0
- package/skills/seoul-subway-arrival/skill.json +9 -0
- package/skills/sh-notice-search/instruction.md +150 -0
- package/skills/sh-notice-search/skill.json +9 -0
- package/skills/srt-booking/instruction.md +181 -0
- package/skills/srt-booking/scripts/srt_booking.py +272 -0
- package/skills/srt-booking/scripts/srt_seats.py +156 -0
- package/skills/srt-booking/skill.json +20 -0
- package/skills/subway-lost-property/instruction.md +93 -0
- package/skills/subway-lost-property/scripts/subway_lost_property.py +244 -0
- package/skills/subway-lost-property/skill.json +8 -0
- package/skills/ticket-availability/instruction.md +175 -0
- package/skills/ticket-availability/scripts/ticket_availability.py +430 -0
- package/skills/ticket-availability/skill.json +9 -0
- package/skills/toss-securities/instruction.md +116 -0
- package/skills/toss-securities/skill.json +9 -0
- package/skills/used-car-price-search/instruction.md +109 -0
- package/skills/used-car-price-search/skill.json +8 -0
- package/skills/yebigun-training/instruction.md +177 -0
- package/skills/yebigun-training/skill.json +9 -0
- package/skills/zipcode-search/instruction.md +159 -0
- package/skills/zipcode-search/scripts/zipcode_search.py +150 -0
- package/skills/zipcode-search/skill.json +8 -0
- package/src/assemble.js +134 -0
- package/src/detect.js +18 -0
- package/templates/action-account.md +4 -0
- package/templates/action-booking.md +5 -0
- package/templates/action-commerce.md +5 -0
- package/templates/action-communication.md +5 -0
- package/templates/action-submission.md +5 -0
- package/templates/browser.md +4 -0
- package/templates/core.md +5 -0
- package/templates/hard-boundary.md +3 -0
- package/templates/local.md +2 -0
- package/templates/lookup.md +2 -0
- package/templates/proxy.md +2 -0
- package/templates/vault.md +4 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Coupang Product Search
|
|
2
|
+
|
|
3
|
+
## What this skill does
|
|
4
|
+
|
|
5
|
+
[retention-corp/coupang_partners](https://github.com/retention-corp/coupang_partners) 저장소의 로컬 Coupang MCP 호환 레이어를 사용해 쿠팡 상품 조회 도구를 실행한다. 기존 유지보수형 HF Space MCP 엔드포인트 대신, 이 저장소의 `bin/coupang_mcp.py`가 제공하는 `local://coupang-mcp` 계약을 호출한다.
|
|
6
|
+
|
|
7
|
+
- 키워드 상품 검색
|
|
8
|
+
- 로켓배송 전용 필터 검색
|
|
9
|
+
- 가격대 범위 검색
|
|
10
|
+
- 상품 비교표 생성
|
|
11
|
+
- 카테고리별 베스트 상품
|
|
12
|
+
- 골드박스 당일 특가
|
|
13
|
+
- 인기 검색어/계절 상품 추천
|
|
14
|
+
|
|
15
|
+
## How it works
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Claude Code / Codex
|
|
19
|
+
→ coupang-product-search/scripts/coupang_partners_mcp.py
|
|
20
|
+
→ git clone/update retention-corp/coupang_partners (user cache)
|
|
21
|
+
→ python3 bin/coupang_mcp.py
|
|
22
|
+
→ local://coupang-mcp compatible tool layer
|
|
23
|
+
├─ Coupang Partners API client (operator keys present)
|
|
24
|
+
└─ hosted fallback → https://a.retn.kr/v1/public/assist (no keys)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Hard rules:
|
|
28
|
+
|
|
29
|
+
- `COUPANG_MCP_ENDPOINT`는 호환성 knob로만 유지한다. 기본값은 `local://coupang-mcp`다.
|
|
30
|
+
- 구형 HF Space hosted MCP 엔드포인트를 사용하거나 새로 지어내지 않는다.
|
|
31
|
+
- upstream 저장소는 `https://github.com/retention-corp/coupang_partners.git`만 사용한다.
|
|
32
|
+
- `tools`와 `init`은 로컬 MCP 계약 확인용으로 먼저 실행한다.
|
|
33
|
+
|
|
34
|
+
## Execution paths
|
|
35
|
+
|
|
36
|
+
`retention-corp/coupang_partners`는 하나의 CLI 뒤에서 두 가지 경로를 자동으로 선택한다. 래퍼(`coupang_partners_mcp.py`)는 두 경로 모두를 그대로 통과시킨다.
|
|
37
|
+
|
|
38
|
+
1. **Operator (local HMAC) path** — `COUPANG_ACCESS_KEY`와 `COUPANG_SECRET_KEY`가 둘 다 설정된 경우. upstream이 Coupang Partners API를 HMAC 서명해 직접 호출한다. 키/시크릿은 절대 답변·문서·커밋에 노출하지 않는다.
|
|
39
|
+
2. **Credentialless hosted fallback path** — 위 두 키 중 하나라도 없는 경우(또는 `OPENCLAW_SHOPPING_FORCE_HOSTED=1`). upstream이 자동으로 Retention Corp의 hosted 백엔드(`https://a.retn.kr/v1/public/assist`)로 떨어진다. 이 경로는 `X-OpenClaw-Client-Id` allowlist로 게이트되어 있으며, upstream이 기본으로 실어 보내는 `openclaw-skill` 값이 현재 Retention Corp allowlist에 등록된 값이다. k-skill 래퍼는 `OPENCLAW_SHOPPING_CLIENT_ID`를 별도로 설정하지 않고 이 upstream 기본값을 그대로 사용한다.
|
|
40
|
+
|
|
41
|
+
두 경로 모두 JSON envelope(`ok`/`data.session_id`/`data.tool`/`data.payload`/`data.result`) 모양은 동일하므로, 답변 로직은 경로를 구별할 필요가 없다. short deeplink는 hosted fallback에서는 `https://a.retn.kr/s/...` 형태로, operator path에서는 `https://link.coupang.com/...` 형태로 온다.
|
|
42
|
+
|
|
43
|
+
### 관련 환경변수
|
|
44
|
+
|
|
45
|
+
| 환경변수 | 역할 | 기본값 |
|
|
46
|
+
|---------|------|--------|
|
|
47
|
+
| `COUPANG_ACCESS_KEY`, `COUPANG_SECRET_KEY` | 운영자 Coupang Partners API 크리덴셜. 둘 다 있을 때만 로컬 HMAC 경로가 활성화된다. | 없음 (없으면 hosted fallback) |
|
|
48
|
+
| `OPENCLAW_SHOPPING_CLIENT_ID` | hosted fallback이 보낼 `X-OpenClaw-Client-Id`. upstream이 `openclaw-skill`을 기본으로 실어 보내며 이 값이 현재 Retention Corp allowlist에 등록되어 있다. k-skill 래퍼는 이 변수를 오버라이드하지 않는 것을 권장한다. | `openclaw-skill` |
|
|
49
|
+
| `OPENCLAW_SHOPPING_FORCE_HOSTED` | `1`이면 키가 있어도 hosted 경로를 강제한다. | 비어있음 |
|
|
50
|
+
| `OPENCLAW_SHOPPING_BASE_URL` | hosted 백엔드 base URL 오버라이드. 스테이징/로컬 backend 테스트용. | `https://a.retn.kr` |
|
|
51
|
+
|
|
52
|
+
## MCP endpoint / contract
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
local://coupang-mcp
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
프로토콜 호환 버전: MCP `2025-03-26`. 네트워크로 붙는 Streamable HTTP 서버가 아니라, upstream 저장소의 로컬 MCP 호환 CLI가 같은 도구 이름과 JSON-RPC 모양의 payload를 반환한다.
|
|
59
|
+
|
|
60
|
+
## When to use
|
|
61
|
+
|
|
62
|
+
- "쿠팡에서 생수 가격 좀 찾아줘"
|
|
63
|
+
- "로켓배송 에어팟 찾아줘"
|
|
64
|
+
- "20만원 이하 키보드 추천해줘"
|
|
65
|
+
- "아이패드 vs 갤럭시탭 비교"
|
|
66
|
+
- "오늘 쿠팡 특가 뭐 있어?"
|
|
67
|
+
- "전자제품 베스트 보여줘"
|
|
68
|
+
|
|
69
|
+
## When not to use
|
|
70
|
+
|
|
71
|
+
- 돌쇠가 아니며 로그인, 장바구니, 결제 자동화가 필요한 경우
|
|
72
|
+
- 돌쇠가 아니며 쿠팡 계정/session 접근이 필요한 경우
|
|
73
|
+
- 실시간 재고/품절 여부를 100% 보장해야 하는 경우 (hosted fallback과 Partners API 모두 캐시·지연이 있을 수 있다)
|
|
74
|
+
|
|
75
|
+
## Workflow
|
|
76
|
+
|
|
77
|
+
### 1. Clarify the need
|
|
78
|
+
|
|
79
|
+
검색어가 너무 넓으면 먼저 의도를 좁힌다.
|
|
80
|
+
|
|
81
|
+
- 권장 질문: `어떤 용도/예산/브랜드/용량을 우선할까요?`
|
|
82
|
+
|
|
83
|
+
### 2. Bootstrap and check the tool contract
|
|
84
|
+
|
|
85
|
+
래퍼는 기본적으로 `~/.cache/k-skill/coupang_partners`에 upstream 저장소를 clone한다. 이미 clone되어 있으면 그대로 사용하고, 최신화가 필요할 때만 `--update`를 붙인다.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py tools
|
|
89
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py init
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
기존 checkout을 명시하거나 CI/검증에서 네트워크 clone을 막으려면:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py \
|
|
96
|
+
--repo-dir /path/to/coupang_partners \
|
|
97
|
+
--no-clone \
|
|
98
|
+
tools
|
|
99
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py \
|
|
100
|
+
--repo-dir /path/to/coupang_partners \
|
|
101
|
+
--no-clone \
|
|
102
|
+
init
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 3. Call tools
|
|
106
|
+
|
|
107
|
+
구체적인 사용자 요청에 맞춰 upstream CLI 명령을 호출한다. 결과는 `ok`, `data.tool`, `data.payload`, `data.result`를 포함하는 JSON으로 반환된다.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# 일반 검색 (키 없이도 hosted fallback으로 작동)
|
|
111
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py search "32인치 4K 모니터"
|
|
112
|
+
|
|
113
|
+
# 로켓배송 필터
|
|
114
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py rocket "에어팟"
|
|
115
|
+
|
|
116
|
+
# 가격대 검색
|
|
117
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py budget "키보드" --max-price 100000
|
|
118
|
+
|
|
119
|
+
# 비교
|
|
120
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py compare "아이패드 vs 갤럭시탭"
|
|
121
|
+
|
|
122
|
+
# 골드박스 (운영자 키가 필요한 upstream 경로)
|
|
123
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py goldbox
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 4. (optional) hosted fallback 강제
|
|
127
|
+
|
|
128
|
+
운영자 키가 있는 상태에서도 hosted fallback 경로를 점검하고 싶으면 `OPENCLAW_SHOPPING_FORCE_HOSTED=1`만 추가하면 된다. `OPENCLAW_SHOPPING_CLIENT_ID`는 upstream이 보내는 기본값 `openclaw-skill`이 현재 Retention Corp allowlist에 등록된 값이므로 별도로 설정하지 않는다.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
export OPENCLAW_SHOPPING_FORCE_HOSTED=1
|
|
132
|
+
python3 coupang-product-search/scripts/coupang_partners_mcp.py search "에어팟"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 5. Continue from discovery to cart and checkout in Dolshoi
|
|
136
|
+
|
|
137
|
+
사용자가 구매나 장바구니 담기를 요청했고 CloakBrowser mode라면 MCP 결과 링크에서 멈추지 않는다.
|
|
138
|
+
|
|
139
|
+
1. 선택한 상품의 공식 쿠팡 URL을 CloakBrowser로 연다.
|
|
140
|
+
2. 현재 상품명, 판매자, 옵션, 수량, 로켓배송 여부, 실시간 가격과 품절 여부를 다시 확인한다.
|
|
141
|
+
3. 로그인이 필요하면 provisioned vault capability를 사용하고, 없으면 `request_vault_credential`로 쿠팡 login을 저장한 뒤 같은 turn에 재개한다.
|
|
142
|
+
4. 옵션과 수량을 선택해 장바구니에 담는다. 장바구니 담기는 가역적이므로 별도 승인 없이 수행하고 실제 담김 상태를 확인한다.
|
|
143
|
+
5. 구매 요청이면 배송지/쿠폰/결제수단 적용 후 최종 주문 직전까지 진행한다.
|
|
144
|
+
6. `clarify`로 상품·옵션·수량·배송지 요약·최종 금액을 승인받고, 승인되면 주문/결제를 실행해 주문번호와 상태를 확인한다.
|
|
145
|
+
|
|
146
|
+
CloakBrowser를 사용할 수 없는 generic runtime에서는 기존 검색/링크 제공 범위를 유지한다.
|
|
147
|
+
|
|
148
|
+
## Available tools
|
|
149
|
+
|
|
150
|
+
| 도구명 | CLI 명령 | 기능 | 파라미터 예시 |
|
|
151
|
+
|--------|----------|------|-------------|
|
|
152
|
+
| `search_coupang_products` | `search` | 일반 상품 검색 | `"생수"` |
|
|
153
|
+
| `search_coupang_rocket` | `rocket` | 로켓배송만 필터링 | `"에어팟"` |
|
|
154
|
+
| `search_coupang_budget` | `budget` | 가격대 범위 검색 | `"키보드" --max-price 100000` |
|
|
155
|
+
| `compare_coupang_products` | `compare` | 상품 비교표 생성 | `"아이패드 vs 갤럭시탭"` |
|
|
156
|
+
| `get_coupang_recommendations` | `recommendations` | 인기 검색어 제안 | `--category 전자제품` |
|
|
157
|
+
| `get_coupang_seasonal` | `seasonal` | 계절/상황별 추천 | `"설날 선물"` |
|
|
158
|
+
| `get_coupang_best_products` | `best` | 카테고리별 베스트 | `--category-id 1016` |
|
|
159
|
+
| `get_coupang_goldbox` | `goldbox` | 당일 특가 정보 | `--limit 10` |
|
|
160
|
+
|
|
161
|
+
주의: `get_coupang_goldbox`와 `get_coupang_best_products`는 upstream 기준 Coupang Partners API 권한이 필요한 경로이므로, 키가 없는 환경에서는 실패할 수 있다. 이런 경우 에러 메시지를 그대로 전달하고 hosted fallback이 커버하는 `search`/`rocket`/`budget`/`compare` 경로로 우회 제안한다.
|
|
162
|
+
|
|
163
|
+
## Response format
|
|
164
|
+
|
|
165
|
+
upstream CLI는 JSON을 출력한다. `data.result` 안의 상품 배열 또는 도구별 객체를 읽고, 답변에서는 로켓배송(rocket)과 일반배송(normal)을 구분한다.
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"ok": true,
|
|
170
|
+
"data": {
|
|
171
|
+
"session_id": "session-...",
|
|
172
|
+
"tool": "search_coupang_products",
|
|
173
|
+
"payload": {
|
|
174
|
+
"jsonrpc": "2.0",
|
|
175
|
+
"result": {
|
|
176
|
+
"content": [
|
|
177
|
+
{"type": "text", "text": "[...]"}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"result": []
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
사용자에게 보여줄 때는 다음처럼 짧게 정리한다.
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
## rocket (상위 후보)
|
|
190
|
+
|
|
191
|
+
1) LG전자 4K UHD 모니터
|
|
192
|
+
가격: 397,750원 (참고용)
|
|
193
|
+
보러가기: https://a.retn.kr/s/... # hosted fallback shortlink
|
|
194
|
+
또는: https://link.coupang.com/a/... # operator HMAC 경로 딥링크
|
|
195
|
+
|
|
196
|
+
## normal (상위 후보)
|
|
197
|
+
|
|
198
|
+
1) 삼성전자 QHD 오디세이 G5 게이밍 모니터
|
|
199
|
+
가격: 283,000원 (참고용)
|
|
200
|
+
보러가기: https://a.retn.kr/s/...
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Response policy
|
|
204
|
+
|
|
205
|
+
- 후보가 여러 개면 상위 3~5개만 짧게 비교한다.
|
|
206
|
+
- 로켓배송/일반배송 구분을 명시한다.
|
|
207
|
+
- 가격/품절/배송 정보는 실시간 변동될 수 있음을 안내한다.
|
|
208
|
+
- upstream checkout, 권한, Coupang Partners 환경변수 문제로 실패하면 실패 원인과 재시도/설정 방법을 짧게 안내한다.
|
|
209
|
+
- **Affiliate 고지(필수)**: 응답에 포함되는 shortlink(`https://a.retn.kr/s/...`)와 직접 coupang 딥링크(`link.coupang.com/...?lptag=AF...`)는 Retention Corp의 쿠팡 파트너스(affiliate) 채널로 트래킹된다. upstream이 돌려주는 `disclosure` 문자열(`"파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음"`)이 있으면 그대로 노출하고, 없으면 같은 취지의 고지를 답변 말미에 덧붙인다.
|
|
210
|
+
|
|
211
|
+
## Done when
|
|
212
|
+
|
|
213
|
+
- `tools`와 `init` 또는 실제 명령으로 retention-corp/coupang_partners 로컬 MCP 계약을 확인했다.
|
|
214
|
+
- 검색 결과가 로켓배송/일반배송으로 구분되어 정리되었다.
|
|
215
|
+
- 사용자 니즈에 맞는 추천 TOP 3이 제시되었다.
|
|
216
|
+
- 가격/배송 정보와 변동 가능성 안내가 포함되었다.
|
|
217
|
+
- affiliate 고지(disclosure)가 답변에 포함되었다.
|
|
218
|
+
- 돌쇠의 장바구니 요청이면 선택 옵션/수량이 실제 장바구니에 담긴 것을 확인했다.
|
|
219
|
+
- 돌쇠의 구매 요청이면 `clarify` 승인 후 주문번호와 결제 상태를 확인했다.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Bootstrap and run retention-corp/coupang_partners Coupang MCP tools.
|
|
3
|
+
|
|
4
|
+
The k-skill repo intentionally does not vendor the third-party implementation.
|
|
5
|
+
This wrapper keeps the skill pointed at the approved upstream repository, clones it
|
|
6
|
+
into a user cache when needed, and then delegates to its local MCP-compatible CLI.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import os
|
|
13
|
+
import pathlib
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
from typing import Sequence
|
|
17
|
+
|
|
18
|
+
UPSTREAM_REPO_URL = "https://github.com/retention-corp/coupang_partners.git"
|
|
19
|
+
DEFAULT_MCP_ENDPOINT = "local://coupang-mcp"
|
|
20
|
+
DEFAULT_REPO_DIR = pathlib.Path(os.getenv("COUPANG_PARTNERS_REPO_DIR", "~/.cache/k-skill/coupang_partners")).expanduser()
|
|
21
|
+
UPSTREAM_CLI = pathlib.Path("bin") / "coupang_mcp.py"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BootstrapError(RuntimeError):
|
|
25
|
+
"""Raised when the upstream checkout cannot be prepared."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
29
|
+
parser = argparse.ArgumentParser(
|
|
30
|
+
description="Run the retention-corp/coupang_partners local Coupang MCP-compatible CLI.",
|
|
31
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
32
|
+
epilog=(
|
|
33
|
+
"Examples:\n"
|
|
34
|
+
" coupang_partners_mcp.py tools\n"
|
|
35
|
+
" coupang_partners_mcp.py init\n"
|
|
36
|
+
" coupang_partners_mcp.py search 생수\n"
|
|
37
|
+
" coupang_partners_mcp.py budget 키보드 --max-price 100000\n"
|
|
38
|
+
"\n"
|
|
39
|
+
"Honored upstream environment variables (forwarded as-is):\n"
|
|
40
|
+
" COUPANG_ACCESS_KEY, COUPANG_SECRET_KEY\n"
|
|
41
|
+
" Operator Coupang Partners API credentials. When set, upstream\n"
|
|
42
|
+
" uses the local HMAC-signed Coupang Partners path.\n"
|
|
43
|
+
" OPENCLAW_SHOPPING_CLIENT_ID\n"
|
|
44
|
+
" Allowlisted client id for the hosted fallback. upstream sends\n"
|
|
45
|
+
" openclaw-skill by default, which is the value currently on the\n"
|
|
46
|
+
" Retention Corp allowlist; k-skill does not override this.\n"
|
|
47
|
+
" OPENCLAW_SHOPPING_FORCE_HOSTED=1\n"
|
|
48
|
+
" Force the hosted fallback even when Coupang keys are present.\n"
|
|
49
|
+
" OPENCLAW_SHOPPING_BASE_URL\n"
|
|
50
|
+
" Override the hosted backend base URL. Default upstream target\n"
|
|
51
|
+
" is https://a.retn.kr and /v1/public/assist is the public entry.\n"
|
|
52
|
+
"\n"
|
|
53
|
+
"When both COUPANG_ACCESS_KEY and COUPANG_SECRET_KEY are missing,\n"
|
|
54
|
+
"upstream falls back to the hosted Retention Corp backend so this\n"
|
|
55
|
+
"skill keeps working without Coupang Partners credentials."
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--repo-dir",
|
|
60
|
+
default=str(DEFAULT_REPO_DIR),
|
|
61
|
+
help="Checkout directory for retention-corp/coupang_partners (default: %(default)s).",
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--no-clone",
|
|
65
|
+
action="store_true",
|
|
66
|
+
help="Do not clone the upstream repository if it is missing; fail with setup guidance instead.",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--update",
|
|
70
|
+
action="store_true",
|
|
71
|
+
help="Run git pull --ff-only in an existing upstream checkout before delegating.",
|
|
72
|
+
)
|
|
73
|
+
parser.add_argument(
|
|
74
|
+
"upstream_args",
|
|
75
|
+
nargs=argparse.REMAINDER,
|
|
76
|
+
help="Arguments passed to bin/coupang_mcp.py, for example: tools, search 생수, rocket 에어팟.",
|
|
77
|
+
)
|
|
78
|
+
args = parser.parse_args(argv)
|
|
79
|
+
if args.upstream_args and args.upstream_args[0] == "--":
|
|
80
|
+
args.upstream_args = args.upstream_args[1:]
|
|
81
|
+
if not args.upstream_args:
|
|
82
|
+
parser.error("missing upstream command; try: tools, init, search <keyword>, rocket <keyword>, budget <keyword>")
|
|
83
|
+
return args
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def upstream_cli_path(repo_dir: pathlib.Path) -> pathlib.Path:
|
|
87
|
+
return repo_dir / UPSTREAM_CLI
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def ensure_repo(repo_dir: pathlib.Path, *, clone: bool = True, update: bool = False) -> pathlib.Path:
|
|
91
|
+
cli_path = upstream_cli_path(repo_dir)
|
|
92
|
+
if cli_path.exists():
|
|
93
|
+
if update:
|
|
94
|
+
run_checked(["git", "-C", str(repo_dir), "pull", "--ff-only"], "failed to update upstream checkout")
|
|
95
|
+
return cli_path
|
|
96
|
+
|
|
97
|
+
if repo_dir.exists():
|
|
98
|
+
raise BootstrapError(
|
|
99
|
+
f"{repo_dir} exists but does not look like retention-corp/coupang_partners "
|
|
100
|
+
f"(missing {UPSTREAM_CLI}). Recreate it with: git clone {UPSTREAM_REPO_URL} {repo_dir}"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if not clone:
|
|
104
|
+
raise BootstrapError(
|
|
105
|
+
f"Missing retention-corp/coupang_partners checkout at {repo_dir}. "
|
|
106
|
+
f"Create it with: git clone {UPSTREAM_REPO_URL} {repo_dir}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
repo_dir.parent.mkdir(parents=True, exist_ok=True)
|
|
110
|
+
run_checked(["git", "clone", "--depth", "1", UPSTREAM_REPO_URL, str(repo_dir)], "failed to clone upstream checkout")
|
|
111
|
+
if not cli_path.exists():
|
|
112
|
+
raise BootstrapError(f"Cloned {UPSTREAM_REPO_URL}, but {UPSTREAM_CLI} was not found in {repo_dir}")
|
|
113
|
+
return cli_path
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def run_checked(command: Sequence[str], context: str) -> None:
|
|
117
|
+
try:
|
|
118
|
+
subprocess.run(command, check=True)
|
|
119
|
+
except FileNotFoundError as exc:
|
|
120
|
+
raise BootstrapError(f"{context}: required executable not found: {command[0]}") from exc
|
|
121
|
+
except subprocess.CalledProcessError as exc:
|
|
122
|
+
raise BootstrapError(f"{context}: {exc}") from exc
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def build_command(cli_path: pathlib.Path, upstream_args: Sequence[str]) -> list[str]:
|
|
126
|
+
return [sys.executable, str(cli_path), *upstream_args]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
130
|
+
args = parse_args(argv)
|
|
131
|
+
repo_dir = pathlib.Path(args.repo_dir).expanduser().resolve()
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
cli_path = ensure_repo(repo_dir, clone=not args.no_clone, update=args.update)
|
|
135
|
+
except BootstrapError as exc:
|
|
136
|
+
print(f"coupang_partners_mcp.py: {exc}", file=sys.stderr)
|
|
137
|
+
return 2
|
|
138
|
+
|
|
139
|
+
env = os.environ.copy()
|
|
140
|
+
env.setdefault("COUPANG_MCP_ENDPOINT", DEFAULT_MCP_ENDPOINT)
|
|
141
|
+
completed = subprocess.run(build_command(cli_path, args.upstream_args), env=env)
|
|
142
|
+
return int(completed.returncode)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coupang-product-search",
|
|
3
|
+
"description": "retention-corp/coupang_partners의 로컬 Coupang MCP 호환 레이어로 쿠팡 상품 검색, 로켓배송 필터, 가격대 검색, 상품 비교, 베스트 상품, 골드박스 특가를 조회한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.",
|
|
4
|
+
"profiles": [
|
|
5
|
+
"vault",
|
|
6
|
+
"browser",
|
|
7
|
+
"action:commerce"
|
|
8
|
+
],
|
|
9
|
+
"frontmatter": "name: coupang-product-search\ndescription: retention-corp/coupang_partners의 로컬 Coupang MCP 호환 레이어로 쿠팡 상품 검색, 로켓배송 필터, 가격대 검색, 상품 비교, 베스트 상품, 골드박스 특가를 조회한다. 돌쇠에서는 공식 표면을 통한 후속 액션까지 진행한다.\nlicense: MIT\nmetadata:\n category: retail\n locale: ko-KR\n phase: v2"
|
|
10
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Court Auction Notice Search
|
|
2
|
+
|
|
3
|
+
## What this skill does
|
|
4
|
+
|
|
5
|
+
대한민국 법원이 운영하는 공식 **법원경매정보** 사이트(`courtauction.go.kr`) 의 매각공고와 사건정보를 에이전트가 활용할 수 있는 JSON 형태로 변환해서 돌려준다.
|
|
6
|
+
|
|
7
|
+
- 공식 OPEN API가 없어 사이트 내부의 WebSquare JSON XHR endpoint를 그대로 호출한다.
|
|
8
|
+
- 1차 transport 는 직접 HTTP다. Workflow C 자유검색에서 raw-HTTP WAF성 HTTP 400이 날 때만 Playwright fallback 으로 전환하며, 명시적 차단(`BLOCKED`/`ipcheck=false`)은 기본적으로 중단한다 (`rebrowser-playwright` 또는 `playwright-core` 가 있을 때만).
|
|
9
|
+
- **기본 조회는 실제 브라우저 없이 동작한다.** 매각공고/사건/물건 조회의 정상 경로는 직접 HTTP다. 브라우저는 Workflow C 자유검색이 WAF성 HTTP 400을 낼 때만 fallback으로 쓰이고, 그때 권장 순서는 사용자가 띄운 BrowserOS/runtime CDP → (닿지 않으면) Chrome CDP → 로컬 Playwright launch다.
|
|
10
|
+
- 사이트는 **IP 단위 봇 차단** 이 매우 공격적이다 (16회/30초 정도면 1시간 차단). 이 패키지는 호출 간 최소 2초 jitter, 세션당 호출 budget(기본 10회), `data.ipcheck === false` 즉시 throw 로 보수적으로 동작한다.
|
|
11
|
+
- **참고용 도구**다. 실제 입찰 전에는 반드시 법원 원문 매각공고를 다시 확인해야 한다.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
- "오늘/내일 어디서 부동산 경매 열려?"
|
|
16
|
+
- "서울중앙지방법원 2026-04-27 매각공고 보여줘"
|
|
17
|
+
- "기일입찰 vs 기간입찰만 나눠서 보여줘"
|
|
18
|
+
- "이 매각공고 안의 사건번호/용도/주소/감정평가액 다 보여줘"
|
|
19
|
+
- "사건번호 2024타경100001 진행 상황 알려줘"
|
|
20
|
+
- "서울 강남구 아파트 최저가 5억 이하 유찰 1회 이상 물건 찾아줘"
|
|
21
|
+
- "법원사무소 코드 표 줘"
|
|
22
|
+
|
|
23
|
+
## When not to use
|
|
24
|
+
|
|
25
|
+
- 동산(자동차·중기) 경매 (이번 v1 범위 밖)
|
|
26
|
+
- 특정 매각기일 날짜의 모든 법원 일정을 한 번에 (Workflow D 별도 follow-up 이슈)
|
|
27
|
+
- 매각물건 사진(전경/개황/내부) URL 노출 (별도 follow-up 이슈)
|
|
28
|
+
- 매각물건명세서 / 현황조사서 / 감정평가서 PDF 다운로드 (별도 follow-up 이슈)
|
|
29
|
+
- 입찰서 자동 작성·자동 제출 (지원하지 않는다, 입찰은 반드시 법원에서 사람이 직접)
|
|
30
|
+
|
|
31
|
+
## Inputs
|
|
32
|
+
|
|
33
|
+
- `date` — 매각기일 월(YYYY-MM 또는 YYYYMM) 또는 특정일(YYYY-MM-DD 또는 YYYYMMDD). 필수. 실제 사이트 검색 버튼은 월(YYYYMM) 단위로 조회하므로 특정일 입력은 월 조회 후 해당 일자만 필터링한다.
|
|
34
|
+
- `courtCode` — 법원사무소코드 (예: `B000210` = 서울중앙지방법원). 비우면 전체. `getCourtCodes()` 또는 `codes courts` 로 받아온다.
|
|
35
|
+
- `bidType` — `date` (= 기일입찰, code 000331) 또는 `period` (= 기간입찰, code 000332). 빈값이면 둘 다.
|
|
36
|
+
- `caseNumber` — 사건번호. `2024타경100001` 형식 권장. `2024-100001` 도 받아서 `2024타경100001` 로 정규화한다.
|
|
37
|
+
|
|
38
|
+
## Mandatory honest framing
|
|
39
|
+
|
|
40
|
+
이 스킬은 사용자에게 다음 사실을 항상 알려야 한다.
|
|
41
|
+
|
|
42
|
+
1. 데이터는 법원경매정보 사이트의 공개 정보를 그대로 옮긴 것이며 **실제 입찰 전에 법원 원문을 재확인**해야 한다.
|
|
43
|
+
2. 사이트는 자동화 호출에 매우 민감해서 **빠른 연속 조회 시 IP가 1시간 차단**될 수 있다. 차단되면 같은 IP에서는 약 1시간을 기다려야 한다.
|
|
44
|
+
3. 가격(감정평가액·최저매각가격)·매각기일·매각장소는 **공고 시점 기준** 이며 정정·취하·연기로 변경될 수 있다 (`correctionCount`, `cancellationCount` 필드를 참고).
|
|
45
|
+
4. 본 스킬은 **read-only**다. 입찰 자체는 자동화하지 않는다.
|
|
46
|
+
|
|
47
|
+
## Official surfaces
|
|
48
|
+
|
|
49
|
+
- 법원경매정보 메인: `https://www.courtauction.go.kr`
|
|
50
|
+
- 부동산매각공고 진입: `https://www.courtauction.go.kr/pgj/index.on?w2xPath=/pgj/ui/pgj100/PGJ143M01.xml&pgjId=143M01`
|
|
51
|
+
- 경매사건검색 진입: `https://www.courtauction.go.kr/pgj/index.on?w2xPath=/pgj/ui/pgj100/PGJ159M00.xml&pgjId=159M00`
|
|
52
|
+
- 직접 호출 endpoint (이 스킬이 사용하는 것):
|
|
53
|
+
- `POST /pgj/pgj143/selectRletDspslPbanc.on` — 매각공고 목록
|
|
54
|
+
- `POST /pgj/pgj143/selectRletDspslPbancDtl.on` — 매각공고 상세 (사건/물건 펼치기)
|
|
55
|
+
- `POST /pgj/pgj15A/selectAuctnCsSrchRslt.on` — 사건 단건 조회
|
|
56
|
+
- `POST /pgj/pgjsearch/searchControllerMain.on` — 물건 자유 조건검색 (PGJ151F00 → PGJ151M01)
|
|
57
|
+
- `POST /pgj/pgjComm/selectCortOfcCdLst.on` — 법원사무소코드 전체
|
|
58
|
+
|
|
59
|
+
## Workflow A — 매각공고 → 사건/물건 펼치기
|
|
60
|
+
|
|
61
|
+
1. 사용자에게 **매각기일(YYYY-MM-DD)** 과 (선택) 법원·입찰구분을 받는다.
|
|
62
|
+
2. `searchSaleNotices({ date, courtCode, bidType })` 호출 → 그 날·그 법원의 매각공고 카드 목록.
|
|
63
|
+
3. 사용자가 카드를 고르면 카드 객체(또는 `raw`)를 그대로 `getSaleNoticeDetail(notice)` 에 넘긴다.
|
|
64
|
+
4. 응답의 `items[]` 가 `caseNumber`, `usage`, `address`, `appraisedPrice`, `minimumSalePrice`, `remarks` 를 가진다 (이슈 본문이 명시한 4필드 모두 포함).
|
|
65
|
+
5. 가격은 원 단위 정수다. 사용자에게 보여줄 때는 한국식 천단위 콤마 + 억/만 단위 환산을 같이 제시한다.
|
|
66
|
+
|
|
67
|
+
## Workflow B — 사건번호 직접 조회
|
|
68
|
+
|
|
69
|
+
1. 사용자에게 **법원사무소코드** + **사건번호(2024타경100001)** 를 받는다.
|
|
70
|
+
2. `getCaseByCaseNumber({ courtCode, caseNumber })` 호출.
|
|
71
|
+
3. `found:false / status:204` 면 사건이 존재하지 않거나 비공개. 사건번호 형식·법원이 맞는지 사용자에게 다시 확인한다.
|
|
72
|
+
4. `found:true` 면 `caseInfo`(사건명·접수일·청구액·재판부·진행상태), `items[]`(매각목적물 — 주소/배당요구종기), `schedule[]`(매각기일별 최저가/감정가/결과), `claimDeadline`, `relatedCases`, `stakeholders` 가 채워진다.
|
|
73
|
+
|
|
74
|
+
## Workflow C — 부동산 물건 자유 조건검색
|
|
75
|
+
|
|
76
|
+
1. 사용자의 조건을 `searchProperties()` 입력으로 매핑한다.
|
|
77
|
+
- `region: { sido, sigungu, dong }` — 코드 또는 대표 정적 sido 코드테이블의 한국어명. 지역을 주면 지번주소 검색(`cortStDvs:"2"`)으로, 지역이 없으면 매각공고 모드(`cortStDvs:"1"`)로 조회한다. 시군구/읍면동은 정적 표가 없으므로 코드로 직접 전달(예: `{ sido:"11", sigungu:"11680", dong:"11680101" }`)한다.
|
|
78
|
+
- `usage: { large, medium, small }` — 용도 대/중/소분류 코드(5자리, 예: 건물=`20000`) 또는 대분류 한국어명(`토지`/`건물`/`차량및운송장비`/`기타`).
|
|
79
|
+
- `priceRange` — 최저매각가격 원 단위 `{ min, max }` (실수 허용)
|
|
80
|
+
- `appraisedPriceRange` — 감정평가액 원 단위 `{ min, max }` (실수 허용)
|
|
81
|
+
- `saleDate` — `{ from, to }`
|
|
82
|
+
- `flbdCount` — 유찰횟수 `{ min, max }` **정수만**
|
|
83
|
+
- `area` — 면적(㎡) `{ min, max }` (실수 허용)
|
|
84
|
+
- `pageSize` — 페이지당 결과 수, upstream PGJ151 드롭다운에서 확인된 `10`/`20`/`50`/`100` 중 하나(기본 10). `1` 등 임의 값은 live endpoint 가 HTTP 400을 반환하므로 로컬에서 거부한다.
|
|
85
|
+
2. `searchProperties({ ... })` 호출 → `POST /pgj/pgjsearch/searchControllerMain.on`.
|
|
86
|
+
- 1차로 direct HTTP 시도. Workflow C raw-HTTP WAF의 HTTP 400을 만나면 자동으로 Playwright fallback 으로 재시도한다. fallback 을 끄려면 `{ fallback: false }`. `BLOCKED`(`ipcheck=false`)는 사이트의 명시적 차단 신호이므로 기본적으로 즉시 중단하며, 사용자가 위험을 이해하고 명시적으로 `{ fallbackOnBlocked: true }` 를 준 경우에만 재시도한다.
|
|
87
|
+
3. 응답의 `items[]` 는 핵심 raw 컬럼을 영문 키로 정규화한다:
|
|
88
|
+
- `saNo` → `caseNumber`, `srnSaNo`/`printCsNo` → `displayCaseNumber`
|
|
89
|
+
- `mokmulSer`/`maemulSer` → `itemNumber`
|
|
90
|
+
- `hjguSido + hjguSigu + hjguDong + daepyoLotno + buldNm` → `address`
|
|
91
|
+
- `gamevalAmt` → `appraisedPrice`, `minmaePrice` → `minimumSalePrice`
|
|
92
|
+
- `yuchalCnt` → `flbdCount`, `mulStatcd` → `statusCode`, `jinstatCd` → `progressStatusCode`
|
|
93
|
+
- `boCd` → `courtCode`, `jiwonNm` → `courtName`, `jpDeptNm` → `judgeDeptName`
|
|
94
|
+
- `lclsUtilCd/mclsUtilCd/sclsUtilCd` → `usageCodes.{large,medium,small}`
|
|
95
|
+
- `srchHjguSidoCd/SiguCd/DongCd` → `regionCodes.{sido,sigungu,dong}`
|
|
96
|
+
- `xCordi/yCordi` → `coordinates`, `wgs84Xcordi/Ycordi` → `coordinatesWgs84`
|
|
97
|
+
- `buldList/areaList/jimokList` → `buildingList/areaList/landCategoryList`
|
|
98
|
+
- `pjbBuldList` → `propertyDescription`, `mulBigo` → `remarks`
|
|
99
|
+
4. `getUsageCodes()` 는 4개 대분류(`10000=토지`, `20000=건물`, `30000=차량및운송장비`, `40000=기타`)와 일부 대표 중/소분류를 정적으로 반환한다. `getRegionCodes()` 는 19개 시도 + 코드만 반환한다. 시군구/읍면동은 upstream cascade XHR이 안정적이지 않아 정적 표에 포함하지 않으며 raw 코드를 그대로 전달하면 된다. 알 수 없는 값은 fail-open으로 통과한다.
|
|
100
|
+
5. **Same-name usage codes 보호**: `resolveUsageCode("아파트", "large")` 처럼 입력 이름이 다른 level 에만 존재하면, 같은 이름의 medium/small 코드를 잘못 리턴하지 않고 fail-open(원문 통과)한다.
|
|
101
|
+
|
|
102
|
+
## Throttling and call-budget rules
|
|
103
|
+
|
|
104
|
+
- 호출 간 최소 2초 (기본). 더 늘리려면 `--min-delay-ms 3000`.
|
|
105
|
+
- 기본 세션 budget 은 **10회**. 더 많은 조회가 필요하면 새 세션을 열거나 (`new CourtAuctionHttpClient`) `maxCallsPerSession` 을 명시적으로 늘린다.
|
|
106
|
+
- 차단(`data.ipcheck === false`)을 만나면 `BLOCKED` 에러를 즉시 throw 하고 멈춘다. 자동 retry 하지 않는다 (차단 연장 위험).
|
|
107
|
+
- 차단된 IP는 **약 1시간** 후 자연 복구된다. 그 사이에는 다른 IP/네트워크에서 작업하거나 사람이 브라우저로 사이트에 접속해서 차단 해제 화면을 거친다.
|
|
108
|
+
- **Workflow C 자유검색은 사이트 WAF 가 raw HTTP 호출을 더 엄격하게 차단**한다. `searchProperties()` 는 1차 direct HTTP에서 WAF성 HTTP 400을 만났을 때만 Playwright fallback 으로 재시도한다. 명시적 차단(`BLOCKED`/`ipcheck=false`)은 기본적으로 즉시 중단하며, 사용자가 위험을 이해하고 `fallbackOnBlocked:true` 를 준 경우에만 재시도한다. Playwright fallback 모듈(`rebrowser-playwright` 또는 `playwright-core`)이 없으면 첫 HTTP 400 실패가 그대로 throw 된다.
|
|
109
|
+
- searchProperties 는 같은 Playwright 클라이언트로 연속 호출하면 **10~15회 간격 호출에서 안정**하다. 그 이상 burst 호출이 필요하면 호출 사이 3~5초 sleep 을 두고 새 클라이언트를 열어라.
|
|
110
|
+
|
|
111
|
+
## Node.js example
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
const {
|
|
115
|
+
searchSaleNotices,
|
|
116
|
+
getSaleNoticeDetail,
|
|
117
|
+
getCaseByCaseNumber,
|
|
118
|
+
getCourtCodes
|
|
119
|
+
} = require("court-auction-notice-search");
|
|
120
|
+
|
|
121
|
+
async function main() {
|
|
122
|
+
const courts = await getCourtCodes();
|
|
123
|
+
console.log(`법원사무소 ${courts.count}개 로드됨`);
|
|
124
|
+
|
|
125
|
+
const notices = await searchSaleNotices({
|
|
126
|
+
date: "2026-04-27",
|
|
127
|
+
courtCode: "B000210",
|
|
128
|
+
bidType: "date"
|
|
129
|
+
});
|
|
130
|
+
console.log(`서울중앙지방법원 매각공고 ${notices.count}건`);
|
|
131
|
+
|
|
132
|
+
if (notices.items.length > 0) {
|
|
133
|
+
const detail = await getSaleNoticeDetail(notices.items[0]);
|
|
134
|
+
for (const item of detail.items) {
|
|
135
|
+
console.log(
|
|
136
|
+
`${item.caseNumber} (${item.usage}) — 감정 ${item.appraisedPrice}원 / 최저 ${item.minimumSalePrice}원`
|
|
137
|
+
);
|
|
138
|
+
console.log(` 주소: ${item.address}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const caseInfo = await getCaseByCaseNumber({
|
|
143
|
+
courtCode: "B000210",
|
|
144
|
+
caseNumber: "2024타경100001"
|
|
145
|
+
});
|
|
146
|
+
if (caseInfo.found) {
|
|
147
|
+
console.log(`사건명: ${caseInfo.caseInfo.caseName}`);
|
|
148
|
+
console.log(`매각기일 횟수: ${caseInfo.schedule.length}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
main().catch((error) => {
|
|
153
|
+
if (error.code === "BLOCKED") {
|
|
154
|
+
console.error("[BLOCKED] 사이트가 1시간 차단했습니다. 다른 IP에서 다시 시도하거나 1시간 뒤 재시도하세요.");
|
|
155
|
+
} else {
|
|
156
|
+
console.error(error);
|
|
157
|
+
}
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## CLI example
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# 1. 법원사무소 코드표
|
|
166
|
+
court-auction-notice-search codes courts --pretty | head -40
|
|
167
|
+
|
|
168
|
+
# 2. 입찰구분 (정적 코드)
|
|
169
|
+
court-auction-notice-search codes bid-types --pretty
|
|
170
|
+
court-auction-notice-search codes usages --pretty
|
|
171
|
+
court-auction-notice-search codes regions --pretty
|
|
172
|
+
|
|
173
|
+
# 3. 매각공고 목록
|
|
174
|
+
court-auction-notice-search notices --date 2026-04 --court-code B000210 --bid-type date --pretty
|
|
175
|
+
|
|
176
|
+
# 4. 매각공고 상세 — list 응답의 row 의 raw 필드를 그대로 detail 호출에 사용한다.
|
|
177
|
+
# (CLI 단발 호출에서는 list -> detail 으로 결과를 파이프할 수 있도록 jq 등을 함께 사용)
|
|
178
|
+
|
|
179
|
+
# 5. 사건번호 직접 조회
|
|
180
|
+
court-auction-notice-search case --court-code B000210 --case-number "2024타경100001" --pretty
|
|
181
|
+
|
|
182
|
+
# 6. 자유 조건검색
|
|
183
|
+
court-auction-notice-search search --sido 서울특별시 --sigungu 11680 --usage-large 건물 --usage-medium 21200 \
|
|
184
|
+
--price-min 100000000 --price-max 500000000 --sale-from 2026-05-01 --sale-to 2026-05-20 --pretty
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Block / Error handling
|
|
188
|
+
|
|
189
|
+
- `error.code === "BLOCKED"` — `data.ipcheck === false`. 1시간 대기 후 다른 IP에서 재시도. 사용자에게 차단 사실과 대기 안내를 그대로 전달한다.
|
|
190
|
+
- `error.code === "BUDGET_EXCEEDED"` — 세션 budget 초과. 의도적인 안전장치다. 정말 필요하면 `--max-calls 20` 같이 늘리지만 차단 위험을 함께 안내한다.
|
|
191
|
+
- `error.code === "UPSTREAM_ERROR"` — 사이트가 일반적인 에러를 돌려준 경우. 세션 만료 또는 잘못된 jdbnCd 가 가장 흔한 원인. warmup 부터 다시.
|
|
192
|
+
- `error.code === "NETWORK_ERROR"` — 타임아웃/연결 실패.
|
|
193
|
+
- `error.code === "PLAYWRIGHT_UNAVAILABLE"` — Playwright fallback 을 명시적으로 쓰려는데 모듈이 깔려있지 않음. `npm i rebrowser-playwright` 또는 `npm i playwright-core` 로 해결.
|
|
194
|
+
|
|
195
|
+
## Done when
|
|
196
|
+
|
|
197
|
+
- 사용자에게 IP 차단 위험과 "참고용·실제 입찰 전 법원 원문 재확인" 고지를 했다.
|
|
198
|
+
- 매각공고를 펼쳐서 `caseNumber/usage/address/appraisedPrice/minimumSalePrice` 가 채워진 JSON을 돌려줬다.
|
|
199
|
+
- 사건번호로 직접 조회한 경우, `found:false` 일 때 사용자가 후속 조치를 알 수 있도록 안내했다.
|
|
200
|
+
- 차단 발생 시 자동 재시도하지 않고 즉시 멈췄다.
|
|
201
|
+
- 작업 후 호출 budget 이 남아있는지 사용자에게 알려서 추가 호출 여지를 명시했다.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "court-auction-notice-search",
|
|
3
|
+
"description": "Browse 대법원경매정보(courtauction.go.kr) 부동산 매각공고 by 매각기일·법원·기일/기간 입찰, expand each notice into 사건번호·용도·주소·감정평가액·최저매각가, search property items by free conditions(지역·용도·가격·면적·유찰횟수), and look up a case directly by 법원+사건번호. Read-only, slow-by-design (~2s/call) to avoid IP blocks. 돌쇠에서는 가능한 가장 가까운 합법적 공식 단계까지 준비한다.",
|
|
4
|
+
"profiles": [
|
|
5
|
+
"browser",
|
|
6
|
+
"hard-boundary"
|
|
7
|
+
],
|
|
8
|
+
"frontmatter": "name: court-auction-notice-search\ndescription: Browse 대법원경매정보(courtauction.go.kr) 부동산 매각공고 by 매각기일·법원·기일/기간 입찰, expand each notice into 사건번호·용도·주소·감정평가액·최저매각가, search property items by free conditions(지역·용도·가격·면적·유찰횟수), and look up a case directly by 법원+사건번호. Read-only, slow-by-design (~2s/call) to avoid IP blocks. 돌쇠에서는 가능한 가장 가까운 합법적 공식 단계까지 준비한다.\nlicense: MIT\nmetadata:\n category: real-estate\n locale: ko-KR\n phase: v1"
|
|
9
|
+
}
|