diff --git a/README.md b/README.md index 040dcc14..23b3b3dc 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,21 @@ AI 에이전트 사용 안내 > [!IMPORTANT] -> **최신 안내:** [현재 버전](https://mcp.aka.page/guide/2026-11-02.2) · [항상 최신 버전으로 이동](https://mcp.aka.page/guide/latest) +> **최신 안내:** [현재 버전](https://mcp.aka.page/guide/2026-11-02.3) · [항상 최신 버전으로 이동](https://mcp.aka.page/guide/latest) > -> **안내 버전: 2026-11-02.2 · 갱신일: 2026-11-02** +> **안내 버전: 2026-11-02.3 · 갱신일: 2026-11-02** > -> **웹 읽기는 실제 링크 ID로 click하세요.** 새 검색 URL을 조립해 open하지 말고 [웹 조회 시작](https://mcp.aka.page/browse/read) 화면의 글자 선택 링크를 사용하세요. 상품의 **매장 지역 선택** 링크에서 지역도 선택할 수 있습니다. 입력 중에는 원본 조회를 실행하지 않습니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. +> **웹 읽기는 실제 링크 ID로 click하세요.** 새 검색 URL을 조립해 open하지 말고 [웹 조회 시작](https://mcp.aka.page/browse/quick) 화면의 빠른 검색어·지역을 통째로 선택하세요. 목록에 없는 입력만 글자 링크를 사용하세요. 상품의 **매장 지역 선택** 링크에서 지역도 선택할 수 있습니다. 입력 중에는 원본 조회를 실행하지 않습니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. > -> **ChatGPT 웹 조회:** 연결된 MCP 도구가 없으면 사용 가능한 실제 브라우저로, 브라우저 도구도 없으면 open/WebFetch로 [HTML GET으로 바로 조회](https://mcp.aka.page/browse/read)하세요. 설치·API 키·MCP 연결 설정은 필요 없습니다. +> **첫 조회 속도:** 빠른 검색어·지역을 통째로 선택하고 목록 밖 입력만 글자 링크를 사용하세요. 재고가 목적이면 상품 카드의 매장 지역 선택을 바로 여세요. +> +> **ChatGPT 웹 조회:** 연결된 MCP 도구가 없으면 사용 가능한 실제 브라우저로, 브라우저 도구도 없으면 open/WebFetch로 [HTML GET으로 바로 조회](https://mcp.aka.page/browse/quick)하세요. 설치·API 키·MCP 연결 설정은 필요 없습니다. > > **로컬 설치·빌드·API 키 준비 없이 실제 조회부터 실행하세요.** 패키지 설치나 전체 스키마 탐색은 필요 없습니다. > > 1. 연결된 MCP 도구가 있으면 `search_products`에 `{"service":"daiso","query":"수납박스","pageSize":3}`를 전달합니다. > 2. 실제 브라우저 도구가 있으면 /browse/ HTML GET 주소를 열고 실제 상품·매장·재고를 읽습니다. Work의 내장 브라우저도 사용 가능합니다. -> 3. 웹 읽기(open/WebFetch)만 가능하면 응답의 실제 링크 ID로 click합니다. 위 글자 선택 링크로 임의 검색어와 지역을 입력하고 실제 상품·재고 조회 링크를 따르세요. 새 URL 조립과 실패한 직접 open을 반복하지 마세요. Crawled가 과거인 수집본을 현재 재고로 단정하지 마세요. +> 3. 웹 읽기(open/WebFetch)만 가능하면 응답의 실제 링크 ID로 click합니다. 빠른 선택에서 요청과 일치하는 검색어·지역을 통째로 선택하고 목록 밖 입력만 글자 링크를 사용하여 실제 상품·재고 조회 링크를 따르세요. 새 URL 조립과 실패한 직접 open을 반복하지 마세요. Crawled가 과거인 수집본을 현재 재고로 단정하지 마세요. > 4. 위 세 수단이 없거나 모두 실패하면 실제 조회 제약과 오류를 알립니다. 셸이 있어도 설치 없는 웹 조회가 우선이며 터미널·셸로 우회하지 않습니다. 셸 DNS 오류를 웹 GET 실패로 확대하거나 실행하지 않은 요청의 실패를 만들지 마세요. > > **느린 다이소 조회:** `pending`은 접수 상태입니다. 각 응답의 새 결과 확인 링크를 최대3번 선택하고, 계속 pending이면 미확인으로 보고하세요. 상태 확인을 위해 원본 조회를 다시 실행하지 마세요. @@ -31,7 +33,7 @@ > > **웹 읽기 전용 에이전트는 `/browse/`의 실제 링크만 선택하세요.** 검색·상세·매장·재고·후속 조회가 모두 자동 HTML이며 쿼리 문자열이나 `format` 지정은 필요 없습니다. 검색어·지역은 글자 링크로 입력하고 응답의 상세·재고 링크를 따르세요. 일반 API 클라이언트의 필터 문법은 상세 사용 안내를 참고하세요. > -> [첫 HTML 조회](https://mcp.aka.page/browse/read) · [REST 사용법](https://mcp.aka.page/prompt) · [원격 MCP 연결](#원격-mcp로-바로-시작하기) +> [첫 HTML 조회](https://mcp.aka.page/browse/quick) · [REST 사용법](https://mcp.aka.page/prompt) · [원격 MCP 연결](#원격-mcp로-바로-시작하기) @@ -134,7 +136,7 @@ 2. 서버 URL을 입력하고, 전송 방식을 묻는 경우 **HTTP / Streamable HTTP**를 선택합니다. 3. 도구를 활성화하고 `다이소 MCP로 수납박스 검색해줘`라고 요청합니다. -[앱별 연결 방법](docs/client-guide.md#원격-mcp-연결) · [WebFetch로 첫 조회](docs/service-reference.md?guide=2026-11-02.2#설치-없이-첫-조회) +[앱별 연결 방법](docs/client-guide.md#원격-mcp-연결) · [WebFetch로 첫 조회](docs/service-reference.md?guide=2026-11-02.3#설치-없이-첫-조회)

스마트폰과 노트북을 클라우드로 연결하는 두 햄스터 @@ -165,7 +167,7 @@ npx daiso products 수납박스 --json MCP 연결 없이 웹 읽기와 GET 요청을 지원하는 에이전트는 [REST 사용법](https://mcp.aka.page/prompt)을 읽고 조회할 수 있습니다. **상품·매장·재고와 모든 후속 조회는 `/browse/` 경로를 사용하세요.** 자동 HTML이며 쿼리 문자열은 필요 없습니다. -[상품·매장·재고 조회 시작](https://mcp.aka.page/browse/read) +[상품·매장·재고 조회 시작](https://mcp.aka.page/browse/quick)

브라우저 검색 결과에서 매장을 찾아가는 햄스터 @@ -212,7 +214,7 @@ MCP 연결 없이 웹 읽기와 GET 요청을 지원하는 에이전트는 [REST | 문서 | 내용 | | :-------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | | [연결·사용 가이드](docs/client-guide.md) | 앱별 MCP 연결, CLI, Skill, OpenAPI | -| [서비스 레퍼런스](docs/service-reference.md?guide=2026-11-02.2) | 서비스별 도구·REST API·응답 모델·가격 비교 | +| [서비스 레퍼런스](docs/service-reference.md?guide=2026-11-02.3) | 서비스별 도구·REST API·응답 모델·가격 비교 | | [일본 다이소](docs/daiso-jp-network-analysis.md) | 일본 상품·매장 검색과 지원 범위 | | [독립·예술영화관](docs/dtryx.md) | 디트릭스 지원 극장과 조회 방법 | | [AI 조회 가이드](docs/ai-instruction.md) | 도구 선택과 상품·매장·재고 조회 순서 | diff --git a/docs/ai-instruction.md b/docs/ai-instruction.md index bceecbd4..d0e0d5f5 100644 --- a/docs/ai-instruction.md +++ b/docs/ai-instruction.md @@ -1,4 +1,6 @@ # DAISO MCP 에이전트 지침 +빠른 조회는 요청과 일치하는 검색어·지역을 **통째로 선택**합니다. 목록에 없으면 기존 글자 입력을 쓰고 다른 단어로 바꾸지 않습니다. 재고가 목적이면 상품 카드의 **매장 지역 선택**을 바로 열고, 상세 정보가 필요할 때만 상품 상세를 조회하세요. 입력 중 원본 API를 호출하지 않습니다. + 다이소의 읽기 세션 조회가 오래 걸리면 `pending` 접수와 실제 **결과 확인** 링크를 반환합니다. 접수는 상품·재고 확인 성공이 아닙니다. 각 응답의 새로운 결과 링크를 최대3번 선택하고, 계속 pending이면 미확인으로 보고하세요. 이전 상태 주소를 반복하거나 원본 조회를 다시 실행하지 마세요. 상태 확인은 저장 결과만 읽으며, 완료 결과는 원본의 성공·오류 상태를 유지합니다. HTML·작업 완료 시각과 원본 재고 갱신 시각은 다릅니다. 작업이 만료되거나 시간 초과된 경우에도 수량을0으로 바꾸지 않습니다. @@ -8,18 +10,18 @@ ## 설치 없이 첫 조회 -최신 서버 안내는 [현재 버전](https://mcp.aka.page/guide/2026-11-02.2)과 [최신 연결](https://mcp.aka.page/guide/latest)에서 제공합니다. 웹 도구는 과거 리디렉션·수집본을 반환할 수 있으므로 표시된 버전과 HTML 생성 시각을 확인하세요. +최신 서버 안내는 [현재 버전](https://mcp.aka.page/guide/2026-11-02.3)과 [최신 연결](https://mcp.aka.page/guide/latest)에서 제공합니다. 웹 도구는 과거 리디렉션·수집본을 반환할 수 있으므로 표시된 버전과 HTML 생성 시각을 확인하세요. 1. 연결된 MCP 도구가 있으면 `search_products`에 `{"service":"daiso","query":"수납박스","pageSize":3}`를 전달합니다. 2. 실제 브라우저 도구가 있으면 /browse/ HTML GET 주소를 열고 실제 상품·매장·재고를 읽습니다. Work의 내장 브라우저도 사용 가능합니다. -3. 웹 읽기(open/WebFetch)만 가능하면 [웹 조회 시작](https://mcp.aka.page/browse/read)의 실제 링크 ID로 click합니다. 글자 입력·상품·매장 지역 선택 링크를 따르고 새 URL을 조립하지 마세요. 이전 HTML이면 같은 조건으로 새 응답 확인 링크를 최대2번 선택합니다. TimeoutError는 같은 실제 링크를 한 번만 재확인하고 계속 실패하면 미확인으로 보고합니다. 새 HTML도 기존 API 캐시를 사용하며 원본 갱신을 보장하지 않습니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. +3. 웹 읽기(open/WebFetch)만 가능하면 [웹 조회 시작](https://mcp.aka.page/browse/quick)의 실제 링크 ID로 click합니다. 빠른 단어·지역 링크를 먼저 선택하고 목록 밖 입력만 글자 링크를 쓰며 새 URL을 조립하지 마세요. 이전 HTML이면 같은 조건으로 새 응답 확인 링크를 최대2번 선택합니다. TimeoutError는 같은 실제 링크를 한 번만 재확인하고 계속 실패하면 미확인으로 보고합니다. 새 HTML도 기존 API 캐시를 사용하며 원본 갱신을 보장하지 않습니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. 4. 위 세 수단이 없거나 모두 실패하면 실제 조회 제약과 오류를 알립니다. 셸이 있어도 설치 없는 웹 조회가 우선이며 터미널·셸로 우회하지 않습니다. 셸 DNS 오류를 웹 GET 실패로 확대하거나 실행하지 않은 요청의 실패를 만들지 마세요. -https://mcp.aka.page/browse/read +https://mcp.aka.page/browse/quick 웹 도구의 `Crawled: today`만으로 현재 재고라고 단정하지 마세요. 응답의 HTML 생성 시각과 실제 수량을 확인하고, 수량이 null이면 품절이 아닌 미확인으로 보고합니다. -응답의 글자 선택 링크로 사용자 검색어를 입력하세요. 첫 조회 전에 npm·CLI·스킬 설치, health/help, 전체 도구 목록이나 OpenAPI 탐색을 하지 않습니다. 연결 설정을 요구하기 전에 현재 웹 도구로 실제 조회부터 실행합니다. +빠른 검색어가 정확히 일치하면 선택하고, 없을 때만 글자 링크로 입력하세요. 첫 조회 전에 npm·CLI·스킬 설치, health/help, 전체 도구 목록이나 OpenAPI 탐색을 하지 않습니다. 연결 설정을 요구하기 전에 현재 웹 도구로 실제 조회부터 실행합니다. **모든 웹 GET 조회는 `/browse/` 경로를 사용합니다. 자동 HTML이며 쿼리 문자열과 format 지정은 필요 없습니다.** 상품 검색·상세·매장·재고·후속 조회 모두 적용합니다. 실제 검색 결과의 ID를 사용하고 문서 예제 ID나 수량을 조회 결과로 쓰지 않습니다. REST GET 성공은 MCP 연결 성공과 구분합니다. diff --git a/docs/client-guide.md b/docs/client-guide.md index 07e5328a..1a4cc288 100644 --- a/docs/client-guide.md +++ b/docs/client-guide.md @@ -1,4 +1,6 @@ # 앱 연결·CLI·Skill 가이드 +빠른 조회는 요청과 일치하는 검색어·지역을 **통째로 선택**합니다. 목록에 없으면 기존 글자 입력을 쓰고 다른 단어로 바꾸지 않습니다. 재고가 목적이면 상품 카드의 **매장 지역 선택**을 바로 열고, 상세 정보가 필요할 때만 상품 상세를 조회하세요. 입력 중 원본 API를 호출하지 않습니다. + 다이소의 읽기 세션 조회가 오래 걸리면 `pending` 접수와 실제 **결과 확인** 링크를 반환합니다. 접수는 상품·재고 확인 성공이 아닙니다. 각 응답의 새로운 결과 링크를 최대3번 선택하고, 계속 pending이면 미확인으로 보고하세요. 이전 상태 주소를 반복하거나 원본 조회를 다시 실행하지 마세요. 상태 확인은 저장 결과만 읽으며, 완료 결과는 원본의 성공·오류 상태를 유지합니다. HTML·작업 완료 시각과 원본 재고 갱신 시각은 다릅니다. 작업이 만료되거나 시간 초과된 경우에도 수량을0으로 바꾸지 않습니다. @@ -6,11 +8,11 @@ 웹 읽기 접근 거절이 계속되면 **현재 조건의 완성 URL을 넣은 복사용 문장**을 제시하고 “아래 문장을 그대로 복사해 이 대화에 보내주세요”라고 안내하세요. 사용자 재전송 뒤 한 번 조회합니다. 단순 허용 문구·URL 일부 제공·실행하지 않은 실패 보고로 대체하지 마세요. [복사 재전송 및 오류 구분](https://github.com/hmmhmmhm/daiso-mcp/blob/main/docs/web-reading-troubleshooting.md)을 따릅니다. -실제 브라우저가 있으면 /browse/ HTML을 엽니다. 웹 읽기(open/WebFetch)만 있으면 /browse/read에서 실제 링크 ID로 click합니다. 이전 HTML은 같은 조건의 새 응답 링크를 최대2번 확인하고 TimeoutError 재확인은 한 번만 합니다. 응답 생성 시각과 원본 재고 갱신은 구분하며 새 브라우저를 반복 생성하지 마세요. +실제 브라우저가 있으면 /browse/ HTML을 엽니다. 웹 읽기(open/WebFetch)만 있으면 /browse/quick에서 실제 링크 ID로 click합니다. 이전 HTML은 같은 조건의 새 응답 링크를 최대2번 확인하고 TimeoutError 재확인은 한 번만 합니다. 응답 생성 시각과 원본 재고 갱신은 구분하며 새 브라우저를 반복 생성하지 마세요. 웹 도구의 `Crawled: today`만으로 현재 재고라고 단정하지 마세요. 응답의 HTML 생성 시각과 실제 수량을 확인하고, 수량이 null이면 품절이 아닌 미확인으로 보고합니다. -연결된 MCP 도구가 있으면 [원격 MCP 안내](../README.md#원격-mcp로-바로-시작하기)를 따릅니다. 웹 읽기만 가능하면 설치·연결 설정 없이 [웹 조회 시작](https://mcp.aka.page/browse/read)의 실제 링크 ID로 click하세요. 검색어와 지역은 글자 선택 링크로 입력하며 새 URL을 조립하지 않습니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. 모든 후속 조회도 쿼리 없는 `/browse/` 경로를 사용하며 자동 HTML입니다. +연결된 MCP 도구가 있으면 [원격 MCP 안내](../README.md#원격-mcp로-바로-시작하기)를 따릅니다. 웹 읽기만 가능하면 설치·연결 설정 없이 [웹 조회 시작](https://mcp.aka.page/browse/quick)의 실제 링크 ID로 click하세요. 검색어와 지역은 빠른 링크를 우선 선택하고 목록에 없을 때만 글자 링크로 입력하며 새 URL을 조립하지 않습니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. 모든 후속 조회도 쿼리 없는 `/browse/` 경로를 사용하며 자동 HTML입니다. 원격 서버는 `https://mcp.aka.page`, 전송 방식은 Streamable HTTP, 사용자 인증은 없습니다. ## 원격 MCP 연결 @@ -243,7 +245,7 @@ npx daiso products 수납박스 --json - 공개 페이지: `https://clawhub.ai/hmmhmmhm/daiso-cli` - 스킬 파일: [skills/daiso-cli/SKILL.md](../skills/daiso-cli/SKILL.md) - 명령 맵: [CLI 명령 맵](../skills/daiso-cli/references/cli-command-map.md) -- 사용자가 무설치 조회를 요청하면 연결된 MCP 도구를 사용하고, 없으면 웹 읽기의 HTML GET을 우선 사용합니다. 셸이 있어도 이 순서를 유지하며, 두 수단이 없으면 실제 도구 제약을 알리고 셸 호출로 우회하지 않습니다. /browse/read의 실제 링크 ID로 click하고 글자 입력·상품·재고 링크를 따르세요. 새 URL을 조립하지 마세요. `npx`나 Skill 설치, 전체 스키마 탐색은 필요하지 않습니다. +- 사용자가 무설치 조회를 요청하면 연결된 MCP 도구를 사용하고, 없으면 웹 읽기의 HTML GET을 우선 사용합니다. 셸이 있어도 이 순서를 유지하며, 두 수단이 없으면 실제 도구 제약을 알리고 셸 호출로 우회하지 않습니다. /browse/quick의 실제 링크 ID로 click하고 빠른 단어·지역 링크를 우선 선택하고 목록 밖 입력만 글자 링크를 쓰세요. 새 URL을 조립하지 마세요. `npx`나 Skill 설치, 전체 스키마 탐색은 필요하지 않습니다. - CLI를 사용하기로 선택했고 쉘 실행이 가능한 환경에서만 `npx daiso`를 사용합니다. AI 앱 연결에는 MCP 서버 URL `https://mcp.aka.page`를 사용합니다. - 구조화 결과가 필요하면 스킬은 `npx daiso ... --json` 형태를 선택합니다. - 상황별 레시피는 `콜라 어디가 싸?`, `강남역 근처 카페`, `다이소 핫식스 재고`, `오늘 강남 CGV 시간표`처럼 실제 사용자 문장 기준으로 정리되어 있습니다. diff --git a/docs/mcp-tools.md b/docs/mcp-tools.md index c56654d5..b2ebf160 100644 --- a/docs/mcp-tools.md +++ b/docs/mcp-tools.md @@ -1,4 +1,6 @@ # 공개 MCP 도구 +빠른 조회는 요청과 일치하는 검색어·지역을 **통째로 선택**합니다. 목록에 없으면 기존 글자 입력을 쓰고 다른 단어로 바꾸지 않습니다. 재고가 목적이면 상품 카드의 **매장 지역 선택**을 바로 열고, 상세 정보가 필요할 때만 상품 상세를 조회하세요. 입력 중 원본 API를 호출하지 않습니다. + 공개 도구는 정확히 10개이며 절대 늘리지 않습니다. 새 기능과 서비스는 기존 도구의 service/operation/requestType 안에서 확장합니다. 이 규칙은 일반 MCP와 PlayMCP 모두에 적용합니다. @@ -17,7 +19,7 @@ ## 호출 예시 -아래 예시는 이미 연결된 MCP 도구의 호출 계약입니다. 웹 읽기만 가능한 환경에서는 설치·연결 설정 없이 [웹 조회 시작](https://mcp.aka.page/browse/read)의 실제 링크를 선택합니다. 접근 거절이 계속되면 [완성 URL 복사 재전송](web-reading-troubleshooting.md)을 안내합니다. 모든 후속 조회는 `/browse/` 경로이며 쿼리 문자열이나 format 지정은 필요 없습니다. +아래 예시는 이미 연결된 MCP 도구의 호출 계약입니다. 웹 읽기만 가능한 환경에서는 설치·연결 설정 없이 [웹 조회 시작](https://mcp.aka.page/browse/quick)의 실제 링크를 선택합니다. 접근 거절이 계속되면 [완성 URL 복사 재전송](web-reading-troubleshooting.md)을 안내합니다. 모든 후속 조회는 `/browse/` 경로이며 쿼리 문자열이나 format 지정은 필요 없습니다. 상품 검색: `search_products {"service":"daiso","query":"수납박스","pageSize":3}` diff --git a/docs/service-reference.md b/docs/service-reference.md index 1f102c4c..270a9b41 100644 --- a/docs/service-reference.md +++ b/docs/service-reference.md @@ -1,4 +1,6 @@ # DAISO MCP 서비스 레퍼런스 +빠른 조회는 요청과 일치하는 검색어·지역을 **통째로 선택**합니다. 목록에 없으면 기존 글자 입력을 쓰고 다른 단어로 바꾸지 않습니다. 재고가 목적이면 상품 카드의 **매장 지역 선택**을 바로 열고, 상세 정보가 필요할 때만 상품 상세를 조회하세요. 입력 중 원본 API를 호출하지 않습니다. + 다이소의 읽기 세션 조회가 오래 걸리면 `pending` 접수와 실제 **결과 확인** 링크를 반환합니다. 접수는 상품·재고 확인 성공이 아닙니다. 각 응답의 새로운 결과 링크를 최대3번 선택하고, 계속 pending이면 미확인으로 보고하세요. 이전 상태 주소를 반복하거나 원본 조회를 다시 실행하지 마세요. 상태 확인은 저장 결과만 읽으며, 완료 결과는 원본의 성공·오류 상태를 유지합니다. HTML·작업 완료 시각과 원본 재고 갱신 시각은 다릅니다. 작업이 만료되거나 시간 초과된 경우에도 수량을0으로 바꾸지 않습니다. @@ -6,7 +8,7 @@ 웹 읽기 접근 거절이 계속되면 **현재 조건의 완성 URL을 넣은 복사용 문장**을 제시하고 “아래 문장을 그대로 복사해 이 대화에 보내주세요”라고 안내하세요. 사용자 재전송 뒤 한 번 조회합니다. 단순 허용 문구·URL 일부 제공·실행하지 않은 실패 보고로 대체하지 마세요. [복사 재전송 및 오류 구분](https://github.com/hmmhmmhm/daiso-mcp/blob/main/docs/web-reading-troubleshooting.md)을 따릅니다. -실제 브라우저 도구가 있으면 /browse/ HTML을 그 도구로 여세요. 웹 읽기(open/WebFetch)는 [웹 조회 시작](https://mcp.aka.page/browse/read) 화면의 글자 선택 링크를 응답의 실제 링크 ID로 click합니다. 새 검색 URL을 조립하지 않고 상품·매장 지역·실제 재고 링크를 선택합니다. 접근 오류 시 사용 가능한 브라우저로 전환할 수 있으나 새 브라우저를 계속 실행하지 마세요. Crawled가 과거인 수집본은 현재 재고 확인이 아닙니다. 응답 생성 시각도 함께 확인하세요. 이전 HTML이면 같은 조건의 새 응답 링크를 최대2번 선택합니다. `TimeoutError`만 같은 실제 링크를 한 번 재확인하고 계속 실패하면 미확인으로 보고하세요. 새 주소도 기존 API 캐시를 사용하며 원본 갱신을 보장하지 않습니다. +실제 브라우저 도구가 있으면 /browse/ HTML을 그 도구로 여세요. 웹 읽기(open/WebFetch)는 [웹 조회 시작](https://mcp.aka.page/browse/quick) 화면의 빠른 단어·지역을 실제 링크 ID로 click하고 목록 밖 입력만 글자 링크를 사용합니다. 새 검색 URL을 조립하지 않고 상품·매장 지역·실제 재고 링크를 선택합니다. 접근 오류 시 사용 가능한 브라우저로 전환할 수 있으나 새 브라우저를 계속 실행하지 마세요. Crawled가 과거인 수집본은 현재 재고 확인이 아닙니다. 응답 생성 시각도 함께 확인하세요. 이전 HTML이면 같은 조건의 새 응답 링크를 최대2번 선택합니다. `TimeoutError`만 같은 실제 링크를 한 번 재확인하고 계속 실패하면 미확인으로 보고하세요. 새 주소도 기존 API 캐시를 사용하며 원본 갱신을 보장하지 않습니다. README 하단에 있던 기능, REST API, 개발, 아키텍처 설명을 분리한 문서입니다. @@ -14,7 +16,7 @@ README 하단에 있던 기능, REST API, 개발, 아키텍처 설명을 분리 **ChatGPT 및 웹 읽기 에이전트는 문서에 표시된 실제 링크 ID로 아래 시작 링크를 click하세요.** 설치·API 키·MCP 연결 설정 없이 화면의 상품·지역 링크로 조회합니다. 새 URL을 조립하거나 터미널로 우회하지 마세요. click이 없는 도구는 실제 링크를 열되 같은 동작을 보장하지 않습니다. -[WebFetch로 상품·매장·재고 조회 시작](https://mcp.aka.page/browse/read) +[WebFetch로 상품·매장·재고 조회 시작](https://mcp.aka.page/browse/quick) 시작 화면에서 수납박스·핫식스 예제 또는 글자 입력 링크를 선택하세요. diff --git a/docs/superpowers/plans/2026-11-02-first-use-speed.md b/docs/superpowers/plans/2026-11-02-first-use-speed.md new file mode 100644 index 00000000..1adcb48d --- /dev/null +++ b/docs/superpowers/plans/2026-11-02-first-use-speed.md @@ -0,0 +1,10 @@ +# 첫 조회 속도 개선 계획 + +**Goal:** 글자별 웹 읽기 왕복을 통째 입력 링크로 줄이고 실제 Chat에서 전후 비교한다. + +- [ ] tests/pages/browseQuick.test.ts에 실제 링크만 선택하는4페이지 흐름/임의상품ID 유지/메뉴 무원본호출/추천 불일치 입력 보존 회귀를 추가하고 RED 확인. +- [ ] src/pages/browseQuickLinks.ts에 상품/지역의 일반 입력 링크를 분리하고 browseComposer.ts·browse.ts·browseRead.ts에 통합. /browse/quick도 기존 읽기 세션 및 접수형 조회를 사용. +- [ ] 공통 agentStart·discovery·README·문서·Skill을 빠른 링크 우선으로 맞추고 현재버전 기대값 갱신. 불일치한 단어 대체·고정ID·가짜 수량 금지. +- [ ] 타입·450줄·빌드·회귀·Coverage100%·독립 리뷰·GitHub검사 후 병합·배포. +- [ ] 이전 질문과 같은 새 Chat 임시 대화2건을 순차 실행, 각 소유탭/캡처 정리. 실제 표시 수량과 API 대조, 원본캐시와 UI 전체시간 구분. +- [ ] 결과와 남은 한계를 PROJECT.md 및 연구 기록에 저장. diff --git a/docs/superpowers/specs/2026-11-02-first-use-speed-design.md b/docs/superpowers/specs/2026-11-02-first-use-speed-design.md new file mode 100644 index 00000000..3a4a3f2f --- /dev/null +++ b/docs/superpowers/specs/2026-11-02-first-use-speed-design.md @@ -0,0 +1,22 @@ +# 첫 무설치 조회의 링크 왕복 축소 + +사용자는 첫 사용 약2분의 원인 확인, 개선, 실제 ChatGPT 재검증을 승인했다. 기존 운영 안정성/정확도/공개MCP10개/JSON·CLI 계약을 유지한다. + +## 근거 + +직전 동일 계정 새 임시 대화의 브라우저 진입 포함116.9/146.7초. 공식 루트 실험의 상품 HTML04:08:19.517UTC와 재고 HTML04:09:23.528UTC 사이64.011초. 같은 조건의 일반 HTTP JSON 대조는 상품1.500초/재고2.077초였다. 현재 한글 입력은 글자마다 초성→중성→완성3번 이동하므로 지갑2글자+강남역3글자에만15번 필요하다. 모델의 내부 처리 시간을 직접 측정한 것은 아니며 왕복 구조를 병목 후보로 검증한다. + +## 선택 + +임의 URL 조립은 기존 접근 거절이 재현되어 기본 경로로 복원하지 않는다. 전체 한글 음절을 한 페이지에 나열하면 응답 크기·링크 누락이 커진다. 기존 실제 링크 계약 안에서 흔한 상품 검색어와 지역을 통째로 선택하도록 줄인다. 새로운 검색/재고 API나 고정 상품ID/수량을 추가하지 않는다. + +## 설계 + +- 24개 이상 일반 상품군과24개 이상 지역의 고정 입력 링크. 요청과 일치하는 선택지만 사용하며 비슷한 다른 검색어로 바꾸지 않는다. 추천은 재고나 매장 존재를 주장하지 않는다. +- 새 /browse/quick 진입점도 읽기 세션을 생성한다. 기존 /browse/read와 JSONAPI를 유지한다. 검색어 선택→실제 상품 검색→카드의 매장 지역 선택→지역 단어 링크→실제 재고로 이동한다. +- 빈 입력 화면에 빠른 선택을 먼저 표시하고 임의 입력의 기존 글자 링크를 보존한다. 선택 상품ID는 실제 응답에서 유지한다. 입력·메뉴 화면은 원본 API와 주소검색을 호출하지 않는다. +- 공통 운영 안내·README접힘 블록·상세문서·Skill을 빠른 선택 우선/불일치 시 글자 입력으로 일치시키며 안내버전을 갱신한다. 기존 이미지·구조·deprecated별칭은 유지한다. + +## 검증 + +실제 링크만 따르는 회귀에서 시작·상품·지역선택·재고까지4번 HTML읽기로 완료되고 조회API는 상품/재고 각1번만 호출되는지 검증한다. 임의 입력·잘못된 ID·공개10도구 계약 유지. 필수 CI/Coverage100%/독립리뷰 후 병합·배포한다. 이전과 같은 자연어/공식루트 두 질문을 새 임시 Chat에서 순차 실행하고 전체시간·결과·현재안내를 비교한다. 계정/캐시/모델 변동을 기록하며 서버시간과 전체시간을 혼동하지 않는다. diff --git a/docs/web-discovery.md b/docs/web-discovery.md index 94d2edbe..b38597a3 100644 --- a/docs/web-discovery.md +++ b/docs/web-discovery.md @@ -1,6 +1,8 @@ # 웹에서 다이소 MCP 발견하기 +빠른 조회는 요청과 일치하는 검색어·지역을 **통째로 선택**합니다. 목록에 없으면 기존 글자 입력을 쓰고 다른 단어로 바꾸지 않습니다. 재고가 목적이면 상품 카드의 **매장 지역 선택**을 바로 열고, 상세 정보가 필요할 때만 상품 상세를 조회하세요. 입력 중 원본 API를 호출하지 않습니다. -실제 브라우저가 있으면 /browse/ HTML을 엽니다. 웹 읽기(open/WebFetch)만 있으면 [웹 조회 시작](https://mcp.aka.page/browse/read)의 실제 링크 ID로 click합니다. 이전 HTML은 같은 조건의 새 응답 링크를 최대2번 확인하고 TimeoutError 재확인은 한 번만 합니다. HTML 생성 시각은 원본 재고 갱신 시각이 아닙니다. + +실제 브라우저가 있으면 /browse/ HTML을 엽니다. 웹 읽기(open/WebFetch)만 있으면 [웹 조회 시작](https://mcp.aka.page/browse/quick)의 실제 링크 ID로 click합니다. 이전 HTML은 같은 조건의 새 응답 링크를 최대2번 확인하고 TimeoutError 재확인은 한 번만 합니다. HTML 생성 시각은 원본 재고 갱신 시각이 아닙니다. 웹 도구의 `Crawled: today`만으로 현재 재고라고 단정하지 마세요. 응답의 HTML 생성 시각과 실제 수량을 확인하고, 수량이 null이면 품절이 아닌 미확인으로 보고합니다. @@ -41,7 +43,7 @@ OpenAI 검색·사용자 요청·학습 봇의 역할과 공개 IP는 [공식 ## 웹 읽기만 가능한 채팅 에이전트 -샌드박스나 셸 없이 웹 읽기만 가능하면 [웹 조회 시작](https://mcp.aka.page/browse/read)의 실제 링크 ID를 먼저 click합니다. 첫 화면과 llms.txt도 이 시작점을 안내합니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. 결과는 서버에서 생성하므로 JavaScript 실행이 필요 없습니다. +샌드박스나 셸 없이 웹 읽기만 가능하면 [웹 조회 시작](https://mcp.aka.page/browse/quick)의 실제 링크 ID를 먼저 click합니다. 첫 화면과 llms.txt도 이 시작점을 안내합니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. 결과는 서버에서 생성하므로 JavaScript 실행이 필요 없습니다. 공개 `/browse/` GET 경로는 같은 실제 조회 결과를 항상 HTML로 제공합니다. 쿼리 문자열은 필요 없습니다. 기존 `/api/`의 JSON과 구형 HTML 주소는 하위호환으로 유지합니다. 일반 API 클라이언트용 기본 경로 예시: `/browse/daiso/stores/%EA%B0%95%EB%82%A8`, `/browse/daiso/inventory/%EC%8B%A4%EC%A0%9C%EC%83%81%ED%92%88ID/%ED%99%95%EC%9D%B8%ED%95%9C%EB%A7%A4%EC%9E%A5%EB%AA%85/lat/%ED%99%95%EC%9D%B8%ED%95%9C%EC%9C%84%EB%8F%84/lng/%ED%99%95%EC%9D%B8%ED%95%9C%EA%B2%BD%EB%8F%84`. 상품 목록의 상세 링크도 HTML로 연결합니다. 다른 서비스의 필수 파라미터는 `/prompt`에서 확인합니다. diff --git a/docs/web-reading-troubleshooting.md b/docs/web-reading-troubleshooting.md index fe9a27be..429f5d40 100644 --- a/docs/web-reading-troubleshooting.md +++ b/docs/web-reading-troubleshooting.md @@ -1,4 +1,6 @@ # 웹 읽기 접근 오류 진단 +빠른 조회는 요청과 일치하는 검색어·지역을 **통째로 선택**합니다. 목록에 없으면 기존 글자 입력을 쓰고 다른 단어로 바꾸지 않습니다. 재고가 목적이면 상품 카드의 **매장 지역 선택**을 바로 열고, 상세 정보가 필요할 때만 상품 상세를 조회하세요. 입력 중 원본 API를 호출하지 않습니다. + 최신 검증: [2026-11-02 재시도·접수형 실험과 운영 반영](web-reading-lab-results-2026-11-02.md). @@ -13,7 +15,7 @@ Cloudflare 관측 구간에는 정상 페이지의 OpenAI 요청에서 200과 1. 연결된 MCP 도구가 있으면 직접 호출합니다. 2. 실제 브라우저 도구가 있으면 `/browse/` 주소를 열고 데이터를 읽습니다. 설치나 MCP 등록은 필요하지 않습니다. -3. 웹 읽기만 가능하면 `/browse/read`에서 응답의 실제 링크 ID를 선택하고 글자·상품·지역 링크를 따릅니다. 새 조회 URL을 직접 조립해 반복하지 마세요. 접근 거절이 계속되면 아래의 완성 URL 복사 재전송을 안내합니다. +3. 웹 읽기만 가능하면 `/browse/quick`에서 응답의 실제 링크 ID를 선택하고 빠른 검색어·상품·지역 링크를 우선 선택하고 목록 밖 입력만 글자 링크를 사용합니다. 새 조회 URL을 직접 조립해 반복하지 마세요. 접근 거절이 계속되면 아래의 완성 URL 복사 재전송을 안내합니다. 4. 가능한 수단이 모두 실패하면 실행한 도구, URL, 오류를 그대로 보고합니다. 접근 오류는 품절이나 로그인 필요를 뜻하지 않습니다. 브라우저 제공 여부는 환경마다 다릅니다. 브라우저가 없는 환경에 있다고 가정하거나 실행하지 않은 셸 명령과 DNS 오류를 만들어 보고하지 마세요. diff --git a/skills/daiso-cli/SKILL.md b/skills/daiso-cli/SKILL.md index 09996b7d..d652808f 100644 --- a/skills/daiso-cli/SKILL.md +++ b/skills/daiso-cli/SKILL.md @@ -15,6 +15,8 @@ metadata: - daiso homepage: https://github.com/hmmhmmhm/daiso-mcp --- +빠른 조회는 요청과 일치하는 검색어·지역을 **통째로 선택**합니다. 목록에 없으면 기존 글자 입력을 쓰고 다른 단어로 바꾸지 않습니다. 재고가 목적이면 상품 카드의 **매장 지역 선택**을 바로 열고, 상세 정보가 필요할 때만 상품 상세를 조회하세요. 입력 중 원본 API를 호출하지 않습니다. + 다이소의 읽기 세션 조회가 오래 걸리면 `pending` 접수와 실제 **결과 확인** 링크를 반환합니다. 접수는 상품·재고 확인 성공이 아닙니다. 각 응답의 새로운 결과 링크를 최대3번 선택하고, 계속 pending이면 미확인으로 보고하세요. 이전 상태 주소를 반복하거나 원본 조회를 다시 실행하지 마세요. 상태 확인은 저장 결과만 읽으며, 완료 결과는 원본의 성공·오류 상태를 유지합니다. HTML·작업 완료 시각과 원본 재고 갱신 시각은 다릅니다. 작업이 만료되거나 시간 초과된 경우에도 수량을0으로 바꾸지 않습니다. @@ -27,14 +29,14 @@ Use this skill to operate the Daiso MCP project through `npx daiso` and the publ ## 설치 없이 첫 조회 -최신 서버 안내는 [현재 버전](https://mcp.aka.page/guide/2026-11-02.2)과 [최신 연결](https://mcp.aka.page/guide/latest)에서 제공합니다. 웹 도구는 과거 리디렉션·수집본을 반환할 수 있으므로 표시된 버전과 HTML 생성 시각을 확인하세요. +최신 서버 안내는 [현재 버전](https://mcp.aka.page/guide/2026-11-02.3)과 [최신 연결](https://mcp.aka.page/guide/latest)에서 제공합니다. 웹 도구는 과거 리디렉션·수집본을 반환할 수 있으므로 표시된 버전과 HTML 생성 시각을 확인하세요. 1. 연결된 MCP 도구가 있으면 `search_products`에 `{"service":"daiso","query":"수납박스","pageSize":3}`를 전달합니다. 2. 실제 브라우저 도구가 있으면 /browse/ HTML GET 주소를 열고 실제 상품·매장·재고를 읽습니다. Work의 내장 브라우저도 사용 가능합니다. -3. 웹 읽기(open/WebFetch)만 가능하면 [웹 조회 시작](https://mcp.aka.page/browse/read)의 실제 링크 ID로 click합니다. 글자 입력·상품·매장 지역 선택 링크를 따르고 새 URL을 조립하지 마세요. 이전 HTML이면 같은 조건으로 새 응답 확인 링크를 최대2번 선택합니다. TimeoutError는 같은 실제 링크를 한 번만 재확인하고 계속 실패하면 미확인으로 보고합니다. 새 HTML도 기존 API 캐시를 사용하며 원본 갱신을 보장하지 않습니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. +3. 웹 읽기(open/WebFetch)만 가능하면 [웹 조회 시작](https://mcp.aka.page/browse/quick)의 실제 링크 ID로 click합니다. 빠른 단어·지역 링크를 먼저 선택하고 목록 밖 입력만 글자 링크를 쓰며 새 URL을 조립하지 마세요. 이전 HTML이면 같은 조건으로 새 응답 확인 링크를 최대2번 선택합니다. TimeoutError는 같은 실제 링크를 한 번만 재확인하고 계속 실패하면 미확인으로 보고합니다. 새 HTML도 기존 API 캐시를 사용하며 원본 갱신을 보장하지 않습니다. click이 없는 도구의 동일 동작은 보장하지 않습니다. 4. 위 세 수단이 없거나 모두 실패하면 실제 조회 제약과 오류를 알립니다. 셸이 있어도 설치 없는 웹 조회가 우선이며 터미널·셸로 우회하지 않습니다. 셸 DNS 오류를 웹 GET 실패로 확대하거나 실행하지 않은 요청의 실패를 만들지 마세요. -https://mcp.aka.page/browse/read +https://mcp.aka.page/browse/quick 웹 도구의 `Crawled: today`만으로 현재 재고라고 단정하지 마세요. 응답의 HTML 생성 시각과 실제 수량을 확인하고, 수량이 null이면 품절이 아닌 미확인으로 보고합니다. diff --git a/src/pages/agentStart.ts b/src/pages/agentStart.ts index 0201390e..168ec249 100644 --- a/src/pages/agentStart.ts +++ b/src/pages/agentStart.ts @@ -1,10 +1,10 @@ /** 검색·읽기 사본의 안내 세대를 구분하는 고정 버전 */ -export const AGENT_GUIDE_VERSION = '2026-11-02.2'; +export const AGENT_GUIDE_VERSION = '2026-11-02.3'; export const AGENT_GUIDE_UPDATED = '2026-11-02'; export const AGENT_GUIDE_STAMP = `안내 버전: ${AGENT_GUIDE_VERSION} · 갱신일: ${AGENT_GUIDE_UPDATED}`; export const AGENT_GUIDE_PATH = `/guide/${AGENT_GUIDE_VERSION}`; export const WEB_SEARCH_DESCRIPTION = - 'ChatGPT 웹 읽기로 설치·API 키·MCP 연결 설정 없이 조회하세요. /browse/read의 실제 링크 ID를 click하고 임의 검색어·지역을 글자로 선택합니다. 같은 조건의 새 응답 링크로 새 HTML 주소를 확인할 수 있습니다. 다이소·편의점 재고와 영화·생활 정보도 지원합니다.'; + 'ChatGPT 웹 읽기로 설치·API 키·MCP 연결 설정 없이 조회하세요. /browse/quick의 실제 링크 ID를 click하고 빠른 검색어·지역을 통째로 선택하고, 목록 밖 입력만 글자 링크를 사용합니다. 같은 조건의 새 응답 링크로 새 HTML 주소를 확인할 수 있습니다. 다이소·편의점 재고와 영화·생활 정보도 지원합니다.'; /** 완성 URL을 사용자 메시지로 제공하는 복구 경로이며 자동 허용을 보장하지 않습니다. */ export const WEB_READ_RECOVERY_RULE = @@ -12,13 +12,13 @@ export const WEB_READ_RECOVERY_RULE = /** 웹 읽기 환경에서 쿼리 없는 HTML 경로를 우선합니다. */ export const WEB_READ_RULE = - '웹 조회는 /browse/ 경로를 사용하세요. 실제 브라우저 도구가 있으면 /browse/ HTML을 열고, 웹 읽기(open/WebFetch)만 있으면 /browse/read의 실제 링크 ID로 click하여 시작하세요. 새 URL을 조립하지 말고 글자 선택·상품·매장 지역 선택 링크를 따르세요. 오래된 수집본이면 같은 조건으로 새 응답 확인 링크를 최대2번 선택해 새 주소와 HTML 생성 시각을 확인하세요. 이 경로도 기존 API 캐시·한도·권한을 사용하며 원본 재고 갱신을 보장하지 않습니다. TimeoutError는 같은 실제 링크를 한 번만 재확인하고 계속 실패하면 미확인으로 보고하세요. URL 접근 거절을 같은 주소의 직접 open으로 반복하지 마세요. Crawled today만으로 현재 재고를 단정하지 말고 응답 생성 시각을 보고하세요. 모든 후속 /browse/ 조회는 자동 HTML이며 쿼리 문자열은 필요 없습니다. JSON API·셸로 우회하거나 브라우저를 추가로 계속 실행하지 마세요. 접근 오류를 상품 없음·재고0·로그인 필요로 단정하지 마세요. 해당 오류만으로 이 환경에서 실행할 수 없다고 단정하지 마세요.' + + '웹 조회는 /browse/ 경로를 사용하세요. 실제 브라우저 도구가 있으면 /browse/ HTML을 열고, 웹 읽기(open/WebFetch)만 있으면 /browse/quick의 실제 링크 ID로 click하여 시작하세요. 새 URL을 조립하지 말고 요청과 일치하는 빠른 검색어·지역 링크를 먼저 선택하세요. 목록 밖 입력만 글자 링크를 사용하세요. 재고가 목적이면 상품 카드의 매장 지역 선택을 바로 여세요. 상세 정보가 필요할 때만 상품 상세를 조회하세요. 오래된 수집본이면 같은 조건으로 새 응답 확인 링크를 최대2번 선택해 새 주소와 HTML 생성 시각을 확인하세요. 이 경로도 기존 API 캐시·한도·권한을 사용하며 원본 재고 갱신을 보장하지 않습니다. TimeoutError는 같은 실제 링크를 한 번만 재확인하고 계속 실패하면 미확인으로 보고하세요. URL 접근 거절을 같은 주소의 직접 open으로 반복하지 마세요. Crawled today만으로 현재 재고를 단정하지 말고 응답 생성 시각을 보고하세요. 모든 후속 /browse/ 조회는 자동 HTML이며 쿼리 문자열은 필요 없습니다. JSON API·셸로 우회하거나 브라우저를 추가로 계속 실행하지 마세요. 접근 오류를 상품 없음·재고0·로그인 필요로 단정하지 마세요. 해당 오류만으로 이 환경에서 실행할 수 없다고 단정하지 마세요.' + WEB_READ_RECOVERY_RULE; /** 셸 존재를 네트워크 접근 가능 여부로 오해하지 않는 실행 순서 */ export const ROUTE_SELECTION_TEXT = `1. 연결된 MCP 도구가 있으면 search_products {"service":"daiso","query":"수납박스","pageSize":3}를 호출합니다. 2. 실제 브라우저 도구가 있으면 /browse/ HTML GET 주소를 열고 페이지의 실제 상품·매장·재고를 읽습니다. Work의 내장 브라우저도 이 경로를 사용합니다. -3. 웹 읽기(open/WebFetch)만 가능하면 /browse/read의 실제 링크 ID로 click합니다. 임의 검색어를 글자로 선택하고 상품의 매장 지역 선택으로 재고를 조회합니다. 이전 HTML이면 같은 조건의 새 응답 링크를 최대2번 선택하고 생성 시각을 확인하세요. TimeoutError 재확인은 한 번만 합니다. 도구에 click이 없으면 실제 링크를 열되 같은 동작을 보장하지 않습니다. 수집본이나 미확인 수량을 현재 재고0으로 바꾸지 마세요. +3. 웹 읽기(open/WebFetch)만 가능하면 /browse/quick의 실제 링크 ID로 click합니다. 빠른 상품 검색에서 일치하는 단어를 통째로 선택하고 상품 카드의 매장 지역 선택에서 지역도 통째로 선택합니다. 빠른 목록에 없을 때만 글자 입력을 쓰며 다른 검색어로 바꾸지 않습니다. 이전 HTML이면 같은 조건의 새 응답 링크를 최대2번 선택하고 생성 시각을 확인하세요. TimeoutError 재확인은 한 번만 합니다. 도구에 click이 없으면 실제 링크를 열되 같은 동작을 보장하지 않습니다. 수집본이나 미확인 수량을 현재 재고0으로 바꾸지 마세요. ${WEB_READ_RECOVERY_RULE} 4. 위 세 수단이 없거나 모두 실패하면 실제 조회 제약과 오류를 알립니다. 셸이 있어도 설치 없는 웹 조회가 우선입니다. 터미널·셸 호출로 우회하지 않습니다. 셸 DNS 오류를 웹 GET 실패나 서버 장애로 확대하지 말고 실제로 실행하지 않은 요청의 실패를 만들지 마세요.`; @@ -45,9 +45,9 @@ ${AGENT_GUIDE_STAMP} ${ROUTE_SELECTION_TEXT} 웹 읽기 시작 (문서의 실제 링크 ID를 선택): -${baseUrl}/browse/read +${baseUrl}/browse/quick 응답의 글자 입력 링크에서 임의 상품명·매장 지역을 선택하세요. 수납박스·핫식스 등 예제도 이 화면의 실제 링크로 조회합니다. 동일 주소의 수집본이 오래됐으면 새 응답 확인 링크를 선택하세요. 새 HTML 주소도 원본 API의 캐시를 사용하며 원본 재고의 새 갱신을 보장하지 않습니다. -이것은 공개 REST GET의 웹 조회이며 MCP 프로토콜 호출이 아닙니다. 상품 결과의 실제 상품 상세·매장 지역 선택 링크를 따르세요. 지역 입력 후 표시된 실제 재고 조회 링크를 선택합니다. 후속 URL을 새로 조립하지 마세요. 상품 검색만으로 매장 재고를 단정하지 마세요. 일반 API 클라이언트의 경로·필터 문법은 상세 사용 안내를 참고하세요. +이것은 공개 REST GET의 웹 조회이며 MCP 프로토콜 호출이 아닙니다. 상품 결과의 실제 매장 지역 선택 링크를 바로 따르세요. 상세 정보가 필요할 때만 상품 상세를 여세요. 지역 입력 후 표시된 실제 재고 조회 링크를 선택합니다. 후속 URL을 새로 조립하지 마세요. 상품 검색만으로 매장 재고를 단정하지 마세요. 일반 API 클라이언트의 경로·필터 문법은 상세 사용 안내를 참고하세요. ${IMAGE_SOURCE_RULE} ${EXECUTION_EVIDENCE_RULE} @@ -82,7 +82,7 @@ function createGuideHtmlResponse(baseUrl: string): Response { DAISO MCP 사용 안내 ${AGENT_GUIDE_VERSION}

DAISO MCP: 설치 없이 시작하기

${AGENT_GUIDE_STAMP}

- +

안내 응답 생성 시각: . 원본 재고 갱신 시각이 아닙니다.

${text}
`, { diff --git a/src/pages/browse.ts b/src/pages/browse.ts index 79ab0cd2..2b5f952f 100644 --- a/src/pages/browse.ts +++ b/src/pages/browse.ts @@ -1,3 +1,4 @@ +import { renderQuickBrowseLinks } from './browseQuickLinks.js'; /** 기존 API의 보호·캐시·응답을 그대로 사용하는 웹 조회 진입점입니다. */ import type { Hono } from 'hono'; import type { AppBindings } from '../api/response.js'; @@ -24,7 +25,7 @@ export function registerBrowseRoutes(app: Hono<{ Bindings: AppBindings }>): void registerBrowseReadRoutes(app); app.get('/browse/compose', () => page( - '

링크를 선택하여 검색하기

검색어를 URL에 직접 조립하지 않고 한 글자씩 선택합니다. 아직 실제 조회를 실행하지 않습니다.

', + `

링크를 선택하여 검색하기

${renderQuickBrowseLinks({ mode: 'products' })}

검색어를 URL에 직접 조립하지 않고 한 글자씩 선택합니다. 아직 실제 조회를 실행하지 않습니다.

`, ), ); app.get('/browse/compose/*', (c) => { @@ -41,7 +42,7 @@ export function registerBrowseRoutes(app: Hono<{ Bindings: AppBindings }>): void }); app.get('/browse', () => page( - `

설치 없이 웹으로 조회하기

임의 검색어를 글자 링크로 입력하기

웹 읽기(open/WebFetch)는 아래 실제 링크 ID로 click하세요. 모든 결과는 HTML입니다. format이나 쿼리 문자열은 필요 없습니다.

수납박스 상품 3개 조회 · 핫식스 검색 · 강남역 매장 검색

웹 읽기는 글자 입력과 응답의 실제 링크 ID를 선택하세요. 아래는 일반 API 클라이언트용 경로 안내입니다. 주요 검색어 뒤에는 /page/2/pageSize/10처럼 조건 이름과 값을 붙입니다. 주요 검색어 없이 조건만 사용하려면 /browse/query/서비스/기능/조건/값을 사용하세요. 상품 검색은 재고 확인이 아닙니다. 실제 상품 ID와 사용자 지역으로 재고를 별도 조회하세요.

`, + `

설치 없이 웹으로 조회하기

${renderQuickBrowseLinks({ mode: 'products' })}

임의 검색어를 글자 링크로 입력하기

웹 읽기(open/WebFetch)는 요청과 일치하는 빠른 상품 검색 링크를 먼저 click하세요. 상품 결과 카드의 매장 지역 선택을 바로 열고 지역도 통째로 선택합니다. 불필요한 상품 상세·전체 경로 탐색은 생략하세요. 모든 결과는 HTML입니다. format이나 쿼리 문자열은 필요 없습니다.

수납박스 상품 3개 조회 · 핫식스 검색 · 강남역 매장 검색

웹 읽기는 글자 입력과 응답의 실제 링크 ID를 선택하세요. 아래는 일반 API 클라이언트용 경로 안내입니다. 주요 검색어 뒤에는 /page/2/pageSize/10처럼 조건 이름과 값을 붙입니다. 주요 검색어 없이 조건만 사용하려면 /browse/query/서비스/기능/조건/값을 사용하세요. 상품 검색은 재고 확인이 아닙니다. 실제 상품 ID와 사용자 지역으로 재고를 별도 조회하세요.

`, ), ); app.get('/browse/', (c) => c.redirect('/browse', 307)); diff --git a/src/pages/browseComposer.ts b/src/pages/browseComposer.ts index 12b160db..378bc070 100644 --- a/src/pages/browseComposer.ts +++ b/src/pages/browseComposer.ts @@ -1,3 +1,4 @@ +import { renderQuickBrowseLinks } from './browseQuickLinks.js'; /** 새 URL을 조립하지 않고 실제 링크만 선택하여 검색 조건을 입력합니다. */ const INITIALS = [...'ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ']; const VOWELS = [...'ㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣ']; @@ -65,6 +66,16 @@ export function renderBrowseComposer(source: URL): string | null { : mode === 'stores' ? '매장 검색어 선택' : '상품 검색어 선택'; + const quick = + !text && !stage + ? renderQuickBrowseLinks( + mode === 'inventory' + ? { mode: 'inventory', productId: productId! } + : mode === 'stores' + ? { mode: 'stores' } + : { mode: 'products' }, + ) + : ''; const query = encodeURIComponent(text); const target = mode === 'inventory' @@ -72,7 +83,7 @@ export function renderBrowseComposer(source: URL): string | null { : mode === 'stores' ? `/browse/daiso/stores/${query}/limit/3` : `/browse/daiso/products/${query}/pageSize/3`; - return `

${title}

${productId ? `

선택한 상품 ID: ${productId}

` : ''} + return `

${title}

${productId ? `

선택한 상품 ID: ${productId}

` : ''}${quick}

현재 입력: ${text || '(비어 있음)'}

아직 상품·매장·재고를 조회하지 않았습니다. 아래 실제 링크 ID로 이동하세요. URL을 직접 조립하지 마세요. 한글은 초성, 중성, 완성 글자를 차례로 선택하고 영문·숫자는 바로 선택합니다. 최대32자이며 개인정보는 입력하지 마세요.

${text.trim() ? `

입력한 검색어로 실제 조회

` : ''} diff --git a/src/pages/browseQuickLinks.ts b/src/pages/browseQuickLinks.ts new file mode 100644 index 00000000..8ee5a072 --- /dev/null +++ b/src/pages/browseQuickLinks.ts @@ -0,0 +1,86 @@ +/** 검색 조건의 실제 링크입니다. 상품·매장이나 재고 응답을 미리 만들지 않습니다. */ +const PRODUCTS = [ + '수납박스', + '물티슈', + '건전지', + '지갑', + '충전기', + '보조배터리', + '케이블', + '이어폰', + '우산', + '양말', + '슬리퍼', + '텀블러', + '머그컵', + '도시락', + '지퍼백', + '종이컵', + '마스크', + '핸드크림', + '선크림', + '화장솜', + '면봉', + '노트', + '볼펜', + '연필', + '테이프', + '스티커', + '파우치', + '쇼핑백', + '휴지', + '청소포', + '세제', + '핫식스', +]; +const AREAS = [ + '강남역', + '홍대입구역', + '서울역', + '잠실역', + '건대입구역', + '신촌역', + '신림역', + '사당역', + '왕십리역', + '합정역', + '안산 중앙역', + '상록수역', + '수원역', + '성남', + '분당', + '판교역', + '인천', + '부평역', + '일산', + '대전', + '천안', + '청주', + '광주', + '전주', + '대구', + '동대구역', + '울산', + '부산', + '서면역', + '창원', + '춘천', + '제주', +]; +type Target = { mode: 'products' | 'stores' } | { mode: 'inventory'; productId: string }; + +export function renderQuickBrowseLinks(target: Target): string { + const products = target.mode === 'products'; + const terms = products ? PRODUCTS : AREAS; + const links = terms.map((term) => { + const encoded = encodeURIComponent(term); + const path = + target.mode === 'inventory' + ? `/browse/daiso/inventory/${target.productId}/${encoded}/pageSize/3` + : products + ? `/browse/daiso/products/${encoded}/pageSize/3` + : `/browse/daiso/stores/${encoded}/limit/3`; + return `
  • ${term}
  • `; + }); + return `

    ${products ? '빠른 상품 검색' : '빠른 지역 선택'}

    요청한 검색어·지역과 일치하는 항목을 통째로 선택하세요. 비슷한 다른 검색어로 바꾸지 마세요. 원하는 항목이 없으면 글자 입력을 사용하세요.

    `; +} diff --git a/src/pages/browseRead.ts b/src/pages/browseRead.ts index 088b43d6..b087a871 100644 --- a/src/pages/browseRead.ts +++ b/src/pages/browseRead.ts @@ -15,7 +15,7 @@ export function registerBrowseReadRoutes(app: Hono): void { return c.text('읽기 세션 경로가 올바르지 않습니다.', 400); const prefix = `/browse/read/${id}`; const scoped = (path: string) => - path === '/browse/read' || path.startsWith('/browse/read/') + path === '/browse/quick' || path === '/browse/read' || path.startsWith('/browse/read/') ? path : prefix + path.slice('/browse'.length); const target = new URL(c.req.url); @@ -88,6 +88,7 @@ export function registerBrowseReadRoutes(app: Hono): void { ? withBrowseJob(c, operation, () => controller.abort()) : operation(); }; + app.all('/browse/quick', (c) => forward(c, crypto.randomUUID(), '')); app.all('/browse/read', (c) => forward(c, crypto.randomUUID(), '')); const existing = (c: Context) => { const id = c.req.path.split('/')[3]; diff --git a/src/pages/discovery.ts b/src/pages/discovery.ts index c1beef66..d22c0683 100644 --- a/src/pages/discovery.ts +++ b/src/pages/discovery.ts @@ -27,7 +27,7 @@ export function buildDiscoveryAssets(info: { tools: string[]; }): Record { const services = info.services.map((service) => escapeHtml(service.name)).join(' · '); - const firstQuery = '/browse/read'; + const firstQuery = '/browse/quick'; return { 'discovery.html': ` @@ -40,7 +40,7 @@ export function buildDiscoveryAssets(info: {

    DAISO MCP

    ${escapeHtml(AGENT_GUIDE_STAMP)} · 현재 버전 안내 · 항상 최신 안내로 이동

    설치 없이 상품·매장·재고와 영화관 정보를 조회하세요. 사용자 API 키나 로그인 없이 쓸 수 있는 공개 서비스입니다.

    ${escapeHtml(WEB_SEARCH_DESCRIPTION)}

    ${escapeHtml(WEB_READ_RULE)}

    -

    바로 조회하기

    상품·매장·재고 웹 조회 시작

    화면의 실제 링크에서 원하는 검색어와 지역을 선택하세요. 이전 HTML은 같은 조건으로 새 응답 확인 링크로 구분합니다. 원본 API 캐시는 유지됩니다.

    +

    바로 조회하기

    상품·매장·재고 웹 조회 시작

    빠른 상품 검색과 지역 선택에서 요청과 일치하는 단어를 통째로 선택하세요. 목록 밖 입력만 글자 링크를 쓰고 재고가 목적이면 상품 카드의 매장 지역 선택을 바로 여세요. 이전 HTML은 같은 조건으로 새 응답 확인 링크로 구분합니다. 원본 API 캐시는 유지됩니다.

    위 링크는 셸·설치 없이 웹 읽기로 호출하는 공개 REST GET의 HTML 결과입니다. 모든 서비스의 /browse/ 경로는 자동 HTML이며 쿼리 문자열은 필요 없습니다. 검색한 상품의 실제 ID로 재고를 조회하세요. 상품 검색만으로 매장 재고를 단정하지 마세요.

    에이전트에서 설치 없이 사용하기

    원격 MCP 주소: ${BASE}
    전송: Streamable HTTP · 인증: 없음

    ${escapeHtml(ROUTE_SELECTION_TEXT).replaceAll('\n', '
    ')}

    diff --git a/tests/pages/agentStart.test.ts b/tests/pages/agentStart.test.ts index a02b98c8..4849d15b 100644 --- a/tests/pages/agentStart.test.ts +++ b/tests/pages/agentStart.test.ts @@ -22,7 +22,7 @@ describe('에이전트 시작 안내', () => { expect(text).toContain('initialize'); expect(text).toContain('REST GET'); expect(text).toContain('MCP 프로토콜 호출이 아닙니다'); - expect(text).toContain('/browse/read'); + expect(text).toContain('/browse/quick'); expect(text).toContain('https://example.com/prompt'); expect(text).toContain('전체 도구 목록이나 OpenAPI를 먼저 읽을 필요는 없습니다'); expect(text.split('\n').length).toBeLessThan(45); diff --git a/tests/pages/apiResult.test.ts b/tests/pages/apiResult.test.ts index 19f547f1..112654f7 100644 --- a/tests/pages/apiResult.test.ts +++ b/tests/pages/apiResult.test.ts @@ -67,8 +67,8 @@ describe('GET HTML 결과', () => { }); it('셸 없는 에이전트에게 HTML GET을 안내하며 txt charset을 명시한다', async () => { const assets = buildDiscoveryAssets({ services: [], tools: [] }); - expect(assets['discovery.html']).toContain('/browse/read'); - expect(assets['llms.txt']).toContain('/browse/read'); + expect(assets['discovery.html']).toContain('/browse/quick'); + expect(assets['llms.txt']).toContain('/browse/quick'); const { default: app } = await import('../../src/index.js'); const entry = await app.request('/browse/read'); expect(entry.status).toBe(200); diff --git a/tests/pages/browse-guidance.test.ts b/tests/pages/browse-guidance.test.ts index 037664d5..6aa6b35c 100644 --- a/tests/pages/browse-guidance.test.ts +++ b/tests/pages/browse-guidance.test.ts @@ -43,7 +43,7 @@ it('완전한 웹 예시 URL은 공백 없이 열리고 등록된 조회로 해 if (!url) continue; expect(url).not.toMatch(/\s/); const source = new URL(url); - if (source.pathname === '/browse/read') { + if (['/browse/read', '/browse/quick'].includes(source.pathname)) { const { default: app } = await import('../../src/index.js'); const response = await app.request(url); expect(response.status).toBe(200); diff --git a/tests/pages/browseQuick.test.ts b/tests/pages/browseQuick.test.ts new file mode 100644 index 00000000..10056720 --- /dev/null +++ b/tests/pages/browseQuick.test.ts @@ -0,0 +1,106 @@ +import { Hono } from 'hono'; +import { describe, expect, it } from 'vitest'; +import type { AppBindings } from '../../src/api/response.js'; +import { apiResultHtml } from '../../src/pages/apiResult.js'; +import { registerBrowseRoutes } from '../../src/pages/browse.js'; + +function fixture() { + const app = new Hono<{ Bindings: AppBindings }>(); + const hits: URL[] = []; + app.use('/api/*', apiResultHtml); + app.get('/api/daiso/products', (c) => { + hits.push(new URL(c.req.url)); + return c.json({ + success: true, + data: { products: [{ id: '900001', name: '테스트 지갑', price: 1000 }] }, + }); + }); + app.get('/api/daiso/inventory', (c) => { + hits.push(new URL(c.req.url)); + return c.json({ + success: true, + data: { + productId: c.req.query('productId'), + storeInventory: { + totalStores: 1, + unknownStockCount: 0, + stores: [{ store: { name: '테스트 강남역점', storeCode: 'TEST' }, quantity: 7 }], + }, + }, + }); + }); + registerBrowseRoutes(app); + return { app, hits }; +} +async function follow(app: ReturnType['app'], body: string, label: string) { + const links = [...body.matchAll(/href="([^"]+)">([^<]+)<\/a>/g)]; + const href = links.find((x) => x[2] === label)?.[1]; + expect(href, `실제 링크 없음: ${label}`).toBeDefined(); + const response = await app.request(href!); + expect(response.status).toBe(200); + return response.text(); +} +describe('첫 조회의 실제 링크 왕복 축소', () => { + it('시작·상품·지역선택·재고4페이지로 끝내고 원본 API는 각1회만 호출한다', async () => { + const { app, hits } = fixture(); + const first = await app.request('/browse/quick'); + expect(first.status).toBe(200); + let body = await first.text(); + expect(hits).toHaveLength(0); + body = await follow(app, body, '지갑'); + expect(hits).toHaveLength(1); + expect(hits[0].searchParams.get('q')).toBe('지갑'); + body = await follow(app, body, '매장 지역 선택'); + expect(hits).toHaveLength(1); + expect(body).toContain('900001'); + body = await follow(app, body, '강남역'); + expect(hits).toHaveLength(2); + expect(hits[1].pathname).toBe('/api/daiso/inventory'); + expect(Object.fromEntries(hits[1].searchParams)).toMatchObject({ + productId: '900001', + keyword: '강남역', + pageSize: '3', + format: 'html', + }); + expect(body).toContain('테스트 강남역점'); + expect(body).toContain('quantity'); + }); + it('추천 목록 밖 입력은 대체하지 않고 기존 글자 선택을 유지한다', async () => { + const { app, hits } = fixture(); + const body = await (await app.request('/browse/compose/daiso/products/~')).text(); + expect(body).toContain('빠른 상품 검색'); + expect(body).toContain('초성 ㄱ'); + expect(body).toContain('일치'); + const partial = await (await app.request('/browse/compose/daiso/products/연')).text(); + expect(partial).toContain('현재 입력'); + expect(partial).toContain('%EC%97%B0/pageSize/3'); + expect(hits).toHaveLength(0); + }); + it('상품과 지역의 빠른 선택을 폭넓게 제공하고 원문과 경로가 일치한다', async () => { + const { app, hits } = fixture(); + for (const mode of ['products', 'stores']) { + const body = await (await app.request(`/browse/compose/daiso/${mode}/~`)).text(); + const links = [...body.matchAll(/href="([^" ]+)">([^<]+)<\/a>/g)].filter((x) => + x[1].startsWith(`/browse/daiso/${mode}/`), + ); + expect(links.length).toBeGreaterThanOrEqual(24); + for (const [, href, label] of links) { + expect(decodeURIComponent(href.split('/')[4])).toBe(label); + } + } + const body = await (await app.request('/browse/compose/daiso/stores/외딴동네')).text(); + expect(body).not.toContain('빠른 지역 선택'); + expect(body).toContain(encodeURIComponent('외딴동네')); + expect(hits).toHaveLength(0); + }); + it('기존 읽기 시작과 매장 입력에서도 빠른 선택을 제공하고 GET만 허용한다', async () => { + const { app, hits } = fixture(); + const body = await (await app.request('/browse/read')).text(); + expect(body).toMatch(/href="\/browse\/read\/[0-9a-f-]+\/daiso\/products\//); + const places = await (await app.request('/browse/compose/daiso/stores/~')).text(); + expect(places).toContain('/browse/daiso/stores/'); + expect(places).toContain('강남역'); + expect((await app.request('/browse/quick', { method: 'POST' })).status).toBe(405); + expect(hits).toHaveLength(0); + }); +}); diff --git a/tests/pages/discovery.test.ts b/tests/pages/discovery.test.ts index c078368a..b97c9a4d 100644 --- a/tests/pages/discovery.test.ts +++ b/tests/pages/discovery.test.ts @@ -20,7 +20,7 @@ describe('공개 발견 자산', () => { expect(assets['discovery.html']).toContain(''); expect(assets['discovery.html']).toContain('설치 없이'); expect(assets['discovery.html']).toContain('search_products'); - expect(assets['discovery.html']).toContain('/browse/read'); + expect(assets['discovery.html']).toContain('/browse/quick'); expect(assets['discovery.html']).toContain('https://mcp.aka.page/'); expect(assets['discovery.html']).not.toContain('lottemart'); expect(assets['discovery.html']).not.toContain('Zyte'); diff --git a/tests/pages/latestGuide.test.ts b/tests/pages/latestGuide.test.ts index 3b21443a..015fec50 100644 --- a/tests/pages/latestGuide.test.ts +++ b/tests/pages/latestGuide.test.ts @@ -4,7 +4,7 @@ import app from '../../src/index.js'; import { buildDiscoveryAssets } from '../../src/pages/discovery.js'; import { apiResultHtml } from '../../src/pages/apiResult.js'; -const version = '2026-11-02.2'; +const version = '2026-11-02.3'; describe('최신 안내 경로와 원본 이미지', () => { it.each(['/guide/latest', '/guide/2026-10-21.2', '/guide/unknown'])( '%s는 캐시 없이 현재 버전으로 연결한다', @@ -21,7 +21,7 @@ describe('최신 안내 경로와 원본 이미지', () => { expect(r.status).toBe(200); expect(r.headers.get('Cache-Control')).toBe('no-store'); expect(text).toContain('안내 버전: ' + version); - expect(text).toContain('/browse/read'); + expect(text).toContain('/browse/quick'); expect(text).toContain('image_query'); expect(text).toContain('이미지 검색'); }); @@ -29,7 +29,7 @@ describe('최신 안내 경로와 원본 이미지', () => { const response = await app.request('/guide/' + version); const text = await response.text(); expect(response.headers.get('Content-Type')).toContain('text/html'); - expect(text).toContain('웹 조회 시작'); + expect(text).toContain('웹 조회 시작'); expect(text).toContain('최신 안내 확인'); expect(text).toContain('응답 생성 시각'); expect(text).toContain(''); @@ -52,7 +52,7 @@ describe('최신 안내 경로와 원본 이미지', () => { const a = buildDiscoveryAssets({ services: [], tools: [] }); for (const key of ['discovery.html', 'robots.txt', 'sitemap.xml']) expect(a[key]).toContain('/guide/' + version); - expect(a['discovery.html']).toContain('/browse/read'); + expect(a['discovery.html']).toContain('/browse/quick'); expect(a['_headers']).toContain('Cache-Control: no-store'); }); it('HTML 후속 링크와 원본 Markdown은 현재 버전을 사용하고 JSON 캐시는 유지한다', async () => { diff --git a/tests/pages/productCards.test.ts b/tests/pages/productCards.test.ts index 28d02c5d..4b8e3689 100644 --- a/tests/pages/productCards.test.ts +++ b/tests/pages/productCards.test.ts @@ -99,7 +99,7 @@ describe('실제 상품 이미지 카드', () => { const assets = buildDiscoveryAssets({ services: [], tools: [] }); expect(assets['discovery.html']).toMatch(/name="description" content="ChatGPT.*browse\//); for (const text of [assets['discovery.html'], buildAgentStartText('https://example.com')]) { - expect(text).toContain('안내 버전: 2026-11-02.2'); + expect(text).toContain('안내 버전: 2026-11-02.3'); expect(text).toContain('갱신일: 2026-11-02'); } }); diff --git a/tests/scripts/repository-config.test.ts b/tests/scripts/repository-config.test.ts index 06b23dd5..f205a19f 100644 --- a/tests/scripts/repository-config.test.ts +++ b/tests/scripts/repository-config.test.ts @@ -315,7 +315,7 @@ describe('repository maintenance configuration', () => { expect(fastStart).toContain('설치·API 키 없이'); expect(fastStart).toMatch(/\| 인증\s*\| 없음\s*\|/); expect(readme).toContain('로컬 설치·빌드·API 키 준비 없이'); - expect(fastStart).toContain('docs/service-reference.md?guide=2026-11-02.2#설치-없이-첫-조회'); + expect(fastStart).toContain('docs/service-reference.md?guide=2026-11-02.3#설치-없이-첫-조회'); const directCall = readText('docs/service-reference.md').split('### MCP 표준 응답 모델')[0]; expect(directCall).toContain('WebFetch'); expect(directCall).toContain('/browse/daiso/products/');