search_products
Search for products on Rakuten Ichiba by keyword, with optional filters for price range, sort order, and pagination.
Instructions
Search for products on Rakuten Ichiba (Japan's largest e-commerce marketplace)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| keyword | Yes | Search keyword (Japanese or English) | |
| hits | No | Number of results (1-30) | |
| page | No | Page number | |
| sort | No | Sort order (prefix + for ascending, - for descending) | standard |
| minPrice | No | Minimum price in yen | |
| maxPrice | No | Maximum price in yen |
Implementation Reference
- src/index.ts:92-163 (registration)The tool 'search_products' is registered with the MCP server via server.tool() call. This is where the tool name, description, Zod schema for inputs, and handler function are all defined together.
server.tool( "search_products", "Search for products on Rakuten Ichiba (Japan's largest e-commerce marketplace)", { keyword: z.string().describe("Search keyword (Japanese or English)"), hits: z .number() .min(1) .max(30) .default(10) .describe("Number of results (1-30)"), page: z.number().min(1).default(1).describe("Page number"), sort: z .enum([ "standard", "+affiliateRate", "-affiliateRate", "+reviewCount", "-reviewCount", "+reviewAverage", "-reviewAverage", "+itemPrice", "-itemPrice", "+updateTimestamp", "-updateTimestamp", ]) .default("standard") .describe("Sort order (prefix + for ascending, - for descending)"), minPrice: z.number().optional().describe("Minimum price in yen"), maxPrice: z.number().optional().describe("Maximum price in yen"), }, async ({ keyword, hits, page, sort, minPrice, maxPrice }) => { const params: Record<string, string> = { keyword, hits: String(hits), page: String(page), sort, }; if (minPrice !== undefined) params.minPrice = String(minPrice); if (maxPrice !== undefined) params.maxPrice = String(maxPrice); const data = (await rakutenRequest( ENDPOINTS.ichibaItemSearch, params )) as { count?: number; Items?: Array<{ Item: Record<string, unknown> }> }; const items = data.Items?.map((i) => ({ name: i.Item.itemName, price: i.Item.itemPrice, url: i.Item.itemUrl, shop: i.Item.shopName, reviewAverage: i.Item.reviewAverage, reviewCount: i.Item.reviewCount, imageUrl: (i.Item.mediumImageUrls as Array<{ imageUrl: string }>)?.[0] ?.imageUrl, })) ?? []; return { content: [ { type: "text", text: JSON.stringify( { totalCount: data.count, items }, null, 2 ), }, ], }; } ); - src/index.ts:123-162 (handler)The handler function for search_products. It builds query parameters from inputs, calls the Rakuten Ichiba Item Search API via rakutenRequest, extracts item data from the response, and returns a formatted JSON result with totalCount and items.
async ({ keyword, hits, page, sort, minPrice, maxPrice }) => { const params: Record<string, string> = { keyword, hits: String(hits), page: String(page), sort, }; if (minPrice !== undefined) params.minPrice = String(minPrice); if (maxPrice !== undefined) params.maxPrice = String(maxPrice); const data = (await rakutenRequest( ENDPOINTS.ichibaItemSearch, params )) as { count?: number; Items?: Array<{ Item: Record<string, unknown> }> }; const items = data.Items?.map((i) => ({ name: i.Item.itemName, price: i.Item.itemPrice, url: i.Item.itemUrl, shop: i.Item.shopName, reviewAverage: i.Item.reviewAverage, reviewCount: i.Item.reviewCount, imageUrl: (i.Item.mediumImageUrls as Array<{ imageUrl: string }>)?.[0] ?.imageUrl, })) ?? []; return { content: [ { type: "text", text: JSON.stringify( { totalCount: data.count, items }, null, 2 ), }, ], }; } - src/index.ts:95-122 (schema)Zod input schema for search_products defining keyword (string), hits (1-30, default 10), page (min 1, default 1), sort (enum with standard/affiliate/price/review/update options, default 'standard'), minPrice (optional number), and maxPrice (optional number).
{ keyword: z.string().describe("Search keyword (Japanese or English)"), hits: z .number() .min(1) .max(30) .default(10) .describe("Number of results (1-30)"), page: z.number().min(1).default(1).describe("Page number"), sort: z .enum([ "standard", "+affiliateRate", "-affiliateRate", "+reviewCount", "-reviewCount", "+reviewAverage", "-reviewAverage", "+itemPrice", "-itemPrice", "+updateTimestamp", "-updateTimestamp", ]) .default("standard") .describe("Sort order (prefix + for ascending, - for descending)"), minPrice: z.number().optional().describe("Minimum price in yen"), maxPrice: z.number().optional().describe("Maximum price in yen"), }, - src/index.ts:49-83 (helper)The rakutenRequest helper function used by the handler to make HTTP requests to the Rakuten API, handling authentication (appId, accessKey), headers (Origin/Referer), and response parsing.
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}`); } }