search_travel
Search for hotels on Rakuten Travel by entering a keyword like hotel name or area.
Instructions
Search for hotels on Rakuten Travel by keyword. For availability/date/price search, use search_travel_vacancy instead.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| keyword | Yes | Search keyword (e.g., hotel name, area) | |
| hits | No | Number of results | |
| page | No | Page number |
Implementation Reference
- src/index.ts:299-321 (handler)The async handler function that executes the search_travel tool logic. Calls rakutenRequest to the travelKeywordHotelSearch endpoint, maps hotelBasicInfo from the response into a simplified result array, and returns JSON content.
async ({ keyword, hits, page }) => { const data = (await rakutenRequest( ENDPOINTS.travelKeywordHotelSearch, { keyword, hits: String(hits), page: String(page) } )) as { hotels?: Array<{ hotel: Array<{ hotelBasicInfo: Record<string, unknown> }> }> }; const hotels = data.hotels?.map((h) => { const info = h.hotel[0]?.hotelBasicInfo ?? {}; return { name: info.hotelName, address: `${info.address1 ?? ""}${info.address2 ?? ""}`, price: info.hotelMinCharge, rating: info.reviewAverage, url: info.hotelInformationUrl, imageUrl: info.hotelImageUrl, }; }) ?? []; return { content: [{ type: "text", text: JSON.stringify(hotels, null, 2) }], }; } - src/index.ts:294-298 (schema)Input schema for search_travel using zod: keyword (string, required), hits (number 1-30, default 10), page (number min 1, default 1).
{ keyword: z.string().describe("Search keyword (e.g., hotel name, area)"), hits: z.number().min(1).max(30).default(10).describe("Number of results"), page: z.number().min(1).default(1).describe("Page number"), }, - src/index.ts:291-322 (registration)Registration of the tool named 'search_travel' on the MCP server via server.tool(), with a description string.
server.tool( "search_travel", "Search for hotels on Rakuten Travel by keyword. For availability/date/price search, use search_travel_vacancy instead.", { keyword: z.string().describe("Search keyword (e.g., hotel name, area)"), hits: z.number().min(1).max(30).default(10).describe("Number of results"), page: z.number().min(1).default(1).describe("Page number"), }, async ({ keyword, hits, page }) => { const data = (await rakutenRequest( ENDPOINTS.travelKeywordHotelSearch, { keyword, hits: String(hits), page: String(page) } )) as { hotels?: Array<{ hotel: Array<{ hotelBasicInfo: Record<string, unknown> }> }> }; const hotels = data.hotels?.map((h) => { const info = h.hotel[0]?.hotelBasicInfo ?? {}; return { name: info.hotelName, address: `${info.address1 ?? ""}${info.address2 ?? ""}`, price: info.hotelMinCharge, rating: info.reviewAverage, url: info.hotelInformationUrl, imageUrl: info.hotelImageUrl, }; }) ?? []; return { content: [{ type: "text", text: JSON.stringify(hotels, null, 2) }], }; } ); - src/index.ts:12-20 (helper)The ENDPOINTS constant defining the travelKeywordHotelSearch URL used by the search_travel handler.
const ENDPOINTS = { ichibaItemSearch: "https://openapi.rakuten.co.jp/ichibams/api/IchibaItem/Search/20260401", ichibaItemRanking: "https://openapi.rakuten.co.jp/ichibaranking/api/IchibaItem/Ranking/20220601", ichibaGenreSearch: "https://openapi.rakuten.co.jp/ichibagt/api/IchibaGenre/Search/20170711", booksTotalSearch: "https://openapi.rakuten.co.jp/services/api/BooksTotal/Search/20170404", booksBookSearch: "https://openapi.rakuten.co.jp/services/api/BooksBook/Search/20170404", travelKeywordHotelSearch: "https://openapi.rakuten.co.jp/engine/api/Travel/KeywordHotelSearch/20170426", travelVacantHotelSearch: "https://openapi.rakuten.co.jp/engine/api/Travel/VacantHotelSearch/20170426", } as const; - src/index.ts:49-83 (helper)The rakutenRequest helper function that makes authenticated API calls to Rakuten endpoints, used by the search_travel handler.
async function rakutenRequest( endpointUrl: string, params: Record<string, string> = {} ): Promise<unknown> { const appId = getAppId(); const accessKey = getAccessKey(); const origin = getOrigin(); const searchParams = new URLSearchParams({ applicationId: appId, accessKey, format: "json", ...params, }); const url = `${endpointUrl}?${searchParams}`; const res = await fetch(url, { headers: { Origin: origin, Referer: origin, }, }); if (!res.ok) { const status = res.status; const body = await res.text(); throw new Error(`Rakuten API error (HTTP ${status}) on ${endpointUrl}: ${body.slice(0, 200)}`); } const text = await res.text(); if (!text) return { success: true }; try { return JSON.parse(text); } catch { throw new Error(`Rakuten API returned malformed JSON on ${endpointUrl}`); } }