BSN Inventory MCP
Enables identification of BSN SKU/material and real-time inventory availability through natural language queries and vector search.
README
BSN Inventory MCP
Streamable HTTP MCP server for BSN SKU/material identification and inventory availability.
Setup
npm install
cp .env.example .env
Edit .env before running. Keep secrets in .env or the parent shell, not in source files.
Required to start: set at least one inbound MCP bearer key:
BSN_MCP_API_KEY=
BSN_MCP_API_KEY_1=
BSN_MCP_API_KEY_2=
Clients must send:
Authorization: Bearer <local_mcp_api_key>
When the server is hosted behind Amazon Bedrock AgentCore Runtime with AWS_IAM,
SigV4 uses the Authorization header. In that case, configure a custom key
header and allowlist that header in the AgentCore runtime:
BSN_MCP_API_KEY_HEADER=X-Api-Key
BSN_MCP_API_KEY is kept as a legacy slot-1 alias. For key rotation, prefer numbered slots. The server accepts any configured value from BSN_MCP_API_KEY, BSN_MCP_API_KEY_1, or BSN_MCP_API_KEY_2.
Inventory lookup defaults to PROD. These values are required only when calling getInventoryAvailability:
BSN_INVENTORY_API_ENV=prod
BSN_API_BASE_URL_QA=https://api-qa.bsnsports.com:443
BSN_API_BASE_URL_PROD=https://api.bsnsports.com:443
BSN_INVENTORY_API_KEY_QA=
BSN_INVENTORY_API_KEY_PROD=
BSN_INVENTORY_API_KEY=
BSN_INVENTORY_RESPONSE_PAYLOAD_FORMAT=pretty
BSN_INVENTORY_RESPONSE_PAYLOAD_FORMAT supports pretty and json. Use pretty by default because the server normalizes that payload into a complete color/size inventory matrix.
SKU/material identification uses local brand SKU catalogs in resources/skus/*.jsonl first. OpenAI file search is the fallback path when the local catalog has weak, ambiguous, or unavailable results. Set at least one OpenAI key before calling getVectorStoreFiles or before relying on vector fallback from identifySkuMaterial:
OPENAI_API_KEY=
OPENAI_API_KEY_1=
OPENAI_API_KEY_2=
OPENAI_SKU_VECTOR_STORE_IDS=
OPENAI_SKU_BRAND_VECTOR_STORE_IDS=
OPENAI_SKU_SEARCH_MODEL=gpt-5.5
OPENAI_SKU_SEARCH_MAX_OUTPUT_TOKENS=900
OPENAI_SKU_SEARCH_MODEL is optional and defaults to gpt-5.5. Set it in .env to use a different model.
OPENAI_API_KEY is kept as a legacy slot-1 alias. For rotation, prefer OPENAI_API_KEY_1 and OPENAI_API_KEY_2. The server tries configured OpenAI keys in order and only falls back to the next configured key when OpenAI rejects the current key for authentication or authorization.
OPENAI_SKU_VECTOR_STORE_IDS is optional. When set, it scopes broad fallback SKU search and getVectorStoreFiles to those comma-separated vector stores. When unset, vector fallback from identifySkuMaterial discovers accessible completed OpenAI vector stores at runtime, and getVectorStoreFiles lists accessible stores from OpenAI. identifySkuMaterial also accepts per-call vectorStoreIds when a client needs to search a specific store such as SanMar.
OPENAI_SKU_BRAND_VECTOR_STORE_IDS is optional. When set, vector fallback from identifySkuMaterial routes a provided brand to that brand's vector store before broad env or discovery search. Use comma-separated brand=vectorStoreId entries, such as nike=vs_...,adidas=vs_...; use | inside one entry if a brand needs multiple stores. Fallback vector-store precedence is explicit vectorStoreIds, then brand route, then OPENAI_SKU_VECTOR_STORE_IDS, then OpenAI discovery.
The local SanMar JSONL catalog is searched as the distributor/multi-brand fallback. Include the SanMar sanmar_skus vector store as the OpenAI fallback for SanMar-sourced and SM* catalog families before using BSN Shop or web search as a last resort; pass its current vector store ID through identifySkuMaterial.vectorStoreIds when it was not already searched.
Key Rotation
Use numbered key slots to rotate without interrupting users:
- Put the current key in
BSN_MCP_API_KEY_1orOPENAI_API_KEY_1. - Put the new key in
BSN_MCP_API_KEY_2orOPENAI_API_KEY_2. - Update clients or the OpenAI project/provider side to use the new key.
- Remove the old key after traffic has moved.
- Optionally move the new key back to slot 1 before the next rotation.
The unnumbered BSN_MCP_API_KEY and OPENAI_API_KEY variables continue to work for existing installs. Avoid keeping different values in both legacy and numbered slot 1 longer than needed; leave only the active numbered slots after rotation.
Run
npm run dev
Or run compiled output:
npm run build
npm run start
Default MCP endpoint:
http://127.0.0.1:3100/mcp
For client-specific connection examples and hosted-client notes, see MCP_CLIENT_CONNECTIONS.md.
Source Layout
src/main.tsis the process entrypoint and owns startup validation pluslisten().src/http/app.tsowns the Streamable HTTP endpoint, bearer auth boundary, and per-request transport lifecycle.src/mcp/server.tscreates theMcpServerand registers resources, prompts, and config metadata.src/tools/registerInventoryTools.tsregisters the public MCP tools.src/inventory/,src/openai/,src/resources/, andsrc/prompts/contain the domain-specific helpers used by those tools.src/index.tsis import-safe and only re-exports modules for tests or local consumers.
Architecture
flowchart TB
Client["MCP Client"] -->|"POST /mcp<br/>Authorization: Bearer key"| App
subgraph Http["HTTP/Auth Boundary"]
App["Express MCP App<br/>src/http/app.ts"]
Auth["Bearer Auth<br/>src/auth/mcpAuth.ts"]
Reject["Unauthorized response<br/>no transport allocated"]
App --> Auth
Auth -->|"authorized"| Transport["Stateless Streamable HTTP Transport<br/>per request"]
Auth -.->|"missing/invalid bearer key"| Reject
end
subgraph Runtime["Per-request MCP Runtime"]
Server["McpServer<br/>src/mcp/server.ts"]
Tools["Public Tool Registration<br/>src/tools/registerInventoryTools.ts"]
Resources["Context Resources<br/>resources/ via BSN_RESOURCES_DIR"]
Prompts["Local Prompts<br/>prompts/"]
ApiConfig["api-config Resource<br/>runtime metadata"]
Server --> Tools
Server --> Resources
Server --> Prompts
Server --> ApiConfig
end
Transport --> Server
Config["Environment Config<br/>src/config/env.ts"] --> Auth
Config --> Resources
Config --> ApiConfig
Tools --> ReturnShape["MCP Tool Return<br/>concise text + structuredContent"]
Tool Workflow
flowchart TB
Need["User needs SKU/material or inventory"] --> Known{"Exact material ID known?"}
Known -->|"yes"| InventoryTool["getInventoryAvailability"]
Known -->|"no"| IdentifyTool["identifySkuMaterial"]
IdentifyTool --> LocalSku["Local resources/skus JSONL lookup<br/>brand file first, SanMar fallback"]
LocalSku --> LocalCandidate{"Confident local candidate?"}
LocalCandidate -->|"yes"| Candidate
LocalCandidate -->|"no"| SearchScope["Resolve fallback vector scope<br/>explicit vectorStoreIds, brand route,<br/>OPENAI_SKU_VECTOR_STORE_IDS, or discovery"]
SanMar["SanMar sanmar_skus vector store<br/>distributor/multi-brand fallback where available"] -.-> SearchScope
SearchScope --> Responses["OpenAI Responses API<br/>file_search"]
Responses --> Candidate{"Usable SKU/material candidate?"}
Candidate -->|"yes"| InventoryTool
Candidate -->|"no"| LastResort["searchBsnShopLastResort<br/>returns host-agent instructions only"]
LastResort -.-> HostSearch["Host agent searches BSN Shop<br/>outside this MCP server"]
HostSearch --> ExactFound{"Exact material ID found?"}
ExactFound -->|"yes"| InventoryTool
ExactFound -->|"no"| AskUser["Ask user for SKU, material ID,<br/>product link, or clearer details"]
InventoryTool --> BsnApi["BSN Inventory API<br/>/v1/private/inventory-inquiry/{material_id}"]
BsnApi --> Normalize["Inventory Normalization<br/>src/inventory/normalize.ts"]
Normalize --> InventoryReturn["Inventory result<br/>structuredContent source of truth"]
VectorFiles["getVectorStoreFiles"] --> StoreScope["Explicit vectorStoreIds,<br/>OPENAI_SKU_VECTOR_STORE_IDS,<br/>or OpenAI discovery"]
StoreScope --> StoresApi["OpenAI Vector Stores API<br/>metadata only"]
StoresApi --> StoreReturn["Vector store/file metadata<br/>no file contents"]
Dashed edges describe guidance or host-agent activity outside this MCP server process.
Tools
identifySkuMaterialgetVectorStoreFilessearchBsnShopLastResortgetInventoryAvailability
Use identifySkuMaterial when a user provides a natural-language product description, partial SKU, or uncertain style/color/size wording. It searches local brand JSONL catalogs in resources/skus first and returns immediately when the local candidate is confident. If local candidates are weak, ambiguous, or unavailable, it searches explicit vectorStoreIds, brand-routed vector stores, configured OpenAI vector stores, or discovered OpenAI vector stores and returns closest SKU/material candidates with evidence. It does not check live inventory.
Use getVectorStoreFiles to inspect accessible OpenAI vector stores and their attached vector store file metadata. It does not fetch file contents. It uses explicit vectorStoreIds input first, then OPENAI_SKU_VECTOR_STORE_IDS, then OpenAI vector store discovery.
Use searchBsnShopLastResort only when local SKU lookup and explicit, configured, or discovered vector-store search, including the SanMar distributor/multi-brand fallback where relevant, return no usable SKU/material candidates. It does not scrape or browse from the MCP server. It returns instructions for the host agent to search https://www.bsnsports.com/shop/, preserve exact product identifiers from matching products, resolve styles/SKUs to a material ID when needed, and then call getInventoryAvailability only after an exact material ID is found.
Use getInventoryAvailability only after the exact material ID is known. Pass optional color and size when the user asks for a specific variant quantity. It calls:
/v1/private/inventory-inquiry/{material_id}
The text response contains a compact inventory summary and, when color, size, or a full material ID can identify a variant, the matched inventory cell. Matched cells include the split stock levels and combined total when available, for example Matched availability: available 1778, IP 1528, DS 250, total 1778. Agents should report IP and DS separately in addition to the combined total for inventory questions. If the upstream pretty payload only exposes segment-coded IP and DS rows, the normalizer maps them to ip and ds; when explicit available and total fields are absent and both values are numeric, it computes available and total as IP + DS. If the requested text does not match the inventory matrix labels, the matcher also tries color and size codes embedded in full material IDs such as NKCJ1611657LRG. structuredContent.data is the source of truth for normalized product, size, color, inventory-cell fields, and matchedAvailability.
Resources
Inventory-only context resources are exposed under:
context://bsn-inventory-mcp/resources/<relative-path>
Use resources/instructions.md as the entry resource and resources/tool-map.md for the tool workflow. BSN_RESOURCES_DIR controls local context resources and the server-side resources/skus/*.jsonl SKU catalogs. Those SKU catalogs are searched before OpenAI vector stores; large JSONL files are intended for server-side lookup, not whole-file model context.
Secret-free runtime metadata is exposed under:
config://bsn-inventory-mcp
The config resource includes registered operations, prompt metadata, environment variable names, selected inventory environment, logging settings, local SKU catalog metadata, brand vector-store routing metadata, and vector-store discovery settings. It does not include credential values.
Prompts
Only inventory/SKU prompt templates belong in prompts/. check-inventory is the default prompt for inventory lookup.
Checks
npm run check
The combined check runs:
npm run typecheck
npm run typecheck:test
npm test
npm run build
GitHub Actions runs npm ci and npm run check on pushes to main and pull requests.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。