本项目实现了一个封装了克罗格(Kroger)API 的模型上下文协议(MCP)服务器。它允许像 Anthropic 的 Claude 这样的大语言模型(LLM)与克罗格的杂货服务进行交互,实现产品搜索、店铺查找和购物车管理等功能。
在运行服务器之前,你需要配置克罗格 API 凭证和 OAuth2 设置。
客户端 ID 和 客户端密钥。config.py(推荐用于本地简单使用):
打开 config.py 文件,将 KROGER_CLIENT_ID 和 KROGER_CLIENT_SECRET 的占位符值替换为你实际的凭证。# config.py
KROGER_CLIENT_ID = "YOUR_ACTUAL_CLIENT_ID"
KROGER_CLIENT_SECRET = "YOUR_ACTUAL_CLIENT_SECRET"
- **环境变量**:如果你更喜欢通过环境变量管理机密信息,可以修改 `auth.py` 脚本,使其也能读取 `os.environ.get("KROGER_CLIENT_ID")` 和 `os.environ.get("KROGER_CLIENT_SECRET")`。(注意:`tools.py` 和 `server.py` 当前的实现直接使用 `config.py` 中的值)。
要使用修改用户购物车的工具(例如 add_to_cart),用户必须授权该应用程序。此服务器使用 OAuth2 授权码授予流程。
config.py 中的 KROGER_REDIRECT_URI 与你在克罗格应用程序中注册的重定向 URI 相匹配。对于本地测试,http://localhost:8080/callback 是常见的默认值,但你需要一种方法来捕获此重定向中的代码。# config.py
KROGER_REDIRECT_URI = "http://localhost:8080/callback" # 或你配置的 URI
auth.py 脚本(python auth.py)。KROGER_REDIRECT_URI。浏览器地址栏中的 URL 现在将包含一个授权 代码(例如,http://localhost:8080/callback?code=YOUR_AUTH_CODE&...)。代码。代码 粘贴回 auth.py 脚本中。刷新令牌。刷新令牌 设置在 config.py 中,或者作为 AuthManager 可以加载的环境变量。auth.py 中的 AuthManager.__init__ 以从 config.py 或环境中加载此 KROGER_USER_REFRESH_TOKEN:# 在 auth.py -> AuthManager.__init__ 中
# self.user_refresh_token = os.environ.get("KROGER_USER_REFRESH_TOKEN")
# 或者
# from config import KROGER_USER_REFRESH_TOKEN # 添加到 config.py
# self.user_refresh_token = KROGER_USER_REFRESH_TOKEN
然后在 config.py 中添加 KROGER_USER_REFRESH_TOKEN = "YOUR_SAVED_REFRESH_TOKEN"。
- 当调用 get_user_token() 时,如果访问令牌过期或缺失,它将尝试使用此刷新令牌。
pip install requests mcp
(注意:假设 mcp 库的名称如此;如果不同,请进行调整,例如 modelcontextprotocol)。
2. 启动服务器:
在终端中运行 server.py 脚本:
python server.py
Ctrl+C。设置。集成(或类似的 MCP 服务器部分)。添加 MCP 服务器(或等效操作)。server.py 的路径。例如:
python /path/to/your/project/server.py/path/to/your/python /path/to/your/project/server.pyfind_stores、search_products)。开发者还可以使用 MCP 客户端库以编程方式与服务器进行交互。
# 这是一个基于 MCP 规范的概念示例。
# 实际的库可能会有所不同。
from modelcontext import Client, StdioClientTransport # 假设库结构
async def main():
client = Client(name="example-kroger-client", version="1.0", capabilities={})
# 如果 python/server.py 不在系统路径中或需要完整路径,请调整命令
python_executable = "python" # 或 Python 解释器的完整路径
server_script_path = "server.py" # 或 server.py 的完整路径
transport = StdioClientTransport(command=[python_executable, server_script_path])
await client.connect(transport)
# 示例:查找店铺
try:
store_results = await client.call_tool(
"find_stores",
{"zip_code": "45202", "limit": 1}
)
print("店铺搜索结果:", store_results)
if store_results and not store_results.get("error") and len(store_results) > 0:
location_id = store_results[0].get("locationId")
if location_id:
# 示例:搜索产品
product_results = await client.call_tool(
"search_products",
{"query": "milk", "location_id": "01400123", "limit": 2}
)
print("产品搜索结果:", product_results)
# 示例:添加到购物车(需要在服务器中设置用户认证令牌)
# 确保 product_results[0] 存在且有 'productId'
if product_results and not product_results.get("error") and len(product_results) > 0:
product_id = product_results[0].get("productId")
cart_result = await client.call_tool(
"add_to_cart",
{"product_id": product_id, "quantity": 1, "location_id": location_id}
)
print("添加到购物车结果:", cart_result)
except Exception as e:
print(f"发生错误: {e}")
finally:
await client.disconnect()
if __name__ == "__main__":
# 如果你的客户端库使用 asyncio
# import asyncio
# asyncio.run(main())
print("如果你的 MCP 客户端库需要,请使用 asyncio 事件循环运行异步 main() 函数。")
用户:“我需要两加仑有机全脂牛奶和一打鸡蛋,从 90210 附近的克罗格店购买。”
大语言模型(助手)内部步骤:
find_stores:{"zip_code": "90210", "limit": 1}
[{ "locationId": "01400123", "name": "比佛利山庄克罗格店", ... }]search_products(搜索牛奶):{"query": "organic whole milk", "location_id": "01400123", "limit": 5}
{"productId": "0001111060404", "description": "Simple Truth 有机牛奶...", ...}search_products(搜索鸡蛋):{"query": "dozen eggs", "location_id": "01400123", "limit": 3}
add_to_cart(添加牛奶):{"product_id": "0001111060404", "quantity": 2, "location_id": "01400123"}
add_to_cart(添加鸡蛋):{"product_id": "...", "quantity": 1, "location_id": "01400123"}
大语言模型(助手)回复用户:“好的,我找到了比佛利山庄克罗格店。我已经将两加仑 Simple Truth 有机全脂牛奶和一打鸡蛋添加到你的购物车中。还有其他需求吗?”
add_to_cart,该工具将返回错误:{
"error": "需要用户认证。",
"message": "未找到用户访问令牌。用户需要授权该应用程序...",
"action_needed": "用户必须完成 OAuth2 授权流程。"
}
大语言模型应引导用户执行授权步骤(见第 1.2 节)。错误消息中可能会包含授权 URL。
AuthManager 将尝试刷新它。如果刷新令牌也无效(例如,在 add_to_cart 出现 401 错误后针对用户令牌),则需要重新授权。error、details、status_code(来自克罗格的 HTTP 状态)以及可能的 raw_response 或 kroger_error 字段的 JSON 字典。服务器向大语言模型公开以下工具:
find_stores(zip_code: str, radius_miles: int = 10, limit: int = 5) -> list | dict
search_products(query: str, location_id: str, limit: int = 10) -> list | dict
get_product(product_id: str, location_id: str) -> dict
add_to_cart(product_id: str, quantity: int, location_id: str) -> dict
(以上描述基于 tools.py 中的 @tool 装饰器)。