Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/superpowers/plans/2026-10-21-compact-mcp.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 공개 MCP 도구 10개 구현 계획

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking.

**Goal:** 기본 MCP 도구를 10개로 고정하고 구형 호출을 보존한다.
**Architecture:** 명시적 작업 매핑과 기존 Zod 입력/핸들러를 재사용하는 어댑터. SDK 등록은 모두 유지하고 목록 응답만 고정 공개 계약으로 제공한다.
Expand All @@ -23,5 +23,5 @@

### 4. 검증과 반영
- [x] `npm run check`, `npm run test:coverage`, `npm run build`를 실행해 전체 성공/네 지표100%를 확인한다. SDK 실제통신과 invalid 조합 회귀는 단순 모킹과 별도로 유지한다.
- [ ] 독립 리뷰 후 검증된 변경을 논리적으로 커밋/푸시하고 PR을 생성·첨부한다. GitHub CI/Coverage/CodeQL 통과 후 병합한다.
- [ ] 운영 / 및 /mcp tools/list10개, 새/구형 상품·매장·재고 호출과 CLI 유지 검증 후 PROJECT.md에 근거와 남은 문제를 쓴다. PlayMCP 콘솔 등록목록은 별도 갱신이 필요함을 구분한다.
- [x] 독립 리뷰 후 검증된 변경을 논리적으로 커밋/푸시하고 PR을 생성·첨부한다. GitHub CI/Coverage/CodeQL 통과 후 병합한다.
- [x] 운영 / 및 /mcp tools/list10개, 새/구형 상품·매장·재고 호출과 CLI 유지 검증 후 PROJECT.md에 근거와 남은 문제를 쓴다. PlayMCP 콘솔 등록목록은 별도 갱신이 필요함을 구분한다.
11 changes: 11 additions & 0 deletions src/services/gs25/tools/checkInventory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,17 @@ async function checkInventory(args: CheckInventoryArgs): Promise<McpToolResponse
}
}

// 카탈로그에 지역 매장이 없거나 주소 변환이 실패하면 지역 자체를 검색합니다.
if (storeKeyword.trim() && !validKoreanCoordinates(resolvedLatitude ?? NaN, resolvedLongitude ?? NaN) &&
typeof latitude !== 'number' && typeof longitude !== 'number') {
const geocoded = await geocodeGs25Address(storeKeyword, { ...args, timeout: timeoutMs, googleMapsApiKey });
if (geocoded) {
resolvedLatitude = geocoded.latitude;
resolvedLongitude = geocoded.longitude;
geocodeUsed = true;
}
}

if (storeKeyword.trim() && !validKoreanCoordinates(resolvedLatitude ?? NaN, resolvedLongitude ?? NaN))
throw new ServiceError('GS25_LOCATION_UNRESOLVED',
'요청한 지역의 위치를 확인하지 못했습니다. 좌표를 지정하거나 잠시 후 다시 조회해주세요.', 503, true);
Expand Down
14 changes: 14 additions & 0 deletions tests/api/gs25-location-resilience.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,17 @@ describe('GS25 지정 지역 위치 보호', () => {
});

});

it('MCP는 매장 카탈로그로 좌표를 못 찾으면 지역 검색으로 무료 좌표를 보완한다',async()=>{
mocks.stores.mockResolvedValueOnce({stores:[],totalCount:0,cacheHit:false});
mocks.geocode.mockResolvedValueOnce({latitude:37.4979,longitude:127.0276});
const result=await createCheckInventoryTool().handler({keyword:'콜라',itemCode:'ITEM',storeKeyword:'강남'});
expect(mocks.geocode).toHaveBeenCalledWith('강남',expect.any(Object));
expect(mocks.stores.mock.calls[1][0]).toMatchObject({itemCode:'ITEM',latitude:37.4979,longitude:127.0276});
expect(JSON.parse(result.content[0].text)).toMatchObject({geocodeUsed:true});
});
it('MCP는 후보 주소 변환이 실패해도 지역 검색이 실패하면 기존 미확인 오류를 유지한다',async()=>{
await expect(createCheckInventoryTool().handler({keyword:'콜라',itemCode:'ITEM',storeKeyword:'부산'})).rejects.toMatchObject({code:'GS25_LOCATION_UNRESOLVED'});
expect(mocks.geocode.mock.calls.map(call=>call[0])).toEqual(['부산 중구 중앙대로 1','부산']);
expect(mocks.stores).toHaveBeenCalledTimes(1);
});
Loading