内容
# ikea-mcp
只读的MCP服务器,用于宜家产品搜索和店内库存查询。
**传输方式:** stdio (Claude Desktop / MCP CLI) · 流式HTTP (远程客户端)
**许可证:** MIT · **本地运行无需身份验证**
## 功能
| 工具 | 功能描述 |
|---|---|
| `list_stores` | 列出已知的商店ID和标签,可选按国家筛选 |
| `search_products` | 按关键词搜索宜家产品 |
| `get_product_details` | 获取单个产品的详细信息,编号 |
| `check_store_stock` | 查询某家商店的库存 |
| `check_multi_item_stock` | 查询多件商品在某家商店的库存 |
| `compare_store_stock` | 比较多家商店的库存或某个国家的目录 |
| `find_best_store_for_item` | 按库存数量排名商店(可选按国家筛选) |
| `check_cart_availability` | 查询购物车中所有商品在某家商店的可用性 |
| `find_best_store_for_cart` | 按购物车履行情况排名商店 |
## 最低可行产品(MVP)限制
- 使用非官方公共宜家API - 无SLA,可能会在无通知的情况下中断
- 加拿大商店覆盖完整(15家商店)
- 美国覆盖不完整 - 4家小格式商店的API ID未知(Queens, Alpharetta, Indianapolis, Arlington)
- 旧金山小格式商店被故意排除(已知ID 3136返回405)
- 不包含额外的商店
- 仅支持现金自提 - 不支持点击收集和送货上门
- 默认情况下HTTP传输是开放的 - 设置`API_KEY`环境变量以在`/mcp`上需要`x-api-key`头部
- 只读 - 无购物车、订单或账户操作
## 工具
### `search_products`
按关键词搜索宜家产品。
**输入**
| 参数 | 类型 | 默认值 | 必需 |
|---|---|---|---|
| `query` | 字符串 | - | 是 |
| `countryCode` | 字符串 | `"US"` | 否 |
| `langCode` | 字符串 | `"en"` | 否 |
| `size` | 数字 | `10` | 否 |
**输出**
```json
{
"total": 97,
"items": [
{
"itemNo": "20522046",
"name": "BILLY",
"typeName": "书架",
"salesPrice": { "amount": 69.99, "currencyCode": "USD" },
"pipUrl": "https://www.ikea.com/us/en/p/...",
"ratingValue": 4.8,
"ratingCount": 1234
}
]
}
```
---
### `get_product_details`
获取单个宜家产品的详细信息,编号。
**输入**
| 参数 | 类型 | 默认值 | 必需 |
|---|---|---|---|
| `itemNo` | 字符串 | - | 是 |
| `countryCode` | 字符串 | `"US"` | 否 |
| `langCode` | 字符串 | `"en"` | 否 |
**输出**
```json
{
"itemNo": "20522046",
"name": "BILLY",
"typeName": "书架",
"salesPrice": { "amount": 79, "currencyCode": "USD" },
"pipUrl": "https://www.ikea.com/us/en/p/billy-bookcase-white-20522046/",
"designText": "白色",
"measureText": "31 1/2x11x79 1/2 \"",
"ratingValue": 4.6,
"ratingCount": 2620
}
```
> `shortDescription` 和 `materials` 不适用于底层API。
---
### `check_store_stock`
查询某家商店的库存。
**输入**
| 参数 | 类型 | 默认值 | 必需 |
|---|---|---|---|
| `itemNo` | 字符串 | - | 是 |
| `storeId` | 字符串 | - | 是 |
| `countryCode` | 字符串 | `"US"` | 否 |
**输出**
```json
{
"storeId": "399",
"availableForCashCarry": true,
"quantity": 110,
"messageType": "HIGH_IN_STOCK",
"errors": null
}
```
在发生错误时(例如,商品未上架):
```json
{
"storeId": "026",
"availableForCashCarry": false,
"quantity": null,
"messageType": null,
"errors": [{ "code": 404, "message": "未找到", "meaning": "商品未在该商店上架" }]
}
```
---
### `compare_store_stock`
比较多家商店的库存或某个国家的目录。提供明确的`storeIds`,或使用`countryCode`扩展到该国的所有目录商店。至少需要`storeIds`或`countryCode`之一。
**输入**
| 参数 | 类型 | 默认值 | 必需 |
|---|---|---|---|
| `itemNo` | 字符串 | - | 是 |
| `storeIds` | 字符串数组(最小2个) | - | `storeIds`/`countryCode`之一 |
| `countryCode` | `"US"` \| `"CA"` | - | `storeIds`/`countryCode`之一 |
| `sortBy` | `"quantity"` \| `"storeId"` | - | 否 |
`storeIds` 优先 - 如果同时提供两者,则`countryCode`仅设置宜家API的本地环境。
`sortBy: "quantity"` 按降序排列,空值数量最后,`storeId`作为打破僵局的手段。`sortBy: "storeId"` 按升序排列。省略`sortBy`保留输入顺序。
**示例**
```json
{ "itemNo": "20522046", "storeIds": ["399", "026", "921"] }
```
```json
{ "itemNo": "20522046", "countryCode": "CA" }
```
**输出** - 数组的形状与`check_store_stock`相同(每个商店一个条目)。
**检测部分故障:** 包含任何代码的`errors`的行(除了`404`)指示商店级或API故障(例如`405` = 无效商店ID)。仅包含`404`错误的行表示商品在该商店无库存 - 这是预期的,而不是故障。
---
### `check_multi_item_stock`
查询多件商品在某家商店的库存。
**输入**
| 参数 | 类型 | 默认值 | 必需 |
|---|---|---|---|
| `storeId` | 字符串 | - | 是 |
| `itemNos` | 字符串数组(最小1个,最大20个) | - | 是 |
**输出** - 数组的形状与`check_store_stock`相同(每个商品一个条目):
```json
[
{
"itemNo": "20522046",
"storeId": "399",
"storeLabel": "399(伯班克,加利福尼亚州)",
"availableForCashCarry": true,
"quantity": 104,
"messageType": "HIGH_IN_STOCK",
"errors": []
}
]
```
未在商店上架的商品以`availableForCashCarry: false`、`quantity: null`和404错误条目显示。无效的`storeId`(405)返回每个条目的错误。
---
### `find_best_store_for_item`
找到具有最高库存数量的商店。并行查询商店,排除无效商店(405),库存不足商店(404)和库存数量未知的商店。结果按数量降序排列;如果数量相同,则按`storeId`字典顺序排列。
**输入**
| 参数 | 类型 | 默认值 | 必需 |
|---|---|---|---|
| `itemNo` | 字符串 | - | 是 |
| `storeIds` | 字符串数组 | 所有已知商店 | 否 |
| `maxResults` | 数字 | `3`(最大50) | 否 |
| `countryCode` | `"US"` \| `"CA"` | - | 否 |
| `minQuantity` | 数字(整数≥1) | - | 否 |
`storeIds` 优先。 如果仅提供`countryCode`,则搜索该国的所有目录商店。 如果两者都不提供,则搜索所有~65个已知商店。`minQuantity` 排除库存数量低于阈值的商店。
**输出** - 数组的形状与`check_store_stock`相同(每个商店一个条目):
```json
[
{
"storeId": "399",
"storeLabel": "399(伯班克,加利福尼亚州)",
"availableForCashCarry": true,
"quantity": 104,
"messageType": "HIGH_IN_STOCK"
}
]
```
如果没有商店有商品库存,则返回空数组。 "所有已知商店" 指`src/data/stores.ts`中的~65个美国和加拿大条目。
**注意失败:** 商店如果返回商店级错误(405无效商店ID),则会静默排除结果,而不是作为行出现。 使用`compare_store_stock`和相同的`storeIds`直接检查每个商店的`errors`。
---
### `check_cart_availability`
查询购物车中所有商品在某家商店的可用性。
**输入**
| 参数 | 类型 | 默认值 | 必需 |
|---|---|---|---|
| `storeId` | 字符串 | - | 是 |
| `items` | `{ itemNo, quantity }`数组 | - | 是 |
| `items[].itemNo` | 字符串 | - | 是 |
| `items[].quantity` | 数字 | `1` | 否 |
**输出**
```json
{
"storeId": "399",
"storeLabel": "399(伯班克,加利福尼亚州)",
"allSufficient": true,
"items": [
{
"itemNo": "20522046",
"quantity": 2,
"inStock": 42,
"sufficient": true,
"eligibleForStockNotification": false,
"errors": []
}
]
}
```
`allSufficient` 仅在每个商品都有`sufficient: true`时为`true`。 未上架的商品显示`inStock: null`和404错误。 无效的`storeId`(405)会传播到所有商品。
---
### `find_best_store_for_cart`
找到购买多件商品的最佳商店。 按购物车履行情况排名,然后按总库存量排名。 可选按`countryCode`筛选或提供明确的`storeIds`。
**输入**
| 参数 | 类型 | 默认值 | 必需 |
|---|---|---|---|
| `items` | `{ itemNo, quantity }`数组 | - | 是 |
| `items[].itemNo` | 字符串 | - | 是 |
| `items[].quantity` | 数字 | `1` | 否 |
| `storeIds` | 字符串数组 | - | 否 |
| `countryCode` | `"US"` \| `"CA"` | - | 否 |
| `maxResults` | 数字 | `3`(最大50) | 否 |
`storeIds` 优先。 如果仅提供`countryCode`,则搜索该国的所有目录商店。 如果两者都不提供,则搜索所有~65个已知商店。
**输出** - 数组的形状与`check_store_stock`相同(每个商店一个条目):
```json
[
{
"storeId": "399",
"storeLabel": "399(伯班克,加利福尼亚州)",
"allSufficient": true,
"fulfilledCount": 3,
"totalCount": 3,
"items": [
{ "itemNo": "20522046", "quantity": 2, "inStock": 42, "sufficient": true },
{ "itemNo": "40477340", "quantity": 1, "inStock": 5, "sufficient": true },
{ "itemNo": "89268919", "quantity": 1, "inStock": 12, "sufficient": true }
]
]
```
`fulfilledCount` = `sufficient: true`的数量。 排序:`fulfilledCount`降序 → 总库存量降序 → `storeId`升序。 无效ID的商店(405)被排除。
---
## 示例工作流
### 1. 搜索 → 检查 → 查询某家商店
```
1. search_products { "query": "BILLY bookcase" }
→ 从结果中选择itemNo,例如"20522046"
2. get_product_details { "itemNo": "20522046" }
→ 确认名称、价格、尺寸后再查询库存
3. check_store_stock { "itemNo": "20522046", "storeId": "399" }
→ { "availableForCashCarry": true, "quantity": 95, "messageType": "HIGH_IN_STOCK" }
```
### 2. 查询某家商店的购物清单
查询多个商品在一次行程中是否可用:
```json
{
"tool": "check_multi_item_stock",
"storeId": "399",
"itemNos": ["20522046", "40477340", "89268919"]
}
```
返回每个商品的条目,顺序与`itemNos`相同 - 未上架的商品显示`availableForCashCarry: false`和404错误。
### 3. 从混合的美国+加拿大子集的最佳商店
```json
{
"tool": "find_best_store_for_item",
"itemNo": "20522046",
"storeIds": ["399", "039", "216", "149", "026"],
"maxResults": 3}
```
返回按库存数量降序排列的前3个商店,涵盖混合的美国/加拿大子集。 省略`storeIds`以搜索所有~65个已知商店。
### 4. 购物车的最佳商店
查找哪些商店可以满足购物车中多个商品:
```json
{
"tool": "find_best_store_for_cart",
"items": [
{ "itemNo": "20522046", "quantity": 2 },
{ "itemNo": "40477340", "quantity": 1 },
{ "itemNo": "89268919", "quantity": 1 }
],
"countryCode": "CA",
"maxResults": 3}
```
返回加拿大排名前3的商店,按购物车履行情况。 使用`check_cart_availability`然后验证确切的数量。
---
## 构建和测试
```bash
npm install
npm run build # tsc → dist/
npm run typecheck # type-check without emit
npm test # unit tests
node smoke.mjs # end-to-end stdio smoke test
```
`smoke.mjs` 对所有4个工具进行实时IKEA API测试,并向stdout输出通过/失败行。
## 传输
**stdio**(默认 - Claude Desktop / MCP CLI):
```bash
npx ikea-mcp # after npm install (uses bin entry)
node dist/index.js # after local build
npm run dev # dev (tsx, no build needed)
```
**流式HTTP**(远程 / 网络客户端):
```bash
node dist/http.js # listens on http://localhost:3000/mcp
PORT=8080 node dist/http.js
# or during dev:
npm run dev:http
```
请求必须包含`Accept: application/json, text/event-stream`。 无状态 - 会话管理。
## 部署(HTTP传输)
测试目标: **Railway**(也适用于Render, Heroku, or any Procfile-aware host)```bash
# 1. build
npm install && npm run dev # tsc → dist/
npm run dev:http
```
请求必须包含`Accept: application/json, text/event-stream`头部 - 如果没有,MCP SDK 将拒绝请求并返回`-32000`错误。
## 商店ID
商店元数据(ID → 城市标签)位于`src/data/stores.ts`。~50家美国商店从`ikea.com/us/en/stores/`页面确认;15家加拿大商店从`ikea.com/ca/en/stores/`页面确认(所有商店均针对库存API进行探测)。
确认兼容的`storeId`格式:
- 标准3位数字:`"399"`(伯班克,加利福尼亚州,美国),`"216"`(卡尔加里,艾伯塔省,加拿大)
- 前导零3位数字:`"026"`(坎顿,密歇根州,美国),`"039"`(蒙特利尔,魁北克省,加拿大)
- 4位数字:`"921"`(布鲁克林,纽约,美国),`"1129"`(锡拉丘兹,纽约,美国)
无效或不支持的`storeId`返回库存API中的`errors`数组中的405错误。
## 限制
- 使用非官方公共宜家API - 无SLA,无身份验证,可能在无通知的情况下中断。
- 只读:无购物车,无订单,无账户操作。
- 全国范围内的扩展(`countryCode: "US"` ≈ 52家商店,`"CA"` ≈ 15家)限制在10个并发请求,并在瞬时5xx/网络错误上重试一次。
- 点击取货和送货上门服务不可用(仅限现金自提)。
- `size`在`search_products`中受宜家API限制(观察到的最大值~24每页;`total`反映整个目录计数)。
- 仅支持美国和加拿大 - 不支持其他国家。
## 商品编号
`itemNo`字段接受几种格式 - 所有格式都标准化为8位数字:
| 输入 | 标准化 |
|---|---|
| `"20522046"` | `"20522046"` |
| `"522132"` | `"00522132"` |
| `"005.221.32"` | `"00522132"` |
| `"5-221-32"` | `"00522132"` |
6位和7位输入左填充为8位。 8位和9位输入保持不变。 除去条纹后输入值在6-9位以外的范围被拒绝。
## 支持的国家
| 国家 | 代码 | 商店数量 |
|---|---|---|
| 美国 | `US` | ~52 |
| 加拿大 | `CA` | ~15 |
使用`list_stores`获取当前目录。 目录中的一些商店ID未经验证 - 可能会列出但返回405库存API。
## 速率限制和可靠性
- 分散请求(全国范围内的`compare_store_stock` / `find_best_store_for_item`)限制在**10个并发**出站请求。
- `fetchJson`在500毫秒后重试**一次** 5xx, 429或网络错误。 404和405不重试(它们是语义响应,而不是瞬时故障)。
- 尊重429响应中的`Retry-After`头部。
- 不要在高频循环中使用 - 宜家API没有公布速率限制,但会阻止重复突发。
## 故障排除
| 症状 | 可能原因 |
|---|---|
| `errors`中的`405` | 无效`storeId` - 使用`list_stores`查找有效ID |
| `errors`中的`404` | 商品未在该商店上架 |
| 空的`find_best_store_for_item`结果 | 没有商店有商品库存,或`minQuantity`太高 |
| 慢速`countryCode`查询 | 正常 - 全国商店分散(限制为10个并发) |
| `itemNo`验证错误 | 输入必须解析为6-9位数字;请参见上面的商品编号 |
---
## 连接本地MCP客户端(stdio)
**Claude Desktop**(`claude_desktop_config.json`):
```json
{
"mcpServers": {
"ikea-mcp": {
"command": "npx",
"args": ["-y", "ikea-mcp"]
}
}
}
```
---
## 连接远程MCP客户端(HTTP)
将您的MCP客户端指向`https://<your-host>/mcp`。
**Claude Desktop**(`claude_desktop_config.json`):
```json
{
"mcpServers": {
"ikea-mcp": {
"type": "http",
"url": "https://<your-host>/mcp"
}
}
}
```
**.mcp.json**(项目本地,Claude Code):
```json
{
"mcpServers": {
"ikea-mcp": {
"type": "http",
"url": "https://<your-host>/mcp"
}
}
}
```
**手动 / curl**(用于调试):
```bash
curl -X POST https://<your-host>/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```
`Accept: application/json, text/event-stream`头部是MCP SDK要求的 - 如果没有该头部,请求将被拒绝并返回`-32000`错误。
## 安全说明
> **安全说明:** 设置`API_KEY`以保护`/mcp`端点。 请求中没有匹配的`x-api-key`头部返回401。 `/health`始终开放。 服务器是只读的 - 没有购物车、订单或帐户操作。
```bash
API_KEY=your-secret node dist/http.js
```
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器