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-autostart | RELAY_MCP_NO_AUTOSTART=1 | 允许自动拉起 | 拒绝自行启动守护进程 |
工具
生命周期
proxy_status 报告代理是否在运行、监听端口、运行时长与失败原因;proxy_start 启动代理(幂等;端口被占返回 start_failed,不会报一个从未 listen 的成功);proxy_stop 停代理但保留守护进程与历史。三者都带只读 / 破坏性 / 幂等注解,且排在工具列表最前。
没有任何代理在运行时,流量类工具返回结构化错误 proxy_not_running(在错误的 data.code 里),而不是一个会被误读为"没有流量"的空列表;代理已停但仍有历史时,历史照常返回。
观察
按 host / path / method / 状态码等过滤已捕获流量(最近优先)。
{
"host": "api.example.com",
"path_contains": "/v1/",
"method": "GET",
"limit": 10
} 按 UUID 取完整流量(头、体、时序、标签)。
{
"id": "b9c84d96-2e49-43fe-9064-58d953e15279"
} 运行时指标:流量总数、内存中条数、待处理断点、规则错误等。
清空已捕获流量与摘要(内存 + 数据库),规则、策略与审计保留。历史为空时再调是幂等空操作。长期运行时,默认 retention(最多 5000 条或 7 天)会在两次 clear 之间自动裁剪。
控制
设置一次性断点;下一条匹配 URL 模式的请求会暂停。
{
"url_pattern": "api.example.com/login",
"phase": "request"
} 列出当前待处理的拦截(HTTP 与 WebSocket)。
恢复暂停的流量。用 request_header_upserts 按名增改头(保留其余);request_headers 会整表替换。
{
"key": "<flow_id>:request_headers",
"action": "continue",
"request_header_upserts": { "X-Debug": "1" }
} 规则
创建或替换一条规则。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
}
} 列出当前已加载的规则。
按 id 删除规则。
{
"id": "probe-mock-…"
} 快速创建 MockResponse 规则。它在请求头阶段结束交换,不再连接上游,响应阶段的规则和脚本不会运行。要加工内容,写进这里的 body 和 headers。
{
"url_pattern": "api.example.com/v1/health",
"status": 200,
"body": "{\"ok\":true}",
"content_type": "application/json"
} 脚本
读取当前已加载的 Deno 脚本源码(若有)。
热加载 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};"
} 分析
以 HAR 1.2 导出一条或多条流量。
{
"host": "example.com",
"limit": 50
} 把已捕获的 HTTP 请求从本代理的监听端口再发一次,记成一条新流量。代理没在运行时失败。
{
"id": "b9c84d96-2e49-43fe-9064-58d953e15279"
} 策略
获取当前策略,含 redaction 与 retention。
完整替换策略。写入后的请求使用新的代理地址、认证、fail_open 或 bypass_hosts。匹配 bypass_hosts 的主机直接拨号。新地址解析失败且 fail_open 为 false 时,继续用上一份连接器。
局部更新。可写 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 管道集成