n8n
Version:
n8n Workflow Automation Tool
123 lines • 4.44 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowserLocalMcpServer = void 0;
const api_types_1 = require("@n8n/api-types");
const zod_to_json_schema_1 = require("zod-to-json-schema");
const NO_DOMAIN = 'browser';
class BrowserLocalMcpServer {
constructor(toolkit, toolContext, logger) {
this.toolContext = toolContext;
this.logger = logger;
this.toolsByName = new Map();
this.mcpTools = [];
for (const tool of toolkit.tools) {
const candidate = {
name: tool.name,
description: tool.description,
inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(tool.inputSchema),
annotations: { category: 'browser' },
};
const parsed = api_types_1.mcpToolSchema.safeParse(candidate);
if (!parsed.success) {
this.logger.warn('Skipping browser tool with unsupported input schema', {
tool: tool.name,
});
continue;
}
this.toolsByName.set(tool.name, tool);
this.mcpTools.push(parsed.data);
}
}
setDomainGate(gate) {
this.gate = gate;
}
getAvailableTools() {
return this.mcpTools;
}
getToolsByCategory(category) {
return category === 'browser' ? this.mcpTools : [];
}
async callTool(req) {
const tool = this.toolsByName.get(req.name);
if (!tool) {
return errorResult(`Unknown browser tool: ${req.name}`);
}
try {
const { _confirmation, ...rawArgs } = req.arguments;
const args = tool.inputSchema.parse(rawArgs);
const gateResult = await this.gateDomainAccess(tool, args, _confirmation);
if (gateResult) {
return gateResult;
}
const result = await tool.execute(args, this.toolContext);
const parsed = api_types_1.mcpToolCallResultSchema.safeParse(result);
if (parsed.success) {
return parsed.data;
}
return {
content: [{ type: 'text', text: JSON.stringify(result.content) }],
...(result.isError === true ? { isError: true } : {}),
};
}
catch (error) {
return errorResult(error instanceof Error ? error.message : String(error));
}
}
async gateDomainAccess(tool, args, confirmation) {
const gate = this.gate;
if (!gate) {
return undefined;
}
const host = await this.affectedHost(tool, args);
if (!host || host === NO_DOMAIN) {
return undefined;
}
if (typeof confirmation === 'string') {
switch (confirmation) {
case 'allowForSession':
await gate.tracker.approveDomain(host);
return undefined;
case 'allowOnce':
gate.tracker.approveOnce(gate.runId, host);
return undefined;
default:
return errorResult('Access denied by user');
}
}
if (gate.permissionMode === 'blocked') {
return errorResult('Browser access blocked by admin');
}
if (gate.permissionMode !== 'always_allow' && !gate.tracker.isHostAllowed(host, gate.runId)) {
return confirmationRequiredResult(host);
}
return undefined;
}
async affectedHost(tool, args) {
try {
const resources = await tool.getAffectedResources(args, this.toolContext);
return resources[0]?.resource;
}
catch {
return undefined;
}
}
}
exports.BrowserLocalMcpServer = BrowserLocalMcpServer;
function confirmationRequiredResult(host) {
const payload = {
toolGroup: 'browser',
resource: host,
description: `Browser: ${host}`,
options: ['denyOnce', 'allowOnce', 'allowForSession'],
};
return {
content: [
{ type: 'text', text: `${api_types_1.GATEWAY_CONFIRMATION_REQUIRED_PREFIX}${JSON.stringify(payload)}` },
],
isError: true,
};
}
function errorResult(message) {
return { content: [{ type: 'text', text: message }], isError: true };
}
//# sourceMappingURL=browser-local-mcp-server.js.map