ikea-mcp 1.2.0 → 1.2.1
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 +50 -2
- package/dist/http.js +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Read-only MCP server for IKEA product search and in-store stock lookup.
|
|
|
19
19
|
- Uses unofficial public IKEA APIs — no SLA, may break without notice
|
|
20
20
|
- ~65 confirmed US and Canada stores in catalog (`src/data/stores.ts`); some IDs remain unverified
|
|
21
21
|
- Cash-and-carry availability only — click-and-collect and home delivery not exposed
|
|
22
|
-
- HTTP transport
|
|
22
|
+
- HTTP transport is open by default — set `API_KEY` env var to require `x-api-key` header on `/mcp`
|
|
23
23
|
- Read-only — no cart, order, or account operations
|
|
24
24
|
|
|
25
25
|
## Tools
|
|
@@ -194,6 +194,50 @@ Returns `[]` if no store has the item in stock. "All known stores" means the ~65
|
|
|
194
194
|
|
|
195
195
|
---
|
|
196
196
|
|
|
197
|
+
## Example workflows
|
|
198
|
+
|
|
199
|
+
### 1. Search → inspect → check one store
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
1. search_products { "query": "BILLY bookcase" }
|
|
203
|
+
→ pick itemNo from results, e.g. "20522046"
|
|
204
|
+
|
|
205
|
+
2. get_product_details { "itemNo": "20522046" }
|
|
206
|
+
→ confirms name, price, dimensions before checking stock
|
|
207
|
+
|
|
208
|
+
3. check_store_stock { "itemNo": "20522046", "storeId": "399" }
|
|
209
|
+
→ { "availableForCashCarry": true, "quantity": 95, "messageType": "HIGH_IN_STOCK" }
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 2. Shopping list at one store
|
|
213
|
+
|
|
214
|
+
Check whether several items are available in a single trip:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"tool": "check_multi_item_stock",
|
|
219
|
+
"storeId": "399",
|
|
220
|
+
"itemNos": ["20522046", "40477340", "89268919"]
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Returns one entry per item in the same order — items not stocked appear with `availableForCashCarry: false` and a 404 error.
|
|
225
|
+
|
|
226
|
+
### 3. Best store from a mixed US + Canada subset
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"tool": "find_best_store_for_item",
|
|
231
|
+
"itemNo": "20522046",
|
|
232
|
+
"storeIds": ["399", "039", "216", "149", "026"],
|
|
233
|
+
"maxResults": 3
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Returns the top 3 stores by in-stock quantity across the mixed US/Canada subset. Omit `storeIds` to search all ~65 known stores.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
197
241
|
## Build and test
|
|
198
242
|
|
|
199
243
|
```bash
|
|
@@ -244,7 +288,11 @@ Endpoints after deploy:
|
|
|
244
288
|
- `POST /mcp` — MCP Streamable HTTP (requires `Accept: application/json, text/event-stream`)
|
|
245
289
|
- `GET /health` — returns `{"status":"ok"}`
|
|
246
290
|
|
|
247
|
-
> **Security note:**
|
|
291
|
+
> **Security note:** Set `API_KEY` to protect the `/mcp` endpoint. Requests without a matching `x-api-key` header return 401. `/health` is always open. The server is read-only — no cart, order, or account operations are possible.
|
|
292
|
+
>
|
|
293
|
+
> ```bash
|
|
294
|
+
> API_KEY=your-secret node dist/http.js
|
|
295
|
+
> ```
|
|
248
296
|
|
|
249
297
|
## Connecting a local MCP client (stdio)
|
|
250
298
|
|
package/dist/http.js
CHANGED
|
@@ -3,6 +3,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
|
|
|
3
3
|
import { createServer } from "./server.js";
|
|
4
4
|
const PORT = Number(process.env.PORT ?? 3000);
|
|
5
5
|
const BODY_LIMIT = 1024 * 1024; // 1 MB
|
|
6
|
+
const API_KEY = process.env.API_KEY ?? "";
|
|
6
7
|
function readBody(req) {
|
|
7
8
|
return new Promise((resolve, reject) => {
|
|
8
9
|
let raw = "";
|
|
@@ -40,6 +41,10 @@ const httpServer = createHttpServer(async (req, res) => {
|
|
|
40
41
|
sendJson(res, 200, { status: "ok" });
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
44
|
+
if (API_KEY && req.headers["x-api-key"] !== API_KEY) {
|
|
45
|
+
sendJson(res, 401, { error: "unauthorized" });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
43
48
|
if (method === "POST" && url === "/mcp") {
|
|
44
49
|
const body = await readBody(req);
|
|
45
50
|
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
@@ -60,4 +65,5 @@ const httpServer = createHttpServer(async (req, res) => {
|
|
|
60
65
|
httpServer.listen(PORT, () => {
|
|
61
66
|
console.error(`ikea-mcp HTTP listening on http://localhost:${PORT}/mcp`);
|
|
62
67
|
console.error(`health: http://localhost:${PORT}/health`);
|
|
68
|
+
console.error(`auth: ${API_KEY ? "API_KEY required (x-api-key header)" : "open (set API_KEY to enable)"}`);
|
|
63
69
|
});
|
package/package.json
CHANGED