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 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 has no auth — do not expose publicly without adding authentication
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:** No auth is implemented in this MVP. Do not expose the `/mcp` endpoint publicly without adding authentication. The server is read-only — no cart, order, or account operations are possible.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ikea-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Read-only MCP server for IKEA product search and in-store stock lookup. Supports stdio and Streamable HTTP transports.",
5
5
  "license": "MIT",
6
6
  "type": "module",