rakuten-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +57 -0
  2. package/dist/index.js +529 -0
  3. package/package.json +36 -0
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @japan-mcp/rakuten
2
+
3
+ MCP server for [Rakuten Web Service APIs](https://webservice.rakuten.co.jp/).
4
+
5
+ Search Japan's largest e-commerce marketplace, plus Rakuten Books and Rakuten Travel.
6
+
7
+ ## Setup
8
+
9
+ 1. Register at [Rakuten Web Service](https://webservice.rakuten.co.jp/) (free)
10
+ 2. Create an application to get an **Application ID** and **Access Key**
11
+ 3. Set the environment variables:
12
+
13
+ ```bash
14
+ export RAKUTEN_APP_ID="your-app-id-here"
15
+ export RAKUTEN_ACCESS_KEY="your-access-key-here"
16
+ ```
17
+
18
+ ## Tools
19
+
20
+ | Tool | Description |
21
+ |------|-------------|
22
+ | `search_products` | Full-text product search with price filters, sorting, and pagination |
23
+ | `get_genre_ranking` | Bestseller rankings (overall or by category) |
24
+ | `search_genres` | Browse product category hierarchy |
25
+ | `search_books` | Search Rakuten Books by title, author, ISBN |
26
+ | `search_travel` | Search hotels on Rakuten Travel by keyword |
27
+ | `search_travel_vacancy` | Search available hotel rooms with date/price/location filters |
28
+ | `get_product_reviews` | Read product reviews with rating and date sorting |
29
+
30
+ ## Usage with Claude Desktop
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "rakuten": {
36
+ "command": "node",
37
+ "args": ["path/to/servers/rakuten-mcp/dist/index.js"],
38
+ "env": {
39
+ "RAKUTEN_APP_ID": "your-app-id-here",
40
+ "RAKUTEN_ACCESS_KEY": "your-access-key-here"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Example Prompts
48
+
49
+ - "Find wireless earphones under 10,000 yen with good reviews"
50
+ - "What are the top sellers on Rakuten right now?"
51
+ - "Search for hotels in Kyoto on Rakuten Travel"
52
+ - "Find available rooms near Tokyo Station for April 15-17 under 15,000 yen"
53
+ - "Find books by Haruki Murakami on Rakuten Books"
54
+
55
+ ## License
56
+
57
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,529 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+ var RAKUTEN_API_BASE = "https://app.rakuten.co.jp/services/api";
8
+ var RAKUTEN_TRAVEL_API_BASE = "https://openapi.rakuten.co.jp/engine/api";
9
+ function getAppId() {
10
+ const appId = process.env.RAKUTEN_APP_ID;
11
+ if (!appId) {
12
+ throw new Error(
13
+ "RAKUTEN_APP_ID environment variable is required. Get one at https://webservice.rakuten.co.jp/"
14
+ );
15
+ }
16
+ return appId;
17
+ }
18
+ function getAccessKey() {
19
+ const key = process.env.RAKUTEN_ACCESS_KEY;
20
+ if (!key) {
21
+ throw new Error(
22
+ "RAKUTEN_ACCESS_KEY environment variable is required. Get one at https://webservice.rakuten.co.jp/"
23
+ );
24
+ }
25
+ return key;
26
+ }
27
+ async function rakutenRequest(endpoint, params = {}, baseUrl = RAKUTEN_API_BASE) {
28
+ const appId = getAppId();
29
+ const accessKey = getAccessKey();
30
+ const searchParams = new URLSearchParams({
31
+ applicationId: appId,
32
+ accessKey,
33
+ format: "json",
34
+ ...params
35
+ });
36
+ const url = `${baseUrl}${endpoint}?${searchParams}`;
37
+ const res = await fetch(url);
38
+ if (!res.ok) {
39
+ const status = res.status;
40
+ await res.text();
41
+ throw new Error(`Rakuten API error (HTTP ${status}) on ${endpoint}`);
42
+ }
43
+ const text = await res.text();
44
+ if (!text) return { success: true };
45
+ try {
46
+ return JSON.parse(text);
47
+ } catch {
48
+ throw new Error(`Rakuten API returned malformed JSON on ${endpoint}`);
49
+ }
50
+ }
51
+ var server = new McpServer({
52
+ name: "rakuten-mcp",
53
+ version: "0.1.0"
54
+ });
55
+ server.tool(
56
+ "search_products",
57
+ "Search for products on Rakuten Ichiba (Japan's largest e-commerce marketplace)",
58
+ {
59
+ keyword: z.string().describe("Search keyword (Japanese or English)"),
60
+ hits: z.number().min(1).max(30).default(10).describe("Number of results (1-30)"),
61
+ page: z.number().min(1).default(1).describe("Page number"),
62
+ sort: z.enum([
63
+ "standard",
64
+ "+affiliateRate",
65
+ "-affiliateRate",
66
+ "+reviewCount",
67
+ "-reviewCount",
68
+ "+reviewAverage",
69
+ "-reviewAverage",
70
+ "+itemPrice",
71
+ "-itemPrice",
72
+ "+updateTimestamp",
73
+ "-updateTimestamp"
74
+ ]).default("standard").describe("Sort order (prefix + for ascending, - for descending)"),
75
+ minPrice: z.number().optional().describe("Minimum price in yen"),
76
+ maxPrice: z.number().optional().describe("Maximum price in yen")
77
+ },
78
+ async ({ keyword, hits, page, sort, minPrice, maxPrice }) => {
79
+ const params = {
80
+ keyword,
81
+ hits: String(hits),
82
+ page: String(page),
83
+ sort
84
+ };
85
+ if (minPrice !== void 0) params.minPrice = String(minPrice);
86
+ if (maxPrice !== void 0) params.maxPrice = String(maxPrice);
87
+ const data = await rakutenRequest(
88
+ "/IchibaItem/Search/20220601",
89
+ params
90
+ );
91
+ const items = data.Items?.map((i) => ({
92
+ name: i.Item.itemName,
93
+ price: i.Item.itemPrice,
94
+ url: i.Item.itemUrl,
95
+ shop: i.Item.shopName,
96
+ reviewAverage: i.Item.reviewAverage,
97
+ reviewCount: i.Item.reviewCount,
98
+ imageUrl: i.Item.mediumImageUrls?.[0]?.imageUrl
99
+ })) ?? [];
100
+ return {
101
+ content: [
102
+ {
103
+ type: "text",
104
+ text: JSON.stringify(
105
+ { totalCount: data.count, items },
106
+ null,
107
+ 2
108
+ )
109
+ }
110
+ ]
111
+ };
112
+ }
113
+ );
114
+ server.tool(
115
+ "get_genre_ranking",
116
+ "Get the Rakuten Ichiba ranking (bestsellers) \u2014 overall or by genre",
117
+ {
118
+ genreId: z.string().default("0").describe("Genre ID (0 for overall ranking)")
119
+ },
120
+ async ({ genreId }) => {
121
+ const data = await rakutenRequest("/IchibaItem/Ranking/20220601", {
122
+ genreId
123
+ });
124
+ const items = data.Items?.map((i) => ({
125
+ rank: i.Item.rank,
126
+ name: i.Item.itemName,
127
+ price: i.Item.itemPrice,
128
+ url: i.Item.itemUrl,
129
+ shop: i.Item.shopName
130
+ })) ?? [];
131
+ return {
132
+ content: [{ type: "text", text: JSON.stringify(items, null, 2) }]
133
+ };
134
+ }
135
+ );
136
+ server.tool(
137
+ "search_genres",
138
+ "Browse Rakuten Ichiba product categories/genres",
139
+ {
140
+ genreId: z.string().default("0").describe("Parent genre ID (0 for top-level)")
141
+ },
142
+ async ({ genreId }) => {
143
+ const data = await rakutenRequest("/IchibaGenre/Search/20140222", {
144
+ genreId
145
+ });
146
+ return {
147
+ content: [
148
+ {
149
+ type: "text",
150
+ text: JSON.stringify(
151
+ {
152
+ current: data.current,
153
+ children: data.children?.map((c) => c.child)
154
+ },
155
+ null,
156
+ 2
157
+ )
158
+ }
159
+ ]
160
+ };
161
+ }
162
+ );
163
+ server.tool(
164
+ "search_books",
165
+ "Search for books on Rakuten Books by title, author, or ISBN. For general keyword searches across all book categories, use keyword (routes to BooksTotal).",
166
+ {
167
+ title: z.string().optional().describe("Book title"),
168
+ author: z.string().optional().describe("Author name"),
169
+ isbn: z.string().optional().describe("ISBN code"),
170
+ keyword: z.string().optional().describe("General keyword (uses cross-category search)"),
171
+ hits: z.number().min(1).max(30).default(10).describe("Number of results")
172
+ },
173
+ async ({ title, author, isbn, keyword, hits }) => {
174
+ if (!title && !author && !isbn && !keyword) {
175
+ return {
176
+ content: [
177
+ {
178
+ type: "text",
179
+ text: "Error: At least one search field is required (title, author, isbn, or keyword)."
180
+ }
181
+ ]
182
+ };
183
+ }
184
+ const params = { hits: String(hits) };
185
+ const useTotal = !title && !author && !isbn && !!keyword;
186
+ if (useTotal) {
187
+ params.keyword = keyword;
188
+ } else {
189
+ if (title) params.title = title;
190
+ if (author) params.author = author;
191
+ if (isbn) params.isbn = isbn;
192
+ }
193
+ const endpoint = useTotal ? "/BooksTotal/Search/20170404" : "/BooksBook/Search/20170404";
194
+ const data = await rakutenRequest(endpoint, params);
195
+ const items = data.Items?.map((i) => ({
196
+ title: i.Item.title,
197
+ author: i.Item.author,
198
+ publisher: i.Item.publisherName,
199
+ price: i.Item.itemPrice,
200
+ isbn: i.Item.isbn,
201
+ url: i.Item.itemUrl,
202
+ imageUrl: i.Item.largeImageUrl,
203
+ salesDate: i.Item.salesDate,
204
+ reviewAverage: i.Item.reviewAverage
205
+ })) ?? [];
206
+ return {
207
+ content: [{ type: "text", text: JSON.stringify(items, null, 2) }]
208
+ };
209
+ }
210
+ );
211
+ server.tool(
212
+ "search_travel",
213
+ "Search for hotels on Rakuten Travel by keyword. For availability/date/price search, use search_travel_vacancy instead.",
214
+ {
215
+ keyword: z.string().describe("Search keyword (e.g., hotel name, area)"),
216
+ hits: z.number().min(1).max(30).default(10).describe("Number of results"),
217
+ page: z.number().min(1).default(1).describe("Page number")
218
+ },
219
+ async ({ keyword, hits, page }) => {
220
+ const data = await rakutenRequest(
221
+ "/Travel/KeywordHotelSearch/20170426",
222
+ { keyword, hits: String(hits), page: String(page) },
223
+ RAKUTEN_TRAVEL_API_BASE
224
+ );
225
+ const hotels = data.hotels?.map((h) => {
226
+ const info = h.hotel[0]?.hotelBasicInfo ?? {};
227
+ return {
228
+ name: info.hotelName,
229
+ address: `${info.address1 ?? ""}${info.address2 ?? ""}`,
230
+ price: info.hotelMinCharge,
231
+ rating: info.reviewAverage,
232
+ url: info.hotelInformationUrl,
233
+ imageUrl: info.hotelImageUrl
234
+ };
235
+ }) ?? [];
236
+ return {
237
+ content: [{ type: "text", text: JSON.stringify(hotels, null, 2) }]
238
+ };
239
+ }
240
+ );
241
+ server.tool(
242
+ "search_travel_vacancy",
243
+ "Search for available hotel rooms on Rakuten Travel by location, date, and price. Requires coordinates (lat/lng) or a hotel number for location.",
244
+ {
245
+ checkinDate: z.string().describe("Check-in date (YYYY-MM-DD)"),
246
+ checkoutDate: z.string().describe("Check-out date (YYYY-MM-DD)"),
247
+ latitude: z.number().optional().describe("Latitude (WGS84 decimal degrees, e.g., 35.6812)"),
248
+ longitude: z.number().optional().describe("Longitude (WGS84 decimal degrees, e.g., 139.7671)"),
249
+ searchRadius: z.number().min(0.1).max(3).optional().describe("Search radius in km (0.1-3, requires lat/lng)"),
250
+ hotelNo: z.number().optional().describe("Specific Rakuten hotel number (alternative to coordinates)"),
251
+ maxCharge: z.number().optional().describe("Maximum price per night in yen"),
252
+ adultNum: z.number().min(1).max(10).default(1).describe("Number of adults (1-10)"),
253
+ hits: z.number().min(1).max(30).default(10).describe("Number of results")
254
+ },
255
+ async ({ checkinDate, checkoutDate, latitude, longitude, searchRadius, hotelNo, maxCharge, adultNum, hits }) => {
256
+ const hasCoords = latitude !== void 0 && longitude !== void 0;
257
+ if (!hasCoords && hotelNo === void 0) {
258
+ return {
259
+ content: [
260
+ {
261
+ type: "text",
262
+ text: "Error: A location is required. Provide either latitude+longitude or hotelNo."
263
+ }
264
+ ]
265
+ };
266
+ }
267
+ if (hasCoords && hotelNo !== void 0) {
268
+ return {
269
+ content: [
270
+ {
271
+ type: "text",
272
+ text: "Error: Provide either latitude+longitude or hotelNo, not both."
273
+ }
274
+ ]
275
+ };
276
+ }
277
+ const params = {
278
+ checkinDate,
279
+ checkoutDate,
280
+ adultNum: String(adultNum),
281
+ hits: String(hits)
282
+ };
283
+ if (hasCoords) {
284
+ params.latitude = String(latitude);
285
+ params.longitude = String(longitude);
286
+ params.datumType = "1";
287
+ }
288
+ if (hasCoords && searchRadius !== void 0) params.searchRadius = String(searchRadius);
289
+ if (hotelNo !== void 0) params.hotelNo = String(hotelNo);
290
+ if (maxCharge !== void 0) params.maxCharge = String(maxCharge);
291
+ const data = await rakutenRequest(
292
+ "/Travel/VacantHotelSearch/20170426",
293
+ params,
294
+ RAKUTEN_TRAVEL_API_BASE
295
+ );
296
+ const hotels = data.hotels?.map((h) => {
297
+ const basic = h.hotel.find((entry) => entry.hotelBasicInfo)?.hotelBasicInfo ?? {};
298
+ const room = h.hotel.find((entry) => entry.roomInfo)?.roomInfo?.[0];
299
+ return {
300
+ name: basic.hotelName,
301
+ address: `${basic.address1 ?? ""}${basic.address2 ?? ""}`,
302
+ price: room?.dailyCharge?.total ?? basic.hotelMinCharge,
303
+ rating: basic.reviewAverage,
304
+ url: basic.hotelInformationUrl,
305
+ imageUrl: basic.hotelImageUrl,
306
+ roomName: room?.roomBasicInfo?.roomName
307
+ };
308
+ }) ?? [];
309
+ return {
310
+ content: [{ type: "text", text: JSON.stringify(hotels, null, 2) }]
311
+ };
312
+ }
313
+ );
314
+ server.tool(
315
+ "get_product_reviews",
316
+ "Get reviews for a specific Rakuten product",
317
+ {
318
+ itemCode: z.string().describe("Rakuten item code (shop:itemId format)"),
319
+ hits: z.number().min(1).max(30).default(10).describe("Number of reviews"),
320
+ sort: z.enum(["+reviewDate", "-reviewDate", "+reviewPoint", "-reviewPoint"]).default("-reviewDate").describe("Sort order")
321
+ },
322
+ async ({ itemCode, hits, sort }) => {
323
+ const data = await rakutenRequest(
324
+ "/IchibaItem/Review/20220601",
325
+ {
326
+ itemCode,
327
+ hits: String(hits),
328
+ sort
329
+ }
330
+ );
331
+ const reviews = data.reviews?.map((r) => ({
332
+ rating: r.review.reviewPoint,
333
+ title: r.review.reviewTitle,
334
+ comment: r.review.reviewComment,
335
+ date: r.review.reviewDate
336
+ })) ?? [];
337
+ return {
338
+ content: [{ type: "text", text: JSON.stringify(reviews, null, 2) }]
339
+ };
340
+ }
341
+ );
342
+ server.prompt(
343
+ "search_products",
344
+ "Search for products on Rakuten Ichiba with optional price filters",
345
+ {
346
+ query: z.string().describe("What to search for"),
347
+ maxPrice: z.string().optional().describe("Maximum price in yen")
348
+ },
349
+ ({ query, maxPrice }) => ({
350
+ messages: [
351
+ {
352
+ role: "user",
353
+ content: {
354
+ type: "text",
355
+ text: maxPrice ? `Search Rakuten for "${query}" under \xA5${maxPrice}` : `Search Rakuten for "${query}"`
356
+ }
357
+ }
358
+ ]
359
+ })
360
+ );
361
+ server.prompt(
362
+ "compare_products",
363
+ "Compare products on Rakuten by searching and sorting by reviews or price",
364
+ {
365
+ query: z.string().describe("Product type to compare"),
366
+ sortBy: z.enum(["reviews", "price_low", "price_high"]).describe("How to sort results")
367
+ },
368
+ ({ query, sortBy }) => {
369
+ const sortMap = { reviews: "-reviewCount", price_low: "+itemPrice", price_high: "-itemPrice" };
370
+ return {
371
+ messages: [
372
+ {
373
+ role: "user",
374
+ content: {
375
+ type: "text",
376
+ text: `Search Rakuten for "${query}" sorted by ${sortBy === "reviews" ? "most reviews" : sortBy === "price_low" ? "lowest price" : "highest price"} and compare the top results`
377
+ }
378
+ }
379
+ ]
380
+ };
381
+ }
382
+ );
383
+ server.prompt(
384
+ "category_bestsellers",
385
+ "Get the current bestseller ranking for a Rakuten product category",
386
+ {
387
+ category: z.string().describe("Product category (e.g., electronics, fashion, food)")
388
+ },
389
+ ({ category }) => ({
390
+ messages: [
391
+ {
392
+ role: "user",
393
+ content: {
394
+ type: "text",
395
+ text: `Show me the current Rakuten bestseller ranking for ${category}`
396
+ }
397
+ }
398
+ ]
399
+ })
400
+ );
401
+ server.prompt(
402
+ "product_reviews",
403
+ "Read reviews for a specific Rakuten product",
404
+ {
405
+ itemCode: z.string().describe("Rakuten item code (shop:itemId format)")
406
+ },
407
+ ({ itemCode }) => ({
408
+ messages: [
409
+ {
410
+ role: "user",
411
+ content: {
412
+ type: "text",
413
+ text: `Get reviews for Rakuten product ${itemCode} and summarize the overall sentiment`
414
+ }
415
+ }
416
+ ]
417
+ })
418
+ );
419
+ server.prompt(
420
+ "find_hotel",
421
+ "Find available hotels on Rakuten Travel for specific dates",
422
+ {
423
+ location: z.string().describe("City or area name"),
424
+ checkin: z.string().describe("Check-in date (YYYY-MM-DD)"),
425
+ checkout: z.string().describe("Check-out date (YYYY-MM-DD)")
426
+ },
427
+ ({ location, checkin, checkout }) => ({
428
+ messages: [
429
+ {
430
+ role: "user",
431
+ content: {
432
+ type: "text",
433
+ text: `Find available hotels in ${location} on Rakuten Travel from ${checkin} to ${checkout}`
434
+ }
435
+ }
436
+ ]
437
+ })
438
+ );
439
+ server.prompt(
440
+ "budget_hotel",
441
+ "Find cheap hotels on Rakuten Travel within a budget",
442
+ {
443
+ location: z.string().describe("City or area name"),
444
+ checkin: z.string().describe("Check-in date (YYYY-MM-DD)"),
445
+ checkout: z.string().describe("Check-out date (YYYY-MM-DD)"),
446
+ maxPrice: z.string().describe("Maximum price per night in yen")
447
+ },
448
+ ({ location, checkin, checkout, maxPrice }) => ({
449
+ messages: [
450
+ {
451
+ role: "user",
452
+ content: {
453
+ type: "text",
454
+ text: `Find hotels in ${location} on Rakuten Travel from ${checkin} to ${checkout} under \xA5${maxPrice} per night`
455
+ }
456
+ }
457
+ ]
458
+ })
459
+ );
460
+ server.prompt(
461
+ "find_book",
462
+ "Search for a book on Rakuten Books",
463
+ {
464
+ query: z.string().describe("Book title, author, or ISBN")
465
+ },
466
+ ({ query }) => ({
467
+ messages: [
468
+ {
469
+ role: "user",
470
+ content: {
471
+ type: "text",
472
+ text: `Search Rakuten Books for "${query}"`
473
+ }
474
+ }
475
+ ]
476
+ })
477
+ );
478
+ server.prompt(
479
+ "books_by_author",
480
+ "Find all books by a specific author on Rakuten Books",
481
+ {
482
+ author: z.string().describe("Author name")
483
+ },
484
+ ({ author }) => ({
485
+ messages: [
486
+ {
487
+ role: "user",
488
+ content: {
489
+ type: "text",
490
+ text: `Find all books by ${author} on Rakuten Books`
491
+ }
492
+ }
493
+ ]
494
+ })
495
+ );
496
+ server.resource(
497
+ "supported-genres",
498
+ "rakuten://genres",
499
+ { description: "Top-level Rakuten Ichiba product categories", mimeType: "application/json" },
500
+ async () => ({
501
+ contents: [
502
+ {
503
+ uri: "rakuten://genres",
504
+ mimeType: "application/json",
505
+ text: JSON.stringify({
506
+ note: "Use search_genres tool with genreId '0' to get the full live category tree. Common top-level genres:",
507
+ genres: [
508
+ { id: "100371", name: "\u30D1\u30BD\u30B3\u30F3\u30FB\u5468\u8FBA\u6A5F\u5668 (Computers)" },
509
+ { id: "100026", name: "\u672C\u30FB\u96D1\u8A8C\u30FB\u30B3\u30DF\u30C3\u30AF (Books)" },
510
+ { id: "100227", name: "\u98DF\u54C1 (Food)" },
511
+ { id: "558885", name: "\u5BB6\u96FB (Electronics)" },
512
+ { id: "100433", name: "\u30D5\u30A1\u30C3\u30B7\u30E7\u30F3 (Fashion)" },
513
+ { id: "101070", name: "\u30A4\u30F3\u30C6\u30EA\u30A2\u30FB\u5BDD\u5177 (Home & Living)" },
514
+ { id: "100533", name: "\u30B9\u30DD\u30FC\u30C4\u30FB\u30A2\u30A6\u30C8\u30C9\u30A2 (Sports)" }
515
+ ]
516
+ }, null, 2)
517
+ }
518
+ ]
519
+ })
520
+ );
521
+ async function main() {
522
+ const transport = new StdioServerTransport();
523
+ await server.connect(transport);
524
+ console.error("Rakuten MCP server running on stdio");
525
+ }
526
+ main().catch((err) => {
527
+ console.error("Fatal error:", err);
528
+ process.exit(1);
529
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "rakuten-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Rakuten APIs — search products, books, hotels, and rankings through AI assistants",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "rakuten-mcp": "./dist/index.js"
9
+ },
10
+ "files": ["dist"],
11
+ "engines": {
12
+ "node": ">=18.0.0"
13
+ },
14
+ "scripts": {
15
+ "build": "tsup --config ../../tsup.config.js",
16
+ "start": "node dist/index.js",
17
+ "dev": "tsx src/index.ts"
18
+ },
19
+ "keywords": ["mcp", "rakuten", "japan", "ecommerce", "ai-agents"],
20
+ "author": "Marsel Bait",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/mrslbt/japan-mcp-servers",
25
+ "directory": "servers/rakuten-mcp"
26
+ },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.12.0",
29
+ "zod": "^3.25.0"
30
+ },
31
+ "devDependencies": {
32
+ "typescript": "^5.7.0",
33
+ "tsx": "^4.0.0",
34
+ "@types/node": "^22.0.0"
35
+ }
36
+ }