ikea-mcp 1.2.1 → 1.3.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.
package/README.md
CHANGED
|
@@ -12,8 +12,8 @@ Read-only MCP server for IKEA product search and in-store stock lookup.
|
|
|
12
12
|
| `get_product_details` | Get details for a single product by item number |
|
|
13
13
|
| `check_store_stock` | Check cash-and-carry stock at one store |
|
|
14
14
|
| `check_multi_item_stock` | Check stock for multiple items at one store |
|
|
15
|
-
| `compare_store_stock` | Compare stock across
|
|
16
|
-
| `find_best_store_for_item` | Rank stores by in-stock quantity |
|
|
15
|
+
| `compare_store_stock` | Compare stock across explicit stores or a country catalog |
|
|
16
|
+
| `find_best_store_for_item` | Rank stores by in-stock quantity (optionally filter by country) |
|
|
17
17
|
|
|
18
18
|
## MVP limitations
|
|
19
19
|
- Uses unofficial public IKEA APIs — no SLA, may break without notice
|
|
@@ -123,14 +123,24 @@ On error (e.g. item not carried):
|
|
|
123
123
|
|
|
124
124
|
### `compare_store_stock`
|
|
125
125
|
|
|
126
|
-
Compare stock for one item across multiple stores.
|
|
126
|
+
Compare stock for one item across multiple stores. Provide explicit `storeIds`, or use `countryCode` to expand to all catalog stores for that country. At least one of `storeIds` or `countryCode` is required.
|
|
127
127
|
|
|
128
128
|
**Input**
|
|
129
129
|
| param | type | default | required |
|
|
130
130
|
|---|---|---|---|
|
|
131
131
|
| `itemNo` | string | — | yes |
|
|
132
|
-
| `storeIds` | string[] (min 2) | — |
|
|
133
|
-
| `countryCode` |
|
|
132
|
+
| `storeIds` | string[] (min 2) | — | one of `storeIds`/`countryCode` |
|
|
133
|
+
| `countryCode` | `"US"` \| `"CA"` | — | one of `storeIds`/`countryCode` |
|
|
134
|
+
|
|
135
|
+
`storeIds` takes precedence — if both are provided, `countryCode` only sets the IKEA API locale.
|
|
136
|
+
|
|
137
|
+
**Examples**
|
|
138
|
+
```json
|
|
139
|
+
{ "itemNo": "20522046", "storeIds": ["399", "026", "921"] }
|
|
140
|
+
```
|
|
141
|
+
```json
|
|
142
|
+
{ "itemNo": "20522046", "countryCode": "CA" }
|
|
143
|
+
```
|
|
134
144
|
|
|
135
145
|
**Output** — array of the same shape as `check_store_stock` (one entry per store).
|
|
136
146
|
|
|
@@ -176,6 +186,9 @@ Find stores with the highest in-stock quantity for an item. Queries stores in pa
|
|
|
176
186
|
| `itemNo` | string | — | yes |
|
|
177
187
|
| `storeIds` | string[] | all known stores | no |
|
|
178
188
|
| `maxResults` | number | `3` (max 10) | no |
|
|
189
|
+
| `countryCode` | `"US"` \| `"CA"` | — | no |
|
|
190
|
+
|
|
191
|
+
`storeIds` takes precedence. If only `countryCode` is given, searches all catalog stores for that country. If neither is given, searches all ~65 known stores.
|
|
179
192
|
|
|
180
193
|
**Output** — array of matching stores, up to `maxResults`:
|
|
181
194
|
```json
|
|
@@ -365,6 +378,6 @@ An invalid or unsupported `storeId` returns a 405 error in the `errors` array.
|
|
|
365
378
|
|
|
366
379
|
- Uses unofficial public IKEA APIs — no SLA, no auth required, may break without notice.
|
|
367
380
|
- Read-only: no cart, no order, no account operations.
|
|
368
|
-
- `compare_store_stock` fires all store requests in parallel — large `storeIds` arrays may hit rate limits.
|
|
381
|
+
- `compare_store_stock` fires all store requests in parallel — large `storeIds` arrays or a full `countryCode` expansion may hit rate limits.
|
|
369
382
|
- Click-and-collect and home-delivery availability are not exposed (cash-and-carry only).
|
|
370
383
|
- `size` in `search_products` is capped by IKEA's API (observed max ~24 per page; `total` reflects the full catalogue count).
|
package/dist/data/stores.js
CHANGED
|
@@ -83,3 +83,11 @@ export const STORE_LABELS = {
|
|
|
83
83
|
export function storeLabel(storeId) {
|
|
84
84
|
return STORE_LABELS[storeId];
|
|
85
85
|
}
|
|
86
|
+
// Canada labels have a province code before the country: "(City, BC, CA)"
|
|
87
|
+
// US state abbreviations like California "(City, CA)" must not match.
|
|
88
|
+
const CA_LABEL_RE = /,\s+[A-Z]{2},\s+CA\)$/;
|
|
89
|
+
export function storeIdsByCountry(countryCode) {
|
|
90
|
+
return Object.entries(STORE_LABELS)
|
|
91
|
+
.filter(([, label]) => countryCode === "CA" ? CA_LABEL_RE.test(label) : !CA_LABEL_RE.test(label))
|
|
92
|
+
.map(([id]) => id);
|
|
93
|
+
}
|
package/dist/schemas/index.js
CHANGED
|
@@ -12,13 +12,14 @@ export const CheckStoreStockInput = z.object({
|
|
|
12
12
|
});
|
|
13
13
|
export const CompareStoreStockInput = z.object({
|
|
14
14
|
itemNo: z.string().min(1),
|
|
15
|
-
storeIds: z.array(z.string().min(1)).min(2).max(10),
|
|
16
|
-
countryCode: z.
|
|
17
|
-
});
|
|
15
|
+
storeIds: z.array(z.string().min(1)).min(2).max(10).optional(),
|
|
16
|
+
countryCode: z.enum(["US", "CA"]).optional(),
|
|
17
|
+
}).refine((d) => d.storeIds !== undefined || d.countryCode !== undefined, { message: "provide storeIds or countryCode" });
|
|
18
18
|
export const FindBestStoreInput = z.object({
|
|
19
19
|
itemNo: z.string().min(1),
|
|
20
20
|
storeIds: z.array(z.string().min(1)).optional(),
|
|
21
21
|
maxResults: z.number().int().min(1).max(10).default(3),
|
|
22
|
+
countryCode: z.enum(["US", "CA"]).optional(),
|
|
22
23
|
});
|
|
23
24
|
export const GetProductDetailsInput = z.object({
|
|
24
25
|
itemNo: z.string().min(1),
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { CompareStoreStockInput } from "../schemas/index.js";
|
|
2
2
|
import { getStoreStock, projectStock } from "../services/ikea.js";
|
|
3
|
-
import { storeLabel } from "../data/stores.js";
|
|
3
|
+
import { storeLabel, storeIdsByCountry } from "../data/stores.js";
|
|
4
4
|
export const compareStoreStockTool = {
|
|
5
5
|
name: "compare_store_stock",
|
|
6
|
-
description: "Compare stock availability for an item across multiple IKEA stores.",
|
|
6
|
+
description: "Compare stock availability for an item across multiple IKEA stores. Provide explicit storeIds, or use countryCode ('US' or 'CA') to compare all catalog stores for that country.",
|
|
7
7
|
inputSchema: {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
10
10
|
itemNo: { type: "string" },
|
|
11
11
|
storeIds: { type: "array", items: { type: "string" }, minItems: 2 },
|
|
12
|
-
countryCode: { type: "string",
|
|
12
|
+
countryCode: { type: "string", enum: ["US", "CA"] },
|
|
13
13
|
},
|
|
14
|
-
required: ["itemNo"
|
|
14
|
+
required: ["itemNo"],
|
|
15
15
|
},
|
|
16
16
|
async handler(rawInput) {
|
|
17
17
|
const input = CompareStoreStockInput.parse(rawInput);
|
|
18
|
-
const
|
|
18
|
+
const targetIds = input.storeIds
|
|
19
|
+
?? storeIdsByCountry(input.countryCode);
|
|
20
|
+
const apiCountry = input.countryCode?.toLowerCase() ?? "us";
|
|
21
|
+
const results = await Promise.all(targetIds.map((storeId) => getStoreStock(input.itemNo, storeId, apiCountry).then((data) => {
|
|
19
22
|
const label = storeLabel(storeId);
|
|
20
23
|
return {
|
|
21
24
|
storeId,
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { FindBestStoreInput } from "../schemas/index.js";
|
|
2
2
|
import { getStoreStock, projectStock } from "../services/ikea.js";
|
|
3
|
-
import { STORE_LABELS, storeLabel } from "../data/stores.js";
|
|
3
|
+
import { STORE_LABELS, storeLabel, storeIdsByCountry } from "../data/stores.js";
|
|
4
4
|
export const findBestStoreForItemTool = {
|
|
5
5
|
name: "find_best_store_for_item",
|
|
6
|
-
description: "Find stores with the highest in-stock quantity for an item. Returns up to maxResults stores sorted by quantity descending.",
|
|
6
|
+
description: "Find stores with the highest in-stock quantity for an item. Returns up to maxResults stores sorted by quantity descending. Optionally filter by countryCode ('US' or 'CA'). Explicit storeIds take precedence over countryCode.",
|
|
7
7
|
inputSchema: {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
10
10
|
itemNo: { type: "string" },
|
|
11
11
|
storeIds: { type: "array", items: { type: "string" } },
|
|
12
12
|
maxResults: { type: "number", default: 3 },
|
|
13
|
+
countryCode: { type: "string", enum: ["US", "CA"] },
|
|
13
14
|
},
|
|
14
15
|
required: ["itemNo"],
|
|
15
16
|
},
|
|
16
17
|
async handler(rawInput) {
|
|
17
18
|
const input = FindBestStoreInput.parse(rawInput);
|
|
18
|
-
const targetIds = input.storeIds
|
|
19
|
+
const targetIds = input.storeIds
|
|
20
|
+
?? (input.countryCode ? storeIdsByCountry(input.countryCode) : Object.keys(STORE_LABELS));
|
|
19
21
|
const results = await Promise.all(targetIds.map(async (storeId) => {
|
|
20
22
|
const data = await getStoreStock(input.itemNo, storeId, "us");
|
|
21
23
|
const stock = projectStock(data);
|
package/package.json
CHANGED