Yeosugaja(여수가자) Travel MCP
by mogoon
README.md
# Yeosugaja(여수가자) Travel MCP
A **read-only** [MCP](https://modelcontextprotocol.io) server that exposes verified
Yeosu travel itineraries, curated POIs, and transport schedules — sourced from the
daily R2 bundle cache the app already produces. Built to be registered on **Kakao
PlayMCP** and consumed by MCP clients (Claude, ChatGPT dev mode, OpenClaw, …).
It is a thin, stateless Cloudflare Worker: on each tool call it loads
`{region}/latest.json` from R2 (60s in-isolate memo) and answers from it. It never
touches the API or the database, so periodic bundle regeneration cannot affect it —
worst case is a few minutes of staleness.
## Architecture
```
API batch ──► R2 {region}/latest.json ──► this Worker (MCP) ──► MCP client
generate_daily_bundle (atomic, gzipped) read-only, stateless (PlayMCP)
```
## Tools (7)
| tool | purpose |
|------|---------|
| `list_itineraries` | list verified itineraries, filter by mbti/transport/duration/party/season |
| `get_itinerary` | full time-ordered timeline of one itinerary |
| `recommend_itinerary` | top matches for an MBTI, ranked by confidence |
| `search_pois` | search POIs by text/tag/grade/indoor/signature |
| `get_poi` | full detail for one POI |
| `get_transport` | ferry / airport / train / bus schedules |
| `get_region_info` | weather, event banner, dining rules, bundle freshness |
All tools are `readOnlyHint: true`, non-destructive, idempotent, closed-world.
## 사용 예시 프롬프트 (Example prompts)
이 MCP를 연결한 에이전트(Claude·ChatGPT·OpenClaw 등)에 자연어로 말하면 아래처럼 도구가 불린다.
**단일 도구**
- "INFP한테 맞는 여수 하루 코스 추천해줘" → `recommend_itinerary(mbti=INFP)`
- "여수 당일치기 일정 목록 보여줘, 도보로 다니는 걸로" → `list_itineraries(duration_type=1day, transport=walk)`
- "그 코스(it1) 시간표 자세히 풀어줘" → `get_itinerary(id=it1)`
- "여수에서 실내 문화 명소 찾아줘" → `search_pois(tag=culture, indoor_outdoor=indoor)`
- "오동도 정보 알려줘" → `search_pois(query=오동도)` → `get_poi(id=…)`
- "여수 들어가는 배편 시간표 알려줘" → `get_transport(mode=ferry)`
- "지금 여수 날씨랑 진행 중인 이벤트 있어?" → `get_region_info()`
**여러 도구 조합 (이 MCP 안에서)**
- "ENFP 커플 여름 여수 코스 추천하고, 1등 코스 시간표까지 보여줘"
→ `recommend_itinerary(mbti=ENFP, party_type=couple, season=summer)` → `get_itinerary(id=…)`
- "비 오는 날 가기 좋은 여수 실내 코스랑 그 안 장소들 상세 정리해줘"
→ `get_region_info()` → `search_pois(indoor_outdoor=indoor)` → `get_poi(...)`
- "차 없이 뚜벅이로 갈 수 있는 시그니처 명소 위주 하루 코스 짜줘"
→ `list_itineraries(transport=walk)` + `search_pois(signature_only=true)` → `get_itinerary(...)`
**PlayMCP(카카오 도구함)와 함께 — 클라이언트 계층 합성**
> 이 MCP + 카카오 서버(맵/톡캘린더/카톡 나에게 보내기)를 같은 도구함에 함께 연결한 경우.
- "INTJ 여수 코스 추천받아서 **톡캘린더에 이번 주 토요일 일정으로** 넣어줘"
→ `recommend_itinerary(mbti=INTJ)` → (카카오) 톡캘린더 등록
- "추천 코스를 **카톡으로 나한테 보내줘**" → `get_itinerary(...)` → (카카오) 나에게 보내기
- "첫 장소를 **카카오맵으로 길찾기** 열어줘" → `get_poi(...)` → (카카오) 지도 길찾기
> 팁: MBTI·계절·이동수단·동행(solo/couple/family/group)을 함께 말하면 더 정확히 매칭된다.
## PlayMCP compliance (dev guide 2026-06-12)
| Requirement | This server |
|---|---|
| Streamable HTTP only | ✅ stateless Streamable HTTP at `POST /mcp` |
| Remote, public URL | ✅ Cloudflare Worker |
| Stateless (no session) | ✅ single request/response, no session id |
| MCP version 2025-03-26 … 2025-11-25 | ✅ `protocolVersion: 2025-03-26` |
| 3–10 tools (≤20) | ✅ 7 |
| `name`/`description`/`inputSchema`/`annotations` present | ✅ |
| all 5 annotation hints set | ✅ |
| English description incl. service name (EN/KO) | ✅ "…from Yeosugaja(여수가자)" |
| no "kakao" in server/tool names | ✅ |
| OAuth only if personal data | ✅ none needed (public, read-only) |
| minimal, cleaned (markdown) results | ✅ |
| p99 ≤ 3s, avg ≤ 100ms | ✅ R2 + memo cache |
| MCP Inspector pre-check | see below |
## Develop
```bash
cd apps/yeosugaja-mcp
npm install
npm run typecheck
npm run dev # local Worker at http://localhost:8787/mcp
```
Local smoke test (JSON-RPC over Streamable HTTP):
```bash
curl -s http://localhost:8787/mcp \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[].name'
curl -s http://localhost:8787/mcp -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"recommend_itinerary","arguments":{"mbti":"INFP"}}}'
```
## MCP Inspector (required before PlayMCP submission)
```bash
npx @modelcontextprotocol/inspector
# Transport: Streamable HTTP URL: http://localhost:8787/mcp (or the deployed URL)
```
Verify `initialize`, `tools/list`, and each `tools/call` succeed.
## Deploy
```bash
npx wrangler login
npm run deploy # → https://yeosugaja-mcp.<subdomain>.workers.dev/mcp
```
Optional, recommended:
- **R2 binding** — uncomment the `r2_buckets` block in `wrangler.jsonc` and set the
real bucket name (same as the API's `R2_BUCKET_NAME`) for edge-local reads. Without
it, the Worker falls back to the public `BUNDLE_PUBLIC_BASE` URL.
- **Custom domain** — uncomment `routes` for a stable registration URL.
## Register on PlayMCP
1. PlayMCP → 개발자 콘솔 → 새로운 MCP 서버 등록.
2. Server URL: the deployed `…/mcp` endpoint.
3. No OAuth (read-only public data) — leave auth unset.
4. Submit for review.
## Notes / limits
- **Read-only over the cache.** Only *verified* itineraries and the *yeosu* region are
in the bundle. Live per-user generation would require calling the API engine and is
intentionally out of scope here.
- **Schema contract.** If the bundle's field names change, update `src/bundle.ts` and
`src/tools.ts`; handlers already guard missing fields defensively.
- **Kakao features** (map, calendar, KakaoTalk share) are composed at the *client*
layer: the user connects this MCP and the relevant PlayMCP tool together and the
agent chains them. This server does not call Kakao services directly.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues