内容
# bedrock-search
远程 **MCP(模型上下文协议)服务器**,将 **AWS Bedrock AgentCore** 管理的工具转换为任何MCP客户端(Claude Code、Cline、Cursor、您自己的代理……)都可以使用的干净的、基于令牌认证的HTTP端点:
- **`search`** → 将查询原样转发到AgentCore的 **网页搜索工具** (Amazon自己的网页索引 + 知识图谱),通过AgentCore的 **网关** MCP端点(SigV4 / `AWS_IAM`)访问。
- **`fetch`** → 通过AgentCore的 **浏览器工具** 获取URL的可读内容:一个远程的无头Chrome,通过 **原始Chrome DevTools协议(CDP)** 在SigV4签名的WebSocket上驱动。
- **`browser`** *(可选,默认关闭)* → 完整的带状态的浏览器自动化(导航 / 点击 / 输入 / 截图 / 多标签页 / 网络拦截 / `execute_cdp`)。
> **请求路径中没有LLM。** `search` 和 `fetch` 是纯API/CDP调用,因此它们不消耗任何模型令牌 — 您只需为底层AgentCore使用付费。该服务器是一个薄的、确定性的适配器。
相同的Go代码有两种运行方式:作为 **Kubernetes/EKS** 上的长期服务,或作为 **AWS Lambda** 函数 — 请参见 [部署](#部署).
## 架构
```mermaid
flowchart LR
subgraph client["MCP客户端(代理)"]
A["Claude Code / Cline / Cursor / 自定义代理"]
end
subgraph svc["bedrock-search(此仓库,Go)"]
B["MCP服务器<br/>可流式HTTP /mcp"]
BA["Bearer令牌认证"]
S["搜索引擎"]
F["获取引擎<br/>(内部/cdp + sigv4ws)"]
BR["浏览器引擎<br/>(可选,带状态)"]
end
subgraph aws["AWS Bedrock AgentCore"]
GW["AgentCore网关<br/>(网页搜索连接器)"]
WS["网页搜索工具<br/>(Amazon网页索引 + 知识图谱)"]
BT["浏览器工具<br/>(远程无头Chrome)"]
end
A -- "MCP + 授权:Bearer" --> BA --> B
B --> S -- "SigV4 MCP工具/调用" --> GW --> WS
B --> F -- "SigV4 WS / 原始CDP" --> BT
B -.-> BR -. "SigV4 WS / 原始CDP" .-> BT
```
**`search` 请求流程:**
```mermaid
sequenceDiagram
participant C as MCP客户端
participant S as bedrock-search
participant G as AgentCore网关
participant W as 网页搜索工具
C->>S: tools/call search {query} + Bearer令牌
S->>S: 验证令牌
S->>G: SigV4签名MCP工具/调用(web-search___WebSearch)
G->>W: 调用托管连接器
W-->>G: 结果(标题,网址,摘要,日期)
G-->>S: MCP结果(content[].text JSON)
S-->>C: 原始结果,原样
```
**`fetch` 请求流程:**
```mermaid
sequenceDiagram
participant C as MCP客户端
participant S as bedrock-search
participant B as AgentCore浏览器工具
C->>S: tools/call fetch {url} + Bearer令牌
S->>B: StartBrowserSession
B-->>S: 自动化流(wss://…)
S->>B: SigV4签名WS升级 → 原始CDP
S->>B: Target.createTarget / Page.navigate / Runtime.evaluate
B-->>S: 页面标题 + 可读文本
S->>B: StopBrowserSession
S-->>C: {url, title, text}
```
## 为什么使用自定义后端(而不是直接调用AgentCore)
- **网页搜索工具** 只能作为MCP工具通过 **AgentCore网关** 访问 — 没有公开的 `InvokeWebSearch` API。此服务器提供了一个前端,以便任何MCP客户端都可以使用简单的Bearer令牌。
- **浏览器工具** 的自动化流是 **通过SigV4签名的WebSocket的CDP** 。标准的Go CDP库无法在WS升级时注入SigV4头部,因此此仓库拥有拨号(`internal/sigv4ws`)并直接使用CDP(`internal/cdp`)。浏览器在 **AgentCore中远程运行** — 容器镜像中没有Chromium或Node。
## 仓库布局
```
backend/ Go服务(MCP服务器,引擎,CDP客户端)。参见 backend/README.md。
pkg/app/ 服务器和Lambda入口共用的处理程序构建器。
aws/lambda/ AWS Lambda入口 — 直接重用 backend/pkg/app。
charts/ Helm图表:部署,服务,令牌Secret,HTTPRoute,ServiceAccount(IRSA)。
scripts/ setup_aws.sh(网关),setup_irsa.sh(IAM角色),deploy.sh,mcp.json。
.github/ CI:lint,构建/发布镜像 + 图表,部署到EKS。
```
## 先决条件
- 具有 **Bedrock AgentCore** 可用性的 **AWS账户** 。网页搜索工具和浏览器工具目前 **仅限`us-east-1`** — 即使您在其他地方部署服务,也请将 `*.region` 值设置为 `us-east-1`。
- AWS CLI v2 + `boto3`/`botocore`(了解AgentCore `connector`目标类型的最新版本),`jq`,以及具有创建AgentCore网关 + IAM角色权限的凭证,用于一次性设置。
- 对于Kubernetes路径:具有OIDC提供程序的EKS集群,Envoy网关(或适配 `HTTPRoute`),`kubectl`,`helm`,`docker`。
- 对于Lambda路径:仅需AWS CLI + Go(`go build`)+ `zip`。
## 在您自己的AWS账户中启用
### 步骤 1 — 为 `search` 创建AgentCore网关
网页搜索工具通过具有托管 `web-search` 连接器的AgentCore网关公开。`scripts/setup_aws.sh` 创建网关,其执行角色和连接器目标,然后打印网关的MCP URL:
```bash
AWS_REGION=us-east-1 ./scripts/setup_aws.sh
# ... 打印:
# 网关MCP端点(设置为 search.gatewayUrl):
# https://<您的网关ID>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
```
将该URL复制到 `charts/values.yaml`(`bedrockSearch.search.gatewayUrl`)或作为 `SEARCH_GATEWAY_URL` 环境变量传递给Lambda。
> 浏览器工具(用于 `fetch` / `browser`)**无需设置** — 它使用内置的 `aws.browser.v1` 浏览器。
### 步骤 2 — 生成API令牌
客户端通过 `Authorization: Bearer <token>` 进行身份验证。生成强令牌并将它们排除在源代码控制之外:
```bash
openssl rand -hex 32
```
### 步骤 3 — 部署
选择以下两个路径之一。
## 部署
### 选项 A — Kubernetes / EKS(长期运行,支持带状态的 `browser` 工具)
1. **创建IRSA角色** (授予Pod最小特权AWS凭证 — AgentCore浏览器会话 + `InvokeGateway`,无其他权限):
```bash
AWS_REGION=<集群区域> EKS_CLUSTER=<集群名称> ./scripts/setup_irsa.sh
# -> 打印角色ARN;设置为 charts/values.yaml 中的 bedrockSearch.irsaRoleArn
```
2. **编辑 `charts/values.yaml`**: 设置 `image.repository`、`host`、`search.gatewayUrl`、`irsaRoleArn`,并在部署时提供令牌。
3. **构建、推送和部署**:
```bash
GHCR_OWNER=<您的组织> ./scripts/deploy.sh all \
--set 'bedrockSearch.auth.tokens={REPLACE_TOKEN_1,REPLACE_TOKEN_2}'
```
`deploy.sh all` 构建 arm64 镜像,推送 Helm 图表(OCI),并运行 `helm upgrade`。图表将令牌渲染到 Kubernetes `Secret` 中,并绑定 IRSA ServiceAccount。然后,您的 MCP 端点为 `https://<host>/mcp`。
### 选项 B — AWS Lambda(无服务器;适用于 `search` + `fetch`)
相同的Go代码在Lambda上作为 **Function URL** 运行:
```bash
SEARCH_GATEWAY_URL="https://<您的网关ID>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp" \
AUTH_TOKENS="REPLACE_TOKEN_1,REPLACE_TOKEN_2" \
AWS_REGION=us-east-1 \
./scripts/deploy.sh lambda
# -> 打印Function URL + MCP端点(<url>/mcp)
```
`deploy.sh lambda` 为 `provided.al2023`/arm64 进行交叉编译,压缩 `bootstrap`,创建最小特权执行角色,并配置Function URL。
> **注意:** MCP传输在Lambda上以*无状态*模式运行(每个调用都是独立的),因此多调用 `browser` 工具不适合此处 — 保持 `BROWSER_ENABLED=false`。`search` 和 `fetch` 是无状态的,理想情况下。一些AWS组织通过SCP阻止匿名Function URL;如果这样,请在API Gateway HTTP API(有效载荷v2.0,无代码更改)前面添加函数。
## 从MCP客户端使用服务器
服务器公开裸工具 `search` / `fetch` (和启用时为 `browser`)。MCP客户端 **将配置的服务器密钥前缀添加到工具名称**,因此使用密钥 `web` 时,它们显示为 `web_search` / `web_fetch` / `web_browser`。
`scripts/mcp.json`:
```json
{
"mcpServers": {
"web": {
"type": "http",
"url": "https://<您的主机>/mcp",
"headers": { "Authorization": "Bearer <您的令牌>" }
}
}
}
```
或通过Claude Code CLI:
```bash
claude mcp add --transport http web https://<您的主机>/mcp \
--header "Authorization: Bearer <您的令牌>"
```
### 每个客户端的 `browser` 切换(`?browser=off`)
默认启用 `browser` 工具。特定客户端可以通过在MCP URL中追加 `?browser=off` 来 **为其自己的连接隐藏它** — 无需更改服务器:
```json
{
"mcpServers": {
"web": {
"type": "http",
"url": "https://<您的主机>/mcp?browser=off",
"headers": { "Authorization": "Bearer <您的令牌>" }
}
}
}
```
然后,该客户端只看到 `search` / `fetch`。接受的“关闭”值:`off`、`false`、`0`、`no`、`disable`。(任何其他值或省略参数均按服务器端配置保留 `browser` 工具。)
### 快速的curl检查
```bash
curl -sS -X POST "https://<您的主机>/mcp" \
-H "Authorization: Bearer <您的令牌>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```
## `browser` 工具
默认启用。通过 `bedrockSearch.browser.enabled: false` (图表)/ `browser.enabled: false` (配置)全局禁用,或通过在MCP URL上使用 `?browser=off` (如上)逐连接隐藏。它是一个单一的、按操作切换的工具,基于AgentCore浏览器工具:
| 类别 | 操作 |
|----------|---------|
| 会话 | `init_session`(返回 `sessionId`),`close` |
| 导航 | `navigate`,`refresh`,`back`,`forward` |
| 交互 | `click`,`type`,`press_key`,`evaluate` |
| 提取 | `get_text`,`get_html`,`screenshot`,`get_cookies` |
| 高级 | `set_cookies`,`new_tab`,`switch_tab`,`close_tab`,`list_tabs`,`network_intercept`,`execute_cdp` |
会话由服务器管理(在 `init_session` 后传递 `sessionId`),在空闲时回收(`idleTimeoutSec`),并上限(`maxSessions`)。每个操作都使用会话ID记录,CDP调用在暂存失败时重试一次(CDP本质上可能很脆弱)。由于会话是有状态的,因此此工具适用于 **Kubernetes** 路径,而不适用于Lambda。
## 安全说明
- **永远不要提交真实的Bearer令牌。** `charts/values.yaml` 带有空令牌列表 — 在部署时(`--set`)或通过密封/外部机密提供令牌。`/healthz` 故意公开;当配置了任何令牌时,`/mcp` 始终需要令牌。
- Pod/函数通过 **IRSA / 最小特权执行角色** 获取AWS访问权限 — 镜像中没有静态AWS密钥。
- 将MCP端点视为凭证API:将其放在TLS后面(图表的 `HTTPRoute` 假设TLS在您的网关处终止),并轮换令牌。
## 许可证
请参见 [LICENSE](LICENSE)。