MCP 服务器

MCP 能力由 relay-core-probe(npm 包 @relay-core/mcp)提供,它是一个桥:把 stdin/stdout 上的 MCP 消息转发到守护进程的 /mcp 端点。

连接一个 MCP 客户端不会启动引擎。代理、流量历史、规则与断点队列都归守护进程所有,所以 CLI、编辑器与 Web UI 看到的是同一份状态,而不是各自一份。守护进程自己也能直接服务 HTTP 版 MCP 端点,能配置 remote MCP 的客户端可以完全跳过桥。

给 Agent

容易写错的形状集中在 /llms.txt。命令是 relay-core。没有 relay 命令,也没有 relay-core set_rule 子命令。

  • 连上 MCP 不会启动代理。要调用 proxy_start,或执行 relay-core start。守护进程默认提供 Web UI,relay-core status 会打印地址。用 --no-web 或 [daemon] webui = false 关掉页面。
  • set_rule 的参数是 { "rule": <Rule> }。actions 是 {type, config} 对象组成的数组。写成单个对象会被拒绝,错误里会指出字段(at actions)。只要固定响应时用 mock_url。它在请求头阶段结束交换,不再连接上游,所以响应阶段的规则和 onResponseHeaders / onResponse 不会运行。要加工内容,写进 mock 自己的 body 和 headers。写规则前先读资源 api://rules,里面是每种 filter 和 action。
  • capture_exclude 匹配请求的目标 host:port。控制 API 和 MCP 端口一直排除。代理端口只在代理正在监听它时排除。
  • upstream 含 proxy_url、可选 auth、bypass_hosts、fail_open。bypass_hosts 的每一项是 IP、cidr: 网段或主机名 glob,匹配的主机直接拨号、不经过上游。get_policy 会立刻看到新值。写入后的请求使用新的代理地址、认证、fail_open 或 bypass_hosts。新地址解析失败且 fail_open 为 false 时,继续用上一份连接器。
  • onWebSocketMessage(context, flow, message) 返回消息、"DROP" 或空。WebSocket flow 的 layer 类型是 WebSocket。抛错只给这一条 flow 打标;set_script 会换掉钩子。写脚本前先读资源 api://script。它是钩子、返回值和 flow 的形状。onRequest 和 onResponse 的参数是 (body, flow)。

前置条件

npx @relay-core/cli ca generate
npx @relay-core/cli ca install   # macOS 可自动安装;其他平台见 ca status 提示
npx @relay-core/cli start        # 守护进程 + 代理(首次也可省略,桥会自行拉起守护进程)

配置 MCP 客户端

在 Cursor、Claude Desktop 等 MCP 客户端中:

{
  "mcpServers": {
    "relay-core": {
      "command": "npx",
      "args": ["-y", "@relay-core/mcp"]
    }
  }
}

守护进程未运行时,桥会自己拉起一个(脱离终端,因此关掉编辑器也不受影响);它不会自动启动代理——代理的启停是显式命令:relay-core start / stop,或 MCP 工具 proxy_start / proxy_stop。设 RELAY_MCP_NO_AUTOSTART=1,或在配置文件里写 [client] autostart_daemon = false,可要求必须手动先起守护进程。

桥的参数

参数环境变量默认值说明
—RELAY_DATA_DIR~/.relay-core连接哪个守护进程
--mcp-url <URL>RELAY_MCP_URL自动发现跳过发现,直接使用该 MCP 端点
--token <T>RELAY_MCP_TOKEN取自清单该端点的 Bearer 令牌
--no-autostartRELAY_MCP_NO_AUTOSTART=1允许自动拉起拒绝自行启动守护进程

工具

生命周期

proxy_status 报告代理是否在运行、监听端口、运行时长与失败原因;proxy_start 启动代理(幂等;端口被占返回 start_failed,不会报一个从未 listen 的成功);proxy_stop 停代理但保留守护进程与历史。三者都带只读 / 破坏性 / 幂等注解,且排在工具列表最前。

没有任何代理在运行时,流量类工具返回结构化错误 proxy_not_running(在错误的 data.code 里),而不是一个会被误读为"没有流量"的空列表;代理已停但仍有历史时,历史照常返回。

观察

search_flows

按 host / path / method / 状态码等过滤已捕获流量(最近优先)。

{
  "host": "api.example.com",
  "path_contains": "/v1/",
  "method": "GET",
  "limit": 10
}
get_flow

按 UUID 取完整流量(头、体、时序、标签)。

{
  "id": "b9c84d96-2e49-43fe-9064-58d953e15279"
}
get_metrics

运行时指标:流量总数、内存中条数、待处理断点、规则错误等。

clear_flows

清空已捕获流量与摘要(内存 + 数据库),规则、策略与审计保留。历史为空时再调是幂等空操作。长期运行时,默认 retention(最多 5000 条或 7 天)会在两次 clear 之间自动裁剪。

控制

set_intercept

设置一次性断点;下一条匹配 URL 模式的请求会暂停。

{
  "url_pattern": "api.example.com/login",
  "phase": "request"
}
get_pending_intercepts

列出当前待处理的拦截(HTTP 与 WebSocket)。

resume_flow

恢复暂停的流量。用 request_header_upserts 按名增改头(保留其余);request_headers 会整表替换。

{
  "key": "<flow_id>:request_headers",
  "action": "continue",
  "request_header_upserts": { "X-Debug": "1" }
}

规则

set_rule

创建或替换一条规则。actions 必须是数组。过滤器和动作都是 {type, config};字符串匹配是 {mode, value};文本 body 是 {type: "Text", value}。constraints 可以为 null。字段说明见 规则引擎。

{
  "rule": {
    "id": "r1",
    "name": "r1",
    "active": true,
    "stage": "RequestHeaders",
    "priority": 200,
    "termination": "Stop",
    "filter": {"type": "Url", "config": {"mode": "Contains", "value": "example"}},
    "actions": [{"type": "AddRequestHeader", "config": {"name": "X-Test", "value": "1"}}],
    "constraints": null
  }
}
list_rules

列出当前已加载的规则。

delete_rule

按 id 删除规则。

{
  "id": "probe-mock-…"
}
mock_url

快速创建 MockResponse 规则。它在请求头阶段结束交换,不再连接上游,响应阶段的规则和脚本不会运行。要加工内容,写进这里的 body 和 headers。

{
  "url_pattern": "api.example.com/v1/health",
  "status": 200,
  "body": "{\"ok\":true}",
  "content_type": "application/json"
}

脚本

get_script

读取当前已加载的 Deno 脚本源码(若有)。

set_script

热加载 Deno 脚本。钩子里 return flow 只改头时会继续流式传上游 body;读 body 有预算与超时,避免卡死引擎。详见 脚本 API。

{
  "script": "globalThis.onResponseHeaders = (ctx, flow) => {\n  flow.layer.data.response.headers.push([\"X-Relay\", \"1\"]);\n  return flow;\n};"
}

分析

export_har

以 HAR 1.2 导出一条或多条流量。

{
  "host": "example.com",
  "limit": 50
}
replay_flow

把已捕获的 HTTP 请求从本代理的监听端口再发一次,记成一条新流量。代理没在运行时失败。

{
  "id": "b9c84d96-2e49-43fe-9064-58d953e15279"
}

策略

get_policy

获取当前策略,含 redaction 与 retention。

update_policy

完整替换策略。写入后的请求使用新的代理地址、认证、fail_open 或 bypass_hosts。匹配 bypass_hosts 的主机直接拨号。新地址解析失败且 fail_open 为 false 时,继续用上一份连接器。

patch_policy

局部更新。可写 redaction / upstream / retention;未知字段会拒绝,而不是静默忽略。upstream.bypass_hosts 的每一项是 IP、cidr: 网段或主机名 glob,匹配的主机直接拨号。写入后 get_policy 立刻可见,之后的请求使用新的代理地址、认证、fail_open 或 bypass_hosts。新地址解析失败且 fail_open 为 false 时,继续用上一份连接器。

{
  "patch": {
    "retention": { "max_flows": 2000, "max_age_secs": 86400 },
    "redaction": { "enabled": true }
  }
}

结果形态

每个工具同时返回 structuredContent(Agent 直接读的 typed 字段,形状稳定处还声明了 outputSchema)与同一份 JSON 的文本回退,因此新旧客户端不会对同一次调用得出不同结论。写操作返回 { ok, message, … }:成功是一个字段,而不是一句需要匹配的提示语。search_flows 返回 { count, flows }。工具契约版本为 2。

资源

MCP 还暴露只读资源:

  • flows:// — 最近流量列表
  • rules:// — 当前规则
  • proxy://status — 代理状态与指标
  • audit://recent — 最近审计事件
  • ca://install — CA 安装指引(含默认路径)

订阅(Subscriptions)

服务端声明了资源订阅能力,并且只接受它确实会推送更新的 URI——flows://、 flows://{id}、rules://、proxy://status、 audit://recent,因此订阅成功的客户端一定会收到通知。其它 URI 会被明确拒绝而不是 静默接受:一个永远不会产生更新的订阅,只会让客户端白等。unsubscribe 会被接受; 服务端是广播给所有连接者,不维护按订阅者的状态。

注意脱敏默认开启:除非改动策略,agent 读到的是被掩码的敏感 header 与 query 值 (见 API § 策略与脱敏)。

使用场景

  • AI 驱动的 API 测试和调试
  • 自动化流量分析
  • 基于流量模式的动态规则生成
  • 与 CI/CD 管道集成