RESO在线商店MCP
基于AWS Lambda的 模型上下文协议(MCP) 它在 reso在线商店服务器MCP接收自然语言对话,运行OpenAI驱动的代理循环,并将响应流式传输回React UI。
建筑
React UI
│
│ POST /mcp/admin or /mcp/client
▼
CloudFront Distribution (HTTPS, custom domain support, SigV4 signing)
│
│ AWS_IAM signed request
▼
Lambda Function URL (RESPONSE_STREAM — no 29s timeout)
│
│ awslambda.streamifyResponse
▼
Lambda Handler (src/index.ts)
│
├── GET /api/auth/me ──► online-store-server (verify JWT, get user)
│
├── OpenAI agentic loop (run.ts)
│ ├── max 20 iterations
│ ├── max 200,000 cumulative tokens
│ └── tool calls ──► online-store-server REST API
│
└── Streams response back to React UI关键设计决策
- 没有Prisma --通过HTTP调用访问所有数据
online-store-server - 令牌转发 --每个工具调用都会转发用户的JWT+
client-country-code头球 - 身份验证类型AWS_IAM --Lambda函数URL不可公开访问;CloudFront使用源访问控制(OAC)+SigV4对请求进行签名
- 添加剂 —
reso-online-store-server无需此MCP即可独立工作
______________________________________________________________________
先决条件
| 工具 | 版本 | 安装 |
|---|---|---|
| Node.js | 20.x | |
| npm | 10.x+ | 与Node捆绑在一起 |
| TypeScript | 5.x | 包含在devDependencies中 |
| AWS SAM命令行界面 | 1.x | aws.amazon.com/serverless/sam |
| AWS命令行界面 | 2.x | aws.amazon.com/cli |
| Docker | 任何 | 需要 sam local invoke 只有 |
______________________________________________________________________
环境变量
复制 .env.example 到 .env 并填写以下值:
cp .env.example .env| 变量 | 必填 | 描述 |
|---|---|---|
API_SERVER_URL | 是 | 的基本URL reso-online-store-server |
OPENAI_API_KEY | 是 | 您的OpenAI API密钥 |
OPENAI_MODEL | 否 | 要使用的模型(默认值: gpt-4o) |
MAX_ITERATIONS | 否 | 最大代理循环迭代次数(默认值: 20) |
MAX_TOTAL_TOKENS | 否 | 每个请求的最大累积令牌数(默认值: 200000) |
PORT | 否 | 本地Express服务器端口(默认值: 3001) |
在AWS中:OPENAI_API_KEY从以下内容读取 AWS SSM参数存储 路径处的(SecureString)/reso-mcp/openai-api-key。在部署之前设置它: ``bash aws ssm put-parameter \ --name /reso-mcp/openai-api-key \ --value "sk-..." \ --type SecureString``
______________________________________________________________________
1.构建和验证
# Install dependencies
npm install
# Build TypeScript (outputs to dist/)
npm run build
# Check for TypeScript errors without emitting
npx tsc --noEmit如果 npm run build 完成时没有错误,项目设置正确。
______________________________________________________________________
2.使用Express Server进行本地开发
SAM本地做 不 支持Lambda函数URL RESPONSE_STREAM。相反,使用内置的Express开发服务器,它反映了Lambda处理程序的行为。
步骤1:启动在线商店服务器
# In the reso-online-store-server directory
cd ../reso-online-store-server
npm run dev
# Server starts on http://localhost:8080步骤2:启动MCP本地服务器
# In this directory
npm run dev
# MCP server starts on http://localhost:3001或者使用辅助脚本:
./scripts/local.sh第3步:使用curl进行测试
健康检查 (无需身份验证):
curl http://localhost:3001/mcp/health
# { "status": "ok", "timestamp": "..." }管理代理 (需要来自在线商店服务器的有效JWT):
curl -X POST http://localhost:3001/mcp/admin \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "client-country-code: MY" \
-H "Content-Type: application/json" \
-d '{
"conversation": [
{ "role": "user", "content": "How many categories do we have?" }
]
}'客户端代理 (任何经过验证的用户):
curl -X POST http://localhost:3001/mcp/client \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "client-country-code: MY" \
-H "Content-Type: application/json" \
-d '{
"conversation": [
{ "role": "user", "content": "Show me all available products" }
]
}'致电获取JWT POST http://localhost:8080/api/auth/signin 在在线商店服务器上。请求头
| 标题 | 必填 | 描述 |
|---|---|---|
Authorization | 是的 | Bearer 从在线商店服务器登录 |
client-country-code | 是 | ISO国家代码(例如。 MY, SG, US) |
Content-Type | 是的 | application/json |
可选查询参数
| 参数 | 说明 |
|---|---|
currency | 货币代码(例如。 MYR, USD).如果省略,则来源于国家代码。 |
______________________________________________________________________
3.使用SAM本地调用(非流式)进行测试
sam local invoke 直接使用Docker运行Lambda处理程序。不支持流式传输,但它对于测试冷启动和事件解析很有用。
设置 .env.json 对于SAM
创建一个 .env.json 文件(gitignored):
{
"McpFunction": {
"API_SERVER_URL": "http://host.docker.internal:8080",
"OPENAI_API_KEY": "sk-...",
"OPENAI_MODEL": "gpt-4o",
"MAX_ITERATIONS": "20",
"MAX_TOTAL_TOKENS": "200000"
}
}host.docker.internal 从Docker容器(SAM运行的地方)解析到您的Mac主机。使用真实的JWT更新事件文件
编辑 events/admin-event.json 并替换 YOUR_JWT_TOKEN_HERE 一个真正的令牌。
调用
# Build first
npm run build && sam build
# Invoke health check
sam local invoke McpFunction -e events/health-event.json
# Invoke admin agent
sam local invoke McpFunction -e events/admin-event.json --env-vars .env.json
# Invoke client agent
sam local invoke McpFunction -e events/client-event.json --env-vars .env.json______________________________________________________________________
4.部署到AWS
代码所在的基础设施 infra/.CI/CD管道(reso-infra kit)通过CloudFormation部署——管道中不需要SAM CLI。
infra/
├── cert-stack.yml — ACM certificate (deployed in us-east-1)
└── app-stack.yml — Lambda + CloudFront stack步骤1-在SSM中设置OpenAI API密钥
OpenAI密钥作为SecureString存储在SSM参数存储中,并在CloudFormation部署时解析。设置一次:
aws ssm put-parameter \
--name /reso-mcp/openai-api-key \
--value "sk-..." \
--type SecureString \
--region ap-southeast-1第二步——添加GitHub机密
首选 设置→ 秘密与变量→ 行动 在这个repo中添加:
必需
| 机密 | 描述 |
|---|---|
AWS_ACCESS_KEY_ID | github-deployer 访问密钥(来自 reso-infra 独自创立 |
AWS_SECRET_ACCESS_KEY | github-deployer 密钥 |
APP_NAME | reso-online-store-mcp |
DOMAIN_NAME | 自定义域(例如。 mcp.reso-store.com) |
HOSTED_ZONE_ID | 53号公路托管区ID |
API_SERVER_URL | 在线商店服务器的基本URL(例如。 https://api.reso-store.com) |
CLIENT_DOMAIN_URLS | 逗号分隔的允许客户端来源(例如。 https://reso-store.com) |
可选--预先配置合理的默认值
| 机密 | 默认值 | 描述 |
|---|---|---|
AWS_REGION | ap-southeast-1 | 目标AWS区域 |
OPENAI_MODEL | gpt-4o | OpenAI模型 |
MAX_ITERATIONS | 20 | 最大代理循环迭代次数 |
MAX_TOTAL_TOKENS | 200000 | 每个请求的最大累积令牌数 |
MEMORY_SIZE | 512 | Lambda内存(MB) |
TIMEOUT | 900 | Lambda超时(秒) |
OpenAI API密钥 不 需要一个GitHub Secret——它直接从SSM参数存储中读取(/reso-mcp/openai-api-key)在部署时由CloudFormation执行。步骤3——部署
推至 main --管道运行三个作业: ci → infra → deploy.
ci--构建TypeScript,修剪dev-deps,压缩工件infra--将证书堆栈部署到us-east-1(幂等),部署应用程序堆栈(幂等的)deploy—aws lambda update-function-code,等待,使CloudFront无效
或手动触发: GitHub→ 行动→ CI/CD部署→ 运行工作流.
______________________________________________________________________
5.通过AWS Web控制台触发Lambda
- 首选 AWS控制台→ 拉姆达→ reso在线商店mcp prod→ Test
- 创建新的测试事件。使用下面的JSON作为模板。
测试事件:健康检查
{
"rawPath": "/mcp/health",
"requestContext": {
"http": { "method": "GET" }
},
"headers": {},
"queryStringParameters": {},
"body": null
}测试事件:管理代理
{
"rawPath": "/mcp/admin",
"requestContext": {
"http": { "method": "POST" }
},
"headers": {
"authorization": "Bearer YOUR_JWT_TOKEN",
"client-country-code": "MY",
"content-type": "application/json"
},
"queryStringParameters": {
"currency": "MYR"
},
"body": "{\"conversation\":[{\"role\":\"user\",\"content\":\"How many products do we have?\"}]}"
}测试事件:客户端代理
{
"rawPath": "/mcp/client",
"requestContext": {
"http": { "method": "POST" }
},
"headers": {
"authorization": "Bearer YOUR_JWT_TOKEN",
"client-country-code": "MY",
"content-type": "application/json"
},
"queryStringParameters": {},
"body": "{\"conversation\":[{\"role\":\"user\",\"content\":\"Show me all available products\"}]}"
}注意:Lambda控制台不支持流媒体;完整响应被缓冲。对于真正的流媒体,请通过CloudFront或本地Express服务器进行呼叫。
______________________________________________________________________
6.流响应格式
响应是一个分块的文本流。每个逻辑块都被包裹在 `` 标签:
I'm checking the categories now...
{"meta":{"type":"function_call","name":"getCategoriesAdmin","arguments":"{}","call_id":"call_abc","id":"fc_xyz","status":"completed"}}
{"meta":{"type":"function_call_output","call_id":"call_abc","output":"[{\"id\":\"cat1\",\"name\":\"T-Shirts\"}]"}}
I found 3 categories: T-Shirts, Trousers, and Hoodies.
{"meta":{"goalStatus":"SUCCESS","chatStatus":"COMPLETED"}}
{"streamCompletedAt":"2026-03-11T10:00:00.000Z"}块类型:
- 内部文本增量
...--直接流式传输到UI {"meta":{...}}内部堵塞 `` --工具调用和工具结果元数据{"streamCompletedAt":"..."}--最后一块,流完成{"streamError":{"status":500,"message":"..."}}--错误,流终止
______________________________________________________________________
7.启用自定义域
自定义域由CI/CD管道通过以下方式自动处理 DOMAIN_NAME, HOSTED_ZONE_ID,并将证书堆栈部署到 us-east-1.
- 添加
DOMAIN_NAME(例如。mcp.reso-store.com)以及HOSTED_ZONE_ID作为GitHub机密(见上文第4节中的步骤2)。 - 推至
main--管道部署ACM证书(通过路由53验证的DNS),并自动创建指向CloudFront的路由53别名记录。 - 部署后,从堆栈输出中获取实时URL:
aws cloudformation describe-stacks \
--stack-name reso-online-store-mcp-stack \
--query "Stacks[0].Outputs[?OutputKey=='WebsiteUrl'].OutputValue" \
--output text______________________________________________________________________
8.将此项目复制到新的后端
此MCP被设计为可重复使用的模板。要使其适应不同的后端:
第一步:分叉/克隆
git clone https://github.com/your-org/reso-online-store-mcp.git your-new-mcp
cd your-new-mcp第二步:添加 GET /api/auth/me 到您的后端
后端需要一个端点来验证JWT并返回用户对象。它必须返回:
{
"success": true,
"data": {
"id": "...",
"email": "...",
"name": "...",
"isActive": true,
"isVerified": true,
"userType": { "id": "...", "name": "administrator" }
}
}MCP检查 userType.name === 'administrator' 以阻止管理员访问。
步骤3:更新 .env.example 和 .env
API_SERVER_URL=https://your-new-backend.com第四步:更换工具
删除中的现有工具文件 src/lib/ai/tools/ 并创建与后端的REST API匹配的新API:
// src/lib/ai/tools/myResource.tool.ts
import { AiAgentTool, ToolContext } from '../types';
export const getMyResources: AiAgentTool = {
meta: {
type: 'function',
name: 'getMyResources',
description: 'Fetch all resources from the new backend.',
parameters: null,
},
fn: async (_args, ctx: ToolContext) => {
return ctx.serverClient.get('/api/my-resource');
},
};步骤5:更新工具注册表
编辑 src/lib/ai/tools/index.ts 导入并注册您的新工具。
步骤6:更新系统提示
编辑 src/lib/ai/prompts/agent.ts 描述您的新域名。
步骤7:更新 samconfig.toml
[dev.deploy.parameters]
stack_name = "your-new-mcp-dev"
parameter_overrides = [
"Environment=dev",
"ApiServerUrl=https://your-new-backend.com",
...
]步骤8:为OpenAI密钥添加SSM参数
aws ssm put-parameter \
--name /your-project/openai-api-key \
--value "sk-..." \
--type SecureString步骤9:部署
通过GitHub操作进行部署。推到相关分支以触发工作流。
框架(Lambda处理程序、Express本地服务器、run.ts、HTTP客户端、身份验证模块、CloudFront+OAC、GitHub Actions)保持不变。
______________________________________________________________________
代币使用和成本
| 场景 | 典型代币 | 估计成本(GPT-4o) |
|---|---|---|
| 简单查询(1-2次工具调用) | ~15K-30K | ~0.03-0.07美元 |
| 复杂任务(3-5次工具调用) | ~40K-80K | ~0.10-0.20美元 |
| 最大预算(20次迭代) | ~200K | ~0.50-0.75美元 |
令牌使用情况记录到 CloudWatch日志 结构化JSON格式:
{
"type": "token_usage",
"iteration": 2,
"input_tokens": 12500,
"output_tokens": 850,
"total_tokens": 13350,
"cumulative_tokens": 26700
}在以下位置查找日志: 云监控→ 日志组→ /aws/lambda/reso-online-store-mcp-prod (日志组由CloudFormation预先创建,保留30天)
