@browserbridge/bbx 1.10.0 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SECURITY.md +14 -2
- package/package.json +8 -10
- package/packages/agent-client/src/cli-proxy-remote.js +7 -4
- package/packages/agent-client/src/command-registry.js +1 -1
- package/packages/agent-client/src/npm-self-update.js +9 -2
- package/packages/agent-client/src/runtime.js +12 -3
- package/packages/agent-client/src/types.ts +2 -0
- package/packages/mcp-server/src/bin.js +1 -1
- package/packages/mcp-server/src/guidance.js +12 -6
- package/packages/mcp-server/src/handlers-utils.js +21 -4
- package/packages/mcp-server/src/handlers.js +10 -4
- package/packages/mcp-server/src/server.js +77 -62
- package/packages/mcp-server/src/toolset.js +25 -0
- package/packages/native-host/bin/postinstall.js +7 -5
- package/packages/native-host/src/auth-token.js +63 -1
- package/packages/native-host/src/daemon.js +148 -45
- package/packages/native-host/src/install-manifest.js +3 -9
- package/packages/native-host/src/native-host.js +3 -3
- package/packages/protocol/src/defaults.js +6 -0
- package/packages/protocol/src/protocol.js +72 -3
- package/packages/protocol/src/types.ts +4 -0
- package/skills/browser-bridge/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -116,7 +116,7 @@ Browser Bridge is optimized for the opposite starting point: **inspect the state
|
|
|
116
116
|
|
|
117
117
|
On Ubuntu, Chromium is commonly installed as a strict snap, and Flatpak Chromium is similarly sandboxed. If native messaging stays disconnected there, use a non-sandboxed Chromium-based browser such as Google Chrome, Brave, or Edge.
|
|
118
118
|
|
|
119
|
-
MCP mode is self-contained: the server exposes tools and startup instructions
|
|
119
|
+
MCP mode is self-contained: the dual-era stdio server supports legacy MCP clients and modern stateless MCP revision `2026-07-28`, and exposes tools and matching startup instructions without requiring a separate CLI skill.
|
|
120
120
|
|
|
121
121
|
## How it works
|
|
122
122
|
|
package/SECURITY.md
CHANGED
|
@@ -52,8 +52,15 @@ Out of scope:
|
|
|
52
52
|
|
|
53
53
|
- By default the daemon listens on a user-owned Unix socket (mode `0700` directory) or,
|
|
54
54
|
on Windows, a named pipe / localhost TCP.
|
|
55
|
-
- Any TCP listener requires
|
|
56
|
-
|
|
55
|
+
- Any TCP listener requires random 256-bit credentials stored with mode `0600`.
|
|
56
|
+
Agents and the Chrome native host use distinct credentials, so a remote agent
|
|
57
|
+
token cannot register an extension-role socket. Registration without the
|
|
58
|
+
role-appropriate credential is rejected and comparison is constant-time.
|
|
59
|
+
- Setup mutation is accepted only from the local socket or authenticated native
|
|
60
|
+
host. A TCP agent remains non-local even when its peer address is loopback,
|
|
61
|
+
preventing SSH port forwarding from turning remote access into local control.
|
|
62
|
+
- Daemon connections, registration time, malformed input, pending requests, and
|
|
63
|
+
per-client pending requests are bounded to limit resource exhaustion.
|
|
57
64
|
- Proxy mode (LAN exposure) is opt-in via `bbx proxy enable` and always provisions a
|
|
58
65
|
token; an invalid `bindHost` in the config rejects the config rather than widening
|
|
59
66
|
the bind address.
|
|
@@ -62,3 +69,8 @@ Out of scope:
|
|
|
62
69
|
- Ordinary Web Storage reads return key/presence metadata only. The separate
|
|
63
70
|
`sensitive.read` method requires one exact key, is excluded from batches and
|
|
64
71
|
automatic retries, and returns a value whole or fails atomically.
|
|
72
|
+
- Fetch/XHR and console capture run in the inspected page's main JavaScript
|
|
73
|
+
world. Results are therefore marked `integrity: "untrusted"`, copied through
|
|
74
|
+
strict bounded schemas, and never treated as daemon or extension authority.
|
|
75
|
+
- Request interception rules are bounded per tab; patterns, bodies, and headers
|
|
76
|
+
have protocol-level size limits and URL matchers are compiled once per rule.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserbridge/bbx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-tools",
|
|
@@ -73,26 +73,24 @@
|
|
|
73
73
|
"install-manifest": "node packages/native-host/bin/install-manifest.js"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@modelcontextprotocol/
|
|
76
|
+
"@modelcontextprotocol/server": "^2.0.0",
|
|
77
77
|
"@mozilla/readability": "^0.6.0",
|
|
78
78
|
"linkedom": "^0.18.13",
|
|
79
79
|
"zod": "^4.4.3"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
|
-
"@
|
|
83
|
-
"@types/
|
|
82
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
83
|
+
"@types/chrome": "^0.2.6",
|
|
84
|
+
"@types/node": "^26.2.0",
|
|
84
85
|
"ajv": "^8.20.0",
|
|
85
86
|
"ajv-formats": "^3.0.1",
|
|
86
87
|
"c8": "^12.0.0",
|
|
87
88
|
"har-schema": "^2.0.0",
|
|
88
|
-
"oxfmt": "^0.
|
|
89
|
-
"oxlint": "^1.
|
|
90
|
-
"tsx": "^4.23.
|
|
89
|
+
"oxfmt": "^0.63.0",
|
|
90
|
+
"oxlint": "^1.78.0",
|
|
91
|
+
"tsx": "^4.23.12",
|
|
91
92
|
"typescript": "^7.0.2"
|
|
92
93
|
},
|
|
93
|
-
"overrides": {
|
|
94
|
-
"@hono/node-server": "2.0.11"
|
|
95
|
-
},
|
|
96
94
|
"engines": {
|
|
97
95
|
"node": ">=20"
|
|
98
96
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
readProxyConfig,
|
|
12
12
|
} from '../../native-host/src/config.js';
|
|
13
13
|
import { pingExistingDaemon } from '../../native-host/src/daemon.js';
|
|
14
|
-
import { restartBridgeDaemon } from '../../native-host/src/daemon-process.js';
|
|
14
|
+
import { restartBridgeDaemon, stopBridgeDaemon } from '../../native-host/src/daemon-process.js';
|
|
15
15
|
import { atomicWriteFile } from './atomic-write.js';
|
|
16
16
|
import { parseIntArg } from './cli-helpers.js';
|
|
17
17
|
import {
|
|
@@ -44,6 +44,8 @@ export async function handleProxyCommand(args) {
|
|
|
44
44
|
randomUUID
|
|
45
45
|
);
|
|
46
46
|
assertProxyBindSafety(existing, options, bindHost);
|
|
47
|
+
// Stop against the current config before changing its port or auth token.
|
|
48
|
+
const stopResult = await stopBridgeDaemon();
|
|
47
49
|
await atomicWriteFile(getBridgeAuthTokenPath(), `${token}\n`, {
|
|
48
50
|
encoding: 'utf8',
|
|
49
51
|
mode: 0o600,
|
|
@@ -75,7 +77,7 @@ export async function handleProxyCommand(args) {
|
|
|
75
77
|
'',
|
|
76
78
|
`Token: ${token}${tokenNote}`,
|
|
77
79
|
`Config: ${configPath}`,
|
|
78
|
-
`Daemon: ${
|
|
80
|
+
`Daemon: ${stopResult.previouslyRunning ? 'restarted' : 'started'} (pid ${result.pid})`,
|
|
79
81
|
];
|
|
80
82
|
if (tokenSource !== 'existing') {
|
|
81
83
|
if (isLoopbackHost(bindHost)) {
|
|
@@ -106,10 +108,11 @@ export async function handleProxyCommand(args) {
|
|
|
106
108
|
|
|
107
109
|
if (subcommand === 'disable') {
|
|
108
110
|
const configPath = getProxyConfigPath();
|
|
111
|
+
const stopResult = await stopBridgeDaemon();
|
|
109
112
|
await fs.promises.rm(configPath, { force: true });
|
|
110
|
-
|
|
113
|
+
await restartBridgeDaemon();
|
|
111
114
|
process.stdout.write(
|
|
112
|
-
`Browser Bridge proxy disabled. Daemon ${
|
|
115
|
+
`Browser Bridge proxy disabled. Daemon ${stopResult.previouslyRunning ? 'restarted' : 'started'}.\n`
|
|
113
116
|
);
|
|
114
117
|
return;
|
|
115
118
|
}
|
|
@@ -267,7 +267,7 @@ export const CLI_HELP_SECTIONS = Object.freeze([
|
|
|
267
267
|
'bbx uninstall Remove native host manifests, Browser Bridge runtime files, and managed MCP/skill installs',
|
|
268
268
|
'bbx install-skill [targets|all] [--global] [--project <path>] Install/update the managed Browser Bridge CLI skill',
|
|
269
269
|
'bbx install-mcp [client|all] [--local] Write MCP config for codex|claude|cursor|copilot|opencode|antigravity|windsurf|agents',
|
|
270
|
-
' MCP
|
|
270
|
+
' MCP uses a compact surface: legacy clients can load typed tools; modern stateless clients use browser_call.',
|
|
271
271
|
'bbx status Check bridge connection',
|
|
272
272
|
'bbx doctor Consolidated local runtime, setup, protocol, and readiness diagnostics',
|
|
273
273
|
'bbx restart Restart the local daemon and running MCP servers',
|
|
@@ -14,6 +14,8 @@ const DEFAULT_LOCK_TIMEOUT_MS = 120_000;
|
|
|
14
14
|
const UPDATE_LOCK_PORT_START = 49_152;
|
|
15
15
|
const UPDATE_LOCK_PORT_COUNT = 16_384;
|
|
16
16
|
const UPDATE_LOCK_PORT_ATTEMPTS = 32;
|
|
17
|
+
const UPDATE_LOCK_PORT_STEP = UPDATE_LOCK_PORT_COUNT / UPDATE_LOCK_PORT_ATTEMPTS;
|
|
18
|
+
const UNAVAILABLE_LOCK_PORT_CODES = new Set(['EACCES', 'EADDRINUSE']);
|
|
17
19
|
|
|
18
20
|
/** @typedef {import('./types.js').NpmUpdateResult} NpmUpdateResult */
|
|
19
21
|
|
|
@@ -113,6 +115,7 @@ export async function runNpmCommand(args, options = {}) {
|
|
|
113
115
|
? process.execPath
|
|
114
116
|
: npmExecPath || (process.platform === 'win32' ? 'npm.cmd' : 'npm');
|
|
115
117
|
const commandArgs = useNode && npmExecPath ? [npmExecPath, ...args] : args;
|
|
118
|
+
const useWindowsShell = process.platform === 'win32' && /\.(?:cmd|bat)$/iu.test(command);
|
|
116
119
|
|
|
117
120
|
return new Promise((resolve, reject) => {
|
|
118
121
|
execFile(
|
|
@@ -124,6 +127,7 @@ export async function runNpmCommand(args, options = {}) {
|
|
|
124
127
|
timeout: options.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS,
|
|
125
128
|
maxBuffer: 4 * 1024 * 1024,
|
|
126
129
|
windowsHide: true,
|
|
130
|
+
shell: useWindowsShell,
|
|
127
131
|
},
|
|
128
132
|
(error, stdout, stderr) => {
|
|
129
133
|
if (!error) {
|
|
@@ -154,7 +158,8 @@ export function getNpmUpdateLockPort(lockKey, offset = 0) {
|
|
|
154
158
|
const digest = createHash('sha256').update(lockKey).digest();
|
|
155
159
|
return (
|
|
156
160
|
UPDATE_LOCK_PORT_START +
|
|
157
|
-
((digest.readUInt32BE(0) + Math.max(0, Math.trunc(offset))) %
|
|
161
|
+
((digest.readUInt32BE(0) + Math.max(0, Math.trunc(offset)) * UPDATE_LOCK_PORT_STEP) %
|
|
162
|
+
UPDATE_LOCK_PORT_COUNT)
|
|
158
163
|
);
|
|
159
164
|
}
|
|
160
165
|
|
|
@@ -200,7 +205,9 @@ export async function acquireNpmUpdateLock(lockKey, timeoutMs = DEFAULT_LOCK_TIM
|
|
|
200
205
|
const server = net.createServer((socket) => socket.end(`${identity}\n`));
|
|
201
206
|
const acquired = await new Promise((resolve, reject) => {
|
|
202
207
|
server.once('error', (error) => {
|
|
203
|
-
if (
|
|
208
|
+
if (
|
|
209
|
+
UNAVAILABLE_LOCK_PORT_CODES.has(/** @type {NodeJS.ErrnoException} */ (error).code ?? '')
|
|
210
|
+
) {
|
|
204
211
|
resolve(false);
|
|
205
212
|
return;
|
|
206
213
|
}
|
|
@@ -106,7 +106,7 @@ export async function ensureClientConnected(client) {
|
|
|
106
106
|
* @param {BridgeClient} client
|
|
107
107
|
* @param {BridgeMethod} method
|
|
108
108
|
* @param {Record<string, unknown>} [params={}]
|
|
109
|
-
* @param {{ tabId?: number | null, source?: BridgeRequestSource, tokenBudget?: number | null, automaticRetry?: 'mcp_second_attempt' }} [options]
|
|
109
|
+
* @param {{ tabId?: number | null, source?: BridgeRequestSource, mcpEra?: import('./types.js').McpProtocolEra, tokenBudget?: number | null, automaticRetry?: 'mcp_second_attempt' }} [options]
|
|
110
110
|
* @returns {Promise<BridgeResponse>}
|
|
111
111
|
*/
|
|
112
112
|
export async function requestBridge(client, method, params = {}, options = {}) {
|
|
@@ -127,7 +127,12 @@ export async function requestBridge(client, method, params = {}, options = {}) {
|
|
|
127
127
|
method,
|
|
128
128
|
params,
|
|
129
129
|
tabId: methodNeedsTab(method) ? (options.tabId ?? null) : null,
|
|
130
|
-
meta: withRequestMeta(
|
|
130
|
+
meta: withRequestMeta(
|
|
131
|
+
options.source,
|
|
132
|
+
options.mcpEra,
|
|
133
|
+
options.tokenBudget,
|
|
134
|
+
options.automaticRetry
|
|
135
|
+
),
|
|
131
136
|
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
132
137
|
});
|
|
133
138
|
}
|
|
@@ -168,16 +173,20 @@ export async function resolveRef(client, refOrSelector, tabId = null, source) {
|
|
|
168
173
|
|
|
169
174
|
/**
|
|
170
175
|
* @param {BridgeRequestSource | undefined} source
|
|
176
|
+
* @param {import('./types.js').McpProtocolEra | undefined} mcpEra
|
|
171
177
|
* @param {number | null | undefined} tokenBudget
|
|
172
178
|
* @param {'mcp_second_attempt' | undefined} automaticRetry
|
|
173
179
|
* @returns {BridgeMeta}
|
|
174
180
|
*/
|
|
175
|
-
function withRequestMeta(source, tokenBudget, automaticRetry) {
|
|
181
|
+
function withRequestMeta(source, mcpEra, tokenBudget, automaticRetry) {
|
|
176
182
|
/** @type {BridgeMeta} */
|
|
177
183
|
const meta = {};
|
|
178
184
|
if (source) {
|
|
179
185
|
meta.source = source;
|
|
180
186
|
}
|
|
187
|
+
if (source === 'mcp' && mcpEra) {
|
|
188
|
+
meta.mcp_era = mcpEra;
|
|
189
|
+
}
|
|
181
190
|
if (typeof tokenBudget === 'number' && Number.isFinite(tokenBudget)) {
|
|
182
191
|
meta.token_budget = tokenBudget;
|
|
183
192
|
}
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
BridgeMethod,
|
|
5
5
|
BridgeRequestSource,
|
|
6
6
|
BridgeResponse,
|
|
7
|
+
McpProtocolEra,
|
|
7
8
|
RecoveryEventKind,
|
|
8
9
|
RecoveryTelemetrySummary,
|
|
9
10
|
ScreenshotResult,
|
|
@@ -17,6 +18,7 @@ export type {
|
|
|
17
18
|
BridgeRequestSource,
|
|
18
19
|
BridgeResponse,
|
|
19
20
|
BridgeTransport,
|
|
21
|
+
McpProtocolEra,
|
|
20
22
|
ScreenshotResult,
|
|
21
23
|
};
|
|
22
24
|
|
|
@@ -9,7 +9,7 @@ import { startBridgeMcpServer } from './server.js';
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @typedef {{
|
|
12
|
-
* start?: () => Promise<void>,
|
|
12
|
+
* start?: () => void | Promise<void>,
|
|
13
13
|
* startControl?: typeof tryStartMcpProcessControl,
|
|
14
14
|
* argv?: string[],
|
|
15
15
|
* stdout?: { write: (chunk: string) => unknown },
|
|
@@ -29,20 +29,26 @@ const WORKFLOW_INSTRUCTIONS = Object.freeze([
|
|
|
29
29
|
const SERVER_HEADER =
|
|
30
30
|
"Browser Bridge MCP inspects and interacts with the user's real Chrome tab through a compact tool set.";
|
|
31
31
|
|
|
32
|
-
const
|
|
32
|
+
const LEGACY_TOOL_RULES = Object.freeze([
|
|
33
33
|
'The common tools are available immediately. When a specialized typed tool is useful, call browser_toolset with its exact tool name; browser_call always reaches every bridge method without loading another tool.',
|
|
34
34
|
'Use browser_call method protocol.describe with method or group params to load unfamiliar signatures cheaply.',
|
|
35
35
|
]);
|
|
36
36
|
|
|
37
|
+
const MODERN_TOOL_RULES = Object.freeze([
|
|
38
|
+
'The compact tool list is static for stateless MCP. browser_call reaches every bridge method, and browser_skill returns runtime groups and limits.',
|
|
39
|
+
'Use browser_call method protocol.describe with method or group params to load unfamiliar signatures cheaply.',
|
|
40
|
+
]);
|
|
41
|
+
|
|
37
42
|
/**
|
|
38
|
-
* Build instructions for
|
|
43
|
+
* Build instructions for the negotiated MCP era.
|
|
39
44
|
*
|
|
45
|
+
* @param {'legacy' | 'modern'} [era='legacy']
|
|
40
46
|
* @returns {string}
|
|
41
47
|
*/
|
|
42
|
-
export function getMcpServerInstructions() {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
);
|
|
48
|
+
export function getMcpServerInstructions(era = 'legacy') {
|
|
49
|
+
const toolRules = era === 'modern' ? MODERN_TOOL_RULES : LEGACY_TOOL_RULES;
|
|
50
|
+
return [SERVER_HEADER, ...toolRules, ...SHARED_INSTRUCTIONS, ...WORKFLOW_INSTRUCTIONS].join('\n');
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
export const MCP_SERVER_INSTRUCTIONS = getMcpServerInstructions();
|
|
54
|
+
export const MODERN_MCP_SERVER_INSTRUCTIONS = getMcpServerInstructions('modern');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
4
|
import { randomUUID } from 'node:crypto';
|
|
4
5
|
|
|
5
6
|
import {
|
|
@@ -46,6 +47,18 @@ import { annotateBridgeSummary, summarizeBridgeResponse } from '../../agent-clie
|
|
|
46
47
|
|
|
47
48
|
export const REQUEST_SOURCE = 'mcp';
|
|
48
49
|
const MCP_CLIENT_ID = `mcp_${randomUUID()}`;
|
|
50
|
+
/** @type {AsyncLocalStorage<import('../../protocol/src/types.js').McpProtocolEra>} */
|
|
51
|
+
const MCP_REQUEST_ERA = new AsyncLocalStorage();
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @template T
|
|
55
|
+
* @param {import('../../protocol/src/types.js').McpProtocolEra} era
|
|
56
|
+
* @param {() => T} callback
|
|
57
|
+
* @returns {T}
|
|
58
|
+
*/
|
|
59
|
+
export function runWithMcpRequestEra(era, callback) {
|
|
60
|
+
return MCP_REQUEST_ERA.run(era, callback);
|
|
61
|
+
}
|
|
49
62
|
|
|
50
63
|
/** @type {ReadonlySet<BridgeMethod>} */
|
|
51
64
|
const RETRY_SAFE_METHODS = new Set([
|
|
@@ -606,14 +619,18 @@ export async function waitForClientReconnect(client, timeoutMs = RECONNECT_WAIT_
|
|
|
606
619
|
* @param {import('../../agent-client/src/client.js').BridgeClient} client
|
|
607
620
|
* @param {BridgeMethod} method
|
|
608
621
|
* @param {Record<string, unknown>} params
|
|
609
|
-
* @param {{ tabId?: number | null, source?: import('../../protocol/src/types.js').BridgeRequestSource, tokenBudget?: number | null, automaticRetry?: 'mcp_second_attempt' }} options
|
|
622
|
+
* @param {{ tabId?: number | null, source?: import('../../protocol/src/types.js').BridgeRequestSource, mcpEra?: import('../../protocol/src/types.js').McpProtocolEra, tokenBudget?: number | null, automaticRetry?: 'mcp_second_attempt' }} options
|
|
610
623
|
* @returns {Promise<BridgeResponse>}
|
|
611
624
|
*/
|
|
612
625
|
export async function requestBridgeWithRetry(client, method, params, options) {
|
|
626
|
+
const requestOptions = {
|
|
627
|
+
...options,
|
|
628
|
+
mcpEra: options.mcpEra ?? MCP_REQUEST_ERA.getStore(),
|
|
629
|
+
};
|
|
613
630
|
/** @type {BridgeResponse} */
|
|
614
631
|
let response;
|
|
615
632
|
try {
|
|
616
|
-
response = await requestBridge(client, method, params,
|
|
633
|
+
response = await requestBridge(client, method, params, requestOptions);
|
|
617
634
|
} catch (error) {
|
|
618
635
|
if (!isConnectionLossError(error) || !isRetrySafeBridgeMethod(method, params)) {
|
|
619
636
|
throw error;
|
|
@@ -625,7 +642,7 @@ export async function requestBridgeWithRetry(client, method, params, options) {
|
|
|
625
642
|
throw error;
|
|
626
643
|
}
|
|
627
644
|
return requestBridge(client, method, params, {
|
|
628
|
-
...
|
|
645
|
+
...requestOptions,
|
|
629
646
|
automaticRetry: 'mcp_second_attempt',
|
|
630
647
|
});
|
|
631
648
|
}
|
|
@@ -640,7 +657,7 @@ export async function requestBridgeWithRetry(client, method, params, options) {
|
|
|
640
657
|
);
|
|
641
658
|
await new Promise((r) => setTimeout(r, delay));
|
|
642
659
|
return requestBridge(client, method, params, {
|
|
643
|
-
...
|
|
660
|
+
...requestOptions,
|
|
644
661
|
automaticRetry: 'mcp_second_attempt',
|
|
645
662
|
});
|
|
646
663
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getToolTokenBudget,
|
|
12
12
|
getBridgeDestinations,
|
|
13
13
|
applyLimitBudgetPreset,
|
|
14
|
+
requestBridgeWithRetry,
|
|
14
15
|
summarizeToolError,
|
|
15
16
|
summarizeToolResponse,
|
|
16
17
|
withToolClient,
|
|
@@ -34,6 +35,7 @@ export {
|
|
|
34
35
|
callBridgeTool,
|
|
35
36
|
dispatchToolAction,
|
|
36
37
|
REQUEST_SOURCE,
|
|
38
|
+
runWithMcpRequestEra,
|
|
37
39
|
} from './handlers-utils.js';
|
|
38
40
|
|
|
39
41
|
export {
|
|
@@ -233,10 +235,14 @@ export async function handleHealthTool(args = {}) {
|
|
|
233
235
|
const destinationId = typeof args.destinationId === 'string' ? args.destinationId : null;
|
|
234
236
|
return withToolClient(
|
|
235
237
|
async (client) => {
|
|
236
|
-
const response = await
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
238
|
+
const response = await requestBridgeWithRetry(
|
|
239
|
+
client,
|
|
240
|
+
'health.ping',
|
|
241
|
+
{},
|
|
242
|
+
{
|
|
243
|
+
source: REQUEST_SOURCE,
|
|
244
|
+
}
|
|
245
|
+
);
|
|
240
246
|
return summarizeToolResponse(response, 'health.ping');
|
|
241
247
|
},
|
|
242
248
|
{ destinationId }
|