mcp-chrome-bridge
Version:
Chrome Native-Messaging host (Node)
145 lines • 6.29 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupTools = void 0;
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const native_messaging_host_1 = __importDefault(require("../native-messaging-host"));
const chrome_mcp_shared_1 = require("chrome-mcp-shared");
async function listDynamicFlowTools() {
try {
const response = await native_messaging_host_1.default.sendRequestToExtensionAndWait({}, 'rr_list_published_flows', 20000);
if (response && response.status === 'success' && Array.isArray(response.items)) {
const tools = [];
for (const item of response.items) {
const name = `flow.${item.slug}`;
const description = (item.meta && item.meta.tool && item.meta.tool.description) ||
item.description ||
'Recorded flow';
const properties = {};
const required = [];
for (const v of item.variables || []) {
const desc = v.label || v.key;
const typ = (v.type || 'string').toLowerCase();
const prop = { description: desc };
if (typ === 'boolean')
prop.type = 'boolean';
else if (typ === 'number')
prop.type = 'number';
else if (typ === 'enum') {
prop.type = 'string';
if (v.rules && Array.isArray(v.rules.enum))
prop.enum = v.rules.enum;
}
else if (typ === 'array') {
// default array of strings; can extend with itemType later
prop.type = 'array';
prop.items = { type: 'string' };
}
else {
prop.type = 'string';
}
if (v.default !== undefined)
prop.default = v.default;
if (v.rules && v.rules.required)
required.push(v.key);
properties[v.key] = prop;
}
// Run options
properties['tabTarget'] = { type: 'string', enum: ['current', 'new'], default: 'current' };
properties['refresh'] = { type: 'boolean', default: false };
properties['captureNetwork'] = { type: 'boolean', default: false };
properties['returnLogs'] = { type: 'boolean', default: false };
properties['timeoutMs'] = { type: 'number', minimum: 0 };
const tool = {
name,
description,
inputSchema: { type: 'object', properties, required },
};
tools.push(tool);
}
return tools;
}
return [];
}
catch (e) {
return [];
}
}
const setupTools = (server) => {
// List tools handler
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
const dynamicTools = await listDynamicFlowTools();
return { tools: [...chrome_mcp_shared_1.TOOL_SCHEMAS, ...dynamicTools] };
});
// Call tool handler
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => handleToolCall(request.params.name, request.params.arguments || {}));
};
exports.setupTools = setupTools;
const handleToolCall = async (name, args) => {
try {
// If calling a dynamic flow tool (name starts with flow.), proxy to common flow-run tool
if (name && name.startsWith('flow.')) {
// We need to resolve flow by slug to ID
try {
const resp = await native_messaging_host_1.default.sendRequestToExtensionAndWait({}, 'rr_list_published_flows', 20000);
const items = (resp && resp.items) || [];
const slug = name.slice('flow.'.length);
const match = items.find((it) => it.slug === slug);
if (!match)
throw new Error(`Flow not found for tool ${name}`);
const flowArgs = { flowId: match.id, args };
const proxyRes = await native_messaging_host_1.default.sendRequestToExtensionAndWait({ name: 'record_replay_flow_run', args: flowArgs }, chrome_mcp_shared_1.NativeMessageType.CALL_TOOL, 120000);
if (proxyRes.status === 'success')
return proxyRes.data;
return {
content: [{ type: 'text', text: `Error calling dynamic flow tool: ${proxyRes.error}` }],
isError: true,
};
}
catch (err) {
return {
content: [
{
type: 'text',
text: `Error resolving dynamic flow tool: ${(err === null || err === void 0 ? void 0 : err.message) || String(err)}`,
},
],
isError: true,
};
}
}
// 发送请求到Chrome扩展并等待响应
const response = await native_messaging_host_1.default.sendRequestToExtensionAndWait({
name,
args,
}, chrome_mcp_shared_1.NativeMessageType.CALL_TOOL, 120000);
if (response.status === 'success') {
return response.data;
}
else {
return {
content: [
{
type: 'text',
text: `Error calling tool: ${response.error}`,
},
],
isError: true,
};
}
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error calling tool: ${error.message}`,
},
],
isError: true,
};
}
};
//# sourceMappingURL=register-tools.js.map